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

20 KiB
Raw Blame History

LinuxCNC Web/WASM 完整仿真机床实施蓝图

文档状态:实施规范 v1.0 适用基线:LinuxCNC 2.10.0~pre0,提交由 context-lock.json 冻结 用途:把“完整移植 LinuxCNC 仿真机床”分解为可实现、可回归、可恢复和可发布的工程闭环 运动学主线:运动学完整移植计划 上位路线:技术路线 覆盖依据:仿真全功能覆盖矩阵 源程序依据:功能与源程序关联目录 通信契约:前端与通信详细设计 执行总控:仿真移植总控执行手册

本文件是实施层的补充规范。运动学实现族、正逆解契约和 K0-K8 里程碑以运动学完整移植计划为专项细则。它不替代来源清单、协议 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、刷新后重建或单一三轴页面宣称完成

每个实施任务开始前运行基线门禁:

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。

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 中写隐式特例。

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 必须使用同一工程输入和同一命令序列。每个样本至少输出:

{
  "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 功能标为完成。