32 KiB
LinuxCNC 纯 Web/WASM 仿真机床技术路线(第一版)
文档状态:Draft v0.2
目标产品:浏览器内、离线可运行的 LinuxCNC 仿真机床
代码基线:本仓库当前 LinuxCNC 2.10.0~pre0(以 linuxcnc/VERSION 为准)
范围:只做仿真,不连接真实机床,不通过网络控制硬件
兼容目标:以 LinuxCNC 仿真模式为行为基线,覆盖全部仿真功能并给出浏览器内等价实现
执行治理:项目执行与交付策略
逐项验收:仿真全功能覆盖矩阵
源码关联:仿真功能与源程序关联目录
前端与通信:前端与 LinuxCNC 通信详细设计
总控执行:仿真移植总控执行手册
本路线定义架构、范围、风险和阶段 A-E;任务上下文预算、提交格式、断网接续、里程碑退出条件和发布门禁统一由项目执行与交付策略约束;功能到 LinuxCNC 实际源文件、配置和测试的关联以源程序关联目录为入口;浏览器 UI、Worker 协议、状态契约、前端任务和 FE-M0..M5 里程碑以前端与通信详细设计为准;端到端依赖、任务目录、场景验收和当前实施状态以总控执行手册为准。
1. 摘要
本项目不是把完整 LinuxCNC 的 Linux 进程、实时内核和桌面 GUI 原样塞进浏览器,而是将 LinuxCNC 面向仿真用户的全部行为拆成可移植核心,并为浏览器补齐进程、文件系统、时钟、HAL、虚拟 I/O、GUI、Python 和配置运行时。真实机床、真实硬件驱动和安全控制链路全部排除;原本由硬件产生的可观察信号,在浏览器中由可配置的数字孪生设备产生。
最终形态如下:
Web UI(React + TypeScript)
├─ G-code 编辑器、MDI、Jog、倍率、报警、配置
├─ Three.js 机床/刀具/轨迹/毛坯显示
└─ OPFS 工程、配置、工具表、日志和快照
│ postMessage
▼
Dedicated Web Worker
├─ Emscripten WASM LinuxCNC 仿真核心
├─ Pyodide/Python 仿真运行时(remap、过滤器、VCP 脚本)
├─ POSIX 兼容工作区(WasmFS/适配层)
├─ 虚拟时钟、任务/运动/I/O 调度
├─ 虚拟 HAL 和全部仿真组件
└─ OPFS 同步访问、事务日志和恢复
浏览器主线程只负责界面和渲染;解释、轨迹计算、配置读取、HAL 周期、OPFS 同步访问和 Python 调用调度都在 Worker 侧完成。仿真默认单线程、串行、确定性;Python remap/过滤器可以在同一仿真 Worker 的协作运行时中执行,也可以拆成受控 Python Worker,但不能引入不可复现的后台线程。WASM pthread 只作为性能优化构建,不能改变仿真语义。
2. 目标与边界
2.1 必须达到的目标
- 在 Chromium、Firefox、Safari 的支持范围内运行纯仿真;主流程不依赖 Linux、Node.js 或服务器 API。
- 能加载 LinuxCNC 仿真配置、G-code、子程序、宏、工具表、INI 和 HAL 配置,并在浏览器内得到明确的兼容结果。
- 对 LinuxCNC 的全部仿真功能做可重复回归:解释器、轨迹规划、全部内置运动学、模态状态、坐标系、补偿、宏、remap、HAL、ClassicLadder、虚拟工具/主轴/探针/换刀/限位、车床/等离子/激光/泡沫/机器人等仿真配置,以及所有仿真操作界面能力。
- 使用 OPFS 持久化工程工作区,支持关闭页面后恢复、崩溃恢复、版本迁移、导入导出和配额诊断。
- 真实硬件功能明确排除;除真实硬件访问外,不允许以“不支持”跳过任何 LinuxCNC 仿真功能。行为有差异的功能必须有机器可读的兼容状态和用户可见的差异说明。
- 通过录制/回放的确定性仿真,能够对照原生 LinuxCNC 生成差异报告。
2.2 明确不做的事情
- 不连接串口、USB、PCI、并口、Mesa、Ethernet、GPIO 或任何真实硬件。
- 不提供真实机床安全保证,不替代急停、限位、安全继电器或认证控制器。
- 不执行任意
fork、exec、shell 命令、系统脚本或宿主机路径操作。 - 不连接真实机床或真实硬件;硬件相关 LinuxCNC 功能只实现仿真数字孪生和故障模型。
- Python 解释器属于仿真必选运行时;使用 Worker 内的 Pyodide/CPython WASM,并以虚拟文件、虚拟时钟和虚拟 I/O 替代宿主能力。
- 不承诺浏览器的墙钟、Worker 调度或后台标签页具有 Linux PREEMPT_RT 的实时保证。
2.3 “仿真全覆盖”的定义
“仿真全覆盖”不是承诺每一行 LinuxCNC 源码都可直接编译,而是对 LinuxCNC 发布版中所有可由仿真机床观察、配置、调用或回放的行为逐项建立以下状态:
- 复用:原生 C/C++ 核心可在 WASM 编译,行为直接复用。
- 等价实现:Linux/进程/硬件依赖不可复用,但在浏览器内提供同等仿真语义。
- 兼容适配:保留配置或 API 形状,内部转换为 Web 运行时模型。
- 仿真器:用通用虚拟机床部件、虚拟 I/O 或虚拟时钟复现仿真配置依赖的行为,不复刻真实设备驱动和现场协议。
- Web 等价:用 Web UI、Web Worker、WebGL/WebGPU 或浏览器 API 提供同等用户能力。
- 排除:真实硬件访问、真实实时内核、安全认证和物理机床输出;排除项不计入仿真覆盖率,但必须在兼容报告中明确标注。
每个功能在 仿真全功能覆盖矩阵 中记录:来源文档/源码、输入、输出、错误码、仿真替代方案、参考测试、当前状态和差异说明。覆盖率只统计 SIM-*、WEB-EQUIV 和 PY-WASM 项,目标是 100%;REAL-EXCLUDED 单独统计,不得伪装为已支持。
3. 基于当前仓库的复用分析
当前仓库的 linuxcnc/configs/sim 已提供大量无特殊硬件的仿真配置,是移植和回归的首批样本。仓库中的核心目录与路线映射如下:
本节只给出架构级目录映射;逐文件范围、功能入口、配置资源和测试来源见仿真功能与源程序关联目录。manifest 生成器必须把目录和通配范围展开为带 source_sha 的具体文件,禁止用目录级状态替代文件级证据。
| 当前源码 | Web/WASM 处理 | 说明 |
|---|---|---|
src/emc/rs274ngc |
优先原样编译并做 ABI 封装 | G-code 解释、宏、循环、模态和 canonical 输出 |
src/emc/tp |
原样复用,接入确定性周期 | 前瞻、速度规划、圆弧、球面弧和轨迹队列 |
src/libnml/posemath |
原样复用 | 位姿、矩阵、坐标变换和几何计算 |
src/emc/kinematics |
原样复用/按配置选择 | trivkins、5 轴、机器人、六足等运动学 |
src/libnml/inifile |
编译或等价解析 | 兼容 INI;前端另提供 JSON 配置视图 |
src/emc/motion |
迁移到 sim_motion |
保留状态机和运动语义,去除设备/共享内存边界 |
src/emc/task |
拆分为单进程 sim_task |
删除 fork、信号、NML 服务和外部命令 |
src/emc/iotask |
迁移为虚拟 I/O 任务 | 主轴、冷却、换刀、探针、限位和消息 |
src/hal |
迁移 HAL 数据模型和调度 | pin、signal、param、thread、function、组件生命周期 |
src/hal/components |
全量移植/重写 | 全部核心组件、仿真组件、测试组件和用户组件模板进入组件 manifest |
src/hal/drivers |
不移植 | 真实设备驱动整体排除;仿真配置所需行为由 sim_* 和通用虚拟机床组件提供 |
src/emc/usr_intf/* |
全部重写为 Web UI | 不移植 GTK、Qt、Tk、OpenGL 桌面窗口 |
| Python plugin/remap | Pyodide/CPython WASM + 虚拟宿主 API | 兼容解释器、remap、过滤器、VCP 脚本和 Python HAL API;禁止宿主进程/线程/设备访问 |
当前 meson.build 依赖 Boost.Python、Python 2 和 libdl 等宿主环境;WASM 工程使用独立 CMake/Emscripten 目标,避免破坏原生构建。
Python 运行时采用 Pyodide/CPython-WASM,放在 Worker 中执行 remap、过滤器、VCP 回调、工具数据库脚本和 Python HAL API;LinuxCNC 的 linuxcnc、hal、gstat 等模块由仿真桥提供。Pyodide 支持在 Web Worker 中运行,但浏览器环境不提供可直接复用的宿主子进程、线程、原始套接字和设备访问,因此这些调用必须映射为虚拟文件、虚拟时钟、虚拟事件或明确的 real_hardware_excluded 错误。Pyodide Web Worker、Pyodide WASM constraints
4. 总体运行时架构
4.1 Worker 分层
sim.worker.ts
├─ CommandRouter 命令序列、取消、错误和权限边界
├─ TaskLedger operation_id、步骤账本、幂等重试和回答/工具失败接续
├─ WorkspaceRuntime OPFS、工作区挂载、日志和恢复
├─ WasmRuntime Emscripten 模块生命周期和 C ABI
├─ LinuxCncCompat INI/HAL/NML/任务/IO 兼容层
├─ SimScheduler 固定步长、倍速、单步、暂停和回放
└─ SnapshotPublisher 二进制状态快照和事件流
主线程通过结构化消息发送低频控制命令;状态使用固定二进制 schema 或可选 JSON 调试模式。所有命令有单调递增 seq 和稳定 operation_id,所有快照有 simulation_tick 和 schema_version。任务账本在执行前写 prepare、验证后写 commit;回答或工具确认丢失时先对账,不盲目重放。
通信信封、握手/能力协商、命令族、快照字段、事件与高频流、背压、错误/取消、租约、离线接续和 UI 工作流的详细契约见前端与 LinuxCNC 通信详细设计。
4.2 WASM 边界
WASM 核心只暴露稳定 C ABI,不把 C++ 类布局、指针或 STL 容器暴露给 TypeScript:
int sim_create(const uint8_t *config, uint32_t config_len);
int sim_load_workspace(const char *root_path);
int sim_command(const uint8_t *cmd, uint32_t cmd_len);
int sim_step(uint32_t cycles);
uint32_t sim_read_snapshot(uint8_t *out, uint32_t capacity);
uint32_t sim_read_event(uint8_t *out, uint32_t capacity);
int sim_export_state(uint8_t *out, uint32_t capacity);
int sim_import_state(const uint8_t *data, uint32_t len);
void sim_destroy(void);
建议使用命令和事件的版本化二进制结构;JSON 只作为调试、日志和外部工程文件格式。
4.3 仿真时钟
LinuxCNC 的实时周期在浏览器中改写为确定性仿真时钟:
- 逻辑周期默认 1 ms,允许配置为 0.25/0.5/1/2/5 ms。
sim_step(n)批量推进 n 个周期,不读取墙钟决定运动结果。- Worker 每次运行有限批次,例如 5~20 ms 逻辑时间,然后发布快照并让出事件循环。
1x模式按墙钟节流,快速模式只按计算能力推进,单步模式一次推进一个周期或一个解释块。- 快照频率与仿真周期解耦,默认 20~60 Hz;轨迹点可按采样策略压缩。
- 暂停、复位、急停、断点和回放都基于 tick,不依赖
setInterval(1)的准确性。
5. OPFS 工程持久化方案
5.1 选择原则
OPFS 是按源隔离、浏览器管理、默认不向用户可见的持久化文件系统;需要安全上下文(HTTPS),并可在 Worker 使用。其同步访问句柄只能用于 Worker,适合承接 LinuxCNC 大量同步 fopen/read/write 语义。MDN OPFS
OPFS 受浏览器配额和存储淘汰策略约束,因此不能假设无限磁盘;应用启动时调用 navigator.storage.estimate(),重要工程可请求 navigator.storage.persist(),并提供显式导出为 ZIP/目录的备份能力。MDN Storage Quotas
5.2 目录布局
opfs:/linuxcnc-web/
format.json # 工作区格式版本和应用版本
projects/
<project-id>/
project.json # 工程元数据、机床类型、单位、入口 INI
config/
*.ini *.hal *.tbl *.var *.ngc
subroutines/ # O-word、M-code 和用户宏
components/ # 允许的静态 HAL 组件配置
programs/ # 用户 G-code
snapshots/ # 可恢复仿真状态
logs/ # 事件、报警、轨迹差异和崩溃日志
journal/ # 写前日志、提交标记和恢复信息
cache/ # 可删除的预解析/轨迹缓存
samples/ # 随应用发布的 configs/sim 样本
migrations/ # 格式迁移标记
路径必须是 POSIX 风格、大小写敏感且经过规范化;禁止 ..、绝对宿主路径和符号链接语义。LinuxCNC 看到的是 /workspace 下的虚拟工作区,而不是浏览器真实路径。
5.3 两层文件系统模型
为了兼容同步 C/C++ 文件访问,不让 LinuxCNC 核心直接调用异步 DOM API,采用两层模型:
WASM 进程内工作树(同步、低延迟)
└─ WasmFS/MEMFS 或定制 POSIX 适配层
▲ 读写同步 / 批量刷新
▼
Worker OPFS 适配器(FileSystemSyncAccessHandle)
└─ OPFS 持久文件、journal、快照、配额和恢复
实现路线分两步:
- 第一阶段:使用 Emscripten WasmFS/虚拟文件系统,Worker 启动时加载工作区索引和热点文件;保存时通过 OPFS 适配器批量写入。WasmFS 是稳定但仍非全部旧 FS API 等价的方案,需要针对 LinuxCNC 使用到的文件调用做测试。Emscripten File System API
- 第二阶段:为
open/read/write/stat/readdir/rename/unlink/fsync建立最小 POSIX 适配层,普通文件通过 OPFS 同步句柄按块读写,目录和元数据通过索引维护;不把 OPFS 文件句柄暴露给 WASM。
OPFS 的 createSyncAccessHandle() 本身是异步获取,获取完成后 read/write/truncate/flush/close 可同步调用;因此所有句柄建立、恢复和关闭都由 Worker 的 WorkspaceRuntime 串行管理。MDN OPFS Worker Access
5.4 事务、一致性和恢复
每次工程写入按以下顺序执行:
- 生成
journal/<seq>.prepare,记录目标路径、旧版本、内容哈希和新版本。 - 写入临时文件
.<name>.tmp.<seq>,分块写入并flush()。 - 校验长度和 SHA-256,写入
journal/<seq>.commit。 - 原子替换目标文件,更新
project.json的索引版本。 - 删除临时文件和已完成日志。
启动恢复时扫描 prepare/commit:只有 commit 且校验通过的事务才生效;未完成事务回滚到旧版本。快照使用“完整基线 + 增量事件”策略,定期合并,避免每个 tick 写 OPFS。
5.5 数据策略
| 数据 | 存储 | 写入策略 |
|---|---|---|
| 当前工程配置 | OPFS | 显式保存 + 防抖自动保存 |
| G-code/宏/子程序 | OPFS | 编辑器保存时事务提交 |
| 工具表/参数/变量 | OPFS | 运行关键点和退出前提交 |
| 仿真快照 | OPFS | 用户手动/周期性 checkpoint |
| 事件和报警日志 | OPFS | 批量追加,达到大小后滚动 |
| 轨迹缓存 | OPFS cache/ |
可删除,哈希命中才复用 |
| UI 偏好 | IndexedDB 或 OPFS 小文件 | 与工程数据分离 |
用户可见文件使用浏览器 File System Access API 导入/导出;OPFS 本身不承诺与用户文件系统一一对应,不能把 OPFS 路径显示成真实磁盘路径。
6. LinuxCNC 全功能对照路线
以下是功能域摘要;完整条目、代码清单、组件清单、配置样本和测试要求以仿真全功能覆盖矩阵为准。每一项都必须进入自动化兼容矩阵;“等价”表示在纯仿真语义下达到相同用户可见结果。开发阶段 P0/P1/P2 只表示交付顺序,不表示可以永久缺失功能。
本节表格中的“全覆盖”是目标范围标签,不是当前实现状态。只有覆盖矩阵完成来源、依赖、测试维度、交互路径和原生/WASM oracle 闭包,并达到 accepted,才可在兼容报告中声明该功能已完成。
6.1 启动、配置和文件
| LinuxCNC 能力 | Web/WASM 路线 | 初始状态 |
|---|---|---|
| INI 文件、include、变量替换 | 虚拟工作区解析;路径限制和错误码保持一致 | P0 |
HAL 文件、loadrt/addf/setp/net |
解析器 + 静态组件注册 + pin/signal 图构建 | P0 |
工具表 .tbl |
兼容解析、编辑、校验和持久化 | P0 |
| 参数/变量文件 | 进程内参数区 + OPFS 提交 | P0 |
| G-code 主程序/子程序 | 工作区相对路径解析、递归深度限制 | P0 |
| M-code 文件 | 白名单虚拟动作;禁止外部程序 | P0 |
| 配置选择器 | Web 工程选择器和 configs/sim 样本导入 |
P0 |
| 环境变量、宿主路径、权限 | 映射为受控 workspace 变量;不暴露宿主环境 | P1 |
| LinuxCNC 启动/退出生命周期 | sim_create/load/start/stop/destroy 状态机 |
P0 |
6.2 G-code 解释器和程序执行
| LinuxCNC 能力 | Web/WASM 路线 | 初始状态 |
|---|---|---|
| RS-274 基础 G/M 代码 | 复用 rs274ngc,canonical 输出接 sim_task |
P0 |
| O-word、宏、命名参数、表达式 | 原生核心优先;禁止未审计宿主回调 | P0 |
| 子程序、循环、条件、递归 | 虚拟工作区路径和资源限额 | P0 |
| 模态组、单位、平面、绝对/增量 | 对照原生状态快照 | P0 |
| G54-G59、G52、G92、旋转/缩放 | 复用解释器和 posemath,逐项差分测试 | P0 |
| 刀具长度/半径补偿 | 复用解释器/TP,覆盖 mill、lathe、5 轴和数字孪生设备 | 全覆盖 |
| 固定循环、螺纹、同步运动 | 按 canonical 输出逐项验收 | 全覆盖 |
| Remap | C++ ABI + Pyodide Python 运行时,覆盖 prolog/epilog、NGC、Python 和队列语义 | 全覆盖 |
| MDI、单步、断点、队列清空 | Worker 命令状态机 | P0 |
| 程序暂停/恢复/中止 | tick 和解释器状态快照恢复 | P0 |
| 语法错误、运行时错误、报警 | 统一错误码 + 行号 + 来源文件 | P0 |
6.3 轨迹规划、运动学和坐标
| LinuxCNC 能力 | Web/WASM 路线 | 初始状态 |
|---|---|---|
tp 前瞻、速度/加速度、拐角融合 |
原样复用,固定 dt 驱动 | P0 |
| 直线、圆弧、球面弧、NURBS | 复用 TP/几何库,轨迹容差对表 | 全覆盖 |
| 关节/笛卡尔坐标 | sim_motion 状态模型 |
P0 |
| trivkins | 默认机床 | P0 |
| 全部内置及示例 kinematics | 编译为静态模块,由配置选择并建立姿态/限位 golden | 全覆盖 |
| 用户运动学模块 | 构建期静态 WASM ABI;以 manifest 注册,不允许宿主动态库 | 全覆盖 |
| 软限位、硬限位、回零 | 虚拟开关和确定性限位模型 | P0 |
| 外部偏置、跟随误差、螺距补偿 | 进程内仿真参数和测试向量 | 全覆盖 |
| 反向时运动学/姿态限制 | 与原生 kinematics 对照 | 全覆盖 |
6.4 HAL 和仿真 I/O
| LinuxCNC 能力 | Web/WASM 路线 | 初始状态 |
|---|---|---|
| pin、signal、parameter、component | 内存图模型,名称和类型保持兼容 | P0 |
| HAL thread/function/addf | 固定周期调度器中的有序函数列表 | P0 |
| 全部逻辑、数学、滤波、PID、计数器和测试组件 | .comp 转静态 WASM 组件并由 manifest 验收 |
全覆盖 |
sim_axis_hardware、sim_spindle、sim_encoder |
直接作为虚拟设备模型 | P0 |
| 探针、限位、回零、换刀 | 可脚本化虚拟 I/O,状态可录制 | P0 |
| 主轴加减速、定向、齿轮/同步 | 虚拟主轴模型 + TP 事件 + 故障注入 | 全覆盖 |
| ClassicLadder | 完整解析/执行;UI 用 Web 梯形图视图 | 全覆盖 |
用户 .comp |
构建期组件 ABI 和 manifest;无宿主动态库 | 全覆盖 |
| 真实 HAL 驱动 | 不编译、不模拟具体设备协议;仅识别配置并返回 real_hardware_excluded |
排除 |
| HAL scope/示波器 | Worker 采样环 + Web 图表 + 触发/回放 | 全覆盖 |
6.5 Task、IO、状态和消息
| LinuxCNC 能力 | Web/WASM 路线 | 初始状态 |
|---|---|---|
| emctask 状态机 | sim_task 单进程状态机 |
P0 |
| emcIO/tooldata | 内存服务 + OPFS 工具表 | P0 |
| NML command/status/error | Worker 内命令/状态/事件总线;保留消息语义 | P0 |
| 共享内存 | 进程内结构体;UI 使用快照,不暴露指针 | P0 |
| 信号、日志、报警 | 事件 schema + OPFS 滚动日志 | P0 |
fork/exec/system |
虚拟 M-code 白名单 | 不适用/受限 |
| 退出、复位、急停 | 明确定义状态转换和恢复点 | P0 |
6.6 用户界面和可视化
| LinuxCNC 能力 | Web/WASM 路线 | 初始状态 |
|---|---|---|
| AXIS | Web 主界面:程序、状态、预览、控制和报警 | P0 |
| Gmoccapy/Touchy | 响应式触控布局和可配置面板 | 全覆盖 |
| QtVCP/GladeVCP/PyVCP | Web Component + JSON schema + Python 回调桥;不解析桌面窗口 | 全覆盖 |
| Gremlin/路径预览 | Three.js 轨迹和坐标可视化 | P0 |
| 机床模型/刀具/毛坯 | glTF + Three.js/WebGPU;切削层独立于运动核心 | 全覆盖 |
| HALShow/HALScope | Web pin 图和波形查看器 | 全覆盖 |
| 仿真配置编辑器 | Web 编辑器生成仿真 JSON/INI/HAL;Stepconf/Pncconf 的真实硬件配置部分排除 | 全覆盖 |
| 国际化、主题、快捷键 | TypeScript UI 层实现 | 全覆盖 |
6.7 宿主和实时相关能力
| LinuxCNC 能力 | Web/WASM 路线 | 初始状态 |
|---|---|---|
| PREEMPT_RT/RTAI/Xenomai | 不移植;用确定性逻辑时钟代替 | 不适用 |
| Linux pthread/优先级/CPU 绑定 | 用确定性 Worker 调度等价;可选 pthread 只做性能优化 | 仿真等价 |
mmap、PCI、ioctl、/proc |
虚拟内存/诊断对象;不访问宿主 | 不适用 |
动态库 dlopen |
构建期静态链接或组件注册表 | 受限 |
| gettext/locale | 编译资源 + Web i18n;数值解析保持 LinuxCNC 规则 | 全覆盖 |
| OpenGL 桌面上下文 | Three.js WebGL/WebGPU | 等价实现 |
7. 代码组织和构建
建议不改动 LinuxCNC 原生构建,在仓库根增加独立目标:
wasm-core/
include/sim_api.h
src/core/sim_engine.cc
src/core/sim_task.cc
src/core/sim_motion.cc
src/core/sim_scheduler.cc
src/core/sim_hal.cc
src/core/sim_io.cc
src/core/sim_nml.cc
src/core/sim_fs.cc
src/compat/ # 对 LinuxCNC 源码的最小补丁/适配
CMakeLists.txt
web/
src/protocol/ # 版本化命令、事件、快照、错误和能力 schema
src/client/WorkerClient.ts # 握手、序列、超时、取消、重试和对账
src/workers/sim.worker.ts
src/workers/ProtocolGateway.ts
src/workers/TaskLedger.ts
src/workers/SnapshotPublisher.ts
src/engine/WasmEngine.ts # C ABI 内存所有权和错误映射
src/state/ # session/program/motion/HAL/IO/recovery stores
src/workspace/OpfsWorkspace.ts
src/viewer/ # Three.js 机床、轨迹、坐标和毛坯
src/ui/ # 控制台、程序、HAL、PLC、工具和配置工作流
tests/
golden/ # 原生 LinuxCNC 参考快照
wasm/ # WASM 结果
protocol/ # C/TS round-trip、schema、幂等和背压
browser/ # Playwright + OPFS + 离线/恢复/视觉场景
scripts/
build-wasm.sh
generate-compat-matrix.sh
推荐 Emscripten/CMake 配置:
emcmake cmake -S wasm-core -B build-wasm -DCMAKE_BUILD_TYPE=Release
cmake --build build-wasm
模块输出使用 MODULARIZE=1、EXPORT_ES6=1、ENVIRONMENT=web,worker。OPFS 同步句柄位于专用 Worker,因此不能依赖 PROXY_TO_WORKER 这类把入口转发到不兼容文件系统路径的模式;OPFS/WasmFS 组合要单独做浏览器测试。WasmFS 在 Emscripten 中仍不是旧 JS FS 的完全替代,必须固定版本并锁定实际使用的 API。
第一版默认单线程构建;若启用 pthread/共享 WASM 内存,需要 HTTPS、Cross-Origin-Opener-Policy: same-origin 和 Cross-Origin-Embedder-Policy: require-corp/credentialless。OPFS 本身不要求线程,但共享内存和高精度计时器需要跨源隔离。Emscripten Pthreads、MDN SharedArrayBuffer
8. 测试和一致性验证
8.1 参考基线
对同一个工程和 G-code,原生 LinuxCNC 与 WASM 分别输出:
- 解释器状态和当前行
- canonical motion 命令
- 每周期位置、速度、加速度和跟随误差
- 模态、坐标系、刀具、参数和变量
- HAL pin/signal/parameter 快照
- 报警、错误、消息和退出状态
测试结果保存为带版本的 JSONL/二进制 golden 文件,差异报告按功能域和首个 divergent tick 定位。
8.2 测试层级
- 原生核心单测:复用
unit_tests/interp、unit_tests/tp,补充边界、NaN、单位和浮点容差。 - WASM ABI 测试:创建/加载/命令/周期/快照/快照恢复/错误码。
- 功能对表测试:覆盖
configs/sim中的仿真 INI/HAL/G-code 样本,逐项登记兼容状态。 - 确定性测试:相同输入运行 1x、快速、暂停恢复、单步后结果一致。
- OPFS 测试:首次创建、断电/异常中止模拟、事务恢复、配额不足、版本迁移、并发打开和句柄关闭。
- 浏览器 E2E:Playwright 测试 Worker、刷新恢复、离线 PWA、导入导出和 WebGL 画面。
- 性能测试:程序行数、HAL 组件数量、周期耗时、Worker 延迟、快照吞吐和 OPFS 写入。
- 任务接续测试:请求超时、回答传输中断、工具结果丢失、重复响应、Worker 重启和任务账本恢复;验证已确认步骤不重复、未确认步骤不跳过。
8.3 验收门槛
- 交付过程必须遵循项目执行与交付策略的 context lock、固定大小上下文包、断网恢复和 G0-G5 门禁;门禁证据作为本路线验收附件。
- 仿真全功能覆盖矩阵 中所有
SIM-*、WEB-EQUIV、PY-WASM条目均有实现、自动测试和差异报告,覆盖率 100%。 - 来源扫描与 manifest 闭包无孤儿项;每个条目覆盖正常、边界、错误、暂停/恢复、持久化/离线和适用交互维度。
- 每个仿真配置、导出符号、HAL 组件、G/M/O/Python/API 和 Web 工作流均能追溯到实现、测试和 oracle;未登记或无法对照的功能阻止发布。
- 原生/WASM 终点和轨迹误差在定义的机床单位容差内,默认目标
1e-6,特殊算法单独声明。 - 强制刷新后最近一次已提交工程和快照可恢复;未提交事务不产生半文件。
- OPFS 配额不足时不丢失已提交版本,并显示可操作错误。
- 请求或工具失败后,任务可从最近已验证步骤继续;同一
operation_id不产生重复副作用,无法对账时保留证据并进入明确阻断状态。 - 主线程在普通 3 轴场景保持可用,WASM 计算不阻塞 UI。
- 所有真实硬件排除项均返回
real_hardware_excluded;仿真配置依赖的通用虚拟主轴、编码器、探针、限位和 I/O 必须可用。真实 Modbus、GPIO、Mesa 或 VFD 协议不属于仿真覆盖范围。
9. 分阶段实施路线
本节的阶段 A-E 映射到执行策略中的 M0-M5;阶段退出条件、任务边界和返工规则以项目执行与交付策略第 4、6、7 节为准。
阶段 A:全量清单和运行时骨架
- 固定 LinuxCNC 版本和编译补丁清单。
- 生成覆盖 manifest,锁定所有文档、源码、仿真配置和测试输入。
- 建立单 Worker、固定 tick、OPFS journal、快照、版本化 ABI 和 Python/WASM 运行时。
- 建立原生 LinuxCNC 参考运行器和全量 golden 产物格式。
阶段 B:核心解释和运动全覆盖
- 移植全部 G/M/O、remap、参数、坐标系、补偿、TP、motion 和全部内置 kinematics。
- 完成 mill、lathe、gantry、robot、delta、pentapod、foam、plasma、laser 和 probe 虚拟机床模型。
阶段 C:HAL、PLC、IO 和虚拟机床部件
- 生成并编译全部 HAL 组件 manifest。
- 完成 ClassicLadder、虚拟寄存器、主轴/换刀/探针/编码器/stepgen/限位/故障模型。
- 完成 halcmd、haltcl、halui、HALScope、HALShow、Python HAL API。
阶段 D:全部 Web UI 和配置工具
- 完成 AXIS、Gmoccapy、Touchy、Gscreen、QtVCP、QtDragon、QtPlasmaC、VCP、Vismach、NGCGUI 等仿真操作能力的 Web 等价工作流,以及独立仿真配置编辑器。
- 完成工程导入导出、离线、国际化、主题和插件权限。
阶段 E:全量回归和发布
- 跑通全部
configs/sim、tests、unit_tests、golden 和浏览器矩阵。 - 生成版本化差异报告、兼容报告、许可证清单和工程迁移工具。
10. 主要风险与决策
| 风险 | 影响 | 决策/缓解 |
|---|---|---|
| LinuxCNC 代码含 Python、动态库和 Linux API | 无法直接链接 | 核心/兼容层分离;Pyodide 提供仿真必需 Python,宿主能力替换为虚拟 API |
| 浏览器不提供硬实时 | 运动结果不可依赖墙钟 | 固定 tick、批量 step、确定性回放 |
| OPFS 有配额和淘汰 | 用户数据可能不可用 | journal、版本保留、配额提示、导出备份、可选 persist |
| WasmFS/OPFS API 版本差异 | 构建或运行时回归 | 锁定 Emscripten 版本,封装适配器,浏览器矩阵测试 |
| 全量 HAL/组件工作量大 | 项目周期失控 | manifest 驱动、自动生成组件适配层和全量配置/测试回归 |
| 浮点和算法差异 | 原生/WASM 轨迹不一致 | 固定编译选项、黄金快照、定义逐算法容差 |
| 浏览器清理存储 | 工程丢失 | OPFS 不是唯一备份,提供工程包导出和恢复检查 |
11. 第一批可执行任务
- 增加
wasm-core最小 CMake 工程,先只编译 posemath、TP 和解释器依赖的最小源集。 - 定义
sim_api.h、命令/事件/快照 schema 和错误码。 - 由
configs/sim/**、tests/**、unit_tests/**和文档/源码 G/M/O、HAL、Python、API 清单生成全量 manifest 和 golden 集;不以抽样集代替来源、维度或交互覆盖。 - 实现 Worker 内
WorkspaceRuntime:OPFS 目录初始化、句柄池、journal、恢复和配额查询。 - 实现
sim_step()固定周期和sim_read_snapshot()。 - 建立原生 LinuxCNC 参考运行器,自动生成 canonical/状态 golden 文件。
- 完成第一个浏览器 E2E:加载样例、运行、暂停、刷新、恢复、导出工程。
- 按前端与通信详细设计完成 COM-01/COM-02 和 FE-01/FE-02,冻结
protocol.v1、能力模型、错误码、WorkerClient 和 TaskLedger 最小闭环。
12. 结论
纯 Web/WASM 仿真版本应把 LinuxCNC 看作“运动和解释行为的参考实现”,把 Linux 进程、实时内核、共享内存、文件系统、HAL 组件和桌面 UI 分别替换为浏览器内等价运行时。OPFS 负责长期工程数据和恢复,不直接承担每个仿真周期的随机写入;Worker 内的同步工作区负责 LinuxCNC 兼容文件语义;固定逻辑时钟负责可重复运动。
按本路线实施,交付顺序从运行时骨架、核心运动、HAL/数字孪生到 Web UI,但最终验收对象始终是覆盖矩阵中的全部仿真功能。三轴只是最早的垂直切片,不是产品能力边界;发布前必须完成完整仿真覆盖和真实硬件排除校验。