Files
linuxcncWebWASM/docs/linuxcnc-web-wasm-full-port-implementation.md

259 lines
20 KiB
Markdown
Raw Permalink 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 完整仿真机床实施蓝图
**文档状态**:实施规范 v1.0
**适用基线**:LinuxCNC `2.10.0~pre0`,提交由 [`context-lock.json`](../context-lock.json) 冻结
**用途**:把“完整移植 LinuxCNC 仿真机床”分解为可实现、可回归、可恢复和可发布的工程闭环
**运动学主线**:[运动学完整移植计划](linuxcnc-web-wasm-kinematics-migration-plan.md)
**上位路线**:[技术路线](linuxcnc-web-wasm-technical-roadmap.md)
**覆盖依据**:[仿真全功能覆盖矩阵](linuxcnc-web-wasm-simulation-coverage.md)
**源程序依据**:[功能与源程序关联目录](linuxcnc-web-wasm-source-map.md)
**通信契约**:[前端与通信详细设计](linuxcnc-web-wasm-frontend-communication-design.md)
**执行总控**:[仿真移植总控执行手册](linuxcnc-web-wasm-integration-playbook.md)
本文件是实施层的补充规范。运动学实现族、正逆解契约和 K0-K8 里程碑以[运动学完整移植计划](linuxcnc-web-wasm-kinematics-migration-plan.md)为专项细则。它不替代来源清单、协议 schema 或覆盖矩阵;当这些文件发生冲突时,`context-lock.json`、协议 schema 和逐文件覆盖记录优先。本文件解决的是:一个 LinuxCNC 仿真入口如何启动,经过哪些运行时层,如何产生确定性结果,如何在浏览器中恢复,以及怎样证明“完整”而不是“演示可用”。
## 1. 完整移植的工程定义
完整移植必须同时闭合六条链。任何一条链缺失,都只能称为部分移植或演示版本:
| 闭合链 | 必须回答的问题 | 发布前证据 |
|---|---|---|
| 来源闭合 | LinuxCNC 文档、源码、符号、配置资源和测试是否全部登记? | `source-index`、`manifest`、依赖闭包、零孤儿报告 |
| 构建闭合 | 可移植代码、适配层、静态组件和 Python 运行时是否可重建? | 编译锁、构建日志、产物 SHA-256、许可证清单 |
| 启动闭合 | 任一纳入范围的 `configs/sim` 入口能否解析并进入 `READY` 或给出可诊断错误? | 配置资源图、启动日志、错误文件/行号 |
| 行为闭合 | 解释器、TP、运动、HAL、IO、PLC 和 Python 的可观察结果是否与原生 oracle 一致? | JSONL golden、首个 divergent tick 报告、容差说明 |
| 工作流闭合 | 编辑、MDI、运行、暂停、单步、急停、jog、home、工具、报警和恢复是否能从 UI 完成? | 浏览器 E2E、键盘/触摸、故障和恢复场景 |
| 交付闭合 | 离线、迁移、配额、崩溃恢复、回滚、兼容性和许可证是否可审计? | 发布清单、离线包、迁移/回滚演练、兼容报告 |
“全覆盖”只统计仿真可观察能力。真实硬件、硬实时内核和安全认证属于 `REAL-EXCLUDED`;但它们在仿真配置中产生的信号必须由数字孪生提供,不能以“不支持”跳过主轴、编码器、探针、限位、换刀、冷却或 IO 的仿真行为。
## 2. 当前起点和硬门禁
当前仓库的实际起点如下,不能用文档中的目标状态替代代码证据:
| 项目 | 当前状态 | 实施含义 |
|---|---|---|
| M0 来源/上下文 | 已生成,锁定 8,026 个来源条目和 context lock | 任何来源变化先重建并审查哈希 |
| M1 ABI 骨架 | 已有生命周期、固定 tick、快照和导入导出骨架 | 只能作为 ABI 起点,不代表 LinuxCNC 核心已接入 |
| posemath/解释器/运动切片 | 已有仓库内适配报告和原生测试证据 | 仍需接入统一 `sim_task`、HAL 和 Worker |
| kinematics 节点 | 旧 `M2-CORE-04` blocked,当前转入 K0 来源审计 | 锁定来源缺少声明的 `linuxcnc/unit_tests/kinematics/**`,不得静默改用其他目录 |
| Python、完整 HAL、UI、OPFS 恢复 | 尚未完成全量闭环 | 不得以 mock、刷新后重建或单一三轴页面宣称完成 |
每个实施任务开始前运行基线门禁:
```bash
python3 scripts/validate_baseline.py
python3 scripts/validate_download_index.py
python3 -m unittest discover -s scripts -p 'test_*.py' -v
python3 scripts/validate_task_pointer.py
```
若锁定提交、输入目录、schema 版本或生成器版本发生变化,先创建新的 `context_id`;不能在旧 context 中覆盖索引或降低校验条件。
## 3. 运行时分层和依赖规则
完整仿真运行时固定为单 Dedicated Worker 单写者模型。主线程只能通过版本化协议读取状态和提交命令,不能读取 WASM 指针、修改 HAL pin 或直接写 OPFS。
```text
UI / Three.js / 编辑器
│ protocol.v1
Worker: CommandRouter → TaskLedger → SimScheduler
│ ├─ SimTask / rs274ngc / canonical
│ ├─ TP / posemath / kinematics
│ ├─ SimMotion / homing / limits
│ ├─ SimHAL / components / ClassicLadder
│ ├─ SimIO / digital-twin devices
│ └─ PythonRuntime / remap / VCP
├─ SnapshotPublisher / EventBus / TraceRecorder
└─ WorkspaceRuntime → WasmFS/POSIX → OPFS journal/checkpoint
```
依赖必须单向流动:
1. 几何、解释器和运动核心不能依赖 DOM、OPFS、Python 或 UI。
2. HAL 组件只能通过注册的 pin/signal/parameter 接口访问运行时;组件不得调用宿主路径、网络或动态加载。
3. Python 只能访问白名单虚拟模块、`/workspace` 和虚拟时钟;超出能力返回稳定错误码。
4. UI 只能消费快照、事件和流;派生显示状态不得回写 authoritative state。
5. OPFS 只承接事务和恢复,不参与每个仿真 tick 的运动计算。
WASM 核心以独立 CMake/Emscripten 目标构建。LinuxCNC 原生 `meson.build` 继续用于 oracle 和原生测试,不作为 WASM 的直接构建入口。动态库、`fork/exec`、NML/CMS 进程边界和共享内存改为静态注册、进程内对象和版本化快照。
## 4. 一个仿真入口的启动闭环
所有 `configs/sim/**` 入口都必须经过相同的启动状态机;配置专属差异只能来自已登记能力和资源,不得在 UI 中写隐式特例。
```text
NEW
→ WORKSPACE_MOUNTED
→ INI_PARSED
→ RESOURCES_RESOLVED
→ CORE_CREATED
→ HAL_TWOPASS_DONE
→ DEVICES_BOUND
→ TASK_READY
→ RUNNING / PAUSED / ESTOP
→ STOPPED → DESTROYED
```
启动步骤及其产物:
1. `HANDSHAKE`:协商 `context_id`、ABI/protocol/snapshot 版本和能力注册表;版本不兼容直接拒绝。
2. `MOUNT`:将工程挂载到虚拟 `/workspace`,规范化大小写、路径和编码,拒绝 `..`、绝对宿主路径和符号链接。
3. `PARSE_INI`:解析 section、include、变量替换、轴/关节/主轴数量、周期和入口文件;错误必须包含文件、行和字段。
4. `RESOLVE`:构建 `.hal`、`.tbl`、`.var`、`.ngc`、`.py`、`.xml/.glade/.ui`、`.clp`、`.halscope/.halshow` 等资源图,报告缺失、循环和宿主依赖。
5. `CREATE_CORE`:按配置静态选择 kinematics、解释器、TP、motion、IO 和工具服务,记录模块版本与 source SHA。
6. `HAL_TWOPASS`:完成 `loadrt`、组件实例、`addf`、`setp`、`net`、`sets`、`link*` 的确定性顺序;任何未注册组件阻断启动。
7. `BIND_DEVICES`:绑定轴、编码器、主轴、探针、限位、回零、换刀、冷却和 PLC 数字孪生,并应用故障模型。
8. `START_TASK`:加载程序、参数、工具表和 Python/remap;运行一次零周期初始化,生成初始状态哈希。
9. `READY`:发布完整快照、能力和资源诊断。UI 只有收到 `READY` 才能开放运行按钮。
任一步骤失败时,必须撤销尚未提交的资源绑定并保留结构化诊断;不能留下半初始化会话,也不能通过“忽略失败文件”进入 `READY`。
## 5. 固定 tick 和状态更新顺序
`sim_step(cycles)` 是唯一改变仿真结果的入口。每个逻辑周期按下列顺序执行,顺序属于协议和 golden 的一部分:
1. 读取本周期已确认的命令,按 `seq` 和 `operation_id` 去重;未确认的命令不能重复产生副作用。
2. 执行 Task/解释器预算,产生 canonical 队列、当前行、模态和错误。
3. 运行 TP 前瞻、blend、速度/加速度/jerk 限制,生成本周期目标轨迹。
4. 调用 kinematics `inverse`,检查奇异姿态、轴/关节限制和 NaN/Inf。
5. 更新 motion、homing、软/硬限位、following error、急停和 jog 状态。
6. 按静态线程清单运行 HAL function、虚拟轴/编码器/主轴/探针/换刀和 ClassicLadder scan。
7. 处理 IO 完成、输入等待、超时、报警和 Python 回调;回调不能绕过当前 tick。
8. 生成事件、采样和状态哈希;先发布内存快照,再按 checkpoint/journal 策略持久化。
暂停只停止后续运动和解释,不丢弃已入队 canonical;急停清空或冻结哪些队列必须由状态机明确规定。快速模式、1x、单步、暂停恢复和回放在相同逻辑终点必须得到相同状态哈希;墙钟只用于节流和让出 Worker 事件循环。
## 6. 各域实施契约
### 6.1 配置、工作区和启动资源
实现顺序为“路径规则 → 文件 API → 解析器 → 资源图 → 启动器”。至少提供 `open/read/write/stat/readdir/rename/unlink/fsync`,并保留 LinuxCNC 相对路径和 include 语义。
| 输入类型 | 处理方式 | 最小验收 |
|---|---|---|
| `.ini/.inc` | 兼容 section、变量、include、相对路径和循环检测 | 逐文件行号诊断、重复加载结果一致 |
| `.hal/.comp` | 解析 twopass、实例、函数顺序和 pin 类型;组件构建期静态注册 | HAL graph 与 native 对齐 |
| `.tbl/.var` | 工具、pocket、offset、参数和 wear 的版本化服务 | 加载、编辑、提交、恢复一致 |
| `.ngc/.nc` | 主程序、子程序、O-word、M-code 文件和过滤器 | 相同入口解析到相同程序图 |
| `.py` | 受控 Python runtime;能力清单和资源限额 | 正常、异常、超时、禁止 API |
| `.xml/.glade/.ui` | 转换为 Web Component descriptor;保留 pin、属性、回调 | 样例面板可加载且权限可审计 |
| `.clp/.halscope/.halshow` | 解析 PLC、采样、触发和回放描述 | 逻辑/波形结果可回放 |
资源图必须记录 `input_sha`、解析器版本、依赖、输出能力和错误。配置加载不成功时,错误报告必须能反向定位到 manifest 条目。
### 6.2 解释器、Task、TP、motion 和 kinematics
移植以原生 oracle 为先:先让 native runner 输出解释器状态、canonical、每 tick 运动和错误,再接入 WASM。核心边界只允许使用固定宽度类型和稳定错误码。
实施顺序:
1. posemath 和数值策略:固定浮点编译选项、角度/单位换算、NaN/Inf 检查。
2. rs274ngc:词法/语法、模态、参数、O-code、循环、补偿、G/M/O 和错误位置。
3. canonical/TP:直线、圆弧、球面弧、样条、NURBS、前瞻、blend、exact stop 和同步运动。
4. Task/NML 兼容:命令、状态、错误、模式转换、MDI、单步、暂停、恢复、abort 和队列清空。
5. motion:轴/关节、jog、home、软硬限位、following error、外部偏置和故障注入。
6. kinematics:trivkins、gantry、CoreXY、5/6 轴、Delta、机器人、foam 等静态模块;每个模块提供 `forward/inverse/limits/capabilities`。
`M2-CORE-04` 当前因锁定输入缺少 `unit_tests/kinematics/**` 而 blocked。处理方式只有两种:补充并重新锁定确实存在的官方测试输入,或由契约变更明确批准替代 oracle 并登记差异;不能把 `tests/matrixkins/**` 直接伪装成原声明输入。
### 6.3 HAL、组件、PLC 和数字孪生
HAL 运行时必须先建立类型化对象图,再执行组件。组件来源按三类登记:可移植核心、构建期 `.comp` 组件、仿真专用数字孪生。每个组件 manifest 至少包含名称、版本、输入/输出 pin、参数、线程要求、依赖、权限、测试和状态。
固定调度顺序为:`base/servo`(如配置启用)→运动反馈→主轴/编码器→通用 HAL function→PLC scan→IO 完成→采样发布。缺少实时墙钟不改变这个逻辑顺序。
数字孪生必须能脚本化以下状态:主轴加减速/不到速/定向失败,编码器计数和丢脉冲,探针触发/未触发,限位/回零,换刀成功/超时/失败,冷却和润滑,模拟/数字 IO 延迟、抖动和故障。故障注入本身也是可回放输入,不能通过随机数产生不可重复结果。
ClassicLadder 需要覆盖工程加载、寄存器、触点/线圈、定时器、计数器、顺序控制、HAL 绑定、扫描周期、暂停/恢复和诊断 UI;真实 Modbus transport 排除,但虚拟寄存器、延迟和错误状态必须可配置。
### 6.4 Python、remap、VCP 和插件
Python runtime 在 Worker 内启动,固定 Pyodide 版本、包索引和 artifact hash。桥接模块只提供 `linuxcnc`、`hal`、`gstat`、INI、工具表、虚拟时间和工作区 API;`subprocess`、`socket`、宿主线程、设备文件和任意路径必须返回稳定的能力拒绝错误。
remap/filter/VCP 每次调用都记录入口、参数摘要、tick、返回值、异常、耗时和资源消耗。Python 异常必须回到解释器/Task 错误语义,不能只写 console。VCP descriptor 转换为带版本和权限的 Web Component schema,插件崩溃不得破坏核心会话。
### 6.5 协议、快照、事件和持久化
命令必须执行 `prepare → execute → verify → commit`;`operation_id` 幂等,回答丢失时先查账本。快照至少分为配置、解释器、Task、TP、motion、kinematics、HAL、IO、Python、工作区和恢复元数据,并带 `schema_version/context_id/simulation_tick/state_hash`。
OPFS 只保存已提交工程、journal、checkpoint、日志和可删除缓存。推荐恢复流程:读取最近有效 checkpoint → 校验哈希 → 按序重放已提交 journal → 对未确认操作逐项对账 → 发布 `RECOVERED` 或明确 `RECOVERY_BLOCKED`。不能以“重新加载 INI 并从头运行”替代状态恢复。
## 7. 原生 oracle 和全量回归
原生 runner 与 WASM runner 必须使用同一工程输入和同一命令序列。每个样本至少输出:
```json
{
"context_id": "...",
"entry_source_id": "...",
"test_id": "...",
"tick": 1234,
"interpreter": {"line": 12, "modal_hash": "...", "parameters_hash": "..."},
"canonical": [{"kind": "straight_feed", "target": [0, 0, 0]}],
"motion": {"axis": [], "joint": [], "following_error": []},
"hal": {"pins_hash": "...", "signals_hash": "..."},
"io": {"spindle": {}, "tool": {}, "probe": {}},
"events": [],
"state_hash": "..."
}
```
差异报告必须给出首个 divergent tick、字段路径、原生值、WASM 值、单位、容差、来源文件和 manifest ID。浮点容差只能用于数值字段,不能掩盖状态转换、错误码、事件顺序、当前行、工具号或 HAL 拓扑差异。
每个入口至少执行以下维度:正常运行、边界数值、语法/配置错误、暂停/单步/abort、急停/限位/设备故障、导出导入、Worker 重启/OPFS 恢复、离线启动;有 UI 或插件的入口再加键盘/触摸、权限和背压维度。
发布前必须满足:
- `source-index`、`manifest`、依赖闭包和配置资源图无孤儿,所有条目已分配 owner/feature/domain。
- 所有可计入条目为 `accepted`;不得残留 `planned`、`needs_owner`、无 oracle 或无测试维度条目。
- 同一输入在至少三次运行、1x/快速/单步/恢复路径下的最终 `state_hash` 一致。
- 所有事件按 `seq/tick` 单调发布,重放不重复产生已提交副作用。
- 配额不足、schema 不兼容、缺失资源、禁止宿主能力和恢复无法对账均有明确错误,不静默降级。
- 浏览器矩阵、性能、内存、离线、许可证、迁移和回滚报告齐全;性能优化不能改变 golden 结果。
## 8. 分阶段实施和产物
| 阶段 | 实施重点 | 必须产物 | 退出条件 |
|---|---|---|---|
| M0 基线闭合 | 来源、符号、配置、测试、依赖和 context lock | 索引、manifest、oracle schema、影响图 | 零孤儿、哈希可重建、缺字段会失败 |
| M1 会话闭环 | C ABI、Worker 队列、虚拟工作区、固定 tick、最小恢复 | WASM/原生 ABI 测试、协议 schema、journal/checkpoint | create/load/step/snapshot/export/import/recover 通过 |
| M2 核心行为 | 解释器、canonical、TP、Task、motion、kinematics、工具服务 | 每个源组的适配报告、golden、差异报告 | 受影响核心条目 `verified`;blocked 输入已处理 |
| M3 仿真系统 | HAL、`.comp`、ClassicLadder、IO、数字孪生、Python/remap | 组件 manifest、HAL graph、故障矩阵、Python 能力报告 | 配置启动和 S1-S6 场景通过 |
| M4 Web 工作流 | 工程、编辑器、MDI、控制台、HAL/PLC、VCP、Three.js、导入导出 | 浏览器 E2E、截图/像素、性能和恢复证据 | 所有真实 Worker 工作流通过,mock 退出 |
| M5 发布闭合 | 全量 configs/tests/unit_tests、浏览器、离线、迁移、许可证 | 兼容报告、排除项、离线包、回滚包 | G0-G5 全绿,所有计入条目 `accepted` |
每个阶段都按“输入冻结 → 失败测试 → 最小实现 → native/WASM 对照 → 故障/恢复 → 文档和 manifest → 门禁”执行。阶段间不得用“先做 UI、后补核心”绕过依赖。
## 9. 实施任务的最小交付包
每个子任务只能有一个主要结果,并在提交中同时更新:
1. 受影响 source/feature/task/test/oracle 的 ID 和 source SHA。
2. C ABI、协议、快照或工作区 schema 的版本和兼容策略(若有变化)。
3. 正常、边界、错误、恢复和离线测试命令及结果路径。
4. native/WASM 差异报告、性能/内存/配额影响和已知限制。
5. 回滚点、未完成输入、下一任务和 `nextTask.md` 指针。
示例任务标题应具体到单一结果,例如:`M3-RT-03b:限位与换刀故障注入可回放`,而不是“完善仿真设备”。任务若同时修改两个独立契约,先拆成契约任务和实现任务。
## 10. 停止线和风险处置
- 来源或测试输入缺失:停止并记录 `blocked`,由新的契约变更决定是否补充输入;不删样本、不改目录级状态。
- 原生/WASM 首个差异未定位:冻结受影响源组和 golden,不进入下一层集成。
- 协议/ABI/schema 破坏性变化:新建版本、迁移和双向测试;旧版本不能静默接受。
- Python、组件或 VCP 需要宿主能力:改为虚拟能力或明确 `REAL-EXCLUDED`,并测试拒绝路径。
- OPFS journal/checkpoint 损坏、配额不足或确认丢失:保留最近已提交版本,进入恢复或导出,不覆盖旧数据。
- 非确定性、随机故障或浏览器墙钟改变结果:禁止合并,固定种子、顺序、tick 和输入后重跑。
- 性能优化导致 golden 变化:按行为回归处理,不能以“更快”豁免差异。
## 11. 当前立即执行顺序
1. 按 `nextTask.md` 执行 `M3-CFG-01`,把 264 个仿真配置入口和 16,093 条资源边从静态索引推进为确定性启动解析;保留 missing/unresolved/host path,禁止静默同名替换。
2. 完成 `M3-RT-02`,将 HAL pin/signal/thread/function、`.comp` 元数据和配置图接入可执行 preflight;运动学只作为已完成的注册/调用依赖,不再重复解析。
3. 完成 `M3-RT-03`,把 spindle/tool/probe/limit/estop 数字孪生接入 Worker command path,并让故障注入可录制、回放和恢复。
4. 完成 `M3-FS-01` 与 `M3-PY-01`,将 workspace recovery/migration 和实际 Pyodide 虚拟 API 接入真实 session;保持现有 schema 和宿主能力拒绝契约。
5. 完成 `FE-M1` 最小真实前端垂直切片,再运行 `REG-01/03/04` 的配置、恢复、浏览器和性能矩阵;K8 release owner blocker 只在真实 owner 输入或新 lock context 到位后单独刷新。
本文件完成的是实施规则和证据闭环,不改变当前任务指针,也不把尚未实现的 LinuxCNC 功能标为完成。