# 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` 执行 `K0-INV-01`,处理旧 `M2-CORE-04` 的 kinematics 输入阻塞,先确认锁定提交中不存在声明路径,并保留报告。 2. 若要采用替代 oracle,先创建可审查的契约变更和新的 context lock;否则保持 blocked,不推进 HAL 节点。 3. 在 kinematics 契约闭合后,完成静态模块注册、`forward/inverse/limits` ABI、确定性状态哈希和失败路径测试。 4. 继续 `M2-CORE-05` 前,先让 native runner、WASM runner 和 `sim_step` 共享同一 tick/golden schema。 5. 只有 M2 核心条目达到 `verified`,才进入 HAL/数字孪生/OPFS 恢复;只有 M3 通过,才把 mock UI 替换成真实 Worker 工作流。 本文件完成的是实施规则和证据闭环,不改变当前任务指针,也不把尚未实现的 LinuxCNC 功能标为完成。