Files
wasm-simulator/README.zh-CN.md
cnc de273bf830 接续上一轮:推进浏览器验证与M428配置收拢
结论:已完成浏览器侧真实加载验证,M428/M429/M430 的配置入口进一步从 LinuxCNC INI/HALFILE/REMAP 来源生成,native 与 source-link 验证通过。
2026-05-30 10:03:21 +08:00

201 lines
15 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 CNC Simulator
本目录是把 LinuxCNC 面向浏览器仿真的改写入口。目标不是把完整 LinuxCNC 直接搬进浏览器,而是复用可移植的解释器、运动学和刀路算法,替换实时 HAL/NML/GUI 层,输出适合 Web Worker、Three.js/WebGPU 和后续碰撞/去料仿真的事件流。
## 关键判断
- 不能按“整套 LinuxCNC 编译为 wasm”做。LinuxCNC 包含实时线程、HAL、NML、动态模块、Python/Boost.Python remap、桌面 GUI 和 POSIX 依赖,这些都不是浏览器仿真核心。
- 可以先复用 `src/emc/rs274ngc` 解释器,接管 `canon.hh` 中的 Canonical Machining Functions`STRAIGHT_FEED``ARC_FEED``DWELL``CHANGE_TOOL` 等调用记录为仿真事件。
- Fanuc、Siemens 等主流系统应作为“方言层”实现先把宏变量、固定循环、坐标旋转、RTCP/TRAORI/CYCLE800 等控制器特性归一化为内部 IR再交给同一个仿真内核。
- 浏览器端不做实时控制,只做可重复、可暂停、可回放的离线仿真,所以速度规划、前瞻、碰撞和去料可以使用非实时算法。
## 分层
1. `dialect`
- LinuxCNC RS274/NGC
- Fanuc Macro B / common milling and turning extensions
- Siemens 840D-style cycles and transforms
2. `interp`
- LinuxCNC interpreter compiled to wasm where possible
- Canon callback adapter records normalized motion/tool/state events
3. `sim-core`
- Units, modal state, work offsets, tool table
- Arc/NURBS segmentation
- 3/4/5-axis kinematics and RTCP
- Feed/time estimation and non-realtime lookahead
4. `verification`
- Golden G-code corpus
- Controller dialect compatibility matrix
- Numeric tolerance checks against LinuxCNC native output
5. `web`
- Web Worker wrapper around wasm
- Toolpath display
- Machine model, fixtures, stock, collision and material removal
## First milestone
The first useful target is:
- compile a small wasm module exposing `cnc_sim_*` C ABI;
- connect LinuxCNC RS274 interpreter to a Canon event sink;
- return a JSON event stream to TypeScript;
- render rapid/feed/arc events in the browser;
- compare the event stream against native LinuxCNC for a small test corpus.
## 当前进度
- 已有稳定 C ABI 和 Web 调用封装。
- 已有数控系统风格网页面板。
- 已有临时 smoke parser用于在 LinuxCNC 后端接入前测试 UI 和 wasm 回调链路。
- 已有 native smoke test 和 JSON dump 工具。
- 已有 LinuxCNC `canon.hh` bridge并已通过独立 smoke test。
- 已有 native `librs274` runner可以让 LinuxCNC 解释器输出 `CncSimEvent`
- 已有最小 LinuxCNC tooldata 初始化native runner 和 API 后端都能覆盖 `T... M6` 换刀路径。
- 已有 LinuxCNC RS274 源码级链接 smoke本项目直接编译 23 个解释器 core 源文件,不再通过 `librs274` 取得解释器主体。
- 已有五轴 RTCP 第一版几何内核可根据编程刀尖点、A/B/C 姿态和刀长计算枢轴/主轴点补偿位置。
- 已有坐标系事件回归语料,覆盖 `G10 L2``G10 L20``G10 P0``G54`-`G59.3` 全部工件坐标系选择、`G52``G92/G92.1/G92.2/G92.3` 的 G5X/G92/XY 旋转事件。
- 已有探针事件回归语料LinuxCNC `G38.2/G38.3/G38.4/G38.5` 会输出独立 `probe` 事件,不再混作普通直线进给;`probe.reserved` 保留 LinuxCNC `probe_type``0=trip/error``1=trip/no-error``2=clear/error``3=clear/no-error`
- 已有主轴方向状态回归语料,`M3/M4/M5` 会通过 `set-spindle.reserved` 输出 `1=顺时针``2=逆时针``0=停止`
- 已有主轴速度模式回归语料,`G96/G97` 会通过 `comment.reserved` 临时输出 `960=CSS``970=constant RPM``feed` 暂存 LinuxCNC `SET_SPINDLE_MODE` 的 mode/max-RPM 值。
- 已有主轴定向回归语料,`M19 R... P... Q...` 会通过 `comment.reserved` 临时输出 `1901=orient spindle``1902=wait orient complete``feed` 暂存 R 角度,`arcTurns` 暂存 P 模式,`dwellSeconds` 暂存 Q 超时。
- 已有主轴同步进给和螺纹回归语料,`G33/G33.1/G76` 会通过 `comment.reserved` 临时输出 `3301=speed-feed sync start``3302=velocity sync start``3300=sync stop``feed` 暂存每转进给量。
- 已有车床直径/半径模式回归语料,`G7/G8` 会切换 X 轴输入缩放,`G76` 会在直径模式下同步缩放线程几何参数。
- 已有 LinuxCNC 常用只读命名参数回归语料,`#<_absolute>``#<_incremental>``#<_metric>``#<_imperial>``#<_lathe_diameter_mode>``#<_lathe_radius_mode>` 能在 O-word 条件里直接读取当前模态状态。
- 已有覆盖控制回归语料,`M48/M49/M50/M51/M52/M53` 会通过 `comment.reserved` 临时输出 `50=feed override``51=speed override``52=adaptive feed``53=feed hold``feed` 暂存启用状态,`tool` 对 speed override 暂存 spindle 号。
- 已有 I/O M-code 回归语料,`M62/M63/M64/M65/M66/M67/M68` 会通过 `comment.reserved` 临时输出对应 M 码号,`tool` 暂存 P/E 通道号,`feed` 暂存输出值或 M66 等待模式,`arcTurns` 暂存 M66 输入类型,`dwellSeconds` 暂存 M66 超时。
- 已有 modal state 回归语料,`M70/M71/M72` 在 smoke parser 中支持主程序级保存、失效、恢复,`M73` 支持 O-word 子程序返回自动恢复native/source 语料验证恢复后的距离模式、单位和 XY 平面运动。
- 已有注释事件行号回归语料LinuxCNC `COMMENT` 事件会保留当前 G-code 行号。
- 已有当前刀号事件行号回归语料LinuxCNC `M61 Q...` 会输出带行号的 `tool-change` 事件。
- 已有停止类事件回归语料,`M0/M1/M60` 会通过 `program-end.reserved` 输出 `1=程序停``2=可选停``3=托盘交换`
- 已有冷却液状态回归语料,`M7/M8/M9` 会通过 `comment.reserved` 临时输出 `10/11=mist off/on``20/21=flood off/on`
- 已有进给模式回归语料,`G93/G94/G95` 会通过 `comment.reserved` 临时输出 `93/94/95`,并通过 `set-feed` 记录 Canon feed mode 回调。
- 已有运动控制模式回归语料,`G61/G61.1/G64` 会通过 `comment.reserved` 临时输出 `611/612/640``feed` 暂存 G64 tolerance。
- smoke parser 已有固定循环第一版展开,覆盖 `G73` 高速啄钻、`G74/G84` 攻丝、`G81` 普通钻、`G82` 孔底暂停钻、`G83` 啄钻、`G85` 镗孔进给退刀、`G86` 镗孔停主轴快速退刀、`G87` 背镗、`G88` 手动退刀镗孔、`G89` 镗孔暂停后进给退刀,以及 `G80` 取消、`G98/G99` 返回模式和 `L` 重复次数;这些循环已按 LinuxCNC `G17/G18/G19` 平面映射进刀轴,`G87` 会按平面重映射 `I/J/K`
- LinuxCNC native/source 对照已扩展到 `G82` 孔底暂停、`G83` 啄钻和 `G18/G19` 固定循环平面关键事件,覆盖 `G74/G84/G86/G87/G88` 非 XY 平面序列、孔底 dwell、啄钻深度序列、R 平面退刀、手动退刀停机和背镗偏置路径。
- smoke parser 已有 Fanuc 风格 `M98 P... L...` / `M99` 子程序调用第一版,支持同文件 `O...` 子程序块和重复调用展开;未知子程序、裸 `M99` 和过深嵌套会返回明确错误。
- smoke parser 已有 LinuxCNC 风格 O-word 子程序第一版,覆盖数字 `O100 ...` 和命名 `O<name> ...``sub` / `call [args]` / `return` / `endsub`,支持简单数字实参、`#1 = ...` 编号参数赋值、`#<name> = ...` 命名参数赋值、`X#1` / `X#<name>` 参数引用、`+ - * /` 可嵌套方括号表达式,以及 `ABS[]``SQRT[]``EXP[]``LN[]`、角度制 `SIN[]` / `COS[]` / `TAN[]` / `ASIN[]` / `ACOS[]`、LinuxCNC 双参数 `ATAN[]/[]``FIX[]` / `FUP[]` / `ROUND[]`,并拒绝和 `M98/M99` 风格混用。
- LinuxCNC native/source runner 已支持显式文件模式执行O-word 子程序语料会通过 LinuxCNC 自身 `open/read/execute` 路径验证而不是逐行模拟native/source 回归现已同时覆盖数字 `O100` 和命名 `O<move_plus>` 子程序、实参传递、命名参数赋值、`return` 跳过体尾,以及 `if` / `elseif` / `else``while``repeat``do` / `while``break``continue` 控制流。
- LinuxCNC Canon bridge 会把当前解释行号补到没有自带 `lineno` 的回调事件上,方便 Web 端诊断和源码高亮。
- 下一步是继续替换源码级链接中残留的 Python、INI/HAL、文件系统和动态加载依赖并把 RTCP 内核接入 canon 事件流和机型配置。
## 本地测试
```bash
./test-all-native.sh
```
当前 native 测试覆盖:
- smoke parser
- Canon event sink
- LinuxCNC Canon bridge
- 直接链接 LinuxCNC `librs274` 的 runner
- 通过 `cnc_sim_api` 选择 `linuxcnc-rs274` 后端
- LinuxCNC RS274 源文件语法探针和对象编译探针
- LinuxCNC RS274 源码级链接 smoke
- 五轴 RTCP 几何内核 smoke
- `M3` / `M4` / `M5` 主轴方向事件回归
- `M61 Q...` 当前刀号事件行号回归
- LinuxCNC 注释事件行号回归
- `M0` / `M1` / `M60` 停止类事件回归
- `M7` / `M8` / `M9` 冷却液状态回归
- `G93` / `G94` / `G95` 进给模式回归
- `G61` / `G61.1` / `G64` 运动控制模式回归
- smoke parser `G73` / `G74` / `G81` / `G82` / `G83` / `G84` / `G85` / `G86` / `G87` / `G88` / `G89` / `G80` / `G98` / `G99` / `L` 固定循环展开回归,以及 `G18/G19` 平面进刀轴和 `G87 I/J/K` 平面映射回归
- LinuxCNC native/source `G74` / `G82` / `G83` / `G84` / `G86` / `G87` / `G88` / `G18` / `G19` 固定循环对照回归
- smoke parser `M98 P... L...` / `M99` 子程序调用回归
- smoke parser LinuxCNC 风格 `O... sub/call/return/endsub` 子程序调用回归
- `G10 L2` / `G10 L20` / `G10 P0` / `G54`-`G59.3` / `G92.1` 坐标系事件回归
- `G38.3` 探针事件回归
## 后端选择
公共 API 使用同一个入口解析程序,后端通过 JSON 配置选择:
```json
{"backend":"linuxcnc-rs274"}
```
`linuxcnc-rs274` 是默认后端wasm 和 UI 默认通过 LinuxCNC RS274 源码路径解析;`smoke` 仅用于显式编译/选择的 legacy 联调路径。
网页面板右上角的 `BACKEND` 下拉框会把该配置传给 wasm/core。`./build-wasm.sh` 会生成 `web/public/cnc_sim.js``web/public/cnc_sim.wasm`,随后运行 `test-web-wasm-node-smoke.cjs``test-web-wasm-browser-smoke.sh`;这些 smoke 会验证 wasm 中的 LinuxCNC RS274 后端可在 Node 和 Chromium 浏览器中加载、可解析基础运动,并保留 `M428/M429/M430` remap 触发的运动学切换事件。
## RTCP 配置
RTCP 默认关闭。开启后,原有 `rapid``linear-feed``arc-feed` 事件仍表示编程刀尖轨迹core 会额外发出 `rtcp-pivot` 事件,`start`/`end` 是按刀长和 A/B/C 姿态补偿后的枢轴/主轴点轨迹,`center``x/y/z` 暂存当前刀具向量。
```json
{
"backend": "linuxcnc-rs274",
"rtcp": {
"enabled": true,
"toolLength": 100,
"toolLengths": {
"7": 125
}
},
"xyzbcTrt": {
"xRotPoint": 0,
"yRotPoint": 0,
"zRotPoint": 0,
"xOffset": -20,
"zOffset": -15,
"conventionalDirections": false
}
}
```
RTCP pivot 事件只在已知 LinuxCNC switchkins 类型下输出。`M428` 切换到 `XYZBC_TRT` 后,`xyzbcTrt` 对应 LinuxCNC `xyzbc-trt-kins` 的 HAL pin`x-rot-point``y-rot-point``z-rot-point``x-offset``z-offset``conventional-directions`;刀长仍来自 `toolLength` / `toolLengths``M430` 使用 LinuxCNC `userkfuncs.c``kins_util.c` identity 运动学路径。未支持或未知的 switchkins 类型不会回退到自研 RTCP 几何。
RTCP G 码控制已开始标准化:
- `G43.4 H...`:开启 RTCP`H` 号进入事件,实际刀长优先从 JSON 配置中的 `toolLengths` 按 H 号读取,未配置时回退到 `toolLength`
- `G43.5 H...`:同样作为 RTCP 开启处理。
- `G49`:关闭 RTCP。
解析后会发出 `rtcp-state` 事件。事件中的 `feed` 表示 RTCP 状态:`0=关闭``1=开启``tool` 表示 H 号;`dwell_seconds` 暂存当前配置刀长。
## 运动学切换 M 码
core 已对原型中的运动学切换 M 码做标准化解析:
- `M429`:切换到三轴恒等运动学 `IDENTITY`
- `M428`:切换到 LinuxCNC `xyzbc-trt-kins` 的 XYZBC TRT 运动学。
- `M430`:切换到 LinuxCNC 默认 `userk` 运动学;当前对齐 `userkfuncs.c` 的 identity 正/逆解。
解析后会发出 `kinematics-switch` 事件。事件中的 `reserved` 字段表示运动学类型:`0=IDENTITY``1=XYZBC_TRT``2=USERK_IDENTITY`。这些 M 码对齐 LinuxCNC `remap_subs/428remap.ngc``429remap.ngc``430remap.ngc`,只设置 `motion.switchkins-type`,不隐式开启或关闭 RTCPRTCP 状态仍由 `G43.4``G43.5``G49` 控制。同一行中的 `M428 M429 M430` 会按出现顺序产生三个切换事件。
`linuxcnc-rs274` native API backend 会在送入 LinuxCNC 解释器前拦截只包含这些切换 M 码的行,避免 LinuxCNC 去查找外部用户 M-code 文件。
## 坐标系事件
Canon bridge 已不再丢弃坐标系相关回调,会输出:
- `set-g5x-offset`:来自 `SET_G5X_OFFSET``tool` 字段存 G5X index`start` 存 XYZABCUVW offset。
- `set-g92-offset`:来自 `SET_G92_OFFSET``start` 存 XYZABCUVW offset。
- `set-xy-rotation`:来自 `SET_XY_ROTATION``feed` 暂存旋转角度。
这些事件先保证 LinuxCNC Canon 层信息完整进入 Web/core。具体 G-code`G10``G92`、坐标旋转)是否触发这些回调,还要继续按 LinuxCNC 解释器参数路径逐项补回归语料。
目前已有多组回归语料:`tests/gcode/linuxcnc_coordinate_offsets.ngc` 覆盖 `G10 L2 P1 X... Y... Z... R...` 触发 `set-g5x-offset` / `set-xy-rotation`,以及 `G92.1` 触发 `set-g92-offset` 清零;`tests/gcode/linuxcnc_coordinate_l20.ngc` 覆盖 `G10 L20 P2` 按当前位置和目标工件坐标计算 G55 偏置;`tests/gcode/linuxcnc_coordinate_p0.ngc` 覆盖 `G10 L2 P0` 指向当前活动坐标系;`tests/gcode/linuxcnc_coordinate_select_all.ngc` 覆盖 `G54`-`G59.3` 选择时的 G5X index 和偏置载入。测试脚本会在坐标系语料之间重置参数文件,避免 LinuxCNC 持久参数互相污染。后续继续扩展持久参数文件读写策略和旋转叠加运动路径。
LinuxCNC 中部分 Canon 函数不携带行号参数bridge 会在调用解释器 `read/execute` 前记录当前输入行,并用它填充这类事件的 `line` 字段。
## CMake native LinuxCNC 后端
手写测试脚本仍是当前主验证路径。如果使用 CMake启用 native LinuxCNC 后端的配置为:
```bash
cmake -S core -B build/native-linuxcnc \
-DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND=ON \
-DCNC_SIM_LINUXCNC_ROOT=/home/cnc/桌面/cnc/linuxcnc
```
该选项是 native-only会链接 LinuxCNC 已构建的 `librs274``libtooldata`
## Legal note
LinuxCNC is GPL licensed. If this wasm module links LinuxCNC code, the combined simulator core must be distributed under GPL-compatible terms unless you replace that part with an independently written interpreter.