Symmetric RWS Philosophy
> Status: PATCH · 已对齐 PCR Master Blueprint v1.0 > 范畴: 跨子域设计原则(无具体路径) > 参考实现: monad/RWS.h、simulation/pipeline/RWSTypes.h、fcc/dsl/FccProgram.h
0. 核心命题
仿真平台的所有"演化机器"都遵循同一抽象:(R, S) → (A, S', W)。
- R(Reader):装配后冻结的环境
- W(Writer):append-only 日志(Monoid)
- S(State):可读写的当前状态
- A:本次计算的返回值
通过把 Dynamics / FCC / Bus / Avionics Device 都统一到 RWS Monad,我们获得:
- 接口对称:每一层都看起来一样,认知负担线性而非乘积
- 副作用封装:日志、状态变更、信道写入全部走 Monad 语义,main loop 没有"暗动作"
- 可测试性:注入 mock R、检查 W、对比 S/S' 即可单测任何层
- 并行就绪:每个 BodyRWS 在自己的 (R, S) 上运行,BodyTick 天然可并行(详见
06_Simulation/Dual_Layer_RWS.md§8)
1. 五层 Monad 矩阵(v1.0)
| 层 | 算子描述 | 解释器 | RWS 实例 | 路径 |
|---|---|---|---|---|
| WorldTick | 全局编排(time → BodyTick × N → bus_route → topology) | sim::world_tick | RWS<WorldEnv, WorldState, WorldLog, WorldOut> | simulation/pipeline/WorldTick.{h,cpp} |
| BodyTick | 单体 5 阶段 pipeline | sim::body_tick | RWS<BodyEnv, RocketBody, BodyLog, BodyOut> | simulation/pipeline/BodyTick.{h,cpp} |
| FCC | Free Monad DSL(8 op) | FccInterpreter | RWS<FccEnv, FccState, FccLog, FccOutFrame> | fcc/dsl/, fcc/interpreter/, fcc/state/ |
| Plant Physics | force computer 链 + Forces Monoid | per-stage compiled pipeline | BodyRWS<Forces> | plant/physics/{Thrust,Drag,Gravity}.{h,cpp} |
| Bus | 语义消息 publish/poll | bus::IBus | BusLog(Writer 内化) | bus/{IBus,BusPayloads}.h |
> 关键变化(v1.0): > - v0 把 dynamics 当作"单层 RWS"——v1 改为 WorldRWS + BodyRWS 双层(详见 06_Simulation/Dual_Layer_RWS.md) > - v0 把 FCC 视为另一台独立机器——v1 让 simulation::FccTick(一个跨域适配 stage)把 FCC 嵌入 BodyTick ②(详见 06_Simulation/Body_World_Tick.md §2.2) > - v0 BusMonitor 是独立可观察对象——v1 内化为 BusLog(Writer Monad,详见 Blueprint §7.1)
2. WorldRWS / BodyRWS 双层
2.1 类型签名
// simulation/pipeline/RWSTypes.h
namespace sim {
template <typename A>
using WorldRWS = monad::RWS<WorldEnv, WorldState, WorldLog, A>;
template <typename A>
using BodyRWS = monad::RWS<BodyEnv, RocketBody, BodyLog, A>;
} // namespace sim2.2 降维-演化-升维
WorldRWS::ask → WorldEnv(全局只读)
│
└─ probe::assemble_body_env(world_env, body, t) ── 降维(per-body 投影)
│
▼
BodyRWS::ask → BodyEnv(局部只读,含 traj/aero/mass_props 私享 ctx)
│
└─ body_tick(dt) 演化 RocketBody
│
▼
BodyOut + BodyLog(出栈)
│
└─ lift_body_log(BodyLog) → WorldLog(升维 Monoid 累加)
▼
WorldRWS::tell ← WorldLog详见 06_Simulation/Dual_Layer_RWS.md §3 与 06_Simulation/WorldEnv_Assembly.md §4。
2.3 不对称性(v1.0 裁定)
WorldRWS 与 BodyRWS 不对称,这是有意为之:
- WorldEnv 是全局 universe(atmosphere/gravity/wind/plant_assets/scheduler)
- BodyEnv 是 per-body 局部 context(含 traj/aero/mass_props 三个私享 ctx + WorldEnv 引用)
强行让两者对称(如"WorldEnv 也持有 traj_ctx")会破坏 N-body 独立性。
3. FCC:Free Monad → RWS
3.1 算子分离
FCC 的算法(导航/制导/控制)是纯函数,不该被 RWS 语义污染。设计上:
- 算子层(DSL):
Free<FccOp, A>,仅 8 个 I/O 算子(ReadIMU/GetTime/GetEnv/GetState/UpdateState/OutputControls/WriteTelemetry/LogMessage) - 算法层:纯 C++ 函数,
Nav(FccState) → NavOutput、Guidance(...) → GuidanceOutput、Control(...) → ControlOutput - 解释器层:
FccInterpreter把 Free Monad tree →RWS<FccEnv, FccState, FccLog, A>
// fcc/dsl/FccProgram.h —— 纯算子描述(与硬件无关)
FccProgram ascent_program =
read_imu()
>>= [](Imu imu) {
return get_state() >>= [imu](FccState s) {
auto nav = run_nav_algorithm(imu, s); // 纯函数
auto guide = run_guidance_algorithm(nav, s); // 纯函数
auto ctrl = run_control_algorithm(guide, s); // 纯函数
return update_state(s.with(nav, guide, ctrl))
>> output_controls(ctrl);
};
};
// fcc/interpreter/FccInterpreter.cpp —— 把算子 tree 翻译成 RWS 动作
auto fcc_rws = FccInterpreter::run(ascent_program);
auto [out, new_state, log] = fcc_rws.run(fcc_env, current_state);3.2 与 simulation 的契约
FCC 被嵌入 BodyTick ② 阶段,由 simulation::FccTick(一个 stage 算子)调用:
BodyTick ② FccTick:
bus → FccInFrame (decode)
↓
fcc::tick(in_frame, dt)
├─ Free Monad tree(mission 编译产物,per-stage 预编译)
├─ Interpreter 翻译
└─ RWS<FccEnv, FccState, FccLog> 执行
↓
FccOutFrame → bus (encode)详见 04_FCC/Free_Monad_DSL.md、04_FCC/Interpreter_and_RWS.md、04_FCC/Pipeline_Factory_and_Compilation.md、04_FCC/FCC_State_Machine.md。
3.3 v0 → v1 关键修正
- v0 的
FccCore.cpp内置一个"主循环 scheduler"——v1 删除,scheduler 移到simulation::MultiRateScheduler(Runner 持有) - v0 把 GNC 算法写进
FccInterpreter::operator()lambda 里——v1 移到fcc/algorithms/(详见08_Cross_Cutting/Algorithm_Integration_Guide.md)
4. Plant Physics:Forces Monoid
4.1 设计
force computer 是 BodyRWS 算子,不是独立 RWS。多个 force computer 通过 Forces Monoid 累加:
// plant/physics/Thrust.h
BodyRWS<Forces> compute_thrust_contribution(const std::vector<EngineEffect>&);
// plant/physics/Drag.h
BodyRWS<Forces> compute_aero_contribution(const DynInFrame&);
// plant/physics/Gravity.h
BodyRWS<Forces> compute_gravity_contribution();
// 组合:Monoid 加法
auto all_forces =
compute_thrust_contribution(effects)
| [&](Forces f1) { return compute_aero_contribution(in) | [f1](Forces f2) { return body_pure(f1 + f2); }; }
| [&](Forces f12) { return compute_gravity_contribution() | [f12](Forces f3) { return body_pure(f12 + f3); }; };详见 05_Dynamics_Core/Forces_Monoid.md。
4.2 Per-stage 预编译
force computer 的组合在装配期完成(per WorldStage 一份);运行期 BodyTick 通过 compiled_dynamics.per_stage[body.world_stage] 直接派发(O(1))。
详见 05_Dynamics_Core/Topology_Algebra.md、04_FCC/Pipeline_Factory_and_Compilation.md。
5. Bus:Writer 内化
旧 BusMonitor 是独立的可观察对象,违背 RWS 纯性。v1 把它内化进 BusLog:
class IBus {
public:
virtual void publish(const BusMessage&) = 0;
virtual std::vector<BusMessage> poll(BusAddr) = 0;
};
// 实现持有 BusLog Writer(每次 publish/poll 追加 trace)
class InMemoryBus : public IBus {
private:
BusLog log_;
public:
BusLog flush_log(); // 由 BodyTick 末尾调用,并入 BodyLog
};详见 03_Avionics_and_Bus/Semantic_Bus_Pattern.md 与 Blueprint §7.1。
6. 三种 Monad 的协同时序
runtime::Runner::run():
while (t < end):
world_tick(dt).run(world_env, world_state)
│
└─ WorldRWS<WorldOut>
│
├─ A. 时间推进 (WorldState mutation)
│
├─ B. for each body:
│ ├─ probe → BodyEnv
│ └─ body_tick(dt).run(body_env, body)
│ │
│ └─ BodyRWS<BodyOut>
│ ├─ ① Avionics step
│ ├─ ② FCC tick:
│ │ fcc.tick(in, dt).run(fcc_env, fcc_state)
│ │ └─ FccRWS<FccOutFrame>(pure Layer-3)
│ ├─ ③ Plant Physics(per-stage compiled)
│ ├─ ④ Dynamics integrate
│ └─ ⑤ DynOutFrame 封装
│
├─ C. Bus 路由 (IBus + BusLog 内化)
│
└─ D. 事件解释 + 拓扑演化
ws.bodies = evolve_topology(...)7. 好处汇总
| 收益 | 机制 |
|---|---|
| 确定性 | (R, S) → (A, S', W) 函数式语义 |
| 可组合性 | >>= / | 把小算子拼大 pipeline |
| 可调试性 | Writer 记录完整决策历史 |
| 可热替换 | Free Monad 让 FCC 策略可在运行期切换(不改解释器) |
| 可并行 | BodyRWS 各自独立 (R, S),BodyTick 天然多线程 |
| HIL 透明 | 信道实现替换不动 Monad 骨架 |
8. 反模式
| 反模式 | 后果 | 正确做法 |
|---|---|---|
| 把日志写 stdout / 直接写文件 | 不可测、不可重定向、违 RWS 纯性 | Writer Monad tell(log);Runner 末尾统一落盘 |
| 在算子内修改 WorldEnv | Reader 语义破坏;并行不安全 | WorldEnv 装配后冻结 |
| BodyTick 内修改 WorldState | 破坏并行合法性 5 条件 | 事件出栈到 BodyOut.emitted_events,World 末尾串行应用 |
| 在 FCC Interpreter 里嵌算法 | 算法与解释器耦合;不可热替换 | 算法纯函数 in fcc/algorithms/,Interpreter 仅 dispatch |
BusMonitor 持有独立 ptr,main loop 拉数据 | 与 Writer 重复;线程安全噩梦 | BusLog 内化 |
Dynamics 单层 RWS(直接 WorldState → Forces) | 多 body 无法独立并行;环境冻结策略不清晰 | WorldRWS + BodyRWS 双层(v1.0 必须) |
9. C-Distillation 路径
| C++ Monad 抽象 | C 蜕化 |
|---|---|
WorldRWS<A>::run(env, state) | int world_tick_c(const WorldEnv*, WorldState*, WorldLog*, WorldOut*, Time) |
BodyRWS<A>::run(env, body) | int body_tick_c(const BodyEnv*, RocketBody*, BodyLog*, BodyOut*, Time) |
>>= / | | 顺序 C 函数调用(编译器内联) |
| Free<FccOp> tree | switch-case 解释器(编译期 inline) |
Writer Monad += | 数组 append + 计数累加 |
Reader Monad ask | const struct* 直接读 |
详见 09_Cross_Cutting/C_Distillation.md(待写)。
10. 引用
- Blueprint §2.6(Simulation:跨域绑定层)、§2.6.4(双层 RWS 实例化)、§3.1(FCC 三层架构)、§7.1(Bus 内化 Writer)
06_Simulation/Dual_Layer_RWS.md(WorldRWS + BodyRWS 详细定义)04_FCC/Free_Monad_DSL.md(FCC 算子层)04_FCC/Interpreter_and_RWS.md(解释器把 DSL 翻译成 RWS)04_FCC/Pipeline_Factory_and_Compilation.md(per-stage 编译)05_Dynamics_Core/Forces_Monoid.md(Forces Monoid 累加)01_Foundation/Monad_Toolkit.md(RWS 模板实现)