Files
linuxcncWebWASM/docs/linuxcnc-web-wasm-frontend-communication-design.md

453 lines
36 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LinuxCNC Web/WASM 仿真前端与通信详细设计
**文档状态**:Draft v0.1
**适用基线**:LinuxCNC `2.10.0~pre0`;浏览器运行时、WASM 核心、Pyodide、OPFS 和 Web UI 采用同一 `context_id`
**上层路线**:[技术路线](linuxcnc-web-wasm-technical-roadmap.md)
**源码关联**:[功能与源程序关联目录](linuxcnc-web-wasm-source-map.md)
**覆盖验收**:[仿真全功能覆盖矩阵](linuxcnc-web-wasm-simulation-coverage.md)
**执行治理**:[项目执行与交付策略](linuxcnc-web-wasm-execution-strategy.md)
**总控执行**:[移植总控执行手册](linuxcnc-web-wasm-integration-playbook.md)
本文件把“浏览器前端”和“LinuxCNC 仿真核心之间的边界”定义为可实现、可测试、可版本化的契约。前端不直接调用 WASM 内部指针、不直接写 OPFS、不直接改变仿真状态;所有命令、状态、事件、日志、快照和恢复操作都通过 Worker 通信层完成。LinuxCNC 的解释器、TP、motion、HAL、IO、Python 和数字孪生是状态权威;前端是命令发起者、状态投影者和用户工作流编排者。
## 1. 设计目标与不可变原则
### 1.1 目标
1. 覆盖 LinuxCNC 仿真用户的完整工作流:工程选择、配置加载、G-code 编辑/预览/运行、MDI、jog、home、override、主轴、换刀、冷却、探针、报警、HAL、ClassicLadder、工具表、参数、日志、快照、恢复、导入导出和离线运行。
2. 前端在 Chromium、Firefox、Safari 支持范围内保持一致的命令语义和状态显示;不要求桌面 GUI 像素级复制,但必须保持工作流、状态、错误和结果等价。
3. 主线程只处理输入、布局、可视化和状态投影;WASM、Python、HAL 周期、文件系统、journal、快照和恢复全部在 Worker 侧执行。
4. 通信协议可录制、回放、限流、取消、重试和恢复;断网、标签页挂起、Worker 重启、回答中断或工具结果丢失不得产生重复副作用或跳过步骤。
5. UI 功能条目与 LinuxCNC 源文件、配置入口、测试和 oracle 互相可追溯,纳入覆盖矩阵和发布门禁。
### 1.2 不可变原则
| 原则 | 约束 |
|---|---|
| Worker 权威 | 前端显示的运行状态必须来自 Worker 快照或事件;前端本地乐观状态只能标记为 `pending`,不能当作仿真已执行 |
| 单写者 | 一个仿真会话只允许一个 Worker 实例写入 WASM 状态和 OPFS journal;多标签页只能观察或显式取得租约 |
| 固定顺序 | 命令按 `seq` 排序,WASM step、HAL thread、事件和 OPFS commit 在会话内串行 |
| 版本化边界 | 命令、事件、快照、能力、工程格式和错误码均带 `schema_version`;未知版本拒绝而不猜测 |
| 可重放 | 任何影响仿真结果的命令都可记录输入、tick、结果和状态哈希;调试 UI 不得改变执行顺序 |
| 本地优先 | 网络不可用不影响已缓存资源和 OPFS 工程的本地仿真;远程同步是可选功能 |
| 安全排除 | 不把宿主路径、shell、真实设备、原始 socket、动态库或任意 Python 宿主能力暴露给前端插件 |
## 2. 技术路线与实现方法
### 2.1 前端技术栈
```text
React + TypeScript
├─ AppShell / Router / PermissionBoundary
├─ Domain stores(session、program、motion、hal、io、workspace、recovery)
├─ WorkerClient(postMessage、序列、超时、重试和协议校验)
├─ Command UX(命令表单、pending、ack、result、错误和取消)
├─ Editors(G-code、INI、HAL、工具表、ClassicLadder、VCP schema)
├─ Machine UI(状态、MDI、jog、home、override、报警、工具和 I/O)
├─ Visualization(Three.js/WebGL/WebGPU 适配、轨迹、坐标、毛坯和模型)
└─ Diagnostics(HAL graph/scope、事件、日志、差异和能力报告)
│ structured clone / transferable ArrayBuffer
▼
Dedicated sim.worker.ts
├─ ProtocolGateway / TaskLedger
├─ SessionManager / CapabilityRegistry
├─ SimScheduler / LinuxCncCompat
├─ WasmRuntime / PythonRuntime
├─ WorkspaceRuntime / OpfsJournal
├─ SnapshotManager / RecoveryManager
└─ StatePublisher / BackpressureController
```
推荐采用 TypeScript 严格模式、运行时 schema 校验(例如手工生成的校验器或项目选定的 schema 工具)、结构化日志和可测试的纯 reducer。React 组件不能直接依赖 `postMessage`;所有消息经过 `WorkerClient`,所有 UI 状态经过 domain store。Three.js 只消费轨迹/姿态快照,不读取 WASM 内存。
### 2.2 分层责任
| 层 | 责任 | 允许访问 | 禁止访问 |
|---|---|---|---|
| View/UI | 用户输入、布局、无障碍、键盘/触摸、状态投影 | domain store、WorkerClient 的只读结果 | WASM、OPFS、网络写入、直接修改 authoritative state |
| Domain store | 规范化状态、reducer、派生选择器、pending/error 状态 | 已校验 Worker 消息 | 猜测未收到的状态、直接持久化 |
| WorkerClient | 握手、序列、超时、取消、重试、消息校验、传输统计 | `postMessage`、transferable buffer | 修改仿真语义、跳过 ack、吞掉错误 |
| ProtocolGateway | schema、权限、幂等键、命令路由和能力检查 | Worker 内模块 | 未版本化命令、跨会话写入 |
| SimRuntime | WASM、Python、HAL、IO、固定 tick、状态快照 | Worker 内 WASM/虚拟 FS | DOM、真实硬件、宿主命令 |
| WorkspaceRuntime | OPFS、journal、checkpoint、导入导出、配额 | Worker OPFS API | UI 线程同步句柄、未提交覆盖 |
| StatePublisher | 快照、事件、轨迹、采样、背压、丢弃策略 | Worker 状态 | 改变核心状态或重排事件 |
### 2.3 状态管理方法
Worker 发布三种数据:
1. **权威快照**:完整或增量的当前状态,带 `simulation_tick` 和状态哈希;UI 刷新、重连和恢复以此为基准。
2. **领域事件**:报警、错误、模式变化、换刀、主轴到速、探针触发、HAL 变化、恢复进度和命令结果;事件不可替代缺失的权威快照。
3. **高频流**:轨迹点、HAL sample、位置/速度/电流等,可按订阅和背压丢弃中间样本;流末端必须有序号和时间范围,不能丢失最终状态。
前端 store 保存 `authoritative`、`pending`、`last_error`、`last_verified_tick` 和 `stream_cursor`。任何本地编辑先进入 draft;保存成功后才更新 committed 版本。仿真状态、工程文件状态和 UI 偏好分开存储。
## 3. 前端功能设计
### 3.1 应用壳和导航
| 功能 | 前端行为 | 通信依赖 |
|---|---|---|
| 启动/能力检查 | 显示浏览器、WASM、Pyodide、OPFS、WebGL/WebGPU、跨源隔离和缓存状态 | `HELLO`、`CAPABILITIES` |
| 会话选择 | 打开、创建、复制、重命名、导入、导出工程;显示租约和最近快照 | `WORKSPACE_LIST`、`SESSION_CREATE`、`PROJECT_*` |
| 运行态导航 | 控制台、程序、预览、HAL、PLC、工具、配置、日志和差异视图 | `STATE_SNAPSHOT`、领域订阅 |
| 多视图同步 | 一个主控视图,其他视图只读订阅;切换不重启仿真 | `SUBSCRIBE`、`SNAPSHOT_REQUEST` |
| 权限边界 | 插件和 Python VCP 只能访问 manifest 授权的 pins、命令和文件 | `CAPABILITIES`、`PLUGIN_*` |
### 3.2 程序、MDI 和编辑器
- G-code 编辑器:语法高亮、行号、错误定位、程序资源树、搜索、跳转行、保存草稿和版本比较。
- 程序预览:解析状态、轨迹、刀具、坐标系、毛坯、当前行和首个差异 tick;预览必须明确“仅解析”或“已运行”。
- 程序控制:加载、运行、暂停、单步、可选停止、恢复、从行运行、重启、清空队列和中止。
- MDI:输入、历史、状态、返回结果、队列破坏和错误行;提交后显示 `pending → ack → result`。
- 宏/remap:显示调用栈、参数、返回值、Python 异常、资源限制和 `real_hardware_excluded` 错误。
### 3.3 手动操作和机床控制
- 模式:manual、mdi、auto、teleop、joint、world、emergency stop、off、on、homing。
- Jog:轴/关节选择、连续/增量、速度、方向、按下/释放、键盘/触控/手轮事件去抖和释放保护。
- Home/limit:单轴和全部回零、序列、共享开关、锁定转台、软限位/硬限位和跟随误差报警。
- Override:feed、spindle、rapid、adaptive、feed hold、feed inhibit、spindle brake 和急停;每个变更显示来源、范围和确认结果。
- 主轴/IO:方向、转速、定向、齿轮、冷却、润滑、换刀、探针、等离子 torch/arc-ok、激光功率和故障注入。
### 3.4 HAL、PLC 和诊断
- HAL browser:组件、pin、signal、param、类型、方向、线程、函数、连接和当前值。
- HAL command console:`loadrt`、`addf`、`setp`、`sets`、`net`、`unlinkp`、`show`、`save`、`source` 的受控子集。
- HAL scope/streamer:订阅通道、采样频率、触发、缩放、暂停、导出、回放和丢样本标记。
- ClassicLadder:工程加载/保存、section、symbol、变量、梯形图、顺序图、扫描周期、虚拟寄存器和调试状态。
- 诊断:事件时间线、报警确认、配置错误、Worker overrun、OPFS 错误、配额、能力报告和原生/WASM 差异。
### 3.5 工程、工具和配置
- 工程文件树:INI/HAL/TBL/VAR/NGC/PY/XML/TCL/CLP、模型和材料表;显示版本、哈希、未保存变更和冲突。
- 配置编辑器:section/键类型、include 图、HALFILE 顺序、资源引用、能力映射、警告和仿真排除项。
- 工具表:pocket/tool、几何、磨损、角度、方向、数据库查询、备份、迁移和换刀预览。
- 快照/恢复:运行态 checkpoint、事件增量、恢复点、差异和回滚;UI 不得把未提交 draft 当作已恢复。
- 导入/导出:工程包、manifest、golden、日志和兼容报告;导入先校验哈希、版本、路径和权限。
### 3.6 可视化与响应式设计
- 三栏工作布局:左侧工程/程序资源,中部程序与 3D 主视图,右侧状态/报警/工具面板;窄屏切换为标签页和底部控制栏。
- 关键状态使用稳定区域:模式、急停、运行/暂停、当前行、坐标、进给、主轴、报警和恢复状态不能因消息刷新跳动。
- 轨迹数据使用 transferable `ArrayBuffer`;渲染线程只处理最新可绘制帧,中间帧可丢弃并显示采样间隙。
- 所有控制支持键盘和触摸;急停、停止、释放 jog 等高风险操作需要可见状态和二次确认策略。
- 前端不使用“看起来完成”的乐观动画;命令尚未 `verified` 时显示 pending,错误必须保留来源和操作 ID。
## 4. 前端与 LinuxCNC 通信协议
### 4.1 通信通道
```text
UI thread
└─ WorkerClient.postMessage()
├─ control channel: command/ack/result/error/recovery
├─ state channel: snapshot/event/alarm/capability
└─ stream channel: trajectory/HAL samples/log chunks (transferable buffers)
```
第一版使用 Dedicated Worker 的 `postMessage` 和结构化克隆;大块轨迹、日志和快照使用 transferable `ArrayBuffer`。SharedArrayBuffer 只作为后续性能优化,必须保持相同命令顺序和结果。通信不使用真实 NML socket;NML 消息类型和状态语义在 `LinuxCncCompat` 中映射到版本化 message bus。
### 4.2 通用消息信封
所有消息都包含以下字段;事件和流也必须带 `seq` 和 `simulation_tick`,不能使用裸 JSON:
```ts
type MessageEnvelope<T> = {
schema_version: 1;
kind: string;
context_id: string;
session_id: string;
seq: number;
operation_id?: string;
simulation_tick?: number;
sent_at_monotonic_ms?: number;
payload: T;
};
```
`seq` 在会话内单调递增;`operation_id` 对所有有副作用命令必填;`sent_at_monotonic_ms` 只用于诊断,不参与仿真结果。消息大小、数组长度、字符串长度、订阅数量和命令速率均由 capability/limits 下发并由 Worker 再次校验。
### 4.3 握手和能力协商
```text
UI -> HELLO { client_version, supported_schema_versions, requested_features }
Worker -> CAPABILITIES { context_id, abi, snapshot, commands, streams, limits, exclusions }
UI -> SESSION_ATTACH { session_id?, project_id, lease_token? }
Worker -> SESSION_READY { session_id, state_hash, simulation_tick, subscriptions }
```
握手失败不得降级到未版本化协议。`CAPABILITIES` 必须列出支持的 G/M/O、HAL 组件、kinematics、Python 模块、UI 插件、最大程序大小、最大快照、可用流和 `REAL-EXCLUDED` 能力。前端按能力隐藏或禁用控制,并显示原因,不把缺失能力伪装成运行时错误。
### 4.4 命令分类
| 命令族 | 示例 | 副作用 | 处理要求 |
|---|---|---:|---|
| 生命周期 | `SESSION_CREATE`、`LOAD_PROJECT`、`START`、`STOP`、`RESET`、`DESTROY` | 是 | prepare/verify/commit;租约和状态机检查 |
| 程序 | `PROGRAM_LOAD`、`PROGRAM_RUN`、`PROGRAM_PAUSE`、`PROGRAM_RESUME`、`PROGRAM_ABORT`、`PROGRAM_STEP`、`PROGRAM_SEEK` | 是 | 记录程序哈希、当前行和 tick;不允许跳过队列语义 |
| MDI/运动 | `MDI_EXECUTE`、`JOG_START`、`JOG_STOP`、`HOME`、`SET_MODE` | 是 | jog 必须有 stop/释放;重复 `operation_id` 返回原结果 |
| 倍率/IO | `SET_OVERRIDE`、`SPINDLE_COMMAND`、`COOLANT_COMMAND`、`TOOL_PREPARE`、`TOOL_CHANGE`、`PROBE_CONFIG` | 是 | 能力、范围、模式和虚拟设备状态校验 |
| HAL/PLC | `HAL_COMMAND`、`HAL_SET`、`HAL_SUBSCRIBE`、`PLC_START/STOP` | 是 | 受控白名单、pin 类型检查、周期和权限检查 |
| 工作区 | `PROJECT_SAVE`、`IMPORT`、`EXPORT`、`CHECKPOINT`、`RECOVER` | 是 | OPFS journal;结果带版本和哈希 |
| 查询/诊断 | `SNAPSHOT_REQUEST`、`HAL_READ`、`LOG_QUERY`、`DIFF_QUERY`、`CAPABILITIES` | 否/只读 | 可取消、可重复;不改变仿真状态 |
| 控制 | `SUBSCRIBE`、`UNSUBSCRIBE`、`CANCEL`、`PING` | 订阅有状态 | 明确 cursor、采样率、背压和取消结果 |
每条命令经过 `validate → authorize → ledger_prepare → execute → verify → ledger_commit → ack/result`。`ACK` 只表示 Worker 已接收和排队,不表示仿真完成;UI 只有收到 `COMMAND_RESULT` 或明确的 `ERROR` 才结束 pending。
### 4.5 状态、事件和流
| 消息 | 频率 | 内容 | 丢失策略 |
|---|---:|---|---|
| `STATE_SNAPSHOT` | 20-60 Hz 或请求 | task/motion/interpreter/IO/HAL/tool/Python/alarms/恢复状态 | 不可丢最终快照;按 cursor 重取 |
| `EVENT`/`ALARM` | 事件驱动 | 状态变化、错误、换刀、探针、主轴、配置和恢复事件 | 不可静默丢;持久化 event log,缺号触发恢复 |
| `TRAJECTORY_CHUNK` | 订阅 | tick 范围、坐标/姿态、刀具、采样和差异标记 | 可丢中间块;保留范围和 gap,最终位置来自快照 |
| `HAL_SAMPLE` | 订阅 | 通道定义、采样值、tick、丢样本数 | 可按背压降采样;触发和边界样本不可丢 |
| `LOG_CHUNK` | 请求/批量 | 级别、来源、操作 ID、错误码和文本 | 可滚动;错误和恢复日志不可丢 |
| `RECOVERY_STATUS` | 恢复期间 | checkpoint、重放、对账、冲突、可继续状态 | 不可丢,UI 必须阻塞依赖状态的命令 |
### 4.6 典型消息
```json
{
"schema_version": 1,
"kind": "MDI_EXECUTE",
"context_id": "linuxcnc-2.10.0-pre0-wasm-c1",
"session_id": "s-42",
"seq": 184,
"operation_id": "op-184-9fd1",
"payload": { "line": "G0 X10 Y5", "source": "mdi", "queue_policy": "queue_buster" }
}
```
```json
{
"schema_version": 1,
"kind": "COMMAND_RESULT",
"context_id": "linuxcnc-2.10.0-pre0-wasm-c1",
"session_id": "s-42",
"seq": 184,
"operation_id": "op-184-9fd1",
"simulation_tick": 93210,
"payload": {
"status": "verified",
"state_hash": "sha256:...",
"canonical_events": 1,
"error": null,
"ledger_commit": "journal/184.commit"
}
}
```
### 4.7 错误、取消和超时
错误码分为 `protocol_*`、`capability_*`、`config_*`、`interpreter_*`、`motion_*`、`hal_*`、`python_*`、`workspace_*`、`recovery_*` 和 `real_hardware_excluded`。错误必须包含 `code`、`message_key`、参数、来源文件/行、`operation_id`、`simulation_tick`、可重试性和建议动作。
取消分为 `requested`、`accepted`、`draining`、`cancelled`、`too_late`;已经提交的有副作用命令不能回滚成“未执行”。超时只表示客户端尚未收到确认,WorkerClient 必须进入 `awaiting_reconcile` 并查询账本/状态,而不是直接重发。
### 4.8 背压、流控和性能预算
1. 控制命令使用有界队列;超过上限返回 `protocol_backpressure`,不丢弃已经接收的副作用命令。
2. 快照发布采用“最新优先”;事件、报警、恢复状态和命令结果采用“顺序优先”,不可用快照降采样替代。
3. 轨迹和 HAL 流按订阅者独立限速;Worker 返回 `cursor`、`sample_period`、`dropped_count` 和 tick 范围。
4. 单次消息、单工程、单快照、单流和订阅数量上限在 `CAPABILITIES.limits` 中声明,并在 Worker 和 UI 双重检查。
5. 前端只渲染当前可见窗口和最近快照;日志、轨迹和波形使用虚拟列表/分块,不把无限历史放入 React state。
6. M1 建立命令确认 P95/P99、快照延迟、流吞吐、UI 主线程阻塞和 Worker overrun 基线;超过预算必须阻止里程碑。
### 4.9 LinuxCNC 源程序和通信映射
ProtocolGateway 不能直接调用零散 LinuxCNC 函数;每个命令族先映射到 `LinuxCncCompat` 的稳定接口,再由兼容层访问对应源码状态机。以下关联必须写入 manifest 的 `source_files`、`source_symbols`、`protocol_messages` 和 `state_fields`:
| 前端通信域 | LinuxCNC 源程序 | Worker/WASM 适配接口 | 前端消息/状态 |
|---|---|---|---|
| 生命周期和 Task | `linuxcnc/src/emc/task/emctaskmain.cc`、`taskclass.cc`、`taskintf.cc`、`emctask.cc` | `SimTask.create/load/start/stop/reset/destroy` | `SESSION_*`、`START/STOP/RESET`、`task.state` |
| 程序和解释器 | `linuxcnc/src/emc/rs274ngc/interp_read.cc`、`interp_execute.cc`、`interp_queue.cc`、`interp_convert.cc`、`modal_state.cc` | `Interpreter.load/run/step/abort/seek` | `PROGRAM_*`、`interpreter.*`、`canonical.*` |
| MDI 和 canonical | `linuxcnc/src/emc/task/emccanon.cc`、`linuxcnc/src/emc/nml_intf/interpl.cc`、`linuxcnc/src/emc/rs274ngc/interp_queue.cc` | `SimTask.mdi`、`CanonicalQueue` | `MDI_EXECUTE`、`command_result`、当前行/队列 |
| Motion/jog/home/limit | `linuxcnc/src/emc/motion/command.c`、`control.c`、`homing.c`、`motion.c`、`motion_struct.h` | `SimMotion.command/jog/home/snapshot` | `JOG_*`、`HOME`、`SET_MODE`、`motion.*`、`homing.*` |
| TP 和轨迹 | `linuxcnc/src/emc/tp/tp.c`、`tc.c`、`tcq.c`、`blendmath.c`、`spherical_arc.c` | `TrajectoryPlanner.step/readChunk` | `TRAJECTORY_CHUNK`、位置/速度/加速度、queue depth |
| Kinematics/姿态 | `linuxcnc/src/emc/kinematics/*kins.c`、`*kins.cc`、`kins_util.c`、`trtfuncs.c` | `Kinematics.forward/inverse/select` | `kinematics.*`、joint/axis/pose、singularity error |
| IO/主轴/换刀/冷却 | `linuxcnc/src/emc/iotask/ioControl.cc`、`ioControl_v2.cc`;`linuxcnc/src/emc/tooldata/**` | `SimIo.command/snapshot`、`ToolService` | `SPINDLE_*`、`TOOL_*`、`COOLANT_*`、`io.*`、`tool.*` |
| HAL/halui | `linuxcnc/src/hal/hal_lib.c`、`hal.h`、`hal_priv.h`、`halmodule.cc`;`linuxcnc/src/emc/usr_intf/halui.cc` | `SimHal.execute/read/subscribe` | `HAL_COMMAND/SET/SUBSCRIBE`、`hal.*`、`HAL_SAMPLE` |
| ClassicLadder | `linuxcnc/src/hal/classicladder/classicladder.c`、`calc.c`、`calc_sequential.c`、`files_project.c`、`module_hal.c` | `SimPlc.load/start/stop/step` | `PLC_*`、PLC section/variable/scan state |
| NML 状态/命令/错误 | `linuxcnc/src/emc/nml_intf/emc.cc`、`emc.hh`、`emc_nml.hh`;`linuxcnc/src/libnml/nml/stat_msg.cc`、`cmd_msg.cc` | `LinuxCncCompat.translateCommand/publishStatus` | envelope、`STATE_SNAPSHOT`、`EVENT`、`ALARM` |
| Python/remap/VCP | `linuxcnc/src/emc/pythonplugin/python_plugin.cc`;`linuxcnc/src/emc/rs274ngc/interp_python.cc`、`interp_remap.cc`;`linuxcnc/lib/python/hal.py`、`qtvcp/**`、`gladevcp/**` | `PythonRuntime.invoke`、虚拟 `linuxcnc/hal` 模块 | `PLUGIN_*`、Python result/error、VCP pin/event |
| 工程和参数 | `linuxcnc/src/libnml/inifile/**`、`linuxcnc/src/emc/ini/**`、`linuxcnc/src/emc/tooldata/**` | `WorkspaceRuntime`、`ConfigService` | `PROJECT_*`、`IMPORT/EXPORT`、配置/工具/参数状态 |
前端所需但 LinuxCNC 原生状态中不存在的字段,例如 `pending`、`operation_id`、`lease_token`、`recovery_progress` 和 `dropped_count`,属于 Web 适配状态,必须明确标记为 `SIM-ADAPTER`,不能写回 LinuxCNC 核心结构。
### 4.10 快照状态契约
快照按领域分区,允许通过位图和版本号发送增量;增量必须基于已确认的 `base_snapshot_id`,基线缺失时前端请求完整快照:
```ts
type SimSnapshotV1 = {
snapshot_id: string;
base_snapshot_id?: string;
full: boolean;
simulation_tick: number;
state_hash: string;
task: { state: string; mode: string; interp_state: string; current_line: number };
motion: { axes: number[]; joints: number[]; velocity: number[]; homed: boolean[]; limits: number };
coordinates: { machine: number[]; relative: number[]; g5x: number; g92: number[]; tool_offset: number[] };
spindle: Array<{ enabled: boolean; direction: number; commanded: number; actual: number; at_speed: boolean }>;
io: { coolant: number; lube: boolean; digital: number[]; analog: number[]; probe: boolean };
tool: { current: number; prepared: number; pocket: number; change_state: string };
overrides: { feed: number; rapid: number; spindle: number[]; adaptive: number; feed_hold: boolean };
hal: { graph_revision: number; changed_values?: ArrayBuffer };
alarms: Array<{ id: string; severity: string; code: string; acknowledged: boolean }>;
recovery: { status: string; last_verified_seq: number; checkpoint_tick: number };
};
```
实际编码可以是生成的二进制 schema,但字段语义必须保持。浮点数组的轴顺序、单位、NaN/Infinity 策略、位掩码和空值语义必须在 schema 中固定;前端不能根据数组长度猜测机床配置。
### 4.11 Worker 到 WASM C ABI 调用规则
| Worker 操作 | C ABI | 调用规则 | 结果 |
|---|---|---|---|
| 创建核心 | `sim_create(config, len)` | 握手和配置校验后调用一次;失败则不创建会话 | 错误码、能力基础和初始状态 |
| 挂载工程 | `sim_load_workspace(root_path)` | WorkspaceRuntime 完成 journal 恢复和 `/workspace` 挂载后调用 | 配置诊断、资源图和可启动状态 |
| 发送命令 | `sim_command(cmd, len)` | ProtocolGateway 编码已校验命令;每个 `operation_id` 最多进入核心一次 | 入队/拒绝错误;最终结果从事件或快照确认 |
| 推进仿真 | `sim_step(cycles)` | 仅 SimScheduler 调用;UI 不得直接指定墙钟结果 | 新 tick、事件和可发布状态 |
| 读取快照 | `sim_read_snapshot(out, capacity)` | 先查询/预分配容量;复制到 Worker 所有的 buffer,再转移给 UI | `STATE_SNAPSHOT` 或容量错误 |
| 读取事件 | `sim_read_event(out, capacity)` | 循环读取到空或有界批次上限;保持核心顺序 | `EVENT`、`ALARM`、`COMMAND_RESULT` |
| 导出运行态 | `sim_export_state(out, capacity)` | 在安全 tick 暂停写入,生成带 schema/context 的 checkpoint | WorkspaceRuntime prepare/commit |
| 导入运行态 | `sim_import_state(data, len)` | 校验 context、schema、工程哈希后导入;导入失败保留旧实例 | `RECOVERY_STATUS` 和完整快照 |
| 销毁核心 | `sim_destroy()` | 关闭订阅、flush journal、关闭句柄后调用;幂等 | `SESSION_DESTROYED` |
WASM 内存由 `WasmRuntime` 独占。输入 buffer 在调用返回前有效;输出在下一次核心调用前复制到 Worker buffer;TypeScript 不长期保存 WASM heap view。C ABI 返回值统一映射为版本化错误码,禁止用异常字符串作为协议。`sim_step`、`sim_command`、状态导入导出和销毁不能并发执行。
## 5. 生命周期、恢复和离线通信
### 5.1 会话生命周期
```text
new → handshaking → attaching → loading → ready
ready → running ↔ paused
running/paused → stopping → stopped
任何状态 → recovering → ready | blocked
任何状态 → destroying → destroyed
```
状态转换由 Worker 权威决定。UI 可以请求转换,但不能本地伪造转换。每次转换发送 `STATE_TRANSITION` 事件并写入任务账本;非法转换返回 `state_transition_invalid`。
### 5.2 刷新、Worker 重启和回答中断
Worker 启动后先发送 `RECOVERY_REQUIRED` 或 `SESSION_READY`。UI 在收到 `RECOVERY_STATUS=verified` 前禁用依赖运行态的运行、jog、MDI 和工具命令,只允许恢复、导出、查看日志和取消。`WorkerClient` 维护未完成操作表;重连时按 `operation_id` 查询结果,已 commit 的操作只关联原结果,未 commit 的操作从 checkpoint 继续。
### 5.3 断网和重新联网
前端的网络状态不能决定本地仿真状态。Service Worker 缓存应用壳、WASM、Pyodide、协议 schema 和样例;Worker 继续使用 OPFS journal、快照和本地命令。重新联网只执行缓存更新或可选同步,必须先比较 `context_id`、工程哈希、最后 `seq`、tick 和状态哈希;冲突时保留本地版本并显示差异。
### 5.4 多标签页与租约
默认一个工程只能有一个写入会话。主标签页持有 `lease_token` 和 heartbeat;其他标签页以只读模式订阅快照。主标签页崩溃后,候选标签页读取 lease expiry、校验 checkpoint 和 journal,再通过 `SESSION_CLAIM` 取得新租约。两个写者不允许同时运行;无法确定租约时进入 `blocked` 而不是并发写入。
## 6. 安全、权限和兼容性
- Worker 对命令、文件、组件、Python 模块和插件能力做白名单检查;UI 隐藏按钮不是安全边界。
- 工程路径统一规范化为 `/workspace` 相对路径,拒绝 `..`、绝对宿主路径、符号链接和未授权扩展名。
- Python remap、filter 和 VCP 只访问虚拟 `linuxcnc`、`hal`、INI、参数、时间和 I/O API;`fork/exec/subprocess/threading/socket/设备` 返回明确错误。
- 所有外部输入(导入工程、G-code、HAL、XML/UI、插件)先解析、限制资源、记录来源哈希,再提交给 Worker。
- 前端显示 `REAL-EXCLUDED` 时必须说明“不连接真实硬件、不提供安全保证”,禁止使用“在线”“已连接”等误导状态。
- schema/ABI/工程格式升级采用新版本号、双读迁移和 context lock;旧版本不能静默解释新字段。
## 7. 详细实施任务
任务标题统一使用 `[WS-x][Mx]`,每个任务绑定 source map/coverage manifest 条目、固定上下文包和测试证据。
### WS-5 前端与可视化任务
| 任务 ID | 任务 | 交付物 | 依赖 | 验收 |
|---|---|---|---|---|
| FE-01 | 应用壳、路由、响应式布局和主题变量 | AppShell、导航、布局断点、无障碍基础 | 无 | 首次启动、窄屏、键盘和只读模式通过 |
| FE-02 | WorkerClient 和协议校验 | 信封类型、schema 校验、序列、超时、取消、重试 | COM-01 | 非法/未知消息拒绝;重复操作不重复副作用 |
| FE-03 | 会话/工程工作流 | 工程选择、租约、导入导出、恢复向导 | WS-3、COM-02 | 刷新、Worker 重启、无网、冲突恢复通过 |
| FE-04 | 程序编辑器和预览 | G-code 编辑、诊断、轨迹预览、当前行和差异 | WS-1、COM-02 | 语法/运行错误定位,预览与 golden 一致 |
| FE-05 | 控制台和手动操作 | MDI、run/pause/step/abort、jog/home/override | WS-2、COM-02 | 模式状态机、释放保护、命令结果完整 |
| FE-06 | 工具、IO、报警和状态面板 | 工具表、主轴、冷却、换刀、探针、报警 | WS-2、WS-3 | 正常/故障/恢复工作流通过 |
| FE-07 | HAL/PLC/诊断界面 | pin 图、命令控制台、scope、Ladder、日志 | WS-2、WS-4 | 订阅、触发、回放和权限限制通过 |
| FE-08 | Three.js 机床和毛坯 | 模型、姿态、轨迹、坐标、切削层 | FE-04、COM-03 | 非空渲染、采样背压、最终姿态一致 |
| FE-09 | VCP/插件 schema 运行时 | XML/descriptor 转 Web Component、权限和生命周期 | WS-4、COM-02 | 样例面板、pin 回调、异常隔离通过 |
| FE-10 | 前端性能和可观测性 | 虚拟列表、帧预算、消息统计、录制回放 | FE-01..09 | P95/P99 和主线程预算满足门禁 |
### WS-2/COM 通信与状态任务
| 任务 ID | 任务 | 交付物 | 依赖 | 验收 |
|---|---|---|---|---|
| COM-01 | schema 和 C/TS 类型生成 | `protocol.v1`、命令/事件/错误/能力定义 | WS-0 | schema 版本锁定,未知字段策略明确 |
| COM-02 | ProtocolGateway/TaskLedger | 路由、权限、幂等、prepare/commit、恢复 | WS-1、WS-3 | 超时、确认丢失、重复响应和重启恢复通过 |
| COM-03 | SnapshotPublisher/流控 | 快照、事件、轨迹、HAL sample、cursor、背压 | WS-2 | 事件不丢、流可降采样、最终快照可重取 |
| COM-04 | LinuxCncCompat message bus | NML command/status/error 到 Worker 消息映射 | WS-1 | 消息类型、顺序、错误和状态 golden 对照 |
| COM-05 | 能力/限制注册表 | G/M/O、组件、kinematics、Python、UI、limits | WS-0、WS-1..4 | UI 正确启用/禁用,排除项可解释 |
| COM-06 | 浏览器和故障矩阵 | Playwright 通讯、离线、租约、刷新、背压测试 | COM-01..05 | Chromium/Firefox/Safari 目标矩阵通过 |
### 7.3 实施批次和依赖顺序
| 批次 | 并行任务 | 串行依赖/冻结点 | 可演示结果 |
|---|---|---|---|
| B0 规范和基线 | COM-01、COM-05、FE-01 | 冻结 `protocol.v1`、能力 ID、错误码和 UI 信息架构 | mock Worker 可完成握手、能力显示和错误页 |
| B1 最小通信闭环 | COM-02、COM-04、FE-02 | 必须先有 `sim_create/load/command/step/snapshot` ABI | 加载最小工程、发送命令、显示权威快照 |
| B2 核心操作 | FE-04、FE-05、COM-03 | interpreter/motion 状态字段冻结 | G-code、MDI、jog、home、override、轨迹闭环 |
| B3 持久化恢复 | FE-03、COM-06 的恢复子集 | WorkspaceRuntime、TaskLedger、checkpoint schema 冻结 | 刷新、Worker 重启、断网和确认丢失自动接续 |
| B4 设备和工具 | FE-06、FE-07 | HAL/IO/tool/PLC schema 和虚拟设备能力冻结 | 主轴、换刀、探针、报警、HAL/PLC 工作流 |
| B5 可视化和插件 | FE-08、FE-09 | 轨迹/姿态流和插件权限冻结 | Three.js 机床、毛坯、VCP/QtVCP 等价工作流 |
| B6 稳定发布 | FE-10、COM-06 全矩阵 | 全量 manifest、浏览器和性能门禁 | 可离线发布候选、兼容/差异/性能报告 |
B0-B2 可以使用只读 mock/golden 驱动 UI 开发,但到对应里程碑退出时必须切换真实 Worker/WASM 状态;mock 不能作为功能完成证据。B3 以后不得修改 `protocol.v1` 语义,只能增加向后兼容字段;破坏性变更必须创建 `protocol.v2` 并回退到 FE-M0 评审。
## 8. 里程碑和退出条件
| 里程碑 | 目标 | 关键输出 | 必须通过的退出条件 |
|---|---|---|---|
| FE-M0 协议冻结 | 定义前端、Worker、WASM 和 OPFS 边界 | `protocol.v1`、能力模型、错误码、UI 信息架构、source/coverage 关联 | 所有消息有 envelope/schema;命令权限和状态转换无歧义 |
| FE-M1 可启动垂直切片 | 工程加载、状态显示、最小 G-code 运行 | AppShell、WorkerClient、`create/load/step/snapshot`、最小 3 轴页面 | 浏览器可加载缓存样例;命令 ack/result、快照和错误完整 |
| FE-M2 操作闭环 | 程序、MDI、jog、home、override、报警 | 控制台、程序编辑/预览、手动操作、状态面板 | 正常/边界/错误/暂停恢复与原生 golden 对照通过 |
| FE-M3 工程和恢复 | OPFS、快照、断网、重启、租约 | 工程树、导入导出、TaskLedger、恢复 UI、离线 PWA | 半提交、配额不足、确认丢失、标签页抢占和冲突均不丢数据 |
| FE-M4 全部仿真工作流 | HAL/PLC/工具/IO/Python/VCP/3D | scope、Ladder、工具、数字孪生、插件和机床视图 | 覆盖矩阵所有 Web/API 条目有 source/test/oracle,关键交互通过 |
| FE-M5 发布候选 | 稳定、离线、高效和可诊断 | 浏览器报告、性能报告、兼容报告、迁移/回滚和离线包 | G0-G5 全绿;无孤儿来源、无未覆盖 UI 工作流、无静默降级 |
每个里程碑都必须附固定 context lock、上下文包、源程序关联 diff、manifest diff、测试日志、golden/difference、性能指标和回滚步骤。发现协议、状态或源文件范围变化时,回退到最早受影响的 FE/COM 里程碑重新验证。
FE 里程碑与项目里程碑映射如下:FE-M0 属于 M0,FE-M1 属于 M1,FE-M2 属于 M2,FE-M3 属于 M3,FE-M4 属于 M4,FE-M5 属于 M5。项目里程碑未通过时,对应 FE 里程碑不能单独签署完成。
## 9. 前端验收与覆盖矩阵绑定
前端功能不得脱离 LinuxCNC 功能条目单独宣布完成。每个前端任务至少绑定:
```text
feature_id → source_files/source_symbols
→ config_inputs
→ worker command/event schema
→ UI workflow test
→ native/WASM oracle or Web-equivalent oracle
→ recovery/offline test
→ context_id + evidence artifact
```
前端与 LinuxCNC 通信条目应使用 `WEB-EQUIV` 或 `SIM-ADAPTER`;Python/VCP 回调使用 `PY-WASM`;虚拟主轴、探针、换刀、等离子、激光、编码器和限位使用 `SIM-DEVICE`。真实设备、真实实时性和安全认证继续使用 `REAL-EXCLUDED`,但其仿真可观察行为必须由数字孪生条目覆盖。
只有以下条件全部满足,才能声明“前端仿真工作流完成”:
1. 能力协商正确反映当前构建和配置,不显示未实现功能。
2. 命令从 UI 到 Worker 到 LinuxCNC 兼容层的顺序、参数、错误和结果可追踪。
3. 快照、事件、流和最终状态在正常、暂停、恢复、断网和 Worker 重启后保持一致。
4. 每个操作具备 pending/ack/result/error 状态,超时先对账,重试不重复副作用。
5. UI、API、Python、HAL 和 OPFS 证据都进入 manifest,且无孤儿来源或未覆盖交互。
## 10. 详细测试矩阵
| 测试层 | 重点用例 | 自动化方式 | 阻断条件 |
|---|---|---|---|
| Schema/类型 | 全部命令、事件、快照、未知版本、缺字段、超限数据 | TS 单测 + C/TS 编解码 round-trip | 任一消息无法双向解析或错误码不稳定 |
| WorkerClient | seq、operation_id、ack/result、timeout、cancel、重复/迟到响应 | 假 Worker、确定性时钟 | 重复副作用、pending 泄漏或错误被吞掉 |
| ProtocolGateway | 权限、状态机、能力、prepare/commit、幂等 | Worker 单测 + WASM stub | 未授权命令进入核心或账本不一致 |
| C ABI | create/load/command/step/snapshot/event/export/import/destroy | Node/WASM harness 和浏览器 Worker | 内存越界、顺序变化、错误映射不一致 |
| Domain store | 完整/增量快照、事件乱序、流 gap、恢复覆盖 | reducer/property tests | UI 状态无法由消息历史重建 |
| UI 工作流 | 工程、程序、MDI、jog、home、override、工具、HAL、PLC、报警 | Playwright + 页面对象 | 任一覆盖条目没有正常/错误/恢复路径 |
| 可视化 | 轨迹、姿态、坐标、毛坯、resize、隐藏/恢复 | Playwright screenshot + canvas pixel checks | 空画布、姿态错误、遮挡或动态内容改变布局 |
| 离线/恢复 | 首次缓存、启动无网、中途断网、刷新、Worker 崩溃、租约转移 | Playwright context/service-worker fault injection | 已提交数据丢失、重复命令、无法继续 |
| 性能/背压 | 大 G-code、多 HAL pin、高采样、长日志、快速模式 | browser benchmark + trace | P95/P99 超预算、无界队列、主线程长任务 |
| 浏览器兼容 | Chromium、Firefox、Safari 支持矩阵 | CI/browser farm 或锁定本地环境 | 必选工作流在任一目标浏览器失败 |
每个测试输出 `context_id`、浏览器、协议版本、工程哈希、最后 seq/tick、状态哈希和产物路径。视觉截图只验证布局和渲染,不能替代 LinuxCNC 状态和轨迹 golden。