Skip to content

Interpreter & RWS

> Aligned with PCR Master Blueprint v1.0 — see Blueprint §3.1(FCC 三层架构)、§3.3(FCC 隔离)。 > 职责:把 Layer 1 的 Free<FccOp, A> 解释为对 Layer 3 纯函数与外部世界的实际调用。当前实现走"std::any 通道 + 递归 trampoline"路径;蒸馏阶段会被静态化(参见 Hardware_Decoupling.md §2.4)。 > C-Distillation notestd::any + 递归 trampoline 在蒸馏阶段被 statically-resolved return types + while-loop 替换。


1. Visitor 形态

FccInterpreter 是一个 operator() 重载族,对每个 FccOp alternative 实现一个分支。来自 src/fcc/free_monad/FccInterpreter.h

cpp
namespace fcc {

class FccInterpreter {
    const FccInFrame& input_;
    FccState&         state_;
    const FccEnv&     env_;
    FccOutFrame       last_output_;

public:
    FccInterpreter(const FccInFrame& input, FccState& state, const FccEnv& env)
        : input_(input), state_(state), env_(env) {}

    FccOutFrame get_last_output() const { return last_output_; }

    // 每个 op 一个分支,返回 std::any
    std::any operator()(const ReadIMU&)        { return std::make_any<ImuMsg>(input_.imu.value_or(ImuMsg{})); }
    std::any operator()(const GetTime&)        { return std::make_any<Time>(input_.t_recv); }
    std::any operator()(const GetEnv&)         { return std::make_any<const FccEnv*>(&env_); }
    std::any operator()(const GetState&)       { return std::make_any<FccState>(state_); }   // 返回 copy
    std::any operator()(const UpdateState& op) { state_ = op.state; return std::make_any<std::monostate>(); }
    std::any operator()(const OutputControls& op) { last_output_ = op.cmd; return std::make_any<std::monostate>(); }
    std::any operator()(const WriteTelemetry& op) { /* flatten log */ return std::make_any<std::monostate>(); }
    std::any operator()(const LogMessage& op)     { /* journal */    return std::make_any<std::monostate>(); }
};

}

关键设计点

设计原因
持有 const FccInFrame&一拍内输入只读
持有 FccState&(可写引用)UpdateState 直接 in-place 写,无需返回新 state
持有 const FccEnv&Env 只读
last_output_ 内部成员OutputControls 不立即发 Bus,先暂存;外层 body_tick 末尾再装包
返回 std::anytype erasure,让 trampoline 可统一处理

2. Trampoline 执行循环

interpret<A> 递归推进 Free 结构:

cpp
template <typename A>
A interpret(const FccFree<A>& program, FccInterpreter& interp) {
    if (program.is_pure()) {
        return program.get_pure();
    } else {
        const auto& op = program.get_op();
        std::any result = std::visit(interp, op);      // 调 visitor
        FccFree<A> next = program.resume(result);      // 喂回 result 推进 Free
        return interpret(next, interp);                // 尾递归(编译器优化)
    }
}

Tail call optimization 风险:MSVC / 部分 GCC 配置可能不做 TCO。深度长 strategy 会触发栈溢出。蒸馏阶段会被 while(true) 循环替换(参见 Hardware_Decoupling.md §2.4.2)。


3. RWS 在哪儿?

> 注意:当前 src/fcc/free_monad/FccInterpreter.h 的实现没有显式 RWS Monad。Reader/Writer/State 三件事被拆开: > - Reader = const FccEnv& env_(直接持引用) > - Writer = WriteTelemetry 算子的副作用 + 外部传入的 FccLog > - State = FccState& state_(可写引用)

未来若要把解释器结果包装为 RWS<FccEnv, FccLog, FccState, A>,需要:

cpp
template <typename A>
RWS<FccEnv, FccLog, FccState, A> interpret_rws(const FccFree<A>& program);

但当前 PoC 阶段直接 in-place 写 state_ + 外部累积 FccLog 是足够的——Blueprint §7.12 软化 C-Distillation 也允许这种实用主义。

形式上"100% RWS 单子"是 Layer 2 的长期目标,不是当前阻塞项。


4. 一次 FCC tick 的全流程

cpp
// 在 simulation/pipeline/BodyTick.cpp 中
void run_fcc_step(FccInFrame in,         // 由 BusManager 解码而来
                  FccState&  state,
                  const FccEnv& env,
                  FccOutFrame& out)
{
    // 1. 构造解释器(绑定 in/state/env)
    FccInterpreter interp(in, state, env);

    // 2. 选 strategy(当前总是 flight_control_loop())
    auto strategy = flight_control_loop();

    // 3. 解释执行
    interpret(strategy, interp);

    // 4. 取 output
    out = interp.get_last_output();
}

state 是 in-place mutated,函数返回后 state 已经是 next tick state。 out 通过引用返回,外层 body_tickBusManager::encode_from_fcc_out(out) 装包。


5. 解释器与 simulation 的契约

解释器不知道 Bus、不知道 simulation 的存在。它只接 4 个东西:

text
解释器输入:FccInFrame & FccState & FccEnv & FccFree<A>
解释器输出:FccState (mutated) & FccOutFrame (via last_output_)

simulation/pipeline/BodyTick.cpp 负责:

  • Bus 上读消息 → FccInFrameBusManager::decode_to_fcc_in
  • run_fcc_step(...) 得到 FccOutFrame
  • FccOutFrame → Bus 消息(BusManager::encode_from_fcc_out

详见 Semantic_Bus_Pattern.md §9 与 06_Simulation/Body_World_Tick.md(待写)。


6. 测试解释器:Mock

解释器的可测试性来自三个引用都是依赖注入:

cpp
TEST(FccInterpreter, ReadImuReturnsInputValue) {
    FccInFrame in;
    in.imu = ImuMsg{ /* ... */ };
    in.t_recv = Time::from_sec(10.0);

    FccState  state;
    FccEnv    env = make_test_env();
    FccInterpreter interp(in, state, env);

    auto program = read_imu();
    ImuMsg got = interpret(program, interp);

    EXPECT_EQ(got.t, Time::from_sec(10.0));
}

对 GNC 三模块的测试完全不需要解释器 —— 它们是纯函数:

cpp
TEST(StepNav, AttitudePropagation) {
    NavState prev = make_zero_nav();
    ImuMsg   imu  = make_static_imu();
    FccEnv   env  = make_test_env();

    NavState next = fcc::navigation::step_nav(prev, imu, env, Time::from_ms(10));

    EXPECT_NEAR(next.q_b2n.norm(), 1.0, 1e-12);
}

这是 Layer 3 解耦的最大收益。


7. 性能问题与演进路线

当前实现的性能瓶颈:

当前蒸馏目标
FccFree 持续传递std::function + 堆分配函数指针 + 静态闭包
std::any 装箱堆分配 + 类型检查模板特化的返回类型
flatMap 拷贝多次拷贝 lambda静态展开为 while 循环
state_ = op.state;FccState 整体拷贝(大)双缓冲乒乓(Ping-Pong)

详见 Hardware_Decoupling.md §2 与 Static_Compilation_FSM.md §3 的演进路线。PoC 阶段不优化(Blueprint §7.12)。


8. 反模式

反模式为什么不行
在解释器分支里实现 GNC 算法违反 Layer 1/3 分层(参见 Free_Monad_DSL.md §6)
解释器持有 Bus 实例FCC 与 Bus 解耦由 simulation 装包负责(§5)
OutputControls 直接调 bus->publish违反 §3.3:FCC 不依赖 bus::IBus
GetState 返回 FccState&必须返回 copy;纯函数计算需 immutable 视图
在解释器内 throw 异常顶层 trampoline 没有 catch;错误应通过 FccLog + pending_output.fault_flags
多次调用 interpret 复用同一个 FccInterpreter解释器是 per-tick 一次性的;last_output_ 会污染

9. Cross References

  • DSL 算子集合 → Free_Monad_DSL.md
  • GNC 三模块纯函数 → FCC_State_Machine.md §2.2
  • 性能演进路线 → Hardware_Decoupling.md
  • 静态编译路线(按 FccStage 预编译 Pipeline)→ Static_Compilation_FSM.md
  • Bus 装包契约 → 03_Avionics_and_Bus/Semantic_Bus_Pattern.md §9
  • Free Monad 模板源 → src/types/monad/Free.h
  • 当前 Interpreter 源 → src/fcc/free_monad/FccInterpreter.h