Initial LinuxCNC WebAssembly workspace snapshot
This commit is contained in:
452
docs/linuxcnc-web-wasm-frontend-communication-design.md
Normal file
452
docs/linuxcnc-web-wasm-frontend-communication-design.md
Normal file
@@ -0,0 +1,452 @@
|
||||
# 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。
|
||||
Reference in New Issue
Block a user