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 note:std::any + 递归 trampoline 在蒸馏阶段被 statically-resolved return types + while-loop 替换。
1. Visitor 形态
FccInterpreter 是一个 operator() 重载族,对每个 FccOp alternative 实现一个分支。来自 src/fcc/free_monad/FccInterpreter.h:
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::any | type erasure,让 trampoline 可统一处理 |
2. Trampoline 执行循环
interpret<A> 递归推进 Free 结构:
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>,需要:
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 的全流程
// 在 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_tick 调 BusManager::encode_from_fcc_out(out) 装包。
5. 解释器与 simulation 的契约
解释器不知道 Bus、不知道 simulation 的存在。它只接 4 个东西:
解释器输入:FccInFrame & FccState & FccEnv & FccFree<A>
解释器输出:FccState (mutated) & FccOutFrame (via last_output_)由 simulation/pipeline/BodyTick.cpp 负责:
- Bus 上读消息 →
FccInFrame(BusManager::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
解释器的可测试性来自三个引用都是依赖注入:
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 三模块的测试完全不需要解释器 —— 它们是纯函数:
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