Compare commits
8 Commits
902c6ecaea
...
1ab5571ae1
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ab5571ae1 | |||
| 5bd8f12872 | |||
| 50757e45ba | |||
| 45420b055a | |||
| e7df5b3eda | |||
| 3698c51917 | |||
| 5116b9894d | |||
| 2658ee3b72 |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
wasm-port/vendor/linuxcnc/tests/** whitespace=-trailing-space,-blank-at-eof
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,4 +2,5 @@
|
||||
/.codex/
|
||||
/git.txt
|
||||
/text.txt
|
||||
/test-results/
|
||||
/linuxcnc/
|
||||
|
||||
274
text2.txt
Normal file
274
text2.txt
Normal file
@@ -0,0 +1,274 @@
|
||||
新下一步执行计划
|
||||
|
||||
生成时间:2026-06-09 14:20 CST
|
||||
|
||||
本计划接替 `text.txt`。后续执行以本文件为准。
|
||||
|
||||
项目纪律:
|
||||
|
||||
- 数控/G-code/remap/tool/parameter/planner/kinematics/user-M 语义必须来自
|
||||
LinuxCNC upstream 或 vendored LinuxCNC C/C++ source。
|
||||
- 本项目允许写的代码范围是:构建、source sync、runtime shim、filesystem
|
||||
staging、OPFS、WASM/browser boundary、测试胶水、文档。
|
||||
- 不通过修改 G-code 语义、JS 解释逻辑、或 standalone `Interp::...` 实现来让
|
||||
测试通过。
|
||||
- 需要 INI/tool table/parameter file/SUBROUTINE_PATH/USER_M_PATH/remap-NGC
|
||||
上下文的程序,必须带上下文运行,不能脱离配置单独判断。
|
||||
- browser 不能依赖目录枚举。browser/Node 共享 staging 时,优先使用
|
||||
`tools/source-manifest.txt` + INI 文本生成计划。
|
||||
|
||||
当前已完成基线:
|
||||
|
||||
- 规划 i:G-code run_step 状态输出已完成第一阶段。
|
||||
已输出当前行号、statement_uri、进度相关 step、X/Y/Z/A/B/C/U/V/W 位置。
|
||||
UI 已优先解析 run_step,fallback 到 canonical event。
|
||||
|
||||
- 规划 ii:基础 G-code smoke 已固化代表集。
|
||||
`nc_files/3D_Chips.ngc` 已按 tool-table context 运行,裸跑缺 tool 的负向边界
|
||||
已明确。
|
||||
|
||||
- configs/sim 代表 runtime edge 已覆盖:
|
||||
`axis/foam` U/V axis mask、bridge-mill W/remap path、`axis/geometry` M110、
|
||||
`external_offsets` M111 和 subroutine staging 已在 native/WASM/browser
|
||||
representative smoke 中验证。
|
||||
|
||||
- 文件上下文 staging 规则已抽成通用 SDK helper:
|
||||
`planIniFileContextStaging()` 负责 generic INI-context manifest-based plan;
|
||||
`planSimConfigStaging()` 是 `configs/sim` wrapper;
|
||||
Node 和真实 browser smoke 已覆盖 INI、OPEN_FILE、TOOL_TABLE、PARAMETER_FILE、
|
||||
multi-directory SUBROUTINE_PATH、USER_M_PATH、executable user-M、remap-NGC 和
|
||||
wasmPath 输出。
|
||||
|
||||
- 测试分层文档已补齐:
|
||||
`docs/compatibility-validation.md` 已记录 4 层测试的入口、当前结果、
|
||||
LinuxCNC-owned behavior、host/WASM adapter allowance 和 expected-failure
|
||||
policy。
|
||||
|
||||
当前验证口径:
|
||||
|
||||
- Layer 1: `wasm-port/tests/native/verify_nc_files.sh`
|
||||
当前基线:total 107, pass 101, expected_fail 6, unexpected_fail 0。
|
||||
|
||||
- Layer 2: `wasm-port/tests/native/verify_sim_configs.sh`
|
||||
当前基线:total 159, pass 151, expected_fail 8, unexpected_fail 0。
|
||||
|
||||
- Layer 3: `wasm-port/tests/native/verify_native_probes.sh`
|
||||
当前应通过,包含 source sync、no-standalone-semantics、native fixture
|
||||
baseline、sim-config 和 nc_files checks。
|
||||
|
||||
- Layer 4: `wasm-port/tests/host/verify_host_smokes.sh`
|
||||
当前应通过,包含 Node WASM、OPFS、browser interpreter 和 browser INI panel
|
||||
representative smoke。
|
||||
|
||||
新执行顺序:
|
||||
|
||||
1. 先稳定当前大批改动,不继续扩大测试面
|
||||
|
||||
目标:
|
||||
|
||||
- 对当前累计改动做一次完整一致性验证。
|
||||
- 确认 vendor manifest、native probes、Node/browser smoke 都仍处于可提交状态。
|
||||
- 在继续新增 upstream tests 前,先把当前批次变成一个清晰、可审查的工作集。
|
||||
|
||||
执行:
|
||||
|
||||
```bash
|
||||
git diff --check
|
||||
wasm-port/tools/verify_vendor_sync.sh
|
||||
wasm-port/tests/wasm/node/verify_interp_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
|
||||
wasm-port/tests/browser/verify_interp_browser.sh
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
wasm-port/tests/native/verify_native_probes.sh
|
||||
```
|
||||
|
||||
通过条件:
|
||||
|
||||
- `git diff --check` clean。
|
||||
- `vendor sync validation complete`。
|
||||
- `interp_wasm_node_smoke=ok`。
|
||||
- `sim_configs_wasm_node_smoke=ok`。
|
||||
- `browser_interp_smoke=ok`。
|
||||
- `host_wasm_opfs_browser_smokes=ok`。
|
||||
- native summaries 中仍为:
|
||||
`sim-configs total 159 pass 151 expected_fail 8 unexpected_fail 0`
|
||||
和
|
||||
`nc_files total 107 pass 101 expected_fail 6 unexpected_fail 0`。
|
||||
|
||||
不做:
|
||||
|
||||
- 不新增 upstream fixture。
|
||||
- 不改 LinuxCNC vendored 文件内容。
|
||||
- 不把 expected failure 改成 pass,除非 runtime edge 已经通过 vendored source
|
||||
路径证明。
|
||||
|
||||
2. 整理当前大批改动的提交/审查边界
|
||||
|
||||
目标:
|
||||
|
||||
- 把当前累计改动按逻辑拆成可审查批次。
|
||||
- 如果不提交,也至少在文档里列出可拆分边界,方便后续 review。
|
||||
|
||||
建议拆分边界:
|
||||
|
||||
- Batch A: run_step 状态输出和 UI/browser/Node 断言。
|
||||
- Batch B: nc_files 代表 smoke 和 `3D_Chips.ngc` tool-table context。
|
||||
- Batch C: upstream `tests/interp/*`、`tests/ccomp/*` selected fixture 扩展。
|
||||
- Batch D: G92 parameter persistence、tool table、G10/G52/G71/G72/G76 等
|
||||
interpreter regression coverage。
|
||||
- Batch E: generic INI-context staging helper 和 Node/browser synthetic coverage。
|
||||
- Batch F: 文档分层、source reuse map、compatibility validation、tracker 更新。
|
||||
|
||||
执行:
|
||||
|
||||
```bash
|
||||
git status --short
|
||||
git diff --stat
|
||||
git diff --name-only
|
||||
```
|
||||
|
||||
输出要求:
|
||||
|
||||
- 记录每个 batch 对应文件。
|
||||
- 标明哪些新增 `wasm-port/vendor/linuxcnc/...` 文件必须由
|
||||
`tools/source-manifest.txt` 和 `verify_vendor_sync.sh` 覆盖。
|
||||
|
||||
3. 收敛 configs/sim 剩余 expected failure,不改语义
|
||||
|
||||
目标:
|
||||
|
||||
- 继续分析 Layer 2 中 8 个 expected_fail。
|
||||
- 明确哪些只是 upstream `rs274` 缺 task/user-M/runtime context;
|
||||
哪些已经由 Layer 3/4 runtime representative smoke 覆盖;
|
||||
哪些仍需要新的 runtime adapter 或 blocked entry。
|
||||
|
||||
重点对象:
|
||||
|
||||
- `axis/foam/foam.ngc`
|
||||
已有 U/V axis mask runtime coverage。确认 Layer 2 expected_fail 是否只属于
|
||||
native upstream standalone context 限制。
|
||||
|
||||
- `axis/vismach/5axis/bridgemill/5axisgui.ngc`
|
||||
已有 bridge-mill W/remap representative coverage。确认 expected_fail 是否只
|
||||
属于 native full sim/task/HAL boundary。
|
||||
|
||||
- `axis/geometry/xyzc.ngc`
|
||||
已有 M110 USER_M_PATH deterministic boundary。确认 native expected_fail 与
|
||||
runtime coverage 对齐。
|
||||
|
||||
- `axis/external_offsets/*.ngc`
|
||||
已有 M111 deterministic boundary 和 `opa_demo.ngc -> circles.ngc`
|
||||
SUBROUTINE_PATH staging。确认剩余 expected_fail 的具体原因。
|
||||
|
||||
- `incremental_repetition_g533.ngc`
|
||||
保留 upstream demo expected failure。禁止通过修改 G-code 语义或 runner
|
||||
语义让它 pass。
|
||||
|
||||
执行:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/native/verify_sim_configs.sh
|
||||
cat wasm-port/build/native/sim-configs/summary.tsv
|
||||
```
|
||||
|
||||
产出:
|
||||
|
||||
- 更新 `docs/compatibility-validation.md` 或
|
||||
`docs/linuxcnc-test-porting-tracker.md`,逐条记录 8 个 expected_fail 的当前
|
||||
层级归因。
|
||||
- 如果某项已由 Layer 3/4 覆盖,写明对应验证命令和 smoke assertion。
|
||||
- 如果某项未覆盖,加入明确 blocked dependency 或下一步 runtime boundary。
|
||||
|
||||
4. 将 generic INI-context staging helper 用到更多手写 staging 点
|
||||
|
||||
目标:
|
||||
|
||||
- 减少 Node/browser 测试中手写 `TOOL_TABLE`、`PARAMETER_FILE`、
|
||||
`SUBROUTINE_PATH`、`USER_M_PATH` 和 remap files 的重复。
|
||||
- 只迁移“已经 vendored 且 manifest 完整”的测试,不扩大功能面。
|
||||
|
||||
候选:
|
||||
|
||||
- `tests/wasm/node/verify_interp_wasm.mjs` 中带 `test.ini`、`test.tbl`、
|
||||
`startup.var`、`subs/*.ngc` 的 upstream regression staging。
|
||||
- `tests/browser/interp_smoke.html` 中与 Node 同构的 upstream regression
|
||||
staging。
|
||||
- 不急于迁移 five-axis 专用 staging,除非能保持 `executionMode:
|
||||
"fiveAxisRemap"` 现有路径清晰。
|
||||
|
||||
执行原则:
|
||||
|
||||
- 先抽小 helper:`loadPlannedVendorFiles(plan)` 或测试局部 helper。
|
||||
- 保持 `planIniFileContextStaging()` 只返回 plan,不读文件、不 fetch。
|
||||
- Node 侧从 filesystem 读 `vendor/linuxcnc/${sourceRel}`。
|
||||
- Browser 侧 fetch `../../vendor/linuxcnc/${sourceRel}`。
|
||||
- 每次迁移后跑:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/wasm/node/verify_interp_wasm.sh
|
||||
wasm-port/tests/browser/verify_interp_browser.sh
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
```
|
||||
|
||||
5. 继续 upstream `tests/interp/*` intake,但必须小批量
|
||||
|
||||
前置条件:
|
||||
|
||||
- 第 1 步完整验证通过。
|
||||
- 第 2 步当前工作集边界清楚。
|
||||
- 第 3 步 expected failure 归因没有新疑点。
|
||||
|
||||
候选优先级:
|
||||
|
||||
- 纯 interpreter file execution。
|
||||
- 不需要 Python remap。
|
||||
- 不需要 full task/motion/HAL process。
|
||||
- 需要 tool table/parameter/subroutine context 的测试,必须通过 INI-context
|
||||
staging 运行。
|
||||
- 有 upstream expected output 或可稳定断言 canonical event/error text。
|
||||
|
||||
候选方向:
|
||||
|
||||
- `tests/interp/oword-unwind`
|
||||
可能适合 continue-on-error runner;先 native probe 验证,再 Node/browser。
|
||||
|
||||
- `tests/interp/g6164`
|
||||
先确认是否纯 interpreter、是否需要额外 machine/runtime context。
|
||||
|
||||
- `tests/interp/compile`
|
||||
先判断它是 compile/source test 还是 runtime interpreter test;不要误归类。
|
||||
|
||||
- `tests/interp/mdi-*`、`oword-mdi-*`
|
||||
默认视为 MDI/UI/full-process 候选,先加入 blocked table,除非能证明存在
|
||||
NGC-only subpath。
|
||||
|
||||
- `tests/interp/pymove`、`python-self`
|
||||
默认 blocked:Python O-word/remap boundary 未实现。
|
||||
|
||||
每个 intake 必须执行 checklist:
|
||||
|
||||
- 记录 upstream path。
|
||||
- byte-for-byte vendor 原始文件。
|
||||
- 更新 `tools/source-manifest.txt` 和 vendor sync。
|
||||
- 确认 LinuxCNC source ownership。
|
||||
- 先 native harness/probe,再 Node WASM,再 browser。
|
||||
- 更新 `docs/source-reuse-map.md`、
|
||||
`docs/compatibility-validation.md`、
|
||||
`docs/linuxcnc-test-porting-tracker.md`。
|
||||
- 如 blocked,加入 blocked-test table 并写清 dependency。
|
||||
|
||||
6. 不做事项
|
||||
|
||||
- 不继续扩大 `configs/sim` 全量 WASM/browser 执行面,除非某个 expected failure
|
||||
已经有明确 runtime boundary 价值。
|
||||
- 不引入 JS G-code parser 或 JS remap/tool/parameter semantics。
|
||||
- 不编辑 `linuxcnc/` upstream 工作树。
|
||||
- 不修改 `wasm-port/vendor/linuxcnc/` vendored 文件内容;只能 byte-for-byte copy
|
||||
upstream 文件。
|
||||
- 不绕过 `tools/verify_no_standalone_cnc_semantics.sh`。
|
||||
|
||||
推荐下一次立即执行:
|
||||
|
||||
1. 运行第 1 步完整一致性验证。
|
||||
2. 如果通过,执行第 2 步,整理当前工作集 batch 边界。
|
||||
3. 然后再进入第 3 步,逐条归因 configs/sim 8 个 expected_fail。
|
||||
386
text3.txt
Normal file
386
text3.txt
Normal file
@@ -0,0 +1,386 @@
|
||||
configs/sim 全面测试下一步实施计划
|
||||
|
||||
生成时间:2026-06-09 15:00 CST
|
||||
|
||||
本计划接替 `text2.txt`。后续 `configs/sim` 相关工作以本文件为准。
|
||||
|
||||
目标定义:
|
||||
|
||||
- 这里的“全部完成全面测试”不等于“让 `linuxcnc/configs/sim` 下所有 `.ngc`
|
||||
在 standalone `bin/rs274` 下全部 PASS”。
|
||||
- 正确目标是:
|
||||
1. 对 `configs/sim` 下所有 `.ngc` 建立完整 inventory;
|
||||
2. 每个文件都被归入明确类别;
|
||||
3. 每个类别都有正确入口点和验证层;
|
||||
4. 能在 standalone native/WASM/browser 中运行的,必须补齐覆盖;
|
||||
5. 不能在 standalone 路径中真实复现的,必须记录为 blocked,并写清依赖;
|
||||
6. 不通过修改 G-code 语义、JS 解释逻辑、或 project-owned standalone
|
||||
interpreter 语义让测试“看起来通过”。
|
||||
|
||||
项目纪律:
|
||||
|
||||
- CNC 语义来源只能是 LinuxCNC upstream 或 vendored LinuxCNC source。
|
||||
- `configs/sim` 程序必须在完整上下文下判断:INI、tool table、parameter file、
|
||||
`SUBROUTINE_PATH`、`USER_M_PATH`、remap-NGC、Python remap、task/runtime
|
||||
边界都要按 LinuxCNC 真实归属处理。
|
||||
- browser 不能依赖目录枚举;Node/browser staging 继续优先使用
|
||||
`tools/source-manifest.txt` + INI 文本生成计划。
|
||||
- 不为了“全面测试”而把 full-process / HAL / UI / linuxcncrsh / Python binding
|
||||
误降级为 standalone interpreter file execution。
|
||||
|
||||
当前真实基线:
|
||||
|
||||
- Layer 2: `wasm-port/tests/native/verify_sim_configs.sh`
|
||||
当前基线:`total 159, pass 151, expected_fail 8, unexpected_fail 0`。
|
||||
- Layer 3: `wasm-port/tests/native/verify_native_probes.sh`
|
||||
当前通过,已经覆盖代表性 runtime edge:
|
||||
`axis/foam` U/V、bridge-mill W/remap、`axis/geometry` M110、
|
||||
`axis/external_offsets` M111、`opa_demo.ngc -> circles.ngc`。
|
||||
- Layer 4:
|
||||
`wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh`、
|
||||
`wasm-port/tests/browser/verify_interp_browser.sh`、
|
||||
`wasm-port/tests/host/verify_host_smokes.sh`
|
||||
当前通过,但只覆盖 representative subset,不是全量 `configs/sim`。
|
||||
|
||||
当前已知剩余 expected failure:
|
||||
|
||||
- `axis/foam/foam.ngc`
|
||||
native upstream standalone `rs274` 缺 U/V axis reader,上层 runtime 已覆盖。
|
||||
- `axis/vismach/5axis/bridgemill/5axisgui.ngc`
|
||||
native upstream standalone `rs274` 缺 W axis reader,上层 runtime 已覆盖。
|
||||
- `axis/geometry/xyzc.ngc`
|
||||
native upstream standalone `rs274` 缺 `USER_M_PATH` M110 注册,上层 runtime 已覆盖。
|
||||
- `axis/external_offsets/dyn_demo.ngc`
|
||||
- `axis/external_offsets/eoffsets.ngc`
|
||||
- `axis/external_offsets/jwp_z.ngc`
|
||||
- `axis/external_offsets/opa_demo.ngc`
|
||||
native upstream standalone `rs274` 缺 `USER_M_PATH` M111 注册,上层 runtime 已覆盖;
|
||||
其中 `opa_demo.ngc` 还依赖 `SUBROUTINE_PATH` `circles.ngc`,上层 runtime 已覆盖。
|
||||
- `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc`
|
||||
保持 upstream demo expected failure;禁止通过修改 G-code 或 JS 解释逻辑让它 PASS。
|
||||
|
||||
全面测试还缺哪些工作:
|
||||
|
||||
1. 完整建立 `configs/sim` 程序级覆盖矩阵
|
||||
|
||||
当前问题:
|
||||
|
||||
- 我们有 harness summary,但还没有一份“每个 `.ngc` 的归属矩阵”:
|
||||
哪些是 `main`、哪些是 `macro_load`、哪些是 `remap_subroutine`、
|
||||
哪些只是 LinuxCNC native baseline、哪些已经进入 Layer 3、哪些已经进入 Layer 4、
|
||||
哪些仍 blocked。
|
||||
|
||||
需要产出:
|
||||
|
||||
- 一份 tracked matrix,建议新增:
|
||||
|
||||
```text
|
||||
wasm-port/docs/sim-configs-coverage-matrix.md
|
||||
```
|
||||
|
||||
矩阵至少包含列:
|
||||
|
||||
- `path`
|
||||
- `class`
|
||||
- `native_rs274_status`
|
||||
- `expected_failure_reason`
|
||||
- `layer3_runtime_probe`
|
||||
- `layer4_node`
|
||||
- `layer4_browser`
|
||||
- `blocked_dependency`
|
||||
- `notes`
|
||||
|
||||
通过条件:
|
||||
|
||||
- `wasm-port/build/native/sim-configs/summary.tsv` 中所有 159 条记录都能映射到 matrix。
|
||||
- matrix 中不能有“未分类”行。
|
||||
|
||||
2. 明确“全部”里的 blocked 边界,而不是继续误扩面
|
||||
|
||||
当前问题:
|
||||
|
||||
- `configs/sim` 下面并不是所有 `.ngc` 都应该被强行推进到 browser full execution。
|
||||
- 还缺一份 `configs/sim` 专属 blocked policy,用来区分:
|
||||
- pure interpreter / remap / INI-context 可移植程序;
|
||||
- task/user-M/runtime edge 程序;
|
||||
- Python remap/full-process/HAL/UI 依赖程序;
|
||||
- demo edge / upstream malformed demo。
|
||||
|
||||
需要明确加入 blocked 的类:
|
||||
|
||||
- 任何本质依赖 linuxcncrsh、GUI driver、HAL process、实时 motion 状态反馈、
|
||||
外部 userspace component、或 Python binding API 的 sim-config path。
|
||||
- `mdi-*`、UI action、或只能通过 full task process 触发的配置流。
|
||||
- 不能通过 NGC-only subpath 提纯的 Python/full-process config family。
|
||||
|
||||
执行:
|
||||
|
||||
- 从 `verify_sim_configs.sh` summary 和 `configs/sim` 路径结构出发,
|
||||
逐目录补 blocked classification。
|
||||
- 先补 family-level blocked,再细化到 program-level。
|
||||
|
||||
产出:
|
||||
|
||||
- 更新 `docs/linuxcnc-test-porting-tracker.md` blocked table;
|
||||
- 更新 `docs/compatibility-validation.md` 的 `configs/sim` 边界说明;
|
||||
- 在新 matrix 中写出每个 blocked 程序或目录的 dependency。
|
||||
|
||||
3. 把 representative coverage 扩展成 class coverage,而不是盲目追求全量 browser
|
||||
|
||||
当前问题:
|
||||
|
||||
- Layer 4 现在只覆盖 representative subset:
|
||||
`foam`、`geometry`、`external_offsets`、bridge-mill。
|
||||
- 这还不足以支撑“全面测试完成”的说法,因为 `configs/sim` 还包含多种 machine class:
|
||||
- plain INI/tool-table/main program;
|
||||
- Python remap main program;
|
||||
- remap subroutine only;
|
||||
- five-axis switchkins / TWP / TDR / TRT;
|
||||
- macro-only config families;
|
||||
- on_abort / macro load / tool-change style files。
|
||||
|
||||
实施原则:
|
||||
|
||||
- 不是把 159 个程序都复制进 browser smoke。
|
||||
- 是每种独立 runtime class 至少补一个 native + Node + browser representative,
|
||||
并且 class 到 program 的归因在 matrix 中完整记录。
|
||||
|
||||
最低还要补齐的 representative class:
|
||||
|
||||
- TWP `table-rotary_spindle-rotary-nutating`:
|
||||
当前 native baseline 已有 14/15 passing inventory,但 Layer 4 还没有代表性 TWP
|
||||
runtime smoke。
|
||||
- gmoccapy Python remap family:
|
||||
native inventory 已通过,但 Layer 4 还没有任何 gmoccapy representative。
|
||||
- axis/laser Python remap family:
|
||||
native inventory 已通过,但 Layer 4 还没有任何 laser representative。
|
||||
- generic `macro_load` family:
|
||||
当前 Layer 4 基本聚焦 main programs,缺少“宏文件只验证 load/parse,不伪造 main”
|
||||
的 shared assertion。
|
||||
- `on_abort` / deterministic user-action macro family:
|
||||
当前 native inventory大量 PASS,但 Node/browser 尚未有独立 representative class。
|
||||
|
||||
通过条件:
|
||||
|
||||
- 每个 runtime class 在 `docs/sim-configs-coverage-matrix.md` 中都能对应至少一个
|
||||
Layer 3/Layer 4 representative。
|
||||
- 不要求 159 个程序全部进入 browser smoke;
|
||||
但要求每个 class 都有代表 sample,且每个非代表 sample 都有“为什么不需要单独进
|
||||
browser”的记录。
|
||||
|
||||
4. 补 `configs/sim` 专属全量 Node inventory runner
|
||||
|
||||
当前问题:
|
||||
|
||||
- Node 侧现在只有 representative smoke,没有一个“只跑能在 standalone/WASM
|
||||
路径中合理成立的 `configs/sim` 全量 inventory”。
|
||||
|
||||
需要新增:
|
||||
|
||||
```text
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
```
|
||||
|
||||
职责:
|
||||
|
||||
- 读取 native `summary.tsv` 或同等来源;
|
||||
- 只挑选:
|
||||
- native PASS 条目;
|
||||
- 以及 Layer 2 expected_fail 但 Layer 3/4 已知有 runtime adapter coverage 的条目;
|
||||
- 跳过已标记 blocked 的 full-process/HAL/UI-only family;
|
||||
- 对 Node WASM 做 inventory run;
|
||||
- 输出 Node 侧 summary.tsv。
|
||||
|
||||
重要限制:
|
||||
|
||||
- 不把 browser 作为 first target;
|
||||
- 不新增 JS CNC 语义;
|
||||
- 不把 Python/full-process family 强行塞进 Emscripten,除非已存在 LinuxCNC-owned
|
||||
runtime path。
|
||||
|
||||
通过条件:
|
||||
|
||||
- Node inventory summary 可重跑、可对比、可纳入 CI;
|
||||
- unexpected failure 为 0;
|
||||
- expected skip / blocked 有明确计数。
|
||||
|
||||
5. 再决定 browser inventory,而不是直接上全量 browser
|
||||
|
||||
当前问题:
|
||||
|
||||
- browser 环境成本高,且不能依赖目录枚举。
|
||||
- 如果先做 browser 全量 inventory,会把大量问题混在 host boundary、
|
||||
asset staging、Emscripten runtime、Playwright orchestration 里,定位价值低。
|
||||
|
||||
实施顺序:
|
||||
|
||||
- 先完成 Node inventory;
|
||||
- 从 Node inventory 中挑出最有价值的 browser representative 扩展;
|
||||
- browser 保持 focused class smoke,不追求一次性全量 159。
|
||||
|
||||
可以进入 browser 扩展候选的 class:
|
||||
|
||||
- TWP representative;
|
||||
- gmoccapy Python remap representative;
|
||||
- axis/laser representative;
|
||||
- one deterministic macro-only family;
|
||||
- one toolchange / on_abort family。
|
||||
|
||||
通过条件:
|
||||
|
||||
- browser 侧 class coverage 明确,不再只是 `foam/geometry/external_offsets/bridgemill`;
|
||||
- 仍然保持稳定、可复现、非目录枚举。
|
||||
|
||||
6. 把 `verify_sim_configs.sh` 从 inventory 提升为 coverage source-of-truth
|
||||
|
||||
当前问题:
|
||||
|
||||
- native `summary.tsv` 已经是事实来源,但还缺少稳定的 machine-readable 统计,
|
||||
用来支撑 docs、Node inventory 和后续 CI 对账。
|
||||
|
||||
需要增强:
|
||||
|
||||
- 保持 `summary.tsv` 不变;
|
||||
- 增加稳定派生 artifact,建议:
|
||||
|
||||
```text
|
||||
wasm-port/build/native/sim-configs/class-summary.tsv
|
||||
wasm-port/build/native/sim-configs/path-matrix.tsv
|
||||
```
|
||||
|
||||
最少字段:
|
||||
|
||||
- `path`
|
||||
- `class`
|
||||
- `status`
|
||||
- `expected_failure`
|
||||
- `ini`
|
||||
- `tbl`
|
||||
- `runtime_family`
|
||||
- `blocked`
|
||||
|
||||
注意:
|
||||
|
||||
- 如果不想提交 generated artifact,就由脚本生成并由 docs 读取规则描述;
|
||||
- 但字段模型要先定下来。
|
||||
|
||||
7. 清理 `configs/sim` 文档的“完成”定义
|
||||
|
||||
当前问题:
|
||||
|
||||
- 现有 `docs/sim-configs-completion-plan.md` 更多描述了“把当前大块 runtime edge
|
||||
打通”的完成,不是“全面测试完成”的完成。
|
||||
|
||||
需要文档调整:
|
||||
|
||||
- 新增一节:`Definition of Done for Full configs/sim coverage`
|
||||
- 明确:
|
||||
- inventory complete
|
||||
- blocked table complete
|
||||
- native source-of-truth complete
|
||||
- Node inventory complete
|
||||
- browser class coverage complete
|
||||
- every expected failure justified
|
||||
- no accidental standalone semantic ownership
|
||||
|
||||
8. 最后再谈“还能不能把 Layer 2 的 8 个 expected_fail 变少”
|
||||
|
||||
原则:
|
||||
|
||||
- `foam` / `bridgemill` / `M110` / `M111` 这些 expected_fail 不是当前首要问题;
|
||||
它们已经由 Layer 3/4 runtime path 证明。
|
||||
- 只有当能够通过 vendored LinuxCNC source path 合法地把 native strict baseline
|
||||
改造成更贴近 runtime 的入口点时,才讨论减少 expected_fail。
|
||||
- 在那之前,不改 `verify_sim_configs.sh` 的语义口径,不把 expected_fail 硬改成 PASS。
|
||||
|
||||
优先级排序:
|
||||
|
||||
1. coverage matrix
|
||||
2. blocked table
|
||||
3. Node inventory
|
||||
4. browser class expansion
|
||||
5. docs done-definition
|
||||
6. 再评估 Layer 2 baseline 是否需要新入口点
|
||||
|
||||
推荐执行顺序:
|
||||
|
||||
1. 先建立 coverage matrix
|
||||
|
||||
执行:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/native/verify_sim_configs.sh
|
||||
cat wasm-port/build/native/sim-configs/summary.tsv
|
||||
```
|
||||
|
||||
产出:
|
||||
|
||||
- 新增 `wasm-port/docs/sim-configs-coverage-matrix.md`
|
||||
- 159 条记录全部分类
|
||||
|
||||
2. 再补 blocked table 和 class taxonomy
|
||||
|
||||
执行:
|
||||
|
||||
- 逐目录梳理 `linuxcnc/configs/sim`
|
||||
- 对照 `verify_sim_configs.sh` summary
|
||||
- 把 full-process/HAL/UI-only family 写进 tracker blocked table
|
||||
|
||||
产出:
|
||||
|
||||
- 更新 `docs/linuxcnc-test-porting-tracker.md`
|
||||
- 更新 `docs/compatibility-validation.md`
|
||||
|
||||
3. 实施 Node inventory runner
|
||||
|
||||
执行:
|
||||
|
||||
```text
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
```
|
||||
|
||||
通过条件:
|
||||
|
||||
- Node inventory 只跑可移植条目
|
||||
- unexpected failure = 0
|
||||
|
||||
4. 扩 browser class coverage
|
||||
|
||||
执行:
|
||||
|
||||
- 先补 TWP representative
|
||||
- 再补 gmoccapy representative
|
||||
- 再补 axis/laser representative
|
||||
- 最后补 macro-only / on_abort representative
|
||||
|
||||
通过条件:
|
||||
|
||||
- `verify_interp_browser.sh` 和 `verify_host_smokes.sh` 仍稳定通过
|
||||
- browser 不引入目录枚举
|
||||
|
||||
5. 更新完成定义和漂移控制
|
||||
|
||||
执行:
|
||||
|
||||
- 更新 `docs/sim-configs-completion-plan.md`
|
||||
- 更新 `docs/compatibility-validation.md`
|
||||
- 更新 `docs/source-reuse-map.md`
|
||||
|
||||
完成判定:
|
||||
|
||||
- `configs/sim` 每个 `.ngc` 都有明确分类;
|
||||
- 每个类别都有合适层级的验证入口;
|
||||
- blocked dependency 清单完整;
|
||||
- Node inventory 可重跑;
|
||||
- browser class coverage 覆盖所有重要 runtime family;
|
||||
- Layer 2 的 8 个 expected_fail 都有稳定归因;
|
||||
- 没有通过 project-owned CNC 语义让测试看起来通过。
|
||||
|
||||
不做事项:
|
||||
|
||||
- 不把 159 个程序全部塞进 browser smoke;
|
||||
- 不把 full-process/HAL/UI/linuxcncrsh 依赖程序误降级为 standalone file execution;
|
||||
- 不修改 `linuxcnc/configs/sim` upstream 文件;
|
||||
- 不通过 JS 解释逻辑、临时字符串匹配、或 standalone `Interp::...` 新语义实现来换取 PASS。
|
||||
248
text4.txt
Normal file
248
text4.txt
Normal file
@@ -0,0 +1,248 @@
|
||||
HAL/UI/full-process standalone/browser boundary design and next plan
|
||||
|
||||
Generated: 2026-06-10 CST
|
||||
|
||||
This plan follows `text3.txt`. The `configs/sim` inventory/matrix work is now
|
||||
complete for the current definition. This file defines the next boundary before
|
||||
any previously blocked HAL/UI/full-process family is promoted into standalone,
|
||||
Node, or browser execution.
|
||||
|
||||
Goal
|
||||
|
||||
- Preserve LinuxCNC as the CNC semantic source of truth.
|
||||
- Make HAL/UI/full-process dependencies machine-readable before attempting
|
||||
execution.
|
||||
- Promote only the subpaths that can run through vendored LinuxCNC source plus
|
||||
explicit standalone runtime adapters.
|
||||
- Keep task, HAL process launch, UI driver process launch, linuxcncrsh, Python
|
||||
remap, and tool-database process behavior blocked until each has a deliberate
|
||||
LinuxCNC-owned runtime boundary.
|
||||
|
||||
Non-goals
|
||||
|
||||
- Do not implement HAL, task, linuxcncrsh, GUI, Python remap, or tool-database
|
||||
semantics in JavaScript.
|
||||
- Do not make blocked `configs/sim` programs pass by editing upstream G-code,
|
||||
changing SDK behavior, or adding project-owned interpreter semantics.
|
||||
- Do not treat native LinuxCNC GUI code as implementation code for the browser
|
||||
UI.
|
||||
|
||||
Current implementation added by this batch
|
||||
|
||||
1. SDK runtime-boundary classifier
|
||||
|
||||
Added:
|
||||
|
||||
```text
|
||||
wasm-port/runtime/sdk/src/sim-config-staging.js
|
||||
analyzeIniRuntimeBoundaries()
|
||||
```
|
||||
|
||||
Exported through:
|
||||
|
||||
```text
|
||||
wasm-port/runtime/sdk/src/index.js
|
||||
```
|
||||
|
||||
The classifier reads INI text, manifest text, source-root metadata, and
|
||||
optional execution text. It reports:
|
||||
|
||||
- declared `HAL` process inputs: `HALFILE`, `HALCMD`, `POSTGUI_HALFILE`,
|
||||
`HALUI`;
|
||||
- declared UI process inputs: `DISPLAY`, `PYVCP`, `GLADEVCP`,
|
||||
`EMBED_TAB_COMMAND`;
|
||||
- `HALUI` MDI command dependencies;
|
||||
- `[EMCIO]DB_PROGRAM` tool-database process dependencies;
|
||||
- Python references in UI, DB, `[PYTHON]`, or Python remap declarations;
|
||||
- `[RS274NGC]USER_M_PATH` plus whether the current execution text calls
|
||||
unstaged external `M100..M199` process codes.
|
||||
|
||||
It returns a policy recommendation for hard Layer 4 blocks:
|
||||
|
||||
- `L4-TOOL-DB` when `[EMCIO]DB_PROGRAM` is present;
|
||||
- `L4-USER-M-PROCESS` when the current execution chain calls unstaged
|
||||
external user-M process codes;
|
||||
- `L4-PYTHON-REMAP` for Python remap runtime declarations;
|
||||
- `-` when the dependency is only a declared process dependency already
|
||||
represented by a narrower standalone adapter or class representative.
|
||||
|
||||
This is policy/accounting only. It does not execute or emulate HAL, task,
|
||||
UI, Python, user-M, or tool-database behavior.
|
||||
|
||||
2. Node coverage for the classifier
|
||||
|
||||
Updated:
|
||||
|
||||
```text
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_wasm.mjs
|
||||
```
|
||||
|
||||
Covered cases:
|
||||
|
||||
- synthetic INI with vendored `M123` user-M file remains unblocked;
|
||||
- `axis/db_demo/db_nonran.ini` reports `L4-TOOL-DB`;
|
||||
- `axis/vismach/millturn/millturn.ini` plus its remap execution chain
|
||||
reports `L4-USER-M-PROCESS` because `M428/M429` call unstaged `M128/M129`;
|
||||
- `axis/vismach/puma/puma_cube.ini` declares HAL/UI/HALUI process
|
||||
dependencies but remains unblocked for the current representative program
|
||||
because the promoted execution path does not call unstaged external
|
||||
user-M process codes;
|
||||
- `axis/gladevcp/gladevcp_panel.ini` declares UI/Python UI process
|
||||
dependencies but remains unblocked for the current `probe.ngc`
|
||||
representative.
|
||||
|
||||
3. Inventory policy guard
|
||||
|
||||
Updated:
|
||||
|
||||
```text
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs
|
||||
```
|
||||
|
||||
The Node inventory now checks vendored INI rows against
|
||||
`analyzeIniRuntimeBoundaries()` before execution/skipping:
|
||||
|
||||
- `L4-TOOL-DB` rows must declare `DB_PROGRAM`;
|
||||
- `L4-USER-M-PROCESS` rows must be backed by an execution chain that calls
|
||||
unstaged external user-M process codes;
|
||||
- generated/tracked blocked policy drift fails the inventory.
|
||||
|
||||
Boundary model
|
||||
|
||||
Layer A: declaration classification
|
||||
|
||||
- Input: INI text, manifest text, optional execution text.
|
||||
- Output: dependency list and recommended blocked kind.
|
||||
- Owner: SDK host-boundary code.
|
||||
- Validation: Node unit/smoke assertions and Node inventory policy checks.
|
||||
|
||||
Layer B: standalone adapter execution
|
||||
|
||||
- Allowed only when a narrow runtime edge already exists and CNC behavior still
|
||||
comes from vendored LinuxCNC source.
|
||||
- Current examples:
|
||||
- HAL named-parameter lookup and switchkins M68/M66 synchronization through
|
||||
the standalone HAL adapter;
|
||||
- deterministic `USER_M_COMMAND` canonical events for already vendored
|
||||
M110/M111-style user-M registration;
|
||||
- five-axis remap execution through vendored LinuxCNC remap/O-word paths;
|
||||
- OPFS/file/parameter/tool-table persistence as host storage boundaries.
|
||||
|
||||
Layer C: browser/UI representation
|
||||
|
||||
- Browser UI may present standalone simulation state and user controls.
|
||||
- Browser UI must not execute native LinuxCNC GUI code.
|
||||
- Browser UI must call SDK/WASM boundaries that are already validated in Node
|
||||
or native probes.
|
||||
- Full LinuxCNC GUI actions remain blocked unless expressed as a deliberate
|
||||
standalone command boundary with LinuxCNC-owned behavior underneath.
|
||||
|
||||
Layer D: full-process blocked boundary
|
||||
|
||||
Keep blocked until explicitly designed:
|
||||
|
||||
- LinuxCNC task process lifecycle;
|
||||
- HAL process/module loading and realtime scheduling;
|
||||
- HALUI/linuxcncrsh command queues;
|
||||
- external userspace components;
|
||||
- native GUI driver processes;
|
||||
- Python remap/prolog/epilog runtime;
|
||||
- tool database process startup and protocol/state behavior.
|
||||
|
||||
Immediate next implementation batches
|
||||
|
||||
Batch 1: make boundary reports durable
|
||||
|
||||
- Add a generated machine-readable boundary artifact:
|
||||
|
||||
```text
|
||||
wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv
|
||||
```
|
||||
|
||||
- Minimum fields:
|
||||
|
||||
```text
|
||||
path
|
||||
ini
|
||||
blocked
|
||||
recommended_blocked
|
||||
dependencies
|
||||
user_m_execution_codes
|
||||
user_m_vendored_count
|
||||
db_program
|
||||
hal_process
|
||||
ui_process
|
||||
halui_mdi_process
|
||||
python_process
|
||||
```
|
||||
|
||||
- The existing Node inventory should fail if `blocked` and
|
||||
`recommended_blocked` conflict for hard blocks.
|
||||
|
||||
Batch 2: promote only safe HAL/UI representatives
|
||||
|
||||
- Keep `axis/gladevcp/probe.ngc`, `woodpecker/on_abort.ngc`,
|
||||
`axis/vismach/puma/puma_cube.ngc`, and `axis/vismach/melfa-sim/example.ngc`
|
||||
as representative file-execution paths.
|
||||
- Add browser assertions that these programs remain file/remap execution
|
||||
representatives and do not claim full UI/HAL process coverage.
|
||||
- Do not promote `axis/vismach/millturn/example.ngc` until the user-M process
|
||||
boundary is designed, because the remap chain calls M128/M129.
|
||||
|
||||
Batch 3: design external user-M process boundary
|
||||
|
||||
- Start with `axis/vismach/millturn` because it is the smallest current hard
|
||||
blocked row with vendored INI and program context.
|
||||
- Required design questions:
|
||||
- Which LinuxCNC source owns M128/M129 behavior in the native config?
|
||||
- Is the behavior expressible as a LinuxCNC-owned deterministic adapter
|
||||
boundary, or does it require spawning external process scripts?
|
||||
- What canonical/state output proves behavior without adding JS CNC
|
||||
semantics?
|
||||
- Do not mark `millturn` unblocked until native, Node, and browser checks can
|
||||
prove the boundary.
|
||||
|
||||
Batch 4: design tool DB boundary
|
||||
|
||||
- Start with `axis/db_demo/db_nonran.ini`.
|
||||
- Required design questions:
|
||||
- Which LinuxCNC source owns `DB_PROGRAM` startup/protocol semantics?
|
||||
- Can a standalone tool database adapter call vendored LinuxCNC tooldata
|
||||
source without reimplementing database behavior?
|
||||
- What fixture demonstrates tool lookup/update behavior across native and
|
||||
WASM?
|
||||
- Keep `axis/db_demo/base.ngc` as `L4-TOOL-DB` until this exists.
|
||||
|
||||
Batch 5: Python remap/full-process family plan
|
||||
|
||||
- Do not vendor or execute `gmoccapy`, `axis/laser`, or TWP nutating Layer 4
|
||||
paths until Python remap runtime ownership is designed.
|
||||
- First artifact should be a dependency inventory, not execution:
|
||||
- Python modules referenced by INI/remap;
|
||||
- prolog/epilog functions;
|
||||
- HAL/task/UI assumptions;
|
||||
- NGC-only subpaths, if any, that can be separated safely.
|
||||
|
||||
Validation gates
|
||||
|
||||
Run after each batch:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
wasm-port/tests/browser/verify_interp_browser.sh
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
wasm-port/tools/verify_vendor_sync.sh
|
||||
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
|
||||
```
|
||||
|
||||
Completion criteria for this boundary phase
|
||||
|
||||
- Every vendored sim-config INI used by Node inventory has a machine-readable
|
||||
runtime-boundary report.
|
||||
- Hard blocked rows have a dependency-backed reason rather than path-only
|
||||
classification.
|
||||
- Safe representatives can declare HAL/UI process dependencies without being
|
||||
mislabeled as full-process coverage.
|
||||
- No blocked family is promoted until a LinuxCNC-owned runtime boundary exists
|
||||
and is validated native, Node, and browser where appropriate.
|
||||
@@ -12,6 +12,12 @@ The primary native validation command is:
|
||||
wasm-port/tests/native/verify_native_probes.sh
|
||||
```
|
||||
|
||||
The native LinuxCNC `nc_files` basic/example validation command is:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/native/verify_nc_files.sh
|
||||
```
|
||||
|
||||
The current WASM smoke validation command is:
|
||||
|
||||
```bash
|
||||
@@ -24,6 +30,24 @@ The current WASM interpreter-core smoke validation command is:
|
||||
wasm-port/tests/wasm/node/verify_interp_wasm.sh
|
||||
```
|
||||
|
||||
The current WASM sim-config smoke validation command is:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
|
||||
```
|
||||
|
||||
The current WASM sim-config inventory validation command is:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
```
|
||||
|
||||
The current WASM `nc_files` smoke validation command is:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/wasm/node/verify_nc_files_wasm.sh
|
||||
```
|
||||
|
||||
The current WASM trajectory-planner smoke validation command is:
|
||||
|
||||
```bash
|
||||
@@ -54,6 +78,341 @@ The current aggregate host/WASM/browser smoke command is:
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
```
|
||||
|
||||
## Validation Layers
|
||||
|
||||
Use these layers when deciding where a LinuxCNC asset belongs. Do not widen a
|
||||
later layer until the narrower layer has either passed or recorded an explicit
|
||||
expected boundary.
|
||||
|
||||
| Layer | Scope | Entry command | Current result | Expected boundary |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | `linuxcnc/nc_files` basic G-code smoke | `wasm-port/tests/native/verify_nc_files.sh` | `total: 107`, `pass: 101`, `expected_fail: 6`, `unexpected_fail: 0` | LinuxCNC-native entry-point/context edges only: W-axis machine context, lathe tool/cutter-comp context, upstream O-word syntax edge, and probe runtime context. |
|
||||
| 2 | `linuxcnc/configs/sim` native strict harness | `wasm-port/tests/native/verify_sim_configs.sh` | `total: 159`, `pass: 151`, `expected_fail: 8`, `unexpected_fail: 0` | Native `bin/rs274` cannot provide task/user-M process execution or some INI axis-mask/runtime context. The upstream `incremental_repetition_g533.ngc` demo remains an expected upstream demo edge and must not be made pass by changing G-code semantics. |
|
||||
| 3 | Standalone native runtime probes | `wasm-port/tests/native/verify_native_probes.sh` | Passes with `native probes complete` after source sync, no-standalone-semantics, fixture baseline, sim-config, and `nc_files` checks | Runtime adapters may cover filesystem, HAL/user-M, remap, parameter, tool, kinematics, TP, and WASM/browser boundary behavior, but CNC semantics must still come from vendored LinuxCNC source. |
|
||||
| 4 | WASM Node/browser representative smoke | `wasm-port/tests/host/verify_host_smokes.sh` plus targeted Node/browser commands below | Current targeted checks pass with `sim_configs_wasm_node_smoke=ok`, `nc_files_wasm_node_smoke=ok`, `browser_interp_smoke=ok`, `browser_ini_opfs_smoke=ok`, and aggregate `host_wasm_opfs_browser_smokes=ok` | JS/browser code may stage files, apply executable bits, persist OPFS text, and forward paths to C ABI calls. It must not implement G-code, remap, tool-table, parameter, planner, kinematics, or user-M semantics. |
|
||||
|
||||
Layer 4 is made of focused smoke commands so failures can be isolated before
|
||||
running the aggregate host check:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/wasm/node/verify_ini_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_interp_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_nc_files_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_tp_wasm.sh
|
||||
wasm-port/tests/opfs/node/verify_file_service.sh
|
||||
wasm-port/tests/browser/verify_ini_panel_browser.sh
|
||||
wasm-port/tests/browser/verify_interp_browser.sh
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
```
|
||||
|
||||
Layer 4 INI-context staging uses `planIniFileContextStaging()` to collect
|
||||
INI-declared `[DISPLAY]OPEN_FILE`, `[EMCIO]TOOL_TABLE`,
|
||||
`[RS274NGC]PARAMETER_FILE`, `[RS274NGC]SUBROUTINE_PATH`,
|
||||
`[RS274NGC]USER_M_PATH`, and remap-NGC files from the vendored source
|
||||
manifest without browser directory enumeration. `planSimConfigStaging()` is the
|
||||
`configs/sim` wrapper around that generic planner. These planners are host
|
||||
filesystem helpers only; execution still goes through vendored LinuxCNC
|
||||
interpreter/remap/tool/parameter code.
|
||||
|
||||
Layer responsibilities are intentionally narrow:
|
||||
|
||||
| Layer | LinuxCNC-owned behavior | Host/WASM adapter allowance | Expected-failure policy |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | `nc_files` parsing and execution through upstream `rs274` | Generate only the native harness INI/tool-table context needed to classify upstream examples | Expected failures document missing machine/task context or known upstream example edges; they must not be made pass by changing G-code semantics. |
|
||||
| 2 | `configs/sim` file execution through upstream `rs274` with sim INI context | Locate the corresponding INI/tool table and classify main programs, macros, and remap subroutines | Expected failures document upstream standalone `rs274` limits such as task/user-M process edges and the preserved `incremental_repetition_g533.ngc` demo edge. |
|
||||
| 3 | Vendored LinuxCNC interpreter, remap, tool, parameter, kinematics, and planner code | Provide deterministic runtime shims for filesystem, HAL/user-M boundaries, machine status, and build/source-probe coverage | A standalone pass may cover a runtime edge that Layer 1/2 cannot provide, but it must still call vendored LinuxCNC source for CNC behavior. |
|
||||
| 4 | Vendored LinuxCNC C ABI behavior from generated WASM modules | Stage files, apply executable bits, persist OPFS text, generate manifest-based staging plans, and forward paths/results between JS/browser and C ABI | A WASM/browser pass proves the host boundary can reproduce the staged LinuxCNC context; it does not replace native source validation or justify JS-owned CNC semantics. |
|
||||
|
||||
## Layer 2 Expected Failures
|
||||
|
||||
The current `wasm-port/tests/native/verify_sim_configs.sh` run reports
|
||||
`total: 159`, `pass: 151`, `expected_fail: 8`, and `unexpected_fail: 0`.
|
||||
The eight expected failures are listed in the table below.
|
||||
|
||||
The tracked per-program inventory for this layer now lives in
|
||||
`wasm-port/docs/sim-configs-coverage-matrix.md`. That matrix maps all 159
|
||||
current `summary.tsv` records to program class, native result, current
|
||||
Layer 3/4 coverage, and the first-pass blocked or follow-up note used for
|
||||
future `configs/sim` coverage work.
|
||||
|
||||
The native harness also emits machine-readable derived artifacts without
|
||||
changing the `summary.tsv` schema:
|
||||
|
||||
```text
|
||||
wasm-port/build/native/sim-configs/class-summary.tsv
|
||||
wasm-port/build/native/sim-configs/path-matrix.tsv
|
||||
```
|
||||
|
||||
`class-summary.tsv` aggregates class/status/expected-failure counts.
|
||||
`path-matrix.tsv` records each path, class, native status, expected-failure
|
||||
reason, INI, tool table, runtime family, and blocked kind for CI and Node
|
||||
inventory reconciliation.
|
||||
|
||||
The Node inventory layer writes its own machine-readable artifacts:
|
||||
|
||||
```text
|
||||
wasm-port/build/wasm/sim-configs-inventory/summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/skip-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/blocked-dependency-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/full-process-boundary-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/user-m-process-state-targets.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/user-m-process-native-state-alignment.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/tool-db-process-protocol-gates.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/tool-db-process-native-protocol-alignment.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/python-remap-runtime-gates.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-alignment.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-readiness.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-state-plan.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-fixture-plan.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/python-remap-family-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/boundary-phase-completion-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/native-proof-alignment-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-native-alignment-summary.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/blocked-runtime-promotion-lock.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/next-boundary-worklist.tsv
|
||||
wasm-port/build/wasm/sim-configs-inventory/boundary-proof-gates.tsv
|
||||
```
|
||||
|
||||
The current skip/block summary is `ASSET-ONLY=65`, `L4-PYTHON-REMAP=53`,
|
||||
`L4-TOOL-DB=1`, `L4-USER-M-PROCESS=1`, `NON_MAIN_CLASS=10`, and
|
||||
`UPSTREAM-DEMO=1`.
|
||||
`skip-summary.tsv` is checked against the skip reasons derived from
|
||||
`path-matrix.tsv`, so both the per-row inventory status and the aggregate
|
||||
skip counts fail on blocked-policy drift.
|
||||
For eligible rows, missing vendored machine context is an inventory failure,
|
||||
not an expected skip; blocked rows must be classified by runtime dependency
|
||||
before the Node inventory filter runs.
|
||||
`boundary-summary.tsv` records one row per native inventory path with the
|
||||
matrix blocked kind, SDK classifier recommendation, declared HAL/UI/HALUI/Python
|
||||
process dependencies, `[EMCIO]DB_PROGRAM`, user-M execution codes, and the
|
||||
subset of execution user-M codes that are not staged by vendored
|
||||
`USER_M_PATH` files. It also separates Python UI/DB process dependencies from
|
||||
Python remap runtime dependencies. For vendored INI rows, the SDK classifier
|
||||
report must be available and `L4-TOOL-DB` / `L4-USER-M-PROCESS` hard blocks
|
||||
must match the classifier recommendation. `recommended_blocked=UNAVAILABLE` is
|
||||
only allowed when the boundary row records a missing vendored INI. Non-vendored
|
||||
Python-remap families remain unavailable until their dependency inventory batch
|
||||
vendors the required INI context.
|
||||
`ini-boundary-summary.tsv` aggregates those path-level reports by INI, giving
|
||||
vendored sim-config INIs a direct `report_available=1` coverage check and
|
||||
recording hard-block recommendation alignment at INI granularity.
|
||||
The inventory also guards safe representative rows such as
|
||||
`axis/gladevcp/probe.ngc`, `woodpecker/on_abort.ngc`,
|
||||
`axis/vismach/puma/puma_cube.ngc`, and
|
||||
`axis/vismach/melfa-sim/example.ngc`: they must keep their declared process
|
||||
dependencies in `boundary-summary.tsv`, remain Node/browser representatives,
|
||||
match the expected HAL/UI/HALUI/Python process flags, and avoid hard-block
|
||||
promotion unless a real hard runtime dependency appears.
|
||||
Deterministic `M110`/`M111` representatives are also guarded: the inventory
|
||||
must see the execution-chain user-M code, a vendored user-M file, and no hard
|
||||
`L4-USER-M-PROCESS` recommendation. `opa_demo.ngc` includes its vendored
|
||||
`circles.ngc` subroutine text in the boundary analysis so the `M111` call is
|
||||
accounted for even though it is reached through `SUBROUTINE_PATH`.
|
||||
`blocked-dependency-summary.tsv` records the hard blocked rows without
|
||||
promoting them: 53 Python-remap rows, one tool-database row, and one external
|
||||
user-M process row. It reads the source `linuxcnc/configs/sim` INI files for
|
||||
dependency accounting only and records Python modules, remap/prolog/epilog
|
||||
function ownership, NGC remap subpaths, HAL/UI/HALUI process declarations,
|
||||
`DB_PROGRAM`, tool database protocol evidence, external user-M execution
|
||||
codes, user-M process script files, user-M process side-effect evidence, and
|
||||
the LinuxCNC source/config files that own the blocked behavior. The user-M
|
||||
evidence is source-derived from the `M128`/`M129` Tcl scripts and records their
|
||||
Tcl/HAL runtime use, kinstype guard, and `ini.[xyz]` HAL pin updates. The tool
|
||||
database evidence is source-derived from `taskclass.cc`, `tooldata_db.cc`, and
|
||||
`axis/db_demo/db.py`, including the `v2.1` handshake, `g`/`FINI` get-all, and
|
||||
`l`/`u`/`p` notification protocol.
|
||||
The final Node inventory `summary.tsv` also guards every hard-blocked row:
|
||||
`L4-TOOL-DB`, `L4-USER-M-PROCESS`, and `L4-PYTHON-REMAP` paths must remain
|
||||
`SKIP` with their matching blocked reason, not only carry a matrix-level
|
||||
blocked label.
|
||||
The detailed boundary design for the current non-Python hard blocks is tracked
|
||||
in `wasm-port/docs/full-process-boundary-design.md`. That document records the
|
||||
LinuxCNC owner sets and proof required before `axis/vismach/millturn/example.ngc`
|
||||
or `axis/db_demo/base.ngc` can move out of `L4-USER-M-PROCESS` or
|
||||
`L4-TOOL-DB`. It is not an execution artifact and does not change the current
|
||||
Layer 4 blocked counts.
|
||||
`full-process-boundary-summary.tsv` is the machine-readable companion for that
|
||||
design record. It has one designed-but-blocked row for `millturn` and one for
|
||||
`db_demo`, records the LinuxCNC runtime owner evidence from the corresponding
|
||||
blocked dependency row, records the required native/Node/browser proof, and
|
||||
keeps `execution_enabled=0` until the corresponding LinuxCNC-owned runtime
|
||||
boundary exists. The tracked matrix and inventory guard also require those two
|
||||
rows to remain non-representative in Node/browser until that proof exists.
|
||||
`user-m-process-state-targets.tsv` expands the `millturn` M128/M129 process
|
||||
boundary into per-pin proof targets: two user-M codes, three axes, and four
|
||||
`ini.[xyz]` HAL pins per axis. Each row records the source Tcl file, remap
|
||||
caller, kinstype guard, INI source section/field, expected value,
|
||||
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`user-m-process-native-state-alignment.tsv` then aligns each generated pin
|
||||
target with the native source probe stdout key/value pair, for example
|
||||
`M128_X_AXIS_X.MIN_LIMIT_ok=1`. This catches drift between generated target
|
||||
rows and the LinuxCNC-owned native source proof without executing Tcl/HAL or
|
||||
allowing promotion.
|
||||
`user-m-process-native-transition-alignment.tsv` performs the same source
|
||||
alignment for the `M428/M429` transition plan. It checks the native probe
|
||||
stdout for `motion.analog-out-03`, kinstype targets `0` and `1`,
|
||||
`G59.1`/`G59.2`, `P7`/`P8`, and the `M428 -> M128` / `M429 -> M129` calls.
|
||||
Those rows also remain `proof_status=pending`, `execution_enabled=0`, and
|
||||
`promotion_allowed=0`.
|
||||
`user-m-process-native-runtime-state-plan.tsv` is the runnable native probe
|
||||
contract that follows those source-alignment tables. It names the required
|
||||
LinuxCNC task/HAL/Tcl user-M process runtime, the required HAL/INI
|
||||
environment, and the exact `ini.[xyz].*` state values that a future native
|
||||
runtime probe must record after `M428` and `M429`. It remains a plan only with
|
||||
`native_runtime_status=pending_native_hal_tcl_process_probe`,
|
||||
`execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`user-m-process-native-runtime-readiness.tsv` is the host capability gate for
|
||||
that future probe. It records availability of `tclsh`, `halrun`, `halcmd`, and
|
||||
`linuxcnc`, captures PATH evidence for available commands, and leaves
|
||||
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`user-m-process-native-runtime-probe-gate.tsv` combines that readiness with the
|
||||
source/state proof for `M428/M128` and `M429/M129`. The native
|
||||
`probe_millturn_user_m_runtime.sh` entry point is wired through
|
||||
`build_native_probes.sh`; without the full LinuxCNC HAL/Tcl command set it
|
||||
reports `skipped_missing_host_runtime`, and with the runtime present it remains
|
||||
disabled by default. When explicitly enabled with
|
||||
`ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1`, it starts the vendored `millturn.ini`,
|
||||
runs the LinuxCNC-owned Tcl `M128`/`M129` scripts, verifies the expected
|
||||
`ini.[xyz].*` HAL pin state, and reports `runtime_state_probe_passed` without
|
||||
enabling promotion.
|
||||
`tool-db-process-protocol-gates.tsv` expands the `db_demo` tool-database
|
||||
boundary into pending protocol, callback, and state gates. It records the
|
||||
LinuxCNC `v2.1`, `g`, `l`, `u`, and `p` protocol requirements, the demo DB
|
||||
callbacks, DB mode state targets such as ignored `TOOL_TABLE`, `T10..T19`,
|
||||
`tno+100` pockets, and OPFS/host persistence boundaries, while keeping
|
||||
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`tool-db-process-native-protocol-alignment.tsv` aligns each generated DB gate
|
||||
with the native source probe stdout proof keys, including the protocol
|
||||
handshake, get-all, notifications, callback registration, and nonrandom state
|
||||
targets. This is still dependency/proof accounting only: it does not spawn
|
||||
`DB_PROGRAM`, emulate the tool database protocol in JavaScript, or fall back to
|
||||
a `.tbl` file.
|
||||
`tool-db-process-native-runtime-readiness.tsv` records the host prerequisites
|
||||
for the guarded DB process protocol probe: `python3`, `linuxcnc`, `milltask`,
|
||||
`halcmd`, the configured `db_nonran.py` executable, and LinuxCNC's Python
|
||||
`linuxcnc.so` / `tooldb.py` modules. It captures availability evidence while
|
||||
keeping `proof_status=pending`, `execution_enabled=0`, and
|
||||
`promotion_allowed=0`. When explicitly enabled with
|
||||
`ENABLE_TOOL_DB_RUNTIME_PROBE=1`, `probe_tool_db_runtime.sh` starts the
|
||||
vendored `DB_PROGRAM`, drives the LinuxCNC `tooldb.py` `v2.1`/`g`/`p`/`l`/`u`
|
||||
protocol, verifies nonrandom startup/update/load/unload state and persistence,
|
||||
and reports `runtime_protocol_probe_passed` without enabling promotion.
|
||||
`python-remap-boundary-summary.tsv` is the machine-readable companion for the
|
||||
Python remap inventory batch. It has one inventory-only row for each
|
||||
`L4-PYTHON-REMAP` path, records Python modules, remap/prolog/epilog functions,
|
||||
NGC-only subpaths, HAL/UI/HALUI assumptions, LinuxCNC Python runtime owner
|
||||
evidence from `interp_python.cc` and `python_plugin.cc`, and keeps
|
||||
`execution_enabled=0` until a LinuxCNC-owned Python runtime boundary exists.
|
||||
`python-remap-runtime-gates.tsv` expands those rows into Python module,
|
||||
remap/prolog/epilog callable, NGC-only subpath, process-assumption, and
|
||||
runtime-owner gates, all with `proof_status=pending`,
|
||||
`execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`python-remap-native-runtime-alignment.tsv` aligns every generated Python gate
|
||||
with native source proof: exact runtime owner gates use keys such as
|
||||
`python_runtime_pycall_dispatch`, while dependency gates use representative
|
||||
family inventory proof or the aggregate
|
||||
`python_remap_native_source_inventory_proof`. It does not initialize Python,
|
||||
import modules, execute callbacks, or promote browser/Node coverage.
|
||||
`python-remap-native-runtime-readiness.tsv` records the native runtime
|
||||
prerequisites for guarded probes by family: `python3`, `linuxcnc`,
|
||||
LinuxCNC's `interp_python.cc` / `python_plugin.cc` owner source files, and the
|
||||
configured Python modules. It is a readiness gate only and keeps every row
|
||||
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`python-remap-native-runtime-state-plan.tsv` records the next native runtime
|
||||
probe target set by family: LinuxCNC Python phases, configured modules,
|
||||
callables, NGC-only subpaths, process assumptions, readiness counts, and
|
||||
source-alignment artifacts. It remains a plan only and does not initialize
|
||||
Python, import modules, execute callbacks, or permit promotion.
|
||||
`python-remap-native-runtime-fixture-plan.tsv` selects the first minimal
|
||||
Python runtime lifecycle fixture, `axis/remap/stop-lookahead/nc_files`, because
|
||||
it exercises Python runtime phases and configured modules without Python
|
||||
callable or NGC-only subpath complexity. It is still a fixture plan only:
|
||||
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
|
||||
When explicitly enabled with `ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1`,
|
||||
`probe_python_remap_runtime.sh` follows the fixture `demo.ini` Python path and
|
||||
toplevel declarations, imports the vendored modules, verifies `queuebuster`
|
||||
callable lookup, generator return, and first `INTERP_EXECUTE_FINISH` yield, and
|
||||
reports `runtime_lifecycle_probe_passed` without promoting Python-remap
|
||||
execution.
|
||||
`python-remap-family-summary.tsv` aggregates the same blocked rows by runtime
|
||||
family, preserving row and INI counts plus family-level Python module,
|
||||
remap/prolog/epilog, NGC-only subpath, Python runtime owner evidence,
|
||||
HAL/UI/HALUI, and `execution_enabled=0` evidence. It is dependency inventory
|
||||
only and must not be used to promote Python-remap execution.
|
||||
`boundary-phase-completion-summary.tsv` records the current boundary-phase
|
||||
completion criteria as machine-readable checks: vendored INI boundary report
|
||||
coverage, hard-block dependency evidence, safe HAL/UI representative coverage,
|
||||
proof that blocked families remain skipped/non-representative with execution
|
||||
disabled, and proof that blocked runtime families carry LinuxCNC-owned user-M,
|
||||
tool DB, and Python runtime evidence fields. It also records native source
|
||||
proof alignment when the native proof summary is available, plus the aggregate
|
||||
native stdout alignment summary for user-M, tool DB, and Python runtime gates.
|
||||
`native-proof-alignment-summary.tsv` aligns native source proof rows with the
|
||||
generated worklist and native proof gates for user-M, tool DB, and Python
|
||||
runtime blockers. It is proof-consumption accounting only and keeps execution
|
||||
and promotion disabled.
|
||||
`runtime-boundary-native-alignment-summary.tsv` summarizes the three detailed
|
||||
native alignment artifacts. It requires every alignment row to have native
|
||||
stdout evidence, `alignment_ok=1`, `proof_status=pending`,
|
||||
`execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`blocked-runtime-promotion-lock.tsv` combines the next-boundary worklist,
|
||||
native/Node/browser proof gates, and runtime native alignment summary into one
|
||||
promotion lock per blocked runtime target. A lock row is active only while
|
||||
every proof layer is still pending, execution and promotion remain disabled,
|
||||
and the matching runtime alignment artifact is complete.
|
||||
`next-boundary-worklist.tsv` records the next blocked runtime-boundary design
|
||||
targets in priority order. It starts with the designed-but-disabled
|
||||
`millturn` external user-M process and `db_demo` tool database process
|
||||
boundaries, then lists Python runtime families from the inventory. Every row
|
||||
keeps `execution_enabled=0` and `promotion_allowed=0`, records the LinuxCNC
|
||||
owner set and runtime owner evidence, records required native, Node, and
|
||||
browser proof, and names the next boundary-design action before promotion.
|
||||
`boundary-proof-gates.tsv` expands that worklist into one pending native,
|
||||
Node, and browser proof gate per target. It is a promotion guard only: current
|
||||
rows keep `proof_status=pending`, `execution_enabled=0`, and
|
||||
`promotion_allowed=0`; user-M gates must require HAL pin state rather than
|
||||
event-only proof, tool-DB gates must require protocol proof rather than a
|
||||
`.tbl` fallback, and Python gates must require a LinuxCNC-owned Python runtime
|
||||
boundary rather than JavaScript semantics.
|
||||
The browser interpreter smoke reads the browser-layer rows from this artifact
|
||||
before running safe representatives, so `millturn`, `db_demo`, and Python
|
||||
runtime families cannot be accidentally treated as browser/full-process
|
||||
coverage while their proof gates remain pending.
|
||||
|
||||
Current `configs/sim` class taxonomy:
|
||||
|
||||
| Class | Meaning | Current policy |
|
||||
| --- | --- | --- |
|
||||
| `main` | A complete standalone-executable sim-config program. | Eligible for native inventory; promote to Layer 3/4 when the required runtime boundary exists. |
|
||||
| `macro_load` | A macro or load/parse asset that is not the primary machine program entry point. | Keep in native inventory, but do not treat it as a browser main-program target by default. Add class-level load/parse representatives instead of widening browser execution blindly. |
|
||||
| `remap_subroutine` | A remap or subroutine asset under `remap_subs/` or similar directories. | Validate through remap parse/execute paths, not by pretending it is a standalone browser main program. |
|
||||
|
||||
Current `configs/sim` blocked policy:
|
||||
|
||||
| Blocked kind | Meaning | Current examples |
|
||||
| --- | --- | --- |
|
||||
| `ASSET-ONLY` | The file is a macro/remap asset or subroutine and is not a standalone browser main-program target. | `configs/sim/*/remap_subs/*.ngc` entries in the matrix. |
|
||||
| `L4-TOOL-DB` | Native LinuxCNC `rs274` coverage exists, but Node/browser inventory is blocked by LinuxCNC tool-database process boundaries such as `[EMCIO]DB_PROGRAM`. | `configs/sim/axis/db_demo/*`. |
|
||||
| `L4-USER-M-PROCESS` | Native LinuxCNC `rs274` coverage exists, but Node/browser inventory is blocked because the config depends on external `USER_M_PATH` process execution rather than the deterministic `M110`/`M111` boundary already modeled by the standalone runtime. | `configs/sim/axis/vismach/millturn/*`. |
|
||||
| `L4-PYTHON-REMAP` | Native LinuxCNC `rs274` coverage exists, but full Node/browser inventory is blocked until an intentional Python-remap runtime boundary is exposed for Layer 4. | `configs/sim/gmoccapy/*`, `configs/sim/axis/laser/*`, `configs/sim/axis/remap/*/nc_files/*.ngc`, `configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/*`, and `configs/sim/axis/vismach/VMC_toolchange/toolchange.ngc`. |
|
||||
| `UPSTREAM-DEMO` | A preserved upstream demo edge that should remain an expected failure instead of being forced through standalone semantics. | `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc`. |
|
||||
|
||||
| Program | Layer 2 classification | Current Layer 3/4 coverage or boundary |
|
||||
| --- | --- | --- |
|
||||
| `axis/foam/foam.ngc` | `ini-axis-mask-UV`; native upstream `bin/rs274` rejects `U`/`V` words before the standalone runtime applies the INI machine axis mask. | Covered by `wasm-port/tests/native/verify_native_probes.sh`, `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh`, and `wasm-port/tests/browser/verify_interp_browser.sh`, which run the vendored file with `axis_foam.ini` and verify `U/V` axis acceptance through LinuxCNC-backed file execution. |
|
||||
| `axis/vismach/5axis/bridgemill/5axisgui.ngc` | `ini-axis-mask-W`; native upstream `bin/rs274` rejects the `W` word outside the bridge-mill runtime context. | Covered by `wasm-port/tests/native/verify_native_probes.sh`, `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh`, and `wasm-port/tests/browser/verify_interp_browser.sh`, which stage `5axis.ini`, the tool table, and remap subroutines, then execute through the vendored LinuxCNC remap/file path. |
|
||||
| `axis/geometry/xyzc.ngc` | `user-m-code-M110`; native upstream `bin/rs274` does not register or execute the sim-config `USER_M_PATH` handler. | Covered by `wasm-port/tests/native/verify_native_probes.sh`, `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh`, and `wasm-port/tests/browser/verify_interp_browser.sh`; the smoke assertions require `canon_event=USER_M_COMMAND code=M110` and reject `Unknown m code used: M110`. |
|
||||
| `axis/external_offsets/dyn_demo.ngc` | `user-m-code-M111`; native upstream `bin/rs274` does not register or execute the sim-config `USER_M_PATH` handler. | Covered by `wasm-port/tests/native/verify_native_probes.sh`, `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh`, and `wasm-port/tests/browser/verify_interp_browser.sh`; the smoke assertions require `canon_event=USER_M_COMMAND code=M111` and reject `Unknown m code used: M111`. |
|
||||
| `axis/external_offsets/eoffsets.ngc` | `user-m-code-M111`; same standalone task/user-M boundary as `dyn_demo.ngc`. | Covered by `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh` and `wasm-port/tests/browser/verify_interp_browser.sh` with the vendored executable `M111` and `eoffset.tbl` staged from the manifest. |
|
||||
| `axis/external_offsets/jwp_z.ngc` | `user-m-code-M111`; same standalone task/user-M boundary as `dyn_demo.ngc`. | Covered by `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh` and `wasm-port/tests/browser/verify_interp_browser.sh` with the vendored executable `M111` and `eoffset.tbl` staged from the manifest. |
|
||||
| `axis/external_offsets/opa_demo.ngc` | `user-m-code-M111`; same standalone task/user-M boundary as `dyn_demo.ngc`, plus a subroutine dependency. | Covered by `wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh` and `wasm-port/tests/browser/verify_interp_browser.sh`, which stage `M111`, `eoffset.tbl`, and the `SUBROUTINE_PATH` dependency `circles.ngc`. |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc` | `upstream-demo-missing-motion-gcode`; upstream standalone `rs274` reports `Cannot use axis values without a g code that uses them`. | Preserved as an upstream demo expected failure. Do not make this pass by editing the G-code, changing JS interpreter behavior, or adding standalone semantics. |
|
||||
|
||||
## Validation Chain
|
||||
|
||||
The native validation script runs these checks in order:
|
||||
@@ -74,10 +433,58 @@ The native validation script runs these checks in order:
|
||||
5. `tools/build_native_probes.sh`
|
||||
Builds native source probes and standalone harnesses from vendored
|
||||
LinuxCNC source plus narrow runtime wrappers.
|
||||
6. `tests/native/verify_native_probes.sh`
|
||||
6. `tests/native/verify_sim_configs.sh`
|
||||
Runs LinuxCNC `configs/sim` `.ngc` programs through the upstream
|
||||
`../linuxcnc/bin/rs274` standalone entry point, using nearest or explicit
|
||||
sim INI/tool-table mappings and classifying main programs, macro-load
|
||||
checks, and remap subroutines separately.
|
||||
7. `tests/native/verify_nc_files.sh`
|
||||
Runs the basic/example subset of upstream `linuxcnc/nc_files` through
|
||||
`../linuxcnc/bin/rs274`, with complete programs executed strictly and
|
||||
macro/library files wrapped only for load/parse validation.
|
||||
8. `tests/native/verify_native_probes.sh`
|
||||
Checks probe exit codes, source-probe coverage, harness stdout, canonical
|
||||
fixture events, and expected error behavior.
|
||||
|
||||
Current `nc_files` basic-suite baseline:
|
||||
|
||||
```text
|
||||
total: 107
|
||||
pass: 101
|
||||
fail: 6
|
||||
timeout: 0
|
||||
expected_fail: 6
|
||||
unexpected_fail: 0
|
||||
```
|
||||
|
||||
The six expected failures are LinuxCNC-native entry-point/context edges:
|
||||
`cone.ngc` needs a W-axis 5-axis machine context; `g76.ngc` and
|
||||
`lathe-g76.ngc` need lathe cutter-compensation/tool context;
|
||||
`lathe_g70_71_demo.ngc` needs lathe profile/tool context; `nestedcall.ngc`
|
||||
uses an upstream O-word `callsub` syntax edge; and `tool-length-probe.ngc`
|
||||
needs probe runtime context.
|
||||
|
||||
The same runner also supports exploratory full-directory inventory:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/native/verify_nc_files.sh --all
|
||||
```
|
||||
|
||||
Current full-directory `nc_files` inventory:
|
||||
|
||||
```text
|
||||
total: 247
|
||||
pass: 219
|
||||
fail: 28
|
||||
timeout: 0
|
||||
expected_fail: 28
|
||||
unexpected_fail: 0
|
||||
```
|
||||
|
||||
The additional expected failures are probe/plasmac runtime-context files and
|
||||
one NURBS sample using `G2.2`, which the current upstream `bin/rs274`
|
||||
standalone entry point reports as `Unknown g code used`.
|
||||
|
||||
The WASM INI smoke script builds `runtime/ui/ini-panel/linuxcnc_ini.js` and
|
||||
`linuxcnc_ini.wasm` from vendored LinuxCNC `inifile.cc`, then loads that
|
||||
module through `runtime/sdk/src/index.js` in Node and verifies INI string and
|
||||
@@ -104,13 +511,20 @@ It also calls vendored `Interp::init_named_parameters()` and
|
||||
`Interp::find_named_param()` through the WASM C ABI to validate the native
|
||||
named-parameter harness path for LinuxCNC built-in, INI-backed, HAL-backed,
|
||||
and missing named-parameter lookup.
|
||||
It also writes selected G-code fixtures into the
|
||||
Emscripten filesystem through the SDK and runs them through LinuxCNC
|
||||
`Interp::open()`, `Interp::read()`, and `Interp::execute()` to validate the
|
||||
file execution path. It also writes the vendored LinuxCNC `xyzac-trt` and
|
||||
It also writes selected G-code fixtures and vendored `tests/interp/g10`
|
||||
regressions into the Emscripten filesystem through the SDK and runs them
|
||||
through LinuxCNC `Interp::open()`, `Interp::read()`, and `Interp::execute()`
|
||||
to validate the file execution path, including G10 tool-table updates,
|
||||
tool-length offset application timing, active G5X offset changes, G92
|
||||
interaction, XY-rotation behavior, G52/G92 shared-offset transitions, and
|
||||
G92 startup parameter-file persistence through vendored
|
||||
`ini_load()`/`Interp::init()`/`restore_parameters()`, including
|
||||
`DISABLE_G92_PERSISTENCE` clearing parameters 5210-5219. It also writes the vendored LinuxCNC `xyzac-trt` and
|
||||
`xyzbc-trt` table-rotary-tilting sample machine INI, `remap_subs/*.ngc`, and
|
||||
`xyzac_switchkins.ngc`/`xyzbc_switchkins.ngc`, `boat-xyzac.ngc`,
|
||||
`boat-xyzbc.ngc`, and `impeller-7bl-xyzac.ngc` demo files into the Emscripten
|
||||
`xyzac_switchkins.ngc`/`xyzbc_switchkins.ngc`,
|
||||
`xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_2.ngc`,
|
||||
`xyzac_switchkins_test_3.ngc`, `boat-xyzac.ngc`, `boat-xyzbc.ngc`, and
|
||||
`impeller-7bl-xyzac.ngc` demo files into the Emscripten
|
||||
filesystem. It also writes the vendored LinuxCNC `xyzab-tdr` table-dual-rotary
|
||||
INI, tool table, `remap_subs/*.ngc`, and `xyzab-tdr-demo.ngc` into the
|
||||
Emscripten filesystem. It also writes the vendored LinuxCNC bridge-mill
|
||||
@@ -119,7 +533,53 @@ Emscripten filesystem. These checks validate the WASM C ABI/SDK path for
|
||||
vendored LinuxCNC `REMAP` parsing, vendored LinuxCNC tool-table loading from
|
||||
the machine INI, O-word remap execution, `M68`/`M66` HAL synchronization, and
|
||||
file `open()`/`read()`/`execute()` completion without JavaScript M-code or
|
||||
kinematics semantics. The same Node smoke writes
|
||||
kinematics semantics. The same Node smoke writes the vendored LinuxCNC
|
||||
`tests/remap/duplicate-o-word`, NGC-only `tests/remap/fail/args.0`,
|
||||
`tests/remap/fail/args.1`, `tests/remap/fail/args.2`,
|
||||
`tests/remap/fail/body-ngc`, `tests/remap/m30-interaction`,
|
||||
`tests/remap/nested-remaps-oword`, `tests/remap/posargs.0`, and
|
||||
`tests/remap/sequencing` INI, program, and remap subroutines into the
|
||||
Emscripten filesystem and validates those
|
||||
upstream NGC remap regressions through the generic `runRemapFile()` C ABI/SDK
|
||||
path, which only reads LinuxCNC INI `SUBROUTINE_PATH`, `REMAP`, and
|
||||
`OWORD_NARGS` entries and calls vendored `Interp::parse_remap()`, `open()`,
|
||||
`read()`, and `execute()`. The NGC-only failure cases that match upstream
|
||||
`rs274 -n 0` flow use the sibling `runRemapFileContinueOnError()` C ABI/SDK
|
||||
path, which records LinuxCNC error text and continues the same vendored
|
||||
`open()`/`read()`/`execute()` loop without implementing failure semantics in
|
||||
JavaScript. The same Node smoke also writes the NGC-only LinuxCNC
|
||||
`tests/remap/remap-io/test-ngc.ini` and `io_*.ngc` subroutines into the
|
||||
Emscripten filesystem, then calls `runRemapIoMdiSequence()` to feed the
|
||||
upstream test-driver MDI sequence into vendored `Interp::execute()`. The SDK
|
||||
does not implement M62-M68, M66 input, or REMAP semantics; the standalone
|
||||
boundary only pre-seeds deterministic external input values and captures
|
||||
LinuxCNC canonical events. The same WASM interpreter smoke also writes minimal `M110` and
|
||||
`M111` fixtures into the Emscripten filesystem, marks the `M1xx` files
|
||||
executable, and validates that the standalone machine-config boundary mirrors
|
||||
LinuxCNC task-layer `[DISPLAY]PROGRAM_PREFIX` plus `[RS274NGC]USER_M_PATH`
|
||||
lookup by registering `USER_DEFINED_FUNCTION` entries and recording
|
||||
deterministic `USER_M_COMMAND` events instead of spawning host processes. The
|
||||
same Node smoke writes the vendored LinuxCNC
|
||||
`tests/interp/do-while-break`, `tests/interp/oword-bug315`,
|
||||
`tests/interp/oword-bug315-p2`, `tests/interp/exists`,
|
||||
`tests/interp/return-value`, `tests/interp/subs-follow-main`,
|
||||
`tests/interp/fractional-linenumbers`, `tests/interp/cam-nisley` with its
|
||||
upstream `test.tbl`, `tests/interp/namedparam-bug424`,
|
||||
selected `tests/interp/rotation` pure interpreter cases, `tests/interp/iniparam`,
|
||||
`tests/interp/iniparam-failassign`,
|
||||
`tests/interp/sub-call-from-sub`, `tests/interp/sequence-number`, and
|
||||
`tests/interp/nested-sub-error`, `tests/interp/nested-sub-in-file-error`, and
|
||||
`tests/interp/abort-hot-comment`
|
||||
files into the Emscripten filesystem and validates those upstream interpreter
|
||||
regressions through `runFile()` or
|
||||
`runFileWithIni()`, which only reads LinuxCNC INI `SUBROUTINE_PATH` where
|
||||
needed and calls vendored LinuxCNC `Interp::open()`, `read()`, and `execute()`.
|
||||
The `iniparam` fixtures validate vendored LinuxCNC `_ini[...]` lookup, missing
|
||||
INI-parameter error text, and read-only named-parameter assignment rejection
|
||||
through the `INI_FILE_NAME` runtime edge. It checks LinuxCNC canonical messages,
|
||||
canonical motion/events, and final interpreter state without JavaScript O-word,
|
||||
subroutine lookup, INI-variable, or read-only-parameter semantics. The
|
||||
same Node smoke writes
|
||||
LinuxCNC-format parameter files into the Emscripten filesystem and validates
|
||||
vendored `Interp::restore_parameters()` and `Interp::save_parameters()`,
|
||||
including the saved parameter values, missing required numeric parameter
|
||||
@@ -203,14 +663,64 @@ override INI-derived names before OPFS text is copied into the LinuxCNC-backed
|
||||
WASM filesystem, and that absent INI file-name values use the default OPFS
|
||||
parameter/tool-table paths in a real browser session. It also writes vendored
|
||||
LinuxCNC `xyzac-trt`/`xyzbc-trt` INI, remap subroutines, and switchkins demo
|
||||
files, including the larger `boat-xyzac.ngc`, `boat-xyzbc.ngc`, and
|
||||
`impeller-7bl-xyzac.ngc` demos, into the browser WASM filesystem. It also
|
||||
files, including `xyzac_switchkins_test_1.ngc`,
|
||||
`xyzac_switchkins_test_2.ngc`, `xyzac_switchkins_test_3.ngc`, and the larger
|
||||
`boat-xyzac.ngc`, `boat-xyzbc.ngc`, and `impeller-7bl-xyzac.ngc` demos, into
|
||||
the browser WASM filesystem. It also
|
||||
writes vendored LinuxCNC `xyzab-tdr` machine files and `xyzab-tdr-demo.ngc`
|
||||
into the browser WASM filesystem. It also writes vendored LinuxCNC bridge-mill
|
||||
machine files and `5axisgui.ngc` into the browser WASM filesystem. The browser
|
||||
smoke verifies `runFiveAxisRemapFile()` for the table-rotary-tilting,
|
||||
table-dual-rotary, and bridge-mill sample machines through the exported
|
||||
LinuxCNC remap/tool-table/file execution path.
|
||||
LinuxCNC remap/tool-table/file execution path. It also verifies the vendored
|
||||
LinuxCNC `configs/sim/axis/foam/foam.ngc`, `axis/geometry/xyzc.ngc`, and
|
||||
`axis/external_offsets/dyn_demo.ngc` programs through `runSimConfigProgram()`,
|
||||
which writes the browser WASM filesystem files and forwards to
|
||||
`runFileWithIni()`. It also verifies the bridge-mill
|
||||
`axis/vismach/5axis/bridgemill/5axisgui.ngc` program through
|
||||
`runSimConfigProgram()` with `executionMode: "fiveAxisRemap"`, which forwards
|
||||
to `runFiveAxisRemapFile()`. These paths use the real vendored executable
|
||||
`M110` and `M111` files for `USER_M_PATH` registration without spawning host
|
||||
processes. It
|
||||
also verifies the vendored
|
||||
LinuxCNC `tests/remap/duplicate-o-word`, NGC-only `tests/remap/fail/args.0`,
|
||||
`tests/remap/fail/args.1`, `tests/remap/fail/args.2`,
|
||||
`tests/remap/fail/body-ngc`, `tests/remap/m30-interaction`,
|
||||
`tests/remap/nested-remaps-oword`, `tests/remap/posargs.0`, and
|
||||
`tests/remap/sequencing` regressions through `runRemapFile()` or
|
||||
`runRemapFileContinueOnError()`, using the browser WASM filesystem and
|
||||
vendored LinuxCNC REMAP/O-word/file execution path without JavaScript remap
|
||||
semantics. It also writes the NGC-only vendored LinuxCNC
|
||||
`tests/remap/remap-io/test-ngc.ini` and `io_*.ngc` subroutines into the
|
||||
browser WASM filesystem and validates `runRemapIoMdiSequence()` through
|
||||
vendored LinuxCNC REMAP parsing and MDI execution without JavaScript M-code or
|
||||
I/O semantics. It also writes vendored LinuxCNC `tests/interp/do-while-break`,
|
||||
`tests/interp/oword-bug315`, `tests/interp/oword-bug315-p2`,
|
||||
`tests/interp/exists`, `tests/interp/return-value`,
|
||||
`tests/interp/subs-follow-main`, `tests/interp/fractional-linenumbers`,
|
||||
`tests/interp/cam-nisley`, `tests/interp/namedparam-bug424`,
|
||||
`tests/interp/inside-corners`,
|
||||
`tests/interp/inverse-time-with-comp`, and selected `tests/interp/rotation`
|
||||
`.ngc` files plus
|
||||
`tests/interp/sub-call-from-sub` `test.ini`, `test.ngc`, and `subs/*.ngc`
|
||||
files and `tests/interp/sequence-number` `test.ini`, `test.ngc`, and
|
||||
`rm400.ngc` plus `tests/interp/nested-sub-error` `test.ini`, `test.ngc`, and
|
||||
`subs/nested.ngc` and `tests/interp/nested-sub-in-file-error` `test.ini`,
|
||||
`test.ngc`, and `subs/sequential.ngc`, plus `tests/interp/abort-hot-comment`
|
||||
`test.ini` and `test.ngc`, plus `tests/interp/m19` `test.ini` and `test.ngc`,
|
||||
plus `tests/interp/magic_comments/param_format_printing` `test.ngc`,
|
||||
plus selected pure-interpreter `tests/interp/m98m99` cases covering
|
||||
Fanuc-style `M98/M99`, missing-P-word, missing-subprogram, mixed
|
||||
Fanuc/RS274NGC sub-style, and `DISABLE_FANUC_STYLE_SUB` INI-gated errors,
|
||||
main-program O-word termination rules, parameter scope, loop counts including
|
||||
`L0`, nested numbered subprograms, subprograms after main program text,
|
||||
leading-zero O-word lookup, named and numbered main programs, and
|
||||
expression-based O-sub/M98 calls,
|
||||
into the browser WASM filesystem and
|
||||
validates those upstream
|
||||
interpreter regressions through `runFile()` or `runFileWithIni()`, without
|
||||
JavaScript O-word, parameter, line-number, spindle-speed, spindle-orient,
|
||||
magic-comment formatting, M98/M99, subroutine lookup, or branch semantics.
|
||||
|
||||
The aggregate host smoke script builds the INI, interpreter-core, and
|
||||
trajectory-planner WASM artifacts once, then runs the Node WASM smokes, the
|
||||
@@ -233,6 +743,7 @@ The validation fails if:
|
||||
| --- | --- |
|
||||
| `linuxcnc_ini_probe` | Validates vendored LinuxCNC INI parsing can be used standalone. |
|
||||
| `linuxcnc_interp_state_probe` | Validates interpreter state constants and structs compile under the standalone boundary. |
|
||||
| `linuxcnc_rs274_compile_probe` | Validates the upstream `tests/interp/compile` class remains a source/compile boundary rather than being misclassified as a runtime `.ngc` interpreter fixture. |
|
||||
| `linuxcnc_emc_status_probe` | Validates the standalone `emcStatus` machine-units status boundary used by vendored interpreter conversion and initialization code. |
|
||||
| `linuxcnc_namedparam_harness` | Validates LinuxCNC named parameter behavior, `_ini[...]`, and `_hal[...]` adapter resolution. |
|
||||
| `linuxcnc_interp_minimal_harness` | Runs G-code fixtures through vendored LinuxCNC parser/execution/conversion code, captures canonical events, and validates feed-rate state readback across length-unit changes. |
|
||||
@@ -240,7 +751,8 @@ The validation fails if:
|
||||
| `linuxcnc_interp_init_harness` | Validates vendored LinuxCNC `Interp::init()` emits canonical initialization boundaries, reads metric/inch machine units, and synchronizes current/selected tool slots through standalone status adapters. |
|
||||
| `linuxcnc_indexer_harness` | Validates vendored LinuxCNC single-axis rotary indexer dispatch emits lock/unlock and motion boundaries through the standalone event sink. |
|
||||
| `linuxcnc_remap_hal_sync_harness` | Validates vendored LinuxCNC `M68`/`M66` execution can drive the standalone HAL adapter boundary used by 5-axis switchkins remap files, including `_hal[motion.switchkins-type]` readback. |
|
||||
| `linuxcnc_5axis_remap_execute_harness` | Validates vendored LinuxCNC `REMAP` parsing plus NGC remap execution for the 5-axis `M429 -> M428 -> M430 -> M429` switchkins path in the `xyzac-trt` and `xyzbc-trt` sample machines, the two-remap `M429 -> M428 -> M429` path in the `xyzab-tdr` sample machine, and the bridge-mill `M429 -> M428 -> M430 -> M429` path where M428 selects the default bridge-mill kinematics. It loads vendored machine tool tables through LinuxCNC `tooldata_load()`, then runs vendored `xyzac_switchkins.ngc`, `xyzbc_switchkins.ngc`, `xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_3.ngc`, `boat-xyzac.ngc`, `boat-xyzbc.ngc`, `impeller-7bl-xyzac.ngc`, `xyzab-tdr-demo.ngc`, and `5axisgui.ngc` through the LinuxCNC file `open/read/execute` path. |
|
||||
| `linuxcnc_5axis_remap_execute_harness` | Validates vendored LinuxCNC `REMAP` parsing plus NGC remap execution for the 5-axis `M429 -> M428 -> M430 -> M429` switchkins path in the `xyzac-trt` and `xyzbc-trt` sample machines, the two-remap `M429 -> M428 -> M429` path in the `xyzab-tdr` sample machine, and the bridge-mill `M429 -> M428 -> M430 -> M429` path where M428 selects the default bridge-mill kinematics. It loads vendored machine tool tables through LinuxCNC `tooldata_load()`, then runs vendored `xyzac_switchkins.ngc`, `xyzbc_switchkins.ngc`, `xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_2.ngc`, `xyzac_switchkins_test_3.ngc`, `boat-xyzac.ngc`, `boat-xyzbc.ngc`, `impeller-7bl-xyzac.ngc`, `xyzab-tdr-demo.ngc`, and `5axisgui.ngc` through the LinuxCNC file `open/read/execute` path. |
|
||||
| `linuxcnc_duplicate_oword_remap_harness` | Validates vendored LinuxCNC upstream `tests/remap/duplicate-o-word`, NGC-only `tests/remap/fail/args.0`, `tests/remap/fail/args.1`, `tests/remap/fail/args.2`, `tests/remap/fail/body-ngc`, `tests/remap/m30-interaction`, `tests/remap/nested-remaps-oword`, `tests/remap/posargs.0`, `tests/remap/sequencing`, and the NGC-only `tests/remap/remap-io/test-ngc.ini` branch through LinuxCNC `REMAP` parsing, O-word remap dispatch, `OWORD_NARGS`, error-text reporting, G/M remap sequencing, remapped M62-M68/M66 MDI execution, and file `open/read/execute`. The standalone harness only supplies INI/file path setup, deterministic external input values, and canonical-event capture; it can continue after LinuxCNC errors for upstream `rs274 -n 0` style tests and does not implement duplicate-label, O-word, M30, positional-argument, failure, sequencing, M62-M68, M66 input, or remap semantics. |
|
||||
| `linuxcnc_tp_api_probe` | Validates vendored LinuxCNC trajectory planner calls for linear, arc, and queued motion paths. |
|
||||
| `linuxcnc_kinematics_probe` | Validates vendored LinuxCNC `trivkins.c` plus `kins_util.c` initialize and perform identity forward/inverse mapping through the standalone HAL/RTAPI boundary. |
|
||||
| `linuxcnc_5axis_kinematics_probe` | Validates vendored LinuxCNC `5axiskins.c` through `switchkins.c`, including 5-axis forward/inverse round-trip behavior and switching to identity kinematics. |
|
||||
@@ -265,12 +777,15 @@ The validation fails if:
|
||||
| Harness | Purpose |
|
||||
| --- | --- |
|
||||
| `tests/wasm/node/verify_ini_wasm.sh` | Validates the browser-facing INI WASM module can be built from vendored LinuxCNC `inifile.cc`, loaded through the JS SDK in Node, and queried through the exported C ABI, including LinuxCNC-backed boolean conversion and machine-session file-name string lookup. |
|
||||
| `tests/wasm/node/verify_interp_wasm.sh` | Validates the interpreter-core WASM module can be built from vendored LinuxCNC interpreter/remap source, loaded through the interpreter JS SDK, run the first fixture group through `Interp::execute()` and selected file fixtures through `Interp::open()`/`read()`/`execute()`, match the native canonical event plus required state readback fixtures, run vendored `xyzac-trt`/`xyzbc-trt` switchkins remap demo files through the WASM C ABI/SDK path, and run parameter-file restore/save through vendored LinuxCNC `Interp::restore_parameters()` and `Interp::save_parameters()`. |
|
||||
| `tests/wasm/node/verify_interp_wasm.sh` | Validates the interpreter-core WASM module can be built from vendored LinuxCNC interpreter/remap source, loaded through the interpreter JS SDK, run the first fixture group through `Interp::execute()` and selected file fixtures plus vendored upstream `tests/interp` regression files through `Interp::open()`/`read()`/`execute()`, match the native canonical event plus required state readback fixtures, emit `run_step` execution-status records with LinuxCNC line number, encoded source statement, return code, and interpreter axis positions for file execution, cover vendored `tests/interp/flowsnake` recursive O-word file execution, `tests/interp/g6164` path-control and naive-cam tolerance execution, `tests/interp/oword-unwind` continue-on-error stack unwind behavior through planner-staged INI context, selected `tests/interp/bad` file-error paths, and `tests/interp/g33.1` rigid-tap file execution, run vendored `xyzac-trt`/`xyzbc-trt` table-rotary-tilting and `xyzab-tdr` table-dual-rotary switchkins remap demo files through the WASM C ABI/SDK path, and run parameter-file restore/save through vendored LinuxCNC `Interp::restore_parameters()` and `Interp::save_parameters()`. |
|
||||
| `tests/wasm/node/verify_sim_configs_wasm.sh` | Validates representative vendored LinuxCNC `configs/sim` programs in Node WASM through `planSimConfigStaging()` plus `runSimConfigProgram()`. The generic `planIniFileContextStaging()` planner and its sim-config wrapper use INI text and `tools/source-manifest.txt` to collect the program, INI, tool table, parameter file when vendored, `SUBROUTINE_PATH` files, `USER_M_PATH` files, and remap-NGC files before forwarding to `runFileWithIni()` or `runFiveAxisRemapFile()`. This covers INI-driven `U/V/W` axis mask handling, real `USER_M_PATH` registration for executable `M110`/`M111`, all four current external-offset M111 expected-failure programs, deterministic `woodpecker/on_abort.ngc` file execution, plain INI/tool-table execution through `axis/gladevcp/probe.ngc`, `SUBROUTINE_PATH` staging for `opa_demo.ngc -> circles.ngc`, bridge-mill remap-subroutine staging, vendored LinuxCNC bridge-mill and `melfa-sim` NGC remap execution, and PUMA machine-context execution without spawning host processes. The same smoke includes synthetic staging-plan assertions for generic `TOOL_TABLE`, `PARAMETER_FILE`, multi-directory `SUBROUTINE_PATH`, `USER_M_PATH`, and `REMAP ... ngc=...` file collection. |
|
||||
| `tests/wasm/node/verify_sim_configs_inventory_wasm.sh` | Validates the first machine-readable Node inventory layer for `configs/sim`. It ensures native `build/native/sim-configs/summary.tsv`, generated `class-summary.tsv`, and generated `path-matrix.tsv` exist; verifies that generated `path-matrix.tsv` and tracked `docs/sim-configs-coverage-matrix.md` contain the same 159 paths as native `summary.tsv`; checks tracked matrix fields for class, native status, expected-failure reason, and blocked kind drift against generated `path-matrix.tsv`; and checks that generated `class-summary.tsv` matches class/status/expected-failure counts derived from `summary.tsv`. It then executes only entries that are currently vendored and have a defined standalone/WASM runtime path, and writes the sim-config inventory TSV artifacts. The boundary summary records one row per native inventory path with the matrix block, SDK classifier recommendation, declared HAL/UI/HALUI/Python process dependencies, `[EMCIO]DB_PROGRAM`, and execution-chain user-M codes; vendored INI rows must have an available classifier report, vendored hard blocks fail on classifier/matrix drift, safe process-declaring representatives must remain Node/browser `REP` rows with the expected process flag combinations rather than full-process promotions, and the designed full-process blocked rows must remain non-`REP` until their proof criteria are met. The generated inventory summary also requires all hard-blocked rows to remain `SKIP` with matching reasons, and `skip-summary.tsv` must match the matrix-derived skip counts. The blocked-dependency and boundary summaries record hard blocked Python-remap, tool-database, and external user-M rows from source `linuxcnc/configs/sim` INI files for dependency accounting only, including LinuxCNC source/config ownership fields, user-M script side effects, tool DB protocol evidence, Python runtime owner evidence, and completion criteria that all remain non-executing. Current gate: `executed=28`, `passed=28`, `skipped=131`, `unexpected_fail=0`; current skip/block counts are `ASSET_ONLY=65`, `L4_PYTHON_REMAP=53`, `L4_TOOL_DB=1`, `L4_USER_M_PROCESS=1`, `NON_MAIN_CLASS=10`, and `UPSTREAM_DEMO=1`. The executed set now includes the deterministic `woodpecker` plus `qtdragon` / `qtdragon_hd` / `qtvcp_screens` `on_abort.ngc` family, `axis/gladevcp/probe.ngc`, `axis/rose_engine/rcone_demo.ngc`, `axis/vismach/melfa-sim/example.ngc`, and the `axis/vismach/puma` sample programs after vendoring the required machine INI, tool-table, and remap-subroutine inputs. When an upstream INI declares a missing local tool table but the native harness resolved a valid fallback table, the Node inventory stages that native-selected table at the INI-declared path so WASM execution uses the same machine context. The skip reasons are explicit: `NON_MAIN_CLASS`, `L4-TOOL-DB`, `L4-USER-M-PROCESS`, `L4-PYTHON-REMAP`, and `UPSTREAM-DEMO`; an eligible row that lacks vendored machine context is reported as an inventory failure. This runner is an inventory source-of-truth for Node WASM and is intentionally narrower than a browser full inventory. |
|
||||
| `tests/wasm/node/verify_nc_files_wasm.sh` | Validates representative vendored LinuxCNC `nc_files` examples in Node WASM by copying `3D_Chips.ngc`, `arcspiral.ngc`, `hole-circle.ngc`, `factorial.ngc`, and `m6demo.ngc` into the Emscripten filesystem and forwarding to the LinuxCNC-backed `Interp::open()`/`read()`/`execute()` path. `3D_Chips.ngc` is staged with a minimal INI-declared `tool.tbl` because the upstream program contains `T1 M6`; JavaScript only stages files and checks LinuxCNC output, including `run_step` status records, and does not implement G-code, O-word, tool-change, or M-code behavior. |
|
||||
| `tests/wasm/node/verify_tp_wasm.sh` | Validates a standalone trajectory-planner WASM module can be built from vendored LinuxCNC TP/TC/Ruckig support source, loaded in Node, and run the same linear, arc, and queued-line planner probe paths covered by the native TP harness. |
|
||||
| `tests/opfs/node/verify_file_service.sh` | Validates the host-owned OPFS text-file adapter, path model, session snapshot store including custom filenames and envelope/path rejection paths, machine file store, G-code text store including filename rejection paths, OPFS-to-WASM parameter/tool-table bridges, and grouped machine-session loading without moving file persistence, parameter semantics, or tool-table semantics into the WASM core. |
|
||||
| `tests/browser/verify_ini_panel_browser.sh` | Validates the INI SDK, INI/interpreter WASM module loading, LinuxCNC-backed INI machine-session file-name string lookup, OPFS text-file round trip, generic session snapshot round trip plus custom filename and envelope/path rejection paths, machine file text round trip, G-code text round trip plus filename rejection paths, and the INI panel UI's machine-session load with default OPFS parameter/tool-table file mapping, G-code run, canonical-event display paths, and 5-axis remap demo action in a real browser runtime. |
|
||||
| `tests/browser/verify_interp_browser.sh` | Validates the interpreter-core WASM module loads through the interpreter JS SDK in a real browser runtime and runs selected positive and negative canonical fixtures through vendored LinuxCNC `Interp::execute()` plus `Interp::open()`/`read()`/`execute()` via the exported C ABI, including direct browser SDK and OPFS-backed parameter-file restore/save, non-random/random tool-table load/save through vendored LinuxCNC source, and vendored `xyzac-trt`/`xyzbc-trt` switchkins remap demo execution. |
|
||||
| `tests/host/verify_host_smokes.sh` | Runs the current host-side Node, WASM interpreter-core, WASM trajectory-planner, OPFS, and browser smoke validation with shared WASM builds. |
|
||||
| `tests/browser/verify_ini_panel_browser.sh` | Validates the INI SDK, INI/interpreter WASM module loading, LinuxCNC-backed INI machine-session file-name string lookup, OPFS text-file round trip, generic session snapshot round trip plus custom filename and envelope/path rejection paths, machine file text round trip, G-code text round trip plus filename rejection paths, and the INI panel UI's machine-session load with default OPFS parameter/tool-table file mapping, G-code run, `run_step`-backed progress/line/statement/axis display, canonical-event display paths, and 5-axis remap demo action in a real browser runtime. |
|
||||
| `tests/browser/verify_interp_browser.sh` | Validates the interpreter-core WASM module loads through the interpreter JS SDK in a real browser runtime and runs selected positive and negative canonical fixtures plus vendored upstream `tests/interp` regression files through vendored LinuxCNC `Interp::execute()` and `Interp::open()`/`read()`/`execute()` via the exported C ABI, including direct browser SDK and OPFS-backed parameter-file restore/save, non-random/random tool-table load/save through vendored LinuxCNC source, vendored `xyzac-trt`/`xyzbc-trt` table-rotary-tilting and `xyzab-tdr` table-dual-rotary switchkins remap demo execution, representative vendored `configs/sim` `foam`, `geometry`, `external_offsets`, `axis/gladevcp/probe.ngc`, deterministic `woodpecker/on_abort.ngc`, bridge-mill, `melfa-sim`, and `puma_cube.ngc` programs through `runSimConfigProgram()`, representative vendored `nc_files` examples through `runFile()` or `runFileWithIni()` when the upstream file requires INI/tool-table context, and a synthetic browser assertion that `planIniFileContextStaging()` can collect INI, program, tool-table, parameter, multi-directory subroutine, executable user-M, and remap-NGC files using only manifest text. |
|
||||
| `tests/host/verify_host_smokes.sh` | Runs the current host-side Node, WASM interpreter-core, sim-config representative smoke, sim-config Node inventory, WASM trajectory-planner, OPFS, and browser smoke validation with shared WASM builds. |
|
||||
|
||||
## Fixture Coverage
|
||||
|
||||
@@ -297,6 +812,7 @@ Positive G-code fixtures currently cover:
|
||||
- comment logging canonical calls
|
||||
- local, INI-backed named parameters and numbered parameters
|
||||
- O-word subroutines
|
||||
- continue-on-error O-word unwind
|
||||
- program-end modal reset
|
||||
- canonical runtime edge calls
|
||||
|
||||
@@ -336,7 +852,8 @@ plus the `g1_zero_feed`, `arc_radius_mismatch`, `arc_zero_radius`,
|
||||
path additionally covers the same canonical-event fixture group, plus
|
||||
`namedparam_ini_semantics` and `namedparam_semantics` through the INI-aware
|
||||
file execution ABI,
|
||||
`file_open_reset`, `percent_file_finish`, and `oword_subroutine`.
|
||||
`file_open_reset`, `percent_file_finish`, `oword_subroutine`,
|
||||
`g6164`, and `oword_unwind`.
|
||||
`position_params` uses a dedicated file-path expectation under
|
||||
`tests/fixtures/canon_file/` because LinuxCNC file execution advances the
|
||||
post-execute position parameters differently than the line-by-line MDI smoke;
|
||||
@@ -588,12 +1105,40 @@ numbered-parameter, local named-parameter plus `_ini[...]` lookup through
|
||||
`rs274 -i`, probing, spindle-orient, file-open reset, file-finish,
|
||||
tool-reload, tool select/change/length-offset, M61 current-tool-number update,
|
||||
canned-cycle, state-tag motion, tool-table setup, and O-word subroutine
|
||||
fixtures, plus threading/rigid tap, NURBS dispatch boundaries, and the
|
||||
fixtures, plus vendored `tests/interp/flowsnake` recursive O-word toolpath,
|
||||
`tests/interp/cam-nisley` storm-door latch cam execution with upstream
|
||||
tool-table context and a bare-run missing-tool negative check,
|
||||
`tests/interp/inside-corners` cutter-compensation concave/convex/tangent
|
||||
geometry across XY and ZX planes,
|
||||
`tests/interp/inverse-time-with-comp` inverse-time feed and cutter-compensation
|
||||
interaction,
|
||||
selected upstream `tests/interp/bad` file-error paths including canned-cycle
|
||||
A-axis rejection, center-format arc radius mismatch rejection, and
|
||||
selected upstream `tests/interp/good` center-format arc tolerance acceptance,
|
||||
selected `tests/interp/g72-*` lathe facing canned-cycle iteration regressions,
|
||||
selected `tests/interp/g71-*` lathe roughing/finish canned-cycle regressions,
|
||||
`tests/interp/g76` lathe threading with upstream tool-table context,
|
||||
cutter-compensation rejection, selected standalone `tests/ccomp`
|
||||
cutter-compensation file execution with upstream tool tables, threading/rigid tap,
|
||||
vendored `tests/interp/g33.1` rigid-tap file execution, NURBS dispatch
|
||||
boundaries, and the
|
||||
comparable canonical runtime edge and program-end cleanup calls. The
|
||||
standalone vendored-source harness, Node WASM smoke, and browser smoke also
|
||||
pin `coordinate_offsets` MDI/file-path, `feed_control_modes`, the
|
||||
pin `coordinate_offsets` MDI/file-path, `feed_control_modes`, `run_step`
|
||||
file-execution status output for line number, URI-encoded source statement,
|
||||
return code, and current interpreter axis positions, the
|
||||
`position_params` MDI/file-path, `tool_semantics`, and `canon_runtime_edges`
|
||||
post-program modal, override, spindle, mist, and flood `_setup` state. The same upstream baseline also
|
||||
post-program modal, override, spindle, mist, and flood `_setup` state. Native
|
||||
and WASM standalone checks also cover the user M-code registration boundary for
|
||||
INI-declared `M110`/`M111` handlers while keeping process execution outside the
|
||||
browser/WASM runtime. Node and browser WASM sim-config coverage now run the real
|
||||
vendored `axis/geometry/xyzc.ngc` and the
|
||||
`axis/external_offsets/dyn_demo.ngc`, `eoffsets.ngc`, `jwp_z.ngc`, and
|
||||
`opa_demo.ngc` programs with their executable `M110`/`M111` files,
|
||||
`opa_demo.ngc`'s `SUBROUTINE_PATH` dependency on `circles.ngc`,
|
||||
`axis/foam/foam.ngc` with its `U/V` machine configuration and shared
|
||||
`axis/sim.tbl` tool table staged beside the INI, and
|
||||
bridge-mill `5axisgui.ngc` with its `W` machine coordinates. The same upstream baseline also
|
||||
validates the current negative fixture error text and absent canonical-event
|
||||
constraints. Fixtures that depend on standalone-only runtime
|
||||
adapters, HAL state, upstream `rs274` output gaps such as
|
||||
@@ -633,3 +1178,44 @@ resulting `_setup.m_remapped` descriptors for each machine's LinuxCNC-defined
|
||||
`M428`/`M429`/`M430` set. Python callbacks remain runtime boundaries. Native
|
||||
and WASM validation now execute the NGC remap/file paths through vendored
|
||||
LinuxCNC O-word dispatch and the standalone HAL adapter boundary.
|
||||
|
||||
`linuxcnc_duplicate_oword_remap_harness` vendors LinuxCNC's upstream
|
||||
`tests/remap/duplicate-o-word` and `tests/remap/m30-interaction` files
|
||||
unchanged and runs each `test.ngc` with its `test.ini` through vendored
|
||||
`Interp::parse_remap()`, `open()`, `read()`, and `execute()`. Native, Node
|
||||
WASM, and browser interpreter validation assert the same successful LinuxCNC
|
||||
regression paths, including the remapped `M207`/`M208` messages and final
|
||||
`FINISH()` event for duplicate O-word, and the `M400 M30` remap-level
|
||||
interaction with `PROGRAM_END`.
|
||||
|
||||
`linuxcnc_interp_minimal_harness` now also runs selected vendored LinuxCNC
|
||||
upstream `tests/interp/*/test.ngc` files through vendored `Interp::open()`,
|
||||
`read()`, and `execute()`, using LinuxCNC INI `SUBROUTINE_PATH` for
|
||||
`tests/interp/sub-call-from-sub`, `tests/interp/sequence-number`, and
|
||||
`tests/interp/nested-sub-error`, `tests/interp/nested-sub-in-file-error`, and
|
||||
`tests/interp/abort-hot-comment`, plus LinuxCNC `[RS274NGC]ORIENT_OFFSET` for
|
||||
`tests/interp/m19`.
|
||||
Native, Node WASM, and browser interpreter validation assert the LinuxCNC
|
||||
canonical messages, canonical events, error texts, and interpreter state for
|
||||
O-word loop/break/subroutine paths, dynamic O-word calls, `EXISTS[]`,
|
||||
subroutine return values, subroutines after main programs, fractional line
|
||||
numbers, named-parameter parsing, M19 spindle-orient offset and wait timeout
|
||||
canonical events, magic-comment parameter formatting, valid external subroutine calls from another
|
||||
external subroutine, external-subroutine `#<_line>` reporting,
|
||||
nested-subroutine-definition rejection, and blocked forward seek to a later
|
||||
numbered subroutine in the same external file, selected `tests/interp/rotation`
|
||||
absolute-position `#<_abs_x>`, `#<_abs_y>`, and `#<_abs_z>` reporting under
|
||||
G54, G92, XY rotation, and unit changes plus rotated-coordinate `G28` and
|
||||
`G53` endpoint behavior, selected `tests/interp/m98m99`
|
||||
Fanuc-style `M98/M99` subprogram calls, missing-P-word, missing-subprogram,
|
||||
mixed Fanuc/RS274NGC sub-style, and `DISABLE_FANUC_STYLE_SUB` INI-gated
|
||||
errors, main-program O-word termination rules, parameter-scope differences,
|
||||
loop counts including `L0`, nested numbered subprograms, subprograms after main
|
||||
program text, leading-zero O-word lookup, named/numbered main programs, and
|
||||
O-expression calls, plus
|
||||
`(ABORT,...)` hot-comment numbered, named, and INI-parameter
|
||||
expansion, with skipped branch/subroutine messages and post-abort program end
|
||||
asserted absent where applicable. The same native, Node WASM, and browser
|
||||
interpreter validation also stages an INI-declared variable file to verify
|
||||
LinuxCNC G92 startup persistence and the `DISABLE_G92_PERSISTENCE` startup
|
||||
clear path through vendored interpreter initialization.
|
||||
|
||||
@@ -45,6 +45,7 @@ semantic rewrites:
|
||||
| Go math C/C++ linkage | `genserkins` runtime probing compiles vendored `gomath.c` through a narrow C++ wrapper so LinuxCNC `genserfuncs.c` can link to the upstream Go math symbols without editing vendored source. |
|
||||
| Switchkins iterative forward | `genhexkins` runtime probing follows LinuxCNC switchkins iterative-forward behavior, including the first-call warmup path before asserting roundtrip convergence. |
|
||||
| Browser storage | OPFS remains outside the native core; `runtime/opfs/file-service.js` owns browser text-file persistence, `runtime/opfs/path-model.js` owns host-side storage paths for INI, tool table, parameter, G-code, preview-cache, and session-snapshot content, `runtime/opfs/snapshot-store.js` owns generic JSON session snapshot persistence, `runtime/opfs/machine-file-store.js` owns pure-text machine-file and G-code persistence, `runtime/opfs/linuxcnc-parameter-bridge.js` only copies parameter files between OPFS text storage and the LinuxCNC-backed WASM parameter-file ABI, `runtime/opfs/linuxcnc-tool-table-bridge.js` only copies tool tables between OPFS text storage and the LinuxCNC-backed WASM tool-table ABI, and `runtime/opfs/linuxcnc-machine-session-bridge.js` groups INI, parameter, and tool-table loading while using the LinuxCNC-backed INI SDK for `[EMCIO]RANDOM_TOOLCHANGER`, `[RS274NGC]PARAMETER_FILE`, and `[EMCIO]TOOL_TABLE` when available; explicit host session file-name options take precedence over INI-derived file names, missing INI file-name values fall back to host default parameter/tool-table paths, and path validation remains owned by the OPFS path model, including rejection of traversal or nested path segments from INI-derived file names. |
|
||||
| INI-context SDK staging | `runtime/sdk/src/sim-config-staging.js` exposes `planIniFileContextStaging()` as a generic host-boundary file planner and `planSimConfigStaging()` as the representative vendored `configs/sim` wrapper. The planner reads INI text plus `tools/source-manifest.txt` to collect `[DISPLAY]OPEN_FILE`, `[EMCIO]TOOL_TABLE`, `[RS274NGC]PARAMETER_FILE`, one or more `[RS274NGC]SUBROUTINE_PATH` entries, one or more `[RS274NGC]USER_M_PATH` entries, and remap-NGC files for staging without relying on browser directory enumeration. `runtime/sdk/src/linuxcnc-interp.js` exposes `runSimConfigProgram()` as the execution convenience only: it writes planned text files into the Emscripten filesystem, applies executable bits for user M-code files, and forwards to existing LinuxCNC-backed C ABI entry points such as `runFileWithIni()` or `runFiveAxisRemapFile()`; neither helper implements interpreter, axis, remap, tool, parameter, or user M-code semantics. |
|
||||
|
||||
## Enforced Non-Drift Rules
|
||||
|
||||
@@ -78,7 +79,9 @@ semantic rewrites:
|
||||
paths.
|
||||
- Host-side smoke validation is aggregated by
|
||||
`tests/host/verify_host_smokes.sh` so Node, WASM, OPFS, and browser checks
|
||||
run from one command.
|
||||
run from one command. The current aggregate run passes with
|
||||
`host_wasm_opfs_browser_smokes=ok`, including the Node and browser
|
||||
sim-config paths staged through `runSimConfigProgram()`.
|
||||
- Identity/trivial, `5axiskins`, TRT `xyzac`/`xyzbc`, delta, SCARA, PUMA,
|
||||
serial, hexapod, pentapod, and related kinematics sources now have native
|
||||
source-probe coverage.
|
||||
|
||||
513
wasm-port/docs/full-process-boundary-design.md
Normal file
513
wasm-port/docs/full-process-boundary-design.md
Normal file
@@ -0,0 +1,513 @@
|
||||
# Full-Process Runtime Boundary Design
|
||||
|
||||
Generated: 2026-06-10 CST
|
||||
|
||||
This document records the first design pass for `configs/sim` rows that are
|
||||
already classified by the Node inventory, but must remain blocked until a
|
||||
LinuxCNC-owned runtime boundary exists. It is a design and accounting artifact,
|
||||
not an execution path.
|
||||
|
||||
## Scope
|
||||
|
||||
- `axis/vismach/millturn/example.ngc`, currently `L4-USER-M-PROCESS`.
|
||||
- `axis/db_demo/base.ngc`, currently `L4-TOOL-DB`.
|
||||
- Python-remap and broader full-process families remain inventoried in
|
||||
`build/wasm/sim-configs-inventory/blocked-dependency-summary.tsv`; they are
|
||||
not promoted here.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Do not implement HAL, task, HALUI, Tcl, Python, tool-database, or external
|
||||
process semantics in JavaScript.
|
||||
- Do not make blocked rows pass by editing upstream G-code, Tcl, Python, INI,
|
||||
or tool-table files.
|
||||
- Do not treat native GUI code or config helper scripts as browser UI
|
||||
implementation code.
|
||||
- Do not mark a row unblocked until native, Node, and browser validation prove
|
||||
the boundary where that layer is applicable.
|
||||
|
||||
## External User-M Process Boundary
|
||||
|
||||
### Current Block
|
||||
|
||||
`axis/vismach/millturn/example.ngc` is blocked because
|
||||
`millturn.ini` declares:
|
||||
|
||||
- `[RS274NGC]USER_M_PATH = ./mcodes`;
|
||||
- `REMAP = M428 ... ngc=428remap`;
|
||||
- `REMAP = M429 ... ngc=429remap`;
|
||||
- `[HAL]HALUI = halui`, `HALFILE`, `HALCMD`, and `POSTGUI_HALFILE`;
|
||||
- `[HALUI]MDI_COMMAND = M128` and `M129`;
|
||||
- `[DISPLAY]PYVCP = millturn.xml`.
|
||||
|
||||
The promoted execution chain reaches `remap_subs/428remap.ngc` and
|
||||
`remap_subs/429remap.ngc`. Those LinuxCNC NGC remaps execute `M68`/`M66`
|
||||
switchkins synchronization and then call external user-M process codes
|
||||
`M128` or `M129`.
|
||||
|
||||
### LinuxCNC Owner Set
|
||||
|
||||
- `configs/sim/axis/vismach/millturn/millturn.ini` owns the machine
|
||||
declarations and search paths.
|
||||
- `configs/sim/axis/vismach/millturn/remap_subs/428remap.ngc` and
|
||||
`429remap.ngc` own the NGC remap call sequence around `M128` and `M129`.
|
||||
- `configs/sim/axis/vismach/millturn/mcodes/M128` and
|
||||
`configs/sim/axis/vismach/millturn/mcodes/M129` own the config side effects.
|
||||
- `src/emc/task/emctask.cc` owns `USER_M_PATH` search, executable detection,
|
||||
`USER_DEFINED_FUNCTION_ADD()`, and queuing `EMC_SYSTEM_CMD`.
|
||||
- `src/emc/task/emctaskmain.cc` owns `emcSystemCmd()` process spawning and
|
||||
completion tracking for queued system commands.
|
||||
- `src/emc/usr_intf/halui.cc` owns HALUI MDI command queue behavior.
|
||||
- The HAL runtime owns `hal getp`/`hal setp` state, including the `ini.*`
|
||||
soft-limit pins used by the scripts.
|
||||
|
||||
### Native Behavior To Preserve
|
||||
|
||||
`M128` is a Tcl process script that:
|
||||
|
||||
- requires the LinuxCNC and HAL Tcl packages;
|
||||
- calls `emc_init -quick`;
|
||||
- calls `parse_ini $::env(INI_FILE_NAME)`;
|
||||
- checks `hal getp kinstype.is-0`;
|
||||
- writes mill limits from `[AXIS_X]`, `[AXIS_Y]`, and `[AXIS_Z]` into
|
||||
`ini.x.*`, `ini.y.*`, and `ini.z.*` HAL pins.
|
||||
|
||||
`M129` follows the same process path, but checks `kinstype.is-1` and writes the
|
||||
`MIN_LIMIT_TURN` / `MAX_LIMIT_TURN` values for X, Y, and Z. Both scripts also
|
||||
restore `MAX_VELOCITY` and `MAX_ACCELERATION`.
|
||||
|
||||
The NGC remap files own the surrounding interpreter-visible work:
|
||||
|
||||
- set `motion.analog-out-03` through `M68`;
|
||||
- force synchronization with `M66`;
|
||||
- set G5x offsets with `G10 L2 P7` or `G10 L2 P8`;
|
||||
- activate `G59.1` or `G59.2`;
|
||||
- verify `_hal[motion.switchkins-type]` after the switch.
|
||||
|
||||
### Boundary Decision
|
||||
|
||||
This is not the same boundary as the existing deterministic `M110`/`M111`
|
||||
registration adapter. `M110` and `M111` can currently be represented as
|
||||
deterministic `USER_M_COMMAND` canonical events because the promoted tests only
|
||||
need the interpreter to accept and queue the user-M command. `M128` and `M129`
|
||||
must also prove HAL pin state updates that affect soft limits after a
|
||||
kinematics switch.
|
||||
|
||||
The row therefore remains `L4-USER-M-PROCESS`.
|
||||
|
||||
### Current Machine-Readable Artifact
|
||||
|
||||
`user-m-process-boundary-summary.tsv` records the current designed-but-blocked
|
||||
state target set for this boundary. It maps `M128` to
|
||||
`remap_subs/428remap.ngc` and `M129` to `remap_subs/429remap.ngc`, records the
|
||||
`kinstype.is-0` and `kinstype.is-1` guards, and expands the source-traceable
|
||||
`ini.[xyz].min_limit`, `ini.[xyz].max_limit`, `ini.[xyz].min_velocity`, and
|
||||
`ini.[xyz].max_acceleration` target pins from `millturn.ini`.
|
||||
`user-m-process-state-targets.tsv` is the normalized companion table for that
|
||||
same evidence. It has one pending proof row per `M128`/`M129` target pin,
|
||||
including the config script source file, remap caller, mill/turn state mode,
|
||||
guard pin, source INI section/field, and expected value. The current table has
|
||||
24 target rows plus a header and keeps `proof_status=pending`,
|
||||
`execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`user-m-process-transition-plan.tsv` records the next non-executing contract
|
||||
for this boundary. It binds `M428` to `M128` and `M429` to `M129`, records the
|
||||
`motion.analog-out-03` switchkins output, target kinstype values `0` and `1`,
|
||||
the active `G59.1`/`G59.2` work offsets, `P7`/`P8` offset pockets, guard pins,
|
||||
and the 12 expected `ini.[xyz].*` state outputs for each user-M code. This is
|
||||
still a pending transition contract only; it does not execute Tcl, start HAL,
|
||||
or permit standalone/browser promotion.
|
||||
`user-m-process-native-state-alignment.tsv` aligns those 24 generated target
|
||||
rows with the native source probe stdout keys, such as
|
||||
`M128_X_AXIS_X.MIN_LIMIT_ok` and `M129_Z_AXIS_Z.MAX_LIMIT_TURN_ok`. This table
|
||||
proves the generated pin targets still match the native LinuxCNC source/state
|
||||
probe output, but it also remains `proof_status=pending`,
|
||||
`execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`user-m-process-native-transition-alignment.tsv` aligns the two generated
|
||||
transition-plan rows with native source probe stdout for `M428` and `M429`.
|
||||
It verifies the source-owned `motion.analog-out-03` switchkins output, target
|
||||
kinstype values `0` and `1`, active `G59.1`/`G59.2` offsets, `P7`/`P8`
|
||||
offset pockets, and `M428 -> M128` / `M429 -> M129` process calls. This is
|
||||
still source-transition alignment only; it does not execute the Tcl user-M
|
||||
processes, start HAL, or allow promotion.
|
||||
`user-m-process-native-runtime-state-plan.tsv` is the next native runnable
|
||||
probe contract. It records the required LinuxCNC task/HAL/Tcl user-M runtime,
|
||||
the required environment (`INI_FILE_NAME`, `motion.switchkins-type`,
|
||||
`kinstype.is-0`, `kinstype.is-1`, and `ini.[xyz].*` pins), the two transition
|
||||
steps, and the exact expected `ini.[xyz].*` values after each user-M process.
|
||||
It requires the source transition/state alignment artifacts to be complete, but
|
||||
keeps `native_runtime_status=pending_native_hal_tcl_process_probe`,
|
||||
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`user-m-process-native-runtime-readiness.tsv` records whether the host has the
|
||||
runtime commands needed to attempt that native probe. It currently checks
|
||||
`tclsh`, `halrun`, `halcmd`, and `linuxcnc`, records PATH evidence where a
|
||||
command exists, and keeps the boundary blocked with `proof_status=pending`,
|
||||
`execution_enabled=0`, and `promotion_allowed=0` even if the commands become
|
||||
available.
|
||||
`user-m-process-native-runtime-probe-gate.tsv` is the generated execution gate
|
||||
for the future runtime probe. It combines source proof readiness with host
|
||||
runtime readiness for `M428/M128` and `M429/M129`, records missing runtime
|
||||
commands, and keeps `proof_status=pending`, `execution_enabled=0`, and
|
||||
`promotion_allowed=0`.
|
||||
`tests/native/probe_millturn_user_m_runtime.sh` is wired into
|
||||
`build_native_probes.sh` as `linuxcnc_millturn_user_m_runtime_probe`. On hosts
|
||||
without `halrun`, `halcmd`, or `linuxcnc`, it exits successfully but reports
|
||||
`millturn_user_m_runtime_probe_status=skipped_missing_host_runtime`; on a host
|
||||
with the full LinuxCNC runtime it remains disabled by default. If explicitly
|
||||
enabled with `ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1`, it starts the vendored
|
||||
`millturn.ini`, runs the LinuxCNC-owned Tcl `M128`/`M129` scripts, and verifies
|
||||
the resulting `ini.[xyz].*` HAL state targets before reporting
|
||||
`runtime_state_probe_passed`.
|
||||
|
||||
The artifact keeps `execution_enabled=0` and `promotion_allowed=0`. It is not
|
||||
a standalone Tcl/HAL executor and does not make `millturn` a Node/browser
|
||||
representative.
|
||||
|
||||
`linuxcnc_millturn_user_m_boundary_probe` is the current native guard for this
|
||||
evidence. It reads the LinuxCNC source `millturn.ini`, `mcodes/M128`,
|
||||
`mcodes/M129`, and the `428remap`/`429remap` callers, then verifies the
|
||||
source-derived guard pins, `M428`/`M429` switchkins transition fields, and
|
||||
`ini.[xyz].*` target values recorded in the machine-readable tables. This is a
|
||||
source/state-target and source-transition proof only: it does not execute Tcl,
|
||||
start HAL, spawn the external user-M process, or permit promotion.
|
||||
|
||||
### Candidate Boundary
|
||||
|
||||
A valid future boundary would have to be LinuxCNC-owned and state based:
|
||||
|
||||
1. Reuse LinuxCNC task user-M search and registration behavior for discovering
|
||||
`M128` and `M129`.
|
||||
2. Reuse a LinuxCNC-owned HAL/INI state boundary for the `ini.x.*`,
|
||||
`ini.y.*`, `ini.z.*`, `kinstype.is-*`, and `motion.switchkins-type` pins.
|
||||
3. Execute or faithfully host the config-owned side-effect source without
|
||||
translating it into JavaScript CNC semantics. If the Tcl scripts cannot be
|
||||
executed in the target layer, the boundary must be narrowed to a documented
|
||||
LinuxCNC-owned state transition with source-traceable inputs and outputs.
|
||||
4. Keep browser execution blocked unless the same state transition can be
|
||||
proven without spawning arbitrary host processes.
|
||||
|
||||
### Required Proof Before Promotion
|
||||
|
||||
Native proof:
|
||||
|
||||
- The existing `linuxcnc_millturn_user_m_boundary_probe` source/state guard
|
||||
must continue to prove that the pending M128/M129 state targets come from
|
||||
LinuxCNC config files rather than project-owned behavior.
|
||||
- A LinuxCNC or standalone-native probe runs the `M428 -> M129 -> M429 -> M128`
|
||||
relevant switch path and records the `ini.[xyz].min_limit`,
|
||||
`ini.[xyz].max_limit`, `ini.[xyz].min_velocity`, and
|
||||
`ini.[xyz].max_acceleration` pin values before and after each user-M call.
|
||||
- The same probe records the kinstype guard result and the active G5x offset.
|
||||
|
||||
Node WASM proof:
|
||||
|
||||
- The SDK stages the same INI, remap, and script assets for dependency
|
||||
accounting.
|
||||
- The WASM runtime proves the same state output through a LinuxCNC-owned
|
||||
boundary. A `USER_M_COMMAND` event alone is not sufficient.
|
||||
|
||||
Browser proof:
|
||||
|
||||
- The browser smoke calls the already validated SDK/WASM boundary.
|
||||
- The test labels the row as a millturn process-boundary representative only
|
||||
after the state proof exists. It must not claim full LinuxCNC HAL/task/UI
|
||||
process coverage.
|
||||
|
||||
## Tool Database Boundary
|
||||
|
||||
### Current Block
|
||||
|
||||
`axis/db_demo/base.ngc` is blocked because `db_nonran.ini` declares:
|
||||
|
||||
```text
|
||||
[EMCIO]
|
||||
RANDOM_TOOLCHANGER = 0
|
||||
DB_PROGRAM = ./db_nonran.py
|
||||
```
|
||||
|
||||
The INI explicitly notes that `TOOL_TABLE` is not used with `DB_PROGRAM`.
|
||||
Standalone interpreter file execution would therefore bypass the tool database
|
||||
startup, command/reply protocol, and persistent database state.
|
||||
|
||||
### LinuxCNC Owner Set
|
||||
|
||||
- `configs/sim/axis/db_demo/db_nonran.ini` owns the DB program declaration.
|
||||
- `configs/sim/axis/db_demo/db.py` owns the demo database behavior.
|
||||
- `src/emc/task/taskclass.cc` owns reading `[EMCIO]DB_PROGRAM`, enabling DB
|
||||
mode, calling `tooldata_db_init()`, loading tooldata, and notifying DB state
|
||||
changes during tool load/unload paths.
|
||||
- `src/emc/tooldata/tooldata_db.cc` owns child process startup, pipe setup,
|
||||
version handshake, `g` get-all requests, and `l`/`u`/`p` notifications.
|
||||
- `src/emc/tooldata/tooldata_common.cc` owns common tooldata storage and DB
|
||||
refresh behavior.
|
||||
|
||||
### Native Behavior To Preserve
|
||||
|
||||
`tooldata_db_init()` splits `DB_PROGRAM` into argv, verifies that the program is
|
||||
executable, forks it, connects stdin/stdout pipes, and waits for the version
|
||||
reply `v2.1`. `tooldata_db_getall()` sends `g`, resets local tooldata, reads
|
||||
tool lines until `FINI`, and calls `tooldata_read_entry()` for each line.
|
||||
|
||||
`tooldata_db_notify()` sends:
|
||||
|
||||
- `l ...` for `SPINDLE_LOAD`;
|
||||
- `u ...` for `SPINDLE_UNLOAD`;
|
||||
- `p ...` for `TOOL_OFFSET`.
|
||||
|
||||
The demo `db.py` uses LinuxCNC's `tooldb` module with callbacks for:
|
||||
|
||||
- `user_get_tool`;
|
||||
- `user_put_tool`;
|
||||
- `user_load_spindle_nonran_tc` or `user_load_spindle_ran_tc`;
|
||||
- `user_unload_spindle_nonran_tc` or `user_unload_spindle_ran_tc`.
|
||||
|
||||
It maintains a persistent flat-file database, updates tool usage minutes, and
|
||||
can call `linuxcnc.command().load_tool_table` to synchronize changes back to
|
||||
LinuxCNC. Those details are DB process behavior, not interpreter file
|
||||
execution behavior.
|
||||
|
||||
### Boundary Decision
|
||||
|
||||
This row remains `L4-TOOL-DB`. A standalone adapter that merely loads a
|
||||
fallback `.tbl` file would be wrong for this config because DB mode explicitly
|
||||
replaces the tool table file path.
|
||||
|
||||
### Current Machine-Readable Artifact
|
||||
|
||||
`tool-db-process-boundary-summary.tsv` records the current designed-but-blocked
|
||||
protocol and state target set for this boundary. It records `DB_PROGRAM =
|
||||
./db_nonran.py`, the LinuxCNC tooldata protocol messages `v2.1`, `g`, `l`, `u`,
|
||||
and `p`, the demo DB callbacks `user_get_tool`, `user_put_tool`,
|
||||
`user_load_spindle_nonran_tc`, and `user_unload_spindle_nonran_tc`, and the
|
||||
nonrandom database state targets such as `T10..T19`, `/tmp/db_nonran_file`,
|
||||
`tno+100` startup pockets, and pocket-0 spindle load/unload behavior.
|
||||
`tool-db-process-protocol-gates.tsv` is the normalized companion table for
|
||||
that evidence. It splits the blocked DB boundary into pending protocol-message,
|
||||
DB-program-callback, and state-target gates, including the `v2.1` startup
|
||||
reply, `g` get-all through `FINI`, `l`/`u`/`p` notifications, demo callback
|
||||
registration, ignored `TOOL_TABLE`, startup tools, nonrandom pocket mapping,
|
||||
and persistence/sync targets. The current table keeps `proof_status=pending`,
|
||||
`execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`tool-db-process-transaction-plan.tsv` records the next non-executing contract
|
||||
for this boundary. It orders the pending DB protocol into startup handshake,
|
||||
initial get-all, spindle load notify, tool offset notify, and spindle unload
|
||||
notify steps. Each step binds one LinuxCNC protocol message to the expected
|
||||
`db.py` callback, required native/Node/browser proof, and source-traceable
|
||||
state targets. This is still a transaction contract only; it does not start
|
||||
`DB_PROGRAM`, run Python, mutate `/tmp/db_nonran_file`, fall back to a tool
|
||||
table, or permit standalone/browser promotion.
|
||||
`tool-db-process-native-protocol-alignment.tsv` aligns those generated gates
|
||||
with the native source probe stdout keys, such as `tool_db_v2_1_handshake`,
|
||||
`tool_db_getall_g_until_fini`, `tool_db_notify_l_u_p_protocol`, and
|
||||
`tool_db_program_nonran_state_targets`. This table proves the generated gates
|
||||
still match LinuxCNC-owned task/tooldata/config evidence, but it also remains
|
||||
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`tool-db-process-native-runtime-readiness.tsv` records whether the host has
|
||||
the runtime pieces needed to attempt a DB process protocol probe. It checks
|
||||
`python3`, `linuxcnc`, `milltask`, `halcmd`, the configured
|
||||
`axis/db_demo/db_nonran.py` program, and the LinuxCNC Python `linuxcnc.so` and
|
||||
`tooldb.py` modules. It records PATH/source evidence where available and keeps
|
||||
the DB boundary blocked with `proof_status=pending`, `execution_enabled=0`,
|
||||
and `promotion_allowed=0`.
|
||||
`tests/native/probe_tool_db_runtime.sh` is wired into `build_native_probes.sh`
|
||||
as `linuxcnc_tool_db_runtime_probe`. On hosts without the required DB runtime
|
||||
commands it exits successfully with `tool_db_runtime_probe_status =
|
||||
skipped_missing_host_runtime`; on a ready host it remains disabled by default.
|
||||
If explicitly enabled with `ENABLE_TOOL_DB_RUNTIME_PROBE=1`, it starts the
|
||||
vendored `DB_PROGRAM`, drives the LinuxCNC `tooldb.py` `v2.1`/`g`/`p`/`l`/`u`
|
||||
protocol over stdin/stdout, verifies nonrandom `T10..T19` startup state, tool
|
||||
update, spindle load/unload, and flat-file persistence, then reports
|
||||
`runtime_protocol_probe_passed`.
|
||||
|
||||
The artifact keeps `execution_enabled=0` and `promotion_allowed=0`. It is not
|
||||
a standalone tool database executor, does not replace `DB_PROGRAM` with a
|
||||
fallback `.tbl`, and does not make `axis/db_demo/base.ngc` a Node/browser
|
||||
representative.
|
||||
|
||||
`linuxcnc_tool_db_boundary_probe` is the current native guard for this
|
||||
evidence. It reads the LinuxCNC source `db_nonran.ini`, `db.py`,
|
||||
`src/emc/task/taskclass.cc`, and `src/emc/tooldata/tooldata_db.cc`, then
|
||||
verifies the DB mode owner path, child-process protocol, `v2.1` startup
|
||||
handshake, `g`/`FINI` get-all path, `l`/`u`/`p` notifications, demo DB
|
||||
callbacks, and nonrandom state targets. This is a source/protocol-target proof
|
||||
only: it does not start `DB_PROGRAM`, run the Python tooldb loop, mutate the
|
||||
flat-file database, or permit promotion.
|
||||
|
||||
### Candidate Boundary
|
||||
|
||||
A valid future boundary should be a narrow tool-database host adapter:
|
||||
|
||||
1. Reuse `tooldata_db.cc` and `tooldata_common.cc` for DB mode behavior.
|
||||
2. Isolate the process-spawn edge behind a host abstraction so native can spawn
|
||||
the configured DB program, while WASM/browser use an explicitly designed
|
||||
equivalent only if it preserves the LinuxCNC protocol.
|
||||
3. Preserve the `v2.1`, `g`, `l`, `u`, and `p` command/reply protocol rather
|
||||
than parsing or inventing tool semantics in JavaScript.
|
||||
4. Treat `db.py` as a config-owned process dependency. If it is not executable
|
||||
in a target layer, that layer remains blocked.
|
||||
|
||||
### Required Proof Before Promotion
|
||||
|
||||
Native proof:
|
||||
|
||||
- The existing `linuxcnc_tool_db_boundary_probe` source/protocol guard must
|
||||
continue to prove that the pending DB gates come from LinuxCNC task/tooldata
|
||||
and demo DB sources rather than project-owned behavior.
|
||||
- A probe starts `db_nonran.py` through the LinuxCNC DB path and verifies the
|
||||
`v2.1` handshake.
|
||||
- A get-all request returns the expected nonrandom startup tools.
|
||||
- A load/unload or update path changes DB state through `l`, `u`, or `p` and
|
||||
is reflected by a subsequent get-all.
|
||||
|
||||
Node WASM proof:
|
||||
|
||||
- The same protocol is visible through the SDK boundary.
|
||||
- Tool lookup/update behavior matches the native proof without staging a fake
|
||||
`.tbl` replacement.
|
||||
|
||||
Browser proof:
|
||||
|
||||
- Browser validation calls the Node-proven SDK/WASM boundary.
|
||||
- OPFS may store DB files only as host persistence. It must not implement DB
|
||||
command semantics.
|
||||
|
||||
## Python-Remap Inventory Boundary
|
||||
|
||||
Python-remap rows remain inventory-only. The current
|
||||
`blocked-dependency-summary.tsv` records Python modules, remap/prolog/epilog
|
||||
functions, NGC-only subpaths, and HAL/UI/HALUI declarations.
|
||||
`python-remap-boundary-summary.tsv` preserves the same evidence per blocked
|
||||
path and records LinuxCNC Python runtime owner evidence from
|
||||
`src/emc/rs274ngc/interp_python.cc` and
|
||||
`src/emc/pythonplugin/python_plugin.cc`. `python-remap-family-summary.tsv`
|
||||
aggregates it by runtime family so mixed Python/NGC-only subpaths remain
|
||||
visible without promoting execution. No Python-remap row should be promoted
|
||||
until a separate LinuxCNC-owned Python runtime boundary exists and has native
|
||||
plus Node/browser proof appropriate to the target layer.
|
||||
`python-remap-runtime-gates.tsv` expands each blocked path into Python module,
|
||||
remap/prolog/epilog callable, NGC-only subpath, process-assumption, and
|
||||
runtime-owner gates. `python-remap-native-runtime-alignment.tsv` aligns those
|
||||
generated gates with the current native source probe stdout. Runtime owner
|
||||
gates map to exact LinuxCNC runtime proof keys such as
|
||||
`python_runtime_pycall_dispatch` and `python_plugin_callable_invoke`; dependency
|
||||
inventory gates map to representative family inventory proof or the aggregate
|
||||
`python_remap_native_source_inventory_proof`. Both tables remain
|
||||
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`python-remap-runtime-contract.tsv` records the next non-executing family-level
|
||||
contract. It keeps one row per blocked Python-remap family, records the
|
||||
LinuxCNC-owned Python runtime phases (`initialize_python`,
|
||||
`apply_ini_python_path`, `execute_toplevel`, callable lookup/invoke,
|
||||
remap-phase dispatch, generator finish, execute-string/file, and reload),
|
||||
aggregates Python modules/callables/NGC-only subpaths, carries HAL/UI/HALUI
|
||||
process assumptions, and binds each family to native, Node, and browser proof
|
||||
requirements. It is a runtime contract only: it does not initialize Python,
|
||||
import modules, execute callbacks, or permit promotion.
|
||||
`python-remap-native-runtime-readiness.tsv` records the current host/source
|
||||
readiness for future native Python-remap runtime probes. It checks `python3`,
|
||||
`linuxcnc`, the LinuxCNC interpreter/plugin owner source files, and each
|
||||
configured Python module by blocked runtime family. The artifact remains a
|
||||
gate only: every row stays `proof_status=pending`, `execution_enabled=0`, and
|
||||
`promotion_allowed=0`.
|
||||
`python-remap-native-runtime-state-plan.tsv` records the non-executing runtime
|
||||
state plan for those future probes. It binds each blocked Python-remap family
|
||||
to the required LinuxCNC Python phases, configured modules, callables,
|
||||
NGC-only subpaths, process assumptions, readiness counts, source-alignment
|
||||
artifacts, and future native proof targets. It does not initialize Python,
|
||||
import modules, execute callbacks, or permit promotion.
|
||||
`python-remap-native-runtime-fixture-plan.tsv` selects the first native runtime
|
||||
fixture target, `axis/remap/stop-lookahead/nc_files`. That family is the
|
||||
smallest current Python runtime lifecycle candidate because it needs the
|
||||
LinuxCNC Python initialization/path/toplevel/runtime phases and configured
|
||||
modules but avoids Python callable and NGC-only subpath complexity. The
|
||||
fixture plan is still non-executing: it records the proof target only and
|
||||
keeps `proof_status=pending`, `execution_enabled=0`, and
|
||||
`promotion_allowed=0`.
|
||||
`tests/native/probe_python_remap_runtime.sh` is wired into
|
||||
`build_native_probes.sh` as `linuxcnc_python_remap_runtime_probe`. It records
|
||||
the stop-lookahead fixture identity, configured Python modules, required
|
||||
LinuxCNC Python runtime phases, host command readiness, and source/module
|
||||
availability. On hosts without `linuxcnc`, it exits successfully but reports
|
||||
`python_remap_runtime_probe_status=skipped_missing_host_runtime`; on hosts with
|
||||
the runtime available it remains disabled by default. If explicitly enabled
|
||||
with `ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1`, it follows the `demo.ini`
|
||||
`[PYTHON]` path/toplevel declarations, imports the vendored stop-lookahead
|
||||
modules, verifies `queuebuster` callable lookup, confirms the callable returns
|
||||
a generator, and checks that the first yield is the LinuxCNC
|
||||
`INTERP_EXECUTE_FINISH` value from `interp_return.hh` before reporting
|
||||
`runtime_lifecycle_probe_passed`.
|
||||
|
||||
`linuxcnc_python_remap_boundary_probe` is the current native guard for this
|
||||
inventory. It reads the LinuxCNC Python runtime owners and representative
|
||||
config families (`axis/laser`, `axis/remap/cycle`, TWP nutating, and
|
||||
`gmoccapy` stdglue), then verifies the runtime dispatch/phase/callable owner
|
||||
evidence plus representative Python modules, remap callables, prolog/epilog
|
||||
callables, and Python path declarations. This is source inventory only: it
|
||||
does not initialize Python, import modules, execute remap callbacks, or permit
|
||||
promotion.
|
||||
|
||||
## Next Boundary Worklist
|
||||
|
||||
`next-boundary-worklist.tsv` is the machine-readable handoff for the next
|
||||
runtime-boundary design phase. It is a planning artifact only: every row keeps
|
||||
`execution_enabled=0` and `promotion_allowed=0`, records the LinuxCNC owner set,
|
||||
records runtime owner evidence, records required native, Node, and browser
|
||||
proof, and names the next boundary-design action before promotion.
|
||||
|
||||
`boundary-proof-gates.tsv` is the machine-readable proof checklist derived from
|
||||
that worklist. It expands every target into native, Node, and browser proof
|
||||
rows and keeps each row at `proof_status=pending`, `execution_enabled=0`, and
|
||||
`promotion_allowed=0`. It must fail validation if a future edit tries to
|
||||
promote a target without first replacing the pending gate with LinuxCNC-owned
|
||||
proof for that layer. For the current blockers, event-only `USER_M_COMMAND`
|
||||
coverage is insufficient for `millturn`, `.tbl` fallback coverage is
|
||||
insufficient for `db_demo`, and JavaScript-owned behavior is insufficient for
|
||||
Python runtime families.
|
||||
|
||||
`native-proof-alignment-summary.tsv` records whether the native source proof
|
||||
summary, when present, is consumed by generated worklist and native proof-gate
|
||||
rows. It currently aligns the `millturn` user-M source-state proof, `db_demo`
|
||||
tool database protocol source proof, and Python runtime source-inventory proof
|
||||
without enabling execution or promotion.
|
||||
`runtime-boundary-native-alignment-summary.tsv` is the aggregate guard for the
|
||||
detailed native alignment artifacts. It requires every user-M, tool DB, and
|
||||
Python alignment row to have native stdout evidence, `alignment_ok=1`,
|
||||
`proof_status=pending`, `execution_enabled=0`, and `promotion_allowed=0`.
|
||||
`build/native/native-runtime-probe-summary.tsv` is the native companion for the
|
||||
disabled runtime probe entry points. It summarizes the current runtime
|
||||
readiness for `millturn` user-M, `db_demo` tool DB, and the Python
|
||||
stop-lookahead fixture, records the missing host runtime requirements, points
|
||||
back to each probe stdout log, and keeps every row at `execution_enabled=0` and
|
||||
`promotion_allowed=0`.
|
||||
`runtime-boundary-contract-summary.tsv` is the aggregate guard for the
|
||||
non-executing runtime contracts. It ties the `millturn` user-M transition
|
||||
contract, `db_demo` tool DB transaction contract, and Python-remap family
|
||||
runtime contract to their native-alignment artifacts, and requires every
|
||||
contract row to remain pending with execution and promotion disabled.
|
||||
`blocked-runtime-promotion-lock.tsv` is the final machine-readable lock for
|
||||
this phase. It combines the worklist, native/Node/browser proof gates, and
|
||||
runtime alignment summary so every blocked target has `lock_active=1` until a
|
||||
LinuxCNC-owned runtime boundary replaces the pending gates.
|
||||
|
||||
The first two priorities are the current designed-but-disabled full-process
|
||||
blocks:
|
||||
|
||||
1. `external_user_m_process` for `axis/vismach/millturn/example.ngc`;
|
||||
2. `tool_database_process` for `axis/db_demo/base.ngc`.
|
||||
|
||||
Python runtime family rows follow as inventory-only targets. They must remain
|
||||
behind a LinuxCNC-owned Python runtime boundary and must not be used to vendor
|
||||
or execute `gmoccapy`, `axis/laser`, TWP nutating, or other Python-remap
|
||||
families by path alone.
|
||||
|
||||
## Current Validation
|
||||
|
||||
These commands must continue to pass after any change to this document or to
|
||||
the inventory code:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
wasm-port/tests/browser/verify_interp_browser.sh
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
wasm-port/tools/verify_vendor_sync.sh
|
||||
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
|
||||
```
|
||||
218
wasm-port/docs/linuxcnc-test-porting-tracker.md
Normal file
218
wasm-port/docs/linuxcnc-test-porting-tracker.md
Normal file
@@ -0,0 +1,218 @@
|
||||
# LinuxCNC Test Porting Tracker
|
||||
|
||||
## Purpose
|
||||
|
||||
This file tracks the continuing work to validate this simulation port against
|
||||
LinuxCNC behavior by using LinuxCNC's own source files and test assets.
|
||||
|
||||
Hard rule: CNC semantics must come directly from LinuxCNC source. Project-owned
|
||||
code may provide build logic, runtime shims, filesystem adapters, C ABI, SDK,
|
||||
browser UI, and test runners, but must not reimplement interpreter, remap, tool,
|
||||
parameter, kinematics, planner, or machine-control behavior.
|
||||
|
||||
## Fixed Validation Gates
|
||||
|
||||
Run these checks as the baseline for every substantial porting step:
|
||||
|
||||
```bash
|
||||
wasm-port/tools/verify_upstream_baseline.sh
|
||||
wasm-port/tools/verify_vendor_sync.sh
|
||||
wasm-port/tests/native/verify_native_probes.sh
|
||||
wasm-port/tests/native/verify_nc_files.sh
|
||||
wasm-port/tests/wasm/node/verify_ini_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_interp_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_nc_files_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_tp_wasm.sh
|
||||
wasm-port/tests/opfs/node/verify_file_service.sh
|
||||
wasm-port/tests/browser/verify_ini_panel_browser.sh
|
||||
wasm-port/tests/browser/verify_interp_browser.sh
|
||||
wasm-port/tests/host/verify_host_smokes.sh
|
||||
```
|
||||
|
||||
## Status Legend
|
||||
|
||||
| Status | Meaning |
|
||||
| --- | --- |
|
||||
| Done | Native, WASM, browser, docs, and source-sync validation are complete for the current scope. |
|
||||
| In progress | Test assets or runners are present, but coverage still needs extension or verification. |
|
||||
| Planned | The item is selected for future work. |
|
||||
| Blocked | The item needs missing LinuxCNC source, runtime boundary work, or upstream baseline clarification. |
|
||||
|
||||
## Continuing Steps
|
||||
|
||||
| Step | Status | Tracking notes |
|
||||
| --- | --- | --- |
|
||||
| Lock LinuxCNC upstream baseline | Done | Baseline commit is recorded in `wasm-port/tools/upstream-baseline.txt`; keep `docs/scope-and-baseline.md` synchronized when it changes. |
|
||||
| Verify vendored source byte identity | Done | `tools/verify_vendor_sync.sh` must pass for every file in `tools/source-manifest.txt`. |
|
||||
| Reject standalone CNC semantics | Done | `tools/verify_no_standalone_cnc_semantics.sh` guards project-owned `Interp::...` definitions outside documented runtime-edge stubs. |
|
||||
| Maintain source reuse map | Done | Current sim-config, remap, interpreter, kinematics, TP, user-M, tool, named-parameter, and OPFS runtime boundaries are recorded in `docs/source-reuse-map.md`; keep updating it whenever LinuxCNC source, test assets, or runtime boundaries are added. |
|
||||
| Maintain compatibility validation notes | Done | Current native, Node WASM, browser, OPFS, and host aggregate checks are recorded in `docs/compatibility-validation.md`; keep updating it whenever a validation command or fixture class is added. |
|
||||
| Maintain sim-config coverage matrix | Done | `docs/sim-configs-coverage-matrix.md` now maps all current `verify_sim_configs.sh` records to program class, native result, current Layer 3/4 coverage, and blocked/class-coverage notes. The native harness emits `class-summary.tsv` and `path-matrix.tsv`, and the Node inventory runner checks generated/tracked path sets plus tracked class/native-status/expected-failure/blocked fields against native outputs before execution. Future changes should update the matrix only with a matching regenerated native inventory. |
|
||||
| Document validation layers | Done | `docs/compatibility-validation.md` now records Layer 1 `nc_files` basic smoke, Layer 2 `configs/sim` native strict harness, Layer 3 standalone native runtime probes, and Layer 4 WASM Node/browser representative smokes with entry commands, current results, expected boundaries, and the no-LinuxCNC-semantics rule. |
|
||||
| Build LinuxCNC test inventory | In progress | `linuxcnc/tests/remap` has been inventoried by dependency class; the first broader `linuxcnc/tests` pass now records full-process, HAL/realtime, linuxcncrsh, Python-binding, and external-component families in the blocked table. |
|
||||
| Prefer pure interpreter tests first | In progress | Continue with tests that can run through `Interp::open()`, `read()`, and `execute()` without realtime hardware or GUI. |
|
||||
| Add upstream-output comparison | In progress | Use upstream `../linuxcnc/bin/rs274` or original `test.sh` output where practical, then compare against native/WASM/browser output. |
|
||||
| Promote tests through three layers | In progress | Add each selected test first to native harnesses, then WASM Node, then browser smoke where meaningful. |
|
||||
| Record blocked tests | In progress | First broader family-level blocked batch is recorded below; continue expanding it as each `linuxcnc/tests` family is inspected. |
|
||||
| Add aggregate smoke coverage | Done | `tests/host/verify_host_smokes.sh` now runs stable Node WASM, sim-config representative WASM, sim-config Node inventory, `nc_files` WASM, OPFS, and browser smoke checks with shared builds. Current run passed with `host_wasm_opfs_browser_smokes=ok`. |
|
||||
| Add `linuxcnc/nc_files` basic examples | Done | `tests/native/verify_nc_files.sh` now runs the basic/example subset of upstream `linuxcnc/nc_files` through LinuxCNC `bin/rs274`; current native baseline is 107 total, 101 pass, 6 expected context/entry-point edges, 0 unexpected failures. Exploratory `--all` inventory is 247 total, 219 pass, 28 expected context/entry-point edges, 0 unexpected failures. A representative vendored subset now passes Node WASM and browser execution through LinuxCNC-backed `Interp::open()`/`read()`/`execute()`, with `3D_Chips.ngc` using an INI-declared tool-table context for its upstream `T1 M6` line. |
|
||||
|
||||
## Current Review Boundaries
|
||||
|
||||
Use these batches when reviewing or splitting the current uncommitted work.
|
||||
|
||||
| Batch | Scope | Files |
|
||||
| --- | --- | --- |
|
||||
| A | `run_step` status output and UI/Node/browser assertions. | `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_harness.cpp`, `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp`, `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_interp_wasm.cpp`, `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_runtime_state.hh`, `wasm-port/tests/wasm/node/verify_interp_wasm.mjs`, `wasm-port/tests/browser/interp_smoke.html`, `wasm-port/tests/fixtures/canon/arc_semantics.events`. |
|
||||
| B | `nc_files` representative smoke and `3D_Chips.ngc` tool-table context. | `wasm-port/tests/native/verify_native_probes.sh`, `wasm-port/tests/wasm/node/verify_interp_wasm.mjs`, `wasm-port/tests/browser/interp_smoke.html`, `wasm-port/tools/source-manifest.txt`, vendored `wasm-port/vendor/linuxcnc/nc_files/3D_Chips.ngc`, `arcspiral.ngc`, `factorial.ngc`, `hole-circle.ngc`, and `m6demo.ngc`. |
|
||||
| C | Selected upstream `tests/interp/*` and `tests/ccomp/*` fixture intake. | `wasm-port/tests/native/verify_native_probes.sh`, `wasm-port/tests/wasm/node/verify_interp_wasm.mjs`, `wasm-port/tests/browser/interp_smoke.html`, `wasm-port/tools/extract_sources.sh`, `wasm-port/tools/source-manifest.txt`, vendored `wasm-port/vendor/linuxcnc/tests/interp/...`, and vendored `wasm-port/vendor/linuxcnc/tests/ccomp/...` files listed in `tools/source-manifest.txt`. |
|
||||
| D | Interpreter regression coverage for G92 parameter persistence, tool tables, G10/G52/G71/G72/G76, rotation, INI parameters, M19, M98/M99, subroutine lookup, sequence numbers, nested-sub errors, and ABORT hot comments. | `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_harness.cpp`, `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_interp_minimal_runtime.cpp`, `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_interp_wasm.cpp`, `wasm-port/tests/native/verify_native_probes.sh`, `wasm-port/tests/wasm/node/verify_interp_wasm.mjs`, `wasm-port/tests/browser/interp_smoke.html`, and the corresponding vendored upstream fixture files under `wasm-port/vendor/linuxcnc/tests/interp/`. |
|
||||
| E | Generic INI-context staging helper and Node/browser synthetic coverage. | `wasm-port/runtime/sdk/src/sim-config-staging.js`, `wasm-port/runtime/sdk/src/index.js`, `wasm-port/runtime/sdk/README.md`, `wasm-port/tests/wasm/node/verify_sim_configs_wasm.mjs`, `wasm-port/tests/browser/interp_smoke.html`. |
|
||||
| F | Validation docs, source reuse map, tracker updates, native probe build/source-sync glue. | `wasm-port/docs/compatibility-validation.md`, `wasm-port/docs/drift-report.md`, `wasm-port/docs/linuxcnc-test-porting-tracker.md`, `wasm-port/docs/source-reuse-map.md`, `wasm-port/tools/build_native_probes.sh`, `wasm-port/tools/extract_sources.sh`, `wasm-port/tools/source-manifest.txt`, `wasm-port/tools/verify_vendor_sync.sh`. |
|
||||
|
||||
Vendored files in the current workset must stay byte-identical to upstream and
|
||||
covered by both `wasm-port/tools/source-manifest.txt` and
|
||||
`wasm-port/tools/verify_vendor_sync.sh`. The current untracked vendor groups
|
||||
are `wasm-port/vendor/linuxcnc/tests/ccomp/`, selected
|
||||
`wasm-port/vendor/linuxcnc/tests/interp/bad/*.ngc`, and selected upstream
|
||||
directories under `wasm-port/vendor/linuxcnc/tests/interp/` including
|
||||
`cam-nisley`, `crazy-paths`, `g10`, `g52`, `g71-*`, `g72-*`, `g76`, `good`,
|
||||
`inside-corners`, `inverse-time-with-comp`, and selected `m98m99` cases.
|
||||
|
||||
## LinuxCNC Test Asset Queue
|
||||
|
||||
| LinuxCNC test path | Status | Current target | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| `nc_files` basic/example programs | Done | Native, WASM Node, browser | Native coverage is tracked by `wasm-port/tests/native/verify_nc_files.sh`; complete programs execute strictly, macro/library files are wrapped with temporary `M2` for load/parse validation, and six known LinuxCNC-native context edges are classified explicitly. `--all` also inventories probe/plasmac/NURBS files and currently classifies all 28 non-passing files as expected runtime-context or entry-point edges. The representative vendored WASM/browser subset covers `3D_Chips.ngc`, `arcspiral.ngc`, `hole-circle.ngc`, `factorial.ngc`, and `m6demo.ngc` through LinuxCNC-backed file execution, with `3D_Chips.ngc` staged through `runFileWithIni()` and an INI-declared `tool.tbl` instead of being judged without its required tool context. |
|
||||
| `tests/remap/duplicate-o-word` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/duplicate-o-word`; validates duplicate O-word behavior through vendored LinuxCNC remap/O-word/file execution paths. |
|
||||
| `tests/remap/fail/args.0` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/fail/args.0`; validates required `P`/`Q` remap arguments when both are present through vendored LinuxCNC remap argument handling. |
|
||||
| `tests/remap/fail/args.1` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/fail/args.1`; validates LinuxCNC missing-`Q` remap error text and upstream-style continue-after-error flow. |
|
||||
| `tests/remap/fail/args.2` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/fail/args.2`; validates LinuxCNC missing-`P,Q` remap error text and upstream-style continue-after-error flow. |
|
||||
| `tests/remap/fail/body-ngc` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/fail/body-ngc`; validates LinuxCNC NGC remap body failure text and remap level restoration after continue-on-error execution. |
|
||||
| `tests/remap/m30-interaction` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/m30-interaction`; validates LinuxCNC remap/M30 interaction through vendored interpreter paths. |
|
||||
| `tests/remap/nested-remaps-oword` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/nested-remaps-oword`; validates nested NGC remaps through vendored LinuxCNC O-word dispatch. |
|
||||
| `tests/remap/posargs.0` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/posargs.0`; validates LinuxCNC positional remap arguments and `OWORD_NARGS` through vendored interpreter paths. |
|
||||
| `tests/remap/sequencing` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/remap/sequencing`; validates LinuxCNC G/M remap execution ordering through vendored interpreter paths. |
|
||||
| `tests/remap/remap-io` NGC-only branch | Done | Native, WASM Node, browser | Vendored `test-ngc.ini` plus `io_*.ngc` subroutines validate LinuxCNC NGC remaps for M62-M68 and M66 immediate input through vendored `Interp::parse_remap()` and `Interp::execute()`; the standalone boundary only stages inputs and captures canonical events. |
|
||||
| `tests/interp/do-while-break` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/do-while-break`; validates LinuxCNC O-word do/while/break flow through vendored `Interp::open()`, `read()`, and `execute()` paths. |
|
||||
| `tests/interp/oword-bug315` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/oword-bug315`; validates LinuxCNC O-word break-out-of-subroutine behavior through vendored file execution paths. |
|
||||
| `tests/interp/oword-bug315-p2` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/oword-bug315-p2`; validates LinuxCNC dynamic O-word subroutine call selection through vendored file execution paths. |
|
||||
| `tests/interp/exists` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/exists`; validates LinuxCNC `EXISTS[]` parameter handling through vendored file execution paths. |
|
||||
| `tests/interp/return-value` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/return-value`; validates LinuxCNC O-word return-value and `#<_value>` behavior through vendored file execution paths. |
|
||||
| `tests/interp/subs-follow-main` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/subs-follow-main`; validates LinuxCNC subroutines after main program text through vendored file execution paths. |
|
||||
| `tests/interp/fractional-linenumbers` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/fractional-linenumbers`; validates LinuxCNC fractional line-number parsing through vendored file execution paths. |
|
||||
| `tests/interp/cam-nisley` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/cam-nisley`; validates the upstream storm-door latch cam toolpath through `Interp::open()`, `read()`, and `execute()` with upstream `test.tbl` staged through an INI-declared tool table. Native, WASM Node, and browser checks also assert that bare execution without that tool-table context fails at the upstream `T1 M6` lookup. |
|
||||
| `tests/interp/namedparam-bug424` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/namedparam-bug424`; validates LinuxCNC named-parameter parsing regression behavior through vendored file execution paths. |
|
||||
| `tests/interp/flowsnake` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/flowsnake`; validates LinuxCNC recursive O-word subroutine execution, expression evaluation, and comment argument handling through a pure file-execution fractal toolpath. |
|
||||
| `tests/interp/inside-corners` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/inside-corners`; validates LinuxCNC cutter-compensation geometry through concave corners, convex corners, and tangencies across XY and ZX plane file execution. |
|
||||
| `tests/interp/inverse-time-with-comp` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/inverse-time-with-comp`; validates LinuxCNC inverse-time feed (`G93`), units-per-minute feed (`G94`), and cutter compensation interaction across uncompensated, right-compensated, and left-compensated subprogram calls. |
|
||||
| `tests/ccomp` selected standalone cases | Done | Native, WASM Node, browser | Vendored `lathe-comp`, `mill-g90g91g92`, `mill-line-arc-entry`, and `mill-zchanges` under `wasm-port/vendor/linuxcnc/tests/ccomp`; validates standalone LinuxCNC cutter-compensation file execution with upstream tool tables staged through LinuxCNC `tooldata_load()` and `Interp::load_tool_table()`, with the standalone minimal interpreter startup now aligned to LinuxCNC's default incremental IJK mode (`G91.1`). |
|
||||
| `tests/interp/bad` selected file errors | Done | Native, WASM Node, browser | Vendored selected upstream files under `wasm-port/vendor/linuxcnc/tests/interp/bad`; validates LinuxCNC file-execution error text for canned-cycle A-axis rejection, center-format arc radius mismatch rejection, cutter-compensation arc-exit/gouging rejection, malformed `EXISTS[]` parameter expressions, nested subroutine definitions, zero-feed G1, missing arc center/radius words, and probe motion without axes. |
|
||||
| `tests/interp/good` selected arc tolerance cases | Done | Native, WASM Node, browser | Vendored selected upstream files under `wasm-port/vendor/linuxcnc/tests/interp/good`; validates LinuxCNC center-format arc tolerance acceptance across small/medium/big inch and metric cases through `ARC_FEED` canonical output and successful program end. |
|
||||
| `tests/interp/g6164` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/g6164`; validates LinuxCNC `G61`/`G64` path-control and naive-cam tolerance canonical output through vendored file execution paths. |
|
||||
| `tests/interp/g72-facing` and `tests/interp/g72-missing-iteration` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/g72-facing` and `wasm-port/vendor/linuxcnc/tests/interp/g72-missing-iteration`; validates LinuxCNC G72 lathe facing canned-cycle iteration behavior through vendored file execution paths, canonical `STRAIGHT_FEED` endpoints, and successful program end without timeout. |
|
||||
| `tests/interp/g71-*` selected lathe canned-cycle cases | Done | Native, WASM Node, browser | Vendored `g71-endless-loop`, `g71-endless-loop2`, `g71-endless-loop_2`, and `g71-with-g70`; validates LinuxCNC G71 roughing and G70 finishing canned-cycle regressions through vendored file execution paths, canonical feed/arc endpoints, and successful program end without timeout. |
|
||||
| `tests/interp/g76` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/g76`; validates LinuxCNC G76 lathe threading with upstream `test.tbl` staged through an INI-declared tool table, covering tool change, spindle start, speed-feed sync, threading feed events, and successful program end. |
|
||||
| `tests/interp/g33.1` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/g33.1`; validates LinuxCNC rigid-tap `G33.1` file execution, spindle start, feed synchronization, and `RIGID_TAP` canonical output through vendored interpreter paths. |
|
||||
| `tests/interp/g10` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/g10`; validates LinuxCNC `G10 L1/L10/L11/L2/L20`, tool-table updates, tool-length offset application timing, active G5X offset changes, G92 interaction, and XY-rotation behavior through vendored file execution paths with upstream `test.tbl` files staged through LinuxCNC tool-table loading. |
|
||||
| `tests/interp/g52/g52-g92-interaction` plus G92 startup persistence | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/g52/g52-g92-interaction`; validates LinuxCNC G52/G92 shared offset parameter behavior, `G92.1`/`G92.2`/`G92.3` transitions, and `SET_G92_OFFSET` canonical output through vendored file execution paths. Native, WASM Node, and browser validation also stage a `[RS274NGC]PARAMETER_FILE` and exercise vendored `ini_load()`/`Interp::init()`/`restore_parameters()` startup behavior, including `DISABLE_G92_PERSISTENCE` clearing parameters 5210-5219. |
|
||||
| `tests/interp/rotation` selected pure interpreter cases | Done | Native, WASM Node, browser | Vendored selected upstream cases under `wasm-port/vendor/linuxcnc/tests/interp/rotation`; validates LinuxCNC machine-coordinate `G53` absolute-position named parameters `#<_abs_x>`, `#<_abs_y>`, and `#<_abs_z>` across G54 offsets, G92 offsets, XY rotation, and inch/millimeter modes, plus rotated-coordinate `G28` and `G53` endpoint behavior through vendored file execution paths. |
|
||||
| `tests/interp/iniparam` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/iniparam`; validates LinuxCNC `_ini[...]` lookup, missing INI-parameter error text, and continued file execution through vendored file execution paths and `INI_FILE_NAME`. |
|
||||
| `tests/interp/iniparam-failassign` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/iniparam-failassign`; validates LinuxCNC `_ini[...]` read-only named-parameter assignment rejection through vendored file execution paths and `INI_FILE_NAME`. |
|
||||
| `tests/interp/m19` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/m19`; validates LinuxCNC `M19` spindle orient canonical events, wait timeout, and `[RS274NGC]ORIENT_OFFSET` handling through vendored file execution paths. |
|
||||
| `tests/interp/magic_comments/param_format_printing` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/magic_comments/param_format_printing`; validates LinuxCNC `(DEBUG,...)` magic-comment parameter formatting for integer, default float, and explicit precision formats through vendored file execution paths. |
|
||||
| `tests/interp/m98m99` selected pure interpreter cases | Done | Native, WASM Node, browser | Vendored selected upstream cases under `wasm-port/vendor/linuxcnc/tests/interp/m98m99`; validates LinuxCNC Fanuc-style `M98/M99`, missing-P-word, missing-subprogram, mixed Fanuc/RS274NGC sub-style, `DISABLE_FANUC_STYLE_SUB` INI-gated errors, main-program O-word termination rules, parameter scope, loop counts including `L0`, nested numbered subprograms, subprograms after main program text, leading-zero O-word lookup, named and numbered main programs, and expression-based O-sub/M98 calls through vendored file execution paths. |
|
||||
| `tests/interp/sub-call-from-sub` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/sub-call-from-sub`; validates LinuxCNC external subroutine calls from another external subroutine through vendored file execution paths and LinuxCNC INI `SUBROUTINE_PATH`. |
|
||||
| `tests/interp/sequence-number` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/sequence-number`; validates LinuxCNC `#<_line>` reporting in main files and external subroutines through vendored file execution paths and LinuxCNC INI `SUBROUTINE_PATH`. |
|
||||
| `tests/interp/nested-sub-error` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/nested-sub-error`; validates LinuxCNC nested-subroutine-definition rejection through vendored file execution paths and LinuxCNC INI `SUBROUTINE_PATH`. |
|
||||
| `tests/interp/nested-sub-in-file-error` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/nested-sub-in-file-error`; validates LinuxCNC rejection of forward seek to a later numbered subroutine in the same external subroutine file through vendored file execution paths and LinuxCNC INI `SUBROUTINE_PATH`. |
|
||||
| `tests/interp/oword-unwind` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/oword-unwind`; validates LinuxCNC O-word stack unwind after a subroutine error through upstream-style continue-on-error file execution and LinuxCNC INI `SUBROUTINE_PATH`. |
|
||||
| `tests/interp/abort-hot-comment` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/tests/interp/abort-hot-comment`; validates LinuxCNC `(ABORT,...)` hot-comment parameter expansion for numbered, named, and INI named parameters through vendored file execution paths and `INI_FILE_NAME`. |
|
||||
| `tests/interp/compile` | Done | Native source probe | Upstream `linuxcnc/tests/interp/compile` is a C++ compile/source test (`use-rs274.cc`), not a runtime `.ngc` interpreter fixture. The current port covers this class through the native `linuxcnc_rs274_compile_probe` object build in `wasm-port/tools/build_native_probes.sh`; do not add it to file-execution intake unless an upstream runtime fixture appears. |
|
||||
| `configs/sim/axis/foam/foam.ngc` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/configs/sim/axis/foam`; validates LinuxCNC INI-driven `U/V` axis mask handling through `axis_foam.ini` and vendored file execution paths. |
|
||||
| `configs/sim/axis/geometry/xyzc.ngc` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/configs/sim/axis/geometry`; validates LinuxCNC `USER_M_PATH` registration with the real executable `M110` while process execution remains a deterministic standalone `USER_M_COMMAND` boundary. |
|
||||
| `configs/sim/axis/external_offsets/*.ngc` M111 demos | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets`; validates `dyn_demo.ngc`, `eoffsets.ngc`, `jwp_z.ngc`, and `opa_demo.ngc` with their corresponding INI files, real executable `M111`, shared `eoffset.tbl`, and `opa_demo.ngc`'s `circles.ngc` subroutine while process execution remains a deterministic standalone boundary. |
|
||||
| `configs/sim/axis/gladevcp/probe.ngc` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/configs/sim/axis/gladevcp`; validates plain INI/tool-table main-program staging through `gladevcp_panel.ini` without treating the GTK panel process as browser runtime. |
|
||||
| `configs/sim/axis/vismach/melfa-sim/example.ngc` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/melfa-sim`; validates a robot/Genser-style remap machine representative through real INI, tool table, and M428/M429/M430 remap subroutines while GUI/HAL process launch remains outside the standalone boundary. |
|
||||
| `configs/sim/axis/vismach/puma/puma_cube.ngc` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/puma`; validates a PUMA machine-context representative through real INI, tool table, and remap subroutine staging while external GUI/HAL process launch remains outside the standalone boundary. |
|
||||
| `configs/sim/woodpecker/on_abort.ngc` | Done | Native, WASM Node, browser | Vendored under `wasm-port/vendor/linuxcnc/configs/sim/woodpecker`; validates deterministic `on_abort` file execution through the real machine INI and tool table without expanding browser coverage to unrelated full-process UI flows. |
|
||||
| More `tests/remap/*` interpreter-only cases | Planned | Native first | Select cases that do not require Python behavior beyond documented runtime-edge stubs unless the required LinuxCNC source path is ported. |
|
||||
| More `tests/interp/*` interpreter-only cases | Planned | Native first | Select upstream interpreter cases that can run through vendored file execution without Python, HAL, realtime motion, or GUI. |
|
||||
| `tests/rs274ngc/*` candidates | Planned | Native first | Use upstream `rs274` as the baseline where output is comparable. |
|
||||
| Tool-table regression assets | Planned | Native and WASM Node | Must call vendored `tooldata_common.cc`; project code may only provide storage callbacks and filesystem boundaries. |
|
||||
| Kinematics tests | Planned | Native probes first | Add runtime probes only around LinuxCNC kinematics entry points; HAL lifecycle remains a shim boundary. |
|
||||
| Trajectory-planner tests | Planned | Native then WASM Node | Must call vendored LinuxCNC TP APIs; project code may only seed deterministic status/config state. |
|
||||
|
||||
## Per-Test Intake Checklist
|
||||
|
||||
Use this checklist when adding each LinuxCNC upstream test:
|
||||
|
||||
- Record the upstream test path.
|
||||
- Copy or extract the original LinuxCNC files into `wasm-port/vendor/linuxcnc/`.
|
||||
- Add the copied files to the relevant manifest or vendor-sync rule.
|
||||
- Keep `.ini`, `.ngc`, `expected`, and original support files unchanged.
|
||||
- Identify the LinuxCNC source files that own the behavior being tested.
|
||||
- Add native runner coverage that calls vendored LinuxCNC code.
|
||||
- Add WASM Node coverage through the exported C ABI and SDK filesystem bridge.
|
||||
- Add browser coverage if the behavior crosses SDK, OPFS, or UI boundaries.
|
||||
- Compare against upstream LinuxCNC output where practical.
|
||||
- Update `docs/source-reuse-map.md`.
|
||||
- Update `docs/compatibility-validation.md`.
|
||||
- Add any blocked dependency to the blocked-test table below.
|
||||
|
||||
## Blocked Test Table
|
||||
|
||||
| LinuxCNC test path | Status | Blocking dependency | Next action |
|
||||
| --- | --- | --- | --- |
|
||||
| `tests/remap/introspect` | Blocked | Python remap modules and Python interpreter introspection. | Port or expose the required LinuxCNC Python remap runtime boundary before executing. |
|
||||
| `tests/remap/fail/body-py` | Blocked | Python remap body error path. | Port the LinuxCNC Python remap body boundary before executing. |
|
||||
| `tests/remap/fail/canon_error` | Blocked | Python prolog plus `emccanon.CANON_ERROR`. | Port LinuxCNC Python prolog and canonical-error boundary before executing. |
|
||||
| `tests/remap/fail/epilog` | Blocked | Python epilog failure path. | Port LinuxCNC Python epilog boundary before executing. |
|
||||
| `tests/remap/fail/prolog` | Blocked | Python prolog failure path. | Port LinuxCNC Python prolog boundary before executing. |
|
||||
| `tests/remap/oword-pycall` | Blocked | Python O-word callable modules plus tool-change path. | Port the LinuxCNC Python O-word call boundary and required tool-change runtime edges. |
|
||||
| `tests/remap/predefined-named-params` | Blocked | Python predefined named parameters. | Port the LinuxCNC Python named-parameter registration boundary. |
|
||||
| `tests/remap/remap-io` full process/UI branch | Blocked | Full LinuxCNC process, HAL pins, motion I/O status propagation, and Python UI/test driver. | Keep the full process test upstream-only until those runtime boundaries exist; the NGC-only `test-ngc.ini` subpath is covered separately above. |
|
||||
| `tests/remap/remap-reentry` | Blocked | Python generator remaps, HAL/motion runtime, and remap reentry. | Port LinuxCNC Python generator remap boundary and motion/HAL synchronization first. |
|
||||
| `tests/remap/spindle` | Blocked | Python remap body and spindle runtime state. | Port the LinuxCNC Python remap boundary and spindle status adapter. |
|
||||
| `tests/remap/variable-injection` | Blocked | Python prolog/epilog variable injection. | Port LinuxCNC Python prolog/epilog boundary before executing. |
|
||||
| `tests/abort/*` | Blocked | Full LinuxCNC task/motion process, abort/stop handling, HAL files, and machine state after interruption. | Keep as upstream process reference until task/motion abort boundaries are available; do not reduce to standalone interpreter execution. |
|
||||
| `tests/ccomp/early-exit` | Blocked | Full LinuxCNC process and motion-position validation after cutter-compensation early exit. | Use interpreter-only `tests/ccomp/*` cases first; revisit after motion state comparison is available. |
|
||||
| `tests/classicladder/estop` | Blocked | ClassicLadder component, full LinuxCNC process, HAL files, and UI driver. | Keep as upstream-only reference until ClassicLadder/HAL process boundaries are represented. |
|
||||
| `tests/halui/*` | Blocked | Full LinuxCNC process with `halui`, postgui HAL files, and UI/test driver interaction. | Establish HALUI/task runtime boundaries before porting. |
|
||||
| `tests/hard-limits` | Blocked | Full LinuxCNC machine process, HAL limit wiring, and motion limit behavior. | Keep as upstream process reference until motion/HAL limit boundaries exist. |
|
||||
| `tests/inifile/python_bindings` | Blocked | LinuxCNC Python INI bindings, not just vendored C++ `inifile.cc`. | Add only after a Python binding boundary is intentionally exposed, or keep covered by upstream. |
|
||||
| `tests/io-startup/*` | Blocked | Full LinuxCNC `io` startup, task process, HAL files, and postgui setup. | Revisit after IO/task startup runtime boundaries are represented. |
|
||||
| `tests/linuxcncrsh` | Blocked | Full LinuxCNC process plus `linuxcncrsh` socket protocol server. | Keep as upstream integration reference until linuxcncrsh/server boundaries are intentionally ported. |
|
||||
| `tests/linuxcncrsh-tcp` | Blocked | Full LinuxCNC process plus TCP socket service and network timing. | Keep as upstream integration reference until linuxcncrsh/server boundaries are intentionally ported. |
|
||||
| `tests/mdi-queue/*` | Blocked | Full LinuxCNC task/MDI queue behavior through linuxcncrsh, tool changes, and queued command synchronization. | Port only after task queue and linuxcncrsh boundaries are available. |
|
||||
| `tests/motion/*` | Blocked | Full LinuxCNC sim process, HAL pins, linuxcncrsh, halsampler, and realtime motion behavior. | Keep as upstream motion integration reference; prefer standalone TP/kinematics probes for now. |
|
||||
| `tests/motion-logger/*` | Blocked | Full LinuxCNC process plus motion-logger userspace program and mock-motion HAL setup. | Revisit after motion logging/runtime process boundaries are represented. |
|
||||
| `tests/halcompile/*` | Blocked | LinuxCNC HAL component build/install flow, often including userspace or realtime component loading. | Keep as build-system/HAL integration reference; do not treat as interpreter semantics. |
|
||||
| `tests/module-loading/*` | Blocked | HAL realtime module loading and RTAPI lifecycle through `halrun`/`loadrt`. | Covered only by narrow source/native probes today; full HAL module loading remains out of scope. |
|
||||
| `tests/mb2hal/*` | Blocked | External `mb2hal` userspace component, Modbus I/O behavior, and HAL process wiring. | Keep as external-component integration reference until a host I/O boundary exists. |
|
||||
| `tests/t0/*` | Blocked | Full LinuxCNC process, linuxcncrsh display, tool table startup, random/nonrandom toolchanger behavior, and HAL files. | Continue validating tool tables through vendored `tooldata_common.cc`; revisit full T0 startup through task/IO boundaries later. |
|
||||
| `tests/tool-info/*` | Blocked | Full LinuxCNC task/IO startup with HALUI, startup tool state, and random/nonrandom toolchanger process behavior. | Keep tool parsing/formatting in standalone probes; revisit process startup behavior later. |
|
||||
| `tests/toolchanger/*` | Blocked | Full LinuxCNC tool-change process, MDI/task behavior, IO state, and subroutine/user-M paths. | Continue standalone interpreter/tooldata coverage first; revisit after task/IO tool-change boundaries exist. |
|
||||
| `tests/trajectory-planner/circular-arcs` | Blocked | Full LinuxCNC sim configs with task, HAL/Tcl files, GUI/display settings, and motion execution logs. | Keep using standalone TP API probes for planner code; full circular-arc machine tests require task/HAL/motion boundaries. |
|
||||
| `tests/lowlevel/emcmot-error-mpsc` | Blocked | RIP-only build against LinuxCNC motion sources and low-level motion error driver. | Revisit only if low-level motion process test drivers are intentionally ported. |
|
||||
| `tests/uspace/spawnv-root` | Blocked | Sudo-installed userspace HAL component and `spawnv` root behavior. | Keep upstream-only unless HAL component install/runtime support is added. |
|
||||
| `configs/sim/*/remap_subs/*.ngc` browser main-program inventory | Blocked | These files are remap/subroutine assets, not standalone browser main-program targets. | Keep validating them through native remap parse/execute paths and explicit five-axis/runtime probes instead of widening browser `runSimConfigProgram()` blindly. |
|
||||
| `configs/sim/axis/db_demo/base.ngc` Layer 4 inventory | Blocked | `[EMCIO]DB_PROGRAM = ./db_nonran.py` requires LinuxCNC task/tooldata DB process startup, command/reply protocol, and database state behavior, not standalone interpreter file execution. | Keep as `L4-TOOL-DB` until a LinuxCNC-owned tool database runtime boundary exists and proves lookup/update behavior across native and WASM. `blocked-dependency-summary.tsv` records `taskclass.cc`, `tooldata_db.cc`, `tooldata_common.cc`, and `configs/sim/axis/db_demo/db.py` as the owner set plus source-derived `tool_db_protocol_evidence` for the `v2.1` handshake, `g`/`FINI` get-all, and `l`/`u`/`p` notification protocol. |
|
||||
| `configs/sim/axis/vismach/millturn/example.ngc` Layer 4 inventory | Blocked | The remap execution chain calls external `USER_M_PATH` process codes `M128` and `M129`, with HAL/HALUI/UI process declarations in `millturn.ini`. | Keep as `L4-USER-M-PROCESS`; do not promote until the M128/M129 LinuxCNC-owned state boundary proves the kinstype guard and `ini.[xyz].*` HAL pin updates. `blocked-dependency-summary.tsv` records the `mcodes/M128` and `mcodes/M129` Tcl scripts plus source-derived `user_m_process_effects` for Tcl/HAL runtime use, kinstype guards, and `ini.[xyz]` HAL pin side effects. |
|
||||
| `configs/sim/gmoccapy/*` Layer 4 full inventory | Blocked | Python remap runtime boundary is present in native LinuxCNC `rs274`, but not yet intentionally exposed for Node/browser full inventory. | Keep native inventory coverage as source-of-truth; add one representative Node/browser class sample only after the Python remap boundary is deliberately designed. Python module and prolog/epilog ownership is inventoried in `blocked-dependency-summary.tsv`; LinuxCNC runtime ownership is recorded as `python_runtime_evidence` in the Python boundary/family summaries. |
|
||||
| `configs/sim/axis/laser/*` Layer 4 full inventory | Blocked | Python remap runtime boundary is present in native LinuxCNC `rs274`, but not yet intentionally exposed for Node/browser full inventory. | Keep native inventory coverage as source-of-truth; add one representative Node/browser class sample only after the Python remap boundary is deliberately designed. Python module and remap ownership is inventoried in `blocked-dependency-summary.tsv`; LinuxCNC runtime ownership is recorded as `python_runtime_evidence` in the Python boundary/family summaries. |
|
||||
| `configs/sim/axis/remap/*/nc_files/*.ngc` Layer 4 full inventory | Blocked | Native LinuxCNC `rs274` coverage exists, but these demo main programs depend on Python remaps, Python prolog/epilog callbacks, or Python-backed queue/tool-change handlers. | Keep native inventory coverage as source-of-truth; do not reduce Python remap demos to missing vendored files or browser standalone execution until the Python remap boundary is deliberately designed. Dependency ownership is inventoried in `blocked-dependency-summary.tsv`; LinuxCNC runtime ownership is recorded as `python_runtime_evidence` in the Python boundary/family summaries. |
|
||||
| `configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/*` Layer 4 full inventory | Blocked | Native LinuxCNC `rs274` coverage exists, but the demo family depends on Python remap entry points such as `g682`, `g69_core`, and `g53x_core` that are not yet intentionally exposed for Node/browser inventory. | Keep native inventory coverage as source-of-truth; revisit Layer 4 only after the Python remap boundary is deliberately designed rather than faked in JS. Python and NGC remap dependencies are inventoried in `blocked-dependency-summary.tsv`; LinuxCNC runtime ownership is recorded as `python_runtime_evidence` in the Python boundary/family summaries. |
|
||||
| `configs/sim/axis/vismach/VMC_toolchange/toolchange.ngc` Layer 4 full inventory | Blocked | Native LinuxCNC `rs274` coverage exists, but the program is the remapped tool-change body for a Python prolog/epilog and full tool-change/HAL process flow. | Keep native inventory coverage as source-of-truth and validate tool-change mechanics through existing standalone interpreter/tooldata probes until task/HAL/tool-change runtime boundaries exist. |
|
||||
| `configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc` | Blocked | Preserved upstream demo edge: line uses bare `X/Y/Z` after `G53.6` without an explicit motion G-code in the upstream standalone baseline. | Keep as expected failure in Layer 2 and do not modify G-code or project-owned interpreter semantics to force a pass. |
|
||||
|
||||
## Next Work Batch
|
||||
|
||||
1. Continue inventorying `linuxcnc/tests/interp/*` for additional pure
|
||||
interpreter cases that can run through vendored file execution without full
|
||||
task/HAL/motion process state.
|
||||
2. Keep adding skipped upstream families to the blocked-test table with a
|
||||
concrete dependency reason before attempting implementation.
|
||||
3. Run `wasm-port/tests/browser/verify_interp_browser.sh` and then
|
||||
`wasm-port/tests/host/verify_host_smokes.sh` after each stable batch.
|
||||
@@ -809,24 +809,33 @@ Current five-axis switchkins status:
|
||||
bridge-mill kinematics, loads vendored machine tool tables through
|
||||
LinuxCNC `tooldata_load()`, and runs vendored `xyzac_switchkins.ngc`,
|
||||
`xyzbc_switchkins.ngc`,
|
||||
`xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_3.ngc`,
|
||||
`boat-xyzac.ngc`, `boat-xyzbc.ngc`, `impeller-7bl-xyzac.ngc`, and
|
||||
`xyzab-tdr-demo.ngc`, plus bridge-mill `5axisgui.ngc` through LinuxCNC
|
||||
remap/file execution paths.
|
||||
`xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_2.ngc`,
|
||||
`xyzac_switchkins_test_3.ngc`, `boat-xyzac.ngc`, `boat-xyzbc.ngc`,
|
||||
`impeller-7bl-xyzac.ngc`, and `xyzab-tdr-demo.ngc`, plus bridge-mill
|
||||
`5axisgui.ngc` through LinuxCNC remap/file execution paths.
|
||||
7. The WASM interpreter core now links vendored `interp_remap.cc` plus the
|
||||
Python-only runtime edge stub, and the Node WASM smoke runs vendored
|
||||
`xyzac_switchkins.ngc`, `xyzbc_switchkins.ngc`, `boat-xyzac.ngc`, and
|
||||
`boat-xyzbc.ngc`, `impeller-7bl-xyzac.ngc`, `xyzab-tdr-demo.ngc`, and
|
||||
`5axisgui.ngc` through the SDK C ABI without JavaScript M-code or
|
||||
kinematics semantics.
|
||||
`xyzac_switchkins.ngc`, `xyzbc_switchkins.ngc`,
|
||||
`xyzac_switchkins_test_1.ngc`, `xyzac_switchkins_test_2.ngc`,
|
||||
`xyzac_switchkins_test_3.ngc`, `boat-xyzac.ngc`, `boat-xyzbc.ngc`,
|
||||
`impeller-7bl-xyzac.ngc`, `xyzab-tdr-demo.ngc`, and `5axisgui.ngc`
|
||||
through the SDK C ABI without JavaScript M-code or kinematics semantics.
|
||||
8. Browser smoke and the INI panel UI now expose the same validated 5-axis
|
||||
remap execution path by copying vendored LinuxCNC sample-machine files into
|
||||
the interpreter WASM filesystem and calling the SDK C ABI.
|
||||
9. LinuxCNC upstream `tests/remap/duplicate-o-word` and
|
||||
`tests/remap/m30-interaction` are now vendored unchanged and validated
|
||||
through the generic remap file execution path. Native, Node WASM, and
|
||||
browser interpreter smokes load the vendored INI, program, and remap
|
||||
subroutines, then call vendored `Interp::parse_remap()`, `open()`,
|
||||
`read()`, and `execute()`; standalone code only supplies the INI/file path
|
||||
boundary.
|
||||
|
||||
Next work:
|
||||
|
||||
1. Continue broadening five-axis fixture coverage through the same
|
||||
source-backed execution boundary.
|
||||
1. Continue broadening source-backed remap coverage through LinuxCNC upstream
|
||||
tests that can run through NGC REMAP/O-word/file execution without Python
|
||||
semantics.
|
||||
2. Evaluate additional vendored LinuxCNC five-axis sample machines and demos
|
||||
after the TRT boat/impeller, TDR demo, and bridge-mill demo paths are
|
||||
stable in native, Node WASM, browser, and UI remap paths.
|
||||
|
||||
635
wasm-port/docs/sim-configs-completion-plan.md
Normal file
635
wasm-port/docs/sim-configs-completion-plan.md
Normal file
@@ -0,0 +1,635 @@
|
||||
# LinuxCNC Sim Config Completion Plan
|
||||
|
||||
This plan tracks the work needed to run the LinuxCNC programs under
|
||||
`linuxcnc/configs/sim` through the standalone native/WASM simulation runtime.
|
||||
LinuxCNC source remains the semantic source of truth. Port code must live under
|
||||
`wasm-port/`; do not edit `linuxcnc/` in place.
|
||||
|
||||
## Current Baseline
|
||||
|
||||
The latest strict harness run used `linuxcnc/configs/sim` as the program source,
|
||||
selected the nearest INI/tool table for each `.ngc` unless an explicit sim
|
||||
mapping is required, and ran `rs274` from the INI directory so relative
|
||||
`SUBROUTINE_PATH` and `REMAP` entries resolve like a real sim config.
|
||||
|
||||
Result file:
|
||||
|
||||
```text
|
||||
wasm-port/build/native/sim-configs/summary.tsv
|
||||
wasm-port/build/native/sim-configs/class-summary.tsv
|
||||
wasm-port/build/native/sim-configs/path-matrix.tsv
|
||||
```
|
||||
|
||||
Baseline:
|
||||
|
||||
```text
|
||||
total: 159
|
||||
pass: 151
|
||||
fail: 8
|
||||
timeout: 0
|
||||
expected_fail: 8
|
||||
unexpected_fail: 0
|
||||
|
||||
main: PASS 40, FAIL 8
|
||||
macro_load: PASS 46, FAIL 0
|
||||
remap_subroutine: PASS 65, FAIL 0
|
||||
```
|
||||
|
||||
Remaining failures are explicit LinuxCNC-native baseline edges:
|
||||
|
||||
- `axis/external_offsets/*`: 4 failures from sim-only user M-codes `M111`.
|
||||
The native harness now resolves the corresponding INI by
|
||||
`[DISPLAY]OPEN_FILE`, so `eoffsets.ngc`, `jwp_z.ngc`, and `opa_demo.ngc`
|
||||
are no longer judged through `dynamic_offsets.ini`.
|
||||
- `axis/geometry/xyzc.ngc`: 1 failure from sim-only user M-code `M110`.
|
||||
- `axis/foam/foam.ngc`: 1 failure from missing `U/V` axis support in the
|
||||
LinuxCNC `bin/rs274` entry point.
|
||||
- `axis/vismach/5axis/bridgemill/5axisgui.ngc`: 1 failure from missing `W`
|
||||
axis support in the LinuxCNC `bin/rs274` entry point.
|
||||
- `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc`:
|
||||
1 failure from an upstream demo line that uses bare `X/Y/Z` words after
|
||||
`G53.6` without an explicit motion G-code.
|
||||
|
||||
## Target
|
||||
|
||||
The first target is not a browser UI feature and not a requirement that every
|
||||
`linuxcnc/configs/sim` `.ngc` program pass through standalone `bin/rs274`.
|
||||
It is a repeatable native and WASM inventory that classifies every
|
||||
`configs/sim` program, runs the entries that are meaningful in standalone
|
||||
native/WASM/browser contexts, and records blocked dependencies for the entries
|
||||
that require full LinuxCNC task, HAL, UI, Python-remap, or external process
|
||||
runtime.
|
||||
|
||||
Acceptance criteria:
|
||||
|
||||
- The native sim-config harness reports `unexpected_fail: 0` and writes
|
||||
`summary.tsv`, `class-summary.tsv`, and `path-matrix.tsv`.
|
||||
- Macro/remap files are validated by the correct entry point: direct execution
|
||||
only for real programs, load/parse or remap-call validation for subroutines.
|
||||
- Node WASM inventory executes only entries with a defined standalone/WASM
|
||||
runtime path and reports `unexpected_fail: 0`.
|
||||
- Browser coverage remains focused on representative runtime classes rather
|
||||
than blind full-directory execution.
|
||||
- Any remaining unsupported files are explicitly classified by dependency, not
|
||||
by accident.
|
||||
|
||||
## Definition Of Done For Full configs/sim Coverage
|
||||
|
||||
Full coverage for this project means:
|
||||
|
||||
- Inventory complete: every `summary.tsv` `.ngc` entry appears in
|
||||
`docs/sim-configs-coverage-matrix.md` and generated `path-matrix.tsv`.
|
||||
- Classification complete: every entry has `main`, `macro_load`, or
|
||||
`remap_subroutine` class.
|
||||
- Blocked table complete: full-process, HAL, UI, linuxcncrsh, external
|
||||
userspace component, Python binding/remap, tool-database, and process user-M
|
||||
dependencies are recorded explicitly.
|
||||
- Native source-of-truth complete: `verify_sim_configs.sh` remains the Layer 2
|
||||
source for per-path status and expected-failure reason.
|
||||
- Node inventory complete: `verify_sim_configs_inventory_wasm.sh` checks the
|
||||
native/generated/tracked path sets, runs eligible vendored programs, and
|
||||
emits stable WASM inventory and skip/block summaries.
|
||||
Any eligible row with missing vendored machine context is an inventory
|
||||
failure, not an expected skip.
|
||||
- Browser class coverage complete: each promoted browser class has a
|
||||
representative sample and no browser test relies on directory enumeration.
|
||||
- Expected failures justified: the current eight Layer 2 expected failures are
|
||||
tied to runtime boundaries or the preserved upstream demo edge.
|
||||
- No accidental standalone semantic ownership: tests must not pass by changing
|
||||
G-code semantics, JS interpreter behavior, or project-owned
|
||||
`Interp::...` implementations.
|
||||
|
||||
## Phase 1: Make The Harness A First-Class Test
|
||||
|
||||
Status: complete. The tracked script exists and is wired into the native
|
||||
validation entry point.
|
||||
|
||||
1. Add a tracked script:
|
||||
|
||||
```text
|
||||
wasm-port/tests/native/verify_sim_configs.sh
|
||||
```
|
||||
|
||||
2. Move the ad hoc test logic into the script:
|
||||
|
||||
- scan only `linuxcnc/configs/sim`;
|
||||
- identify `.ngc`, nearest `.ini`, nearest `.tbl`;
|
||||
- run from the INI directory;
|
||||
- write `summary.tsv`, `class-summary.tsv`, `path-matrix.tsv`, stdout,
|
||||
stderr, and interpreter output under
|
||||
`wasm-port/build/native/sim-configs/`;
|
||||
- classify programs as `main`, `macro_load`, `remap_subroutine`, or
|
||||
`unsupported_runtime_edge`;
|
||||
- fail the script only on unexpected failures.
|
||||
|
||||
3. Add deterministic classification rules:
|
||||
|
||||
- files under `remap_subs/` and `nc_subroutines/` are not standalone main
|
||||
programs;
|
||||
- files with no `M2`, `M30`, or `%` are load/parse fixtures unless the INI
|
||||
references them through `REMAP`;
|
||||
- user M-code files are tested through `USER_M_PATH` resolution, not by
|
||||
pretending LinuxCNC native process services exist.
|
||||
|
||||
4. Wire the script into existing validation:
|
||||
|
||||
```text
|
||||
wasm-port/tests/native/verify_native_probes.sh
|
||||
```
|
||||
|
||||
5. Completion check:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/native/verify_sim_configs.sh
|
||||
```
|
||||
|
||||
Latest result:
|
||||
|
||||
```text
|
||||
total: 159
|
||||
pass: 151
|
||||
fail: 8
|
||||
timeout: 0
|
||||
expected_fail: 8
|
||||
unexpected_fail: 0
|
||||
skipped: 0
|
||||
```
|
||||
|
||||
## Phase 2: Load Machine Axes From INI
|
||||
|
||||
Status: partially complete for the port runtime. The standalone native/WASM
|
||||
runtime now parses `[TRAJ] COORDINATES`, updates the standalone external axis
|
||||
mask used by LinuxCNC `GET_EXTERNAL_AXIS_MASK()`, and applies the same
|
||||
`Interp::_readers` filtering that upstream `rs274ngc_pre.cc` uses. Native
|
||||
regression coverage verifies that `axis_foam.ini` enables `U/V` readers and
|
||||
`bridgemill/5axis.ini` enables the `W` reader.
|
||||
|
||||
The `verify_sim_configs.sh` baseline still records the LinuxCNC `bin/rs274`
|
||||
entry-point failures as expected failures. That harness is intentionally kept
|
||||
as a LinuxCNC-native baseline while the port runtime coverage tracks this
|
||||
phase's standalone behavior.
|
||||
|
||||
Current failures:
|
||||
|
||||
- `axis/foam/foam.ngc`: `Bad character 'u' used`
|
||||
- `axis/vismach/5axis/bridgemill/5axisgui.ngc`: `Bad character 'w' used`
|
||||
|
||||
Root cause:
|
||||
|
||||
The current standalone interpreter initialization does not configure the active
|
||||
axis mask from `[TRAJ] COORDINATES` / `[KINS] KINEMATICS`. Native LinuxCNC
|
||||
accepts `U/V/W` only when the machine config declares those axes.
|
||||
|
||||
Implementation steps:
|
||||
|
||||
1. Add a machine-config loader in `runtime/core/linuxcnc_wrap/`, for example:
|
||||
|
||||
```text
|
||||
linuxcnc_machine_config.hh
|
||||
linuxcnc_machine_config.cpp
|
||||
```
|
||||
|
||||
2. Reuse vendored `inifile.cc` to parse:
|
||||
|
||||
- `[TRAJ] COORDINATES`
|
||||
- `[KINS] KINEMATICS`
|
||||
- `[DISPLAY] GEOMETRY`
|
||||
- `[RS274NGC] PARAMETER_FILE`
|
||||
- `[RS274NGC] SUBROUTINE_PATH`
|
||||
- `[RS274NGC] USER_M_PATH`
|
||||
- `[EMCIO] TOOL_TABLE`
|
||||
|
||||
3. Extend `initialize_minimal_interp()` / the runtime equivalent to set the
|
||||
interpreter setup fields from the parsed machine config instead of hardcoded
|
||||
`XYZ`.
|
||||
|
||||
4. Add focused native fixtures:
|
||||
|
||||
- `axis_foam.ini` + `foam.ngc` accepts `U/V`.
|
||||
- `bridgemill/5axis.ini` + `5axisgui.ngc` accepts `W`.
|
||||
|
||||
5. Add the same coverage to the WASM SDK once native is green.
|
||||
|
||||
Completion check:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/native/verify_sim_configs.sh --only axis/foam/foam.ngc
|
||||
wasm-port/tests/native/verify_sim_configs.sh --only axis/vismach/5axis/bridgemill/5axisgui.ngc
|
||||
```
|
||||
|
||||
## Phase 3: Implement Standalone User M-Code Dispatch
|
||||
|
||||
Status: complete for the standalone native/WASM runtime boundary. The port now
|
||||
reads `[DISPLAY] PROGRAM_PREFIX` and `[RS274NGC] USER_M_PATH`, searches
|
||||
executable `M100` through `M199` files using the same order as LinuxCNC task
|
||||
initialization, registers them in LinuxCNC's `USER_DEFINED_FUNCTION` table, and
|
||||
records `USER_M_COMMAND` canonical boundary events instead of spawning host
|
||||
processes. Native probes cover `axis/geometry` `M110` and
|
||||
`axis/external_offsets` `M111`; Node WASM covers minimal `M110` and `M111`
|
||||
fixtures through the Emscripten filesystem.
|
||||
|
||||
The `verify_sim_configs.sh` baseline still records these files as expected
|
||||
failures because it intentionally runs LinuxCNC `bin/rs274`, not the standalone
|
||||
task/runtime adapter.
|
||||
|
||||
Current failures:
|
||||
|
||||
- `axis/external_offsets/*.ngc`: unknown `M111`
|
||||
- `axis/geometry/xyzc.ngc`: unknown `M110`
|
||||
|
||||
Root cause:
|
||||
|
||||
Native LinuxCNC resolves user M-codes through `[DISPLAY] PROGRAM_PREFIX` and
|
||||
`[RS274NGC] USER_M_PATH` during task initialization, registers matching
|
||||
executable `M100..M199` handlers with the interpreter, and later runs external
|
||||
scripts through the task process boundary. The standalone runtime must register
|
||||
the same interpreter boundary without running host processes in WASM/browser.
|
||||
|
||||
Implementation steps:
|
||||
|
||||
1. Locate the upstream M-code dispatch path in LinuxCNC and vendor the minimum
|
||||
source needed to preserve semantics, or add a narrow runtime-edge adapter if
|
||||
the upstream path is process-bound. Done: the adapter mirrors
|
||||
`src/emc/task/emctask.cc` search/registration behavior and keeps process
|
||||
execution outside CNC semantics.
|
||||
|
||||
2. Add `USER_M_PATH` parsing to the machine-config loader. Done.
|
||||
|
||||
3. For native standalone tests. Done:
|
||||
|
||||
- resolve `M100` through `M199` against configured search paths;
|
||||
- register only executable files;
|
||||
- emit deterministic `USER_M_COMMAND` boundary events.
|
||||
|
||||
4. For WASM/browser. Done for Node WASM:
|
||||
|
||||
- do not spawn host processes;
|
||||
- use a host-boundary user-M adapter that registers deterministic
|
||||
message-emitting handlers for sim-only notification M-codes;
|
||||
- document this as a runtime edge, not CNC semantics.
|
||||
|
||||
5. Add tests. Done:
|
||||
|
||||
- `M110` from `axis/geometry`;
|
||||
- `M111` from `axis/external_offsets`;
|
||||
- minimal WASM `M110` and `M111` fixtures.
|
||||
|
||||
Completion check:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/native/verify_sim_configs.sh --only axis/external_offsets
|
||||
wasm-port/tests/native/verify_sim_configs.sh --only axis/geometry/xyzc.ngc
|
||||
```
|
||||
|
||||
Standalone runtime checks:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/native/verify_native_probes.sh
|
||||
wasm-port/tests/wasm/node/verify_interp_wasm.sh
|
||||
```
|
||||
|
||||
## Phase 4: Classify Python Remap Runtime Edges
|
||||
|
||||
Status: complete for the native sim-config harness and blocked for Layer 4
|
||||
full inventory. Current native checks:
|
||||
|
||||
```text
|
||||
gmoccapy: total 38, pass 38, fail 0
|
||||
axis/laser: total 3, pass 3, fail 0
|
||||
axis/remap main demos: total 6, pass 6, fail 0
|
||||
VMC_toolchange: total 1, pass 1, fail 0
|
||||
```
|
||||
|
||||
Boundary:
|
||||
|
||||
The configs use LinuxCNC Python remap modules. Native LinuxCNC `rs274` can
|
||||
resolve and execute these configs under the current baseline, but Node/browser
|
||||
Layer 4 full inventory is intentionally blocked until a Python-remap runtime
|
||||
boundary is deliberately designed. Do not make these pass in browser by
|
||||
translating Python remap behavior into JavaScript or by adding project-owned
|
||||
CNC semantics.
|
||||
|
||||
Recorded dependency:
|
||||
|
||||
- `configs/sim/gmoccapy/python/toplevel.py`
|
||||
- `configs/sim/gmoccapy/python/remap.py`
|
||||
- `configs/sim/gmoccapy/python/stdglue.py`
|
||||
- `configs/sim/axis/laser/python/toplevel.py`
|
||||
- `configs/sim/axis/laser/python/remap.py`
|
||||
- `configs/sim/axis/remap/*/python/*.py` for Python remap, prolog, epilog,
|
||||
queue, and tool-change callbacks.
|
||||
- `configs/sim/axis/vismach/VMC_toolchange/remap.py` and `toplevel.py` for
|
||||
Python tool-change prolog/epilog handling.
|
||||
|
||||
Current policy:
|
||||
|
||||
- Keep native inventory as source-of-truth.
|
||||
- Mark `gmoccapy/*`, `axis/laser/*`, `axis/remap/*/nc_files/*.ngc`, and
|
||||
`axis/vismach/VMC_toolchange/toolchange.ngc` rows as `L4-PYTHON-REMAP` in
|
||||
`docs/sim-configs-coverage-matrix.md`.
|
||||
- Revisit Layer 4 only after the runtime boundary is designed from LinuxCNC
|
||||
source ownership, not as a browser smoke expansion.
|
||||
|
||||
Completion check:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/native/verify_sim_configs.sh --only gmoccapy
|
||||
wasm-port/tests/native/verify_sim_configs.sh --only axis/laser
|
||||
wasm-port/tests/native/verify_sim_configs.sh --only axis/remap
|
||||
wasm-port/tests/native/verify_sim_configs.sh --only VMC_toolchange
|
||||
```
|
||||
|
||||
## Phase 5: Classify TWP Remaps Including G69
|
||||
|
||||
Status: native mapping complete and Layer 4 blocked on Python-remap runtime
|
||||
ownership. The harness now maps
|
||||
`axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/*` through the
|
||||
explicit `xyzacb-trsrn_twp/xyzacb-trsrn.ini` machine config instead of falling
|
||||
back to `axis/axis.ini`. That loads the TWP `G69` remap declarations correctly,
|
||||
and 14 of the 15 TWP demo/remap programs now pass.
|
||||
|
||||
Current failures:
|
||||
|
||||
- `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc`
|
||||
fails in the LinuxCNC `rs274` baseline because line 10 uses bare
|
||||
`x50y50z150` after `G53.6`; same-directory demos use explicit `G0` motion
|
||||
words at this point.
|
||||
|
||||
Boundary:
|
||||
|
||||
The demo programs rely on table/spindle rotary TWP remaps. The original test
|
||||
mapping fell back to `axis/axis.ini` for these demo files because the actual
|
||||
INI is in a child directory:
|
||||
|
||||
```text
|
||||
axis/vismach/5axis/table-rotary_spindle-rotary-nutating/
|
||||
xyzacb-trsrn_twp/xyzacb-trsrn.ini
|
||||
xyzbca-trsrn_twp/xyzbca-trsrn.ini
|
||||
```
|
||||
|
||||
Current policy:
|
||||
|
||||
- Keep native mapping and expected-failure classification in
|
||||
`verify_sim_configs.sh`.
|
||||
- Keep `incremental_repetition_g533.ngc` as `UPSTREAM-DEMO`; do not edit the
|
||||
upstream G-code or add standalone semantics to force a pass.
|
||||
- Keep TWP demo Layer 4 rows as `L4-PYTHON-REMAP` until Python remap entry
|
||||
points such as `g682`, `g69_core`, and `g53x_core` have an intentional
|
||||
runtime boundary.
|
||||
|
||||
Latest result:
|
||||
|
||||
```text
|
||||
total: 15
|
||||
pass: 14
|
||||
fail: 1
|
||||
timeout: 0
|
||||
expected_fail: 1
|
||||
unexpected_fail: 0
|
||||
skipped: 0
|
||||
```
|
||||
|
||||
Completion check:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/native/verify_sim_configs.sh --only table-rotary_spindle-rotary-nutating
|
||||
```
|
||||
|
||||
## Phase 6: Promote Eligible Native Coverage To WASM
|
||||
|
||||
Status: complete for the current representative Node/browser class coverage
|
||||
and the Node inventory layer. Dedicated Node and browser smokes now pass a
|
||||
representative vendored `configs/sim` subset through the SDK
|
||||
`runSimConfigProgram()` host boundary, which copies files into the Emscripten
|
||||
filesystem and forwards execution to existing LinuxCNC-backed C ABI paths. They
|
||||
cover:
|
||||
|
||||
- `axis/foam/foam.ngc` with `axis_foam.ini`, verifying INI-driven `U/V` axis
|
||||
mask handling in WASM.
|
||||
- `axis/vismach/5axis/bridgemill/5axisgui.ngc` with `5axis.ini`, verifying
|
||||
INI-driven `W` axis mask handling and bridge-mill NGC remap execution in
|
||||
WASM.
|
||||
- `axis/geometry/xyzc.ngc` with `xyzc.ini` and real executable `M110`, verifying
|
||||
`USER_M_PATH` registration in WASM.
|
||||
- `axis/external_offsets/dyn_demo.ngc`, `eoffsets.ngc`, `jwp_z.ngc`, and
|
||||
`opa_demo.ngc` with their corresponding INI files, real executable `M111`,
|
||||
shared `eoffset.tbl`, and `opa_demo.ngc`'s `circles.ngc` subroutine,
|
||||
verifying the same user-M and `SUBROUTINE_PATH` boundaries against upstream
|
||||
sim programs.
|
||||
The Node inventory guard keeps these deterministic `M110`/`M111` rows
|
||||
unblocked, requires a vendored user-M file, and includes the `opa_demo.ngc`
|
||||
`circles.ngc` subroutine text in boundary analysis so the reached `M111`
|
||||
call is accounted for instead of being mistaken for an external user-M
|
||||
process gap.
|
||||
- `axis/gladevcp/probe.ngc` as a plain INI/tool-table main-program sample.
|
||||
- `axis/external_offsets/circles.ngc` as the macro-load class representative,
|
||||
wrapped only with an added `M2` for load/parse style execution.
|
||||
- `axis/vismach/5axis/table-dual-rotary/demos/xyzab-tdr-demo.ngc` and
|
||||
`axis/vismach/5axis/table-rotary-tilting/demos/*` through the existing
|
||||
five-axis remap execution C ABI.
|
||||
- `axis/vismach/melfa-sim/example.ngc` and `axis/vismach/puma/puma_cube.ngc`
|
||||
as additional remap/kinematics machine representatives.
|
||||
- `woodpecker/on_abort.ngc` as the deterministic on-abort/user-action macro
|
||||
class representative.
|
||||
- Node inventory for the eligible vendored program set:
|
||||
`executed=28`, `passed=28`, `skipped=131`, `unexpected_fail=0`.
|
||||
- Node inventory skip/block summary:
|
||||
`ASSET-ONLY=65`, `L4-PYTHON-REMAP=53`, `L4-TOOL-DB=1`,
|
||||
`L4-USER-M-PROCESS=1`, `NON_MAIN_CLASS=10`, `UPSTREAM-DEMO=1`.
|
||||
- Node blocked-dependency inventory for the hard blocked row set:
|
||||
`L4-PYTHON-REMAP=53`, `L4-TOOL-DB=1`, `L4-USER-M-PROCESS=1`.
|
||||
The inventory reads source `linuxcnc/configs/sim` INI files for dependency
|
||||
accounting only and writes
|
||||
`build/wasm/sim-configs-inventory/blocked-dependency-summary.tsv` without
|
||||
vendoring or executing those blocked families. The blocked summary includes
|
||||
source-derived `user_m_process_effects` for `millturn`,
|
||||
`tool_db_protocol_evidence` for `db_demo`, and Python module/remap/prolog/
|
||||
epilog dependency ownership for Python-remap families. Python-remap rows are
|
||||
also projected into
|
||||
`build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv`, which
|
||||
keeps all 53 `L4-PYTHON-REMAP` rows inventory-only with
|
||||
`python_runtime_evidence` and `execution_enabled=0`.
|
||||
|
||||
Boundary decisions for the current hard blocks:
|
||||
|
||||
- `axis/vismach/millturn/example.ngc` remains `L4-USER-M-PROCESS`. The
|
||||
source INI declares HAL, HALUI MDI, and UI process dependencies, and the
|
||||
remap chain enters `remap_subs/428remap.ngc` and `429remap.ngc`, which call
|
||||
external user-M process codes `M128` and `M129`. Those process scripts live
|
||||
at `configs/sim/axis/vismach/millturn/mcodes/M128` and `M129`; they use Tcl
|
||||
LinuxCNC/HAL packages, `emc_init`, `parse_ini`, `hal getp`, and `hal setp`
|
||||
to change INI axis limit HAL pins after the kinematics switch. The current
|
||||
LinuxCNC owner is therefore task user-M process dispatch plus HALUI/MDI and
|
||||
the config-owned Tcl process scripts, not the standalone interpreter. Do not
|
||||
promote this row until the boundary design in
|
||||
`docs/full-process-boundary-design.md` has a deterministic state proof for
|
||||
these HAL pin updates across native, Node, and browser. The Node inventory
|
||||
guard requires this row to stay non-`REP` in Node and browser while it remains
|
||||
`L4-USER-M-PROCESS`, and the generated inventory summary must keep it as
|
||||
`SKIP` with that reason.
|
||||
- `axis/db_demo/base.ngc` remains `L4-TOOL-DB`. The source INI declares
|
||||
`[EMCIO]DB_PROGRAM = ./db_nonran.py`, so standalone interpreter execution
|
||||
would miss LinuxCNC tool-database process startup and protocol/state
|
||||
behavior. The LinuxCNC owner is `src/emc/task/taskclass.cc` for DB_PROGRAM
|
||||
activation, `src/emc/tooldata/tooldata_db.cc` for DB process startup and
|
||||
command/reply protocol, `src/emc/tooldata/tooldata_common.cc` for DB-backed
|
||||
tooldata behavior, and the config program `configs/sim/axis/db_demo/db.py`.
|
||||
Do not promote until the boundary design in
|
||||
`docs/full-process-boundary-design.md` has a LinuxCNC-owned tool database
|
||||
boundary that proves lookup/update behavior across native and WASM. The Node
|
||||
inventory guard requires this row to stay non-`REP` in Node and browser while
|
||||
it remains `L4-TOOL-DB`, and the generated inventory summary must keep it as
|
||||
`SKIP` with that reason.
|
||||
- Python-remap/full-process families remain `L4-PYTHON-REMAP`. The blocked
|
||||
dependency inventory records Python modules, remap/prolog/epilog function
|
||||
ownership, NGC remap subpaths, and HAL/UI/HALUI declarations for gmoccapy,
|
||||
axis/laser, axis/remap demos, TWP nutating demos, and VMC tool-change
|
||||
rows. The Python-specific boundary summary records the same row set as
|
||||
inventory-only and requires a LinuxCNC-owned Python runtime boundary before
|
||||
any promotion. Every `L4-PYTHON-REMAP` row must also remain `SKIP` with
|
||||
`L4-PYTHON-REMAP` in the generated inventory summary. This is an inventory
|
||||
artifact, not a Layer 4 execution path.
|
||||
|
||||
Completion checks:
|
||||
|
||||
```bash
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
|
||||
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
|
||||
wasm-port/tests/browser/verify_interp_browser.sh
|
||||
```
|
||||
|
||||
Current Layer 4 non-goals:
|
||||
|
||||
- Do not run all 159 programs in browser.
|
||||
- Do not promote Python remap/full-process/HAL/UI-only families into
|
||||
Emscripten without LinuxCNC-owned runtime support.
|
||||
- Do not add JS-owned G-code, remap, tool, kinematics, parameter, or planner
|
||||
semantics.
|
||||
|
||||
Browser inventory decision record:
|
||||
|
||||
- Promoted after Node inventory: plain INI/tool-table execution
|
||||
(`axis/gladevcp/probe.ngc`), deterministic macro/load execution
|
||||
(`axis/external_offsets/circles.ngc`), deterministic on-abort/user-action
|
||||
execution (`woodpecker/on_abort.ngc`), TDR/TRT five-axis remap execution,
|
||||
bridge-mill W-axis remap execution, `melfa-sim`, and PUMA.
|
||||
The Node inventory guard requires process-declaring representatives such as
|
||||
`axis/gladevcp/probe.ngc`, `woodpecker/on_abort.ngc`,
|
||||
`axis/vismach/melfa-sim/example.ngc`, and
|
||||
`axis/vismach/puma/puma_cube.ngc` to keep their dependency rows in
|
||||
`boundary-summary.tsv`, match the expected HAL/UI/HALUI/Python process flag
|
||||
combinations, remain Node/browser `REP` rows, and avoid hard-block promotion
|
||||
unless the classifier finds a real hard runtime dependency.
|
||||
- Kept as blocked: TWP `table-rotary_spindle-rotary-nutating`, `gmoccapy`,
|
||||
`axis/laser`, `axis/remap/*/nc_files`, and VMC tool-change Python remap
|
||||
families. These are not missing browser staging work; they depend on
|
||||
Python-remap, prolog/epilog, or full runtime process boundaries that have not
|
||||
been intentionally exposed in Layer 4.
|
||||
- Kept out of browser full inventory: remap subroutine assets and macro-only
|
||||
non-representatives. They remain covered by native inventory, native remap
|
||||
parse/execute probes, Node inventory where eligible, or a class
|
||||
representative in browser.
|
||||
|
||||
## Phase 7: Documentation And Drift Control
|
||||
|
||||
Status: complete for the current full `configs/sim` coverage definition. The
|
||||
documentation and manifest now record native inventory, generated class/path
|
||||
artifacts, blocked policy, Node inventory, browser class coverage, source
|
||||
reuse, and vendor-sync state.
|
||||
|
||||
1. Update `docs/compatibility-validation.md` with the sim-config matrix,
|
||||
blocked policy, and validation commands. Done.
|
||||
|
||||
2. Add `docs/sim-configs-coverage-matrix.md` as the tracked review surface for
|
||||
all 159 current native inventory rows. Done.
|
||||
|
||||
3. Update `docs/source-reuse-map.md` for newly vendored source files. Done.
|
||||
|
||||
4. Update `tools/source-manifest.txt` and `tools/verify_vendor_sync.sh` if new
|
||||
LinuxCNC files are copied into `wasm-port/vendor/linuxcnc`. Done for the
|
||||
current representative and inventory-backed sim-config files; no
|
||||
`verify_vendor_sync.sh` logic change was needed.
|
||||
|
||||
5. Preserve the latest result summary as a machine-readable artifact, but do
|
||||
not commit generated logs unless they are intentionally used as fixtures.
|
||||
Done: `summary.tsv` remains the source row set, and `class-summary.tsv` plus
|
||||
`path-matrix.tsv` are generated artifacts under `build/native/sim-configs/`.
|
||||
Node inventory also writes `build/wasm/sim-configs-inventory/summary.tsv`
|
||||
`build/wasm/sim-configs-inventory/skip-summary.tsv`, and
|
||||
`build/wasm/sim-configs-inventory/boundary-summary.tsv` plus
|
||||
`build/wasm/sim-configs-inventory/ini-boundary-summary.tsv` for Layer 4
|
||||
execution, skip/block accounting, and runtime-boundary dependency reporting.
|
||||
The same inventory now writes the full blocked-boundary handoff artifacts:
|
||||
`blocked-dependency-summary.tsv`, `full-process-boundary-summary.tsv`,
|
||||
`user-m-process-boundary-summary.tsv`, `user-m-process-state-targets.tsv`,
|
||||
`tool-db-process-boundary-summary.tsv`,
|
||||
`tool-db-process-protocol-gates.tsv`,
|
||||
`python-remap-boundary-summary.tsv`, `python-remap-family-summary.tsv`,
|
||||
`boundary-phase-completion-summary.tsv`,
|
||||
`native-proof-alignment-summary.tsv`, `next-boundary-worklist.tsv`, and
|
||||
`boundary-proof-gates.tsv`. These remain accounting/proof-gate artifacts;
|
||||
they do not enable blocked execution or promotion.
|
||||
The inventory guard checks `skip-summary.tsv` against skip reasons derived
|
||||
from `path-matrix.tsv`, so aggregate skip counts drift when any blocked or
|
||||
class policy changes without updating the tracked matrix and inventory.
|
||||
The inventory guard requires every vendored INI row to have an available
|
||||
SDK runtime-boundary classifier report; `UNAVAILABLE` is reserved for rows
|
||||
whose boundary summary records a missing vendored INI.
|
||||
Hard blocked rows are additionally recorded in
|
||||
`build/wasm/sim-configs-inventory/blocked-dependency-summary.tsv` without
|
||||
promotion into standalone/WASM execution. That artifact includes source
|
||||
owner fields and runtime proof fields such as `user_m_process_files`,
|
||||
`user_m_process_effects`, `tool_db_protocol_evidence`, and `linuxcnc_owner`,
|
||||
so `millturn` and `db_demo` remain blocked for dependency-backed reasons
|
||||
rather than path-only classification. The follow-up boundary design and
|
||||
required proof for those two blocked rows are tracked in
|
||||
`docs/full-process-boundary-design.md`. Node inventory also writes
|
||||
`build/wasm/sim-configs-inventory/full-process-boundary-summary.tsv` for
|
||||
the designed-but-blocked non-Python full-process rows, including their
|
||||
`runtime_owner_evidence`, and
|
||||
`build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv` for
|
||||
the Python remap inventory-only row set. The Python boundary and family
|
||||
summaries include `python_runtime_evidence`. All blocked boundary artifacts
|
||||
require `execution_enabled=0`.
|
||||
|
||||
## Layer 2 Expected-Failure Policy
|
||||
|
||||
The current Layer 2 baseline remains:
|
||||
|
||||
```text
|
||||
total: 159
|
||||
pass: 151
|
||||
fail: 8
|
||||
timeout: 0
|
||||
expected_fail: 8
|
||||
unexpected_fail: 0
|
||||
```
|
||||
|
||||
Do not reduce the eight expected failures by changing upstream G-code, JS
|
||||
interpreter behavior, or project-owned standalone interpreter semantics. Reducing
|
||||
this count is only valid if a new LinuxCNC-owned native/runtime entry point can
|
||||
legitimately supply the missing task/user-M/axis/TWP context while preserving the
|
||||
strict native baseline as a separately documented source of truth.
|
||||
|
||||
## Recommended Next Work
|
||||
|
||||
1. Keep `verify_sim_configs.sh`, `path-matrix.tsv`, and
|
||||
`docs/sim-configs-coverage-matrix.md` in sync whenever the source manifest or
|
||||
vendored sim-config set changes.
|
||||
2. Add new Node/browser representatives only when they cover a new runtime class
|
||||
with a LinuxCNC-owned execution path.
|
||||
3. Treat Python remap, full-process HAL/UI, tool database, and external
|
||||
userspace process families as blocked until their runtime boundary is
|
||||
intentionally designed and the proof criteria in
|
||||
`docs/full-process-boundary-design.md` are met where applicable.
|
||||
4. Re-evaluate Layer 2 expected failures only after a new legitimate
|
||||
LinuxCNC-owned entry point exists.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Do not implement a new JavaScript or project-authored G-code interpreter.
|
||||
- Do not edit `linuxcnc/configs/sim` or any upstream `linuxcnc/` file to make
|
||||
tests pass.
|
||||
- Do not fake path, modal, kinematic, tool, or parameter semantics in the SDK.
|
||||
- Do not treat gmoccapy native GUI code as browser UI implementation. Only its
|
||||
simulation/remap behavior is relevant to this runtime.
|
||||
421
wasm-port/docs/sim-configs-coverage-matrix.md
Normal file
421
wasm-port/docs/sim-configs-coverage-matrix.md
Normal file
@@ -0,0 +1,421 @@
|
||||
# Sim Configs Coverage Matrix
|
||||
|
||||
This matrix maps every current `wasm-port/tests/native/verify_sim_configs.sh` entry
|
||||
to its native classification and current Layer 3/4 coverage status. It is the
|
||||
tracked review surface for `linuxcnc/configs/sim` inventory; the native harness
|
||||
also emits machine-readable artifacts for automation.
|
||||
|
||||
Current baseline:
|
||||
|
||||
- Source file: `wasm-port/build/native/sim-configs/summary.tsv`
|
||||
- Generated class summary: `wasm-port/build/native/sim-configs/class-summary.tsv`
|
||||
- Generated path matrix: `wasm-port/build/native/sim-configs/path-matrix.tsv`
|
||||
- Total records: `159`
|
||||
- Native result: `pass 151`, `expected_fail 8`, `unexpected_fail 0`
|
||||
- Current Layer 4 representative subset: `axis/foam`, `axis/geometry`,
|
||||
`axis/external_offsets`, `axis/gladevcp`, `woodpecker/on_abort.ngc`,
|
||||
bridge-mill `5axisgui.ngc`, TDR/TRT five-axis switchkins demos,
|
||||
`melfa-sim`, and `puma_cube.ngc`
|
||||
- `wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs` checks that
|
||||
this tracked matrix and generated `path-matrix.tsv` contain the same 159 paths
|
||||
as native `summary.tsv` before running Node inventory.
|
||||
- Node inventory artifact: `wasm-port/build/wasm/sim-configs-inventory/summary.tsv`
|
||||
- Node skip/block artifact: `wasm-port/build/wasm/sim-configs-inventory/skip-summary.tsv`
|
||||
- Node runtime-boundary artifact: `wasm-port/build/wasm/sim-configs-inventory/boundary-summary.tsv`
|
||||
- Node INI runtime-boundary artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv`
|
||||
- Node blocked-dependency artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/blocked-dependency-summary.tsv`
|
||||
- Node full-process boundary design artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/full-process-boundary-summary.tsv`
|
||||
- Node user-M process boundary design artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/user-m-process-boundary-summary.tsv`
|
||||
- Node user-M process transition contract artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/user-m-process-transition-plan.tsv`
|
||||
- Node tool DB process boundary design artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/tool-db-process-boundary-summary.tsv`
|
||||
- Node tool DB process transaction contract artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/tool-db-process-transaction-plan.tsv`
|
||||
- Node Python-remap boundary inventory artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/python-remap-boundary-summary.tsv`
|
||||
- Node Python-remap family inventory artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/python-remap-family-summary.tsv`
|
||||
- Node Python-remap runtime contract artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/python-remap-runtime-contract.tsv`
|
||||
- Node Python-remap native runtime readiness artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-readiness.tsv`
|
||||
- Node Python-remap native runtime state-plan artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-state-plan.tsv`
|
||||
- Node Python-remap native runtime fixture-plan artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/python-remap-native-runtime-fixture-plan.tsv`
|
||||
- Node runtime-boundary contract summary artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/runtime-boundary-contract-summary.tsv`
|
||||
- Node boundary-phase completion artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/boundary-phase-completion-summary.tsv`
|
||||
- Node next-boundary worklist artifact:
|
||||
`wasm-port/build/wasm/sim-configs-inventory/next-boundary-worklist.tsv`
|
||||
- Current Node inventory: `executed 28`, `passed 28`, `skipped 131`,
|
||||
`unexpected_fail 0`
|
||||
- Current Node skip/block counts: `ASSET-ONLY 65`, `L4-PYTHON-REMAP 53`,
|
||||
`L4-TOOL-DB 1`, `L4-USER-M-PROCESS 1`, `NON_MAIN_CLASS 10`,
|
||||
`UPSTREAM-DEMO 1`
|
||||
|
||||
Legend:
|
||||
|
||||
- `Layer 3`: `SIM-*` means dedicated native runtime probe coverage; `5AX-EXEC` means
|
||||
native five-axis remap execution coverage; `-` means no dedicated runtime probe yet.
|
||||
- `Layer 4 Node/Browser`: `REP` means the current representative smoke covers this
|
||||
exact program; `INV` means the Node inventory runner covers the exact program;
|
||||
`N/A` means the row is a subroutine asset and should not be judged as a standalone
|
||||
browser main program; `-` means the row is not individually promoted at that layer
|
||||
and must be interpreted with the row's blocked or class-coverage note.
|
||||
- `Blocked`: current hard blocked dependency or non-target classification for future
|
||||
inventory work. `ASSET-ONLY` means the row is not a browser/main-program target.
|
||||
`L4-PYTHON-REMAP` means native `rs274` coverage exists but Node/browser full inventory
|
||||
is blocked on an intentional Python-remap runtime boundary decision. `UPSTREAM-DEMO`
|
||||
means the file is a preserved upstream expected-failure edge.
|
||||
- `boundary-summary.tsv`: one row per native inventory path. It records the
|
||||
matrix blocked kind, the SDK classifier's recommended hard block when an INI
|
||||
is vendored, declared HAL/UI/HALUI/Python process dependencies, tool database
|
||||
declarations, user-M process codes seen in the execution text, and which of
|
||||
those user-M codes are not staged by vendored `USER_M_PATH` files. Python is
|
||||
split into total process dependency, UI/DB process dependency, and Python
|
||||
remap runtime dependency so safe UI representatives such as `gladevcp` are
|
||||
not mislabeled as Python-remap coverage. Rows whose INI has not been
|
||||
vendored yet use `recommended_blocked=UNAVAILABLE` and keep their tracked
|
||||
matrix policy as the source of truth until the relevant boundary inventory
|
||||
batch vendors the missing context.
|
||||
- `ini-boundary-summary.tsv`: one row per unique INI referenced by the native
|
||||
inventory. It aggregates path-level boundary reports so vendored INIs have a
|
||||
direct machine-readable `report_available=1` check and hard block
|
||||
recommendations can be audited at INI granularity.
|
||||
- `blocked-dependency-summary.tsv`: one row per hard blocked
|
||||
`L4-PYTHON-REMAP`, `L4-TOOL-DB`, and `L4-USER-M-PROCESS` path. It reads the
|
||||
source `linuxcnc/configs/sim` INI files without vendoring or executing those
|
||||
families, records Python modules, remap/prolog/epilog function ownership,
|
||||
NGC remap subpaths, HAL/UI/HALUI process declarations, `DB_PROGRAM`, and
|
||||
tool database protocol evidence, plus external user-M execution codes, their
|
||||
process script files, and process side-effect evidence. The `linuxcnc_owner`
|
||||
field records the LinuxCNC source/config files that own the blocked behavior,
|
||||
and keeps those rows blocked until a LinuxCNC-owned runtime boundary is
|
||||
designed.
|
||||
- `full-process-boundary-summary.tsv`: one row for each designed-but-still-blocked
|
||||
non-Python full-process boundary. It currently covers `axis/db_demo/base.ngc`
|
||||
and `axis/vismach/millturn/example.ngc`, records LinuxCNC runtime owner
|
||||
evidence, records required native/Node/browser proof, and requires
|
||||
`execution_enabled=0`.
|
||||
- `user-m-process-boundary-summary.tsv`: one row for each designed-but-still-blocked
|
||||
external user-M process boundary. It currently records the `millturn`
|
||||
`M128`/`M129` process files, remap call chain, kinstype guards, and
|
||||
source-traceable `ini.[xyz].*` HAL pin state targets, while requiring
|
||||
`execution_enabled=0` and `promotion_allowed=0`.
|
||||
- `user-m-process-transition-plan.tsv`: the pending non-executing transition
|
||||
contract for the `millturn` user-M boundary. It binds `M428` to `M128` and
|
||||
`M429` to `M129`, records switchkins output/target, active G5x work offset,
|
||||
guard pin, state mode, and expected `ini.[xyz].*` outputs, and still requires
|
||||
`execution_enabled=0` and `promotion_allowed=0`.
|
||||
- `user-m-process-native-transition-alignment.tsv`: the native source
|
||||
alignment for the two `millturn` transition rows. It proves the generated
|
||||
`M428/M429` transition contract still matches the native probe stdout for
|
||||
switchkins output/target, G5x, offset pocket, and `M428 -> M128` /
|
||||
`M429 -> M129` calls, while remaining pending and non-executing.
|
||||
- `user-m-process-native-runtime-state-plan.tsv`: the pending native runnable
|
||||
state-probe contract for `millturn`. It records the required LinuxCNC
|
||||
task/HAL/Tcl user-M runtime, the HAL/INI environment, and exact
|
||||
`ini.[xyz].*` values that a future runtime probe must observe after the
|
||||
transitions, while keeping `execution_enabled=0` and `promotion_allowed=0`.
|
||||
- `user-m-process-native-runtime-readiness.tsv`: the host capability gate for
|
||||
that future native probe. It records whether `tclsh`, `halrun`, `halcmd`,
|
||||
and `linuxcnc` are available on PATH and keeps the boundary pending and
|
||||
non-promotable regardless of host readiness.
|
||||
- `user-m-process-native-runtime-probe-gate.tsv`: the generated execution gate
|
||||
for that future native probe. It records per-transition runtime readiness,
|
||||
missing host commands, source-proof readiness, and the pending gate status.
|
||||
The native `probe_millturn_user_m_runtime.sh` entry point reports
|
||||
`skipped_missing_host_runtime` on hosts without the full LinuxCNC HAL/Tcl
|
||||
runtime, `ready_disabled_by_default` when the host is ready but the guarded
|
||||
runtime probe is not explicitly enabled, or `runtime_state_probe_passed` when
|
||||
`ENABLE_MILLTURN_USER_M_RUNTIME_PROBE=1` proves the LinuxCNC-owned
|
||||
`M128`/`M129` Tcl scripts update the expected `ini.[xyz].*` HAL pins.
|
||||
command set and does not enable execution or promotion.
|
||||
- `tool-db-process-boundary-summary.tsv`: one row for each designed-but-still-blocked
|
||||
tool database process boundary. It currently records `db_nonran.py`,
|
||||
LinuxCNC tooldata protocol messages, DB program callbacks, nonrandom tool
|
||||
database state targets, and runtime owner evidence, while requiring
|
||||
`execution_enabled=0` and `promotion_allowed=0`.
|
||||
- `tool-db-process-transaction-plan.tsv`: the pending non-executing transaction
|
||||
contract for the `db_demo` tool database boundary. It orders startup
|
||||
handshake, get-all, spindle load notify, tool offset notify, and spindle
|
||||
unload notify steps, binds each protocol message to the expected `db.py`
|
||||
callback and state targets, and still requires `execution_enabled=0` and
|
||||
`promotion_allowed=0`.
|
||||
- `tool-db-process-native-runtime-readiness.tsv`: the host capability gate for
|
||||
the guarded native DB process protocol probe. It records whether `python3`,
|
||||
`linuxcnc`, `milltask`, `halcmd`, the configured `db_nonran.py`, and
|
||||
LinuxCNC's `linuxcnc.so` / `tooldb.py` Python modules are available, while
|
||||
keeping the DB boundary pending and non-promotable. The native
|
||||
`probe_tool_db_runtime.sh` entry point reports `skipped_missing_host_runtime`
|
||||
when the host runtime is unavailable, `ready_disabled_by_default` when it is
|
||||
available but not explicitly enabled, or `runtime_protocol_probe_passed` when
|
||||
`ENABLE_TOOL_DB_RUNTIME_PROBE=1` proves the LinuxCNC-owned `DB_PROGRAM`
|
||||
`v2.1`/`g`/`p`/`l`/`u` protocol, nonrandom state updates, and flat-file
|
||||
persistence.
|
||||
- `python-remap-boundary-summary.tsv`: one row for each `L4-PYTHON-REMAP`
|
||||
path. It records Python modules, remap/prolog/epilog functions, NGC-only
|
||||
subpaths, HAL/UI/HALUI assumptions, the LinuxCNC Python owner set, LinuxCNC
|
||||
Python runtime owner evidence, and requires `execution_enabled=0`.
|
||||
- `python-remap-family-summary.tsv`: one row per blocked Python-remap runtime
|
||||
family. It aggregates row and INI counts plus Python module,
|
||||
remap/prolog/epilog, NGC-only subpath, Python runtime owner evidence,
|
||||
HAL/UI/HALUI, and `execution_enabled=0` evidence without vendoring or
|
||||
executing those families.
|
||||
- `python-remap-runtime-contract.tsv`: one row per blocked Python-remap runtime
|
||||
family. It records LinuxCNC-owned Python runtime phases, Python modules,
|
||||
remap/prolog/epilog callables, NGC-only subpaths, HAL/UI/HALUI assumptions,
|
||||
native/Node/browser proof requirements, and keeps `proof_status=pending`,
|
||||
`execution_enabled=0`, and `promotion_allowed=0`.
|
||||
- `python-remap-native-runtime-readiness.tsv`: the host/source capability gate
|
||||
for guarded native Python-remap runtime probes. It records `python3`,
|
||||
`linuxcnc`, LinuxCNC's Python interpreter/plugin owner source files, and the
|
||||
configured Python modules for each blocked family, while keeping every row
|
||||
pending, non-executing, and non-promotable.
|
||||
- `python-remap-native-runtime-state-plan.tsv`: one row per blocked
|
||||
Python-remap family. It records the runtime phases, modules, callables,
|
||||
NGC-only subpaths, process assumptions, readiness counts, source-alignment
|
||||
inputs, and exact future native state-probe targets while keeping every row
|
||||
pending, non-executing, and non-promotable.
|
||||
- `python-remap-native-runtime-fixture-plan.tsv`: the first non-executing
|
||||
Python runtime fixture plan. It selects
|
||||
`axis/remap/stop-lookahead/nc_files` as the minimal lifecycle fixture because
|
||||
it has Python modules and runtime phases without Python callable or NGC-only
|
||||
subpath complexity, and keeps execution/promotion disabled. The native
|
||||
`probe_python_remap_runtime.sh` entry point reports
|
||||
`skipped_missing_host_runtime` without the host runtime,
|
||||
`ready_disabled_by_default` when the host is ready but the probe is not
|
||||
explicitly enabled, or `runtime_lifecycle_probe_passed` when
|
||||
`ENABLE_PYTHON_REMAP_RUNTIME_PROBE=1` proves the `demo.ini`
|
||||
path/toplevel import, `queuebuster` callable lookup, generator return, and
|
||||
first `INTERP_EXECUTE_FINISH` yield.
|
||||
- `runtime-boundary-contract-summary.tsv`: one row per blocked runtime boundary
|
||||
class. It unifies the user-M transition contract, tool DB transaction
|
||||
contract, and Python runtime contract with their native-alignment artifact,
|
||||
and requires every contract row to remain pending with execution and
|
||||
promotion disabled.
|
||||
- `boundary-phase-completion-summary.tsv`: one row per boundary-phase
|
||||
completion criterion. It requires vendored INI boundary reports, dependency
|
||||
evidence for hard blocks, safe HAL/UI representative coverage, blocked
|
||||
families remaining skipped/non-representative with execution disabled, and
|
||||
LinuxCNC-owned runtime proof fields for user-M, tool DB, and Python runtime
|
||||
blockers. When native source proof is available, it also requires that proof
|
||||
to be consumed by generated worklist/native proof-gate rows without enabling
|
||||
execution or promotion.
|
||||
- `native-proof-alignment-summary.tsv`: one row per native source proof class.
|
||||
It aligns the native source proof summary with generated worklist/native
|
||||
proof-gate consumers for user-M, tool DB, and Python runtime blockers.
|
||||
- `next-boundary-worklist.tsv`: one row per next blocked runtime-boundary
|
||||
design target. It keeps execution and promotion disabled, records the
|
||||
LinuxCNC owner set and runtime owner evidence, records native, Node, and
|
||||
browser proof requirements, and names the next boundary-design action before
|
||||
any blocked family can be promoted.
|
||||
|
||||
## Summary
|
||||
|
||||
| Class | Count |
|
||||
| --- | --- |
|
||||
| `main` | `48` |
|
||||
| `macro_load` | `46` |
|
||||
| `remap_subroutine` | `65` |
|
||||
|
||||
## Family Summary
|
||||
|
||||
| Family | Count |
|
||||
| --- | --- |
|
||||
| `axis/db_demo` | `1` |
|
||||
| `axis/external_offsets` | `6` |
|
||||
| `axis/foam` | `1` |
|
||||
| `axis/geometry` | `1` |
|
||||
| `axis/gladevcp` | `1` |
|
||||
| `axis/laser` | `3` |
|
||||
| `axis/lathe-fanucy` | `1` |
|
||||
| `axis/remap` | `29` |
|
||||
| `axis/rose_engine` | `2` |
|
||||
| `axis/vismach` | `57` |
|
||||
| `gmoccapy/lathe_configs` | `8` |
|
||||
| `gmoccapy/macros` | `25` |
|
||||
| `gmoccapy/non_trivial_kinematics` | `5` |
|
||||
| `gscreen/industrial_lathe_wear` | `1` |
|
||||
| `gscreen/silverdragon` | `4` |
|
||||
| `qtaxis/non-trivial` | `3` |
|
||||
| `qtdragon/qtdragon_multi_joint` | `1` |
|
||||
| `qtdragon/qtdragon_xyz` | `1` |
|
||||
| `qtdragon/qtdragon_xyz45` | `1` |
|
||||
| `qtdragon_hd/qtdragon_hd_xyz` | `1` |
|
||||
| `qtdragon_hd/qtdragon_hd_z_compensation` | `1` |
|
||||
| `qtvcp_screens/industrial_lathe_wear` | `1` |
|
||||
| `qtvcp_screens/non-trivial` | `3` |
|
||||
| `qtvcp_screens/qtdragon` | `1` |
|
||||
| `woodpecker` | `1` |
|
||||
|
||||
## Program Matrix
|
||||
|
||||
| Path | Family | Class | Native | Expected Failure | Layer 3 | Layer 4 Node | Layer 4 Browser | Blocked | Notes |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| `axis/db_demo/base.ngc` | `axis/db_demo` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-TOOL-DB` | native pass, but Node/browser inventory is blocked by the LinuxCNC tool-database process boundary declared by `[EMCIO]DB_PROGRAM` |
|
||||
| `axis/external_offsets/circles.ngc` | `axis/external_offsets` | `macro_load` | `PASS` | `-` | `SIM-EOFF` | `REP` | `REP` | `-` | Layer 4 macro-only class representative now covered through wrapped execution with native staging inputs |
|
||||
| `axis/external_offsets/dyn_demo.ngc` | `axis/external_offsets` | `main` | `FAIL` | `user-m-code-M111` | `SIM-EOFF` | `REP` | `REP` | `-` | Layer 2 expected-fail; M111 runtime edge covered |
|
||||
| `axis/external_offsets/eoffsets.ngc` | `axis/external_offsets` | `main` | `FAIL` | `user-m-code-M111` | `SIM-EOFF` | `REP` | `REP` | `-` | Layer 2 expected-fail; M111 runtime edge covered |
|
||||
| `axis/external_offsets/jwp_z.ngc` | `axis/external_offsets` | `main` | `FAIL` | `user-m-code-M111` | `SIM-EOFF` | `REP` | `REP` | `-` | Layer 2 expected-fail; M111 runtime edge covered |
|
||||
| `axis/external_offsets/opa_demo.ngc` | `axis/external_offsets` | `main` | `FAIL` | `user-m-code-M111` | `SIM-EOFF` | `REP` | `REP` | `-` | Layer 2 expected-fail; M111 runtime edge covered |
|
||||
| `axis/external_offsets/queuebuster.ngc` | `axis/external_offsets` | `macro_load` | `PASS` | `-` | `SIM-EOFF` | `-` | `-` | `-` | inventory-only macro/load path; macro_load class coverage is represented by `axis/external_offsets/circles.ngc` |
|
||||
| `axis/foam/foam.ngc` | `axis/foam` | `main` | `FAIL` | `ini-axis-mask-UV` | `SIM-FOAM` | `REP` | `REP` | `-` | Layer 2 expected-fail; Layer 3/4 runtime edge covered |
|
||||
| `axis/geometry/xyzc.ngc` | `axis/geometry` | `main` | `FAIL` | `user-m-code-M110` | `SIM-XYZC` | `REP` | `REP` | `-` | Layer 2 expected-fail; M110 runtime edge covered |
|
||||
| `axis/gladevcp/probe.ngc` | `axis/gladevcp` | `main` | `PASS` | `-` | `-` | `REP` | `REP` | `-` | plain INI/tool-table main-program class representative covered in Node and browser through vendored staging inputs |
|
||||
| `axis/laser/raster_test.ngc` | `axis/laser` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `axis/laser/vector_test.ngc` | `axis/laser` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `axis/laser/vector_test2.ngc` | `axis/laser` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `axis/lathe-fanucy/toolchange.ngc` | `axis/lathe-fanucy` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `-` | inventory-only macro/load path; macro_load class coverage is represented by `axis/external_offsets/circles.ngc` unless this family is deliberately promoted later |
|
||||
| `axis/remap/cycle/nc_files/examples.ngc` | `axis/remap` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native baseline pass, but Layer 4 full inventory is blocked by Python remap/prolog/epilog runtime boundaries; remap subroutine assets remain `ASSET-ONLY` |
|
||||
| `axis/remap/cycle/nc_subroutines/g843.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/extend-builtins/nc_files/examples.ngc` | `axis/remap` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native baseline pass, but Layer 4 full inventory is blocked by Python prolog/epilog remap runtime boundaries; remap subroutine assets remain `ASSET-ONLY` |
|
||||
| `axis/remap/extend-builtins/nc_subroutines/change.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/extend-builtins/nc_subroutines/extend_m0.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/extend-builtins/nc_subroutines/extend_m1.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/extend-builtins/nc_subroutines/extend_m60.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/extend-builtins/nc_subroutines/extend_m7.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/extend-builtins/nc_subroutines/extend_m8.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/extend-builtins/nc_subroutines/extend_m9.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/extend-builtins/nc_subroutines/prepare.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/extend-builtins/nc_subroutines/setfeed.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/extend-builtins/nc_subroutines/setspeed.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/extend-builtins/nc_subroutines/settool.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/getting-started/nc_files/examples.ngc` | `axis/remap` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native baseline pass, but Layer 4 full inventory is blocked by Python remap handlers such as `G88.1`; remap subroutine assets remain `ASSET-ONLY` |
|
||||
| `axis/remap/getting-started/nc_subroutines/change_min.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/getting-started/nc_subroutines/m400.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/getting-started/nc_subroutines/m410.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/getting-started/nc_subroutines/prepare_min.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/manual-toolchange-with-tool-length-switch/nc_files/tcdemo.ngc` | `axis/remap` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native baseline pass, but Layer 4 full inventory is blocked by Python tool-change prolog/epilog runtime boundaries; remap subroutine assets remain `ASSET-ONLY` |
|
||||
| `axis/remap/manual-toolchange-with-tool-length-switch/nc_subroutines/manual_change.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/manual-toolchange-with-tool-length-switch/nc_subroutines/on_abort.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/manual-toolchange-with-tool-length-switch/nc_subroutines/restore.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/rack-toolchange/nc_files/tcdemo.ngc` | `axis/remap` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native baseline pass, but Layer 4 full inventory is blocked by Python tool-change prolog/epilog runtime boundaries; remap subroutine assets remain `ASSET-ONLY` |
|
||||
| `axis/remap/rack-toolchange/nc_subroutines/on_abort.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/rack-toolchange/nc_subroutines/rack_change.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/rack-toolchange/nc_subroutines/tool_getput_move.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/rack-toolchange/nc_subroutines/tool_holder_clear_move.ngc` | `axis/remap` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/remap/stop-lookahead/nc_files/examples.ngc` | `axis/remap` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native baseline pass, but Layer 4 full inventory is blocked by Python `queuebuster` remap runtime boundaries; remap subroutine assets remain `ASSET-ONLY` |
|
||||
| `axis/rose_engine/rcone.ngc` | `axis/rose_engine` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `-` | inventory-only macro/load path; macro_load class coverage is represented by `axis/external_offsets/circles.ngc` unless this family is deliberately promoted later |
|
||||
| `axis/rose_engine/rcone_demo.ngc` | `axis/rose_engine` | `main` | `PASS` | `-` | `-` | `INV` | `-` | `-` | Node inventory coverage uses the native harness-selected `axis/sim.tbl` fallback staged at the INI-declared tool-table path; browser class coverage is represented by other plain INI/tool-table samples |
|
||||
| `axis/vismach/5axis/bridgemill/5axisgui.ngc` | `axis/vismach` | `main` | `FAIL` | `ini-axis-mask-W` | `SIM-BRIDGE,5AX-EXEC` | `REP` | `REP` | `-` | Layer 2 expected-fail; W-axis and remap runtime edge covered |
|
||||
| `axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `5AX-EXEC` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/bridgemill/remap_subs/429remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `5AX-EXEC` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/bridgemill/remap_subs/430remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `5AX-EXEC` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-dual-rotary/demos/xyzab-tdr-demo.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `5AX-EXEC` | `REP` | `REP` | `-` | TDR five-axis remap class covered in Node and browser through vendored INI, tool-table, remap-subroutine, and demo staging |
|
||||
| `axis/vismach/5axis/table-dual-rotary/remap_subs/428remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `5AX-EXEC` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-dual-rotary/remap_subs/429remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `5AX-EXEC` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/demos/boat-xyzac.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `5AX-EXEC` | `REP` | `REP` | `-` | TRT five-axis remap class covered in Node and browser through vendored INI, tool-table, remap-subroutine, and demo staging |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/demos/boat-xyzbc.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `5AX-EXEC` | `REP` | `REP` | `-` | TRT five-axis remap class covered in Node and browser through vendored INI, tool-table, remap-subroutine, and demo staging |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/demos/impeller-7bl-xyzac.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `5AX-EXEC` | `REP` | `REP` | `-` | TRT five-axis remap class covered in Node and browser through vendored INI, tool-table, remap-subroutine, and demo staging |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `5AX-EXEC` | `REP` | `REP` | `-` | TRT five-axis remap class covered in Node and browser through vendored INI, tool-table, remap-subroutine, and demo staging |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins_test_1.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `5AX-EXEC` | `REP` | `REP` | `-` | TRT five-axis remap class covered in Node and browser through vendored INI, tool-table, remap-subroutine, and demo staging |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins_test_2.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `5AX-EXEC` | `REP` | `REP` | `-` | TRT five-axis remap class covered in Node and browser through vendored INI, tool-table, remap-subroutine, and demo staging |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins_test_3.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `5AX-EXEC` | `REP` | `REP` | `-` | TRT five-axis remap class covered in Node and browser through vendored INI, tool-table, remap-subroutine, and demo staging |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `5AX-EXEC` | `REP` | `REP` | `-` | TRT five-axis remap class covered in Node and browser through vendored INI, tool-table, remap-subroutine, and demo staging |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/remap_subs/428remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `5AX-EXEC` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/remap_subs/429remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `5AX-EXEC` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/remap_subs/430remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `5AX-EXEC` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/remap_subs/centering.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `5AX-EXEC` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/remap_subs/helix_ac.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `5AX-EXEC` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/remap_subs/helix_bc.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `5AX-EXEC` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/remap_subs/xyzac_switchkins_sub.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `5AX-EXEC` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary-tilting/remap_subs/xyzbc_switchkins_sub.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `5AX-EXEC` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/circular_pocket.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass, but Layer 4 full inventory is blocked on Python remap entry points such as g682, g69_core, and g53x_core |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass, but Layer 4 full inventory is blocked on Python remap entry points such as g682, g69_core, and g53x_core |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_back_and_forth.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass, but Layer 4 full inventory is blocked on Python remap entry points such as g682, g69_core, and g53x_core |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc` | `axis/vismach` | `main` | `FAIL` | `upstream-demo-missing-motion-gcode` | `-` | `-` | `-` | `UPSTREAM-DEMO` | preserve upstream demo failure; do not force PASS |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/simple_example.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass, but Layer 4 full inventory is blocked on Python remap entry points such as g682, g69_core, and g53x_core |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/square.ngc` | `axis/vismach` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass, but Layer 4 full inventory is blocked on Python remap entry points such as g682, g69_core, and g53x_core |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/428remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/429remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/430remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g531remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g533remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g536remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/g69remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/on_abort_no_twp_reset.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/on_abort_with_twp_reset.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/VMC_toolchange/toolchange.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native baseline pass, but Layer 4 full inventory is blocked by Python tool-change prolog/epilog and full tool-change runtime boundaries |
|
||||
| `axis/vismach/hexapod-sim/remap_subs/428remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/hexapod-sim/remap_subs/429remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/hexapod-sim/remap_subs/430remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/melfa-sim/example.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `-` | `REP` | `REP` | `-` | robot/Genser-style remap machine class representative covered in Node and browser through vendored INI, tool-table, and remap-subroutine staging |
|
||||
| `axis/vismach/melfa-sim/remap_subs/428remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/melfa-sim/remap_subs/429remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/melfa-sim/remap_subs/430remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/millturn/example.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-USER-M-PROCESS` | native pass, but Node/browser inventory is blocked by external `USER_M_PATH` process M-codes `M128` and `M129` rather than the deterministic `M110`/`M111` boundary already modeled by the standalone runtime |
|
||||
| `axis/vismach/millturn/remap_subs/428remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/millturn/remap_subs/429remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/puma/puma_cube.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `-` | `REP` | `REP` | `-` | PUMA kinematics/remap machine class representative covered in Node and browser through vendored INI, tool-table, and remap-subroutine staging |
|
||||
| `axis/vismach/puma/puma_seam_weld.ngc` | `axis/vismach` | `main` | `PASS` | `-` | `-` | `INV` | `-` | `-` | Node inventory covered with the same vendored puma machine context; browser class coverage is represented by `puma_cube.ngc` |
|
||||
| `axis/vismach/puma/remap_subs/428remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/puma/remap_subs/429remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/puma/remap_subs/430remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/scara/remap_subs/428remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/scara/remap_subs/429remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `axis/vismach/scara/remap_subs/430remap.ngc` | `axis/vismach` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `gmoccapy/lathe_configs/boring.ngc` | `gmoccapy/lathe_configs` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/lathe_configs/chamfer.ngc` | `gmoccapy/lathe_configs` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/lathe_configs/drilling.ngc` | `gmoccapy/lathe_configs` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/lathe_configs/facing.ngc` | `gmoccapy/lathe_configs` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/lathe_configs/grooving.ngc` | `gmoccapy/lathe_configs` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/lathe_configs/radius.ngc` | `gmoccapy/lathe_configs` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/lathe_configs/threading.ngc` | `gmoccapy/lathe_configs` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/lathe_configs/turning.ngc` | `gmoccapy/lathe_configs` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/change.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/change_g43.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/go_to_position.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/halo_world.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/i_am_lost.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/increment.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/jog_around.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_0.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_1.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_10.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_11.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_12.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_13.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_14.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_15.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_2.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_3.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_4.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_5.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_6.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_7.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_8.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/macro_9.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/on_abort.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/macros/settool_g43.ngc` | `gmoccapy/macros` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzac.ngc` | `gmoccapy/non_trivial_kinematics` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/boat-xyzbc.ngc` | `gmoccapy/non_trivial_kinematics` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/impeller-7bl-xyzac.ngc` | `gmoccapy/non_trivial_kinematics` | `main` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/test-xyzac.ngc` | `gmoccapy/non_trivial_kinematics` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gmoccapy/non_trivial_kinematics/table-rotary-tilting/examples/test-xyzbc.ngc` | `gmoccapy/non_trivial_kinematics` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `L4-PYTHON-REMAP` | native pass; Layer 4 is blocked until the Python-remap runtime boundary is deliberately designed |
|
||||
| `gscreen/industrial_lathe_wear/toolchange.ngc` | `gscreen/industrial_lathe_wear` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `-` | inventory-only macro/load path; macro_load class coverage is represented by `axis/external_offsets/circles.ngc` unless this family is deliberately promoted later |
|
||||
| `gscreen/silverdragon/macros/auto_zref.ngc` | `gscreen/silverdragon` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `-` | inventory-only macro/load path; macro_load class coverage is represented by `axis/external_offsets/circles.ngc` unless this family is deliberately promoted later |
|
||||
| `gscreen/silverdragon/macros/laserzero.ngc` | `gscreen/silverdragon` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `-` | inventory-only macro/load path; macro_load class coverage is represented by `axis/external_offsets/circles.ngc` unless this family is deliberately promoted later |
|
||||
| `gscreen/silverdragon/macros/tool_sensor.ngc` | `gscreen/silverdragon` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `-` | inventory-only macro/load path; macro_load class coverage is represented by `axis/external_offsets/circles.ngc` unless this family is deliberately promoted later |
|
||||
| `gscreen/silverdragon/macros/touch_plate.ngc` | `gscreen/silverdragon` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `-` | inventory-only macro/load path; macro_load class coverage is represented by `axis/external_offsets/circles.ngc` unless this family is deliberately promoted later |
|
||||
| `qtaxis/non-trivial/scara/remap_subs/428remap.ngc` | `qtaxis/non-trivial` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `qtaxis/non-trivial/scara/remap_subs/429remap.ngc` | `qtaxis/non-trivial` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `qtaxis/non-trivial/scara/remap_subs/430remap.ngc` | `qtaxis/non-trivial` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `qtdragon/qtdragon_multi_joint/on_abort.ngc` | `qtdragon/qtdragon_multi_joint` | `main` | `PASS` | `-` | `-` | `INV` | `-` | `-` | vendored INI/tool-table now allow Node inventory coverage; browser representative still delegated to the shared `woodpecker/on_abort.ngc` class sample |
|
||||
| `qtdragon/qtdragon_xyz/on_abort.ngc` | `qtdragon/qtdragon_xyz` | `main` | `PASS` | `-` | `-` | `INV` | `-` | `-` | vendored INI/tool-table now allow Node inventory coverage; browser representative still delegated to the shared `woodpecker/on_abort.ngc` class sample |
|
||||
| `qtdragon/qtdragon_xyz45/on_abort.ngc` | `qtdragon/qtdragon_xyz45` | `main` | `PASS` | `-` | `-` | `INV` | `-` | `-` | vendored INI/tool-table now allow Node inventory coverage; browser representative still delegated to the shared `woodpecker/on_abort.ngc` class sample |
|
||||
| `qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc` | `qtdragon_hd/qtdragon_hd_xyz` | `main` | `PASS` | `-` | `-` | `INV` | `-` | `-` | vendored INI/tool-table now allow Node inventory coverage; browser representative still delegated to the shared `woodpecker/on_abort.ngc` class sample |
|
||||
| `qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc` | `qtdragon_hd/qtdragon_hd_z_compensation` | `main` | `PASS` | `-` | `-` | `INV` | `-` | `-` | vendored INI/tool-table now allow Node inventory coverage; browser representative still delegated to the shared `woodpecker/on_abort.ngc` class sample |
|
||||
| `qtvcp_screens/industrial_lathe_wear/toolchange.ngc` | `qtvcp_screens/industrial_lathe_wear` | `macro_load` | `PASS` | `-` | `-` | `-` | `-` | `-` | inventory-only macro/load path; macro_load class coverage is represented by `axis/external_offsets/circles.ngc` unless this family is deliberately promoted later |
|
||||
| `qtvcp_screens/non-trivial/scara/remap_subs/428remap.ngc` | `qtvcp_screens/non-trivial` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `qtvcp_screens/non-trivial/scara/remap_subs/429remap.ngc` | `qtvcp_screens/non-trivial` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `qtvcp_screens/non-trivial/scara/remap_subs/430remap.ngc` | `qtvcp_screens/non-trivial` | `remap_subroutine` | `PASS` | `-` | `-` | `N/A` | `N/A` | `ASSET-ONLY` | subroutine asset; validate by parse/remap path rather than standalone main-program execution |
|
||||
| `qtvcp_screens/qtdragon/on_abort.ngc` | `qtvcp_screens/qtdragon` | `main` | `PASS` | `-` | `-` | `INV` | `-` | `-` | vendored INI/tool-table now allow Node inventory coverage; browser representative still delegated to the shared `woodpecker/on_abort.ngc` class sample |
|
||||
| `woodpecker/on_abort.ngc` | `woodpecker` | `main` | `PASS` | `-` | `-` | `REP` | `REP` | `-` | Layer 4 deterministic on_abort class representative now covered |
|
||||
@@ -40,13 +40,19 @@ Current validation is intentionally mechanical:
|
||||
| Identity/trivial kinematics | `src/emc/kinematics/kinematics.h`, `cubic.h`, `kins_util.c`, `trivkins.c` | Copy unchanged | HAL component lifecycle and RTAPI module metadata are replaced by standalone shims; forward/inverse mapping behavior remains LinuxCNC source | Vendor byte sync, per-file source probes, `linuxcnc_kinematics_probe` |
|
||||
| Switchable 5-axis bridge kinematics | `src/emc/kinematics/5axiskins.c`, `switchkins.c`, `switchkins.h`, `userkfuncs.c`, plus `src/rtapi/rtapi_ctype.h` | Copy unchanged | HAL pin allocation, HAL component lifecycle, and RTAPI module metadata are standalone runtime edges; switchable 5-axis forward/inverse behavior remains LinuxCNC source | Vendor byte sync, per-file source probes, `linuxcnc_5axis_kinematics_probe` |
|
||||
| TRT table-rotary kinematics | `src/emc/kinematics/trtfuncs.c`, `xyzac-trt-kins.c`, `xyzbc-trt-kins.c` | Copy unchanged | HAL pin allocation and switchkins lifecycle stay runtime boundaries; XYZAC/XYZBC TRT forward/inverse behavior remains LinuxCNC source | Vendor byte sync, per-file source probes, `linuxcnc_xyzac_trt_kinematics_probe`, `linuxcnc_xyzbc_trt_kinematics_probe` |
|
||||
| Five-axis switchkins machine configs and M428/M429/M430 remaps | `configs/sim/axis/vismach/5axis/bridgemill/*`, `table-dual-rotary/*`, and `table-rotary-tilting/*` selected INI, HAL, tool-table, XML, demo, and `remap_subs/*.ngc` files plus `src/emc/rs274ngc/interp_remap.cc` | Copy unchanged | `M428`, `M429`, and `M430` remain LinuxCNC `REMAP` entries that call LinuxCNC NGC subroutines using `M68`, `M66`, `_hal[motion.switchkins-type]`, and the INI/HAL `motion.analog-out-03 => motion.switchkins-type` link; standalone remap descriptor parsing routes through vendored `Interp::parse_remap()` and `find_ngc_file()`, and NGC remap/file execution routes through vendored LinuxCNC O-word and `open()`/`read()`/`execute()` paths while HAL synchronization stays a runtime adapter boundary | Vendor byte sync, `linuxcnc_5axis_remap_asset_probe`, `linuxcnc_remap_parse_harness`, `linuxcnc_remap_hal_sync_harness`, `linuxcnc_5axis_remap_execute_harness`, `tests/wasm/node/verify_interp_wasm.sh`, `tests/browser/verify_interp_browser.sh`, `tests/browser/verify_ini_panel_browser.sh` |
|
||||
| Five-axis switchkins machine configs and M428/M429/M430 remaps | `configs/sim/axis/vismach/5axis/bridgemill/*`, `table-dual-rotary/*`, and `table-rotary-tilting/*` selected INI, HAL, tool-table, XML, demo, and `remap_subs/*.ngc` files plus `src/emc/rs274ngc/interp_remap.cc` | Copy unchanged | `M428`, `M429`, and `M430` remain LinuxCNC `REMAP` entries that call LinuxCNC NGC subroutines using `M68`, `M66`, `_hal[motion.switchkins-type]`, and the INI/HAL `motion.analog-out-03 => motion.switchkins-type` link; standalone remap descriptor parsing routes through vendored `Interp::parse_remap()` and `find_ngc_file()`, and NGC remap/file execution routes through vendored LinuxCNC O-word and `open()`/`read()`/`execute()` paths while HAL synchronization stays a runtime adapter boundary | Vendor byte sync, `linuxcnc_5axis_remap_asset_probe`, `linuxcnc_remap_parse_harness`, `linuxcnc_remap_hal_sync_harness`, `linuxcnc_5axis_remap_execute_harness`, `tests/wasm/node/verify_interp_wasm.sh`, `tests/wasm/node/verify_sim_configs_wasm.sh`, `tests/browser/verify_interp_browser.sh`, `tests/browser/verify_ini_panel_browser.sh` |
|
||||
| Representative sim-config machine programs | `configs/sim/axis/foam/*`, `configs/sim/axis/geometry/M110`, `xyzc.ini`, `xyzc.ngc`, `configs/sim/axis/external_offsets/M111`, `dyn_demo.ngc`, `dynamic_offsets.ini`, `eoffsets.ngc`, `eoffsets.ini`, `jwp_z.ngc`, `jwp_z.ini`, `opa_demo.ngc`, `opa.ini`, `circles.ngc`, `eoffset.tbl`, `configs/sim/axis/sim.tbl`, `configs/sim/axis/gladevcp/gladevcp_panel.ini`, `probe.ngc`, `sim.tbl`, `configs/sim/axis/vismach/melfa-sim/*`, selected `configs/sim/axis/vismach/puma/*`, and `configs/sim/woodpecker/on_abort.ngc`, `tool.tbl`, `woodpecker.ini` | Copy unchanged | The `foam`, `xyzc`, external-offset, plain `gladevcp/probe.ngc`, `melfa-sim`, PUMA, and deterministic `woodpecker/on_abort.ngc` sample programs remain LinuxCNC sim-config assets. The standalone boundary only copies INI, tool table, G-code, executable user-M files, `SUBROUTINE_PATH` files, and remap-NGC files into the native or WASM filesystem, then calls vendored LinuxCNC INI parsing and `Interp::open()`/`read()`/`execute()` or the existing five-axis remap execution C ABI through the machine-config adapter. The native sim-config harness now prefers `[DISPLAY]OPEN_FILE` when several INI files live beside a program, so each program is judged with its corresponding machine context. The JS SDK `planIniFileContextStaging()` is a generic manifest-based file-plan helper, `planSimConfigStaging()` is its `configs/sim` wrapper, and `runSimConfigProgram()` only writes caller-provided text files to the Emscripten filesystem, applies executable bits, and forwards to LinuxCNC-backed C ABI entry points. `M110`/`M111` process execution stays a runtime boundary represented by deterministic `USER_M_COMMAND` canonical events, while GUI driver, HAL process launch, and external userspace process startup remain outside standalone Node/browser execution. The TWP `table-rotary_spindle-rotary-nutating` demo family remains outside this representative row because its Layer 4 promotion still depends on a deliberate Python-remap runtime boundary, not on additional JS-owned staging logic. | Vendor byte sync, `tests/native/verify_sim_configs.sh`, `tests/wasm/node/verify_sim_configs_wasm.sh`, `tests/wasm/node/verify_sim_configs_inventory_wasm.sh`, `tests/browser/verify_interp_browser.sh`, `tests/host/verify_host_smokes.sh` |
|
||||
| Sim-config inventory and blocked-boundary assets | Selected `configs/sim/axis/db_demo/*`, `axis/rose_engine/*`, `axis/vismach/millturn/*`, `configs/sim/qtdragon/*`, `configs/sim/qtdragon_hd/*`, and `configs/sim/qtvcp_screens/qtdragon/*` files | Copy unchanged | These assets are vendored for the machine-readable `configs/sim` Node inventory and blocked-boundary accounting. Eligible `on_abort` and plain INI/tool-table rows run through the same LinuxCNC-backed `runSimConfigProgram()` staging path as other sim-config samples. Rows such as `axis/db_demo/base.ngc` and `axis/vismach/millturn/example.ngc` remain explicit Layer 4 blocked entries because their native LinuxCNC configs depend on tool-database or external user-M process boundaries; Node/browser inventory records those dependencies instead of reducing them to standalone file execution. | Vendor byte sync, `tests/native/verify_sim_configs.sh`, `tests/wasm/node/verify_sim_configs_inventory_wasm.sh`, `tests/host/verify_host_smokes.sh` |
|
||||
| Representative `nc_files` examples | `nc_files/3D_Chips.ngc`, `nc_files/arcspiral.ngc`, `nc_files/factorial.ngc`, `nc_files/hole-circle.ngc`, `nc_files/m6demo.ngc` | Copy unchanged | These upstream `linuxcnc/nc_files` examples are copied byte-for-byte into the WASM vendor tree after the native `nc_files` harness classifies the broader directory. Node and browser tests only stage the original `.ngc` text and, for `3D_Chips.ngc`, the minimal INI-declared tool table context required by its upstream `T1 M6` line in the Emscripten filesystem, then call the LinuxCNC-backed `Interp::open()`, `read()`, and `execute()` path through the existing SDK; no G-code, O-word, tool-change, or M-code behavior is implemented in JavaScript | Vendor byte sync, `tests/native/verify_nc_files.sh`, `tests/wasm/node/verify_nc_files_wasm.sh`, `tests/browser/verify_interp_browser.sh`, `tests/host/verify_host_smokes.sh` |
|
||||
| LinuxCNC remap regression fixtures | `tests/remap/duplicate-o-word/*`, `tests/remap/fail/args.0/*`, `tests/remap/fail/args.1/*`, `tests/remap/fail/args.2/*`, `tests/remap/fail/body-ngc/*`, `tests/remap/m30-interaction/*`, `tests/remap/nested-remaps-oword/*`, `tests/remap/posargs.0/*`, `tests/remap/sequencing/*`, selected NGC-only `tests/remap/remap-io/test-ngc.ini` plus `io_*.ngc`, and `src/emc/rs274ngc/interp_remap.cc` plus `interp_o_word.cc` | Copy unchanged | The upstream duplicate O-word, NGC-only remap failure, M30/remap-level interaction, nested O-word remap, positional-argument remap, G/M remap sequencing, and remap-IO NGC subroutine regressions remain LinuxCNC REMAP/O-word/file or MDI execution tests. The standalone boundary only copies the upstream test files into native or WASM filesystems, reads LinuxCNC INI `SUBROUTINE_PATH`, `REMAP`, and `OWORD_NARGS` entries, and calls vendored `Interp::parse_remap()`, `open()`, `read()`, and `execute()` or feeds the upstream remap-IO MDI sequence into vendored `Interp::execute()`; the continue-on-error runner path only mirrors LinuxCNC `rs274 -n 0` test execution and does not implement duplicate-label, O-word, nested remap, M30, positional-argument, failure, sequencing, M62-M68, M66 input, or remap semantics | Vendor byte sync, `linuxcnc_duplicate_oword_remap_harness`, `tests/wasm/node/verify_interp_wasm.sh`, `tests/browser/verify_interp_browser.sh` |
|
||||
| LinuxCNC interpreter regression fixtures | `tests/interp/do-while-break/*`, `tests/interp/oword-bug315/*`, `tests/interp/oword-bug315-p2/*`, `tests/interp/exists/*`, `tests/interp/return-value/*`, `tests/interp/subs-follow-main/*`, `tests/interp/fractional-linenumbers/*`, `tests/interp/cam-nisley/*`, `tests/interp/namedparam-bug424/*`, `tests/interp/flowsnake/*`, selected `tests/interp/good/*.ngc` center-format arc tolerance fixtures, `tests/interp/g6164/*`, `tests/interp/inside-corners/*`, `tests/interp/inverse-time-with-comp/*`, selected `tests/ccomp/*` standalone cutter-compensation fixtures, selected `tests/interp/bad/*.ngc` file-error fixtures, `tests/interp/g33.1/*`, selected `tests/interp/g71-*/*` and `tests/interp/g72-*/*` lathe canned-cycle fixtures, `tests/interp/g76/*`, `tests/interp/g10/*`, `tests/interp/g52/g52-g92-interaction/*`, selected `tests/interp/rotation/*` pure interpreter cases, `tests/interp/iniparam/*`, `tests/interp/iniparam-failassign/*`, `tests/interp/m19/*`, `tests/interp/magic_comments/param_format_printing/*`, selected `tests/interp/m98m99/*` pure interpreter cases, `tests/interp/sub-call-from-sub/*`, `tests/interp/sequence-number/*`, `tests/interp/nested-sub-error/*`, `tests/interp/nested-sub-in-file-error/*`, `tests/interp/oword-unwind/*`, `tests/interp/abort-hot-comment/*`, plus vendored interpreter/O-word sources including `interp_o_word.cc`, `interp_read.cc`, `interp_execute.cc`, `interp_find.cc`, `interp_namedparams.cc`, and `rs274ngc_pre.cc` | Copy unchanged | The upstream do/while/break, O-word bug315, `EXISTS[]`, subroutine return-value, subs-after-main, fractional line-number, storm-door latch cam toolpath with upstream tool-table context, named-parameter, recursive O-word flowsnake toolpath, selected center-format arc tolerance acceptance/rejection cases, `G61`/`G64` path-control and naive-cam tolerance behavior, cutter-compensation concave/convex/tangent geometry, inverse-time feed and cutter-compensation interaction, selected standalone cutter-compensation file execution with upstream tool tables, selected file-execution error cases including canned-cycle A-axis rejection, center-format arc radius mismatch rejection, cutter-compensation arc-exit/gouging rejection, and malformed `EXISTS[]`, rigid-tap `G33.1`, G71/G72 lathe canned-cycle iteration behavior, G76 lathe threading with upstream tool-table context, `G10 L1/L10/L11/L2/L20` tool-table/offset/G5X/G92/rotation regressions, G52/G92 shared-offset interaction, selected rotation/G53/G28 absolute-position and endpoint, INI named-parameter lookup/read-only protection, M19 spindle-orient offset/timeout handling, magic-comment parameter formatting, selected Fanuc `M98/M99` call, loop, missing-P-word, missing-subprogram, mixed Fanuc/RS274NGC sub-style, `DISABLE_FANUC_STYLE_SUB`, main-program O-word termination, and O-expression regressions, external-subroutine-call, external-subroutine line-number, nested-subroutine-definition rejection, blocked forward-seek-to-later-numbered-sub, O-word stack unwind after a subroutine error, `(ABORT,...)` hot-comment parameter-expansion regressions, and INI-declared G92 parameter-file startup persistence remain LinuxCNC interpreter file-execution or initialization tests. The standalone boundary only vendors the original upstream assets, copies `*.ngc`, `test.ini`, `test.tbl`, `subs/*.ngc`, referenced external subroutine `.ngc` files, and staged variable files into native or WASM filesystems where applicable, sets the LinuxCNC `INI_FILE_NAME` runtime edge for `_ini[...]`, applies LinuxCNC INI machine settings such as `[RS274NGC]ORIENT_OFFSET`, loads upstream tool tables through LinuxCNC `tooldata_load()`/`Interp::load_tool_table()`, reads LinuxCNC INI `SUBROUTINE_PATH`, passes `[RS274NGC]PARAMETER_FILE` through vendored `ini_load()`/`Interp::init()`/`restore_parameters()` when the staged variable file exists, captures LinuxCNC `(PRINT,...)` output through the existing stdout/Emscripten `print` boundary, and calls vendored `Interp::open()`, `read()`, and `execute()`; it does not implement O-word loop, break, unwind, subroutine, dynamic call, `M98/M99`, `EXISTS[]`, named-parameter, canned-cycle, cutter-compensation, inverse-time feed, recursive O-word or cam toolpath generation, path-control, naive-cam tolerance, arc tolerance, threading, rigid tap, `G10`, G52/G92 offset behavior, G92 persistence, tool-offset, absolute-position named-parameter, G28/G53 endpoint, INI-variable, read-only parameter, line-number, spindle-speed, spindle-orient, magic-comment formatting, subroutine lookup, ABORT hot-comment, error, or branch semantics | Vendor byte sync, `linuxcnc_interp_minimal_harness`, `tests/wasm/node/verify_interp_wasm.sh`, `tests/browser/verify_interp_browser.sh` |
|
||||
| Additional non-switchable kinematics | `src/emc/kinematics/corexykins.c`, `rotatekins.c`, `rosekins.c`, `maxkins.c`, `lineardeltakins.c`, `lineardeltakins-common.h`, `rotarydeltakins.c`, `rotarydeltakins-common.h`, `scorbot-kins.c`, `tripodkins.c`, `scarakins.c`, `pumakins.c`, `pumakins.h`, `genhexkins.c`, `genhexkins.h`, `genserfuncs.c`, `genserkins.c`, `genserkins.h`, `ugenserkins.c`, `pentakins.c`, `pentakins.h`, `cubic.c` | Copy unchanged | HAL pin allocation, HAL parameter allocation, HAL component lifecycle, RTAPI module metadata, Go math C/C++ linkage, switchkins iterative-forward warmup, and userspace test-program process entry remain standalone runtime edges; forward/inverse behavior remains LinuxCNC source where the module exposes it | Vendor byte sync, per-file source probes, `linuxcnc_corexy_kinematics_probe`, `linuxcnc_rotate_kinematics_probe`, `linuxcnc_rose_kinematics_probe`, `linuxcnc_max_kinematics_probe`, `linuxcnc_lineardelta_kinematics_probe`, `linuxcnc_rotarydelta_kinematics_probe`, `linuxcnc_scorbot_kinematics_probe`, `linuxcnc_tripod_kinematics_probe`, `linuxcnc_scara_kinematics_probe`, `linuxcnc_puma_kinematics_probe`, `linuxcnc_genser_kinematics_probe`, `linuxcnc_genhex_kinematics_probe`, `linuxcnc_pentakins_kinematics_probe` |
|
||||
| Trajectory planner | `src/emc/tp/tp.c`, `tc.c`, `tcq.c`, `spherical_arc.c`, `blendmath.c`, `sp_scurve.c`, `ruckig_wrapper.c`, plus matching `*.h` files | Copy unchanged | Native realtime scheduling and motion process state are replaced by standalone probe setup; the WASM TP probe uses the same deterministic status/config boundary and calls vendored TP APIs through a narrow C ABI | Vendor byte sync, per-file source probes, `linuxcnc_tp_api_probe`, `tests/wasm/node/verify_tp_wasm.sh` |
|
||||
| Ruckig C planner support | Selected `src/emc/tp/cruckig/*.c` and `*.h` files in the manifest | Copy unchanged | Used as LinuxCNC planner support code through vendored TP sources | Vendor byte sync, per-file source probes, `tests/wasm/node/verify_tp_wasm.sh` |
|
||||
| Posemath | `src/libnml/posemath/posemath.cc`, `_posemath.c`, `gomath.c`, `sincos.c`, and matching headers | Copy unchanged | `gomath.c` is compiled as C; `rtapi.h` shim is C/C++ compatible for this boundary | Vendor byte sync, per-file source probes, TP native and WASM probes |
|
||||
| RS274 interpreter state and parser | `src/emc/rs274ngc/modal_state.*`, `interp_internal.*`, `interp_read.cc`, `interp_check.cc`, `interp_execute.cc`, `interp_find.cc`, `interp_array.cc`, `interp_queue.*`, `rs274ngc*`, `units.h` | Copy unchanged | Python/remap/runtime edges are isolated in standalone wrappers and shims; parser and execution logic remain LinuxCNC source | Vendor byte sync, per-file source probes, interpreter harness fixtures |
|
||||
| RS274 conversion semantics | `src/emc/rs274ngc/interp_convert.cc`, `interp_arc.cc`, `interp_inverse.cc`, `interp_cycles.cc`, `interp_g7x.cc`, `interp_o_word.cc`, `interp_write.cc` | Copy unchanged | Canonical calls are captured by standalone event sink functions; conversion behavior stays in vendored LinuxCNC files; feed-rate state is read back through the canonical runtime boundary during length-unit conversion; single-axis rotary indexer lock/unlock dispatch remains vendored LinuxCNC `issue_straight_index()` behavior | Vendor byte sync, per-file source probes, canonical fixture suite, no standalone `Interp::convert_g()` guard, `linuxcnc_indexer_harness`, `tests/wasm/node/verify_interp_wasm.sh` and `tests/browser/verify_interp_browser.sh` rotary-indexer assertions |
|
||||
| User M-code registration boundary | `src/emc/task/emctask.cc`, `src/emc/rs274ngc/interp_convert.cc`, `src/emc/rs274ngc/interp_queue.cc` | Runtime-edge adapter | Vendored interpreter conversion still checks LinuxCNC `USER_DEFINED_FUNCTION` before accepting `M100..M199` and queues through vendored `enqueue_M_USER_COMMAND()`. The standalone machine-config adapter mirrors LinuxCNC task search order for `[DISPLAY]PROGRAM_PREFIX` and `[RS274NGC]USER_M_PATH`, registers executable M-code files in `USER_DEFINED_FUNCTION`, calls the canonical `FINISH()` boundary, and records deterministic `USER_M_COMMAND` events instead of spawning host processes in WASM/browser. | `linuxcnc_interp_minimal_harness` sim `M110`/`M111` probes, `tests/wasm/node/verify_interp_wasm.sh` minimal `M110`/`M111` fixtures |
|
||||
| Named parameters and tool slot status | `src/emc/rs274ngc/interp_namedparams.cc`, related interpreter headers | Copy unchanged | `_ini[...]` and `_hal[...]` resolve through standalone INI/HAL adapters while lookup order stays LinuxCNC-derived; current/selected tool slot reads for vendored `Interp::synch()` come from the standalone tool adapter | Vendor byte sync, source probe, `linuxcnc_namedparam_harness`, `linuxcnc_interp_init_harness`, `tests/wasm/node/verify_interp_wasm.sh` and `tests/browser/verify_interp_browser.sh` `Interp::synch()` tool-slot assertions plus `Interp::init_named_parameters()`/`find_named_param()` named-parameter lookup assertions |
|
||||
| Tool table parsing and formatting | `src/emc/tooldata/tooldata_common.cc` | Copy unchanged plus standalone storage callbacks | Native file parsing/formatting stays in vendored LinuxCNC source; the standalone tool adapter supplies `tooldata_get()`/`tooldata_put()` storage and index lookup, the WASM boundary only selects the LinuxCNC non-random or random-toolchanger branch via `tooldata_init()`, and OPFS remains a host-side persistence boundary | Vendor byte sync, `linuxcnc_tooldata_common_source_probe`, `tests/wasm/node/verify_interp_wasm.sh` non-random/random load/save tool-table assertions |
|
||||
| Dynamic interpreter base | `src/emc/rs274ngc/interp_base.*` | Copy unchanged | `interp_base.cc` source probe uses standalone `EMC2_HOME` compile-time path boundary for LinuxCNC dynamic interpreter lookup | Vendor byte sync, `linuxcnc_interp_base_source_probe` |
|
||||
@@ -55,12 +61,13 @@ Current validation is intentionally mechanical:
|
||||
|
||||
| Dependency | LinuxCNC files that expose it | Standalone treatment |
|
||||
| --- | --- | --- |
|
||||
| Native file IO | `inifile.cc`, `rs274ngc_pre.cc`, `tooldata_common.cc`, parameter file paths, tool table paths | Allowed in native probes; the interpreter WASM C ABI validates parameter-file restore/save by calling vendored `Interp::restore_parameters()` and `Interp::save_parameters()`, including missing required numeric parameter defaulting, and tool-table load/save by calling vendored `tooldata_load()` and `tooldata_save()` against Emscripten filesystem paths; browser OPFS remains a host-side adapter under `runtime/opfs/`, with path ownership in `runtime/opfs/path-model.js`, generic snapshot persistence in `runtime/opfs/snapshot-store.js`, pure-text machine-file persistence in `runtime/opfs/machine-file-store.js`, OPFS-to-WASM parameter-file copying in `runtime/opfs/linuxcnc-parameter-bridge.js`, OPFS-to-WASM tool-table copying in `runtime/opfs/linuxcnc-tool-table-bridge.js`, and grouped INI/parameter/tool-table session loading in `runtime/opfs/linuxcnc-machine-session-bridge.js`, including INI-derived `[RS274NGC]PARAMETER_FILE` and `[EMCIO]TOOL_TABLE` OPFS filename selection through the LinuxCNC-backed INI SDK; explicit host session file-name options take precedence over INI-derived names, missing INI file-name values fall back to host default `linuxcnc.var` and `tool.tbl` paths, and OPFS path validation rejects traversal and nested segments before host storage access |
|
||||
| Native file IO | `inifile.cc`, `rs274ngc_pre.cc`, `tooldata_common.cc`, parameter file paths, tool table paths | Allowed in native probes; the interpreter WASM C ABI validates direct parameter-file restore/save by calling vendored `Interp::restore_parameters()` and `Interp::save_parameters()`, validates startup G92 parameter persistence by passing staged `[RS274NGC]PARAMETER_FILE` paths through vendored `ini_load()`/`Interp::init()`/`restore_parameters()`, including missing required numeric parameter defaulting and `DISABLE_G92_PERSISTENCE`, and validates tool-table load/save by calling vendored `tooldata_load()` and `tooldata_save()` against Emscripten filesystem paths; browser OPFS remains a host-side adapter under `runtime/opfs/`, with path ownership in `runtime/opfs/path-model.js`, generic snapshot persistence in `runtime/opfs/snapshot-store.js`, pure-text machine-file persistence in `runtime/opfs/machine-file-store.js`, OPFS-to-WASM parameter-file copying in `runtime/opfs/linuxcnc-parameter-bridge.js`, OPFS-to-WASM tool-table copying in `runtime/opfs/linuxcnc-tool-table-bridge.js`, and grouped INI/parameter/tool-table session loading in `runtime/opfs/linuxcnc-machine-session-bridge.js`, including INI-derived `[RS274NGC]PARAMETER_FILE` and `[EMCIO]TOOL_TABLE` OPFS filename selection through the LinuxCNC-backed INI SDK; explicit host session file-name options take precedence over INI-derived names, missing INI file-name values fall back to host default `linuxcnc.var` and `tool.tbl` paths, and OPFS path validation rejects traversal and nested segments before host storage access |
|
||||
| RTAPI | `rtapi_*.h`, TP, posemath, motion headers | Minimal standalone shim in `runtime/core/shims/rtapi.h` |
|
||||
| NML transport | `emc.hh`, motion/NML type headers | Transport is not ported; only the status/type edges needed by vendored compute code are exposed through standalone shims and probes |
|
||||
| HAL runtime | named parameter lookup, kinematics component lifecycle, and runtime status edges | Standalone HAL adapter under `runtime/core/linuxcnc_wrap/` |
|
||||
| Python/remap | `rs274ngc_pre.cc`, `interp_o_word.cc`, `interp_remap.cc`, remap hooks, selected LinuxCNC `configs/sim/axis/vismach/5axis/*/remap_subs/*.ngc` files | Python calls remain stubbed at the runtime boundary today; five-axis M428/M429/M430 source assets are vendored unchanged, the NGC remap descriptor path parses through vendored LinuxCNC code, and native validation now executes the switchkins NGC remap files plus selected demo programs through vendored LinuxCNC O-word and file execution paths while `_hal[...]` synchronization is supplied by the standalone HAL adapter |
|
||||
| Canonical machine actions | `interp_convert.cc`, `interp_execute.cc`, `interp_queue.cc` | Captured by standalone canonical event sink functions for regression fixtures |
|
||||
| User M-code process execution | `emctask.cc`, `interp_convert.cc`, `interp_queue.cc` | Search and registration are mirrored at the standalone machine-config boundary; native/WASM tests record deterministic `USER_M_COMMAND` events and do not spawn host processes |
|
||||
| Python/remap | `rs274ngc_pre.cc`, `interp_o_word.cc`, `interp_remap.cc`, remap hooks, selected LinuxCNC `configs/sim/axis/vismach/5axis/*/remap_subs/*.ngc` files, `tests/remap/duplicate-o-word/*`, `tests/remap/fail/args.0/*`, `tests/remap/fail/args.1/*`, `tests/remap/fail/args.2/*`, `tests/remap/fail/body-ngc/*`, `tests/remap/m30-interaction/*`, `tests/remap/nested-remaps-oword/*`, `tests/remap/posargs.0/*`, `tests/remap/sequencing/*`, and NGC-only `tests/remap/remap-io/test-ngc.ini` plus `io_*.ngc` | Python calls remain stubbed at the runtime boundary today; five-axis M428/M429/M430 source assets and the upstream NGC remap regression files are vendored unchanged. NGC remap descriptor paths parse through vendored LinuxCNC code, native/WASM/browser validation executes the NGC remap files through vendored LinuxCNC O-word and file execution paths, and the remap-IO NGC-only branch feeds the upstream MDI sequence into vendored LinuxCNC `Interp::execute()` while external M66 input values and `_hal[...]` synchronization remain standalone runtime adapter boundaries |
|
||||
| Canonical machine actions | `interp_convert.cc`, `interp_execute.cc`, `interp_queue.cc`, selected `tests/interp/*` regression assets | Captured by standalone canonical event sink functions for regression fixtures; vendored upstream interpreter test assets remain unchanged and are executed through LinuxCNC file execution |
|
||||
| Realtime scheduling | TP and motion headers | Not ported; native TP probes seed deterministic status/config state |
|
||||
| GUI | None used as implementation | Native LinuxCNC GUI remains reference-only |
|
||||
|
||||
|
||||
@@ -362,6 +362,12 @@ int main()
|
||||
"xyzac_switchkins_test_1.ngc",
|
||||
0.0,
|
||||
},
|
||||
{
|
||||
"xyzac_switchkins_test_2",
|
||||
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/"
|
||||
"xyzac_switchkins_test_2.ngc",
|
||||
0.0,
|
||||
},
|
||||
{
|
||||
"xyzac_switchkins_test_3",
|
||||
"configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/"
|
||||
@@ -404,10 +410,11 @@ int main()
|
||||
ok &= execute_demo_file(cases[0], demos[2]);
|
||||
ok &= execute_demo_file(cases[0], demos[3]);
|
||||
ok &= execute_demo_file(cases[0], demos[4]);
|
||||
ok &= execute_demo_file(cases[1], demos[5]);
|
||||
ok &= execute_demo_file(cases[0], demos[6]);
|
||||
ok &= execute_demo_file(cases[2], demos[7]);
|
||||
ok &= execute_demo_file(cases[3], demos[8]);
|
||||
ok &= execute_demo_file(cases[0], demos[5]);
|
||||
ok &= execute_demo_file(cases[1], demos[6]);
|
||||
ok &= execute_demo_file(cases[0], demos[7]);
|
||||
ok &= execute_demo_file(cases[2], demos[8]);
|
||||
ok &= execute_demo_file(cases[3], demos[9]);
|
||||
|
||||
print_bool("fiveaxis_linuxcnc_remap_execute_path", ok);
|
||||
return ok ? 0 : 1;
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
#define private public
|
||||
#include "emc/rs274ngc/rs274ngc_interp.hh"
|
||||
#undef private
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "canon_event_sink.hh"
|
||||
#include "emc/ini/inifile.hh"
|
||||
#include "emc/rs274ngc/interp_internal.hh"
|
||||
#include "linuxcnc_hal_adapter.hh"
|
||||
#include "linuxcnc_tool_adapter.hh"
|
||||
|
||||
#ifndef LINUXCNC_VENDOR_DIR
|
||||
#error "LINUXCNC_VENDOR_DIR must point at wasm-port/vendor/linuxcnc"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
std::filesystem::path vendor_path(const std::string &rel)
|
||||
{
|
||||
return std::filesystem::path(LINUXCNC_VENDOR_DIR) / rel;
|
||||
}
|
||||
|
||||
void print_bool(const std::string &name, bool value)
|
||||
{
|
||||
std::cout << name << "=" << (value ? 1 : 0) << "\n";
|
||||
}
|
||||
|
||||
void print_error_text(Interp &interp, const std::string &name, int rc)
|
||||
{
|
||||
char error_buf[LINELEN] = {0};
|
||||
interp.error_text(rc, error_buf, sizeof(error_buf));
|
||||
std::cout << name << error_buf << "\n";
|
||||
}
|
||||
|
||||
void initialize_interp(Interp &interp)
|
||||
{
|
||||
standalone::reset_hal_adapter();
|
||||
standalone::reset_tool_adapter();
|
||||
standalone::reset_canon_events();
|
||||
|
||||
interp._setup.length_units = CANON_UNITS_MM;
|
||||
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
interp._setup.feed_mode = FEED_MODE::UNITS_PER_MINUTE;
|
||||
interp._setup.plane = CANON_PLANE::XY;
|
||||
interp._setup.motion_mode = G_0;
|
||||
interp._setup.percent_flag = false;
|
||||
interp._setup.sequence_number = 0;
|
||||
interp._setup.parameter_occurrence = 0;
|
||||
interp._setup.num_spindles = 1;
|
||||
interp._setup.feature_set = FEATURE_INI_VARS | FEATURE_HAL_PIN_VARS;
|
||||
interp._setup.parameter_g73_peck_clearance = 1.0;
|
||||
interp._setup.parameter_g83_peck_clearance = 1.0;
|
||||
interp._setup.parameters[5599] = 1.0;
|
||||
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
|
||||
interp.init_named_parameters();
|
||||
}
|
||||
|
||||
std::filesystem::path subroutine_path(const std::filesystem::path &machine_dir,
|
||||
const std::string &entry)
|
||||
{
|
||||
const std::filesystem::path path(entry);
|
||||
if (path.is_absolute()) {
|
||||
return path;
|
||||
}
|
||||
return machine_dir / path;
|
||||
}
|
||||
|
||||
bool configure_remaps(Interp &interp,
|
||||
const std::filesystem::path &ini_path,
|
||||
const char *label,
|
||||
int expected_remaps)
|
||||
{
|
||||
linuxcnc::IniFile ini(ini_path.string());
|
||||
const bool opened = static_cast<bool>(ini);
|
||||
print_bool(std::string(label) + "_ini_open", opened);
|
||||
if (!opened) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto machine_dir = ini_path.parent_path();
|
||||
std::snprintf(interp._setup.program_prefix, sizeof(interp._setup.program_prefix), "%s",
|
||||
machine_dir.c_str());
|
||||
|
||||
int subroutine_count = 0;
|
||||
if (auto subroutine_path_text = ini.findString("SUBROUTINE_PATH", "RS274NGC")) {
|
||||
std::stringstream paths(*subroutine_path_text);
|
||||
std::string entry;
|
||||
while (std::getline(paths, entry, ':') && subroutine_count < MAX_SUB_DIRS) {
|
||||
const auto resolved = subroutine_path(machine_dir, entry);
|
||||
interp._setup.subroutines[subroutine_count++] = strstore(resolved.c_str());
|
||||
}
|
||||
}
|
||||
std::cout << label << "_subroutine_path_count=" << subroutine_count << "\n";
|
||||
|
||||
if (ini.findBoolV("OWORD_NARGS", "RS274NGC", false)) {
|
||||
interp._setup.feature_set |= FEATURE_OWORD_N_ARGS;
|
||||
}
|
||||
|
||||
bool ok = subroutine_count > 0;
|
||||
int remap_count = 0;
|
||||
while (auto remap = ini.findString(++remap_count, "REMAP", "RS274NGC")) {
|
||||
const int line_number = ini.lineOf(remap_count, "REMAP", "RS274NGC").second;
|
||||
const int rc = interp.parse_remap(remap->c_str(), line_number);
|
||||
std::cout << label << "_parse_remap_" << remap_count << "=" << rc << "\n";
|
||||
ok &= rc == INTERP_OK;
|
||||
}
|
||||
remap_count -= 1;
|
||||
std::cout << label << "_parsed_remap_count=" << remap_count << "\n";
|
||||
ok &= remap_count == expected_remaps;
|
||||
|
||||
print_bool(std::string(label) + "_remaps_ready", ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool execute_file(Interp &interp,
|
||||
const std::filesystem::path &program_path,
|
||||
const char *label,
|
||||
bool continue_on_error,
|
||||
bool expect_error)
|
||||
{
|
||||
bool ok = true;
|
||||
const int open_rc = interp.open(program_path.c_str());
|
||||
std::cout << label << "_file_open=" << open_rc << "\n";
|
||||
if (open_rc != INTERP_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr int max_file_steps = 20000;
|
||||
int read_count = 0;
|
||||
int execute_count = 0;
|
||||
int finish_count = 0;
|
||||
int final_rc = INTERP_OK;
|
||||
bool reached_exit = false;
|
||||
bool reached_endfile = false;
|
||||
bool saw_error = false;
|
||||
|
||||
while (read_count < max_file_steps && execute_count < max_file_steps) {
|
||||
const int read_rc = interp.read();
|
||||
if (read_rc == INTERP_ENDFILE) {
|
||||
final_rc = read_rc;
|
||||
reached_endfile = true;
|
||||
break;
|
||||
}
|
||||
|
||||
++read_count;
|
||||
if ((read_rc != INTERP_OK) && (read_rc != INTERP_EXECUTE_FINISH)) {
|
||||
std::cout << label << "_file_read_" << read_count << "=" << read_rc << "\n";
|
||||
print_error_text(interp, std::string(label) + "_file_error_text=", read_rc);
|
||||
final_rc = read_rc;
|
||||
saw_error = true;
|
||||
if (!expect_error) {
|
||||
ok = false;
|
||||
}
|
||||
if (!continue_on_error) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const int execute_rc = interp.execute();
|
||||
++execute_count;
|
||||
final_rc = execute_rc;
|
||||
if (execute_rc == INTERP_EXECUTE_FINISH) {
|
||||
++finish_count;
|
||||
}
|
||||
if ((execute_rc != INTERP_OK) &&
|
||||
(execute_rc != INTERP_EXECUTE_FINISH) &&
|
||||
(execute_rc != INTERP_EXIT)) {
|
||||
std::cout << label << "_file_execute_" << execute_count << "=" << execute_rc << "\n";
|
||||
print_error_text(interp, std::string(label) + "_file_error_text=", execute_rc);
|
||||
saw_error = true;
|
||||
if (!expect_error) {
|
||||
ok = false;
|
||||
}
|
||||
if (!continue_on_error) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (execute_rc == INTERP_EXIT) {
|
||||
reached_exit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << label << "_file_read_count=" << read_count << "\n";
|
||||
std::cout << label << "_file_execute_count=" << execute_count << "\n";
|
||||
std::cout << label << "_file_finish_count=" << finish_count << "\n";
|
||||
std::cout << label << "_file_final_rc=" << final_rc << "\n";
|
||||
print_bool(std::string(label) + "_file_reached_exit", reached_exit);
|
||||
print_bool(std::string(label) + "_file_reached_endfile", reached_endfile);
|
||||
print_bool(std::string(label) + "_file_saw_error", saw_error);
|
||||
std::cout << label << "_canon_event_count=" << standalone::canon_events().size() << "\n";
|
||||
for (const auto &event : standalone::canon_events()) {
|
||||
std::cout << label << "_canon_event=" << event << "\n";
|
||||
}
|
||||
|
||||
ok &= read_count > 0;
|
||||
ok &= execute_count > 0;
|
||||
ok &= read_count < max_file_steps;
|
||||
ok &= execute_count < max_file_steps;
|
||||
ok &= reached_exit || reached_endfile;
|
||||
ok &= expect_error ? saw_error : !saw_error;
|
||||
print_bool(std::string(label) + "_linuxcnc_remap_file_execute", ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
std::string command_label(const char *command)
|
||||
{
|
||||
std::string value = command ? command : "";
|
||||
while (!value.empty() && (value.back() == '\n' || value.back() == '\r')) {
|
||||
value.pop_back();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool execute_mdi(Interp &interp, const char *label, const char *command)
|
||||
{
|
||||
int rc = interp.execute(command);
|
||||
std::cout << label << "_mdi_" << command_label(command) << "_execute_0=" << rc << "\n";
|
||||
|
||||
int finish_count = 0;
|
||||
while (rc == INTERP_EXECUTE_FINISH && finish_count < 16) {
|
||||
++finish_count;
|
||||
rc = interp.execute();
|
||||
std::cout << label << "_mdi_" << command_label(command)
|
||||
<< "_execute_" << finish_count << "=" << rc << "\n";
|
||||
}
|
||||
|
||||
std::cout << label << "_mdi_" << command_label(command)
|
||||
<< "_execute_finish_count=" << finish_count << "\n";
|
||||
return rc == INTERP_OK;
|
||||
}
|
||||
|
||||
bool execute_remap_io_mdi_case()
|
||||
{
|
||||
const auto test_dir = vendor_path("tests/remap/remap-io");
|
||||
const auto ini_path = test_dir / "test-ngc.ini";
|
||||
setenv("INI_FILE_NAME", ini_path.c_str(), 1);
|
||||
|
||||
Interp interp;
|
||||
initialize_interp(interp);
|
||||
|
||||
bool ok = configure_remaps(interp, ini_path, "remap_io_ngc", 7);
|
||||
|
||||
const struct MdiStep {
|
||||
const char *command;
|
||||
bool digital_input;
|
||||
int digital_value;
|
||||
bool analog_input;
|
||||
double analog_value;
|
||||
} steps[] = {
|
||||
{"M62 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X1.0\n", false, 0, false, 0.0},
|
||||
{"M63 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X2.0\n", false, 0, false, 0.0},
|
||||
{"M62 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X3.0\n", false, 0, false, 0.0},
|
||||
{"M63 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X4.0\n", false, 0, false, 0.0},
|
||||
{"M64 P1\n", false, 0, false, 0.0},
|
||||
{"M65 P1\n", false, 0, false, 0.0},
|
||||
{"M64 P1\n", false, 0, false, 0.0},
|
||||
{"M65 P1\n", false, 0, false, 0.0},
|
||||
{"M66 P1\n", true, 1, false, 0.0},
|
||||
{"M66 P1\n", true, 0, false, 0.0},
|
||||
{"M66 E1 L0\n", false, 0, true, 42.13},
|
||||
{"M66 E1 L0\n", false, 0, true, -13.42},
|
||||
{"M67 E1 Q42.13\n", false, 0, false, 0.0},
|
||||
{"G0 X-1.0\n", false, 0, false, 0.0},
|
||||
{"M67 E1 Q-13.42\n", false, 0, false, 0.0},
|
||||
{"G0 X-2.0\n", false, 0, false, 0.0},
|
||||
{"M67 E1 Q0.0\n", false, 0, false, 0.0},
|
||||
{"G0 X-3.0\n", false, 0, false, 0.0},
|
||||
{"M68 E1 Q42.13\n", false, 0, false, 0.0},
|
||||
{"M68 E1 Q-13.42\n", false, 0, false, 0.0},
|
||||
{"M68 E1 Q0.0\n", false, 0, false, 0.0},
|
||||
};
|
||||
|
||||
for (const auto &step : steps) {
|
||||
if (step.digital_input) {
|
||||
standalone::set_external_digital_input(0, step.digital_value);
|
||||
}
|
||||
if (step.analog_input) {
|
||||
standalone::set_external_analog_input(0, step.analog_value);
|
||||
}
|
||||
ok &= execute_mdi(interp, "remap_io_ngc", step.command);
|
||||
std::cout << "remap_io_ngc_parameter_5399=" << interp._setup.parameters[5399] << "\n";
|
||||
std::cout << "remap_io_ngc_parameter_100=" << interp._setup.parameters[100] << "\n";
|
||||
}
|
||||
|
||||
std::cout << "remap_io_ngc_canon_event_count=" << standalone::canon_events().size() << "\n";
|
||||
for (const auto &event : standalone::canon_events()) {
|
||||
std::cout << "remap_io_ngc_canon_event=" << event << "\n";
|
||||
}
|
||||
|
||||
ok &= std::fabs(interp._setup.parameters[5399] - -13.42) < 1e-9;
|
||||
ok &= std::fabs(interp._setup.parameters[100] - -13.42) < 1e-9;
|
||||
print_bool("remap_io_ngc_linuxcnc_mdi_sequence", ok);
|
||||
unsetenv("INI_FILE_NAME");
|
||||
return ok;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
struct RegressionCase {
|
||||
const char *label;
|
||||
const char *rel;
|
||||
int expected_remaps;
|
||||
bool continue_on_error;
|
||||
bool expect_error;
|
||||
};
|
||||
|
||||
const RegressionCase cases[] = {
|
||||
{"duplicate_oword", "tests/remap/duplicate-o-word", 2, false, false},
|
||||
{"fail_args_0", "tests/remap/fail/args.0", 1, false, false},
|
||||
{"fail_args_1", "tests/remap/fail/args.1", 1, true, true},
|
||||
{"fail_args_2", "tests/remap/fail/args.2", 1, true, true},
|
||||
{"fail_body_ngc", "tests/remap/fail/body-ngc", 1, true, true},
|
||||
{"m30_interaction", "tests/remap/m30-interaction", 1, false, false},
|
||||
{"nested_remaps_oword", "tests/remap/nested-remaps-oword", 4, false, false},
|
||||
{"posargs_0", "tests/remap/posargs.0", 1, false, false},
|
||||
{"sequencing", "tests/remap/sequencing", 7, false, false},
|
||||
};
|
||||
|
||||
bool all_ok = true;
|
||||
for (const auto &test : cases) {
|
||||
const auto test_dir = vendor_path(test.rel);
|
||||
const auto ini_path = test_dir / "test.ini";
|
||||
const auto program_path = test_dir / "test.ngc";
|
||||
|
||||
setenv("INI_FILE_NAME", ini_path.c_str(), 1);
|
||||
|
||||
Interp interp;
|
||||
initialize_interp(interp);
|
||||
|
||||
bool ok = configure_remaps(interp, ini_path, test.label, test.expected_remaps);
|
||||
ok &= execute_file(interp,
|
||||
program_path,
|
||||
test.label,
|
||||
test.continue_on_error,
|
||||
test.expect_error);
|
||||
print_bool(std::string(test.label) + "_linuxcnc_regression_path", ok);
|
||||
all_ok &= ok;
|
||||
|
||||
unsetenv("INI_FILE_NAME");
|
||||
}
|
||||
|
||||
all_ok &= execute_remap_io_mdi_case();
|
||||
|
||||
print_bool("linuxcnc_upstream_remap_regression_path", all_ok);
|
||||
return all_ok ? 0 : 1;
|
||||
}
|
||||
@@ -59,6 +59,18 @@ std::unordered_map<std::string, std::vector<std::string>> &pin_aliases()
|
||||
return values;
|
||||
}
|
||||
|
||||
std::unordered_map<int, int> &digital_inputs()
|
||||
{
|
||||
static std::unordered_map<int, int> values;
|
||||
return values;
|
||||
}
|
||||
|
||||
std::unordered_map<int, double> &analog_inputs()
|
||||
{
|
||||
static std::unordered_map<int, double> values;
|
||||
return values;
|
||||
}
|
||||
|
||||
int lookup_hal_value(const char *name, std::unordered_map<std::string, HalEntry> &values,
|
||||
hal_type_t *type, hal_data_u **ptr, bool *connected)
|
||||
{
|
||||
@@ -89,6 +101,8 @@ void reset_hal_adapter()
|
||||
signals().clear();
|
||||
params().clear();
|
||||
pin_aliases().clear();
|
||||
digital_inputs().clear();
|
||||
analog_inputs().clear();
|
||||
}
|
||||
|
||||
void set_hal_value(HalValueKind kind, const char *name, hal_type_t type, hal_data_u value,
|
||||
@@ -130,6 +144,28 @@ void sync_motion_analog_output(int index, double value)
|
||||
}
|
||||
}
|
||||
|
||||
void set_external_digital_input(int index, int value)
|
||||
{
|
||||
digital_inputs()[index] = value ? 1 : 0;
|
||||
}
|
||||
|
||||
void set_external_analog_input(int index, double value)
|
||||
{
|
||||
analog_inputs()[index] = value;
|
||||
}
|
||||
|
||||
int external_digital_input(int index, int def)
|
||||
{
|
||||
auto it = digital_inputs().find(index);
|
||||
return it == digital_inputs().end() ? def : it->second;
|
||||
}
|
||||
|
||||
double external_analog_input(int index, double def)
|
||||
{
|
||||
auto it = analog_inputs().find(index);
|
||||
return it == analog_inputs().end() ? def : it->second;
|
||||
}
|
||||
|
||||
} // namespace standalone
|
||||
|
||||
int hal_init(const char *)
|
||||
|
||||
@@ -15,5 +15,9 @@ void set_hal_value(HalValueKind kind, const char *name, hal_type_t type, hal_dat
|
||||
bool connected = true);
|
||||
void link_hal_pin_alias(const char *source, const char *alias);
|
||||
void sync_motion_analog_output(int index, double value);
|
||||
void set_external_digital_input(int index, int value);
|
||||
void set_external_analog_input(int index, double value);
|
||||
int external_digital_input(int index, int def);
|
||||
double external_analog_input(int index, double def);
|
||||
|
||||
} // namespace standalone
|
||||
|
||||
@@ -3,13 +3,20 @@
|
||||
#undef private
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "canon_event_sink.hh"
|
||||
#include "emc/ini/inifile.hh"
|
||||
#include "linuxcnc_hal_adapter.hh"
|
||||
#include "linuxcnc_machine_config.hh"
|
||||
#include "linuxcnc_runtime_state.hh"
|
||||
#include "linuxcnc_tool_adapter.hh"
|
||||
|
||||
namespace {
|
||||
@@ -70,9 +77,10 @@ void initialize_minimal_interp(Interp &interp)
|
||||
{
|
||||
seed_hal_values();
|
||||
seed_tool_values();
|
||||
standalone::reset_external_axis_mask();
|
||||
interp._setup.length_units = CANON_UNITS_MM;
|
||||
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
interp._setup.ijk_distance_mode = DISTANCE_MODE::INCREMENTAL;
|
||||
interp._setup.feed_mode = FEED_MODE::UNITS_PER_MINUTE;
|
||||
interp._setup.plane = CANON_PLANE::XY;
|
||||
interp._setup.motion_mode = G_0;
|
||||
@@ -85,7 +93,85 @@ void initialize_minimal_interp(Interp &interp)
|
||||
interp._setup.parameter_g83_peck_clearance = 1.0;
|
||||
interp._setup.parameters[5599] = 1.0;
|
||||
interp.load_tool_table();
|
||||
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
|
||||
standalone::apply_axis_mask_to_interp(interp, standalone::axis_mask_from_coordinates("XYZABCUVW"));
|
||||
standalone::reset_user_m_code_registry();
|
||||
}
|
||||
|
||||
void apply_ini_search_paths(Interp &interp, const char *ini_path)
|
||||
{
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto machine_dir = std::filesystem::path(ini_path).parent_path();
|
||||
standalone::apply_machine_axis_config(interp, ini);
|
||||
standalone::load_tool_table_from_ini(interp, ini, machine_dir.c_str());
|
||||
standalone::register_user_m_codes_from_ini(ini, machine_dir.c_str());
|
||||
|
||||
std::snprintf(interp._setup.program_prefix, sizeof(interp._setup.program_prefix), "%s",
|
||||
machine_dir.c_str());
|
||||
|
||||
int subroutine_count = 0;
|
||||
if (auto subroutine_path_text = ini.findString("SUBROUTINE_PATH", "RS274NGC")) {
|
||||
std::stringstream paths(*subroutine_path_text);
|
||||
std::string entry;
|
||||
while (std::getline(paths, entry, ':') && subroutine_count < MAX_SUB_DIRS) {
|
||||
std::filesystem::path resolved(entry);
|
||||
if (resolved.is_relative()) {
|
||||
resolved = machine_dir / resolved;
|
||||
}
|
||||
interp._setup.subroutines[subroutine_count++] = strstore(resolved.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (ini.findBoolV("OWORD_NARGS", "RS274NGC", false)) {
|
||||
interp._setup.feature_set |= FEATURE_OWORD_N_ARGS;
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path resolve_machine_relative_path(const std::filesystem::path &machine_dir,
|
||||
const std::string &entry)
|
||||
{
|
||||
std::filesystem::path path(entry);
|
||||
if (path.is_relative()) {
|
||||
path = machine_dir / path;
|
||||
}
|
||||
return path.lexically_normal();
|
||||
}
|
||||
|
||||
int initialize_parameter_file_from_ini(Interp &interp, const char *ini_path)
|
||||
{
|
||||
standalone::reset_parameter_file_name();
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini || !ini.findString("PARAMETER_FILE", "RS274NGC")) {
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
const int ini_rc = interp.ini_load(ini_path);
|
||||
if (ini_rc != INTERP_OK) {
|
||||
return ini_rc;
|
||||
}
|
||||
|
||||
char parameter_file_name[LINELEN] = {0};
|
||||
standalone::get_parameter_file_name(parameter_file_name, sizeof(parameter_file_name));
|
||||
const auto resolved = resolve_machine_relative_path(
|
||||
std::filesystem::path(ini_path).parent_path(), parameter_file_name);
|
||||
if (access(resolved.c_str(), F_OK) != 0) {
|
||||
standalone::reset_parameter_file_name();
|
||||
return INTERP_OK;
|
||||
}
|
||||
standalone::set_parameter_file_name(resolved.c_str());
|
||||
|
||||
return interp.init();
|
||||
}
|
||||
|
||||
std::vector<std::string> load_program(int argc, char **argv)
|
||||
@@ -113,15 +199,84 @@ std::vector<std::string> load_program(int argc, char **argv)
|
||||
return lines;
|
||||
}
|
||||
|
||||
bool is_uri_unreserved(char value)
|
||||
{
|
||||
return (value >= 'A' && value <= 'Z') ||
|
||||
(value >= 'a' && value <= 'z') ||
|
||||
(value >= '0' && value <= '9') ||
|
||||
value == '-' || value == '_' || value == '.' || value == '~';
|
||||
}
|
||||
|
||||
void append_percent_encoded_text(std::ostream &output, const char *text)
|
||||
{
|
||||
constexpr char hex[] = "0123456789ABCDEF";
|
||||
for (const char *cursor = text ? text : ""; *cursor; ++cursor) {
|
||||
char value = *cursor;
|
||||
switch (*cursor) {
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\t':
|
||||
value = ' ';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (is_uri_unreserved(value)) {
|
||||
output << value;
|
||||
} else {
|
||||
const auto byte = static_cast<unsigned char>(value);
|
||||
output << '%' << hex[(byte >> 4) & 0x0f] << hex[byte & 0x0f];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void append_run_step(std::ostream &output,
|
||||
const Interp &interp,
|
||||
const char *phase,
|
||||
int step,
|
||||
int rc)
|
||||
{
|
||||
output << "run_step phase=" << phase
|
||||
<< " step=" << step
|
||||
<< " rc=" << rc
|
||||
<< " line=" << interp._setup.sequence_number
|
||||
<< " x=" << interp._setup.current_x
|
||||
<< " y=" << interp._setup.current_y
|
||||
<< " z=" << interp._setup.current_z
|
||||
<< " a=" << interp._setup.AA_current
|
||||
<< " b=" << interp._setup.BB_current
|
||||
<< " c=" << interp._setup.CC_current
|
||||
<< " u=" << interp._setup.u_current
|
||||
<< " v=" << interp._setup.v_current
|
||||
<< " w=" << interp._setup.w_current
|
||||
<< " statement_uri=";
|
||||
append_percent_encoded_text(output, interp._setup.linetext);
|
||||
output << "\n";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
bool continue_on_error = false;
|
||||
int arg_offset = 1;
|
||||
if (argc > 1 && std::strcmp(argv[1], "--continue-on-error") == 0) {
|
||||
continue_on_error = true;
|
||||
arg_offset = 2;
|
||||
}
|
||||
|
||||
Interp interp;
|
||||
standalone::reset_canon_events();
|
||||
const char *ini_path = argc > arg_offset + 1 ? argv[arg_offset + 1] : nullptr;
|
||||
if (ini_path && ini_path[0] != '\0') {
|
||||
setenv("INI_FILE_NAME", ini_path, 1);
|
||||
} else {
|
||||
unsetenv("INI_FILE_NAME");
|
||||
}
|
||||
initialize_minimal_interp(interp);
|
||||
apply_ini_search_paths(interp, ini_path);
|
||||
|
||||
const std::vector<std::string> program = load_program(argc, argv);
|
||||
const std::vector<std::string> program = load_program(argc - arg_offset + 1, argv + arg_offset - 1);
|
||||
if (program.empty()) {
|
||||
std::cerr << "empty fixture\n";
|
||||
return 2;
|
||||
@@ -138,6 +293,7 @@ int main(int argc, char **argv)
|
||||
const int read_rc = interp.read(program.front().c_str());
|
||||
|
||||
initialize_minimal_interp(interp);
|
||||
apply_ini_search_paths(interp, ini_path);
|
||||
for (std::size_t idx = 0; idx < program.size(); ++idx) {
|
||||
interp._setup.sequence_number = static_cast<int>(idx + 1);
|
||||
const int execute_rc = interp.execute(program[idx].c_str());
|
||||
@@ -152,10 +308,23 @@ int main(int argc, char **argv)
|
||||
int file_open_rc = INTERP_FILE_NOT_OPEN;
|
||||
int file_read_count = 0;
|
||||
int file_execute_count = 0;
|
||||
if (argc > 1) {
|
||||
bool file_saw_error = false;
|
||||
if (argc > arg_offset) {
|
||||
Interp file_interp;
|
||||
standalone::reset_canon_events();
|
||||
initialize_minimal_interp(file_interp);
|
||||
file_open_rc = file_interp.open(argv[1]);
|
||||
apply_ini_search_paths(file_interp, ini_path);
|
||||
const int init_rc = initialize_parameter_file_from_ini(file_interp, ini_path);
|
||||
if (init_rc != INTERP_OK) {
|
||||
std::cout << "file_init=" << init_rc << "\n";
|
||||
if (init_rc > INTERP_MIN_ERROR) {
|
||||
char error_buf[LINELEN] = {0};
|
||||
file_interp.error_text(init_rc, error_buf, sizeof(error_buf));
|
||||
std::cout << "file_error_text=" << error_buf << "\n";
|
||||
}
|
||||
}
|
||||
apply_ini_search_paths(file_interp, ini_path);
|
||||
file_open_rc = init_rc == INTERP_OK ? file_interp.open(argv[arg_offset]) : init_rc;
|
||||
if (file_open_rc == INTERP_OK) {
|
||||
while (true) {
|
||||
const int file_read_rc = file_interp.read();
|
||||
@@ -165,13 +334,24 @@ int main(int argc, char **argv)
|
||||
}
|
||||
++file_read_count;
|
||||
std::cout << "file_read_" << file_read_count << "=" << file_read_rc << "\n";
|
||||
append_run_step(std::cout, file_interp, "read", file_read_count, file_read_rc);
|
||||
if ((file_read_rc != INTERP_OK) && (file_read_rc != INTERP_EXECUTE_FINISH)) {
|
||||
break;
|
||||
if (file_read_rc > INTERP_MIN_ERROR) {
|
||||
char error_buf[LINELEN] = {0};
|
||||
file_interp.error_text(file_read_rc, error_buf, sizeof(error_buf));
|
||||
std::cout << "file_error_text=" << error_buf << "\n";
|
||||
}
|
||||
file_saw_error = true;
|
||||
if (!continue_on_error) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const int file_execute_rc = file_interp.execute();
|
||||
++file_execute_count;
|
||||
std::cout << "file_execute_" << file_execute_count << "=" << file_execute_rc << "\n";
|
||||
append_run_step(std::cout, file_interp, "execute", file_execute_count, file_execute_rc);
|
||||
if (file_execute_rc > INTERP_MIN_ERROR) {
|
||||
char error_buf[LINELEN] = {0};
|
||||
file_interp.error_text(file_execute_rc, error_buf, sizeof(error_buf));
|
||||
@@ -180,6 +360,7 @@ int main(int argc, char **argv)
|
||||
if ((file_execute_rc != INTERP_OK) &&
|
||||
(file_execute_rc != INTERP_EXECUTE_FINISH) &&
|
||||
(file_execute_rc != INTERP_EXIT)) {
|
||||
file_saw_error = true;
|
||||
break;
|
||||
}
|
||||
if (file_execute_rc == INTERP_EXIT) {
|
||||
@@ -200,6 +381,7 @@ int main(int argc, char **argv)
|
||||
std::cout << "file_open=" << file_open_rc << "\n";
|
||||
std::cout << "file_read_count=" << file_read_count << "\n";
|
||||
std::cout << "file_execute_count=" << file_execute_count << "\n";
|
||||
std::cout << "file_saw_error=" << (file_saw_error ? 1 : 0) << "\n";
|
||||
std::cout << "raw_line=" << raw_line << "\n";
|
||||
std::cout << "cooked_line=" << cooked_line << "\n";
|
||||
std::cout << "line_length=" << length << "\n";
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "emc/rs274ngc/rs274ngc_return.hh"
|
||||
#include "canon_event_sink.hh"
|
||||
#include "linuxcnc_hal_adapter.hh"
|
||||
#include "linuxcnc_runtime_state.hh"
|
||||
#include "linuxcnc_tool_adapter.hh"
|
||||
#include "nml_intf/emc.hh"
|
||||
|
||||
@@ -20,6 +21,8 @@ namespace standalone {
|
||||
|
||||
static std::vector<std::string> g_canon_events;
|
||||
static double g_external_feed_rate = 0.0;
|
||||
static int g_external_axis_mask = Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z;
|
||||
static std::string g_parameter_file_name;
|
||||
|
||||
void reset_canon_events()
|
||||
{
|
||||
@@ -36,6 +39,42 @@ const std::vector<std::string> &canon_events()
|
||||
return g_canon_events;
|
||||
}
|
||||
|
||||
void reset_external_axis_mask()
|
||||
{
|
||||
g_external_axis_mask = Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z;
|
||||
}
|
||||
|
||||
void set_external_axis_mask(int axis_mask)
|
||||
{
|
||||
g_external_axis_mask = axis_mask == 0
|
||||
? (Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z)
|
||||
: axis_mask;
|
||||
}
|
||||
|
||||
int external_axis_mask()
|
||||
{
|
||||
return g_external_axis_mask;
|
||||
}
|
||||
|
||||
void reset_parameter_file_name()
|
||||
{
|
||||
g_parameter_file_name.clear();
|
||||
}
|
||||
|
||||
void set_parameter_file_name(const char *filename)
|
||||
{
|
||||
g_parameter_file_name = filename ? filename : "";
|
||||
}
|
||||
|
||||
void get_parameter_file_name(char *filename, int max_size)
|
||||
{
|
||||
if (!filename || max_size <= 0) {
|
||||
return;
|
||||
}
|
||||
std::snprintf(filename, static_cast<std::size_t>(max_size), "%s",
|
||||
g_parameter_file_name.c_str());
|
||||
}
|
||||
|
||||
} // namespace standalone
|
||||
|
||||
bool GET_BLOCK_DELETE(void) { return false; }
|
||||
@@ -470,11 +509,12 @@ double GET_EXTERNAL_ORIGIN_Y() { return 0.0; }
|
||||
double GET_EXTERNAL_ORIGIN_Z() { return 0.0; }
|
||||
void GET_EXTERNAL_PARAMETER_FILE_NAME(char *filename, int max_size)
|
||||
{
|
||||
if (max_size > 0) {
|
||||
filename[0] = '\0';
|
||||
}
|
||||
standalone::get_parameter_file_name(filename, max_size);
|
||||
}
|
||||
void SET_PARAMETER_FILE_NAME(const char *filename)
|
||||
{
|
||||
standalone::set_parameter_file_name(filename);
|
||||
}
|
||||
void SET_PARAMETER_FILE_NAME(const char *) {}
|
||||
CANON_PLANE GET_EXTERNAL_PLANE() { return CANON_PLANE::XY; }
|
||||
double GET_EXTERNAL_POSITION_A() { return 0.0; }
|
||||
double GET_EXTERNAL_POSITION_B() { return 0.0; }
|
||||
@@ -523,9 +563,15 @@ int GET_EXTERNAL_FEED_OVERRIDE_ENABLE() { return 1; }
|
||||
int GET_EXTERNAL_SPINDLE_OVERRIDE_ENABLE(int) { return 1; }
|
||||
int GET_EXTERNAL_ADAPTIVE_FEED_ENABLE() { return 1; }
|
||||
int GET_EXTERNAL_FEED_HOLD_ENABLE() { return 1; }
|
||||
int GET_EXTERNAL_DIGITAL_INPUT(int, int def) { return def; }
|
||||
double GET_EXTERNAL_ANALOG_INPUT(int, double def) { return def; }
|
||||
int GET_EXTERNAL_AXIS_MASK() { return 7; }
|
||||
int GET_EXTERNAL_DIGITAL_INPUT(int index, int def)
|
||||
{
|
||||
return standalone::external_digital_input(index, def);
|
||||
}
|
||||
double GET_EXTERNAL_ANALOG_INPUT(int index, double def)
|
||||
{
|
||||
return standalone::external_analog_input(index, def);
|
||||
}
|
||||
int GET_EXTERNAL_AXIS_MASK() { return standalone::external_axis_mask(); }
|
||||
int GET_EXTERNAL_OFFSET_APPLIED() { return 0; }
|
||||
EmcPose GET_EXTERNAL_OFFSETS() { return {{0, 0, 0}, 0, 0, 0, 0, 0, 0}; }
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "emc/rs274ngc/rs274ngc_interp.hh"
|
||||
#undef private
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
@@ -9,10 +10,13 @@
|
||||
#include <vector>
|
||||
|
||||
#include <emscripten/emscripten.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "canon_event_sink.hh"
|
||||
#include "emc/ini/inifile.hh"
|
||||
#include "linuxcnc_hal_adapter.hh"
|
||||
#include "linuxcnc_machine_config.hh"
|
||||
#include "linuxcnc_runtime_state.hh"
|
||||
#include "linuxcnc_tool_adapter.hh"
|
||||
#include "nml_intf/emc.hh"
|
||||
#include "tooldata.hh"
|
||||
@@ -105,28 +109,7 @@ void initialize_minimal_interp(Interp &interp)
|
||||
{
|
||||
seed_hal_values();
|
||||
seed_tool_values();
|
||||
interp._setup.length_units = CANON_UNITS_MM;
|
||||
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
interp._setup.ijk_distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
interp._setup.feed_mode = FEED_MODE::UNITS_PER_MINUTE;
|
||||
interp._setup.plane = CANON_PLANE::XY;
|
||||
interp._setup.motion_mode = G_0;
|
||||
interp._setup.percent_flag = false;
|
||||
interp._setup.sequence_number = 0;
|
||||
interp._setup.parameter_occurrence = 0;
|
||||
interp._setup.num_spindles = 1;
|
||||
interp._setup.feature_set = FEATURE_INI_VARS | FEATURE_HAL_PIN_VARS;
|
||||
interp._setup.parameter_g73_peck_clearance = 1.0;
|
||||
interp._setup.parameter_g83_peck_clearance = 1.0;
|
||||
interp._setup.parameters[5599] = 1.0;
|
||||
interp.load_tool_table();
|
||||
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
|
||||
}
|
||||
|
||||
void initialize_fiveaxis_remap_interp(Interp &interp)
|
||||
{
|
||||
seed_switchkins_hal();
|
||||
seed_tool_values();
|
||||
standalone::reset_external_axis_mask();
|
||||
interp._setup.length_units = CANON_UNITS_MM;
|
||||
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
interp._setup.ijk_distance_mode = DISTANCE_MODE::INCREMENTAL;
|
||||
@@ -142,7 +125,32 @@ void initialize_fiveaxis_remap_interp(Interp &interp)
|
||||
interp._setup.parameter_g83_peck_clearance = 1.0;
|
||||
interp._setup.parameters[5599] = 1.0;
|
||||
interp.load_tool_table();
|
||||
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
|
||||
standalone::apply_axis_mask_to_interp(interp, standalone::axis_mask_from_coordinates("XYZABCUVW"));
|
||||
standalone::reset_user_m_code_registry();
|
||||
}
|
||||
|
||||
void initialize_fiveaxis_remap_interp(Interp &interp)
|
||||
{
|
||||
seed_switchkins_hal();
|
||||
seed_tool_values();
|
||||
standalone::reset_external_axis_mask();
|
||||
interp._setup.length_units = CANON_UNITS_MM;
|
||||
interp._setup.distance_mode = DISTANCE_MODE::ABSOLUTE;
|
||||
interp._setup.ijk_distance_mode = DISTANCE_MODE::INCREMENTAL;
|
||||
interp._setup.feed_mode = FEED_MODE::UNITS_PER_MINUTE;
|
||||
interp._setup.plane = CANON_PLANE::XY;
|
||||
interp._setup.motion_mode = G_0;
|
||||
interp._setup.percent_flag = false;
|
||||
interp._setup.sequence_number = 0;
|
||||
interp._setup.parameter_occurrence = 0;
|
||||
interp._setup.num_spindles = 1;
|
||||
interp._setup.feature_set = FEATURE_INI_VARS | FEATURE_HAL_PIN_VARS;
|
||||
interp._setup.parameter_g73_peck_clearance = 1.0;
|
||||
interp._setup.parameter_g83_peck_clearance = 1.0;
|
||||
interp._setup.parameters[5599] = 1.0;
|
||||
interp.load_tool_table();
|
||||
standalone::apply_axis_mask_to_interp(interp, standalone::axis_mask_from_coordinates("XYZABCUVW"));
|
||||
standalone::reset_user_m_code_registry();
|
||||
interp.init_named_parameters();
|
||||
}
|
||||
|
||||
@@ -257,6 +265,61 @@ void append_tool_table_state(std::ostringstream &output)
|
||||
output << "tool_index_for_tool_2=" << standalone::find_tool_index_for_tool(2) << "\n";
|
||||
}
|
||||
|
||||
bool is_uri_unreserved(char value)
|
||||
{
|
||||
return (value >= 'A' && value <= 'Z') ||
|
||||
(value >= 'a' && value <= 'z') ||
|
||||
(value >= '0' && value <= '9') ||
|
||||
value == '-' || value == '_' || value == '.' || value == '~';
|
||||
}
|
||||
|
||||
void append_percent_encoded_text(std::ostringstream &output, const char *text)
|
||||
{
|
||||
constexpr char hex[] = "0123456789ABCDEF";
|
||||
for (const char *cursor = text ? text : ""; *cursor; ++cursor) {
|
||||
char value = *cursor;
|
||||
switch (*cursor) {
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\t':
|
||||
value = ' ';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (is_uri_unreserved(value)) {
|
||||
output << value;
|
||||
} else {
|
||||
const auto byte = static_cast<unsigned char>(value);
|
||||
output << '%' << hex[(byte >> 4) & 0x0f] << hex[byte & 0x0f];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void append_run_step(std::ostringstream &output,
|
||||
const Interp &interp,
|
||||
const char *phase,
|
||||
int step,
|
||||
int rc)
|
||||
{
|
||||
output << "run_step phase=" << phase
|
||||
<< " step=" << step
|
||||
<< " rc=" << rc
|
||||
<< " line=" << interp._setup.sequence_number
|
||||
<< " x=" << interp._setup.current_x
|
||||
<< " y=" << interp._setup.current_y
|
||||
<< " z=" << interp._setup.current_z
|
||||
<< " a=" << interp._setup.AA_current
|
||||
<< " b=" << interp._setup.BB_current
|
||||
<< " c=" << interp._setup.CC_current
|
||||
<< " u=" << interp._setup.u_current
|
||||
<< " v=" << interp._setup.v_current
|
||||
<< " w=" << interp._setup.w_current
|
||||
<< " statement_uri=";
|
||||
append_percent_encoded_text(output, interp._setup.linetext);
|
||||
output << "\n";
|
||||
}
|
||||
|
||||
void append_error_text(std::ostringstream &output, Interp &interp, const char *prefix, int rc)
|
||||
{
|
||||
if (rc > INTERP_MIN_ERROR) {
|
||||
@@ -274,6 +337,15 @@ void append_named_value(std::ostringstream &output, Interp &interp, const char *
|
||||
output << name << ": rc=" << rc << " found=" << status << " value=" << value << "\n";
|
||||
}
|
||||
|
||||
std::string command_label(const char *command)
|
||||
{
|
||||
std::string value = command ? command : "";
|
||||
while (!value.empty() && (value.back() == '\n' || value.back() == '\r')) {
|
||||
value.pop_back();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string parent_path(const char *path)
|
||||
{
|
||||
std::string value = path ? path : "";
|
||||
@@ -329,6 +401,129 @@ bool parse_machine_remaps(Interp &interp, std::ostringstream &output, const char
|
||||
return ok;
|
||||
}
|
||||
|
||||
std::string remap_subroutine_path(const std::string &machine_dir, const std::string &entry)
|
||||
{
|
||||
if (!entry.empty() && entry[0] == '/') {
|
||||
return entry;
|
||||
}
|
||||
if (machine_dir == "/") {
|
||||
return machine_dir + entry;
|
||||
}
|
||||
return machine_dir + "/" + entry;
|
||||
}
|
||||
|
||||
std::string resolve_machine_relative_path(const std::string &machine_dir, const std::string &entry)
|
||||
{
|
||||
if (entry.empty() || entry[0] == '/') {
|
||||
return entry;
|
||||
}
|
||||
if (machine_dir == "/") {
|
||||
return machine_dir + entry;
|
||||
}
|
||||
return machine_dir + "/" + entry;
|
||||
}
|
||||
|
||||
int apply_ini_search_paths(Interp &interp, const linuxcnc::IniFile &ini, const std::string &machine_dir)
|
||||
{
|
||||
standalone::apply_machine_axis_config(interp, ini);
|
||||
standalone::load_tool_table_from_ini(interp, ini, machine_dir.c_str());
|
||||
standalone::register_user_m_codes_from_ini(ini, machine_dir.c_str());
|
||||
|
||||
std::snprintf(interp._setup.program_prefix, sizeof(interp._setup.program_prefix), "%s",
|
||||
machine_dir.c_str());
|
||||
|
||||
int subroutine_count = 0;
|
||||
if (auto subroutine_path_text = ini.findString("SUBROUTINE_PATH", "RS274NGC")) {
|
||||
std::istringstream paths(*subroutine_path_text);
|
||||
std::string entry;
|
||||
while (std::getline(paths, entry, ':') && subroutine_count < MAX_SUB_DIRS) {
|
||||
const std::string resolved = remap_subroutine_path(machine_dir, entry);
|
||||
interp._setup.subroutines[subroutine_count++] = strstore(resolved.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (ini.findBoolV("OWORD_NARGS", "RS274NGC", false)) {
|
||||
interp._setup.feature_set |= FEATURE_OWORD_N_ARGS;
|
||||
}
|
||||
|
||||
return subroutine_count;
|
||||
}
|
||||
|
||||
bool apply_ini_search_paths(Interp &interp, const char *ini_path)
|
||||
{
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
return true;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini) {
|
||||
return false;
|
||||
}
|
||||
|
||||
apply_ini_search_paths(interp, ini, parent_path(ini_path));
|
||||
return true;
|
||||
}
|
||||
|
||||
int initialize_parameter_file_from_ini(Interp &interp, const char *ini_path)
|
||||
{
|
||||
standalone::reset_parameter_file_name();
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini || !ini.findString("PARAMETER_FILE", "RS274NGC")) {
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
const int ini_rc = interp.ini_load(ini_path);
|
||||
if (ini_rc != INTERP_OK) {
|
||||
return ini_rc;
|
||||
}
|
||||
|
||||
char parameter_file_name[LINELEN] = {0};
|
||||
standalone::get_parameter_file_name(parameter_file_name, sizeof(parameter_file_name));
|
||||
const std::string resolved =
|
||||
resolve_machine_relative_path(parent_path(ini_path), parameter_file_name);
|
||||
if (access(resolved.c_str(), F_OK) != 0) {
|
||||
standalone::reset_parameter_file_name();
|
||||
return INTERP_OK;
|
||||
}
|
||||
standalone::set_parameter_file_name(resolved.c_str());
|
||||
|
||||
return interp.init();
|
||||
}
|
||||
|
||||
bool parse_remaps_from_ini(Interp &interp, std::ostringstream &output, const char *ini_path)
|
||||
{
|
||||
const std::string machine_dir = parent_path(ini_path);
|
||||
|
||||
linuxcnc::IniFile ini(ini_path ? ini_path : "");
|
||||
const bool opened = static_cast<bool>(ini);
|
||||
output << "remap_ini_open=" << (opened ? 1 : 0) << "\n";
|
||||
if (!opened) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int subroutine_count = apply_ini_search_paths(interp, ini, machine_dir);
|
||||
output << "remap_subroutine_path_count=" << subroutine_count << "\n";
|
||||
|
||||
bool ok = subroutine_count > 0;
|
||||
int remap_count = 0;
|
||||
while (auto remap = ini.findString(++remap_count, "REMAP", "RS274NGC")) {
|
||||
const int line_number = ini.lineOf(remap_count, "REMAP", "RS274NGC").second;
|
||||
const int rc = interp.parse_remap(remap->c_str(), line_number);
|
||||
output << "remap_parse_remap_" << remap_count << "=" << rc << "\n";
|
||||
ok &= rc == INTERP_OK;
|
||||
}
|
||||
|
||||
remap_count -= 1;
|
||||
output << "remap_parsed_remap_count=" << remap_count << "\n";
|
||||
ok &= remap_count > 0;
|
||||
output << "remap_remaps_ready=" << (ok ? 1 : 0) << "\n";
|
||||
return ok;
|
||||
}
|
||||
|
||||
void apply_parameter_assignments(Interp &interp, const char *assignments)
|
||||
{
|
||||
std::istringstream input(assignments ? assignments : "");
|
||||
@@ -371,6 +566,7 @@ char *lcinterp_run_program_with_ini(const char *program_text, const char *ini_pa
|
||||
Interp interp;
|
||||
standalone::reset_canon_events();
|
||||
initialize_minimal_interp(interp);
|
||||
apply_ini_search_paths(interp, ini_path);
|
||||
|
||||
const std::vector<std::string> program = split_program_lines(program_text);
|
||||
std::ostringstream output;
|
||||
@@ -401,8 +597,7 @@ char *lcinterp_run_program(const char *program_text)
|
||||
return lcinterp_run_program_with_ini(program_text, nullptr);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
char *run_file_with_ini_internal(const char *path, const char *ini_path, bool continue_on_error)
|
||||
{
|
||||
if (ini_path && ini_path[0] != '\0') {
|
||||
setenv("INI_FILE_NAME", ini_path, 1);
|
||||
@@ -415,6 +610,16 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
initialize_minimal_interp(interp);
|
||||
|
||||
std::ostringstream output;
|
||||
apply_ini_search_paths(interp, ini_path);
|
||||
const int init_rc = initialize_parameter_file_from_ini(interp, ini_path);
|
||||
if (init_rc != INTERP_OK) {
|
||||
output << "file_init=" << init_rc << "\n";
|
||||
append_error_text(output, interp, "file_error_text=", init_rc);
|
||||
append_events_and_state(output, interp);
|
||||
return copy_result(output.str());
|
||||
}
|
||||
apply_ini_search_paths(interp, ini_path);
|
||||
|
||||
const int open_rc = interp.open(path);
|
||||
output << "file_open=" << open_rc << "\n";
|
||||
if (open_rc != INTERP_OK) {
|
||||
@@ -429,6 +634,7 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
|
||||
int file_read_count = 0;
|
||||
int file_execute_count = 0;
|
||||
bool saw_error = false;
|
||||
while (true) {
|
||||
const int read_rc = interp.read();
|
||||
if (read_rc == INTERP_ENDFILE) {
|
||||
@@ -438,18 +644,24 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
|
||||
++file_read_count;
|
||||
output << "file_read_" << file_read_count << "=" << read_rc << "\n";
|
||||
append_run_step(output, interp, "read", file_read_count, read_rc);
|
||||
if ((read_rc != INTERP_OK) && (read_rc != INTERP_EXECUTE_FINISH)) {
|
||||
if (read_rc > INTERP_MIN_ERROR) {
|
||||
char error_buf[LINELEN] = {0};
|
||||
interp.error_text(read_rc, error_buf, sizeof(error_buf));
|
||||
output << "file_error_text=" << error_buf << "\n";
|
||||
}
|
||||
break;
|
||||
saw_error = true;
|
||||
if (!continue_on_error) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const int execute_rc = interp.execute();
|
||||
++file_execute_count;
|
||||
output << "file_execute_" << file_execute_count << "=" << execute_rc << "\n";
|
||||
append_run_step(output, interp, "execute", file_execute_count, execute_rc);
|
||||
if (execute_rc > INTERP_MIN_ERROR) {
|
||||
char error_buf[LINELEN] = {0};
|
||||
interp.error_text(execute_rc, error_buf, sizeof(error_buf));
|
||||
@@ -458,6 +670,7 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
if ((execute_rc != INTERP_OK) &&
|
||||
(execute_rc != INTERP_EXECUTE_FINISH) &&
|
||||
(execute_rc != INTERP_EXIT)) {
|
||||
saw_error = true;
|
||||
break;
|
||||
}
|
||||
if (execute_rc == INTERP_EXIT) {
|
||||
@@ -467,10 +680,23 @@ char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
|
||||
output << "file_read_count=" << file_read_count << "\n";
|
||||
output << "file_execute_count=" << file_execute_count << "\n";
|
||||
output << "file_saw_error=" << (saw_error ? 1 : 0) << "\n";
|
||||
append_events_and_state(output, interp);
|
||||
return copy_result(output.str());
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_file_with_ini(const char *path, const char *ini_path)
|
||||
{
|
||||
return run_file_with_ini_internal(path, ini_path, false);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_file_with_ini_continue_on_error(const char *path, const char *ini_path)
|
||||
{
|
||||
return run_file_with_ini_internal(path, ini_path, true);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_file(const char *path)
|
||||
{
|
||||
@@ -573,6 +799,230 @@ char *lcinterp_run_fiveaxis_remap_file(const char *path, const char *ini_path)
|
||||
return copy_result(output.str());
|
||||
}
|
||||
|
||||
char *run_remap_file_internal(const char *path,
|
||||
const char *ini_path,
|
||||
bool continue_on_error,
|
||||
bool expect_error)
|
||||
{
|
||||
if (ini_path && ini_path[0] != '\0') {
|
||||
setenv("INI_FILE_NAME", ini_path, 1);
|
||||
} else {
|
||||
unsetenv("INI_FILE_NAME");
|
||||
}
|
||||
|
||||
Interp interp;
|
||||
standalone::reset_canon_events();
|
||||
initialize_minimal_interp(interp);
|
||||
interp.init_named_parameters();
|
||||
|
||||
std::ostringstream output;
|
||||
bool ok = parse_remaps_from_ini(interp, output, ini_path);
|
||||
|
||||
const int open_rc = interp.open(path);
|
||||
output << "remap_file_open=" << open_rc << "\n";
|
||||
if (open_rc != INTERP_OK) {
|
||||
append_error_text(output, interp, "remap_file_error_text=", open_rc);
|
||||
output << "remap_linuxcnc_file_execute=0\n";
|
||||
unsetenv("INI_FILE_NAME");
|
||||
return copy_result(output.str());
|
||||
}
|
||||
|
||||
constexpr int max_file_steps = 20000;
|
||||
int file_read_count = 0;
|
||||
int file_execute_count = 0;
|
||||
int file_finish_count = 0;
|
||||
int final_rc = INTERP_OK;
|
||||
bool reached_exit = false;
|
||||
bool reached_endfile = false;
|
||||
bool saw_error = false;
|
||||
|
||||
while ((file_read_count < max_file_steps) && (file_execute_count < max_file_steps)) {
|
||||
const int read_rc = interp.read();
|
||||
if (read_rc == INTERP_ENDFILE) {
|
||||
output << "remap_file_read_eof=" << read_rc << "\n";
|
||||
final_rc = read_rc;
|
||||
reached_endfile = true;
|
||||
break;
|
||||
}
|
||||
|
||||
++file_read_count;
|
||||
if ((read_rc != INTERP_OK) && (read_rc != INTERP_EXECUTE_FINISH)) {
|
||||
output << "remap_file_read_" << file_read_count << "=" << read_rc << "\n";
|
||||
append_error_text(output, interp, "remap_file_error_text=", read_rc);
|
||||
final_rc = read_rc;
|
||||
saw_error = true;
|
||||
if (!expect_error) {
|
||||
ok = false;
|
||||
}
|
||||
if (!continue_on_error) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const int execute_rc = interp.execute();
|
||||
++file_execute_count;
|
||||
final_rc = execute_rc;
|
||||
if (execute_rc == INTERP_EXECUTE_FINISH) {
|
||||
++file_finish_count;
|
||||
}
|
||||
if ((execute_rc != INTERP_OK) && (execute_rc != INTERP_EXIT) &&
|
||||
(execute_rc != INTERP_EXECUTE_FINISH)) {
|
||||
output << "remap_file_execute_" << file_execute_count << "=" << execute_rc << "\n";
|
||||
append_error_text(output, interp, "remap_file_error_text=", execute_rc);
|
||||
saw_error = true;
|
||||
if (!expect_error) {
|
||||
ok = false;
|
||||
}
|
||||
if (!continue_on_error) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (execute_rc == INTERP_EXIT) {
|
||||
reached_exit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
output << "remap_file_read_count=" << file_read_count << "\n";
|
||||
output << "remap_file_execute_count=" << file_execute_count << "\n";
|
||||
output << "remap_file_finish_count=" << file_finish_count << "\n";
|
||||
output << "remap_file_final_rc=" << final_rc << "\n";
|
||||
output << "remap_file_reached_exit=" << (reached_exit ? 1 : 0) << "\n";
|
||||
output << "remap_file_reached_endfile=" << (reached_endfile ? 1 : 0) << "\n";
|
||||
output << "remap_file_saw_error=" << (saw_error ? 1 : 0) << "\n";
|
||||
for (const auto &event : standalone::canon_events()) {
|
||||
output << "remap_canon_event=" << event << "\n";
|
||||
}
|
||||
|
||||
ok &= file_read_count > 0;
|
||||
ok &= file_execute_count > 0;
|
||||
ok &= file_read_count < max_file_steps;
|
||||
ok &= file_execute_count < max_file_steps;
|
||||
ok &= reached_exit || reached_endfile;
|
||||
ok &= expect_error ? saw_error : !saw_error;
|
||||
|
||||
output << "remap_linuxcnc_file_execute=" << (ok ? 1 : 0) << "\n";
|
||||
unsetenv("INI_FILE_NAME");
|
||||
return copy_result(output.str());
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
bool execute_remap_mdi_command(Interp &interp, std::ostringstream &output, const char *command)
|
||||
{
|
||||
const std::string label = command_label(command);
|
||||
int rc = interp.execute(command);
|
||||
output << "remap_mdi_" << label << "_execute_0=" << rc << "\n";
|
||||
|
||||
int finish_count = 0;
|
||||
while (rc == INTERP_EXECUTE_FINISH && finish_count < 16) {
|
||||
++finish_count;
|
||||
rc = interp.execute();
|
||||
output << "remap_mdi_" << label << "_execute_" << finish_count << "=" << rc << "\n";
|
||||
}
|
||||
|
||||
output << "remap_mdi_" << label << "_execute_finish_count=" << finish_count << "\n";
|
||||
return rc == INTERP_OK;
|
||||
}
|
||||
|
||||
char *run_remap_io_mdi_sequence_internal(const char *ini_path)
|
||||
{
|
||||
if (ini_path && ini_path[0] != '\0') {
|
||||
setenv("INI_FILE_NAME", ini_path, 1);
|
||||
} else {
|
||||
unsetenv("INI_FILE_NAME");
|
||||
}
|
||||
|
||||
Interp interp;
|
||||
standalone::reset_canon_events();
|
||||
initialize_minimal_interp(interp);
|
||||
interp.init_named_parameters();
|
||||
|
||||
std::ostringstream output;
|
||||
bool ok = parse_remaps_from_ini(interp, output, ini_path);
|
||||
ok &= output.str().find("remap_parsed_remap_count=7") != std::string::npos;
|
||||
|
||||
const struct MdiStep {
|
||||
const char *command;
|
||||
bool digital_input;
|
||||
int digital_value;
|
||||
bool analog_input;
|
||||
double analog_value;
|
||||
} steps[] = {
|
||||
{"M62 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X1.0\n", false, 0, false, 0.0},
|
||||
{"M63 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X2.0\n", false, 0, false, 0.0},
|
||||
{"M62 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X3.0\n", false, 0, false, 0.0},
|
||||
{"M63 P1\n", false, 0, false, 0.0},
|
||||
{"G0 X4.0\n", false, 0, false, 0.0},
|
||||
{"M64 P1\n", false, 0, false, 0.0},
|
||||
{"M65 P1\n", false, 0, false, 0.0},
|
||||
{"M64 P1\n", false, 0, false, 0.0},
|
||||
{"M65 P1\n", false, 0, false, 0.0},
|
||||
{"M66 P1\n", true, 1, false, 0.0},
|
||||
{"M66 P1\n", true, 0, false, 0.0},
|
||||
{"M66 E1 L0\n", false, 0, true, 42.13},
|
||||
{"M66 E1 L0\n", false, 0, true, -13.42},
|
||||
{"M67 E1 Q42.13\n", false, 0, false, 0.0},
|
||||
{"G0 X-1.0\n", false, 0, false, 0.0},
|
||||
{"M67 E1 Q-13.42\n", false, 0, false, 0.0},
|
||||
{"G0 X-2.0\n", false, 0, false, 0.0},
|
||||
{"M67 E1 Q0.0\n", false, 0, false, 0.0},
|
||||
{"G0 X-3.0\n", false, 0, false, 0.0},
|
||||
{"M68 E1 Q42.13\n", false, 0, false, 0.0},
|
||||
{"M68 E1 Q-13.42\n", false, 0, false, 0.0},
|
||||
{"M68 E1 Q0.0\n", false, 0, false, 0.0},
|
||||
};
|
||||
|
||||
for (const auto &step : steps) {
|
||||
if (step.digital_input) {
|
||||
standalone::set_external_digital_input(0, step.digital_value);
|
||||
}
|
||||
if (step.analog_input) {
|
||||
standalone::set_external_analog_input(0, step.analog_value);
|
||||
}
|
||||
ok &= execute_remap_mdi_command(interp, output, step.command);
|
||||
output << "remap_mdi_parameter_5399=" << interp._setup.parameters[5399] << "\n";
|
||||
output << "remap_mdi_parameter_100=" << interp._setup.parameters[100] << "\n";
|
||||
}
|
||||
|
||||
output << "remap_mdi_canon_event_count=" << standalone::canon_events().size() << "\n";
|
||||
for (const auto &event : standalone::canon_events()) {
|
||||
output << "remap_mdi_canon_event=" << event << "\n";
|
||||
}
|
||||
|
||||
ok &= std::fabs(interp._setup.parameters[5399] - -13.42) < 1e-9;
|
||||
ok &= std::fabs(interp._setup.parameters[100] - -13.42) < 1e-9;
|
||||
output << "remap_io_ngc_linuxcnc_mdi_sequence=" << (ok ? 1 : 0) << "\n";
|
||||
unsetenv("INI_FILE_NAME");
|
||||
return copy_result(output.str());
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_remap_file(const char *path, const char *ini_path)
|
||||
{
|
||||
return run_remap_file_internal(path, ini_path, false, false);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_remap_file_continue_on_error(const char *path, const char *ini_path)
|
||||
{
|
||||
return run_remap_file_internal(path, ini_path, true, true);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_run_remap_io_mdi_sequence(const char *ini_path)
|
||||
{
|
||||
return run_remap_io_mdi_sequence_internal(ini_path);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
char *lcinterp_restore_parameters(const char *path)
|
||||
{
|
||||
|
||||
280
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_machine_config.cpp
Normal file
280
wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_machine_config.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
#define private public
|
||||
#include "emc/rs274ngc/rs274ngc_interp.hh"
|
||||
#undef private
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "emc/ini/inifile.hh"
|
||||
#include "canon_event_sink.hh"
|
||||
#include "linuxcnc_machine_config.hh"
|
||||
#include "linuxcnc_runtime_state.hh"
|
||||
#include "tooldata.hh"
|
||||
|
||||
namespace standalone {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kDefaultAxisMask = Interp::AXIS_MASK_X | Interp::AXIS_MASK_Y | Interp::AXIS_MASK_Z;
|
||||
constexpr int kMaxUserMDirs = 6;
|
||||
|
||||
std::string g_user_m_paths[USER_DEFINED_FUNCTION_NUM];
|
||||
|
||||
bool is_executable_file(const std::filesystem::path &path)
|
||||
{
|
||||
struct stat st {};
|
||||
return stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode) && (st.st_mode & S_IXUSR);
|
||||
}
|
||||
|
||||
std::filesystem::path resolve_machine_path(const std::filesystem::path &machine_dir, const std::string &entry)
|
||||
{
|
||||
std::filesystem::path path(entry);
|
||||
if (path.is_relative()) {
|
||||
path = machine_dir / path;
|
||||
}
|
||||
return path.lexically_normal();
|
||||
}
|
||||
|
||||
void standalone_user_m_code(int num, double arg1, double arg2)
|
||||
{
|
||||
FINISH();
|
||||
const int code = num + 100;
|
||||
std::ostringstream event;
|
||||
event << "USER_M_COMMAND code=M" << code
|
||||
<< " path=" << g_user_m_paths[num]
|
||||
<< " p=" << arg1
|
||||
<< " q=" << arg2;
|
||||
push_canon_event(event.str());
|
||||
}
|
||||
|
||||
int axis_mask_for_letter(char letter)
|
||||
{
|
||||
switch (std::toupper(static_cast<unsigned char>(letter))) {
|
||||
case 'X':
|
||||
return Interp::AXIS_MASK_X;
|
||||
case 'Y':
|
||||
return Interp::AXIS_MASK_Y;
|
||||
case 'Z':
|
||||
return Interp::AXIS_MASK_Z;
|
||||
case 'A':
|
||||
return Interp::AXIS_MASK_A;
|
||||
case 'B':
|
||||
return Interp::AXIS_MASK_B;
|
||||
case 'C':
|
||||
return Interp::AXIS_MASK_C;
|
||||
case 'U':
|
||||
return Interp::AXIS_MASK_U;
|
||||
case 'V':
|
||||
return Interp::AXIS_MASK_V;
|
||||
case 'W':
|
||||
return Interp::AXIS_MASK_W;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int axis_mask_from_coordinates(const char *coordinates)
|
||||
{
|
||||
if (!coordinates || coordinates[0] == '\0') {
|
||||
return kDefaultAxisMask;
|
||||
}
|
||||
|
||||
int axis_mask = 0;
|
||||
for (const char *cursor = coordinates; *cursor; ++cursor) {
|
||||
axis_mask |= axis_mask_for_letter(*cursor);
|
||||
}
|
||||
return axis_mask == 0 ? kDefaultAxisMask : axis_mask;
|
||||
}
|
||||
|
||||
int axis_mask_from_ini(const char *ini_path)
|
||||
{
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
return kDefaultAxisMask;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini) {
|
||||
return kDefaultAxisMask;
|
||||
}
|
||||
|
||||
if (auto coordinates = ini.findString("COORDINATES", "TRAJ")) {
|
||||
return axis_mask_from_coordinates(coordinates->c_str());
|
||||
}
|
||||
return kDefaultAxisMask;
|
||||
}
|
||||
|
||||
void apply_axis_mask_to_interp(Interp &interp, int axis_mask)
|
||||
{
|
||||
if (axis_mask == 0) {
|
||||
axis_mask = kDefaultAxisMask;
|
||||
}
|
||||
|
||||
set_external_axis_mask(axis_mask);
|
||||
std::memcpy(interp._readers, Interp::default_readers, sizeof(Interp::default_readers));
|
||||
if (!(axis_mask & Interp::AXIS_MASK_X)) {
|
||||
interp._readers[static_cast<int>('x')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_Y)) {
|
||||
interp._readers[static_cast<int>('y')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_Z)) {
|
||||
interp._readers[static_cast<int>('z')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_A)) {
|
||||
interp._readers[static_cast<int>('a')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_B)) {
|
||||
interp._readers[static_cast<int>('b')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_C)) {
|
||||
interp._readers[static_cast<int>('c')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_U)) {
|
||||
interp._readers[static_cast<int>('u')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_V)) {
|
||||
interp._readers[static_cast<int>('v')] = nullptr;
|
||||
}
|
||||
if (!(axis_mask & Interp::AXIS_MASK_W)) {
|
||||
interp._readers[static_cast<int>('w')] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void apply_machine_axis_config(Interp &interp, const linuxcnc::IniFile &ini)
|
||||
{
|
||||
interp._setup.orient_offset = ini.findRealV("ORIENT_OFFSET", "RS274NGC", 0.0);
|
||||
interp._setup.disable_fanuc_style_sub =
|
||||
ini.findBoolV("DISABLE_FANUC_STYLE_SUB", "RS274NGC", false);
|
||||
|
||||
if (auto coordinates = ini.findString("COORDINATES", "TRAJ")) {
|
||||
apply_axis_mask_to_interp(interp, axis_mask_from_coordinates(coordinates->c_str()));
|
||||
return;
|
||||
}
|
||||
apply_axis_mask_to_interp(interp, kDefaultAxisMask);
|
||||
}
|
||||
|
||||
bool apply_machine_axis_config(Interp &interp, const char *ini_path)
|
||||
{
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
apply_axis_mask_to_interp(interp, kDefaultAxisMask);
|
||||
return true;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini) {
|
||||
apply_axis_mask_to_interp(interp, kDefaultAxisMask);
|
||||
return false;
|
||||
}
|
||||
apply_machine_axis_config(interp, ini);
|
||||
return true;
|
||||
}
|
||||
|
||||
int load_tool_table_from_ini(Interp &interp, const linuxcnc::IniFile &ini, const char *machine_dir)
|
||||
{
|
||||
if (auto tool_table = ini.findString("TOOL_TABLE", "EMCIO")) {
|
||||
const std::filesystem::path base_dir = (machine_dir && machine_dir[0] != '\0')
|
||||
? std::filesystem::path(machine_dir)
|
||||
: std::filesystem::path();
|
||||
const std::filesystem::path tool_path = resolve_machine_path(base_dir, *tool_table);
|
||||
const bool random_toolchanger = ini.findBoolV("RANDOM_TOOLCHANGER", "EMCIO", false);
|
||||
|
||||
interp._setup.random_toolchanger = random_toolchanger;
|
||||
tooldata_init(random_toolchanger);
|
||||
tooldata_set_db(DB_NOTUSED);
|
||||
const int rc = tooldata_load(tool_path.c_str());
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
return interp.load_tool_table();
|
||||
}
|
||||
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
int load_tool_table_from_ini(Interp &interp, const char *ini_path)
|
||||
{
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
return INTERP_OK;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini) {
|
||||
return -1;
|
||||
}
|
||||
const std::filesystem::path machine_dir = std::filesystem::path(ini_path).parent_path();
|
||||
return load_tool_table_from_ini(interp, ini, machine_dir.c_str());
|
||||
}
|
||||
|
||||
void reset_user_m_code_registry()
|
||||
{
|
||||
for (int index = 0; index < USER_DEFINED_FUNCTION_NUM; ++index) {
|
||||
USER_DEFINED_FUNCTION[index] = nullptr;
|
||||
g_user_m_paths[index].clear();
|
||||
}
|
||||
}
|
||||
|
||||
int register_user_m_codes_from_ini(const linuxcnc::IniFile &ini, const char *machine_dir)
|
||||
{
|
||||
std::filesystem::path base_dir = (machine_dir && machine_dir[0] != '\0')
|
||||
? std::filesystem::path(machine_dir)
|
||||
: std::filesystem::current_path();
|
||||
|
||||
std::string program_prefix = "nc_files";
|
||||
if (auto prefix = ini.findString("PROGRAM_PREFIX", "DISPLAY")) {
|
||||
program_prefix = *prefix;
|
||||
}
|
||||
|
||||
std::filesystem::path dirs[kMaxUserMDirs];
|
||||
int dir_count = 0;
|
||||
dirs[dir_count++] = resolve_machine_path(base_dir, program_prefix);
|
||||
|
||||
if (auto user_m_path = ini.findString("USER_M_PATH", "RS274NGC")) {
|
||||
std::istringstream entries(*user_m_path);
|
||||
std::string entry;
|
||||
while (std::getline(entries, entry, ':') && dir_count < kMaxUserMDirs) {
|
||||
if (!entry.empty()) {
|
||||
dirs[dir_count++] = resolve_machine_path(base_dir, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int registered = 0;
|
||||
for (int num = 0; num < USER_DEFINED_FUNCTION_NUM; ++num) {
|
||||
for (int dir_index = 0; dir_index < dir_count; ++dir_index) {
|
||||
char file_name[8] = {0};
|
||||
std::snprintf(file_name, sizeof(file_name), "M1%02d", num);
|
||||
std::filesystem::path candidate = dirs[dir_index] / file_name;
|
||||
if (!is_executable_file(candidate)) {
|
||||
continue;
|
||||
}
|
||||
USER_DEFINED_FUNCTION_ADD(standalone_user_m_code, num);
|
||||
g_user_m_paths[num] = candidate.string();
|
||||
++registered;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return registered;
|
||||
}
|
||||
|
||||
int register_user_m_codes_from_ini(const char *ini_path)
|
||||
{
|
||||
if (!ini_path || ini_path[0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
if (!ini) {
|
||||
return 0;
|
||||
}
|
||||
const std::filesystem::path machine_dir = std::filesystem::path(ini_path).parent_path();
|
||||
return register_user_m_codes_from_ini(ini, machine_dir.c_str());
|
||||
}
|
||||
|
||||
} // namespace standalone
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
class Interp;
|
||||
|
||||
namespace linuxcnc {
|
||||
class IniFile;
|
||||
}
|
||||
|
||||
namespace standalone {
|
||||
|
||||
int axis_mask_from_coordinates(const char *coordinates);
|
||||
int axis_mask_from_ini(const char *ini_path);
|
||||
void apply_axis_mask_to_interp(Interp &interp, int axis_mask);
|
||||
void apply_machine_axis_config(Interp &interp, const linuxcnc::IniFile &ini);
|
||||
bool apply_machine_axis_config(Interp &interp, const char *ini_path);
|
||||
int load_tool_table_from_ini(Interp &interp, const linuxcnc::IniFile &ini, const char *machine_dir);
|
||||
int load_tool_table_from_ini(Interp &interp, const char *ini_path);
|
||||
void reset_user_m_code_registry();
|
||||
int register_user_m_codes_from_ini(const linuxcnc::IniFile &ini, const char *machine_dir);
|
||||
int register_user_m_codes_from_ini(const char *ini_path);
|
||||
|
||||
} // namespace standalone
|
||||
@@ -0,0 +1,215 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
|
||||
#include "emc/ini/inifile.hh"
|
||||
|
||||
#ifndef LINUXCNC_SOURCE_CONFIG_DIR
|
||||
#error "LINUXCNC_SOURCE_CONFIG_DIR must point at the LinuxCNC configs/sim tree"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
struct UserMCase {
|
||||
const char *code;
|
||||
const char *remap_code;
|
||||
const char *script_rel;
|
||||
const char *remap_rel;
|
||||
const char *state_mode;
|
||||
const char *guard_pin;
|
||||
const char *switchkins_target;
|
||||
const char *active_g5x;
|
||||
const char *work_offset_pocket;
|
||||
const char *work_offset_words;
|
||||
const char *min_limit_key;
|
||||
const char *max_limit_key;
|
||||
};
|
||||
|
||||
std::string source_config_path(const std::string &rel)
|
||||
{
|
||||
return std::string(LINUXCNC_SOURCE_CONFIG_DIR) + "/" + rel;
|
||||
}
|
||||
|
||||
std::string read_text(const std::string &path)
|
||||
{
|
||||
std::ifstream input(path);
|
||||
return std::string(
|
||||
std::istreambuf_iterator<char>(input),
|
||||
std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
bool contains(const std::string &text, const std::string &needle)
|
||||
{
|
||||
return text.find(needle) != std::string::npos;
|
||||
}
|
||||
|
||||
void print_bool(const std::string &name, bool value)
|
||||
{
|
||||
std::cout << name << "=" << (value ? 1 : 0) << "\n";
|
||||
}
|
||||
|
||||
bool check_ini_value(
|
||||
const linuxcnc::IniFile &ini,
|
||||
const std::string &label,
|
||||
const std::string §ion,
|
||||
const std::string &tag,
|
||||
const std::string &expected)
|
||||
{
|
||||
const auto actual = ini.findString(tag, section);
|
||||
std::cout << label << "_" << section << "." << tag << "="
|
||||
<< (actual ? *actual : "<missing>") << "\n";
|
||||
const bool ok = actual && *actual == expected;
|
||||
print_bool(label + "_" + section + "." + tag + "_ok", ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool check_axis_targets(
|
||||
const linuxcnc::IniFile &ini,
|
||||
const UserMCase &user_m,
|
||||
const char *axis,
|
||||
const char *expected_min,
|
||||
const char *expected_max,
|
||||
const char *expected_velocity,
|
||||
const char *expected_acceleration)
|
||||
{
|
||||
const std::string section = std::string("AXIS_") + axis;
|
||||
const std::string label = std::string(user_m.code) + "_" + axis;
|
||||
bool ok = true;
|
||||
ok &= check_ini_value(ini, label, section, user_m.min_limit_key, expected_min);
|
||||
ok &= check_ini_value(ini, label, section, user_m.max_limit_key, expected_max);
|
||||
ok &= check_ini_value(ini, label, section, "MAX_VELOCITY", expected_velocity);
|
||||
ok &= check_ini_value(ini, label, section, "MAX_ACCELERATION", expected_acceleration);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool check_script(const UserMCase &user_m)
|
||||
{
|
||||
const std::string text = read_text(source_config_path(user_m.script_rel));
|
||||
bool ok = true;
|
||||
ok &= contains(text, "#!/usr/bin/tclsh");
|
||||
ok &= contains(text, "package require Linuxcnc");
|
||||
ok &= contains(text, "package require Hal");
|
||||
ok &= contains(text, "parse_ini $::env(INI_FILE_NAME)");
|
||||
ok &= contains(text, std::string("hal getp ") + user_m.guard_pin);
|
||||
ok &= contains(text, "hal setp ini.$l.min_limit");
|
||||
ok &= contains(text, "hal setp ini.$l.max_limit");
|
||||
ok &= contains(text, "hal setp ini.$l.min_velocity");
|
||||
ok &= contains(text, "hal setp ini.$l.max_acceleration");
|
||||
ok &= contains(text, user_m.min_limit_key);
|
||||
ok &= contains(text, user_m.max_limit_key);
|
||||
print_bool(std::string(user_m.code) + "_script_tcl_hal_runtime", ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool check_remap_caller(const UserMCase &user_m)
|
||||
{
|
||||
const std::string text = read_text(source_config_path(user_m.remap_rel));
|
||||
const std::string remap_code(user_m.remap_code);
|
||||
const bool ok =
|
||||
contains(text, user_m.code) &&
|
||||
contains(text, "#<SWITCHKINS_PIN> = 3") &&
|
||||
contains(text, std::string("#<kinstype> = ") + user_m.switchkins_target) &&
|
||||
contains(text, "M68") &&
|
||||
contains(text, "M68 E#<SWITCHKINS_PIN> Q#<kinstype>") &&
|
||||
contains(text, "M66") &&
|
||||
contains(text, "M66 E0 L0") &&
|
||||
contains(text, std::string("G10 L2 ") + user_m.work_offset_pocket + " " + user_m.work_offset_words) &&
|
||||
contains(text, user_m.active_g5x) &&
|
||||
contains(text, "#<_hal[motion.switchkins-type]>") &&
|
||||
contains(text, std::string("[#<_hal[motion.switchkins-type]> NE ") + user_m.switchkins_target + "]");
|
||||
std::cout << remap_code << "_switchkins_output_pin=motion.analog-out-03\n";
|
||||
std::cout << remap_code << "_switchkins_target=" << user_m.switchkins_target << "\n";
|
||||
std::cout << remap_code << "_work_offset_pocket=" << user_m.work_offset_pocket << "\n";
|
||||
std::cout << remap_code << "_work_offset_words=" << user_m.work_offset_words << "\n";
|
||||
std::cout << remap_code << "_active_g5x=" << user_m.active_g5x << "\n";
|
||||
print_bool(remap_code + "_calls_" + user_m.code, contains(text, user_m.code));
|
||||
print_bool(remap_code + "_source_transition_from_linuxcnc", ok);
|
||||
print_bool(std::string(user_m.code) + "_remap_calls_user_m_process", ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool check_user_m_case(const linuxcnc::IniFile &ini, const UserMCase &user_m)
|
||||
{
|
||||
bool ok = true;
|
||||
ok &= check_script(user_m);
|
||||
ok &= check_remap_caller(user_m);
|
||||
if (std::string(user_m.code) == "M128") {
|
||||
ok &= check_axis_targets(ini, user_m, "X", "-300", "300", "60.0", "400.0");
|
||||
ok &= check_axis_targets(ini, user_m, "Y", "-100", "100", "60.0", "400.0");
|
||||
ok &= check_axis_targets(ini, user_m, "Z", "-240", "0", "60.0", "400.0");
|
||||
} else {
|
||||
ok &= check_axis_targets(ini, user_m, "X", "-240", "0", "60.0", "400.0");
|
||||
ok &= check_axis_targets(ini, user_m, "Y", "-100", "100", "60.0", "400.0");
|
||||
ok &= check_axis_targets(ini, user_m, "Z", "-300", "300", "60.0", "400.0");
|
||||
}
|
||||
print_bool(std::string(user_m.code) + "_source_state_targets_from_linuxcnc", ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
const std::string ini_path = source_config_path("axis/vismach/millturn/millturn.ini");
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
const bool opened = static_cast<bool>(ini);
|
||||
print_bool("millturn_ini_open", opened);
|
||||
if (!opened) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
ok &= check_ini_value(ini, "millturn", "RS274NGC", "USER_M_PATH", "./mcodes");
|
||||
ok &= check_ini_value(ini, "millturn", "RS274NGC", "SUBROUTINE_PATH", "./remap_subs");
|
||||
ok &= check_ini_value(ini, "millturn", "DISPLAY", "PYVCP", "millturn.xml");
|
||||
ok &= check_ini_value(ini, "millturn", "HAL", "HALUI", "halui");
|
||||
ok &= check_ini_value(
|
||||
ini,
|
||||
"millturn",
|
||||
"HAL",
|
||||
"HALCMD",
|
||||
"net :kinstype-select <= motion.analog-out-03 => motion.switchkins-type");
|
||||
|
||||
const UserMCase cases[] = {
|
||||
{
|
||||
"M128",
|
||||
"M428",
|
||||
"axis/vismach/millturn/mcodes/M128",
|
||||
"axis/vismach/millturn/remap_subs/428remap.ngc",
|
||||
"mill",
|
||||
"kinstype.is-0",
|
||||
"0",
|
||||
"G59.1",
|
||||
"P7",
|
||||
"X-290 Y0 Z-160 A0",
|
||||
"MIN_LIMIT",
|
||||
"MAX_LIMIT",
|
||||
},
|
||||
{
|
||||
"M129",
|
||||
"M429",
|
||||
"axis/vismach/millturn/mcodes/M129",
|
||||
"axis/vismach/millturn/remap_subs/429remap.ngc",
|
||||
"turn",
|
||||
"kinstype.is-1",
|
||||
"1",
|
||||
"G59.2",
|
||||
"P8",
|
||||
"X-160 Y0 Z-290 A0",
|
||||
"MIN_LIMIT_TURN",
|
||||
"MAX_LIMIT_TURN",
|
||||
},
|
||||
};
|
||||
|
||||
for (const auto &user_m : cases) {
|
||||
std::cout << user_m.code << "_state_mode=" << user_m.state_mode << "\n";
|
||||
std::cout << user_m.code << "_guard_pin=" << user_m.guard_pin << "\n";
|
||||
ok &= check_user_m_case(ini, user_m);
|
||||
}
|
||||
|
||||
print_bool("millturn_user_m_native_source_state_proof", ok);
|
||||
print_bool("millturn_user_m_execution_enabled", false);
|
||||
print_bool("millturn_user_m_promotion_allowed", false);
|
||||
return ok ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
|
||||
#ifndef LINUXCNC_SOURCE_DIR
|
||||
#error "LINUXCNC_SOURCE_DIR must point at the LinuxCNC source tree"
|
||||
#endif
|
||||
|
||||
#ifndef LINUXCNC_SOURCE_CONFIG_DIR
|
||||
#error "LINUXCNC_SOURCE_CONFIG_DIR must point at the LinuxCNC configs/sim tree"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
std::string source_path(const std::string &rel)
|
||||
{
|
||||
return std::string(LINUXCNC_SOURCE_DIR) + "/" + rel;
|
||||
}
|
||||
|
||||
std::string source_config_path(const std::string &rel)
|
||||
{
|
||||
return std::string(LINUXCNC_SOURCE_CONFIG_DIR) + "/" + rel;
|
||||
}
|
||||
|
||||
std::string read_text(const std::string &path)
|
||||
{
|
||||
std::ifstream input(path);
|
||||
return std::string(
|
||||
std::istreambuf_iterator<char>(input),
|
||||
std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
bool contains(const std::string &text, const std::string &needle)
|
||||
{
|
||||
return text.find(needle) != std::string::npos;
|
||||
}
|
||||
|
||||
void print_bool(const std::string &name, bool value)
|
||||
{
|
||||
std::cout << name << "=" << (value ? 1 : 0) << "\n";
|
||||
}
|
||||
|
||||
bool check_python_runtime_owner()
|
||||
{
|
||||
const std::string interp_python = read_text(source_path("src/emc/rs274ngc/interp_python.cc"));
|
||||
const std::string python_plugin = read_text(source_path("src/emc/pythonplugin/python_plugin.cc"));
|
||||
|
||||
const bool pycall_dispatch =
|
||||
contains(interp_python, "PythonPlugin *python_plugin") &&
|
||||
contains(interp_python, "Interp::pycall") &&
|
||||
contains(interp_python, "python_plugin->call(module,funcname");
|
||||
const bool remap_phases =
|
||||
contains(interp_python, "PY_PROLOG") &&
|
||||
contains(interp_python, "PY_BODY") &&
|
||||
contains(interp_python, "PY_EPILOG");
|
||||
const bool generator_finish =
|
||||
contains(interp_python, "PyGen_Check(retval.ptr())") &&
|
||||
contains(interp_python, "PY_FINISH_PROLOG") &&
|
||||
contains(interp_python, "PY_FINISH_EPILOG");
|
||||
const bool callable_lookup =
|
||||
contains(interp_python, "Interp::is_pycallable") &&
|
||||
contains(interp_python, "python_plugin->is_callable(module,funcname)");
|
||||
const bool execute_runtime =
|
||||
contains(interp_python, "Interp::py_execute") &&
|
||||
contains(interp_python, "python_plugin->run_string(cmd, retval, as_file)");
|
||||
|
||||
const bool initializes_python =
|
||||
contains(python_plugin, "PyConfig_InitPythonConfig(&config)") &&
|
||||
contains(python_plugin, "Py_InitializeFromConfig(&config)");
|
||||
const bool toplevel_exec =
|
||||
contains(python_plugin, "findString(\"TOPLEVEL\", section)") &&
|
||||
contains(python_plugin, "bp::exec_file(abs_path, main_namespace, main_namespace)");
|
||||
const bool ini_python_path =
|
||||
contains(python_plugin, "findString(n, \"PATH_PREPEND\", \"PYTHON\")") &&
|
||||
contains(python_plugin, "findString(n, \"PATH_APPEND\", \"PYTHON\")") &&
|
||||
contains(python_plugin, "PyRun_SimpleString(pycmd)");
|
||||
const bool callable_invoke =
|
||||
contains(python_plugin, "PythonPlugin::call") &&
|
||||
contains(python_plugin, "PyObject_Call(function.ptr(), tupleargs.ptr(), kwargs.ptr())");
|
||||
const bool reload_on_change =
|
||||
contains(python_plugin, "PythonPlugin::reload") &&
|
||||
contains(python_plugin, "reload_on_change");
|
||||
|
||||
print_bool("python_runtime_pycall_dispatch", pycall_dispatch);
|
||||
print_bool("python_runtime_remap_phases", remap_phases);
|
||||
print_bool("python_runtime_generator_finish", generator_finish);
|
||||
print_bool("python_runtime_callable_lookup", callable_lookup);
|
||||
print_bool("python_runtime_execute_runtime", execute_runtime);
|
||||
print_bool("python_plugin_initializes_python", initializes_python);
|
||||
print_bool("python_plugin_toplevel_exec_file", toplevel_exec);
|
||||
print_bool("python_plugin_ini_python_path", ini_python_path);
|
||||
print_bool("python_plugin_callable_invoke", callable_invoke);
|
||||
print_bool("python_plugin_reload_on_change", reload_on_change);
|
||||
|
||||
return pycall_dispatch && remap_phases && generator_finish && callable_lookup &&
|
||||
execute_runtime && initializes_python && toplevel_exec && ini_python_path &&
|
||||
callable_invoke && reload_on_change;
|
||||
}
|
||||
|
||||
bool check_axis_laser_family()
|
||||
{
|
||||
const std::string ini = read_text(source_config_path("axis/laser/laser.ini"));
|
||||
const std::string remap = read_text(source_config_path("axis/laser/python/remap.py"));
|
||||
const bool ok =
|
||||
contains(ini, "REMAP= M10 modalgroup=10 python=rasterStop") &&
|
||||
contains(ini, "REMAP= M11 modalgroup=10 python=rasterBegin") &&
|
||||
contains(ini, "REMAP= M12 modalgroup=10 python=rasterData") &&
|
||||
contains(ini, "REMAP= M13 modalgroup=10 python=rasterStart") &&
|
||||
contains(ini, "TOPLEVEL=python/toplevel.py") &&
|
||||
contains(ini, "PATH_APPEND=python") &&
|
||||
contains(remap, "def rasterBegin(self, **words):") &&
|
||||
contains(remap, "def rasterData(self, **words):") &&
|
||||
contains(remap, "def rasterStart(self, **words):") &&
|
||||
contains(remap, "def rasterStop(self, **words):");
|
||||
print_bool("python_family_axis_laser_inventory", ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool check_twp_nutating_family()
|
||||
{
|
||||
const std::string ini = read_text(source_config_path(
|
||||
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini"));
|
||||
const std::string remap = read_text(source_config_path(
|
||||
"axis/vismach/5axis/table-rotary_spindle-rotary-nutating/python/remap.py"));
|
||||
const bool ok =
|
||||
contains(ini, "REMAP = G68.2") &&
|
||||
contains(ini, "python=g682") &&
|
||||
contains(ini, "REMAP = G68.3") &&
|
||||
contains(ini, "python=g683") &&
|
||||
contains(ini, "REMAP = G68.4") &&
|
||||
contains(ini, "python=g684") &&
|
||||
contains(ini, "python=g53x_core") &&
|
||||
contains(ini, "python=g69_core") &&
|
||||
contains(ini, "PATH_APPEND = ../python") &&
|
||||
contains(ini, "TOPLEVEL = ../python/toplevel.py") &&
|
||||
contains(remap, "def g53x_core(self):") &&
|
||||
contains(remap, "def g69_core(self):") &&
|
||||
contains(remap, "def g682(self, **words):") &&
|
||||
contains(remap, "def g683(self, **words):") &&
|
||||
contains(remap, "def g684(self, **words):");
|
||||
print_bool("python_family_twp_nutating_inventory", ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool check_cycle_and_stdglue_families()
|
||||
{
|
||||
const std::string cycle_ini = read_text(source_config_path("axis/remap/cycle/cycle.ini"));
|
||||
const std::string cycle_remap = read_text(source_config_path("axis/remap/cycle/python/remap.py"));
|
||||
const std::string stdglue = read_text(source_config_path(
|
||||
"axis/nc_files/remap_lib/python-stdglue/stdglue.py"));
|
||||
const std::string gmoccapy_stdglue = read_text(source_config_path("gmoccapy/python/stdglue.py"));
|
||||
|
||||
const bool cycle_ok =
|
||||
contains(cycle_ini, "python=g842") &&
|
||||
contains(cycle_ini, "prolog=cycle_prolog") &&
|
||||
contains(cycle_ini, "epilog=cycle_epilog") &&
|
||||
contains(cycle_ini, "PATH_PREPEND=./python") &&
|
||||
contains(cycle_ini, "PATH_APPEND=../../nc_files/remap_lib/python-stdglue") &&
|
||||
contains(cycle_remap, "def g842(self,**words):") &&
|
||||
contains(stdglue, "def cycle_prolog(self,**words):") &&
|
||||
contains(stdglue, "def cycle_epilog(self,**words):");
|
||||
|
||||
const bool gmoccapy_ok =
|
||||
contains(gmoccapy_stdglue, "def settool_prolog(self,**words):") &&
|
||||
contains(gmoccapy_stdglue, "def settool_epilog(self,**words):") &&
|
||||
contains(gmoccapy_stdglue, "def change_prolog(self, **words):") &&
|
||||
contains(gmoccapy_stdglue, "def change_epilog(self, **words):");
|
||||
|
||||
print_bool("python_family_axis_remap_cycle_inventory", cycle_ok);
|
||||
print_bool("python_family_gmoccapy_stdglue_inventory", gmoccapy_ok);
|
||||
return cycle_ok && gmoccapy_ok;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
bool ok = true;
|
||||
ok &= check_python_runtime_owner();
|
||||
ok &= check_axis_laser_family();
|
||||
ok &= check_twp_nutating_family();
|
||||
ok &= check_cycle_and_stdglue_families();
|
||||
|
||||
print_bool("python_remap_native_source_inventory_proof", ok);
|
||||
print_bool("python_remap_execution_enabled", false);
|
||||
print_bool("python_remap_promotion_allowed", false);
|
||||
return ok ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
namespace standalone {
|
||||
|
||||
void reset_external_axis_mask();
|
||||
void set_external_axis_mask(int axis_mask);
|
||||
int external_axis_mask();
|
||||
void reset_parameter_file_name();
|
||||
void set_parameter_file_name(const char *filename);
|
||||
void get_parameter_file_name(char *filename, int max_size);
|
||||
|
||||
} // namespace standalone
|
||||
@@ -0,0 +1,177 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
|
||||
#include "emc/ini/inifile.hh"
|
||||
|
||||
#ifndef LINUXCNC_SOURCE_DIR
|
||||
#error "LINUXCNC_SOURCE_DIR must point at the LinuxCNC source tree"
|
||||
#endif
|
||||
|
||||
#ifndef LINUXCNC_SOURCE_CONFIG_DIR
|
||||
#error "LINUXCNC_SOURCE_CONFIG_DIR must point at the LinuxCNC configs/sim tree"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
std::string source_path(const std::string &rel)
|
||||
{
|
||||
return std::string(LINUXCNC_SOURCE_DIR) + "/" + rel;
|
||||
}
|
||||
|
||||
std::string source_config_path(const std::string &rel)
|
||||
{
|
||||
return std::string(LINUXCNC_SOURCE_CONFIG_DIR) + "/" + rel;
|
||||
}
|
||||
|
||||
std::string read_text(const std::string &path)
|
||||
{
|
||||
std::ifstream input(path);
|
||||
return std::string(
|
||||
std::istreambuf_iterator<char>(input),
|
||||
std::istreambuf_iterator<char>());
|
||||
}
|
||||
|
||||
bool contains(const std::string &text, const std::string &needle)
|
||||
{
|
||||
return text.find(needle) != std::string::npos;
|
||||
}
|
||||
|
||||
void print_bool(const std::string &name, bool value)
|
||||
{
|
||||
std::cout << name << "=" << (value ? 1 : 0) << "\n";
|
||||
}
|
||||
|
||||
bool check_ini_value(
|
||||
const linuxcnc::IniFile &ini,
|
||||
const std::string &label,
|
||||
const std::string §ion,
|
||||
const std::string &tag,
|
||||
const std::string &expected)
|
||||
{
|
||||
const auto actual = ini.findString(tag, section);
|
||||
std::cout << label << "_" << section << "." << tag << "="
|
||||
<< (actual ? *actual : "<missing>") << "\n";
|
||||
const bool ok = actual && *actual == expected;
|
||||
print_bool(label + "_" + section + "." + tag + "_ok", ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool check_task_owner()
|
||||
{
|
||||
const std::string text = read_text(source_path("src/emc/task/taskclass.cc"));
|
||||
const bool db_init =
|
||||
contains(text, "findString(\"DB_PROGRAM\", \"EMCIO\")") &&
|
||||
contains(text, "tooldata_set_db(db_mode)") &&
|
||||
contains(text, "tooldata_db_init(db_program, random_toolchanger)");
|
||||
const bool tbl_ignored = contains(text, "DB_PROGRAM active: IGNORING tool table file");
|
||||
const bool load_unload_notify =
|
||||
contains(text, "tooldata_db_notify(SPINDLE_UNLOAD") &&
|
||||
contains(text, "tooldata_db_notify(SPINDLE_LOAD");
|
||||
|
||||
print_bool("tool_db_task_db_mode_init", db_init);
|
||||
print_bool("tool_db_task_ignores_tool_table_with_db_program", tbl_ignored);
|
||||
print_bool("tool_db_task_load_unload_notify", load_unload_notify);
|
||||
return db_init && tbl_ignored && load_unload_notify;
|
||||
}
|
||||
|
||||
bool check_tooldata_db_owner()
|
||||
{
|
||||
const std::string text = read_text(source_path("src/emc/tooldata/tooldata_db.cc"));
|
||||
const bool child_process =
|
||||
contains(text, "pipe2(pipes[PARENT_READ_PIPE]") &&
|
||||
contains(text, "pipe2(pipes[PARENT_WRITE_PIPE]") &&
|
||||
contains(text, "fork()") &&
|
||||
contains(text, "execv(myargv[0], myargv)");
|
||||
const bool executable_check =
|
||||
contains(text, "access(child_argv[0],X_OK)") &&
|
||||
contains(text, "not executable");
|
||||
const bool version_handshake =
|
||||
contains(text, "#define DB_VERSION \"v2.1\"") &&
|
||||
contains(text, "read_reply(reply,sizeof(reply))") &&
|
||||
contains(text, "strncmp(reply,DB_VERSION");
|
||||
const bool getall =
|
||||
contains(text, "send_request((char*)\"g\\n\")") &&
|
||||
contains(text, "tooldata_reset()") &&
|
||||
contains(text, "strstr(reply,\"FINI\")") &&
|
||||
contains(text, "tooldata_read_entry(reply)");
|
||||
const bool notify =
|
||||
contains(text, "snprintf(msg,sizeof(msg),\"l %s\\n\",buffer)") &&
|
||||
contains(text, "snprintf(msg,sizeof(msg),\"u %s\\n\",buffer)") &&
|
||||
contains(text, "snprintf(msg,sizeof(msg),\"p %s\\n\",buffer)") &&
|
||||
contains(text, "send_and_verify(msg)");
|
||||
|
||||
print_bool("tool_db_child_process_boundary", child_process);
|
||||
print_bool("tool_db_program_executable_check", executable_check);
|
||||
print_bool("tool_db_v2_1_handshake", version_handshake);
|
||||
print_bool("tool_db_getall_g_until_fini", getall);
|
||||
print_bool("tool_db_notify_l_u_p_protocol", notify);
|
||||
return child_process && executable_check && version_handshake && getall && notify;
|
||||
}
|
||||
|
||||
bool check_db_program_owner()
|
||||
{
|
||||
const std::string text = read_text(source_config_path("axis/db_demo/db.py"));
|
||||
const bool imports =
|
||||
contains(text, "from tooldb import tooldb_callbacks") &&
|
||||
contains(text, "from tooldb import tooldb_tools") &&
|
||||
contains(text, "from tooldb import tooldb_loop");
|
||||
const bool callbacks =
|
||||
contains(text, "def user_get_tool(tno):") &&
|
||||
contains(text, "def user_put_tool(tno,params):") &&
|
||||
contains(text, "def user_load_spindle_nonran_tc(tno,params):") &&
|
||||
contains(text, "def user_unload_spindle_nonran_tc(tno,params):") &&
|
||||
contains(text, "tooldb_callbacks(user_get_tool") &&
|
||||
contains(text, "tooldb_tools(toollist)") &&
|
||||
contains(text, "tooldb_loop()");
|
||||
const bool state_targets =
|
||||
contains(text, "db_nonran_savefile = \"/tmp/db_nonran_file\"") &&
|
||||
contains(text, "toolno_min = 10") &&
|
||||
contains(text, "toolno_max = 19") &&
|
||||
contains(text, "pocket_offset = 100") &&
|
||||
contains(text, "return tno+pocket_offset") &&
|
||||
contains(text, "D['P'] = \"0\"") &&
|
||||
contains(text, "nonran_restore_pocket(spindle_tool)") &&
|
||||
contains(text, "linuxcnc.command().load_tool_table") &&
|
||||
contains(text, "save_tools_to_file(db_savefile");
|
||||
const bool reload_rules =
|
||||
contains(text, "apply_db_rules()") &&
|
||||
contains(text, "G10L0");
|
||||
|
||||
print_bool("tool_db_program_tooldb_module", imports);
|
||||
print_bool("tool_db_program_nonran_callbacks", callbacks);
|
||||
print_bool("tool_db_program_nonran_state_targets", state_targets);
|
||||
print_bool("tool_db_program_reload_rules", reload_rules);
|
||||
return imports && callbacks && state_targets && reload_rules;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
const std::string ini_path = source_config_path("axis/db_demo/db_nonran.ini");
|
||||
const std::string ini_text = read_text(ini_path);
|
||||
linuxcnc::IniFile ini(ini_path);
|
||||
const bool opened = static_cast<bool>(ini);
|
||||
print_bool("db_nonran_ini_open", opened);
|
||||
if (!opened) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
ok &= check_ini_value(ini, "db_nonran", "EMCIO", "RANDOM_TOOLCHANGER", "0");
|
||||
ok &= check_ini_value(ini, "db_nonran", "EMCIO", "DB_PROGRAM", "./db_nonran.py");
|
||||
const bool tool_table_ignored = contains(ini_text, "TOOL_TABLE= is not used with DB_PROGRAM");
|
||||
print_bool("db_nonran_tool_table_ignored_with_db_program", tool_table_ignored);
|
||||
ok &= tool_table_ignored;
|
||||
|
||||
ok &= check_task_owner();
|
||||
ok &= check_tooldata_db_owner();
|
||||
ok &= check_db_program_owner();
|
||||
|
||||
print_bool("tool_db_native_source_protocol_proof", ok);
|
||||
print_bool("tool_db_execution_enabled", false);
|
||||
print_bool("tool_db_promotion_allowed", false);
|
||||
return ok ? 0 : 1;
|
||||
}
|
||||
@@ -23,8 +23,11 @@ Use `src/index.js` for stable imports:
|
||||
|
||||
```js
|
||||
import {
|
||||
analyzeIniRuntimeBoundaries,
|
||||
createLinuxCncIniSdk,
|
||||
createLinuxCncInterpSdk,
|
||||
planIniFileContextStaging,
|
||||
planSimConfigStaging,
|
||||
} from "./src/index.js";
|
||||
```
|
||||
|
||||
@@ -39,7 +42,11 @@ vendored LinuxCNC RS274NGC sources and exposes:
|
||||
- `runProgramWithIni(programText, iniPath)`
|
||||
- `runFile(path)`
|
||||
- `runFileWithIni(path, iniPath)`
|
||||
- `runFileWithIniContinueOnError(path, iniPath)`
|
||||
- `runSimConfigProgram({ iniPath, programPath, files, executionMode })`
|
||||
- `runFiveAxisRemapFile(path, iniPath)`
|
||||
- `runRemapFile(path, iniPath)`
|
||||
- `runRemapIoMdiSequence(iniPath)`
|
||||
- `restoreParameters(path)`
|
||||
- `saveParameters(path, values)`
|
||||
- `loadToolTable(path, options)`
|
||||
@@ -55,3 +62,64 @@ LinuxCNC random-toolchanger branch before calling vendored
|
||||
sample-machine remap validation. Callers provide INI/remap/demo files in the
|
||||
WASM filesystem; the C ABI runs vendored LinuxCNC `REMAP`, O-word, file
|
||||
execution, and HAL adapter paths.
|
||||
|
||||
`runRemapFile()` is the generic remap-test equivalent for vendored LinuxCNC
|
||||
tests such as `tests/remap/duplicate-o-word`. It reads LinuxCNC INI
|
||||
`SUBROUTINE_PATH` and `REMAP` entries, then calls vendored LinuxCNC
|
||||
`Interp::parse_remap()`, `open()`, `read()`, and `execute()`.
|
||||
|
||||
`runRemapIoMdiSequence()` is a narrow regression boundary for the NGC-only
|
||||
branch of vendored LinuxCNC `tests/remap/remap-io/test-ngc.ini`. The C ABI
|
||||
reads LinuxCNC `REMAP` entries, then feeds the upstream test driver's MDI
|
||||
command sequence into vendored `Interp::execute()`. JavaScript only copies the
|
||||
vendored INI/subroutine files into the WASM filesystem and forwards the INI
|
||||
path; it does not implement M62-M68, M66 input, or remap semantics.
|
||||
|
||||
`runFileWithIni()` is the plain interpreter-file equivalent for upstream tests
|
||||
that need LinuxCNC INI search paths, such as
|
||||
`tests/interp/sub-call-from-sub`. The C ABI reads LinuxCNC
|
||||
`SUBROUTINE_PATH` and then calls vendored `Interp::open()`, `read()`, and
|
||||
`execute()`; the SDK only copies files and forwards paths.
|
||||
|
||||
`runSimConfigProgram()` is a convenience host boundary for representative
|
||||
LinuxCNC `configs/sim` programs. Callers provide `{ path, text, executable }`
|
||||
file entries, an INI path, and a program path. The SDK writes those text files
|
||||
into the Emscripten filesystem, applies executable bits for user M-code files,
|
||||
then forwards to `runFileWithIni()` by default or to `runFiveAxisRemapFile()`
|
||||
when `executionMode: "fiveAxisRemap"` is explicitly requested.
|
||||
|
||||
`planIniFileContextStaging()` is the generic file-staging planner for
|
||||
LinuxCNC INI-driven interpreter runs. Callers provide vendored source-manifest
|
||||
text, a source root, an INI file name, INI text, and a target WASM directory.
|
||||
The planner reads only INI file references and the manifest, then returns the
|
||||
INI, `[DISPLAY]OPEN_FILE`, `[EMCIO]TOOL_TABLE`,
|
||||
`[RS274NGC]PARAMETER_FILE`, `[RS274NGC]SUBROUTINE_PATH`,
|
||||
`[RS274NGC]USER_M_PATH`, and remap-NGC files that should be copied into the
|
||||
Emscripten filesystem. Each planned file includes `sourceRel`, `wasmPath`, the
|
||||
legacy alias `path`, and `executable`. It does not implement G-code, tool,
|
||||
parameter, remap, or user-M semantics.
|
||||
|
||||
`planSimConfigStaging()` is the companion convenience wrapper for
|
||||
representative LinuxCNC `configs/sim` programs. Callers provide the vendored
|
||||
source manifest text, machine relative path, INI file name, and INI text. It
|
||||
uses `planIniFileContextStaging()` with a `configs/sim/<machine>` source root
|
||||
and `configs/sim` upward-search boundary.
|
||||
|
||||
`analyzeIniRuntimeBoundaries()` is a host-boundary classifier for LinuxCNC
|
||||
INI-driven runs. It reads INI text plus optional execution text and manifest
|
||||
text, then reports declared HAL, UI, HALUI MDI, Python, tool-database, and
|
||||
external user-M process dependencies. User-M accounting is per execution code:
|
||||
`executionCodes` lists `M100..M199` codes seen in the supplied execution text,
|
||||
while `unstagedExecutionCodes` lists the subset not backed by vendored
|
||||
`USER_M_PATH` files. Python accounting keeps UI/DB references separate from
|
||||
Python remap runtime references, so a UI handler or DB program does not imply
|
||||
Python-remap coverage. It also returns the currently recommended Layer 4
|
||||
blocked kind for hard process boundaries such as `L4-TOOL-DB` and
|
||||
`L4-USER-M-PROCESS`. The classifier is policy/accounting only: it does not
|
||||
execute HAL, task, UI, Python, user-M, or tool-database behavior and does not
|
||||
change interpreter semantics.
|
||||
|
||||
`runFileWithIniContinueOnError()` uses the same LinuxCNC-backed file execution
|
||||
path but keeps the runner loop going after LinuxCNC reports an error, matching
|
||||
upstream `rs274 -n 0` regression tests such as `tests/interp/oword-unwind`.
|
||||
It does not implement or reinterpret LinuxCNC error semantics.
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
export { createLinuxCncIniSdk } from "./linuxcnc-ini.js";
|
||||
export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js";
|
||||
export {
|
||||
analyzeIniRuntimeBoundaries,
|
||||
planIniFileContextStaging,
|
||||
planSimConfigStaging,
|
||||
} from "./sim-config-staging.js";
|
||||
|
||||
@@ -58,6 +58,34 @@ export async function createLinuxCncInterpSdk(moduleOptions = {}) {
|
||||
return mod.FS.readFile(path, { encoding: "utf8" });
|
||||
},
|
||||
|
||||
runSimConfigProgram({
|
||||
iniPath,
|
||||
programPath,
|
||||
files = [],
|
||||
executionMode = "fileWithIni",
|
||||
}) {
|
||||
for (const file of files) {
|
||||
ensureParentPath(mod, file.path);
|
||||
mod.FS.writeFile(file.path, file.text, { encoding: "utf8" });
|
||||
if (file.executable) {
|
||||
mod.FS.chmod(file.path, 0o755);
|
||||
}
|
||||
}
|
||||
|
||||
if (executionMode === "fiveAxisRemap") {
|
||||
return callStringResult(
|
||||
mod,
|
||||
"lcinterp_run_fiveaxis_remap_file",
|
||||
programPath,
|
||||
iniPath,
|
||||
);
|
||||
}
|
||||
if (executionMode === "fileWithIni") {
|
||||
return callStringResult(mod, "lcinterp_run_file_with_ini", programPath, iniPath);
|
||||
}
|
||||
throw new Error(`unsupported sim config execution mode: ${executionMode}`);
|
||||
},
|
||||
|
||||
runProgram(programText) {
|
||||
return callStringResult(mod, "lcinterp_run_program", programText);
|
||||
},
|
||||
@@ -74,10 +102,31 @@ export async function createLinuxCncInterpSdk(moduleOptions = {}) {
|
||||
return callStringResult(mod, "lcinterp_run_file_with_ini", path, iniPath);
|
||||
},
|
||||
|
||||
runFileWithIniContinueOnError(path, iniPath) {
|
||||
return callStringResult(
|
||||
mod,
|
||||
"lcinterp_run_file_with_ini_continue_on_error",
|
||||
path,
|
||||
iniPath,
|
||||
);
|
||||
},
|
||||
|
||||
runFiveAxisRemapFile(path, iniPath) {
|
||||
return callStringResult(mod, "lcinterp_run_fiveaxis_remap_file", path, iniPath);
|
||||
},
|
||||
|
||||
runRemapFile(path, iniPath) {
|
||||
return callStringResult(mod, "lcinterp_run_remap_file", path, iniPath);
|
||||
},
|
||||
|
||||
runRemapFileContinueOnError(path, iniPath) {
|
||||
return callStringResult(mod, "lcinterp_run_remap_file_continue_on_error", path, iniPath);
|
||||
},
|
||||
|
||||
runRemapIoMdiSequence(iniPath) {
|
||||
return callStringResult(mod, "lcinterp_run_remap_io_mdi_sequence", iniPath);
|
||||
},
|
||||
|
||||
restoreParameters(path) {
|
||||
return callStringResult(mod, "lcinterp_restore_parameters", path);
|
||||
},
|
||||
|
||||
486
wasm-port/runtime/sdk/src/sim-config-staging.js
Normal file
486
wasm-port/runtime/sdk/src/sim-config-staging.js
Normal file
@@ -0,0 +1,486 @@
|
||||
function cleanManifest(manifestText) {
|
||||
return manifestText
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => line && !line.startsWith("#"));
|
||||
}
|
||||
|
||||
function normalizeRel(path) {
|
||||
const parts = [];
|
||||
for (const part of path.split("/")) {
|
||||
if (!part || part === ".") {
|
||||
continue;
|
||||
}
|
||||
if (part === "..") {
|
||||
parts.pop();
|
||||
continue;
|
||||
}
|
||||
parts.push(part);
|
||||
}
|
||||
return parts.join("/");
|
||||
}
|
||||
|
||||
function dirname(path) {
|
||||
const clean = normalizeRel(path);
|
||||
const index = clean.lastIndexOf("/");
|
||||
return index === -1 ? "" : clean.slice(0, index);
|
||||
}
|
||||
|
||||
function basename(path) {
|
||||
const clean = normalizeRel(path);
|
||||
const index = clean.lastIndexOf("/");
|
||||
return index === -1 ? clean : clean.slice(index + 1);
|
||||
}
|
||||
|
||||
function stripIniComment(line) {
|
||||
const hash = line.indexOf("#");
|
||||
const semicolon = line.indexOf(";");
|
||||
const indexes = [hash, semicolon].filter((index) => index >= 0);
|
||||
if (indexes.length === 0) {
|
||||
return line;
|
||||
}
|
||||
return line.slice(0, Math.min(...indexes));
|
||||
}
|
||||
|
||||
function parseIni(iniText) {
|
||||
const values = new Map();
|
||||
let section = "";
|
||||
for (const rawLine of iniText.split("\n")) {
|
||||
const sectionMatch = rawLine.match(/^\s*\[([^\]]+)\]/);
|
||||
if (sectionMatch) {
|
||||
section = sectionMatch[1].trim().toUpperCase();
|
||||
continue;
|
||||
}
|
||||
|
||||
const line = stripIniComment(rawLine);
|
||||
const equals = line.indexOf("=");
|
||||
if (equals === -1 || !section) {
|
||||
continue;
|
||||
}
|
||||
const key = line.slice(0, equals).trim().toUpperCase();
|
||||
const value = line.slice(equals + 1).trim();
|
||||
if (!key || !value) {
|
||||
continue;
|
||||
}
|
||||
const mapKey = `${section}.${key}`;
|
||||
const existing = values.get(mapKey) ?? [];
|
||||
existing.push(value);
|
||||
values.set(mapKey, existing);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
function firstIniValue(values, section, key) {
|
||||
return values.get(`${section.toUpperCase()}.${key.toUpperCase()}`)?.[0] ?? null;
|
||||
}
|
||||
|
||||
function allIniValues(values, section, key) {
|
||||
return values.get(`${section.toUpperCase()}.${key.toUpperCase()}`) ?? [];
|
||||
}
|
||||
|
||||
function splitSearchPath(value) {
|
||||
return value.split(":").map((entry) => entry.trim()).filter(Boolean);
|
||||
}
|
||||
|
||||
function sourceRelFor(machineRel, path) {
|
||||
return normalizeRel(`configs/sim/${machineRel}/${path}`);
|
||||
}
|
||||
|
||||
function targetPathFor(wasmDir, targetRel) {
|
||||
return `${wasmDir}/${normalizeRel(targetRel)}`;
|
||||
}
|
||||
|
||||
function isUserMCodePath(path) {
|
||||
return /^M1\d\d$/i.test(basename(path));
|
||||
}
|
||||
|
||||
function isSubroutinePath(path) {
|
||||
return path.toLowerCase().endsWith(".ngc");
|
||||
}
|
||||
|
||||
function findUpwardByBasename(manifestSet, sourceDir, fileName, searchRootRel) {
|
||||
let current = sourceDir;
|
||||
while (!searchRootRel || current === searchRootRel || current.startsWith(`${searchRootRel}/`)) {
|
||||
const candidate = normalizeRel(`${current}/${fileName}`);
|
||||
if (manifestSet.has(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
if (!current || current === searchRootRel) {
|
||||
break;
|
||||
}
|
||||
current = dirname(current);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function sourceForReference(manifestSet, sourceDir, reference, searchRootRel) {
|
||||
if (!reference || reference.startsWith("/")) {
|
||||
return null;
|
||||
}
|
||||
const sourceRel = normalizeRel(`${sourceDir}/${reference}`);
|
||||
if (manifestSet.has(sourceRel)) {
|
||||
return sourceRel;
|
||||
}
|
||||
return findUpwardByBasename(manifestSet, sourceDir, basename(reference), searchRootRel);
|
||||
}
|
||||
|
||||
function addPlannedFile(plan, manifestSet, sourceRel, targetRel, options = {}) {
|
||||
if (!sourceRel || !manifestSet.has(sourceRel)) {
|
||||
if (options.required) {
|
||||
throw new Error(`missing vendored sim-config file: ${sourceRel}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const existing = plan.get(sourceRel);
|
||||
const executable = options.executable || isUserMCodePath(sourceRel);
|
||||
if (existing) {
|
||||
existing.executable ||= executable;
|
||||
return;
|
||||
}
|
||||
|
||||
plan.set(sourceRel, {
|
||||
sourceRel,
|
||||
wasmPath: targetPathFor(options.wasmDir, targetRel),
|
||||
path: targetPathFor(options.wasmDir, targetRel),
|
||||
executable,
|
||||
});
|
||||
}
|
||||
|
||||
function addDirectoryFiles(plan, manifestEntries, manifestSet, sourceDir, targetDir, predicate, options) {
|
||||
const prefix = sourceDir ? `${sourceDir}/` : "";
|
||||
for (const sourceRel of manifestEntries) {
|
||||
if (!sourceRel.startsWith(prefix)) {
|
||||
continue;
|
||||
}
|
||||
const childRel = sourceRel.slice(prefix.length);
|
||||
if (childRel.includes("/") || !predicate(sourceRel)) {
|
||||
continue;
|
||||
}
|
||||
addPlannedFile(plan, manifestSet, sourceRel, normalizeRel(`${targetDir}/${childRel}`), options);
|
||||
}
|
||||
}
|
||||
|
||||
function remapNgcNames(iniValues) {
|
||||
const names = [];
|
||||
for (const remap of allIniValues(iniValues, "RS274NGC", "REMAP")) {
|
||||
const match = remap.match(/(?:^|\s)ngc=([^\s]+)/i);
|
||||
if (match) {
|
||||
names.push(`${match[1]}.ngc`);
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
function sectionValues(iniValues, section) {
|
||||
const prefix = `${section.toUpperCase()}.`;
|
||||
const values = [];
|
||||
for (const [key, entries] of iniValues.entries()) {
|
||||
if (!key.startsWith(prefix)) {
|
||||
continue;
|
||||
}
|
||||
for (const value of entries) {
|
||||
values.push({ key: key.slice(prefix.length), value });
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
function valuesMatching(iniValues, section, keys) {
|
||||
const wanted = new Set(keys.map((key) => key.toUpperCase()));
|
||||
return sectionValues(iniValues, section)
|
||||
.filter((entry) => wanted.has(entry.key))
|
||||
.map((entry) => entry.value);
|
||||
}
|
||||
|
||||
function looksLikePythonReference(value) {
|
||||
return /(^|\s|=|:)["']?[^"'\s]*\.py(["'\s]|$)/i.test(value);
|
||||
}
|
||||
|
||||
function userMCodesInText(text) {
|
||||
const codes = new Set();
|
||||
for (const match of text.matchAll(/(?<![A-Za-z0-9_])M\s*(1\d\d)(?![0-9])/gi)) {
|
||||
codes.add(`M${match[1]}`);
|
||||
}
|
||||
return [...codes].sort();
|
||||
}
|
||||
|
||||
function vendoredUserMCodes({
|
||||
manifestEntries,
|
||||
sourceDir,
|
||||
normalizedSearchRoot,
|
||||
userMPathValues,
|
||||
}) {
|
||||
const manifestSet = new Set(manifestEntries);
|
||||
const executableCodes = new Set();
|
||||
for (const dirEntry of userMPathValues.flatMap(splitSearchPath)) {
|
||||
if (dirEntry.startsWith("/")) {
|
||||
continue;
|
||||
}
|
||||
const sourceUserMDir = normalizeRel(`${sourceDir}/${dirEntry}`);
|
||||
const prefix = sourceUserMDir ? `${sourceUserMDir}/` : "";
|
||||
for (const sourceRel of manifestEntries) {
|
||||
if (
|
||||
sourceRel.startsWith(prefix) &&
|
||||
!sourceRel.slice(prefix.length).includes("/") &&
|
||||
isUserMCodePath(sourceRel)
|
||||
) {
|
||||
executableCodes.add(basename(sourceRel).toUpperCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const dirEntry of userMPathValues.flatMap(splitSearchPath)) {
|
||||
if (dirEntry.startsWith("/")) {
|
||||
continue;
|
||||
}
|
||||
const sourceUserMDir = normalizeRel(`${sourceDir}/${dirEntry}`);
|
||||
for (let code = 100; code <= 199; code += 1) {
|
||||
const candidate = findUpwardByBasename(
|
||||
manifestSet,
|
||||
sourceUserMDir,
|
||||
`M${code}`,
|
||||
normalizedSearchRoot,
|
||||
);
|
||||
if (candidate) {
|
||||
executableCodes.add(`M${code}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...executableCodes].sort();
|
||||
}
|
||||
|
||||
export function analyzeIniRuntimeBoundaries({
|
||||
manifestText = "",
|
||||
sourceRootRel,
|
||||
sourceSearchRootRel = sourceRootRel,
|
||||
iniFile = "",
|
||||
iniText,
|
||||
executionTexts = [],
|
||||
}) {
|
||||
const manifestEntries = cleanManifest(manifestText);
|
||||
const iniValues = parseIni(iniText);
|
||||
const normalizedSourceRoot = normalizeRel(sourceRootRel);
|
||||
const normalizedSearchRoot = normalizeRel(sourceSearchRootRel);
|
||||
const sourceDir = normalizeRel(`${normalizedSourceRoot}/${dirname(iniFile)}`);
|
||||
const userMPathValues = allIniValues(iniValues, "RS274NGC", "USER_M_PATH");
|
||||
const halValues = valuesMatching(iniValues, "HAL", [
|
||||
"HALFILE",
|
||||
"HALCMD",
|
||||
"POSTGUI_HALFILE",
|
||||
"HALUI",
|
||||
]);
|
||||
const displayValues = valuesMatching(iniValues, "DISPLAY", [
|
||||
"DISPLAY",
|
||||
"PYVCP",
|
||||
"GLADEVCP",
|
||||
"EMBED_TAB_COMMAND",
|
||||
]);
|
||||
const halUiMdiCommands = allIniValues(iniValues, "HALUI", "MDI_COMMAND");
|
||||
const dbProgram = firstIniValue(iniValues, "EMCIO", "DB_PROGRAM");
|
||||
const remapValues = allIniValues(iniValues, "RS274NGC", "REMAP");
|
||||
const pythonRemapReferences = [
|
||||
...sectionValues(iniValues, "PYTHON").map((entry) => entry.value),
|
||||
...remapValues.filter((value) => /(?:^|\s)python=/i.test(value)),
|
||||
];
|
||||
const pythonUiReferences = [
|
||||
...displayValues.filter(looksLikePythonReference),
|
||||
...(dbProgram && looksLikePythonReference(dbProgram) ? [dbProgram] : []),
|
||||
];
|
||||
const pythonReferences = [...pythonRemapReferences, ...pythonUiReferences];
|
||||
const vendoredUserMCodeList = vendoredUserMCodes({
|
||||
manifestEntries,
|
||||
sourceDir,
|
||||
normalizedSearchRoot,
|
||||
userMPathValues,
|
||||
});
|
||||
const vendoredUserMCodeSet = new Set(vendoredUserMCodeList);
|
||||
const hasUserMPath = userMPathValues.length > 0;
|
||||
const executionUserMCodes = [...new Set(executionTexts.flatMap(userMCodesInText))].sort();
|
||||
const unstagedExecutionUserMCodes = executionUserMCodes
|
||||
.filter((code) => !vendoredUserMCodeSet.has(code));
|
||||
const hasExternalUserMUse = hasUserMPath && unstagedExecutionUserMCodes.length > 0;
|
||||
|
||||
const dependencies = [];
|
||||
if (dbProgram) {
|
||||
dependencies.push("tool_database_process");
|
||||
}
|
||||
if (halValues.length > 0) {
|
||||
dependencies.push("hal_process");
|
||||
}
|
||||
if (displayValues.some((value) => !/^axis$/i.test(value))) {
|
||||
dependencies.push("ui_process");
|
||||
}
|
||||
if (halUiMdiCommands.length > 0) {
|
||||
dependencies.push("halui_mdi_process");
|
||||
}
|
||||
if (pythonReferences.length > 0) {
|
||||
dependencies.push("python_runtime");
|
||||
}
|
||||
if (hasExternalUserMUse) {
|
||||
dependencies.push("external_user_m_process");
|
||||
}
|
||||
|
||||
let recommendedBlockedKind = "-";
|
||||
if (dbProgram) {
|
||||
recommendedBlockedKind = "L4-TOOL-DB";
|
||||
} else if (hasExternalUserMUse) {
|
||||
recommendedBlockedKind = "L4-USER-M-PROCESS";
|
||||
} else if (pythonRemapReferences.length > 0) {
|
||||
recommendedBlockedKind = "L4-PYTHON-REMAP";
|
||||
}
|
||||
|
||||
return {
|
||||
dependencies: [...new Set(dependencies)].sort(),
|
||||
recommendedBlockedKind,
|
||||
toolDatabaseProgram: dbProgram ?? "",
|
||||
halRuntime: {
|
||||
values: halValues,
|
||||
requiresProcess: halValues.length > 0,
|
||||
},
|
||||
uiRuntime: {
|
||||
values: displayValues,
|
||||
requiresProcess: displayValues.some((value) => !/^axis$/i.test(value)),
|
||||
},
|
||||
haluiRuntime: {
|
||||
mdiCommands: halUiMdiCommands,
|
||||
requiresProcess: halUiMdiCommands.length > 0,
|
||||
},
|
||||
userMRuntime: {
|
||||
paths: userMPathValues,
|
||||
vendoredExecutableCount: vendoredUserMCodeList.length,
|
||||
executionCodes: executionUserMCodes,
|
||||
unstagedExecutionCodes: unstagedExecutionUserMCodes,
|
||||
requiresExternalProcess: hasExternalUserMUse,
|
||||
},
|
||||
pythonRuntime: {
|
||||
references: pythonReferences,
|
||||
remapReferences: pythonRemapReferences,
|
||||
uiReferences: pythonUiReferences,
|
||||
requiresProcess: pythonReferences.length > 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function planSimConfigStaging({
|
||||
manifestText,
|
||||
machineRel,
|
||||
iniFile,
|
||||
iniText,
|
||||
programFile = null,
|
||||
wasmDir = `/work/sim/${machineRel}`,
|
||||
}) {
|
||||
return planIniFileContextStaging({
|
||||
manifestText,
|
||||
sourceRootRel: `configs/sim/${machineRel}`,
|
||||
sourceSearchRootRel: "configs/sim",
|
||||
iniFile,
|
||||
iniText,
|
||||
programFile,
|
||||
wasmDir,
|
||||
});
|
||||
}
|
||||
|
||||
export function planIniFileContextStaging({
|
||||
manifestText,
|
||||
sourceRootRel,
|
||||
iniFile,
|
||||
iniText,
|
||||
programFile = null,
|
||||
wasmDir,
|
||||
sourceSearchRootRel = sourceRootRel,
|
||||
}) {
|
||||
const manifestEntries = cleanManifest(manifestText);
|
||||
const manifestSet = new Set(manifestEntries);
|
||||
const iniValues = parseIni(iniText);
|
||||
const plan = new Map();
|
||||
const normalizedSourceRoot = normalizeRel(sourceRootRel);
|
||||
const normalizedSearchRoot = normalizeRel(sourceSearchRootRel);
|
||||
const sourceDir = normalizeRel(`${normalizedSourceRoot}/${dirname(iniFile)}`);
|
||||
const iniSourceRel = normalizeRel(`${normalizedSourceRoot}/${iniFile}`);
|
||||
const programReference = programFile ?? firstIniValue(iniValues, "DISPLAY", "OPEN_FILE");
|
||||
|
||||
addPlannedFile(plan, manifestSet, iniSourceRel, iniFile, { wasmDir, required: true });
|
||||
if (programReference) {
|
||||
const programSourceRel = sourceForReference(
|
||||
manifestSet,
|
||||
sourceDir,
|
||||
programReference,
|
||||
normalizedSearchRoot,
|
||||
);
|
||||
addPlannedFile(plan, manifestSet, programSourceRel, programReference, {
|
||||
wasmDir,
|
||||
required: true,
|
||||
});
|
||||
}
|
||||
|
||||
for (const [section, key] of [
|
||||
["EMCIO", "TOOL_TABLE"],
|
||||
["RS274NGC", "PARAMETER_FILE"],
|
||||
]) {
|
||||
const reference = firstIniValue(iniValues, section, key);
|
||||
if (!reference) {
|
||||
continue;
|
||||
}
|
||||
const sourceRel = sourceForReference(manifestSet, sourceDir, reference, normalizedSearchRoot);
|
||||
addPlannedFile(plan, manifestSet, sourceRel, reference, { wasmDir });
|
||||
}
|
||||
|
||||
const subroutineDirs = allIniValues(iniValues, "RS274NGC", "SUBROUTINE_PATH")
|
||||
.flatMap(splitSearchPath);
|
||||
for (const dirEntry of subroutineDirs) {
|
||||
if (dirEntry.startsWith("/")) {
|
||||
continue;
|
||||
}
|
||||
const sourceSubdir = normalizeRel(`${sourceDir}/${dirEntry}`);
|
||||
const targetSubdir = normalizeRel(dirEntry);
|
||||
addDirectoryFiles(
|
||||
plan,
|
||||
manifestEntries,
|
||||
manifestSet,
|
||||
sourceSubdir,
|
||||
targetSubdir,
|
||||
isSubroutinePath,
|
||||
{ wasmDir },
|
||||
);
|
||||
}
|
||||
|
||||
for (const dirEntry of allIniValues(iniValues, "RS274NGC", "USER_M_PATH").flatMap(splitSearchPath)) {
|
||||
if (dirEntry.startsWith("/")) {
|
||||
continue;
|
||||
}
|
||||
const sourceUserMDir = normalizeRel(`${sourceDir}/${dirEntry}`);
|
||||
const targetUserMDir = normalizeRel(dirEntry);
|
||||
addDirectoryFiles(
|
||||
plan,
|
||||
manifestEntries,
|
||||
manifestSet,
|
||||
sourceUserMDir,
|
||||
targetUserMDir,
|
||||
isUserMCodePath,
|
||||
{ wasmDir, executable: true },
|
||||
);
|
||||
}
|
||||
|
||||
for (const remapName of remapNgcNames(iniValues)) {
|
||||
for (const dirEntry of subroutineDirs) {
|
||||
const sourceRel = sourceForReference(
|
||||
manifestSet,
|
||||
normalizeRel(`${sourceDir}/${dirEntry}`),
|
||||
remapName,
|
||||
normalizedSearchRoot,
|
||||
);
|
||||
addPlannedFile(plan, manifestSet, sourceRel, normalizeRel(`${dirEntry}/${remapName}`), {
|
||||
wasmDir,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
wasmDir,
|
||||
iniPath: targetPathFor(wasmDir, iniFile),
|
||||
programPath: programReference ? targetPathFor(wasmDir, programReference) : null,
|
||||
files: [...plan.values()],
|
||||
};
|
||||
}
|
||||
@@ -67,6 +67,22 @@ const eventCountNode = document.getElementById("event-count");
|
||||
const wasmBadge = document.getElementById("wasm-badge");
|
||||
const interpBadge = document.getElementById("interp-badge");
|
||||
const opfsBadge = document.getElementById("opfs-badge");
|
||||
const runProgressNode = document.getElementById("run-progress");
|
||||
const runProgressLabelNode = document.getElementById("run-progress-label");
|
||||
const runMotionNode = document.getElementById("run-motion");
|
||||
const runLineNode = document.getElementById("run-line");
|
||||
const runStatementNode = document.getElementById("run-statement");
|
||||
const axisNodes = {
|
||||
x: document.getElementById("axis-x"),
|
||||
y: document.getElementById("axis-y"),
|
||||
z: document.getElementById("axis-z"),
|
||||
a: document.getElementById("axis-a"),
|
||||
b: document.getElementById("axis-b"),
|
||||
c: document.getElementById("axis-c"),
|
||||
u: document.getElementById("axis-u"),
|
||||
v: document.getElementById("axis-v"),
|
||||
w: document.getElementById("axis-w"),
|
||||
};
|
||||
|
||||
const fields = {
|
||||
machine: document.getElementById("field-machine"),
|
||||
@@ -88,6 +104,7 @@ let iniSdk = null;
|
||||
let interpSdk = null;
|
||||
let loadedSession = null;
|
||||
let canonicalEventText = "";
|
||||
let runPlaybackTimer = null;
|
||||
|
||||
function setBadge(node, text, className = "badge") {
|
||||
node.className = className;
|
||||
@@ -119,6 +136,155 @@ function setField(name, value) {
|
||||
fields[name].textContent = value ?? "-";
|
||||
}
|
||||
|
||||
function nextFrame() {
|
||||
return new Promise((resolve) => requestAnimationFrame(resolve));
|
||||
}
|
||||
|
||||
function formatAxisValue(value) {
|
||||
return Number.isFinite(value) ? value.toFixed(3) : "0.000";
|
||||
}
|
||||
|
||||
function clearRunPlayback() {
|
||||
if (runPlaybackTimer) {
|
||||
clearInterval(runPlaybackTimer);
|
||||
runPlaybackTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function setRunMonitor(progress, label, axes = {}, motionText = "idle", lineText = "line -", statement = "-") {
|
||||
runProgressNode.value = Math.max(0, Math.min(100, progress));
|
||||
runProgressLabelNode.textContent = label;
|
||||
runMotionNode.textContent = motionText;
|
||||
runLineNode.textContent = lineText;
|
||||
runStatementNode.textContent = statement;
|
||||
for (const [axis, node] of Object.entries(axisNodes)) {
|
||||
node.textContent = formatAxisValue(axes[axis] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
function readCanonicalNumber(line, name) {
|
||||
const match = line.match(new RegExp(`(?:^| )${name}=([-+0-9.eE]+)`));
|
||||
return match ? Number(match[1]) : null;
|
||||
}
|
||||
|
||||
function programLineMap(programText) {
|
||||
const lines = new Map();
|
||||
programText.split(/\r?\n/).forEach((line, index) => {
|
||||
lines.set(index + 1, line.trim() || "(blank)");
|
||||
});
|
||||
return lines;
|
||||
}
|
||||
|
||||
function parseRunMotion(resultText, programText) {
|
||||
const axes = { x: 0, y: 0, z: 0, a: 0, b: 0, c: 0, u: 0, v: 0, w: 0 };
|
||||
const snapshots = [];
|
||||
const sourceLines = programLineMap(programText);
|
||||
const motionByLine = new Map();
|
||||
|
||||
for (const line of resultText.split("\n")) {
|
||||
const motion = line.match(/^canon_event=(STRAIGHT_TRAVERSE|STRAIGHT_FEED|ARC_FEED)\b/);
|
||||
if (!motion) {
|
||||
continue;
|
||||
}
|
||||
const sourceLine = readCanonicalNumber(line, "line");
|
||||
if (Number.isFinite(sourceLine)) {
|
||||
motionByLine.set(sourceLine, motion[1]);
|
||||
}
|
||||
}
|
||||
|
||||
for (const line of resultText.split("\n")) {
|
||||
if (!line.startsWith("run_step phase=execute ")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const axis of Object.keys(axes)) {
|
||||
const value = readCanonicalNumber(line, axis);
|
||||
if (value !== null && Number.isFinite(value)) {
|
||||
axes[axis] = value;
|
||||
}
|
||||
}
|
||||
|
||||
const sourceLine = readCanonicalNumber(line, "line");
|
||||
const statementIndex = line.indexOf(" statement_uri=");
|
||||
const statementValue =
|
||||
statementIndex >= 0 ? line.slice(statementIndex + " statement_uri=".length).trim() : "";
|
||||
let statement = "";
|
||||
try {
|
||||
statement = statementValue ? decodeURIComponent(statementValue) : "";
|
||||
} catch {
|
||||
statement = statementValue;
|
||||
}
|
||||
snapshots.push({
|
||||
type: motionByLine.get(sourceLine) ?? "EXECUTE",
|
||||
line: sourceLine,
|
||||
statement: statement || (Number.isFinite(sourceLine) ? (sourceLines.get(sourceLine) ?? "-") : "-"),
|
||||
axes: { ...axes },
|
||||
});
|
||||
}
|
||||
|
||||
if (snapshots.length > 0) {
|
||||
return snapshots;
|
||||
}
|
||||
|
||||
for (const line of resultText.split("\n")) {
|
||||
const motion = line.match(/^canon_event=(STRAIGHT_TRAVERSE|STRAIGHT_FEED|ARC_FEED)\b/);
|
||||
if (!motion) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const axis of Object.keys(axes)) {
|
||||
const value = readCanonicalNumber(line, axis);
|
||||
if (value !== null && Number.isFinite(value)) {
|
||||
axes[axis] = value;
|
||||
}
|
||||
}
|
||||
|
||||
const sourceLine = readCanonicalNumber(line, "line");
|
||||
snapshots.push({
|
||||
type: motion[1],
|
||||
line: sourceLine,
|
||||
statement: Number.isFinite(sourceLine) ? (sourceLines.get(sourceLine) ?? "-") : "-",
|
||||
axes: { ...axes },
|
||||
});
|
||||
}
|
||||
|
||||
return snapshots;
|
||||
}
|
||||
|
||||
function showRunSnapshot(snapshot, index, total) {
|
||||
const progress = total > 0 ? Math.round(((index + 1) / total) * 100) : 0;
|
||||
const lineText = Number.isFinite(snapshot.line) ? `line ${snapshot.line}` : "line -";
|
||||
setRunMonitor(
|
||||
progress,
|
||||
`${progress}%`,
|
||||
snapshot.axes,
|
||||
`${snapshot.type} ${lineText} (${index + 1}/${total})`,
|
||||
lineText,
|
||||
snapshot.statement,
|
||||
);
|
||||
}
|
||||
|
||||
function playRunMotion(resultText, programText) {
|
||||
clearRunPlayback();
|
||||
const snapshots = parseRunMotion(resultText, programText);
|
||||
if (snapshots.length === 0) {
|
||||
setRunMonitor(100, "100%", {}, "complete; no axis motion");
|
||||
return;
|
||||
}
|
||||
|
||||
let index = 0;
|
||||
showRunSnapshot(snapshots[index], index, snapshots.length);
|
||||
runPlaybackTimer = setInterval(() => {
|
||||
index += 1;
|
||||
if (index >= snapshots.length) {
|
||||
clearRunPlayback();
|
||||
showRunSnapshot(snapshots[snapshots.length - 1], snapshots.length - 1, snapshots.length);
|
||||
return;
|
||||
}
|
||||
showRunSnapshot(snapshots[index], index, snapshots.length);
|
||||
}, 220);
|
||||
}
|
||||
|
||||
function requireIniSdk() {
|
||||
if (!iniSdk) {
|
||||
throw new Error("INI WASM module is not ready yet.");
|
||||
@@ -287,10 +453,15 @@ document.getElementById("run-gcode").addEventListener("click", async () => {
|
||||
const interp = requireInterpSdk();
|
||||
const programText = await loadGcodeProgram(GCODE_FILENAME);
|
||||
interp.writeTextFile(GCODE_WASM_FILE, programText);
|
||||
clearRunPlayback();
|
||||
setField("runStatus", "running");
|
||||
setRunMonitor(5, "running", {}, "running");
|
||||
await nextFrame();
|
||||
const result = interp.runFileWithIni(GCODE_WASM_FILE, loadedSession.ini.wasmPath);
|
||||
setField("sessionGcode", GCODE_WASM_FILE);
|
||||
setField("runStatus", "ok");
|
||||
setCanonicalEvents(result.trim());
|
||||
playRunMotion(result.trim(), programText);
|
||||
setLog(
|
||||
[
|
||||
"Ran G-code through LinuxCNC interpreter WASM.",
|
||||
@@ -302,6 +473,8 @@ document.getElementById("run-gcode").addEventListener("click", async () => {
|
||||
} catch (error) {
|
||||
setField("runStatus", "failed");
|
||||
setCanonicalEvents("");
|
||||
clearRunPlayback();
|
||||
setRunMonitor(0, "failed", {}, "failed");
|
||||
setLog(`G-code run failed: ${error.message}`, true);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -123,6 +123,53 @@
|
||||
font: 13px/1.5 "SFMono-Regular", "Consolas", monospace;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.run-monitor {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
background: #fbfdff;
|
||||
padding: 12px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
.progress-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
progress {
|
||||
width: 100%;
|
||||
height: 14px;
|
||||
accent-color: var(--accent-2);
|
||||
}
|
||||
.run-readout {
|
||||
font: 13px/1.4 "SFMono-Regular", "Consolas", monospace;
|
||||
color: var(--muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.axis-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
.axis-cell {
|
||||
border: 1px solid #edf2f6;
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
.axis-cell span:first-child {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.axis-cell span:last-child {
|
||||
font: 14px/1.2 "SFMono-Regular", "Consolas", monospace;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.events {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
@@ -154,6 +201,11 @@
|
||||
.grid { grid-template-columns: 1fr; }
|
||||
textarea { min-height: 320px; }
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.axis-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.progress-row { grid-template-columns: 1fr; }
|
||||
.run-readout { white-space: normal; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -226,6 +278,28 @@
|
||||
<dd id="field-fiveaxis-remap">-</dd>
|
||||
</dl>
|
||||
|
||||
<h2>Run Monitor</h2>
|
||||
<div class="run-monitor">
|
||||
<div class="progress-row">
|
||||
<progress id="run-progress" max="100" value="0"></progress>
|
||||
<span id="run-progress-label" class="run-readout">0%</span>
|
||||
</div>
|
||||
<div id="run-motion" class="run-readout">idle</div>
|
||||
<div id="run-line" class="run-readout">line -</div>
|
||||
<div id="run-statement" class="run-readout">-</div>
|
||||
<div class="axis-grid" id="axis-grid">
|
||||
<div class="axis-cell"><span>X</span><span id="axis-x">0.000</span></div>
|
||||
<div class="axis-cell"><span>Y</span><span id="axis-y">0.000</span></div>
|
||||
<div class="axis-cell"><span>Z</span><span id="axis-z">0.000</span></div>
|
||||
<div class="axis-cell"><span>A</span><span id="axis-a">0.000</span></div>
|
||||
<div class="axis-cell"><span>B</span><span id="axis-b">0.000</span></div>
|
||||
<div class="axis-cell"><span>C</span><span id="axis-c">0.000</span></div>
|
||||
<div class="axis-cell"><span>U</span><span id="axis-u">0.000</span></div>
|
||||
<div class="axis-cell"><span>V</span><span id="axis-v">0.000</span></div>
|
||||
<div class="axis-cell"><span>W</span><span id="axis-w">0.000</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="log" id="log"></div>
|
||||
|
||||
<h2>Canonical Events</h2>
|
||||
|
||||
@@ -344,6 +344,24 @@ TOOL_TABLE = browser-tool.tbl
|
||||
) {
|
||||
throw new Error(`UI canonical event panel missing LinuxCNC events: ${eventText}`);
|
||||
}
|
||||
await waitFor(
|
||||
() => uiDocument.getElementById("run-motion")?.textContent.includes("STRAIGHT_FEED"),
|
||||
"G-code run motion playback",
|
||||
);
|
||||
assertEqual(
|
||||
uiDocument.getElementById("run-progress-label").textContent,
|
||||
"100%",
|
||||
"UI G-code run progress",
|
||||
);
|
||||
assertEqual(uiDocument.getElementById("axis-x").textContent, "3.000", "UI G-code X axis");
|
||||
assertEqual(uiDocument.getElementById("axis-y").textContent, "4.000", "UI G-code Y axis");
|
||||
assertEqual(uiDocument.getElementById("axis-z").textContent, "0.000", "UI G-code Z axis");
|
||||
assertEqual(uiDocument.getElementById("run-line").textContent, "line 2", "UI G-code line");
|
||||
assertEqual(
|
||||
uiDocument.getElementById("run-statement").textContent,
|
||||
"G1 X3.0 Y4.0 F120.0",
|
||||
"UI G-code statement",
|
||||
);
|
||||
const eventFilter = uiDocument.getElementById("event-filter");
|
||||
eventFilter.value = "STRAIGHT_FEED";
|
||||
eventFilter.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,33 @@ set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
CHROMIUM="${CHROMIUM:-$(command -v chromium || command -v chromium-browser || command -v google-chrome || command -v google-chrome-stable || true)}"
|
||||
BOUNDARY_PROOF_GATES="$ROOT_DIR/build/wasm/sim-configs-inventory/boundary-proof-gates.tsv"
|
||||
BOUNDARY_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/boundary-summary.tsv"
|
||||
INI_BOUNDARY_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/ini-boundary-summary.tsv"
|
||||
NATIVE_PROOF_ALIGNMENT_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/native-proof-alignment-summary.tsv"
|
||||
NATIVE_RUNTIME_PROBE_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/native-runtime-probe-summary.tsv"
|
||||
RUNTIME_BOUNDARY_NATIVE_ALIGNMENT_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-native-alignment-summary.tsv"
|
||||
RUNTIME_BOUNDARY_CONTRACT_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/runtime-boundary-contract-summary.tsv"
|
||||
USER_M_PROCESS_NATIVE_STATE_ALIGNMENT="$ROOT_DIR/build/wasm/sim-configs-inventory/user-m-process-native-state-alignment.tsv"
|
||||
USER_M_PROCESS_NATIVE_TRANSITION_ALIGNMENT="$ROOT_DIR/build/wasm/sim-configs-inventory/user-m-process-native-transition-alignment.tsv"
|
||||
USER_M_PROCESS_NATIVE_RUNTIME_STATE_PLAN="$ROOT_DIR/build/wasm/sim-configs-inventory/user-m-process-native-runtime-state-plan.tsv"
|
||||
USER_M_PROCESS_NATIVE_RUNTIME_READINESS="$ROOT_DIR/build/wasm/sim-configs-inventory/user-m-process-native-runtime-readiness.tsv"
|
||||
USER_M_PROCESS_NATIVE_RUNTIME_PROBE_GATE="$ROOT_DIR/build/wasm/sim-configs-inventory/user-m-process-native-runtime-probe-gate.tsv"
|
||||
USER_M_PROCESS_TRANSITION_PLAN="$ROOT_DIR/build/wasm/sim-configs-inventory/user-m-process-transition-plan.tsv"
|
||||
TOOL_DB_PROCESS_NATIVE_PROTOCOL_ALIGNMENT="$ROOT_DIR/build/wasm/sim-configs-inventory/tool-db-process-native-protocol-alignment.tsv"
|
||||
TOOL_DB_PROCESS_NATIVE_RUNTIME_READINESS="$ROOT_DIR/build/wasm/sim-configs-inventory/tool-db-process-native-runtime-readiness.tsv"
|
||||
TOOL_DB_PROCESS_TRANSACTION_PLAN="$ROOT_DIR/build/wasm/sim-configs-inventory/tool-db-process-transaction-plan.tsv"
|
||||
PYTHON_REMAP_NATIVE_RUNTIME_ALIGNMENT="$ROOT_DIR/build/wasm/sim-configs-inventory/python-remap-native-runtime-alignment.tsv"
|
||||
PYTHON_REMAP_NATIVE_RUNTIME_READINESS="$ROOT_DIR/build/wasm/sim-configs-inventory/python-remap-native-runtime-readiness.tsv"
|
||||
PYTHON_REMAP_NATIVE_RUNTIME_STATE_PLAN="$ROOT_DIR/build/wasm/sim-configs-inventory/python-remap-native-runtime-state-plan.tsv"
|
||||
PYTHON_REMAP_NATIVE_RUNTIME_FIXTURE_PLAN="$ROOT_DIR/build/wasm/sim-configs-inventory/python-remap-native-runtime-fixture-plan.tsv"
|
||||
PYTHON_REMAP_RUNTIME_CONTRACT="$ROOT_DIR/build/wasm/sim-configs-inventory/python-remap-runtime-contract.tsv"
|
||||
BLOCKED_RUNTIME_PROMOTION_LOCK="$ROOT_DIR/build/wasm/sim-configs-inventory/blocked-runtime-promotion-lock.tsv"
|
||||
BOUNDARY_PHASE_COMPLETION_SUMMARY="$ROOT_DIR/build/wasm/sim-configs-inventory/boundary-phase-completion-summary.tsv"
|
||||
NEXT_BOUNDARY_RECOMMENDATIONS="$ROOT_DIR/build/wasm/sim-configs-inventory/next-boundary-recommendations.tsv"
|
||||
INVENTORY_SCRIPT="$ROOT_DIR/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs"
|
||||
NATIVE_SOURCE_PROOF_SUMMARY="$ROOT_DIR/build/native/native-source-proof-summary.tsv"
|
||||
NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY="$ROOT_DIR/build/native/native-runtime-probe-summary.tsv"
|
||||
|
||||
if [[ -z "$CHROMIUM" ]]; then
|
||||
echo "missing Chromium-compatible browser; set CHROMIUM=/path/to/browser" >&2
|
||||
@@ -15,6 +42,45 @@ fi
|
||||
if [[ "${SKIP_INI_BUILD:-0}" != "1" ]]; then
|
||||
"$ROOT_DIR/tools/build_ini_panel.sh"
|
||||
fi
|
||||
INVENTORY_REQUIRED=0
|
||||
for artifact in \
|
||||
"$BOUNDARY_PROOF_GATES" \
|
||||
"$BOUNDARY_SUMMARY" \
|
||||
"$INI_BOUNDARY_SUMMARY" \
|
||||
"$NATIVE_PROOF_ALIGNMENT_SUMMARY" \
|
||||
"$RUNTIME_BOUNDARY_NATIVE_ALIGNMENT_SUMMARY" \
|
||||
"$RUNTIME_BOUNDARY_CONTRACT_SUMMARY" \
|
||||
"$USER_M_PROCESS_NATIVE_STATE_ALIGNMENT" \
|
||||
"$USER_M_PROCESS_NATIVE_TRANSITION_ALIGNMENT" \
|
||||
"$USER_M_PROCESS_NATIVE_RUNTIME_STATE_PLAN" \
|
||||
"$USER_M_PROCESS_NATIVE_RUNTIME_READINESS" \
|
||||
"$USER_M_PROCESS_NATIVE_RUNTIME_PROBE_GATE" \
|
||||
"$USER_M_PROCESS_TRANSITION_PLAN" \
|
||||
"$TOOL_DB_PROCESS_NATIVE_PROTOCOL_ALIGNMENT" \
|
||||
"$TOOL_DB_PROCESS_NATIVE_RUNTIME_READINESS" \
|
||||
"$TOOL_DB_PROCESS_TRANSACTION_PLAN" \
|
||||
"$PYTHON_REMAP_NATIVE_RUNTIME_ALIGNMENT" \
|
||||
"$PYTHON_REMAP_NATIVE_RUNTIME_READINESS" \
|
||||
"$PYTHON_REMAP_NATIVE_RUNTIME_STATE_PLAN" \
|
||||
"$PYTHON_REMAP_NATIVE_RUNTIME_FIXTURE_PLAN" \
|
||||
"$PYTHON_REMAP_RUNTIME_CONTRACT" \
|
||||
"$NATIVE_RUNTIME_PROBE_SUMMARY" \
|
||||
"$BLOCKED_RUNTIME_PROMOTION_LOCK" \
|
||||
"$BOUNDARY_PHASE_COMPLETION_SUMMARY" \
|
||||
"$NEXT_BOUNDARY_RECOMMENDATIONS"; do
|
||||
if [[ ! -f "$artifact" || "$INVENTORY_SCRIPT" -nt "$artifact" ]]; then
|
||||
INVENTORY_REQUIRED=1
|
||||
fi
|
||||
done
|
||||
if [[ -f "$NATIVE_SOURCE_PROOF_SUMMARY" && "$NATIVE_SOURCE_PROOF_SUMMARY" -nt "$NATIVE_PROOF_ALIGNMENT_SUMMARY" ]]; then
|
||||
INVENTORY_REQUIRED=1
|
||||
fi
|
||||
if [[ -f "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" && "$NATIVE_RUNTIME_PROBE_SOURCE_SUMMARY" -nt "$NATIVE_RUNTIME_PROBE_SUMMARY" ]]; then
|
||||
INVENTORY_REQUIRED=1
|
||||
fi
|
||||
if [[ "$INVENTORY_REQUIRED" == "1" ]]; then
|
||||
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_sim_configs_inventory_wasm.sh"
|
||||
fi
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
PORT_FILE="$TMP_DIR/port"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
canon_event=STRAIGHT_TRAVERSE line=1 x=0 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=COMMENT: interpreter: IJK distance mode changed to incremental
|
||||
canon_event=SET_FEED_RATE rate=60
|
||||
canon_event=ARC_FEED line=3 first_end=1 second_end=1 first_axis=1 second_axis=0 rotation=-1 axis_end_point=0 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=COMMENT: interpreter: IJK distance mode changed to absolute
|
||||
|
||||
2
wasm-port/tests/fixtures/canon/ini_tool_table.events
vendored
Normal file
2
wasm-port/tests/fixtures/canon/ini_tool_table.events
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
canon_event=USE_TOOL_LENGTH_OFFSET x=0 y=0 z=6.25 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=PROGRAM_END
|
||||
4
wasm-port/tests/fixtures/canon/spindle_orient_offset.events
vendored
Normal file
4
wasm-port/tests/fixtures/canon/spindle_orient_offset.events
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
canon_event=SELECT_PLANE plane=1
|
||||
canon_event=STRAIGHT_TRAVERSE line=1 x=0 y=0 z=0 a=0 b=0 c=0 u=0 v=0 w=0
|
||||
canon_event=ORIENT_SPINDLE spindle=0 orientation=57.5 mode=2
|
||||
canon_event=WAIT_SPINDLE_ORIENT_COMPLETE spindle=0 timeout=1.5
|
||||
4
wasm-port/tests/fixtures/gcode/disable_fanuc_style_sub_m98.ngc
vendored
Normal file
4
wasm-port/tests/fixtures/gcode/disable_fanuc_style_sub_m98.ngc
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
M98 P1
|
||||
O1
|
||||
M99
|
||||
M2
|
||||
2
wasm-port/tests/fixtures/gcode/ini_tool_table.ngc
vendored
Normal file
2
wasm-port/tests/fixtures/gcode/ini_tool_table.ngc
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
G43 H2
|
||||
M2
|
||||
2
wasm-port/tests/fixtures/gcode/spindle_orient_offset.ngc
vendored
Normal file
2
wasm-port/tests/fixtures/gcode/spindle_orient_offset.ngc
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
G90 G17 G0 X0 Y0 Z0
|
||||
M19 R45 P2 Q1.5
|
||||
5
wasm-port/tests/fixtures/ini/disable_fanuc_style_sub.ini
vendored
Normal file
5
wasm-port/tests/fixtures/ini/disable_fanuc_style_sub.ini
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[RS274NGC]
|
||||
DISABLE_FANUC_STYLE_SUB = 1
|
||||
|
||||
[TRAJ]
|
||||
COORDINATES = XYZ
|
||||
6
wasm-port/tests/fixtures/ini/ini_tool_table.ini
vendored
Normal file
6
wasm-port/tests/fixtures/ini/ini_tool_table.ini
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
[TRAJ]
|
||||
LINEAR_UNITS = mm
|
||||
|
||||
[EMCIO]
|
||||
TOOL_TABLE = ini_tool_table.tbl
|
||||
RANDOM_TOOLCHANGER = 0
|
||||
1
wasm-port/tests/fixtures/ini/ini_tool_table.tbl
vendored
Normal file
1
wasm-port/tests/fixtures/ini/ini_tool_table.tbl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
T2 P7 Z6.25 D1.5 ;ini-driven tool table entry
|
||||
5
wasm-port/tests/fixtures/ini/spindle_orient_offset.ini
vendored
Normal file
5
wasm-port/tests/fixtures/ini/spindle_orient_offset.ini
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[TRAJ]
|
||||
COORDINATES = XYZ
|
||||
|
||||
[RS274NGC]
|
||||
ORIENT_OFFSET = 12.5
|
||||
@@ -71,5 +71,6 @@ export const INTERP_BROWSER_FILE_FIXTURES = INTERP_FILE_FIXTURES;
|
||||
|
||||
export const INTERP_INI_FIXTURE = "namedparam_semantics";
|
||||
export const INTERP_BASELINE_INI_FIXTURE = "namedparam_ini_semantics";
|
||||
export const INTERP_SPINDLE_ORIENT_OFFSET_FIXTURE = "spindle_orient_offset";
|
||||
export const INTERP_COORDINATE_OFFSETS_FILE_FIXTURE = "coordinate_offsets";
|
||||
export const INTERP_POSITION_PARAMS_FILE_FIXTURE = "position_params";
|
||||
|
||||
@@ -8,6 +8,9 @@ ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
"$ROOT_DIR/tools/build_tp_wasm.sh"
|
||||
SKIP_INI_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_ini_wasm.sh"
|
||||
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_interp_wasm.sh"
|
||||
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_sim_configs_wasm.sh"
|
||||
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_sim_configs_inventory_wasm.sh"
|
||||
SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_nc_files_wasm.sh"
|
||||
SKIP_TP_BUILD=1 "$ROOT_DIR/tests/wasm/node/verify_tp_wasm.sh"
|
||||
"$ROOT_DIR/tests/opfs/node/verify_file_service.sh"
|
||||
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 "$ROOT_DIR/tests/browser/verify_ini_panel_browser.sh"
|
||||
|
||||
202
wasm-port/tests/native/probe_millturn_user_m_runtime.sh
Normal file
202
wasm-port/tests/native/probe_millturn_user_m_runtime.sh
Normal file
@@ -0,0 +1,202 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
LINUXCNC_ROOT="${LINUXCNC_ROOT:-$ROOT_DIR/../linuxcnc}"
|
||||
MACHINE_DIR="$LINUXCNC_ROOT/configs/sim/axis/vismach/millturn"
|
||||
INI_FILE="$MACHINE_DIR/millturn.ini"
|
||||
RUN_DIR="$ROOT_DIR/build/native/millturn-user-m-runtime"
|
||||
LINUXCNC_STDOUT="$RUN_DIR/linuxcnc.stdout.log"
|
||||
LINUXCNC_STDERR="$RUN_DIR/linuxcnc.stderr.log"
|
||||
|
||||
required_commands=(tclsh halrun halcmd linuxcnc)
|
||||
missing_commands=()
|
||||
LINUXCNC_PID=""
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "$LINUXCNC_PID" ]]; then
|
||||
kill "$LINUXCNC_PID" 2>/dev/null || true
|
||||
wait "$LINUXCNC_PID" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
command_path() {
|
||||
command -v "$1" 2>/dev/null || true
|
||||
}
|
||||
|
||||
csv_join() {
|
||||
local IFS=,
|
||||
printf '%s' "$*"
|
||||
}
|
||||
|
||||
print_kv() {
|
||||
printf '%s=%s\n' "$1" "$2"
|
||||
}
|
||||
|
||||
runtime_requirements=()
|
||||
for command_name in "${required_commands[@]}"; do
|
||||
path="$(command_path "$command_name")"
|
||||
if [[ -n "$path" ]]; then
|
||||
runtime_requirements+=("$command_name:1")
|
||||
print_kv "millturn_user_m_runtime_${command_name}_path" "$path"
|
||||
else
|
||||
runtime_requirements+=("$command_name:0")
|
||||
missing_commands+=("$command_name")
|
||||
fi
|
||||
done
|
||||
|
||||
source_ready=1
|
||||
for rel in \
|
||||
millturn.ini \
|
||||
mcodes/M128 \
|
||||
mcodes/M129 \
|
||||
remap_subs/428remap.ngc \
|
||||
remap_subs/429remap.ngc; do
|
||||
if [[ ! -f "$MACHINE_DIR/$rel" ]]; then
|
||||
source_ready=0
|
||||
print_kv "millturn_user_m_missing_source_${rel//[^A-Za-z0-9]/_}" "$MACHINE_DIR/$rel"
|
||||
fi
|
||||
done
|
||||
|
||||
runtime_ready=0
|
||||
if [[ "${#missing_commands[@]}" -eq 0 ]]; then
|
||||
runtime_ready=1
|
||||
fi
|
||||
|
||||
print_kv millturn_user_m_runtime_requirements "$(csv_join "${runtime_requirements[@]}")"
|
||||
if [[ "${#missing_commands[@]}" -eq 0 ]]; then
|
||||
print_kv millturn_user_m_missing_requirements "-"
|
||||
else
|
||||
print_kv millturn_user_m_missing_requirements "$(csv_join "${missing_commands[@]}")"
|
||||
fi
|
||||
print_kv millturn_user_m_source_proof_ready "$source_ready"
|
||||
print_kv millturn_user_m_runtime_ready "$runtime_ready"
|
||||
print_kv millturn_user_m_execution_enabled 0
|
||||
print_kv millturn_user_m_promotion_allowed 0
|
||||
|
||||
if [[ "$source_ready" != "1" ]]; then
|
||||
print_kv millturn_user_m_runtime_probe_status "blocked_missing_source"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$runtime_ready" != "1" ]]; then
|
||||
print_kv millturn_user_m_runtime_probe_status "skipped_missing_host_runtime"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${ENABLE_MILLTURN_USER_M_RUNTIME_PROBE:-0}" != "1" ]]; then
|
||||
print_kv millturn_user_m_runtime_probe_status "ready_disabled_by_default"
|
||||
print_kv millturn_user_m_runtime_probe_note "set_ENABLE_MILLTURN_USER_M_RUNTIME_PROBE_1_to_run_full_process_probe"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "$RUN_DIR"
|
||||
|
||||
wait_for_hal_pin() {
|
||||
local pin="$1"
|
||||
local limit="${MILLTURN_USER_M_RUNTIME_WAIT_SECONDS:-30}"
|
||||
local end=$((SECONDS + limit))
|
||||
while (( SECONDS < end )); do
|
||||
if halcmd getp "$pin" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.25
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
read_hal_pin() {
|
||||
halcmd getp "$1" 2>/dev/null | tr -d '[:space:]'
|
||||
}
|
||||
|
||||
expect_hal_pin() {
|
||||
local label="$1"
|
||||
local pin="$2"
|
||||
local expected="$3"
|
||||
local actual
|
||||
actual="$(read_hal_pin "$pin")"
|
||||
print_kv "millturn_user_m_${label}_${pin//[^A-Za-z0-9]/_}" "$actual"
|
||||
[[ "$actual" == "$expected" ]]
|
||||
}
|
||||
|
||||
set_switchkins_target() {
|
||||
local target="$1"
|
||||
local guard_pin="kinstype.is-$target"
|
||||
if ! halcmd setp motion.switchkins-type "$target" >/dev/null 2>&1; then
|
||||
halcmd unlinkp motion.switchkins-type >/dev/null 2>&1 || true
|
||||
halcmd setp motion.switchkins-type "$target"
|
||||
fi
|
||||
for _ in $(seq 1 40); do
|
||||
if [[ "$(read_hal_pin "$guard_pin")" =~ ^(TRUE|1)$ ]]; then
|
||||
return 0
|
||||
fi
|
||||
sleep 0.05
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
run_user_m_case() {
|
||||
local code="$1"
|
||||
local mode="$2"
|
||||
local switchkins_target="$3"
|
||||
local expected_x_min="$4"
|
||||
local expected_x_max="$5"
|
||||
local expected_y_min="$6"
|
||||
local expected_y_max="$7"
|
||||
local expected_z_min="$8"
|
||||
local expected_z_max="$9"
|
||||
|
||||
print_kv "millturn_user_m_${code}_runtime_mode" "$mode"
|
||||
if ! set_switchkins_target "$switchkins_target"; then
|
||||
print_kv "millturn_user_m_${code}_switchkins_target_ready" 0
|
||||
return 1
|
||||
fi
|
||||
print_kv "millturn_user_m_${code}_switchkins_target_ready" 1
|
||||
|
||||
INI_FILE_NAME="$INI_FILE" "$MACHINE_DIR/mcodes/$code"
|
||||
|
||||
local ok=1
|
||||
expect_hal_pin "${code}" ini.x.min_limit "$expected_x_min" || ok=0
|
||||
expect_hal_pin "${code}" ini.x.max_limit "$expected_x_max" || ok=0
|
||||
expect_hal_pin "${code}" ini.y.min_limit "$expected_y_min" || ok=0
|
||||
expect_hal_pin "${code}" ini.y.max_limit "$expected_y_max" || ok=0
|
||||
expect_hal_pin "${code}" ini.z.min_limit "$expected_z_min" || ok=0
|
||||
expect_hal_pin "${code}" ini.z.max_limit "$expected_z_max" || ok=0
|
||||
expect_hal_pin "${code}" ini.x.max_velocity "60" || ok=0
|
||||
expect_hal_pin "${code}" ini.y.max_velocity "60" || ok=0
|
||||
expect_hal_pin "${code}" ini.z.max_velocity "60" || ok=0
|
||||
expect_hal_pin "${code}" ini.x.max_acceleration "400" || ok=0
|
||||
expect_hal_pin "${code}" ini.y.max_acceleration "400" || ok=0
|
||||
expect_hal_pin "${code}" ini.z.max_acceleration "400" || ok=0
|
||||
|
||||
print_kv "millturn_user_m_${code}_runtime_state_ok" "$ok"
|
||||
[[ "$ok" == "1" ]]
|
||||
}
|
||||
|
||||
print_kv millturn_user_m_runtime_probe_ini "$INI_FILE"
|
||||
print_kv millturn_user_m_runtime_probe_linuxcnc_stdout "$LINUXCNC_STDOUT"
|
||||
print_kv millturn_user_m_runtime_probe_linuxcnc_stderr "$LINUXCNC_STDERR"
|
||||
|
||||
linuxcnc -r "$INI_FILE" >"$LINUXCNC_STDOUT" 2>"$LINUXCNC_STDERR" &
|
||||
LINUXCNC_PID="$!"
|
||||
|
||||
if ! wait_for_hal_pin ini.x.min_limit || ! wait_for_hal_pin motion.switchkins-type; then
|
||||
print_kv millturn_user_m_runtime_probe_status "runtime_state_probe_failed"
|
||||
print_kv millturn_user_m_runtime_probe_note "linuxcnc_started_but_required_hal_pins_did_not_appear"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
probe_ok=1
|
||||
run_user_m_case M128 mill 0 -300 300 -100 100 -240 0 || probe_ok=0
|
||||
run_user_m_case M129 turn 1 -240 0 -100 100 -300 300 || probe_ok=0
|
||||
|
||||
if [[ "$probe_ok" == "1" ]]; then
|
||||
print_kv millturn_user_m_runtime_probe_status "runtime_state_probe_passed"
|
||||
print_kv millturn_user_m_runtime_probe_note "linuxcnc_owned_M128_M129_tcl_hal_state_probe_passed_without_promotion"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
print_kv millturn_user_m_runtime_probe_status "runtime_state_probe_failed"
|
||||
print_kv millturn_user_m_runtime_probe_note "linuxcnc_owned_M128_M129_tcl_hal_state_probe_failed"
|
||||
exit 1
|
||||
189
wasm-port/tests/native/probe_python_remap_runtime.sh
Executable file
189
wasm-port/tests/native/probe_python_remap_runtime.sh
Executable file
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
LINUXCNC_ROOT="${LINUXCNC_ROOT:-$ROOT_DIR/../linuxcnc}"
|
||||
FIXTURE_FAMILY="axis/remap/stop-lookahead/nc_files"
|
||||
FIXTURE_DIR="$LINUXCNC_ROOT/configs/sim/axis/remap/stop-lookahead"
|
||||
FIXTURE_INI="$FIXTURE_DIR/demo.ini"
|
||||
RUN_DIR="$ROOT_DIR/build/native/python-remap-runtime"
|
||||
PYTHON_RUNTIME_STDOUT="$RUN_DIR/python_lifecycle.stdout.log"
|
||||
PYTHON_RUNTIME_STDERR="$RUN_DIR/python_lifecycle.stderr.log"
|
||||
|
||||
required_commands=(python3 linuxcnc)
|
||||
missing_requirements=()
|
||||
|
||||
command_path() {
|
||||
command -v "$1" 2>/dev/null || true
|
||||
}
|
||||
|
||||
csv_join() {
|
||||
local IFS=,
|
||||
printf '%s' "$*"
|
||||
}
|
||||
|
||||
print_kv() {
|
||||
printf '%s=%s\n' "$1" "$2"
|
||||
}
|
||||
|
||||
runtime_requirements=()
|
||||
for command_name in "${required_commands[@]}"; do
|
||||
path="$(command_path "$command_name")"
|
||||
if [[ -n "$path" ]]; then
|
||||
runtime_requirements+=("$command_name:1")
|
||||
print_kv "python_remap_runtime_${command_name}_path" "$path"
|
||||
else
|
||||
runtime_requirements+=("$command_name:0")
|
||||
missing_requirements+=("$command_name")
|
||||
fi
|
||||
done
|
||||
|
||||
source_ready=1
|
||||
for rel in \
|
||||
src/emc/rs274ngc/interp_python.cc \
|
||||
src/emc/pythonplugin/python_plugin.cc \
|
||||
configs/sim/axis/remap/stop-lookahead/demo.ini \
|
||||
configs/sim/axis/remap/stop-lookahead/python/remap.py \
|
||||
configs/sim/axis/remap/stop-lookahead/python/toplevel.py; do
|
||||
if [[ ! -f "$LINUXCNC_ROOT/$rel" ]]; then
|
||||
source_ready=0
|
||||
missing_requirements+=("$rel")
|
||||
print_kv "python_remap_missing_source_${rel//[^A-Za-z0-9]/_}" "$LINUXCNC_ROOT/$rel"
|
||||
fi
|
||||
done
|
||||
|
||||
runtime_ready=0
|
||||
if [[ "${#missing_requirements[@]}" -eq 0 ]]; then
|
||||
runtime_ready=1
|
||||
fi
|
||||
|
||||
print_kv python_remap_runtime_fixture_family "$FIXTURE_FAMILY"
|
||||
print_kv python_remap_runtime_fixture_id "stop_lookahead_python_runtime_lifecycle"
|
||||
print_kv python_remap_runtime_fixture_scope "no_python_callables_no_ngc_only_subpaths"
|
||||
print_kv python_remap_runtime_phases "initialize_python,apply_ini_python_path,execute_toplevel,callable_lookup,pycall_dispatch,callable_invoke,remap_phase_dispatch,generator_finish,execute_python_runtime,reload_on_change"
|
||||
print_kv python_remap_runtime_modules "axis/remap/stop-lookahead/python/remap.py,axis/remap/stop-lookahead/python/toplevel.py"
|
||||
print_kv python_remap_runtime_requirements "$(csv_join "${runtime_requirements[@]}")"
|
||||
if [[ "${#missing_requirements[@]}" -eq 0 ]]; then
|
||||
print_kv python_remap_missing_requirements "-"
|
||||
else
|
||||
print_kv python_remap_missing_requirements "$(csv_join "${missing_requirements[@]}")"
|
||||
fi
|
||||
print_kv python_remap_source_proof_ready "$source_ready"
|
||||
print_kv python_remap_runtime_ready "$runtime_ready"
|
||||
print_kv python_remap_execution_enabled 0
|
||||
print_kv python_remap_promotion_allowed 0
|
||||
|
||||
if [[ "$source_ready" != "1" ]]; then
|
||||
print_kv python_remap_runtime_probe_status "blocked_missing_source"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$runtime_ready" != "1" ]]; then
|
||||
print_kv python_remap_runtime_probe_status "skipped_missing_host_runtime"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${ENABLE_PYTHON_REMAP_RUNTIME_PROBE:-0}" != "1" ]]; then
|
||||
print_kv python_remap_runtime_probe_status "ready_disabled_by_default"
|
||||
print_kv python_remap_runtime_probe_note "set_ENABLE_PYTHON_REMAP_RUNTIME_PROBE_1_to_run_python_lifecycle_probe"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "$RUN_DIR"
|
||||
rm -f "$PYTHON_RUNTIME_STDOUT" "$PYTHON_RUNTIME_STDERR"
|
||||
|
||||
print_kv python_remap_runtime_probe_fixture "$FIXTURE_DIR"
|
||||
print_kv python_remap_runtime_probe_ini "$FIXTURE_INI"
|
||||
print_kv python_remap_runtime_probe_stdout "$PYTHON_RUNTIME_STDOUT"
|
||||
print_kv python_remap_runtime_probe_stderr "$PYTHON_RUNTIME_STDERR"
|
||||
|
||||
if python3 - "$LINUXCNC_ROOT" "$FIXTURE_DIR" "$FIXTURE_INI" "$RUN_DIR" \
|
||||
>"$PYTHON_RUNTIME_STDOUT" 2>"$PYTHON_RUNTIME_STDERR" <<'PY'
|
||||
import configparser
|
||||
import importlib
|
||||
import inspect
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
linuxcnc_root = Path(sys.argv[1])
|
||||
fixture_dir = Path(sys.argv[2])
|
||||
fixture_ini = Path(sys.argv[3])
|
||||
run_dir = Path(sys.argv[4])
|
||||
|
||||
def emit(name, value):
|
||||
print(f"{name}={value}")
|
||||
|
||||
def require(condition, message):
|
||||
if not condition:
|
||||
raise RuntimeError(message)
|
||||
|
||||
interp_return = linuxcnc_root / "src/emc/nml_intf/interp_return.hh"
|
||||
match = re.search(
|
||||
r"INTERP_EXECUTE_FINISH\s*=\s*([0-9]+)",
|
||||
interp_return.read_text(encoding="utf-8"),
|
||||
)
|
||||
require(match, "missing_INTERP_EXECUTE_FINISH_source_value")
|
||||
execute_finish = int(match.group(1))
|
||||
emit("python_remap_lifecycle_execute_finish_source_value", execute_finish)
|
||||
|
||||
stub_dir = Path(tempfile.mkdtemp(prefix="python-remap-interpreter-", dir=run_dir))
|
||||
(stub_dir / "interpreter.py").write_text(
|
||||
f"INTERP_EXECUTE_FINISH = {execute_finish}\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
parser = configparser.ConfigParser(strict=False)
|
||||
parser.optionxform = str
|
||||
parser.read(fixture_ini)
|
||||
require(parser.has_section("PYTHON"), "missing_PYTHON_section")
|
||||
path_prepend = parser.get("PYTHON", "PATH_PREPEND", fallback="")
|
||||
toplevel = parser.get("PYTHON", "TOPLEVEL", fallback="")
|
||||
require(path_prepend == "python", f"PATH_PREPEND_drift:{path_prepend}")
|
||||
require(toplevel == "python/toplevel.py", f"TOPLEVEL_drift:{toplevel}")
|
||||
emit("python_remap_lifecycle_ini_path_prepend", path_prepend)
|
||||
emit("python_remap_lifecycle_ini_toplevel", toplevel)
|
||||
|
||||
sys.path.insert(0, str(stub_dir))
|
||||
sys.path.insert(0, str(fixture_dir / path_prepend))
|
||||
sys.path.insert(0, str(linuxcnc_root / "lib/python"))
|
||||
|
||||
import interpreter # noqa: E402
|
||||
require(interpreter.INTERP_EXECUTE_FINISH == execute_finish, "interpreter_sentinel_drift")
|
||||
emit("python_remap_lifecycle_interpreter_sentinel_ok", 1)
|
||||
|
||||
toplevel_module = importlib.import_module(Path(toplevel).stem)
|
||||
emit("python_remap_lifecycle_toplevel_imported", int(toplevel_module.__name__ == "toplevel"))
|
||||
remap_module = importlib.import_module("remap")
|
||||
emit("python_remap_lifecycle_remap_imported", int(remap_module.__name__ == "remap"))
|
||||
require(hasattr(remap_module, "queuebuster"), "missing_queuebuster_callable")
|
||||
require(callable(remap_module.queuebuster), "queuebuster_not_callable")
|
||||
emit("python_remap_lifecycle_callable_lookup_ok", 1)
|
||||
|
||||
result = remap_module.queuebuster(object())
|
||||
require(inspect.isgenerator(result), "queuebuster_not_generator")
|
||||
emit("python_remap_lifecycle_generator_returned", 1)
|
||||
first_yield = next(result)
|
||||
require(first_yield == execute_finish, f"queuebuster_yield_drift:{first_yield}")
|
||||
emit("python_remap_lifecycle_generator_first_yield", first_yield)
|
||||
try:
|
||||
next(result)
|
||||
raise RuntimeError("queuebuster_generator_did_not_finish")
|
||||
except StopIteration:
|
||||
emit("python_remap_lifecycle_generator_finish_ok", 1)
|
||||
|
||||
emit("python_remap_runtime_lifecycle_probe_ok", 1)
|
||||
PY
|
||||
then
|
||||
cat "$PYTHON_RUNTIME_STDOUT"
|
||||
print_kv python_remap_runtime_probe_status "runtime_lifecycle_probe_passed"
|
||||
print_kv python_remap_runtime_probe_note "linuxcnc_stop_lookahead_python_lifecycle_probe_passed_without_promotion"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cat "$PYTHON_RUNTIME_STDOUT" || true
|
||||
print_kv python_remap_runtime_probe_status "runtime_lifecycle_probe_failed"
|
||||
print_kv python_remap_runtime_probe_note "linuxcnc_stop_lookahead_python_lifecycle_probe_failed"
|
||||
exit 1
|
||||
232
wasm-port/tests/native/probe_tool_db_runtime.sh
Normal file
232
wasm-port/tests/native/probe_tool_db_runtime.sh
Normal file
@@ -0,0 +1,232 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
LINUXCNC_ROOT="${LINUXCNC_ROOT:-$ROOT_DIR/../linuxcnc}"
|
||||
DB_DEMO_DIR="$LINUXCNC_ROOT/configs/sim/axis/db_demo"
|
||||
DB_PROGRAM="$DB_DEMO_DIR/db_nonran.py"
|
||||
RUN_DIR="$ROOT_DIR/build/native/tool-db-runtime"
|
||||
DB_SAVEFILE="$RUN_DIR/db_nonran_probe"
|
||||
DB_STDOUT="$RUN_DIR/db_program.stdout.log"
|
||||
DB_STDERR="$RUN_DIR/db_program.stderr.log"
|
||||
|
||||
required_commands=(python3 linuxcnc milltask halcmd)
|
||||
missing_requirements=()
|
||||
|
||||
command_path() {
|
||||
command -v "$1" 2>/dev/null || true
|
||||
}
|
||||
|
||||
csv_join() {
|
||||
local IFS=,
|
||||
printf '%s' "$*"
|
||||
}
|
||||
|
||||
print_kv() {
|
||||
printf '%s=%s\n' "$1" "$2"
|
||||
}
|
||||
|
||||
runtime_requirements=()
|
||||
for command_name in "${required_commands[@]}"; do
|
||||
path="$(command_path "$command_name")"
|
||||
if [[ -n "$path" ]]; then
|
||||
runtime_requirements+=("$command_name:1")
|
||||
print_kv "tool_db_runtime_${command_name}_path" "$path"
|
||||
else
|
||||
runtime_requirements+=("$command_name:0")
|
||||
missing_requirements+=("$command_name")
|
||||
fi
|
||||
done
|
||||
|
||||
source_ready=1
|
||||
for rel in \
|
||||
configs/sim/axis/db_demo/db_nonran.ini \
|
||||
configs/sim/axis/db_demo/db.py \
|
||||
lib/python/tooldb.py \
|
||||
lib/python/linuxcnc.so; do
|
||||
if [[ ! -e "$LINUXCNC_ROOT/$rel" ]]; then
|
||||
source_ready=0
|
||||
missing_requirements+=("$rel")
|
||||
print_kv "tool_db_missing_source_${rel//[^A-Za-z0-9]/_}" "$LINUXCNC_ROOT/$rel"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ! -x "$DB_PROGRAM" ]]; then
|
||||
source_ready=0
|
||||
missing_requirements+=("configs/sim/axis/db_demo/db_nonran.py:executable")
|
||||
fi
|
||||
|
||||
runtime_ready=0
|
||||
if [[ "${#missing_requirements[@]}" -eq 0 ]]; then
|
||||
runtime_ready=1
|
||||
fi
|
||||
|
||||
print_kv tool_db_runtime_requirements "$(csv_join "${runtime_requirements[@]}")"
|
||||
if [[ "${#missing_requirements[@]}" -eq 0 ]]; then
|
||||
print_kv tool_db_missing_requirements "-"
|
||||
else
|
||||
print_kv tool_db_missing_requirements "$(csv_join "${missing_requirements[@]}")"
|
||||
fi
|
||||
print_kv tool_db_source_proof_ready "$source_ready"
|
||||
print_kv tool_db_runtime_ready "$runtime_ready"
|
||||
print_kv tool_db_execution_enabled 0
|
||||
print_kv tool_db_promotion_allowed 0
|
||||
|
||||
if [[ "$source_ready" != "1" ]]; then
|
||||
print_kv tool_db_runtime_probe_status "blocked_missing_source"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$runtime_ready" != "1" ]]; then
|
||||
print_kv tool_db_runtime_probe_status "skipped_missing_host_runtime"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${ENABLE_TOOL_DB_RUNTIME_PROBE:-0}" != "1" ]]; then
|
||||
print_kv tool_db_runtime_probe_status "ready_disabled_by_default"
|
||||
print_kv tool_db_runtime_probe_note "set_ENABLE_TOOL_DB_RUNTIME_PROBE_1_to_run_db_protocol_probe"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "$RUN_DIR"
|
||||
rm -f "$DB_SAVEFILE" "$DB_STDOUT" "$DB_STDERR"
|
||||
|
||||
print_kv tool_db_runtime_probe_program "$DB_PROGRAM"
|
||||
print_kv tool_db_runtime_probe_db_savefile "$DB_SAVEFILE"
|
||||
print_kv tool_db_runtime_probe_stdout "$DB_STDOUT"
|
||||
print_kv tool_db_runtime_probe_stderr "$DB_STDERR"
|
||||
|
||||
if python3 - "$DB_PROGRAM" "$DB_DEMO_DIR" "$LINUXCNC_ROOT/lib/python" "$DB_SAVEFILE" "$DB_STDOUT" "$DB_STDERR" <<'PY'
|
||||
import os
|
||||
import select
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
db_program, db_demo_dir, python_path, db_savefile, stdout_log, stderr_log = sys.argv[1:]
|
||||
|
||||
env = os.environ.copy()
|
||||
env["PYTHONPATH"] = python_path + (os.pathsep + env["PYTHONPATH"] if env.get("PYTHONPATH") else "")
|
||||
|
||||
stdout_file = open(stdout_log, "w", encoding="utf-8")
|
||||
stderr_file = open(stderr_log, "w", encoding="utf-8")
|
||||
proc = subprocess.Popen(
|
||||
[db_program, "--period_minutes=999", db_savefile],
|
||||
cwd=db_demo_dir,
|
||||
env=env,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=stderr_file,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
)
|
||||
|
||||
def emit(name, value):
|
||||
print(f"{name}={value}")
|
||||
|
||||
def read_line(timeout=5.0):
|
||||
deadline = time.monotonic() + timeout
|
||||
while time.monotonic() < deadline:
|
||||
ready, _, _ = select.select([proc.stdout], [], [], 0.1)
|
||||
if ready:
|
||||
line = proc.stdout.readline()
|
||||
if line == "":
|
||||
raise RuntimeError("db_program_stdout_closed")
|
||||
line = line.rstrip("\n")
|
||||
stdout_file.write(line + "\n")
|
||||
stdout_file.flush()
|
||||
return line
|
||||
if proc.poll() is not None:
|
||||
raise RuntimeError(f"db_program_exited_{proc.returncode}")
|
||||
raise RuntimeError("timeout_waiting_for_db_program_reply")
|
||||
|
||||
def send(command):
|
||||
proc.stdin.write(command + "\n")
|
||||
proc.stdin.flush()
|
||||
|
||||
def read_until_fini(command, timeout=5.0):
|
||||
send(command)
|
||||
lines = []
|
||||
deadline = time.monotonic() + timeout
|
||||
while time.monotonic() < deadline:
|
||||
line = read_line(max(0.1, deadline - time.monotonic()))
|
||||
lines.append(line)
|
||||
if line.startswith("FINI"):
|
||||
return lines
|
||||
raise RuntimeError(f"timeout_waiting_for_fini_{command.split()[0]}")
|
||||
|
||||
def require(condition, message):
|
||||
if not condition:
|
||||
raise RuntimeError(message)
|
||||
|
||||
ok = False
|
||||
try:
|
||||
version = read_line()
|
||||
emit("tool_db_protocol_version", version)
|
||||
require(version == "v2.1", f"version_drift:{version}")
|
||||
|
||||
get_all = read_until_fini("g")
|
||||
tool_lines = [line for line in get_all if line.startswith("T")]
|
||||
emit("tool_db_get_all_count", len(tool_lines))
|
||||
require(len(tool_lines) == 10, "get_all_tool_count_drift")
|
||||
for toolno in range(10, 20):
|
||||
expected = f"T{toolno} "
|
||||
require(any(line.startswith(expected) for line in tool_lines), f"missing_tool_{toolno}")
|
||||
require(any(line.startswith(expected) and f"P{toolno + 100}" in line for line in tool_lines), f"pocket_drift_{toolno}")
|
||||
emit("tool_db_get_all_fini", int(get_all[-1].startswith("FINI")))
|
||||
|
||||
put_reply = read_until_fini("p t11 p111 d0.33 z0.11")
|
||||
emit("tool_db_put_tool_update_fini", int(put_reply[-1].startswith("FINI")))
|
||||
|
||||
get_after_put = read_until_fini("g")
|
||||
t11_rows = [line for line in get_after_put if line.startswith("T11 ")]
|
||||
require(t11_rows and "D0.33" in t11_rows[0] and "Z0.11" in t11_rows[0], "put_update_state_drift")
|
||||
emit("tool_db_put_tool_update_state_ok", 1)
|
||||
|
||||
load_reply = read_until_fini("l t14 p0")
|
||||
emit("tool_db_load_spindle_fini", int(load_reply[-1].startswith("FINI")))
|
||||
t14_loaded = read_until_fini("t 14")
|
||||
require(any(line.startswith("T14 ") and "P0" in line for line in t14_loaded), "load_spindle_state_drift")
|
||||
emit("tool_db_load_spindle_state_ok", 1)
|
||||
|
||||
unload_reply = read_until_fini("u t0 p0")
|
||||
emit("tool_db_unload_spindle_fini", int(unload_reply[-1].startswith("FINI")))
|
||||
t14_unloaded = read_until_fini("t 14")
|
||||
require(any(line.startswith("T14 ") and "P114" in line for line in t14_unloaded), "unload_spindle_state_drift")
|
||||
emit("tool_db_unload_spindle_state_ok", 1)
|
||||
|
||||
require(os.path.exists(db_savefile), "db_savefile_missing")
|
||||
with open(db_savefile, "r", encoding="utf-8") as handle:
|
||||
saved = handle.read()
|
||||
require("T11 P111 D0.33 Z0.11" in saved, "db_savefile_put_update_missing")
|
||||
require("T14 P114" in saved, "db_savefile_unload_state_missing")
|
||||
emit("tool_db_persistence_state_ok", 1)
|
||||
ok = True
|
||||
finally:
|
||||
try:
|
||||
if proc.stdin:
|
||||
proc.stdin.close()
|
||||
except Exception:
|
||||
pass
|
||||
if proc.poll() is None:
|
||||
proc.terminate()
|
||||
try:
|
||||
proc.wait(timeout=2)
|
||||
except subprocess.TimeoutExpired:
|
||||
proc.kill()
|
||||
proc.wait(timeout=2)
|
||||
stderr_file.close()
|
||||
stdout_file.close()
|
||||
|
||||
emit("tool_db_runtime_protocol_probe_ok", int(ok))
|
||||
sys.exit(0 if ok else 1)
|
||||
PY
|
||||
then
|
||||
print_kv tool_db_runtime_probe_status "runtime_protocol_probe_passed"
|
||||
print_kv tool_db_runtime_probe_note "linuxcnc_owned_DB_PROGRAM_protocol_probe_passed_without_promotion"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
print_kv tool_db_runtime_probe_status "runtime_protocol_probe_failed"
|
||||
print_kv tool_db_runtime_probe_note "linuxcnc_owned_DB_PROGRAM_protocol_probe_failed"
|
||||
exit 1
|
||||
File diff suppressed because it is too large
Load Diff
313
wasm-port/tests/native/verify_nc_files.sh
Executable file
313
wasm-port/tests/native/verify_nc_files.sh
Executable file
@@ -0,0 +1,313 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
WORKSPACE_ROOT="$(cd "$ROOT_DIR/.." && pwd)"
|
||||
LINUXCNC_DIR="${LINUXCNC_DIR:-$WORKSPACE_ROOT/linuxcnc}"
|
||||
NC_DIR="$LINUXCNC_DIR/nc_files"
|
||||
BUILD_DIR="${NC_FILES_BUILD_DIR:-$ROOT_DIR/build/native/nc-files}"
|
||||
RS274="${RS274:-$LINUXCNC_DIR/bin/rs274}"
|
||||
TIMEOUT_SECONDS="${NC_FILES_TIMEOUT_SECONDS:-30}"
|
||||
ONLY_FILTER=""
|
||||
SCAN_ALL=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--all)
|
||||
SCAN_ALL=1
|
||||
shift
|
||||
;;
|
||||
--only)
|
||||
[[ $# -ge 2 ]] || { echo "missing value for --only" >&2; exit 2; }
|
||||
ONLY_FILTER="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help|-h)
|
||||
cat <<EOF
|
||||
Usage: $0 [--all] [--only PATH_SUBSTRING]
|
||||
|
||||
Runs LinuxCNC nc_files programs through LinuxCNC's rs274 standalone entry point.
|
||||
The default suite covers the basic/example programs and common macro libraries.
|
||||
Use --all to scan every .ngc under linuxcnc/nc_files.
|
||||
|
||||
Results are written under:
|
||||
$BUILD_DIR
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "unknown argument: $1" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ! -x "$RS274" ]]; then
|
||||
echo "missing executable rs274: $RS274" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$NC_DIR" ]]; then
|
||||
echo "missing LinuxCNC nc_files directory: $NC_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export LD_LIBRARY_PATH="$LINUXCNC_DIR/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
export PYTHONPATH="$LINUXCNC_DIR/lib/python${PYTHONPATH:+:$PYTHONPATH}"
|
||||
|
||||
mkdir -p "$BUILD_DIR"/{logs,out,wrapped,params}
|
||||
SUMMARY_FILE="$BUILD_DIR/summary.tsv"
|
||||
INI_FILE="$BUILD_DIR/test-rs274.ini"
|
||||
TOOL_TABLE="$BUILD_DIR/test-tools.tbl"
|
||||
PARAMETER_TEMPLATE="$BUILD_DIR/test.var.template"
|
||||
printf 'path\tclass\tstatus\texpected_failure\texit_code\tseconds\toutput_lines\tstderr_summary\n' > "$SUMMARY_FILE"
|
||||
|
||||
relpath() {
|
||||
local path="$1"
|
||||
printf '%s\n' "${path#"$NC_DIR"/}"
|
||||
}
|
||||
|
||||
safe_name() {
|
||||
local rel="$1"
|
||||
rel="${rel//\//_}"
|
||||
rel="${rel// /_}"
|
||||
rel="${rel//#/_}"
|
||||
rel="${rel//=/}"
|
||||
printf '%s\n' "$rel"
|
||||
}
|
||||
|
||||
write_fixture_config() {
|
||||
{
|
||||
printf '[RS274NGC]\n'
|
||||
printf 'PARAMETER_FILE = test.var\n'
|
||||
printf 'SUBROUTINE_PATH = %s\n' "$(
|
||||
find "$NC_DIR" -type d | sort | paste -sd ':' -
|
||||
)"
|
||||
printf 'OWORD_NARGS = 1\n'
|
||||
printf 'FEATURES = 30\n'
|
||||
printf '\n[DISPLAY]\n'
|
||||
printf 'PROGRAM_PREFIX = %s\n' "$NC_DIR"
|
||||
printf '\n[TRAJ]\n'
|
||||
printf 'COORDINATES = X Y Z A B C U V W\n'
|
||||
printf '\n[EMCIO]\n'
|
||||
printf 'TOOL_TABLE = %s\n' "$TOOL_TABLE"
|
||||
} > "$INI_FILE"
|
||||
|
||||
: > "$TOOL_TABLE"
|
||||
local tool
|
||||
for tool in $(seq 1 200); do
|
||||
printf 'T%s P%s D0.125000 Z+0.000000 ; generated nc_files test tool\n' "$tool" "$tool" >> "$TOOL_TABLE"
|
||||
done
|
||||
|
||||
{
|
||||
printf '5161 0.0\n'
|
||||
printf '5162 0.0\n'
|
||||
printf '5163 0.0\n'
|
||||
printf '5220 1\n'
|
||||
printf '5221 0.0\n'
|
||||
printf '5222 0.0\n'
|
||||
printf '5223 0.0\n'
|
||||
printf '5399 0.0\n'
|
||||
} > "$PARAMETER_TEMPLATE"
|
||||
}
|
||||
|
||||
has_program_end() {
|
||||
local ngc="$1"
|
||||
awk '
|
||||
BEGIN { found = 0 }
|
||||
/^[[:space:]]*%[[:space:]]*$/ { found = 1 }
|
||||
{
|
||||
line = tolower($0)
|
||||
gsub(/\([^)]*\)/, "", line)
|
||||
if (line ~ /(^|[[:space:]])m0*(2|30)([[:space:]]|$)/) {
|
||||
found = 1
|
||||
}
|
||||
}
|
||||
END { exit(found ? 0 : 1) }
|
||||
' "$ngc"
|
||||
}
|
||||
|
||||
is_default_basic_path() {
|
||||
local rel="$1"
|
||||
case "$rel" in
|
||||
*/*)
|
||||
case "$rel" in
|
||||
gladevcp_lib/*.ngc|macros/lathe/*.ngc|macros/mill/*.ngc|ngcgui_lib/*.ngc)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
return 1
|
||||
;;
|
||||
*.ngc)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
classify_program() {
|
||||
local rel="$1"
|
||||
local ngc="$2"
|
||||
case "$rel" in
|
||||
macros/*|ngcgui_lib/*|gladevcp_lib/*|remap-subroutines/*|remap_lib/*|probe/*)
|
||||
printf 'macro_load\n'
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
if has_program_end "$ngc"; then
|
||||
printf 'main\n'
|
||||
else
|
||||
printf 'macro_load\n'
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_input() {
|
||||
local class="$1"
|
||||
local ngc="$2"
|
||||
local wrapped="$3"
|
||||
if [[ "$class" == "main" ]]; then
|
||||
printf '%s\n' "$ngc"
|
||||
return 0
|
||||
fi
|
||||
cp "$ngc" "$wrapped"
|
||||
printf '\nM2\n' >> "$wrapped"
|
||||
printf '%s\n' "$wrapped"
|
||||
}
|
||||
|
||||
known_expected_failure() {
|
||||
local rel="$1"
|
||||
local combined="$2"
|
||||
case "$rel" in
|
||||
cone.ngc)
|
||||
[[ "$combined" == *"Bad character 'w' used"* ]] && { printf 'five-axis-W-machine-context\n'; return 0; }
|
||||
;;
|
||||
g76.ngc|lathe-g76.ngc)
|
||||
[[ "$combined" == *"Length of cutter compensation entry move is not greater than the tool radius"* ]] && { printf 'lathe-cutter-comp-tool-context\n'; return 0; }
|
||||
;;
|
||||
lathe_g70_71_demo.ngc)
|
||||
[[ "$combined" == *"Arc move in concave corner cannot be reached by the tool without gouging"* ]] && { printf 'lathe-profile-tool-context\n'; return 0; }
|
||||
;;
|
||||
nestedcall.ngc)
|
||||
[[ "$combined" == *"Unexpected character after O-word"* ]] && { printf 'upstream-oword-callsub-syntax-edge\n'; return 0; }
|
||||
;;
|
||||
nurbs/G5/Curva_Stella\ #1=1_G5.ngc)
|
||||
[[ "$combined" == *"Unknown g code used"* && "$combined" == *"G2.2"* ]] && { printf 'nurbs-g22-entrypoint-edge\n'; return 0; }
|
||||
;;
|
||||
probe*.ngc|probe/*|smartprobe.ngc|tool-length-probe.ngc|touchoff.ngc)
|
||||
[[ "$combined" == *"probe"* || "$combined" == *"G38"* ]] && { printf 'probe-runtime-context\n'; return 0; }
|
||||
;;
|
||||
plasmac/*|plasmatest.ngc)
|
||||
[[ "$combined" == *"Unknown m code"* || "$combined" == *"Named parameter"* ]] && { printf 'plasmac-runtime-context\n'; return 0; }
|
||||
;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
summarize_text() {
|
||||
local file="$1"
|
||||
if [[ ! -f "$file" ]]; then
|
||||
return 0
|
||||
fi
|
||||
tr '\n\t' ' ' < "$file" | sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//' | cut -c 1-260
|
||||
}
|
||||
|
||||
write_fixture_config
|
||||
|
||||
total=0
|
||||
pass=0
|
||||
fail=0
|
||||
timeout_count=0
|
||||
expected_fail=0
|
||||
unexpected_fail=0
|
||||
|
||||
while IFS= read -r ngc; do
|
||||
rel="$(relpath "$ngc")"
|
||||
if [[ "$SCAN_ALL" -eq 0 ]] && ! is_default_basic_path "$rel"; then
|
||||
continue
|
||||
fi
|
||||
if [[ -n "$ONLY_FILTER" && "$rel" != *"$ONLY_FILTER"* ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
total=$((total + 1))
|
||||
class="$(classify_program "$rel" "$ngc")"
|
||||
safe="$(safe_name "$rel")"
|
||||
wrapped="$BUILD_DIR/wrapped/$safe"
|
||||
input="$(prepare_input "$class" "$ngc" "$wrapped")"
|
||||
out_file="$BUILD_DIR/out/$safe.out"
|
||||
stdout_file="$BUILD_DIR/logs/$safe.stdout"
|
||||
stderr_file="$BUILD_DIR/logs/$safe.stderr"
|
||||
param_file="$BUILD_DIR/params/$safe.var"
|
||||
cp "$PARAMETER_TEMPLATE" "$param_file"
|
||||
|
||||
start_time="$(date +%s)"
|
||||
set +e
|
||||
(
|
||||
cd "$WORKSPACE_ROOT"
|
||||
INI_FILE_NAME="$INI_FILE" timeout "$TIMEOUT_SECONDS" "$RS274" \
|
||||
-g \
|
||||
-i "$INI_FILE" \
|
||||
-t "$TOOL_TABLE" \
|
||||
-v "$param_file" \
|
||||
"$input" \
|
||||
"$out_file"
|
||||
) >"$stdout_file" 2>"$stderr_file"
|
||||
rc=$?
|
||||
set -e
|
||||
end_time="$(date +%s)"
|
||||
seconds=$((end_time - start_time))
|
||||
|
||||
status="FAIL"
|
||||
if [[ "$rc" -eq 0 ]]; then
|
||||
status="PASS"
|
||||
pass=$((pass + 1))
|
||||
elif [[ "$rc" -eq 124 || "$rc" -eq 137 ]]; then
|
||||
status="TIMEOUT"
|
||||
timeout_count=$((timeout_count + 1))
|
||||
fail=$((fail + 1))
|
||||
else
|
||||
fail=$((fail + 1))
|
||||
fi
|
||||
|
||||
combined="$(cat "$stdout_file" "$stderr_file" 2>/dev/null || true)"
|
||||
expected=""
|
||||
if [[ "$status" != "PASS" ]]; then
|
||||
expected="$(known_expected_failure "$rel" "$combined" || true)"
|
||||
if [[ -n "$expected" ]]; then
|
||||
expected_fail=$((expected_fail + 1))
|
||||
else
|
||||
unexpected_fail=$((unexpected_fail + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
output_lines=0
|
||||
[[ -f "$out_file" ]] && output_lines="$(wc -l < "$out_file" | tr -d '[:space:]')"
|
||||
stderr_summary="$(summarize_text "$stderr_file")"
|
||||
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
|
||||
"$rel" \
|
||||
"$class" \
|
||||
"$status" \
|
||||
"$expected" \
|
||||
"$rc" \
|
||||
"$seconds" \
|
||||
"$output_lines" \
|
||||
"$stderr_summary" >> "$SUMMARY_FILE"
|
||||
done < <(find "$NC_DIR" -type f -name '*.ngc' | sort)
|
||||
|
||||
{
|
||||
printf 'nc_files harness summary\n'
|
||||
printf 'summary: %s\n' "$SUMMARY_FILE"
|
||||
printf 'suite: %s\n' "$([[ "$SCAN_ALL" -eq 1 ]] && printf all || printf basic)"
|
||||
printf 'total: %s\n' "$total"
|
||||
printf 'pass: %s\n' "$pass"
|
||||
printf 'fail: %s\n' "$fail"
|
||||
printf 'timeout: %s\n' "$timeout_count"
|
||||
printf 'expected_fail: %s\n' "$expected_fail"
|
||||
printf 'unexpected_fail: %s\n' "$unexpected_fail"
|
||||
} | tee "$BUILD_DIR/summary.txt"
|
||||
|
||||
if [[ "$unexpected_fail" -ne 0 ]]; then
|
||||
echo "unexpected nc_files failures:" >&2
|
||||
awk -F '\t' 'NR > 1 && $3 != "PASS" && $4 == "" { print $1 "\t" $3 "\t" $8 }' "$SUMMARY_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
478
wasm-port/tests/native/verify_sim_configs.sh
Executable file
478
wasm-port/tests/native/verify_sim_configs.sh
Executable file
@@ -0,0 +1,478 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||
WORKSPACE_ROOT="$(cd "$ROOT_DIR/.." && pwd)"
|
||||
LINUXCNC_DIR="${LINUXCNC_DIR:-$WORKSPACE_ROOT/linuxcnc}"
|
||||
SIM_DIR="$LINUXCNC_DIR/configs/sim"
|
||||
BUILD_DIR="${SIM_CONFIG_BUILD_DIR:-$ROOT_DIR/build/native/sim-configs}"
|
||||
RS274="${RS274:-$LINUXCNC_DIR/bin/rs274}"
|
||||
TIMEOUT_SECONDS="${SIM_CONFIG_TIMEOUT_SECONDS:-30}"
|
||||
ONLY_FILTER=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--only)
|
||||
[[ $# -ge 2 ]] || { echo "missing value for --only" >&2; exit 2; }
|
||||
ONLY_FILTER="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help|-h)
|
||||
cat <<EOF
|
||||
Usage: $0 [--only PATH_SUBSTRING]
|
||||
|
||||
Runs LinuxCNC configs/sim .ngc programs through LinuxCNC's rs274 standalone
|
||||
entry point and writes logs plus summary.tsv, class-summary.tsv, and
|
||||
path-matrix.tsv under:
|
||||
$BUILD_DIR
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "unknown argument: $1" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ ! -x "$RS274" ]]; then
|
||||
echo "missing executable rs274: $RS274" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$SIM_DIR" ]]; then
|
||||
echo "missing LinuxCNC sim config directory: $SIM_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
exec 9>"$BUILD_DIR/inventory.lock"
|
||||
flock 9
|
||||
|
||||
"$ROOT_DIR/tools/build_native_probes.sh"
|
||||
|
||||
export LD_LIBRARY_PATH="$LINUXCNC_DIR/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||
export PYTHONPATH="$LINUXCNC_DIR/lib/python${PYTHONPATH:+:$PYTHONPATH}"
|
||||
|
||||
mkdir -p "$BUILD_DIR"/{logs,out,wrapped,params}
|
||||
: > "$BUILD_DIR/skipped.tsv"
|
||||
SUMMARY_FILE="$BUILD_DIR/summary.tsv"
|
||||
CLASS_SUMMARY_FILE="$BUILD_DIR/class-summary.tsv"
|
||||
PATH_MATRIX_FILE="$BUILD_DIR/path-matrix.tsv"
|
||||
printf 'path\tclass\tstatus\texpected_failure\texit_code\tseconds\tini\ttbl\toutput_lines\tstderr_summary\n' > "$SUMMARY_FILE"
|
||||
|
||||
relpath() {
|
||||
local path="$1"
|
||||
printf '%s\n' "${path#"$SIM_DIR"/}"
|
||||
}
|
||||
|
||||
safe_name() {
|
||||
local rel="$1"
|
||||
rel="${rel//\//_}"
|
||||
rel="${rel// /_}"
|
||||
printf '%s\n' "$rel"
|
||||
}
|
||||
|
||||
find_nearest_ini() {
|
||||
local dir="$1"
|
||||
local candidate
|
||||
while [[ "$dir" == "$SIM_DIR"* ]]; do
|
||||
candidate="$(find "$dir" -maxdepth 1 -type f -name '*.ini' | sort | sed -n '1p')"
|
||||
if [[ -n "$candidate" ]]; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
[[ "$dir" == "$SIM_DIR" ]] && break
|
||||
dir="$(dirname "$dir")"
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
find_ini_by_open_file() {
|
||||
local ngc="$1"
|
||||
local ngc_dir="$2"
|
||||
local ini open_file resolved
|
||||
while IFS= read -r ini; do
|
||||
open_file="$(ini_value "$ini" DISPLAY OPEN_FILE || true)"
|
||||
[[ -z "$open_file" ]] && continue
|
||||
if [[ "$open_file" = /* ]]; then
|
||||
resolved="$open_file"
|
||||
else
|
||||
resolved="$(realpath -m "$(dirname "$ini")/$open_file")"
|
||||
fi
|
||||
if [[ "$resolved" == "$(realpath -m "$ngc")" ]]; then
|
||||
printf '%s\n' "$ini"
|
||||
return 0
|
||||
fi
|
||||
done < <(find "$ngc_dir" -maxdepth 1 -type f -name '*.ini' | sort)
|
||||
return 1
|
||||
}
|
||||
|
||||
find_sim_ini() {
|
||||
local rel="$1"
|
||||
local ngc_dir="$2"
|
||||
local ngc="$3"
|
||||
if find_ini_by_open_file "$ngc" "$ngc_dir"; then
|
||||
return 0
|
||||
fi
|
||||
case "$rel" in
|
||||
axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/*)
|
||||
printf '%s\n' "$SIM_DIR/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
find_nearest_ini "$ngc_dir"
|
||||
}
|
||||
|
||||
ini_value() {
|
||||
local ini="$1"
|
||||
local section="$2"
|
||||
local key="$3"
|
||||
awk -v section="$section" -v key="$key" '
|
||||
BEGIN { in_section = 0 }
|
||||
/^[[:space:]]*[#;]/ { next }
|
||||
/^[[:space:]]*\[/ {
|
||||
in_section = (toupper($0) ~ "\\[" toupper(section) "\\]")
|
||||
next
|
||||
}
|
||||
in_section {
|
||||
line = $0
|
||||
sub(/[[:space:]]*[#;].*$/, "", line)
|
||||
split(line, parts, "=")
|
||||
name = parts[1]
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", name)
|
||||
if (toupper(name) == toupper(key)) {
|
||||
value = substr(line, index(line, "=") + 1)
|
||||
gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
|
||||
print value
|
||||
exit
|
||||
}
|
||||
}
|
||||
' "$ini"
|
||||
}
|
||||
|
||||
find_upward_file_by_name() {
|
||||
local start_dir="$1"
|
||||
local basename="$2"
|
||||
local dir="$start_dir"
|
||||
while [[ "$dir" == "$SIM_DIR"* ]]; do
|
||||
if [[ -f "$dir/$basename" ]]; then
|
||||
printf '%s\n' "$dir/$basename"
|
||||
return 0
|
||||
fi
|
||||
[[ "$dir" == "$SIM_DIR" ]] && break
|
||||
dir="$(dirname "$dir")"
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
find_nearest_tbl() {
|
||||
local ngc_dir="$1"
|
||||
local ini="$2"
|
||||
local tool_table
|
||||
tool_table="$(ini_value "$ini" EMCIO TOOL_TABLE || true)"
|
||||
if [[ -n "$tool_table" ]]; then
|
||||
if [[ "$tool_table" = /* && -f "$tool_table" ]]; then
|
||||
printf '%s\n' "$tool_table"
|
||||
return 0
|
||||
fi
|
||||
if [[ -f "$(dirname "$ini")/$tool_table" ]]; then
|
||||
printf '%s\n' "$(dirname "$ini")/$tool_table"
|
||||
return 0
|
||||
fi
|
||||
if find_upward_file_by_name "$ngc_dir" "$(basename "$tool_table")"; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
local dir="$ngc_dir"
|
||||
local candidate
|
||||
while [[ "$dir" == "$SIM_DIR"* ]]; do
|
||||
candidate="$(find "$dir" -maxdepth 1 -type f -name '*.tbl' | sort | sed -n '1p')"
|
||||
if [[ -n "$candidate" ]]; then
|
||||
printf '%s\n' "$candidate"
|
||||
return 0
|
||||
fi
|
||||
[[ "$dir" == "$SIM_DIR" ]] && break
|
||||
dir="$(dirname "$dir")"
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
copy_parameter_file() {
|
||||
local ini="$1"
|
||||
local dst="$2"
|
||||
local parameter_file
|
||||
parameter_file="$(ini_value "$ini" RS274NGC PARAMETER_FILE || true)"
|
||||
rm -f "$dst" "$dst.bak"
|
||||
if [[ -n "$parameter_file" ]]; then
|
||||
if [[ "$parameter_file" = /* && -f "$parameter_file" ]]; then
|
||||
cp "$parameter_file" "$dst"
|
||||
return 0
|
||||
fi
|
||||
if [[ -f "$(dirname "$ini")/$parameter_file" ]]; then
|
||||
cp "$(dirname "$ini")/$parameter_file" "$dst"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
has_program_end() {
|
||||
local ngc="$1"
|
||||
awk '
|
||||
BEGIN { found = 0 }
|
||||
/^[[:space:]]*%[[:space:]]*$/ { found = 1 }
|
||||
{
|
||||
line = tolower($0)
|
||||
gsub(/\([^)]*\)/, "", line)
|
||||
if (line ~ /(^|[[:space:]])m0*(2|30)([[:space:]]|$)/) {
|
||||
found = 1
|
||||
}
|
||||
}
|
||||
END { exit(found ? 0 : 1) }
|
||||
' "$ngc"
|
||||
}
|
||||
|
||||
classify_program() {
|
||||
local rel="$1"
|
||||
local ngc="$2"
|
||||
if [[ "$rel" == */remap_subs/* || "$rel" == */nc_subroutines/* ]]; then
|
||||
printf 'remap_subroutine\n'
|
||||
elif [[ "$rel" == */macros/* || "$rel" == */lathe_configs/* ]]; then
|
||||
printf 'macro_load\n'
|
||||
elif has_program_end "$ngc"; then
|
||||
printf 'main\n'
|
||||
else
|
||||
printf 'macro_load\n'
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_input() {
|
||||
local class="$1"
|
||||
local ngc="$2"
|
||||
local wrapped="$3"
|
||||
if [[ "$class" == "main" ]]; then
|
||||
printf '%s\n' "$ngc"
|
||||
return 0
|
||||
fi
|
||||
cp "$ngc" "$wrapped"
|
||||
printf '\nM2\n' >> "$wrapped"
|
||||
printf '%s\n' "$wrapped"
|
||||
}
|
||||
|
||||
known_expected_failure() {
|
||||
local rel="$1"
|
||||
local combined="$2"
|
||||
case "$rel" in
|
||||
gmoccapy/*)
|
||||
[[ "$combined" == *"python/toplevel.py"* ]] && { printf 'python-remap-runtime-edge\n'; return 0; }
|
||||
;;
|
||||
axis/laser/*)
|
||||
[[ "$combined" == *"python/toplevel.py"* ]] && { printf 'python-remap-runtime-edge\n'; return 0; }
|
||||
;;
|
||||
axis/external_offsets/dyn_demo.ngc|axis/external_offsets/eoffsets.ngc|axis/external_offsets/jwp_z.ngc|axis/external_offsets/opa_demo.ngc)
|
||||
[[ "$combined" == *"Unknown m code used: M111"* ]] && { printf 'user-m-code-M111\n'; return 0; }
|
||||
;;
|
||||
axis/geometry/xyzc.ngc)
|
||||
[[ "$combined" == *"Unknown m code used: M110"* ]] && { printf 'user-m-code-M110\n'; return 0; }
|
||||
;;
|
||||
axis/foam/foam.ngc)
|
||||
[[ "$combined" == *"Bad character 'u' used"* ]] && { printf 'ini-axis-mask-UV\n'; return 0; }
|
||||
;;
|
||||
axis/vismach/5axis/bridgemill/5axisgui.ngc)
|
||||
[[ "$combined" == *"Bad character 'w' used"* ]] && { printf 'ini-axis-mask-W\n'; return 0; }
|
||||
;;
|
||||
axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/incremental_repetition_g533.ngc)
|
||||
[[ "$combined" == *"Cannot use axis values without a g code that uses them"* ]] && { printf 'upstream-demo-missing-motion-gcode\n'; return 0; }
|
||||
;;
|
||||
axis/vismach/5axis/table-rotary_spindle-rotary-nutating/demos/*)
|
||||
[[ "$combined" == *"python/toplevel.py"* ]] && { printf 'python-remap-runtime-edge\n'; return 0; }
|
||||
;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
summarize_text() {
|
||||
local file="$1"
|
||||
if [[ ! -f "$file" ]]; then
|
||||
return 0
|
||||
fi
|
||||
tr '\n\t' ' ' < "$file" | sed -E 's/[[:space:]]+/ /g; s/^ //; s/ $//' | cut -c 1-260
|
||||
}
|
||||
|
||||
total=0
|
||||
pass=0
|
||||
fail=0
|
||||
timeout_count=0
|
||||
skip=0
|
||||
expected_fail=0
|
||||
unexpected_fail=0
|
||||
|
||||
while IFS= read -r ngc; do
|
||||
rel="$(relpath "$ngc")"
|
||||
if [[ -n "$ONLY_FILTER" && "$rel" != *"$ONLY_FILTER"* ]]; then
|
||||
continue
|
||||
fi
|
||||
if [[ "$rel" == */python/*.ngc ]]; then
|
||||
printf '%s\tunsupported_runtime_edge\tpython-ngc-helper\n' "$rel" >> "$BUILD_DIR/skipped.tsv"
|
||||
skip=$((skip + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
ini="$(find_sim_ini "$rel" "$(dirname "$ngc")" "$ngc" || true)"
|
||||
if [[ -z "$ini" ]]; then
|
||||
printf '%s\tunsupported_runtime_edge\tmissing-ini\n' "$rel" >> "$BUILD_DIR/skipped.tsv"
|
||||
skip=$((skip + 1))
|
||||
continue
|
||||
fi
|
||||
tbl="$(find_nearest_tbl "$(dirname "$ngc")" "$ini" || true)"
|
||||
if [[ -z "$tbl" ]]; then
|
||||
printf '%s\tunsupported_runtime_edge\tmissing-tool-table\n' "$rel" >> "$BUILD_DIR/skipped.tsv"
|
||||
skip=$((skip + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
total=$((total + 1))
|
||||
class="$(classify_program "$rel" "$ngc")"
|
||||
safe="$(safe_name "$rel")"
|
||||
wrapped="$BUILD_DIR/wrapped/$safe"
|
||||
input="$(prepare_input "$class" "$ngc" "$wrapped")"
|
||||
out_file="$BUILD_DIR/out/$safe.out"
|
||||
stdout_file="$BUILD_DIR/logs/$safe.stdout"
|
||||
stderr_file="$BUILD_DIR/logs/$safe.stderr"
|
||||
param_file="$BUILD_DIR/params/$safe.var"
|
||||
copy_parameter_file "$ini" "$param_file"
|
||||
|
||||
start_time="$(date +%s)"
|
||||
set +e
|
||||
(
|
||||
cd "$(dirname "$ini")"
|
||||
INI_FILE_NAME="$ini" timeout "$TIMEOUT_SECONDS" "$RS274" \
|
||||
-g \
|
||||
-i "$(basename "$ini")" \
|
||||
-t "$tbl" \
|
||||
-v "$param_file" \
|
||||
"$input" \
|
||||
"$out_file"
|
||||
) >"$stdout_file" 2>"$stderr_file"
|
||||
rc=$?
|
||||
set -e
|
||||
end_time="$(date +%s)"
|
||||
seconds=$((end_time - start_time))
|
||||
|
||||
status="FAIL"
|
||||
if [[ "$rc" -eq 0 ]]; then
|
||||
status="PASS"
|
||||
pass=$((pass + 1))
|
||||
elif [[ "$rc" -eq 124 || "$rc" -eq 137 ]]; then
|
||||
status="TIMEOUT"
|
||||
timeout_count=$((timeout_count + 1))
|
||||
else
|
||||
fail=$((fail + 1))
|
||||
fi
|
||||
|
||||
combined="$(cat "$stdout_file" "$stderr_file" 2>/dev/null || true)"
|
||||
expected=""
|
||||
if [[ "$status" != "PASS" ]]; then
|
||||
expected="$(known_expected_failure "$rel" "$combined" || true)"
|
||||
if [[ -n "$expected" ]]; then
|
||||
expected_fail=$((expected_fail + 1))
|
||||
else
|
||||
unexpected_fail=$((unexpected_fail + 1))
|
||||
fi
|
||||
fi
|
||||
|
||||
output_lines=0
|
||||
[[ -f "$out_file" ]] && output_lines="$(wc -l < "$out_file" | tr -d '[:space:]')"
|
||||
stderr_summary="$(summarize_text "$stderr_file")"
|
||||
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
|
||||
"$rel" \
|
||||
"$class" \
|
||||
"$status" \
|
||||
"$expected" \
|
||||
"$rc" \
|
||||
"$seconds" \
|
||||
"$(relpath "$ini")" \
|
||||
"$(relpath "$tbl")" \
|
||||
"$output_lines" \
|
||||
"$stderr_summary" >> "$SUMMARY_FILE"
|
||||
done < <(find "$SIM_DIR" -type f -name '*.ngc' | sort)
|
||||
|
||||
{
|
||||
printf 'class\tstatus\texpected_failure\tcount\n'
|
||||
awk -F '\t' '
|
||||
NR > 1 {
|
||||
class[$2] += 1
|
||||
status[$2 "\t" $3] += 1
|
||||
if ($4 != "") {
|
||||
expected[$2 "\t" $4] += 1
|
||||
}
|
||||
}
|
||||
END {
|
||||
for (key in class) {
|
||||
print key "\tALL\t-\t" class[key]
|
||||
}
|
||||
for (key in status) {
|
||||
split(key, parts, "\t")
|
||||
print parts[1] "\t" parts[2] "\t-\t" status[key]
|
||||
}
|
||||
for (key in expected) {
|
||||
split(key, parts, "\t")
|
||||
print parts[1] "\tEXPECTED_FAIL\t" parts[2] "\t" expected[key]
|
||||
}
|
||||
}
|
||||
' "$SUMMARY_FILE" | sort
|
||||
} > "$CLASS_SUMMARY_FILE"
|
||||
|
||||
awk -F '\t' '
|
||||
BEGIN {
|
||||
OFS = "\t"
|
||||
print "path", "class", "status", "expected_failure", "ini", "tbl", "runtime_family", "blocked"
|
||||
}
|
||||
NR > 1 {
|
||||
runtime_family = $1
|
||||
sub("/[^/]+$", "", runtime_family)
|
||||
blocked = ""
|
||||
if ($1 ~ /^axis\/db_demo\//) {
|
||||
blocked = "L4-TOOL-DB"
|
||||
} else if ($1 ~ /^axis\/vismach\/millturn\// && $2 == "main") {
|
||||
blocked = "L4-USER-M-PROCESS"
|
||||
} else if ($4 == "upstream-demo-missing-motion-gcode") {
|
||||
blocked = "UPSTREAM-DEMO"
|
||||
} else if ($1 ~ /^axis\/remap\// && $2 == "main") {
|
||||
blocked = "L4-PYTHON-REMAP"
|
||||
} else if ($1 ~ /^axis\/vismach\/VMC_toolchange\// && $2 == "main") {
|
||||
blocked = "L4-PYTHON-REMAP"
|
||||
} else if ($1 ~ /^gmoccapy\//) {
|
||||
blocked = "L4-PYTHON-REMAP"
|
||||
} else if ($1 ~ /^axis\/laser\//) {
|
||||
blocked = "L4-PYTHON-REMAP"
|
||||
} else if ($1 ~ /^axis\/vismach\/5axis\/table-rotary_spindle-rotary-nutating\/demos\//) {
|
||||
blocked = "L4-PYTHON-REMAP"
|
||||
} else if ($2 == "remap_subroutine") {
|
||||
blocked = "ASSET-ONLY"
|
||||
}
|
||||
print $1, $2, $3, ($4 == "" ? "-" : $4), $7, $8, runtime_family, (blocked == "" ? "-" : blocked)
|
||||
}
|
||||
' "$SUMMARY_FILE" > "$PATH_MATRIX_FILE"
|
||||
|
||||
{
|
||||
printf 'sim config harness summary\n'
|
||||
printf 'summary: %s\n' "$SUMMARY_FILE"
|
||||
printf 'class_summary: %s\n' "$CLASS_SUMMARY_FILE"
|
||||
printf 'path_matrix: %s\n' "$PATH_MATRIX_FILE"
|
||||
printf 'total: %s\n' "$total"
|
||||
printf 'pass: %s\n' "$pass"
|
||||
printf 'fail: %s\n' "$fail"
|
||||
printf 'timeout: %s\n' "$timeout_count"
|
||||
printf 'expected_fail: %s\n' "$expected_fail"
|
||||
printf 'unexpected_fail: %s\n' "$unexpected_fail"
|
||||
printf 'skipped: %s\n' "$skip"
|
||||
} | tee "$BUILD_DIR/summary.txt"
|
||||
|
||||
if [[ "$unexpected_fail" -ne 0 ]]; then
|
||||
echo "unexpected sim-config failures:" >&2
|
||||
awk -F '\t' 'NR > 1 && $3 != "PASS" && $4 == "" { print $1 "\t" $3 "\t" $10 }' "$SUMMARY_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
File diff suppressed because it is too large
Load Diff
156
wasm-port/tests/wasm/node/verify_nc_files_wasm.mjs
Normal file
156
wasm-port/tests/wasm/node/verify_nc_files_wasm.mjs
Normal file
@@ -0,0 +1,156 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { createLinuxCncInterpSdk } from "../../../runtime/sdk/src/index.js";
|
||||
|
||||
function verifyExpectedOutput(fixtureName, output, expectedText) {
|
||||
const expectedLines = expectedText.split("\n").filter(Boolean);
|
||||
|
||||
for (const expectedLine of expectedLines) {
|
||||
if (expectedLine.startsWith("absent=")) {
|
||||
const forbidden = expectedLine.slice("absent=".length);
|
||||
assert.equal(
|
||||
output.includes(forbidden),
|
||||
false,
|
||||
`${fixtureName}: unexpected ${forbidden}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert.equal(
|
||||
output.includes(expectedLine),
|
||||
true,
|
||||
`${fixtureName}: ${expectedLine}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const rootDir = resolve(__dirname, "../../..");
|
||||
const wasmPath = resolve(rootDir, "build/wasm/core/linuxcnc_interp.wasm");
|
||||
const ncFilesDir = resolve(rootDir, "vendor/linuxcnc/nc_files");
|
||||
|
||||
const interp = await createLinuxCncInterpSdk({
|
||||
wasmBinary: readFileSync(wasmPath),
|
||||
print() {},
|
||||
printErr(message) {
|
||||
console.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
function runVendorNcFile(filename) {
|
||||
const wasmPath = `/work/nc-files/${filename}`;
|
||||
interp.writeTextFile(wasmPath, readFileSync(resolve(ncFilesDir, filename), "utf8"));
|
||||
return interp.runFile(wasmPath);
|
||||
}
|
||||
|
||||
function runVendorNcFileWithToolTable(filename) {
|
||||
const wasmDir = "/work/nc-files-context";
|
||||
const wasmPath = `${wasmDir}/${filename}`;
|
||||
const iniPath = `${wasmDir}/nc_files_context.ini`;
|
||||
interp.writeTextFile(wasmPath, readFileSync(resolve(ncFilesDir, filename), "utf8"));
|
||||
interp.writeTextFile(
|
||||
`${wasmDir}/tool.tbl`,
|
||||
"T1 P1 D10.000000 Z+0.000000 ; nc_files representative context tool\n",
|
||||
);
|
||||
interp.writeTextFile(
|
||||
iniPath,
|
||||
[
|
||||
"[RS274NGC]",
|
||||
"SUBROUTINE_PATH = .",
|
||||
"",
|
||||
"[TRAJ]",
|
||||
"COORDINATES = X Y Z A B C U V W",
|
||||
"",
|
||||
"[EMCIO]",
|
||||
"TOOL_TABLE = tool.tbl",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
return interp.runFileWithIni(wasmPath, iniPath);
|
||||
}
|
||||
|
||||
verifyExpectedOutput(
|
||||
"nc_files_3d_chips_context_wasm",
|
||||
runVendorNcFileWithToolTable("3D_Chips.ngc"),
|
||||
[
|
||||
"file_open=0",
|
||||
"file_read_count=4706",
|
||||
"file_execute_count=4706",
|
||||
"file_saw_error=0",
|
||||
"run_step phase=execute step=18 rc=2 line=18",
|
||||
"statement_uri=N50T%23%3Ctoolno%3EM6",
|
||||
"run_step phase=execute step=19 rc=0 line=19",
|
||||
"statement_uri=N60M8",
|
||||
"canon_event=CHANGE_TOOL",
|
||||
"canon_event=START_SPINDLE_CLOCKWISE spindle=0",
|
||||
"canon_event=STRAIGHT_TRAVERSE line=21 x=0 y=0 z=10",
|
||||
"canon_event=STRAIGHT_FEED line=23 x=53 y=-56.128 z=-25.372",
|
||||
"canon_event=PROGRAM_END",
|
||||
"absent=file_error_text=",
|
||||
"absent=Requested tool 1 not found",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
verifyExpectedOutput(
|
||||
"nc_files_arcspiral_wasm",
|
||||
runVendorNcFile("arcspiral.ngc"),
|
||||
[
|
||||
"file_open=0",
|
||||
"file_read_count=1008",
|
||||
"file_execute_count=1008",
|
||||
"file_saw_error=0",
|
||||
"canon_event=STRAIGHT_TRAVERSE line=3 x=0 y=0 z=1",
|
||||
"canon_event=STRAIGHT_FEED line=6 x=1.72464 y=-1.01273 z=-0.1",
|
||||
"canon_event=PROGRAM_END",
|
||||
"absent=file_error_text=",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
verifyExpectedOutput(
|
||||
"nc_files_hole_circle_wasm",
|
||||
runVendorNcFile("hole-circle.ngc"),
|
||||
[
|
||||
"file_open=0",
|
||||
"file_read_count=12",
|
||||
"file_execute_count=12",
|
||||
"file_saw_error=0",
|
||||
"canon_event=STRAIGHT_TRAVERSE line=7 x=1.5 y=0 z=0",
|
||||
"canon_event=STRAIGHT_FEED line=10 x=0.75 y=1.29904 z=-0.4",
|
||||
"canon_event=PROGRAM_END",
|
||||
"absent=file_error_text=",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
verifyExpectedOutput(
|
||||
"nc_files_factorial_wasm",
|
||||
runVendorNcFile("factorial.ngc"),
|
||||
[
|
||||
"file_open=0",
|
||||
"file_read_count=11",
|
||||
"file_execute_count=11",
|
||||
"file_saw_error=0",
|
||||
"canon_event=PROGRAM_END",
|
||||
"absent=file_error_text=",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
verifyExpectedOutput(
|
||||
"nc_files_m6demo_wasm",
|
||||
runVendorNcFile("m6demo.ngc"),
|
||||
[
|
||||
"file_open=0",
|
||||
"file_read_count=88",
|
||||
"file_execute_count=88",
|
||||
"file_saw_error=0",
|
||||
"canon_event=SET_G5X_OFFSET index=1",
|
||||
"canon_event=STOP_SPINDLE_TURNING spindle=0",
|
||||
"canon_event=PROGRAM_END",
|
||||
"absent=file_error_text=",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
console.log("nc_files_wasm_node_smoke=ok");
|
||||
10
wasm-port/tests/wasm/node/verify_nc_files_wasm.sh
Executable file
10
wasm-port/tests/wasm/node/verify_nc_files_wasm.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
|
||||
if [[ "${SKIP_INTERP_BUILD:-0}" != "1" ]]; then
|
||||
"$ROOT_DIR/tools/build_wasm_core.sh"
|
||||
fi
|
||||
|
||||
node "$ROOT_DIR/tests/wasm/node/verify_nc_files_wasm.mjs"
|
||||
8648
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs
Normal file
8648
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs
Normal file
File diff suppressed because it is too large
Load Diff
45
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
Executable file
45
wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
NATIVE_SUMMARY="$ROOT_DIR/build/native/sim-configs/summary.tsv"
|
||||
NATIVE_CLASS_SUMMARY="$ROOT_DIR/build/native/sim-configs/class-summary.tsv"
|
||||
NATIVE_PATH_MATRIX="$ROOT_DIR/build/native/sim-configs/path-matrix.tsv"
|
||||
NATIVE_SOURCE_PROOF_SUMMARY="$ROOT_DIR/build/native/native-source-proof-summary.tsv"
|
||||
NATIVE_BUILD_DIR="$ROOT_DIR/build/native/sim-configs"
|
||||
NATIVE_SOURCE_PROOF_INPUTS=(
|
||||
"$ROOT_DIR/tools/build_native_probes.sh"
|
||||
"$ROOT_DIR/tools/source-manifest.txt"
|
||||
"$ROOT_DIR/runtime/core/linuxcnc_wrap/linuxcnc_millturn_user_m_boundary_probe.cpp"
|
||||
"$ROOT_DIR/runtime/core/linuxcnc_wrap/linuxcnc_tool_db_boundary_probe.cpp"
|
||||
"$ROOT_DIR/runtime/core/linuxcnc_wrap/linuxcnc_python_remap_boundary_probe.cpp"
|
||||
)
|
||||
|
||||
native_source_proof_refresh_required() {
|
||||
if [[ ! -f "$NATIVE_SOURCE_PROOF_SUMMARY" ]]; then
|
||||
return 0
|
||||
fi
|
||||
for input in "${NATIVE_SOURCE_PROOF_INPUTS[@]}"; do
|
||||
if [[ "$input" -nt "$NATIVE_SOURCE_PROOF_SUMMARY" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
if [[ ! -f "$NATIVE_SUMMARY" || ! -f "$NATIVE_CLASS_SUMMARY" || ! -f "$NATIVE_PATH_MATRIX" ]]; then
|
||||
"$ROOT_DIR/tests/native/verify_sim_configs.sh"
|
||||
fi
|
||||
if native_source_proof_refresh_required; then
|
||||
"$ROOT_DIR/tools/build_native_probes.sh"
|
||||
fi
|
||||
|
||||
mkdir -p "$NATIVE_BUILD_DIR"
|
||||
exec 9>"$NATIVE_BUILD_DIR/inventory.lock"
|
||||
flock 9
|
||||
|
||||
if [[ "${SKIP_INTERP_BUILD:-0}" != "1" ]]; then
|
||||
"$ROOT_DIR/tools/build_wasm_core.sh"
|
||||
fi
|
||||
|
||||
node "$ROOT_DIR/tests/wasm/node/verify_sim_configs_inventory_wasm.mjs"
|
||||
592
wasm-port/tests/wasm/node/verify_sim_configs_wasm.mjs
Normal file
592
wasm-port/tests/wasm/node/verify_sim_configs_wasm.mjs
Normal file
@@ -0,0 +1,592 @@
|
||||
import { readFileSync, statSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
analyzeIniRuntimeBoundaries,
|
||||
createLinuxCncInterpSdk,
|
||||
planIniFileContextStaging,
|
||||
planSimConfigStaging,
|
||||
} from "../../../runtime/sdk/src/index.js";
|
||||
|
||||
function verifyExpectedOutput(fixtureName, output, expectedText) {
|
||||
const expectedLines = expectedText.split("\n").filter(Boolean);
|
||||
|
||||
for (const expectedLine of expectedLines) {
|
||||
if (expectedLine.startsWith("absent=")) {
|
||||
const forbidden = expectedLine.slice("absent=".length);
|
||||
assert.equal(
|
||||
output.includes(forbidden),
|
||||
false,
|
||||
`${fixtureName}: unexpected ${forbidden}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
assert.equal(
|
||||
output.includes(expectedLine),
|
||||
true,
|
||||
`${fixtureName}: ${expectedLine}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
const rootDir = resolve(__dirname, "../../..");
|
||||
const wasmPath = resolve(rootDir, "build/wasm/core/linuxcnc_interp.wasm");
|
||||
const vendorRoot = resolve(rootDir, "vendor/linuxcnc");
|
||||
const manifestText = readFileSync(resolve(rootDir, "tools/source-manifest.txt"), "utf8");
|
||||
|
||||
const interp = await createLinuxCncInterpSdk({
|
||||
wasmBinary: readFileSync(wasmPath),
|
||||
print() {},
|
||||
printErr(message) {
|
||||
console.error(message);
|
||||
},
|
||||
});
|
||||
|
||||
function readSimIni(machineRel, iniFile) {
|
||||
return readFileSync(resolve(vendorRoot, `configs/sim/${machineRel}/${iniFile}`), "utf8");
|
||||
}
|
||||
|
||||
const syntheticPlan = planSimConfigStaging({
|
||||
manifestText: [
|
||||
"configs/sim/example/test.ini",
|
||||
"configs/sim/example/test.ngc",
|
||||
"configs/sim/example/tool.tbl",
|
||||
"configs/sim/example/linuxcnc.var",
|
||||
"configs/sim/example/subs/helper.ngc",
|
||||
"configs/sim/example/M123",
|
||||
].join("\n"),
|
||||
machineRel: "example",
|
||||
iniFile: "test.ini",
|
||||
iniText: [
|
||||
"[DISPLAY]",
|
||||
"OPEN_FILE = test.ngc",
|
||||
"[RS274NGC]",
|
||||
"PARAMETER_FILE = linuxcnc.var",
|
||||
"SUBROUTINE_PATH = subs",
|
||||
"USER_M_PATH = .",
|
||||
"[EMCIO]",
|
||||
"TOOL_TABLE = tool.tbl",
|
||||
].join("\n"),
|
||||
});
|
||||
assert.deepEqual(
|
||||
syntheticPlan.files.map((file) => [file.sourceRel, file.executable]).sort(),
|
||||
[
|
||||
["configs/sim/example/M123", true],
|
||||
["configs/sim/example/linuxcnc.var", false],
|
||||
["configs/sim/example/subs/helper.ngc", false],
|
||||
["configs/sim/example/test.ini", false],
|
||||
["configs/sim/example/test.ngc", false],
|
||||
["configs/sim/example/tool.tbl", false],
|
||||
],
|
||||
"synthetic sim-config staging plan",
|
||||
);
|
||||
|
||||
const syntheticBoundary = analyzeIniRuntimeBoundaries({
|
||||
manifestText: [
|
||||
"configs/sim/example/test.ini",
|
||||
"configs/sim/example/test.ngc",
|
||||
"configs/sim/example/tool.tbl",
|
||||
"configs/sim/example/M123",
|
||||
].join("\n"),
|
||||
sourceRootRel: "configs/sim/example",
|
||||
sourceSearchRootRel: "configs/sim",
|
||||
iniFile: "test.ini",
|
||||
iniText: [
|
||||
"[DISPLAY]",
|
||||
"DISPLAY = axis",
|
||||
"OPEN_FILE = test.ngc",
|
||||
"[RS274NGC]",
|
||||
"USER_M_PATH = .",
|
||||
"[EMCIO]",
|
||||
"TOOL_TABLE = tool.tbl",
|
||||
].join("\n"),
|
||||
});
|
||||
assert.deepEqual(
|
||||
syntheticBoundary,
|
||||
{
|
||||
dependencies: [],
|
||||
recommendedBlockedKind: "-",
|
||||
toolDatabaseProgram: "",
|
||||
halRuntime: { values: [], requiresProcess: false },
|
||||
uiRuntime: { values: ["axis"], requiresProcess: false },
|
||||
haluiRuntime: { mdiCommands: [], requiresProcess: false },
|
||||
userMRuntime: {
|
||||
paths: ["."],
|
||||
vendoredExecutableCount: 1,
|
||||
executionCodes: [],
|
||||
unstagedExecutionCodes: [],
|
||||
requiresExternalProcess: false,
|
||||
},
|
||||
pythonRuntime: { references: [], remapReferences: [], uiReferences: [], requiresProcess: false },
|
||||
},
|
||||
"synthetic runtime-boundary analysis",
|
||||
);
|
||||
|
||||
const mixedUserMBoundary = analyzeIniRuntimeBoundaries({
|
||||
manifestText: [
|
||||
"configs/sim/example/test.ini",
|
||||
"configs/sim/example/test.ngc",
|
||||
"configs/sim/example/M123",
|
||||
].join("\n"),
|
||||
sourceRootRel: "configs/sim/example",
|
||||
sourceSearchRootRel: "configs/sim",
|
||||
iniFile: "test.ini",
|
||||
iniText: [
|
||||
"[DISPLAY]",
|
||||
"DISPLAY = axis",
|
||||
"[RS274NGC]",
|
||||
"USER_M_PATH = .",
|
||||
].join("\n"),
|
||||
executionTexts: ["M123\nM124\n"],
|
||||
});
|
||||
assert.equal(
|
||||
mixedUserMBoundary.recommendedBlockedKind,
|
||||
"L4-USER-M-PROCESS",
|
||||
"mixed vendored and unstaged user-M calls must block",
|
||||
);
|
||||
assert.deepEqual(
|
||||
mixedUserMBoundary.userMRuntime.executionCodes,
|
||||
["M123", "M124"],
|
||||
"mixed user-M execution codes",
|
||||
);
|
||||
assert.deepEqual(
|
||||
mixedUserMBoundary.userMRuntime.unstagedExecutionCodes,
|
||||
["M124"],
|
||||
"only unstaged user-M execution codes require the external process boundary",
|
||||
);
|
||||
assert.deepEqual(
|
||||
syntheticPlan.files.map((file) => [file.sourceRel, file.wasmPath]).sort(),
|
||||
[
|
||||
["configs/sim/example/M123", "/work/sim/example/M123"],
|
||||
["configs/sim/example/linuxcnc.var", "/work/sim/example/linuxcnc.var"],
|
||||
["configs/sim/example/subs/helper.ngc", "/work/sim/example/subs/helper.ngc"],
|
||||
["configs/sim/example/test.ini", "/work/sim/example/test.ini"],
|
||||
["configs/sim/example/test.ngc", "/work/sim/example/test.ngc"],
|
||||
["configs/sim/example/tool.tbl", "/work/sim/example/tool.tbl"],
|
||||
],
|
||||
"synthetic sim-config staging wasm paths",
|
||||
);
|
||||
|
||||
const genericPlan = planIniFileContextStaging({
|
||||
manifestText: [
|
||||
"tests/interp/example/test.ini",
|
||||
"tests/interp/example/test.ngc",
|
||||
"tests/interp/example/test.tbl",
|
||||
"tests/interp/example/startup.var",
|
||||
"tests/interp/example/subs/helper.ngc",
|
||||
"tests/interp/example/subs/rm145.ngc",
|
||||
"tests/interp/example/more-subs/other.ngc",
|
||||
"tests/interp/example/M145",
|
||||
].join("\n"),
|
||||
sourceRootRel: "tests/interp/example",
|
||||
iniFile: "test.ini",
|
||||
iniText: [
|
||||
"[DISPLAY]",
|
||||
"OPEN_FILE = test.ngc",
|
||||
"[RS274NGC]",
|
||||
"PARAMETER_FILE = startup.var",
|
||||
"SUBROUTINE_PATH = subs:more-subs",
|
||||
"USER_M_PATH = .",
|
||||
"REMAP = M145 modalgroup=10 ngc=rm145",
|
||||
"[EMCIO]",
|
||||
"TOOL_TABLE = test.tbl",
|
||||
].join("\n"),
|
||||
wasmDir: "/work/interp/example",
|
||||
});
|
||||
assert.deepEqual(
|
||||
genericPlan.files.map((file) => [file.sourceRel, file.wasmPath, file.executable]).sort(),
|
||||
[
|
||||
["tests/interp/example/M145", "/work/interp/example/M145", true],
|
||||
["tests/interp/example/more-subs/other.ngc", "/work/interp/example/more-subs/other.ngc", false],
|
||||
["tests/interp/example/startup.var", "/work/interp/example/startup.var", false],
|
||||
["tests/interp/example/subs/helper.ngc", "/work/interp/example/subs/helper.ngc", false],
|
||||
["tests/interp/example/subs/rm145.ngc", "/work/interp/example/subs/rm145.ngc", false],
|
||||
["tests/interp/example/test.ini", "/work/interp/example/test.ini", false],
|
||||
["tests/interp/example/test.ngc", "/work/interp/example/test.ngc", false],
|
||||
["tests/interp/example/test.tbl", "/work/interp/example/test.tbl", false],
|
||||
],
|
||||
"generic INI file-context staging plan",
|
||||
);
|
||||
|
||||
function simBoundary(machineRel, iniFile, executionRelFiles = []) {
|
||||
return analyzeIniRuntimeBoundaries({
|
||||
manifestText,
|
||||
sourceRootRel: `configs/sim/${machineRel}`,
|
||||
sourceSearchRootRel: "configs/sim",
|
||||
iniFile,
|
||||
iniText: readSimIni(machineRel, iniFile),
|
||||
executionTexts: executionRelFiles.map((relFile) =>
|
||||
readFileSync(resolve(vendorRoot, `configs/sim/${machineRel}/${relFile}`), "utf8"),
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
const dbDemoBoundary = simBoundary("axis/db_demo", "db_nonran.ini");
|
||||
assert.deepEqual(
|
||||
dbDemoBoundary.dependencies,
|
||||
["python_runtime", "tool_database_process"],
|
||||
"db_demo tool database boundary dependencies",
|
||||
);
|
||||
assert.equal(dbDemoBoundary.recommendedBlockedKind, "L4-TOOL-DB", "db_demo blocked kind");
|
||||
assert.equal(dbDemoBoundary.toolDatabaseProgram, "./db_nonran.py", "db_demo DB_PROGRAM");
|
||||
assert.deepEqual(
|
||||
dbDemoBoundary.pythonRuntime.uiReferences,
|
||||
["./db_nonran.py"],
|
||||
"db_demo Python dependency is the DB program process",
|
||||
);
|
||||
assert.deepEqual(
|
||||
dbDemoBoundary.pythonRuntime.remapReferences,
|
||||
[],
|
||||
"db_demo does not declare Python remap runtime",
|
||||
);
|
||||
|
||||
const millturnBoundary = simBoundary("axis/vismach/millturn", "millturn.ini", [
|
||||
"example.ngc",
|
||||
"remap_subs/428remap.ngc",
|
||||
"remap_subs/429remap.ngc",
|
||||
]);
|
||||
assert.equal(
|
||||
millturnBoundary.recommendedBlockedKind,
|
||||
"L4-USER-M-PROCESS",
|
||||
"millturn blocked kind",
|
||||
);
|
||||
assert.deepEqual(
|
||||
millturnBoundary.dependencies,
|
||||
["external_user_m_process", "hal_process", "halui_mdi_process", "ui_process"],
|
||||
"millturn full-process boundary dependencies",
|
||||
);
|
||||
assert.equal(
|
||||
millturnBoundary.userMRuntime.vendoredExecutableCount,
|
||||
0,
|
||||
"millturn external user-M files are not staged",
|
||||
);
|
||||
assert.deepEqual(
|
||||
millturnBoundary.userMRuntime.executionCodes,
|
||||
["M128", "M129"],
|
||||
"millturn execution chain calls external user-M processes",
|
||||
);
|
||||
assert.deepEqual(
|
||||
millturnBoundary.userMRuntime.unstagedExecutionCodes,
|
||||
["M128", "M129"],
|
||||
"millturn execution chain calls unstaged user-M processes",
|
||||
);
|
||||
|
||||
const pumaBoundary = simBoundary("axis/vismach/puma", "puma_cube.ini");
|
||||
assert.equal(pumaBoundary.recommendedBlockedKind, "-", "puma_cube representative is not blocked");
|
||||
assert.equal(pumaBoundary.userMRuntime.vendoredExecutableCount, 0, "puma_cube has no vendored user-M processes");
|
||||
assert.equal(pumaBoundary.halRuntime.requiresProcess, true, "puma_cube declares HAL process files");
|
||||
assert.equal(pumaBoundary.haluiRuntime.requiresProcess, true, "puma_cube declares HALUI MDI commands");
|
||||
|
||||
const melfaBoundary = simBoundary("axis/vismach/melfa-sim", "melfa.ini");
|
||||
assert.equal(melfaBoundary.recommendedBlockedKind, "-", "melfa representative is not blocked");
|
||||
assert.deepEqual(
|
||||
melfaBoundary.dependencies,
|
||||
["hal_process", "halui_mdi_process", "ui_process"],
|
||||
"melfa declares HAL/UI/HALUI process dependencies",
|
||||
);
|
||||
assert.equal(melfaBoundary.pythonRuntime.requiresProcess, false, "melfa representative has no Python runtime");
|
||||
|
||||
const woodpeckerBoundary = simBoundary("woodpecker", "woodpecker.ini");
|
||||
assert.equal(woodpeckerBoundary.recommendedBlockedKind, "-", "woodpecker representative is not blocked");
|
||||
assert.deepEqual(
|
||||
woodpeckerBoundary.dependencies,
|
||||
["hal_process", "ui_process"],
|
||||
"woodpecker declares HAL/UI process dependencies",
|
||||
);
|
||||
assert.equal(woodpeckerBoundary.haluiRuntime.requiresProcess, false, "woodpecker representative has no HALUI MDI");
|
||||
|
||||
const gladevcpBoundary = simBoundary("axis/gladevcp", "gladevcp_panel.ini");
|
||||
assert.equal(gladevcpBoundary.recommendedBlockedKind, "-", "gladevcp probe representative is not blocked");
|
||||
assert.ok(
|
||||
gladevcpBoundary.dependencies.includes("ui_process"),
|
||||
"gladevcp declares UI process dependency",
|
||||
);
|
||||
assert.equal(gladevcpBoundary.pythonRuntime.requiresProcess, true, "gladevcp declares Python UI handler");
|
||||
assert.deepEqual(
|
||||
gladevcpBoundary.pythonRuntime.uiReferences,
|
||||
["-u hitcounter.py manual-example.ui"],
|
||||
"gladevcp Python dependency is a UI handler",
|
||||
);
|
||||
assert.deepEqual(
|
||||
gladevcpBoundary.pythonRuntime.remapReferences,
|
||||
[],
|
||||
"gladevcp representative must not be treated as Python remap runtime",
|
||||
);
|
||||
|
||||
function simMachine(machineRel, iniFile, programFile = null) {
|
||||
const iniText = readFileSync(
|
||||
resolve(vendorRoot, `configs/sim/${machineRel}/${iniFile}`),
|
||||
"utf8",
|
||||
);
|
||||
const plan = planSimConfigStaging({
|
||||
manifestText,
|
||||
machineRel,
|
||||
iniFile,
|
||||
iniText,
|
||||
programFile,
|
||||
wasmDir: `/work/sim/${machineRel}`,
|
||||
});
|
||||
return {
|
||||
...plan,
|
||||
files: plan.files.map((file) => ({
|
||||
path: file.path,
|
||||
text: readFileSync(resolve(vendorRoot, file.sourceRel), "utf8"),
|
||||
executable:
|
||||
file.executable || (statSync(resolve(vendorRoot, file.sourceRel)).mode & 0o111) !== 0,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
function wrapSimProgram(machine, wrappedName) {
|
||||
const sourceFile = machine.files.find((file) => file.path === machine.programPath);
|
||||
assert.ok(sourceFile, `${machine.programPath}: wrapped source file`);
|
||||
return {
|
||||
...machine,
|
||||
programPath: `${machine.wasmDir}/${wrappedName}`,
|
||||
files: [
|
||||
...machine.files,
|
||||
{
|
||||
path: `${machine.wasmDir}/${wrappedName}`,
|
||||
text: `${sourceFile.text}\nM2\n`,
|
||||
executable: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const foam = simMachine("axis/foam", "axis_foam.ini", "foam.ngc");
|
||||
verifyExpectedOutput(
|
||||
"sim_axis_foam_uv_wasm",
|
||||
interp.runSimConfigProgram({
|
||||
files: foam.files,
|
||||
programPath: foam.programPath,
|
||||
iniPath: foam.iniPath,
|
||||
}),
|
||||
[
|
||||
"file_open=0",
|
||||
"file_saw_error=0",
|
||||
"canon_event=PROGRAM_END",
|
||||
"absent=Bad character 'u' used",
|
||||
"absent=Bad character 'v' used",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
const bridgeMill = simMachine(
|
||||
"axis/vismach/5axis/bridgemill",
|
||||
"5axis.ini",
|
||||
"5axisgui.ngc",
|
||||
);
|
||||
verifyExpectedOutput(
|
||||
"sim_axis_bridgemill_w_wasm",
|
||||
interp.runSimConfigProgram({
|
||||
files: bridgeMill.files,
|
||||
programPath: bridgeMill.programPath,
|
||||
iniPath: bridgeMill.iniPath,
|
||||
executionMode: "fiveAxisRemap",
|
||||
}),
|
||||
[
|
||||
"fiveaxis_ini_open=1",
|
||||
"fiveaxis_tool_table_load=0",
|
||||
"fiveaxis_parse_remap_1=0",
|
||||
"fiveaxis_parse_remap_2=0",
|
||||
"fiveaxis_parse_remap_3=0",
|
||||
"fiveaxis_parsed_remap_count=3",
|
||||
"fiveaxis_remaps_ready=1",
|
||||
"fiveaxis_file_open=0",
|
||||
"fiveaxis_file_read_count=2197",
|
||||
"fiveaxis_file_execute_count=2197",
|
||||
"fiveaxis_file_finish_count=137",
|
||||
"fiveaxis_file_reached_exit=1",
|
||||
"fiveaxis_hal_switchkins: rc=0 found=1 value=0",
|
||||
"fiveaxis_linuxcnc_remap_file_execute=1",
|
||||
"absent=Bad character 'w' used",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
const geometry = simMachine("axis/geometry", "xyzc.ini", "xyzc.ngc");
|
||||
verifyExpectedOutput(
|
||||
"sim_axis_geometry_xyzc_user_m110_wasm",
|
||||
interp.runSimConfigProgram({
|
||||
files: geometry.files,
|
||||
programPath: geometry.programPath,
|
||||
iniPath: geometry.iniPath,
|
||||
}),
|
||||
[
|
||||
"file_open=0",
|
||||
"file_saw_error=0",
|
||||
"canon_event=USER_M_COMMAND code=M110",
|
||||
"canon_event=PROGRAM_END",
|
||||
"absent=Unknown m code used: M110",
|
||||
"absent=Bad character 'c' used",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
const gladevcpProbe = simMachine("axis/gladevcp", "gladevcp_panel.ini", "probe.ngc");
|
||||
verifyExpectedOutput(
|
||||
"sim_axis_gladevcp_probe_wasm",
|
||||
interp.runSimConfigProgram({
|
||||
files: gladevcpProbe.files,
|
||||
programPath: gladevcpProbe.programPath,
|
||||
iniPath: gladevcpProbe.iniPath,
|
||||
}),
|
||||
[
|
||||
"file_open=0",
|
||||
"file_saw_error=0",
|
||||
"canon_event=PROGRAM_END",
|
||||
"absent=file_error_text=",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
const externalOffsetsMacro = wrapSimProgram(
|
||||
simMachine("axis/external_offsets", "dynamic_offsets.ini", "circles.ngc"),
|
||||
"__wrapped_circles.ngc",
|
||||
);
|
||||
verifyExpectedOutput(
|
||||
"sim_axis_external_offsets_circles_macro_wasm",
|
||||
interp.runSimConfigProgram({
|
||||
files: externalOffsetsMacro.files,
|
||||
programPath: externalOffsetsMacro.programPath,
|
||||
iniPath: externalOffsetsMacro.iniPath,
|
||||
}),
|
||||
[
|
||||
"file_open=0",
|
||||
"file_execute_45=1",
|
||||
"statement_uri=M2",
|
||||
"file_read_count=45",
|
||||
"file_execute_count=45",
|
||||
"file_saw_error=0",
|
||||
"canon_event=COMMENT: info: Multiple circles at radius R, use 0 for current val",
|
||||
"canon_event=PROGRAM_END",
|
||||
"post_execute.origin_index=1",
|
||||
"post_execute.mist=0",
|
||||
"post_execute.flood=0",
|
||||
"absent=file_error_text=",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
for (const externalOffsetCase of [
|
||||
{
|
||||
name: "dyn_demo",
|
||||
program: "dyn_demo.ngc",
|
||||
ini: "dynamic_offsets.ini",
|
||||
readCount: 833,
|
||||
firstTraverse: "canon_event=STRAIGHT_TRAVERSE line=21 x=0 y=0 z=0",
|
||||
firstFeed: "canon_event=STRAIGHT_FEED line=26 x=9 y=0 z=1.5",
|
||||
},
|
||||
{
|
||||
name: "eoffsets",
|
||||
program: "eoffsets.ngc",
|
||||
ini: "eoffsets.ini",
|
||||
readCount: 35,
|
||||
firstTraverse: "canon_event=STRAIGHT_TRAVERSE line=24 x=0 y=0 z=0",
|
||||
firstFeed: "canon_event=STRAIGHT_FEED line=27 x=2.25 y=2.64 z=0",
|
||||
},
|
||||
{
|
||||
name: "jwp_z",
|
||||
program: "jwp_z.ngc",
|
||||
ini: "jwp_z.ini",
|
||||
readCount: 35,
|
||||
firstTraverse: "canon_event=STRAIGHT_TRAVERSE line=17 x=0 y=0 z=0.5",
|
||||
firstFeed: "canon_event=STRAIGHT_FEED line=24 x=2.5 y=3.3 z=0",
|
||||
},
|
||||
{
|
||||
name: "opa_demo",
|
||||
program: "opa_demo.ngc",
|
||||
ini: "opa.ini",
|
||||
readCount: 58,
|
||||
firstTraverse: "canon_event=STRAIGHT_TRAVERSE line=13 x=1 y=0 z=0",
|
||||
firstFeed: "canon_event=STRAIGHT_FEED line=35 x=1 y=0 z=0",
|
||||
},
|
||||
]) {
|
||||
const externalOffsets = simMachine(
|
||||
"axis/external_offsets",
|
||||
externalOffsetCase.ini,
|
||||
externalOffsetCase.program,
|
||||
);
|
||||
verifyExpectedOutput(
|
||||
`sim_axis_external_offsets_${externalOffsetCase.name}_user_m111_wasm`,
|
||||
interp.runSimConfigProgram({
|
||||
files: externalOffsets.files,
|
||||
programPath: externalOffsets.programPath,
|
||||
iniPath: externalOffsets.iniPath,
|
||||
}),
|
||||
[
|
||||
"file_open=0",
|
||||
`file_read_count=${externalOffsetCase.readCount}`,
|
||||
`file_execute_count=${externalOffsetCase.readCount}`,
|
||||
"file_saw_error=0",
|
||||
"canon_event=USER_M_COMMAND code=M111",
|
||||
externalOffsetCase.firstTraverse,
|
||||
externalOffsetCase.firstFeed,
|
||||
"canon_event=PROGRAM_END",
|
||||
"absent=Unknown m code used: M111",
|
||||
"absent=Bad character",
|
||||
"absent=Cannot use axis values",
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
const melfaSim = simMachine("axis/vismach/melfa-sim", "melfa.ini", "example.ngc");
|
||||
verifyExpectedOutput(
|
||||
"sim_axis_vismach_melfa_wasm",
|
||||
interp.runSimConfigProgram({
|
||||
files: melfaSim.files,
|
||||
programPath: melfaSim.programPath,
|
||||
iniPath: melfaSim.iniPath,
|
||||
executionMode: "fiveAxisRemap",
|
||||
}),
|
||||
[
|
||||
"fiveaxis_ini_open=1",
|
||||
"fiveaxis_tool_table_load=0",
|
||||
"fiveaxis_file_open=0",
|
||||
"fiveaxis_file_reached_exit=1",
|
||||
"fiveaxis_linuxcnc_remap_file_execute=1",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
const pumaCube = simMachine("axis/vismach/puma", "puma_cube.ini", "puma_cube.ngc");
|
||||
verifyExpectedOutput(
|
||||
"sim_axis_vismach_puma_cube_wasm",
|
||||
interp.runSimConfigProgram({
|
||||
files: pumaCube.files,
|
||||
programPath: pumaCube.programPath,
|
||||
iniPath: pumaCube.iniPath,
|
||||
}),
|
||||
[
|
||||
"file_open=0",
|
||||
"file_saw_error=0",
|
||||
"canon_event=PROGRAM_END",
|
||||
"absent=file_error_text=",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
const woodpeckerOnAbort = simMachine("woodpecker", "woodpecker.ini", "on_abort.ngc");
|
||||
verifyExpectedOutput(
|
||||
"sim_woodpecker_on_abort_wasm",
|
||||
interp.runSimConfigProgram({
|
||||
files: woodpeckerOnAbort.files,
|
||||
programPath: woodpeckerOnAbort.programPath,
|
||||
iniPath: woodpeckerOnAbort.iniPath,
|
||||
}),
|
||||
[
|
||||
"file_open=0",
|
||||
"run_step phase=execute step=17 rc=1 line=17",
|
||||
"statement_uri=m2",
|
||||
"file_read_count=17",
|
||||
"file_execute_count=17",
|
||||
"file_saw_error=0",
|
||||
"canon_event=ON_RESET",
|
||||
"canon_event=PROGRAM_END",
|
||||
"post_execute.origin_index=1",
|
||||
"post_execute.mist=0",
|
||||
"post_execute.flood=0",
|
||||
"absent=file_error_text=",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
console.log("sim_configs_wasm_node_smoke=ok");
|
||||
10
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
Executable file
10
wasm-port/tests/wasm/node/verify_sim_configs_wasm.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||
|
||||
if [[ "${SKIP_INTERP_BUILD:-0}" != "1" ]]; then
|
||||
"$ROOT_DIR/tools/build_wasm_core.sh"
|
||||
fi
|
||||
|
||||
node "$ROOT_DIR/tests/wasm/node/verify_sim_configs_wasm.mjs"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -65,6 +65,7 @@ INTERP_CORE_SOURCES=(
|
||||
"$WRAP_DIR/linuxcnc_hal_adapter.cpp"
|
||||
"$WRAP_DIR/linuxcnc_emc_status_runtime.cpp"
|
||||
"$WRAP_DIR/linuxcnc_interp_python_edge_stubs.cpp"
|
||||
"$WRAP_DIR/linuxcnc_machine_config.cpp"
|
||||
"$WRAP_DIR/linuxcnc_runtime_state_stubs.cpp"
|
||||
"$WRAP_DIR/linuxcnc_tool_adapter.cpp"
|
||||
"$WRAP_DIR/linuxcnc_interp_minimal_runtime.cpp"
|
||||
@@ -97,5 +98,5 @@ link_wasm_module \
|
||||
-s STACK_SIZE=2MB \
|
||||
-s NO_EXIT_RUNTIME=1 \
|
||||
-s FORCE_FILESYSTEM=1 \
|
||||
-s EXPORTED_FUNCTIONS='["_malloc","_free","_lcinterp_run_program","_lcinterp_run_program_with_ini","_lcinterp_run_file","_lcinterp_run_file_with_ini","_lcinterp_run_fiveaxis_remap_file","_lcinterp_restore_parameters","_lcinterp_save_parameters","_lcinterp_load_tool_table","_lcinterp_load_tool_table_random","_lcinterp_save_tool_table","_lcinterp_probe_init_and_synch","_lcinterp_probe_indexer","_lcinterp_probe_named_parameters","_lcinterp_free_string"]' \
|
||||
-s EXPORTED_FUNCTIONS='["_malloc","_free","_lcinterp_run_program","_lcinterp_run_program_with_ini","_lcinterp_run_file","_lcinterp_run_file_with_ini","_lcinterp_run_file_with_ini_continue_on_error","_lcinterp_run_fiveaxis_remap_file","_lcinterp_run_remap_file","_lcinterp_run_remap_file_continue_on_error","_lcinterp_run_remap_io_mdi_sequence","_lcinterp_restore_parameters","_lcinterp_save_parameters","_lcinterp_load_tool_table","_lcinterp_load_tool_table_random","_lcinterp_save_tool_table","_lcinterp_probe_init_and_synch","_lcinterp_probe_indexer","_lcinterp_probe_named_parameters","_lcinterp_free_string"]' \
|
||||
-s EXPORTED_RUNTIME_METHODS='["FS","UTF8ToString","stringToUTF8","lengthBytesUTF8"]'
|
||||
|
||||
@@ -17,7 +17,13 @@ copy_file() {
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$dst")"
|
||||
cp "$src" "$dst"
|
||||
if [[ -f "$dst" ]] && cmp -s "$src" "$dst"; then
|
||||
touch -r "$src" "$dst"
|
||||
echo "unchanged $src_rel"
|
||||
return
|
||||
fi
|
||||
|
||||
cp -p "$src" "$dst"
|
||||
echo "copied $src_rel"
|
||||
}
|
||||
|
||||
@@ -26,9 +32,29 @@ if [[ ! -f "$MANIFEST_FILE" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
validate_manifest_path() {
|
||||
local src_rel="$1"
|
||||
if [[ "$src_rel" = /* || "$src_rel" == */ || "$src_rel" == *"//"* ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local component
|
||||
local -a path_components
|
||||
IFS=/ read -r -a path_components <<< "$src_rel"
|
||||
for component in "${path_components[@]}"; do
|
||||
if [[ -z "$component" || "$component" == "." || "$component" == ".." ]]; then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
while IFS= read -r src_rel; do
|
||||
[[ -z "$src_rel" ]] && continue
|
||||
[[ "${src_rel:0:1}" == "#" ]] && continue
|
||||
if ! validate_manifest_path "$src_rel"; then
|
||||
echo "invalid manifest path: $src_rel" >&2
|
||||
exit 1
|
||||
fi
|
||||
copy_file "$src_rel"
|
||||
done < "$MANIFEST_FILE"
|
||||
|
||||
|
||||
@@ -150,6 +150,365 @@ src/emc/rs274ngc/interp_remap.cc
|
||||
src/emc/rs274ngc/interp_convert.cc
|
||||
src/emc/rs274ngc/interp_cycles.cc
|
||||
src/emc/rs274ngc/interp_g7x.cc
|
||||
tests/remap/duplicate-o-word/README
|
||||
tests/remap/duplicate-o-word/expected
|
||||
tests/remap/duplicate-o-word/rm207.ngc
|
||||
tests/remap/duplicate-o-word/rm208.ngc
|
||||
tests/remap/duplicate-o-word/test.ini
|
||||
tests/remap/duplicate-o-word/test.ngc
|
||||
tests/remap/duplicate-o-word/test.sh
|
||||
tests/remap/fail/args.0/README
|
||||
tests/remap/fail/args.0/expected
|
||||
tests/remap/fail/args.0/rm400.ngc
|
||||
tests/remap/fail/args.0/test.ini
|
||||
tests/remap/fail/args.0/test.ngc
|
||||
tests/remap/fail/args.0/test.sh
|
||||
tests/remap/fail/args.1/README
|
||||
tests/remap/fail/args.1/expected
|
||||
tests/remap/fail/args.1/rm400.ngc
|
||||
tests/remap/fail/args.1/test.ini
|
||||
tests/remap/fail/args.1/test.ngc
|
||||
tests/remap/fail/args.1/test.sh
|
||||
tests/remap/fail/args.2/README
|
||||
tests/remap/fail/args.2/expected
|
||||
tests/remap/fail/args.2/rm400.ngc
|
||||
tests/remap/fail/args.2/test.ini
|
||||
tests/remap/fail/args.2/test.ngc
|
||||
tests/remap/fail/args.2/test.sh
|
||||
tests/remap/fail/body-ngc/README
|
||||
tests/remap/fail/body-ngc/expected
|
||||
tests/remap/fail/body-ngc/rm400.ngc
|
||||
tests/remap/fail/body-ngc/test.ini
|
||||
tests/remap/fail/body-ngc/test.ngc
|
||||
tests/remap/fail/body-ngc/test.sh
|
||||
tests/remap/m30-interaction/README
|
||||
tests/remap/m30-interaction/expected
|
||||
tests/remap/m30-interaction/rm400.ngc
|
||||
tests/remap/m30-interaction/test.ini
|
||||
tests/remap/m30-interaction/test.ngc
|
||||
tests/remap/m30-interaction/test.sh
|
||||
tests/remap/nested-remaps-oword/README
|
||||
tests/remap/nested-remaps-oword/expected
|
||||
tests/remap/nested-remaps-oword/rm400.ngc
|
||||
tests/remap/nested-remaps-oword/rm401.ngc
|
||||
tests/remap/nested-remaps-oword/rm402.ngc
|
||||
tests/remap/nested-remaps-oword/rm403.ngc
|
||||
tests/remap/nested-remaps-oword/test.ini
|
||||
tests/remap/nested-remaps-oword/test.ngc
|
||||
tests/remap/nested-remaps-oword/test.sh
|
||||
tests/remap/nested-remaps-oword/testsub.ngc
|
||||
tests/remap/posargs.0/README
|
||||
tests/remap/posargs.0/expected
|
||||
tests/remap/posargs.0/rg881.ngc
|
||||
tests/remap/posargs.0/test.ini
|
||||
tests/remap/posargs.0/test.ngc
|
||||
tests/remap/posargs.0/test.sh
|
||||
tests/remap/sequencing/README
|
||||
tests/remap/sequencing/expected
|
||||
tests/remap/sequencing/permute.py
|
||||
tests/remap/sequencing/rg881.ngc
|
||||
tests/remap/sequencing/rm405.ngc
|
||||
tests/remap/sequencing/rm406.ngc
|
||||
tests/remap/sequencing/rm407.ngc
|
||||
tests/remap/sequencing/rm408.ngc
|
||||
tests/remap/sequencing/rm409.ngc
|
||||
tests/remap/sequencing/rm410.ngc
|
||||
tests/remap/sequencing/test.ini
|
||||
tests/remap/sequencing/test.ngc
|
||||
tests/remap/sequencing/test.sh
|
||||
tests/remap/remap-io/README
|
||||
tests/remap/remap-io/expected
|
||||
tests/remap/remap-io/io_input_m66.ngc
|
||||
tests/remap/remap-io/io_output_m62.ngc
|
||||
tests/remap/remap-io/io_output_m63.ngc
|
||||
tests/remap/remap-io/io_output_m64.ngc
|
||||
tests/remap/remap-io/io_output_m65.ngc
|
||||
tests/remap/remap-io/io_output_m67.ngc
|
||||
tests/remap/remap-io/io_output_m68.ngc
|
||||
tests/remap/remap-io/test-ngc.ini
|
||||
tests/remap/remap-io/test.sh
|
||||
tests/interp/m98m99/01-basics/expected
|
||||
tests/interp/m98m99/01-basics/test.ngc
|
||||
tests/interp/m98m99/01-basics/test.sh
|
||||
tests/interp/m98m99/02-variables/expected
|
||||
tests/interp/m98m99/02-variables/test.ngc
|
||||
tests/interp/m98m99/02-variables/test.sh
|
||||
tests/interp/m98m99/03-error-M98-no-P-word/expected
|
||||
tests/interp/m98m99/03-error-M98-no-P-word/test.ngc
|
||||
tests/interp/m98m99/03-error-M98-no-P-word/test.sh
|
||||
tests/interp/m98m99/04-M98-but-no-sub/expected
|
||||
tests/interp/m98m99/04-M98-but-no-sub/test.ngc
|
||||
tests/interp/m98m99/04-M98-but-no-sub/test.sh
|
||||
tests/interp/m98m99/05-M98-loops/expected
|
||||
tests/interp/m98m99/05-M98-loops/test.ngc
|
||||
tests/interp/m98m99/05-M98-loops/test.sh
|
||||
tests/interp/m98m99/06-error-mixed-sub-styles/O...-called-with-O..._call.ngc
|
||||
tests/interp/m98m99/06-error-mixed-sub-styles/O...-ended-with-O..._endsub.ngc
|
||||
tests/interp/m98m99/06-error-mixed-sub-styles/O...-sub-called-with-M98.ngc
|
||||
tests/interp/m98m99/06-error-mixed-sub-styles/O...-sub-ended-with-M99.ngc
|
||||
tests/interp/m98m99/06-error-mixed-sub-styles/expected
|
||||
tests/interp/m98m99/06-error-mixed-sub-styles/test.sh
|
||||
tests/interp/m98m99/07-nested-subs/expected
|
||||
tests/interp/m98m99/07-nested-subs/test.ngc
|
||||
tests/interp/m98m99/07-nested-subs/test.sh
|
||||
tests/interp/m98m99/08-sub-follows-main/expected
|
||||
tests/interp/m98m99/08-sub-follows-main/test.ngc
|
||||
tests/interp/m98m99/08-sub-follows-main/test.sh
|
||||
tests/interp/m98m99/09-disable-fanuc-subs/expected
|
||||
tests/interp/m98m99/09-disable-fanuc-subs/test-fanuc.ini
|
||||
tests/interp/m98m99/09-disable-fanuc-subs/test-fanuc.ngc
|
||||
tests/interp/m98m99/09-disable-fanuc-subs/test-no-fanuc.ini
|
||||
tests/interp/m98m99/09-disable-fanuc-subs/test-rs274ngc.ngc
|
||||
tests/interp/m98m99/09-disable-fanuc-subs/test.sh
|
||||
tests/interp/m98m99/10-M98-P001/expected
|
||||
tests/interp/m98m99/10-M98-P001/test.ngc
|
||||
tests/interp/m98m99/10-M98-P001/test.sh
|
||||
tests/interp/m98m99/11-main-program-oword/expected
|
||||
tests/interp/m98m99/11-main-program-oword/test-illegal-end-main-with-eof.ngc
|
||||
tests/interp/m98m99/11-main-program-oword/test-illegal-no-m30-before-osub.ngc
|
||||
tests/interp/m98m99/11-main-program-oword/test-illegal-sub-after-percent.ngc
|
||||
tests/interp/m98m99/11-main-program-oword/test-legal-end-main-with-m02.ngc
|
||||
tests/interp/m98m99/11-main-program-oword/test-legal-end-main-with-m2.ngc
|
||||
tests/interp/m98m99/11-main-program-oword/test-legal-end-main-with-m30.ngc
|
||||
tests/interp/m98m99/11-main-program-oword/test-legal-end-main-with-percent.ngc
|
||||
tests/interp/m98m99/11-main-program-oword/test.sh
|
||||
tests/interp/m98m99/13-named-program/expected
|
||||
tests/interp/m98m99/13-named-program/test-named.ngc
|
||||
tests/interp/m98m99/13-named-program/test-numbered.ngc
|
||||
tests/interp/m98m99/13-named-program/test.sh
|
||||
tests/interp/m98m99/14-o-expression-call/expected
|
||||
tests/interp/m98m99/14-o-expression-call/test.ngc
|
||||
tests/interp/m98m99/14-o-expression-call/test.sh
|
||||
tests/interp/do-while-break/README
|
||||
tests/interp/do-while-break/bug.ngc
|
||||
tests/interp/do-while-break/expected
|
||||
tests/interp/do-while-break/test.ngc
|
||||
tests/interp/do-while-break/test.sh
|
||||
tests/interp/oword-bug315/README
|
||||
tests/interp/oword-bug315/expected
|
||||
tests/interp/oword-bug315/test.ngc
|
||||
tests/interp/oword-bug315/test.sh
|
||||
tests/interp/oword-bug315-p2/README
|
||||
tests/interp/oword-bug315-p2/expected
|
||||
tests/interp/oword-bug315-p2/test.ngc
|
||||
tests/interp/oword-bug315-p2/test.sh
|
||||
tests/interp/exists/README
|
||||
tests/interp/exists/expected
|
||||
tests/interp/exists/test.ngc
|
||||
tests/interp/exists/test.sh
|
||||
tests/interp/return-value/expected
|
||||
tests/interp/return-value/test.ngc
|
||||
tests/interp/return-value/test.sh
|
||||
tests/interp/subs-follow-main/expected
|
||||
tests/interp/subs-follow-main/test.ngc
|
||||
tests/interp/subs-follow-main/test.sh
|
||||
tests/interp/fractional-linenumbers/README
|
||||
tests/interp/fractional-linenumbers/expected
|
||||
tests/interp/fractional-linenumbers/test.ngc
|
||||
tests/interp/fractional-linenumbers/test.sh
|
||||
tests/interp/cam-nisley/cam.ngc
|
||||
tests/interp/cam-nisley/expected
|
||||
tests/interp/cam-nisley/test.sh
|
||||
tests/interp/cam-nisley/test.tbl
|
||||
tests/interp/crazy-paths/README
|
||||
tests/interp/crazy-paths/expected
|
||||
tests/interp/crazy-paths/test.ngc
|
||||
tests/interp/crazy-paths/test.sh
|
||||
tests/interp/namedparam-bug424/README
|
||||
tests/interp/namedparam-bug424/expected
|
||||
tests/interp/namedparam-bug424/test.ngc
|
||||
tests/interp/namedparam-bug424/test.sh
|
||||
tests/interp/flowsnake/README
|
||||
tests/interp/flowsnake/expected
|
||||
tests/interp/flowsnake/flowsnake.ngc
|
||||
tests/interp/flowsnake/test.sh
|
||||
tests/interp/inside-corners/README
|
||||
tests/interp/inside-corners/expected
|
||||
tests/interp/inside-corners/test.ngc
|
||||
tests/interp/inside-corners/test.sh
|
||||
tests/interp/inverse-time-with-comp/README
|
||||
tests/interp/inverse-time-with-comp/expected
|
||||
tests/interp/inverse-time-with-comp/inverse.ngc
|
||||
tests/interp/inverse-time-with-comp/test.sh
|
||||
tests/ccomp/lathe-comp/expected
|
||||
tests/ccomp/lathe-comp/test.ngc
|
||||
tests/ccomp/lathe-comp/test.sh
|
||||
tests/ccomp/lathe-comp/test.tbl
|
||||
tests/ccomp/mill-g90g91g92/expected
|
||||
tests/ccomp/mill-g90g91g92/test.ngc
|
||||
tests/ccomp/mill-g90g91g92/test.sh
|
||||
tests/ccomp/mill-g90g91g92/test.tbl
|
||||
tests/ccomp/mill-line-arc-entry/expected
|
||||
tests/ccomp/mill-line-arc-entry/test.ngc
|
||||
tests/ccomp/mill-line-arc-entry/test.sh
|
||||
tests/ccomp/mill-line-arc-entry/test.tbl
|
||||
tests/ccomp/mill-zchanges/expected
|
||||
tests/ccomp/mill-zchanges/test.ngc
|
||||
tests/ccomp/mill-zchanges/test.sh
|
||||
tests/ccomp/mill-zchanges/test.tbl
|
||||
tests/interp/bad/a-in-canned-cycle.ngc
|
||||
tests/interp/bad/a-in-canned-cycle2.ngc
|
||||
tests/interp/bad/bad-arc.big.imperial.center-format.ngc
|
||||
tests/interp/bad/bad-arc.big.metric.center-format.ngc
|
||||
tests/interp/bad/bad-arc.medium.imperial.center-format.ngc
|
||||
tests/interp/bad/bad-arc.medium.metric.center-format.ngc
|
||||
tests/interp/bad/bad-arc.small.imperial.center-format.ngc
|
||||
tests/interp/bad/bad-arc.small.metric.center-format.ngc
|
||||
tests/interp/bad/ccomp-arcexit.ngc
|
||||
tests/interp/bad/ccomp-gouging.ngc
|
||||
tests/interp/bad/exists-1.ngc
|
||||
tests/interp/bad/exists-2.ngc
|
||||
tests/interp/bad/exists-3.ngc
|
||||
tests/interp/bad/exists-4.ngc
|
||||
tests/interp/bad/exists-5.ngc
|
||||
tests/interp/bad/exists-6.ngc
|
||||
tests/interp/bad/exists-7.ngc
|
||||
tests/interp/bad/nested.ngc
|
||||
tests/interp/bad/no-feed-rate.ngc
|
||||
tests/interp/bad/no-ijr.ngc
|
||||
tests/interp/bad/probe-no-axes.ngc
|
||||
tests/interp/g33.1/expected
|
||||
tests/interp/g33.1/g33.1.ngc
|
||||
tests/interp/g33.1/test.sh
|
||||
tests/interp/g6164/expected
|
||||
tests/interp/g6164/test.ngc
|
||||
tests/interp/g6164/test.sh
|
||||
tests/interp/good/good-arc.big.imperial.center-format.ngc
|
||||
tests/interp/good/good-arc.big.metric.center-format.ngc
|
||||
tests/interp/good/good-arc.medium.imperial.center-format.ngc
|
||||
tests/interp/good/good-arc.medium.metric.center-format.ngc
|
||||
tests/interp/good/good-arc.small.imperial.center-format.ngc
|
||||
tests/interp/good/good-arc.small.metric.center-format.ngc
|
||||
tests/interp/g72-facing/expected
|
||||
tests/interp/g72-facing/g72-iterations-present.ngc
|
||||
tests/interp/g72-facing/test.sh
|
||||
tests/interp/g72-missing-iteration/expected
|
||||
tests/interp/g72-missing-iteration/g72-iterations-missing.ngc
|
||||
tests/interp/g72-missing-iteration/test.sh
|
||||
tests/interp/g71-endless-loop/expected
|
||||
tests/interp/g71-endless-loop/g71-endless-loop.ngc
|
||||
tests/interp/g71-endless-loop/test.sh
|
||||
tests/interp/g71-endless-loop2/expected
|
||||
tests/interp/g71-endless-loop2/g71-endless-loop2.ngc
|
||||
tests/interp/g71-endless-loop2/test.sh
|
||||
tests/interp/g71-endless-loop_2/expected
|
||||
tests/interp/g71-endless-loop_2/g71-endless-loop_2.ngc
|
||||
tests/interp/g71-endless-loop_2/test.sh
|
||||
tests/interp/g71-with-g70/expected
|
||||
tests/interp/g71-with-g70/g71-with-g70.ngc
|
||||
tests/interp/g71-with-g70/test.sh
|
||||
tests/interp/g76/README
|
||||
tests/interp/g76/expected
|
||||
tests/interp/g76/g76only.ngc
|
||||
tests/interp/g76/test.sh
|
||||
tests/interp/g76/test.tbl
|
||||
tests/interp/g10/g10-l1-l10/expected
|
||||
tests/interp/g10/g10-l1-l10/test.ngc
|
||||
tests/interp/g10/g10-l1-l10/test.sh
|
||||
tests/interp/g10/g10-l1-l10/test.tbl
|
||||
tests/interp/g10/g10-l11/expected
|
||||
tests/interp/g10/g10-l11/test.ngc
|
||||
tests/interp/g10/g10-l11/test.sh
|
||||
tests/interp/g10/g10-l11/test.tbl
|
||||
tests/interp/g10/g10-l2-while-active/expected
|
||||
tests/interp/g10/g10-l2-while-active/test.ngc
|
||||
tests/interp/g10/g10-l2-while-active/test.sh
|
||||
tests/interp/g10/g10-l20-while-active/expected
|
||||
tests/interp/g10/g10-l20-while-active/test.ngc
|
||||
tests/interp/g10/g10-l20-while-active/test.sh
|
||||
tests/interp/g10/g10-with-g92/expected
|
||||
tests/interp/g10/g10-with-g92/test.ngc
|
||||
tests/interp/g10/g10-with-g92/test.sh
|
||||
tests/interp/g10/g10-with-g92/test.tbl
|
||||
tests/interp/g52/g52-g92-interaction/expected
|
||||
tests/interp/g52/g52-g92-interaction/g52-g92-interaction.ngc
|
||||
tests/interp/g52/g52-g92-interaction/test.sh
|
||||
tests/interp/rotation/abs-pts/expected
|
||||
tests/interp/rotation/abs-pts/test.ngc
|
||||
tests/interp/rotation/abs-pts/test.sh
|
||||
tests/interp/rotation/abs-pts/test.tbl
|
||||
tests/interp/rotation/g28/expected
|
||||
tests/interp/rotation/g28/g28.ngc
|
||||
tests/interp/rotation/g28/test.sh
|
||||
tests/interp/rotation/g53/expected
|
||||
tests/interp/rotation/g53/g53.ngc
|
||||
tests/interp/rotation/g53/test.sh
|
||||
tests/interp/iniparam/README
|
||||
tests/interp/iniparam/expected
|
||||
tests/interp/iniparam/test.ini
|
||||
tests/interp/iniparam/test.ngc
|
||||
tests/interp/iniparam/test.sh
|
||||
tests/interp/iniparam-failassign/README
|
||||
tests/interp/iniparam-failassign/expected
|
||||
tests/interp/iniparam-failassign/test.ini
|
||||
tests/interp/iniparam-failassign/test.ngc
|
||||
tests/interp/iniparam-failassign/test.sh
|
||||
tests/interp/m19/README
|
||||
tests/interp/m19/expected
|
||||
tests/interp/m19/test.ini
|
||||
tests/interp/m19/test.ngc
|
||||
tests/interp/m19/test.sh
|
||||
tests/interp/magic_comments/param_format_printing/expected
|
||||
tests/interp/magic_comments/param_format_printing/test.ngc
|
||||
tests/interp/magic_comments/param_format_printing/test.sh
|
||||
tests/interp/sub-call-from-sub/expected
|
||||
tests/interp/sub-call-from-sub/subs/caller.ngc
|
||||
tests/interp/sub-call-from-sub/subs/helper.ngc
|
||||
tests/interp/sub-call-from-sub/test.ini
|
||||
tests/interp/sub-call-from-sub/test.ngc
|
||||
tests/interp/sub-call-from-sub/test.sh
|
||||
tests/interp/sequence-number/README
|
||||
tests/interp/sequence-number/expected
|
||||
tests/interp/sequence-number/rm400.ngc
|
||||
tests/interp/sequence-number/test.ini
|
||||
tests/interp/sequence-number/test.ngc
|
||||
tests/interp/sequence-number/test.sh
|
||||
tests/interp/nested-sub-error/expected
|
||||
tests/interp/nested-sub-error/subs/nested.ngc
|
||||
tests/interp/nested-sub-error/test.ini
|
||||
tests/interp/nested-sub-error/test.ngc
|
||||
tests/interp/nested-sub-error/test.sh
|
||||
tests/interp/nested-sub-in-file-error/expected
|
||||
tests/interp/nested-sub-in-file-error/subs/sequential.ngc
|
||||
tests/interp/nested-sub-in-file-error/test.ini
|
||||
tests/interp/nested-sub-in-file-error/test.ngc
|
||||
tests/interp/nested-sub-in-file-error/test.sh
|
||||
tests/interp/oword-unwind/README
|
||||
tests/interp/oword-unwind/expected
|
||||
tests/interp/oword-unwind/fail.ngc
|
||||
tests/interp/oword-unwind/test.ini
|
||||
tests/interp/oword-unwind/test.ngc
|
||||
tests/interp/oword-unwind/test.sh
|
||||
tests/interp/abort-hot-comment/README
|
||||
tests/interp/abort-hot-comment/expected
|
||||
tests/interp/abort-hot-comment/test.ini
|
||||
tests/interp/abort-hot-comment/test.ngc
|
||||
tests/interp/abort-hot-comment/test.sh
|
||||
configs/sim/axis/sim.tbl
|
||||
configs/sim/axis/external_offsets/M111
|
||||
configs/sim/axis/external_offsets/circles.ngc
|
||||
configs/sim/axis/external_offsets/dyn_demo.ngc
|
||||
configs/sim/axis/external_offsets/dynamic_offsets.ini
|
||||
configs/sim/axis/external_offsets/eoffset.tbl
|
||||
configs/sim/axis/external_offsets/eoffsets.ini
|
||||
configs/sim/axis/external_offsets/eoffsets.ngc
|
||||
configs/sim/axis/external_offsets/jwp_z.ini
|
||||
configs/sim/axis/external_offsets/jwp_z.ngc
|
||||
configs/sim/axis/external_offsets/opa.ini
|
||||
configs/sim/axis/external_offsets/opa_demo.ngc
|
||||
configs/sim/axis/db_demo/base.ngc
|
||||
configs/sim/axis/db_demo/db_nonran.ini
|
||||
configs/sim/axis/foam/axis_foam.ini
|
||||
configs/sim/axis/foam/foam.ngc
|
||||
configs/sim/axis/gladevcp/gladevcp_panel.ini
|
||||
configs/sim/axis/gladevcp/probe.ngc
|
||||
configs/sim/axis/gladevcp/sim.tbl
|
||||
configs/sim/axis/geometry/M110
|
||||
configs/sim/axis/geometry/xyzc.ini
|
||||
configs/sim/axis/geometry/xyzc.ngc
|
||||
configs/sim/axis/rose_engine/rcone.ngc
|
||||
configs/sim/axis/rose_engine/rcone_demo.ngc
|
||||
configs/sim/axis/rose_engine/rose_engine.ini
|
||||
configs/sim/axis/vismach/5axis/bridgemill/5axis.ini
|
||||
configs/sim/axis/vismach/5axis/bridgemill/5axis.tbl
|
||||
configs/sim/axis/vismach/5axis/bridgemill/5axis.xml
|
||||
@@ -160,6 +519,25 @@ configs/sim/axis/vismach/5axis/bridgemill/README
|
||||
configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc
|
||||
configs/sim/axis/vismach/5axis/bridgemill/remap_subs/429remap.ngc
|
||||
configs/sim/axis/vismach/5axis/bridgemill/remap_subs/430remap.ngc
|
||||
configs/sim/axis/vismach/melfa-sim/example.ngc
|
||||
configs/sim/axis/vismach/melfa-sim/melfa.ini
|
||||
configs/sim/axis/vismach/melfa-sim/melfa.tbl
|
||||
configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc
|
||||
configs/sim/axis/vismach/melfa-sim/remap_subs/429remap.ngc
|
||||
configs/sim/axis/vismach/melfa-sim/remap_subs/430remap.ngc
|
||||
configs/sim/axis/vismach/millturn/example.ngc
|
||||
configs/sim/axis/vismach/millturn/millturn.ini
|
||||
configs/sim/axis/vismach/millturn/millturn.tbl
|
||||
configs/sim/axis/vismach/millturn/remap_subs/428remap.ngc
|
||||
configs/sim/axis/vismach/millturn/remap_subs/429remap.ngc
|
||||
configs/sim/axis/vismach/puma/puma.ini
|
||||
configs/sim/axis/vismach/puma/puma.tbl
|
||||
configs/sim/axis/vismach/puma/puma_cube.ini
|
||||
configs/sim/axis/vismach/puma/puma_cube.ngc
|
||||
configs/sim/axis/vismach/puma/puma_seam_weld.ngc
|
||||
configs/sim/axis/vismach/puma/remap_subs/428remap.ngc
|
||||
configs/sim/axis/vismach/puma/remap_subs/429remap.ngc
|
||||
configs/sim/axis/vismach/puma/remap_subs/430remap.ngc
|
||||
configs/sim/axis/vismach/5axis/table-dual-rotary/README
|
||||
configs/sim/axis/vismach/5axis/table-dual-rotary/demos/xyzab-tdr-demo.ngc
|
||||
configs/sim/axis/vismach/5axis/table-dual-rotary/remap_subs/428remap.ngc
|
||||
@@ -199,3 +577,29 @@ configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini
|
||||
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.tbl
|
||||
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.txt
|
||||
configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.xml
|
||||
configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc
|
||||
configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini
|
||||
configs/sim/qtdragon/qtdragon_multi_joint/tool.tbl
|
||||
configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc
|
||||
configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini
|
||||
configs/sim/qtdragon/qtdragon_xyz/tool.tbl
|
||||
configs/sim/qtdragon/qtdragon_xyz45/on_abort.ngc
|
||||
configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini
|
||||
configs/sim/qtdragon/qtdragon_xyz45/tool.tbl
|
||||
configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc
|
||||
configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini
|
||||
configs/sim/qtdragon_hd/qtdragon_hd_xyz/tool.tbl
|
||||
configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc
|
||||
configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini
|
||||
configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/tool.tbl
|
||||
configs/sim/qtvcp_screens/qtdragon/on_abort.ngc
|
||||
configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini
|
||||
configs/sim/qtvcp_screens/qtdragon/tool.tbl
|
||||
configs/sim/woodpecker/on_abort.ngc
|
||||
configs/sim/woodpecker/tool.tbl
|
||||
configs/sim/woodpecker/woodpecker.ini
|
||||
nc_files/3D_Chips.ngc
|
||||
nc_files/arcspiral.ngc
|
||||
nc_files/factorial.ngc
|
||||
nc_files/hole-circle.ngc
|
||||
nc_files/m6demo.ngc
|
||||
|
||||
@@ -7,6 +7,9 @@ GCODE_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode"
|
||||
CANON_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon"
|
||||
GCODE_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode_errors"
|
||||
CANON_ERROR_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon_errors"
|
||||
BUILD_DIR="$ROOT_DIR/build/native"
|
||||
CACHE_KEY_FILE="$BUILD_DIR/native-linuxcnc-fixture-baseline.input.sha256"
|
||||
CACHE_OK_FILE="$BUILD_DIR/native-linuxcnc-fixture-baseline.ok"
|
||||
|
||||
FIXTURES=(
|
||||
minimal_linear
|
||||
@@ -56,6 +59,35 @@ if [[ ! -x "$UPSTREAM_RS274" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
hash_tree() {
|
||||
find "$@" -type f -print0 | sort -z | xargs -0 -r sha256sum
|
||||
}
|
||||
|
||||
fixture_baseline_fingerprint() {
|
||||
{
|
||||
sha256sum \
|
||||
"$ROOT_DIR/tools/verify_native_linuxcnc_fixture_baseline.sh" \
|
||||
"$UPSTREAM_RS274"
|
||||
"$UPSTREAM_RS274" --version 2>/dev/null || true
|
||||
hash_tree \
|
||||
"$GCODE_FIXTURE_DIR" \
|
||||
"$CANON_FIXTURE_DIR" \
|
||||
"$GCODE_ERROR_FIXTURE_DIR" \
|
||||
"$CANON_ERROR_FIXTURE_DIR" \
|
||||
"$ROOT_DIR/tests/fixtures/ini"
|
||||
} | sha256sum | awk '{ print $1 }'
|
||||
}
|
||||
|
||||
FIXTURE_BASELINE_FINGERPRINT="$(fixture_baseline_fingerprint)"
|
||||
if [[ -f "$CACHE_KEY_FILE" && -f "$CACHE_OK_FILE" ]] &&
|
||||
[[ "$(tr -d '[:space:]' < "$CACHE_KEY_FILE")" == "$FIXTURE_BASELINE_FINGERPRINT" ]] &&
|
||||
[[ "$(tr -d '[:space:]' < "$CACHE_OK_FILE")" == "$FIXTURE_BASELINE_FINGERPRINT" ]]; then
|
||||
echo "native LinuxCNC fixture baseline up to date"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||
TOOL_TABLE_BASELINE="$TMP_DIR/tool_table_setup.tbl"
|
||||
@@ -967,4 +999,6 @@ for name in "${ERROR_FIXTURES[@]}"; do
|
||||
done < "$expected_file"
|
||||
done
|
||||
|
||||
printf '%s\n' "$FIXTURE_BASELINE_FINGERPRINT" > "$CACHE_KEY_FILE"
|
||||
printf '%s\n' "$FIXTURE_BASELINE_FINGERPRINT" > "$CACHE_OK_FILE"
|
||||
echo "native LinuxCNC fixture baseline validation complete"
|
||||
|
||||
@@ -5,6 +5,9 @@ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
UPSTREAM_DIR="$ROOT_DIR/../linuxcnc"
|
||||
VENDOR_DIR="$ROOT_DIR/vendor/linuxcnc"
|
||||
MANIFEST_FILE="$ROOT_DIR/tools/source-manifest.txt"
|
||||
BUILD_DIR="$ROOT_DIR/build/native"
|
||||
CACHE_KEY_FILE="$BUILD_DIR/vendor-sync.input.sha256"
|
||||
CACHE_OK_FILE="$BUILD_DIR/vendor-sync.ok"
|
||||
|
||||
if [[ ! -d "$UPSTREAM_DIR" ]]; then
|
||||
echo "missing upstream directory: $UPSTREAM_DIR" >&2
|
||||
@@ -21,6 +24,52 @@ if [[ ! -f "$MANIFEST_FILE" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
validate_manifest_path() {
|
||||
local src_rel="$1"
|
||||
if [[ "$src_rel" = /* || "$src_rel" == */ || "$src_rel" == *"//"* ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
local component
|
||||
local -a path_components
|
||||
IFS=/ read -r -a path_components <<< "$src_rel"
|
||||
for component in "${path_components[@]}"; do
|
||||
if [[ -z "$component" || "$component" == "." || "$component" == ".." ]]; then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
vendor_sync_fingerprint() {
|
||||
{
|
||||
git -C "$UPSTREAM_DIR" rev-parse HEAD
|
||||
sha256sum \
|
||||
"$ROOT_DIR/tools/verify_vendor_sync.sh" \
|
||||
"$MANIFEST_FILE"
|
||||
while IFS= read -r src_rel; do
|
||||
[[ -z "$src_rel" ]] && continue
|
||||
[[ "${src_rel:0:1}" == "#" ]] && continue
|
||||
printf '%s\0' "$UPSTREAM_DIR/$src_rel"
|
||||
done < "$MANIFEST_FILE" | xargs -0 -r sha256sum 2>/dev/null || true
|
||||
while IFS= read -r src_rel; do
|
||||
[[ -z "$src_rel" ]] && continue
|
||||
[[ "${src_rel:0:1}" == "#" ]] && continue
|
||||
printf '%s\0' "$VENDOR_DIR/$src_rel"
|
||||
done < "$MANIFEST_FILE" | xargs -0 -r sha256sum 2>/dev/null || true
|
||||
find "$VENDOR_DIR" -type f -printf 'vendor-list:%P\n' | sort
|
||||
} | sha256sum | awk '{ print $1 }'
|
||||
}
|
||||
|
||||
VENDOR_SYNC_FINGERPRINT="$(vendor_sync_fingerprint)"
|
||||
if [[ -f "$CACHE_KEY_FILE" && -f "$CACHE_OK_FILE" ]] &&
|
||||
[[ "$(tr -d '[:space:]' < "$CACHE_KEY_FILE")" == "$VENDOR_SYNC_FINGERPRINT" ]] &&
|
||||
[[ "$(tr -d '[:space:]' < "$CACHE_OK_FILE")" == "$VENDOR_SYNC_FINGERPRINT" ]]; then
|
||||
echo "vendor sync up to date"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||
|
||||
@@ -32,7 +81,7 @@ while IFS= read -r src_rel; do
|
||||
[[ -z "$src_rel" ]] && continue
|
||||
[[ "${src_rel:0:1}" == "#" ]] && continue
|
||||
|
||||
if [[ "$src_rel" = /* || "$src_rel" == *".."* ]]; then
|
||||
if ! validate_manifest_path "$src_rel"; then
|
||||
echo "invalid manifest path: $src_rel" >&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -82,4 +131,6 @@ while IFS= read -r src_rel; do
|
||||
fi
|
||||
done < "$MANIFEST_SORTED"
|
||||
|
||||
printf '%s\n' "$VENDOR_SYNC_FINGERPRINT" > "$CACHE_KEY_FILE"
|
||||
printf '%s\n' "$VENDOR_SYNC_FINGERPRINT" > "$CACHE_OK_FILE"
|
||||
echo "vendor sync validation complete"
|
||||
|
||||
17
wasm-port/vendor/linuxcnc/configs/sim/axis/db_demo/base.ngc
vendored
Normal file
17
wasm-port/vendor/linuxcnc/configs/sim/axis/db_demo/base.ngc
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
%
|
||||
#<toolno> = 10
|
||||
#<ct> = 0
|
||||
#<howmany> = 5
|
||||
|
||||
g49
|
||||
o100 while [[#<ct> lt #<howmany>] and [#<_task> ne 0]]
|
||||
#<ct> = [#<ct> +1]
|
||||
t#<toolno> m6 g43
|
||||
(debug,ct=#<ct> zoff=#5403)
|
||||
g4p[0.05*60] ;approx 0.05 minutes
|
||||
t0m6
|
||||
g10l0 ; reload tooldata (apply_db_rules)
|
||||
o100 endwhile
|
||||
g49
|
||||
(debug, fini)
|
||||
%
|
||||
17
wasm-port/vendor/linuxcnc/configs/sim/axis/db_demo/db_nonran.ini
vendored
Normal file
17
wasm-port/vendor/linuxcnc/configs/sim/axis/db_demo/db_nonran.ini
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
#INCLUDE base.inc
|
||||
|
||||
[EMC]
|
||||
VERSION = 1.1
|
||||
MACHINE = db_nonran NONRANDOM toolchanger
|
||||
|
||||
[RS274NGC]
|
||||
PARAMETER_FILE = db_nonran.var
|
||||
SUBROUTINE_PATH = .
|
||||
|
||||
[EMCIO]
|
||||
RANDOM_TOOLCHANGER = 0
|
||||
DB_PROGRAM = ./db_nonran.py
|
||||
# alternate spec using args:
|
||||
# DB_PROGRAM = ./db_nonran.py --period_minutes=10 /tmp/db_nonran_special
|
||||
|
||||
# TOOL_TABLE= is not used with DB_PROGRAM
|
||||
5
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/M111
vendored
Executable file
5
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/M111
vendored
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
halcmd setp axisui.notifications-clear-info 1
|
||||
sleep .1
|
||||
halcmd setp axisui.notifications-clear-info 0
|
||||
exit 0
|
||||
43
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/circles.ngc
vendored
Normal file
43
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/circles.ngc
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
(info: Multiple circles at radius R, use 0 for current val)
|
||||
;Notes:
|
||||
; 1) z axis not altered (remains at current z)
|
||||
; 2) for inside polygon cutting (fnum==0):
|
||||
; start with rfraction negative
|
||||
; and gradually increase rfraction to zero as program runs
|
||||
; multiple circles
|
||||
; 3) for outside polygon cutting (fnum==1):
|
||||
; start with rfraction positive
|
||||
; and gradually decrease to zero as program runs multiple
|
||||
; circles
|
||||
;
|
||||
; Alternate method for polygons:
|
||||
; 1) use #<radius>=0 and jog to safe starting position
|
||||
; 2) use #<revs>=1, run program for one revolution
|
||||
; 3) Run program repeatedly and manually jog x (radius)
|
||||
; before each program start to increase/decrease
|
||||
; current radius for inside/outside polygon respectively
|
||||
|
||||
o<circles> sub
|
||||
#<radius> = #1 (= 1 R 0==>current)
|
||||
#<aspeed> = #2 (= 8 aspeed: RPM)
|
||||
#<lspeed> = #3 (= 10 lspeed: units/sec)
|
||||
#<revs> = #4 (= 1 +/- Revolutions)
|
||||
|
||||
#<c> = #<_c> ;starting value
|
||||
#<ctarget> = [#<c> + #<revs> * 360]
|
||||
#<arate> = [#<aspeed> * 360]
|
||||
|
||||
m111 ;clear prior info messages
|
||||
o<if1> if [#<radius> EQ 0]
|
||||
#<ref_radius> = #<_x> ;use current x
|
||||
o<if1> else
|
||||
#<ref_radius> = #<radius>
|
||||
f#<lspeed> g1 x#<ref_radius>
|
||||
o<if1> endif
|
||||
(debug, ref_radius=#<ref_radius>)
|
||||
|
||||
m68 e0 q#<ref_radius> ; set ref_radius for eoffsets
|
||||
|
||||
f#<arate> g1 c#<ctarget> ; circles at arate
|
||||
|
||||
o<circles> endsub
|
||||
33
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc
vendored
Normal file
33
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
#<xmax> = 9
|
||||
#<ymax> = 4
|
||||
#<zmax> = 3
|
||||
#<rct> = 100
|
||||
#<feed> = 100
|
||||
|
||||
#<x1> = #<xmax>
|
||||
#<y1> = 0
|
||||
#<z1> = [#<zmax> * 0.5]
|
||||
|
||||
#<x2> = #<xmax>
|
||||
#<y2> = #<ymax>
|
||||
#<z2> = 0
|
||||
|
||||
#<x3> = 0
|
||||
#<y3> = #<ymax>
|
||||
#<z3> = #<zmax>
|
||||
|
||||
m111 ;clear notifications
|
||||
g61
|
||||
g0 x0 y0 z0
|
||||
|
||||
o<rpt> repeat [#<rct>]
|
||||
f #<feed>
|
||||
g0 x 0 y 0 z 0
|
||||
g1 x #<x1> y #<y1> z #<z1>
|
||||
g1 x #<x2> y #<y2> z #<z2>
|
||||
g1 x #<x3> y #<y3> z #<z3>
|
||||
g1 x 0 y 0 z 0
|
||||
o<rpt> endrepeat
|
||||
|
||||
m111 ;clear notifications
|
||||
m2
|
||||
83
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini
vendored
Normal file
83
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
[APPLICATIONS]
|
||||
# delay because some items are done in postgui
|
||||
DELAY = 5
|
||||
APP = halscope -i dynamic_offsets.halscope
|
||||
|
||||
[HAL]
|
||||
HALUI = halui
|
||||
HALFILE = LIB:basic_sim.tcl -no_sim_spindle
|
||||
POSTGUI_HALFILE = dynamic_offsets_panel.hal
|
||||
|
||||
[EMC]
|
||||
MACHINE = Dynamic External Offsets
|
||||
VERSION = 1.1
|
||||
|
||||
[DISPLAY]
|
||||
PYVCP = dynamic_offsets_panel.xml
|
||||
DISPLAY = axis
|
||||
POSITION_OFFSET = RELATIVE
|
||||
POSITION_FEEDBACK = ACTUAL
|
||||
MAX_LINEAR_VELOCITY = 2
|
||||
OPEN_FILE = ./dyn_demo.ngc
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.001
|
||||
|
||||
[RS274NGC]
|
||||
USER_M_PATH = .
|
||||
PARAMETER_FILE = sim.var
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[TRAJ]
|
||||
COORDINATES = XYZ
|
||||
LINEAR_UNITS = inch
|
||||
ANGULAR_UNITS = degree
|
||||
|
||||
[KINS]
|
||||
JOINTS = 3
|
||||
KINEMATICS = trivkins coordinates=XYZ
|
||||
|
||||
[AXIS_X]
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
MIN_LIMIT = -1
|
||||
MAX_LIMIT = 10
|
||||
|
||||
[AXIS_Y]
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
MIN_LIMIT = -1
|
||||
MAX_LIMIT = 10
|
||||
|
||||
[AXIS_Z]
|
||||
# Note:give half to external_offsets:
|
||||
OFFSET_AV_RATIO = 0.5
|
||||
|
||||
# Note: modified in pyvcp panel:
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 50
|
||||
|
||||
MIN_LIMIT = -5
|
||||
MAX_LIMIT = 5
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
|
||||
[JOINT_2]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 50
|
||||
1
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/eoffset.tbl
vendored
Normal file
1
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/eoffset.tbl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
T1 P1 D0.125 Z0.5
|
||||
104
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini
vendored
Normal file
104
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
# Notes:
|
||||
# 1) [AXIS_n]OFFSET_AV_RATIO= controls external offsets a,v
|
||||
# Allowed values are 0.0 <= OFFSET_AV_RATIO <= 0.9
|
||||
# Value of 0.0 disables external offsets
|
||||
# Disallowed values are superseded with msg to stdout
|
||||
# 2) Immediate homing herein
|
||||
# ([JOINT_*]HOME_SEQUENCE=0, other HOME_* items omitted)
|
||||
# 3) uncomment [JOINT_0]HOME_SEARCH_VEL,HOME_LATCH_VEL
|
||||
# to demonstrate non-zero joint.0.motor-offset
|
||||
|
||||
[APPLICATIONS]
|
||||
APP = sim_pin --title "MPG Simulator" \
|
||||
e:xcounts \
|
||||
e:ycounts \
|
||||
e:zcounts
|
||||
|
||||
# make sure [xyz]:ecounts are zero if machine off:
|
||||
APP = eoffsets_monitor.tcl
|
||||
|
||||
[HAL]
|
||||
HALUI = halui
|
||||
HALFILE = LIB:basic_sim.tcl -no_use_hal_manualtoolchange
|
||||
HALFILE = eoffsets.hal
|
||||
POSTGUI_HALFILE = eoffsets_panel.hal
|
||||
|
||||
[EMC]
|
||||
MACHINE = External Offsets Demo
|
||||
VERSION = 1.1
|
||||
|
||||
[DISPLAY]
|
||||
PYVCP = eoffsets_panel.xml
|
||||
DISPLAY = axis
|
||||
POSITION_OFFSET = RELATIVE
|
||||
POSITION_FEEDBACK = ACTUAL
|
||||
MAX_LINEAR_VELOCITY = 2
|
||||
OPEN_FILE = ./eoffsets.ngc
|
||||
|
||||
TKPKG = Ngcgui 1.0
|
||||
NGCGUI_FONT = Helvetica -12 normal
|
||||
NGCGUI_SUBFILE = queuebuster.ngc
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.001
|
||||
|
||||
[RS274NGC]
|
||||
SUBROUTINE_PATH = .
|
||||
USER_M_PATH = .
|
||||
PARAMETER_FILE = sim.var
|
||||
|
||||
[EMCIO]
|
||||
TOOL_TABLE = eoffset.tbl
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[TRAJ]
|
||||
COORDINATES = XYZ
|
||||
LINEAR_UNITS = inch
|
||||
ANGULAR_UNITS = degree
|
||||
|
||||
[KINS]
|
||||
JOINTS = 3
|
||||
KINEMATICS = trivkins coordinates=XYZ
|
||||
|
||||
[AXIS_X]
|
||||
OFFSET_AV_RATIO = 0.2
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
MIN_LIMIT = -0.1
|
||||
MAX_LIMIT = 10
|
||||
|
||||
[AXIS_Y]
|
||||
OFFSET_AV_RATIO = 0.2
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
MIN_LIMIT = -0.1
|
||||
MAX_LIMIT = 5
|
||||
|
||||
[AXIS_Z]
|
||||
OFFSET_AV_RATIO = 0.2
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
MIN_LIMIT = -1
|
||||
MAX_LIMIT = 1
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
|
||||
[JOINT_2]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
35
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc
vendored
Normal file
35
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
#<xmax> = 9
|
||||
#<ymax> = 4
|
||||
#<zdepth> = 0
|
||||
#<feed> = 20
|
||||
|
||||
#<x1> = [0.25 * #<xmax>]
|
||||
#<y1> = [0.66 * #<ymax>]
|
||||
#<z1> = 0
|
||||
|
||||
#<x2> = [0.50 * #<xmax>]
|
||||
#<y2> = 0
|
||||
#<z2> = -.5
|
||||
|
||||
#<x3> = [0.75 * #<xmax>]
|
||||
#<y3> = 0
|
||||
#<z3> = 0
|
||||
|
||||
#<x4> = #<x3>
|
||||
#<y4> = #<y1>
|
||||
#<z4> = 0
|
||||
|
||||
m111 ;clear notifications
|
||||
g61
|
||||
g0x0y0z0
|
||||
|
||||
f #<feed>
|
||||
g1 x #<x1> y #<y1> z #<z1>
|
||||
g1 x #<x2> y #<y2> z #<z2>
|
||||
g1 x #<x3> y #<y3> z #<z3>
|
||||
g1 x #<x4> y #<y4> z #<z4>
|
||||
g1 x #<xmax> y #<ymax>
|
||||
g0x0y0z0
|
||||
|
||||
m111 ;clear notifications
|
||||
m2
|
||||
86
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/jwp_z.ini
vendored
Normal file
86
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/jwp_z.ini
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
# Notes:
|
||||
# 1) [AXIS_n]OFFSET_AV_RATIO= controls external offsets a,v
|
||||
# Allowed values are 0.0 <= OFFSET_AV_RATIO <= 0.9
|
||||
# Value of 0.0 disables external offsets
|
||||
# Disallowed values are superseded with msg to stdout
|
||||
# 2) Immediate homing herein
|
||||
# ([JOINT_*]HOME_SEQUENCE=0, other HOME_* items omitted)
|
||||
# 3) uncomment [JOINT_0]HOME_SEARCH_VEL,HOME_LATCH_VEL
|
||||
# to demonstrate non-zero joint.0.motor-offset
|
||||
|
||||
|
||||
[HAL]
|
||||
HALUI = halui
|
||||
HALFILE = LIB:basic_sim.tcl
|
||||
POSTGUI_HALFILE = jwp_z_panel.hal
|
||||
|
||||
[EMC]
|
||||
MACHINE = Jog-While-Pause Z Demo
|
||||
VERSION = 1.1
|
||||
|
||||
[DISPLAY]
|
||||
PYVCP = jwp_z_panel.xml
|
||||
DISPLAY = axis
|
||||
POSITION_OFFSET = RELATIVE
|
||||
POSITION_FEEDBACK = ACTUAL
|
||||
MAX_LINEAR_VELOCITY = 2
|
||||
OPEN_FILE = ./jwp_z.ngc
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.001
|
||||
|
||||
[RS274NGC]
|
||||
USER_M_PATH = .
|
||||
PARAMETER_FILE = sim.var
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[TRAJ]
|
||||
COORDINATES = XYZ
|
||||
LINEAR_UNITS = inch
|
||||
ANGULAR_UNITS = degree
|
||||
|
||||
[KINS]
|
||||
JOINTS = 3
|
||||
KINEMATICS = trivkins coordinates=XYZ
|
||||
|
||||
[AXIS_X]
|
||||
MIN_LIMIT = -1
|
||||
MAX_LIMIT = 11
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
|
||||
[AXIS_Y]
|
||||
MIN_LIMIT = -1
|
||||
MAX_LIMIT = 5
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
|
||||
[AXIS_Z]
|
||||
# Note: low OFFSET_AV_RATIO:
|
||||
OFFSET_AV_RATIO = 0.05
|
||||
MIN_LIMIT = -1
|
||||
MAX_LIMIT = 1
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
|
||||
[JOINT_2]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
MAX_VELOCITY = 2
|
||||
MAX_ACCELERATION = 20
|
||||
35
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/jwp_z.ngc
vendored
Normal file
35
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/jwp_z.ngc
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
#<xmax> = 10
|
||||
#<ymax> = 5
|
||||
#<zsafe> = 0.5
|
||||
#<feed> = 100
|
||||
|
||||
#<x1> = [0.25 * #<xmax>]
|
||||
#<y1> = [0.66 * #<ymax>]
|
||||
|
||||
#<x2> = [0.50 * #<xmax>]
|
||||
#<y2> = [0.33 * #<ymax>]
|
||||
|
||||
#<x3> = [0.75 * #<xmax>]
|
||||
#<y3> = [0.33 * #<ymax>]
|
||||
|
||||
m111 ;clear notifications
|
||||
g61
|
||||
g0 x0 y0 z#<zsafe>
|
||||
(debug, Enter offsets only when paused, s to continue)
|
||||
m0
|
||||
m111
|
||||
|
||||
f #<feed>
|
||||
g0 z0
|
||||
g1 x #<x1> y #<y1>
|
||||
g1 x #<x2> y #<y2>
|
||||
g1 x #<x3> y #<y3>
|
||||
g1 x #<xmax> y #<ymax>
|
||||
|
||||
g0 z#<zsafe>
|
||||
(debug, Program End: Clear offsets, s to continue)
|
||||
m0
|
||||
g0 x0 y0
|
||||
|
||||
m111 ;clear notifications
|
||||
m2
|
||||
87
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/opa.ini
vendored
Normal file
87
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/opa.ini
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
[APPLICATIONS]
|
||||
DELAY = 3
|
||||
# For assessing system accel/vel:
|
||||
# (particularly when using func3 square wave modulation)
|
||||
APP = sim_pin ini.x.max_acceleration ini.x.max_velocity
|
||||
|
||||
[HAL]
|
||||
HALUI = halui
|
||||
HALFILE = LIB:basic_sim.tcl
|
||||
HALFILE = opa.hal
|
||||
POSTGUI_HALFILE = opa_panel.hal
|
||||
|
||||
[EMC]
|
||||
MACHINE = External Offset(X) per Angle(C) Demo
|
||||
VERSION = 1.1
|
||||
|
||||
[DISPLAY]
|
||||
PYVCP = opa_panel.xml
|
||||
DISPLAY = axis
|
||||
OPEN_FILE = opa_demo.ngc
|
||||
GEOMETRY = !CXZ
|
||||
POSITION_OFFSET = RELATIVE
|
||||
POSITION_FEEDBACK = ACTUAL
|
||||
MAX_LINEAR_VELOCITY = 2
|
||||
MAX_ANGULAR_VELOCITY = 60
|
||||
|
||||
TKPKG = Ngcgui 1.0
|
||||
NGCGUI_FONT = Helvetica -12 normal
|
||||
NGCGUI_SUBFILE = circles.ngc
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.001
|
||||
|
||||
[RS274NGC]
|
||||
SUBROUTINE_PATH = .
|
||||
USER_M_PATH = .
|
||||
PARAMETER_FILE = sim.var
|
||||
|
||||
[EMCIO]
|
||||
TOOL_TABLE = eoffset.tbl
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[TRAJ]
|
||||
COORDINATES = XZC
|
||||
LINEAR_UNITS = inch
|
||||
ANGULAR_UNITS = degree
|
||||
|
||||
[KINS]
|
||||
JOINTS = 3
|
||||
KINEMATICS = trivkins coordinates=XZC
|
||||
|
||||
[AXIS_X]
|
||||
OFFSET_AV_RATIO = 0.5
|
||||
MAX_VELOCITY = 5
|
||||
# increase MAX_ACCELERATION to improve
|
||||
# waveform tracking:
|
||||
MAX_ACCELERATION = 50
|
||||
|
||||
[AXIS_Z]
|
||||
MAX_VELOCITY = 5
|
||||
MAX_ACCELERATION = 10
|
||||
|
||||
[AXIS_C]
|
||||
MAX_VELOCITY = 60
|
||||
MAX_ACCELERATION = 600
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
MAX_VELOCITY = 5
|
||||
MAX_ACCELERATION = 50
|
||||
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
MAX_VELOCITY = 5
|
||||
MAX_ACCELERATION = 10
|
||||
|
||||
[JOINT_2]
|
||||
TYPE = ANGULAR
|
||||
HOME_SEQUENCE = 0
|
||||
MAX_VELOCITY = 60
|
||||
MAX_ACCELERATION = 600
|
||||
15
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/opa_demo.ngc
vendored
Normal file
15
wasm-port/vendor/linuxcnc/configs/sim/axis/external_offsets/opa_demo.ngc
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
; opa_demo.ngc:
|
||||
; invoke subroutine circles.ngc for multiple circles (#<revs>)
|
||||
; with:
|
||||
; x = #<radius>
|
||||
; z = #<_z>
|
||||
|
||||
#<radius> = 1
|
||||
#<z> = #<_z> ; current value
|
||||
#<aspeed> = 8 ; angular speed (RPM)
|
||||
#<lspeed> = 10 ; linear speed (units/minute)
|
||||
#<revs> = 100
|
||||
|
||||
g0 x#<radius> z#<z>
|
||||
o<circles> call [#<radius>][#<aspeed>][#<lspeed>][#<revs>]
|
||||
m2
|
||||
126
wasm-port/vendor/linuxcnc/configs/sim/axis/foam/axis_foam.ini
vendored
Normal file
126
wasm-port/vendor/linuxcnc/configs/sim/axis/foam/axis_foam.ini
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
[EMC]
|
||||
VERSION = 1.1
|
||||
MACHINE = LinuxCNC-HAL-SIM-AXIS
|
||||
DEBUG = 0
|
||||
|
||||
[DISPLAY]
|
||||
DISPLAY = axis
|
||||
CYCLE_TIME = 0.100
|
||||
HELP_FILE = doc/help.txt
|
||||
POSITION_OFFSET = RELATIVE
|
||||
POSITION_FEEDBACK = ACTUAL
|
||||
MAX_FEED_OVERRIDE = 1.2
|
||||
MAX_SPINDLE_OVERRIDE = 1.0
|
||||
PROGRAM_PREFIX = ../../nc_files/
|
||||
OPEN_FILE = ./foam.ngc
|
||||
INTRO_GRAPHIC = linuxcnc.gif
|
||||
INTRO_TIME = 5
|
||||
#EDITOR = geany
|
||||
INCREMENTS = 1 mm, .01 in, .1mm, 1 mil, .1 mil, 1/8000 in
|
||||
GEOMETRY = XY;UV
|
||||
FOAM = 1
|
||||
|
||||
[FILTER]
|
||||
PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image
|
||||
PROGRAM_EXTENSION = .py Python Script
|
||||
png = image-to-gcode
|
||||
gif = image-to-gcode
|
||||
jpg = image-to-gcode
|
||||
py = python3
|
||||
|
||||
[RS274NGC]
|
||||
PARAMETER_FILE = foam.var
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
COMM_TIMEOUT = 1.0
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.001
|
||||
|
||||
[HAL]
|
||||
HALUI = halui
|
||||
HALFILE = LIB:basic_sim.tcl
|
||||
|
||||
[TRAJ]
|
||||
NO_FORCE_HOMING = 1
|
||||
COORDINATES = X Y U V
|
||||
LINEAR_UNITS = inch
|
||||
ANGULAR_UNITS = degree
|
||||
DEFAULT_LINEAR_VELOCITY = 1.0
|
||||
DEFAULT_ANGULAR_VELOCITY = 45.0
|
||||
MAX_LINEAR_VELOCITY = 1.2
|
||||
MAX_ANGULAR_VELOCITY = 90.0
|
||||
|
||||
[EMCIO]
|
||||
TOOL_TABLE = sim.tbl
|
||||
TOOL_CHANGE_POSITION = 0 0 2
|
||||
|
||||
[KINS]
|
||||
KINEMATICS = trivkins coordinates=xyuv
|
||||
JOINTS = 4
|
||||
|
||||
[AXIS_X]
|
||||
MIN_LIMIT = -10.0
|
||||
MAX_LIMIT = 10.0
|
||||
MAX_VELOCITY = 1.2
|
||||
MAX_ACCELERATION = 20.0
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 1.2
|
||||
MAX_ACCELERATION = 20.0
|
||||
MIN_LIMIT = -10.0
|
||||
MAX_LIMIT = 10.0
|
||||
HOME_SEARCH_VEL = 0.0
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[AXIS_Y]
|
||||
MIN_LIMIT = -10.0
|
||||
MAX_LIMIT = 10.0
|
||||
MAX_VELOCITY = 1.2
|
||||
MAX_ACCELERATION = 20.0
|
||||
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 1.2
|
||||
MAX_ACCELERATION = 20.0
|
||||
MIN_LIMIT = -10.0
|
||||
MAX_LIMIT = 10.0
|
||||
HOME_SEARCH_VEL = 0.0
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[AXIS_U]
|
||||
MIN_LIMIT = -10.0
|
||||
MAX_LIMIT = 10.0
|
||||
MAX_VELOCITY = 1.2
|
||||
MAX_ACCELERATION = 20.0
|
||||
|
||||
[JOINT_2]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.0
|
||||
MAX_VELOCITY = 1.2
|
||||
MAX_ACCELERATION = 20.0
|
||||
MIN_LIMIT = -10.0
|
||||
MAX_LIMIT = 10.0
|
||||
HOME_SEARCH_VEL = 0.0
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[AXIS_V]
|
||||
MIN_LIMIT = -10.0
|
||||
MAX_LIMIT = 10.0
|
||||
MAX_VELOCITY = 1.2
|
||||
MAX_ACCELERATION = 20.0
|
||||
|
||||
[JOINT_3]
|
||||
TYPE = ANGULAR
|
||||
HOME = 0.0
|
||||
MAX_VELOCITY = 90.0
|
||||
MAX_ACCELERATION = 1200.0
|
||||
HOME_SEARCH_VEL = 0.0
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
24
wasm-port/vendor/linuxcnc/configs/sim/axis/foam/foam.ngc
vendored
Normal file
24
wasm-port/vendor/linuxcnc/configs/sim/axis/foam/foam.ngc
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
(test this with axis_9axis sim machine )
|
||||
G17 G21 G90 G61 G54
|
||||
(AXIS,XY_Z_POS,5)
|
||||
(AXIS,UV_Z_POS,30)
|
||||
(AXIS,GRID,5)
|
||||
|
||||
G0 x0 y0 u0 v0
|
||||
|
||||
(square uv 45 deg turned offset 10mm to zero )
|
||||
G0 x5 y5
|
||||
G0 x10 y10 u22.5 v22.5
|
||||
G1 x10 y35 u10 v35 F200
|
||||
G1 x10 y60 u22.5 v47.5
|
||||
G1 x35 y60 u35 v60
|
||||
G2 r25 x60 y35 u60 v35
|
||||
G1 x60 y10 u47.5 v22.5
|
||||
G1 x35 y10 u35 v10
|
||||
G1 X10 Y10 u22.5 v22.5
|
||||
G1 X5 Y5 U5 V5
|
||||
G0 x0 u0
|
||||
|
||||
G0 x0 y0 u0 v0
|
||||
M30
|
||||
%
|
||||
7
wasm-port/vendor/linuxcnc/configs/sim/axis/geometry/M110
vendored
Executable file
7
wasm-port/vendor/linuxcnc/configs/sim/axis/geometry/M110
vendored
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
halcmd setp axisui.notifications-clear-info 1
|
||||
sleep .200
|
||||
halcmd setp axisui.notifications-clear-info 0
|
||||
|
||||
exit 0
|
||||
72
wasm-port/vendor/linuxcnc/configs/sim/axis/geometry/xyzc.ini
vendored
Normal file
72
wasm-port/vendor/linuxcnc/configs/sim/axis/geometry/xyzc.ini
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
# minimal sim config to demonstrate '!' in [DISPLAY]GEOMETRY
|
||||
# showing rotations respecting g5x,g92 offsets
|
||||
[EMC]
|
||||
VERSION = 1.1
|
||||
MACHINE = xyzc (offset rotations)
|
||||
|
||||
[DISPLAY]
|
||||
OPEN_FILE = ./xyzc.ngc
|
||||
GEOMETRY = !CXYZ
|
||||
DISPLAY = axis
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.001
|
||||
|
||||
[RS274NGC]
|
||||
PARAMETER_FILE = xyzc.var
|
||||
USER_M_PATH = .
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[HAL]
|
||||
HALFILE = LIB:basic_sim.tcl
|
||||
|
||||
[KINS]
|
||||
JOINTS = 4
|
||||
KINEMATICS = trivkins coordinates=XYZC kinstype=both
|
||||
|
||||
[TRAJ]
|
||||
COORDINATES = X Y Z C
|
||||
LINEAR_UNITS = inch
|
||||
ANGULAR_UNITS = degree
|
||||
MAX_LINEAR_VELOCITY = 10
|
||||
MAX_LINEAR_ACCELERATION = 10000
|
||||
MAX_ANGULAR_VELOCITY = 3600
|
||||
MAX_ANGULAR_ACCELERATION = 36000
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[JOINT_2]
|
||||
TYPE = LINEAR
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[JOINT_3]
|
||||
TYPE = ANGULAR
|
||||
HOME_SEQUENCE = 0
|
||||
MAX_VELOCITY=3600
|
||||
MAX_ACCELERATION=36000
|
||||
|
||||
[AXIS_X]
|
||||
MAX_VELOCITY=10
|
||||
MAX_ACCELERATION=1000
|
||||
|
||||
[AXIS_Y]
|
||||
MAX_VELOCITY=10
|
||||
MAX_ACCELERATION=1000
|
||||
|
||||
[AXIS_Z]
|
||||
MAX_VELOCITY=10
|
||||
MAX_ACCELERATION=1000
|
||||
|
||||
[AXIS_C]
|
||||
MAX_VELOCITY=3600
|
||||
MAX_ACCELERATION=36000
|
||||
66
wasm-port/vendor/linuxcnc/configs/sim/axis/geometry/xyzc.ngc
vendored
Normal file
66
wasm-port/vendor/linuxcnc/configs/sim/axis/geometry/xyzc.ngc
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
o110 sub
|
||||
f100
|
||||
g61
|
||||
g0 x1.0 y0.0
|
||||
g1 x1.5 y0.0
|
||||
g1 x1.5 y0.5
|
||||
g1 x1.0 y0.5
|
||||
g1 x1.0 y0.0
|
||||
o110 endsub
|
||||
|
||||
o100 sub
|
||||
g0 c90
|
||||
o110 call
|
||||
g0 c180
|
||||
o110 call
|
||||
g0 c270
|
||||
o110 call
|
||||
g0x0y0z0c0
|
||||
o100 endsub
|
||||
|
||||
;#########################################################
|
||||
m110 ; clear messages
|
||||
g0x0y0z0c0
|
||||
o110 call
|
||||
|
||||
o200 if [#5210 ne 0]
|
||||
(debug, ! Prog loaded with g92 offsets)
|
||||
(debug, RELOAD program required)
|
||||
g92.1
|
||||
(debug, bye)
|
||||
m2
|
||||
o200 endif
|
||||
|
||||
o210 if [[#5221 ne 0] or [#5222 ne 0] or [#5223 ne 0] or [#5223 ne 0] or [#5225 ne 0] or [#5226 ne 0] or [#5227 ne 0] or [#5228 ne 0] or [#5229 ne 0] ]
|
||||
(debug, ! Prog loaded with g5x offsets)
|
||||
(debug, RELOAD program required)
|
||||
g92.1
|
||||
g10l2p0x0y0z0c0
|
||||
(debug, bye)
|
||||
m2
|
||||
o210 endif
|
||||
|
||||
g0x0y0z0c0
|
||||
g10l2p0x0y0z0c0
|
||||
(debug, First: run with NO offsets, s to continue)
|
||||
m0
|
||||
o100 call
|
||||
|
||||
o300 if [#<_task> eq 1]
|
||||
g10l2p0x1
|
||||
(debug, Next: run with g5x xoffset=#5221, s to continue)
|
||||
m0
|
||||
o100 call
|
||||
|
||||
g10l2p0x0y0z0c0
|
||||
g92x2
|
||||
(debug, Next: run with g92 xoffset=#5211, s to continue)
|
||||
m0
|
||||
o100 call
|
||||
o300 endif
|
||||
|
||||
g10l2p0x0y0z0c0
|
||||
g92.1
|
||||
g0x0y0z0c0
|
||||
(debug,fini)
|
||||
m2
|
||||
125
wasm-port/vendor/linuxcnc/configs/sim/axis/gladevcp/gladevcp_panel.ini
vendored
Normal file
125
wasm-port/vendor/linuxcnc/configs/sim/axis/gladevcp/gladevcp_panel.ini
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
[EMC]
|
||||
VERSION = 1.1
|
||||
MACHINE = LinuxCNC-PANEL-GLADEVCP
|
||||
# Debug level, 0 means no messages. See src/emc/nml_int/emcglb.h for others
|
||||
DEBUG = 0
|
||||
|
||||
[DISPLAY]
|
||||
GLADEVCP= -u hitcounter.py manual-example.ui
|
||||
DISPLAY = axis
|
||||
CYCLE_TIME = 0.100
|
||||
HELP_FILE = doc/help.txt
|
||||
POSITION_OFFSET = RELATIVE
|
||||
POSITION_FEEDBACK = ACTUAL
|
||||
MAX_FEED_OVERRIDE = 1.2
|
||||
MAX_SPINDLE_OVERRIDE = 1.0
|
||||
MAX_LINEAR_VELOCITY = 1.2
|
||||
DEFAULT_LINEAR_VELOCITY = .25
|
||||
PROGRAM_PREFIX = ../../nc_files/
|
||||
INTRO_GRAPHIC = linuxcnc.gif
|
||||
INTRO_TIME = 5
|
||||
#EDITOR = geany
|
||||
TOOL_EDITOR = tooledit
|
||||
INCREMENTS = 1 in, 0.1 in, 10 mil, 1 mil, 1mm, .1mm, 1/8000 in
|
||||
|
||||
[FILTER]
|
||||
PROGRAM_EXTENSION = .png,.gif,.jpg Grayscale Depth Image
|
||||
PROGRAM_EXTENSION = .py Python Script
|
||||
png = image-to-gcode
|
||||
gif = image-to-gcode
|
||||
jpg = image-to-gcode
|
||||
py = python3
|
||||
|
||||
[RS274NGC]
|
||||
PARAMETER_FILE = sim.var
|
||||
SUBROUTINE_PATH = ../../nc_files/gladevcp_lib
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
COMM_TIMEOUT = 1.0
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.001
|
||||
|
||||
[HAL]
|
||||
HALUI = halui
|
||||
HALFILE = LIB:basic_sim.tcl
|
||||
POSTGUI_HALFILE= manual-example.hal
|
||||
|
||||
[TRAJ]
|
||||
COORDINATES = X Y Z
|
||||
HOME = 0 0 0
|
||||
LINEAR_UNITS = inch
|
||||
ANGULAR_UNITS = degree
|
||||
DEFAULT_LINEAR_VELOCITY = 1.2
|
||||
POSITION_FILE = position.txt
|
||||
MAX_LINEAR_VELOCITY = 1.2
|
||||
NO_FORCE_HOMING = 1
|
||||
|
||||
[EMCIO]
|
||||
TOOL_TABLE = sim.tbl
|
||||
TOOL_CHANGE_POSITION = 0 0 0
|
||||
TOOL_CHANGE_QUILL_UP = 1
|
||||
|
||||
[KINS]
|
||||
KINEMATICS = trivkins
|
||||
JOINTS = 3
|
||||
|
||||
[AXIS_X]
|
||||
HOME = 0.000
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 100.0
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 100.0
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
HOME_OFFSET = 0.0
|
||||
HOME_SEARCH_VEL = 20.0
|
||||
HOME_LATCH_VEL = 20.0
|
||||
HOME_SEQUENCE = 1
|
||||
|
||||
[AXIS_Y]
|
||||
HOME = 0.000
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 100.0
|
||||
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.000
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 100.0
|
||||
MIN_LIMIT = -40.0
|
||||
MAX_LIMIT = 40.0
|
||||
HOME_OFFSET = 0.0
|
||||
HOME_SEARCH_VEL = 20.0
|
||||
HOME_LATCH_VEL = 20.0
|
||||
HOME_SEQUENCE = 1
|
||||
|
||||
[AXIS_Z]
|
||||
HOME = 0.0
|
||||
MIN_LIMIT = -8.0
|
||||
MAX_LIMIT = 0.0001
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 100.0
|
||||
|
||||
[JOINT_2]
|
||||
TYPE = LINEAR
|
||||
HOME = 0.0
|
||||
MAX_VELOCITY = 4
|
||||
MAX_ACCELERATION = 100.0
|
||||
MIN_LIMIT = -8.0
|
||||
MAX_LIMIT = 0.0001
|
||||
HOME_OFFSET = 1.0
|
||||
HOME_SEARCH_VEL = 20.0
|
||||
HOME_LATCH_VEL = 20.0
|
||||
HOME_SEQUENCE = 0
|
||||
57
wasm-port/vendor/linuxcnc/configs/sim/axis/gladevcp/probe.ngc
vendored
Normal file
57
wasm-port/vendor/linuxcnc/configs/sim/axis/gladevcp/probe.ngc
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
; caveat - this changes feed and abs/relative mode
|
||||
|
||||
O<probe> SUB
|
||||
|
||||
(print, _Probe_Axis= #<_Probe_Axis>)
|
||||
(print, _Probe_Speed = #<_Probe_Speed>)
|
||||
(print, _Probe_Retract = #<_Probe_Retract>)
|
||||
(print, _Probe_Distance = #<_Probe_Distance>)
|
||||
(print, _Probe_Diameter = #<_Probe_Diameter>)
|
||||
(print, _Probe_System = #<_Probe_System>)
|
||||
|
||||
|
||||
O<xaxis> if [#<_Probe_Axis> eq 0]
|
||||
|
||||
G91 G38.3 X#<_Probe_Distance> F#<_Probe_Speed>
|
||||
O<xresult> if [#5070]
|
||||
(MSG, probe succeeded)
|
||||
G10 L20 P#<_Probe_System> X#<_Probe_Diameter>
|
||||
G90 G0 X#<_Probe_Retract>
|
||||
O<xresult> else
|
||||
(MSG,probe failed)
|
||||
G91 G0 X[0 - #<_Probe_Distance>]
|
||||
O<xresult> endif
|
||||
G90
|
||||
O<xaxis> endif
|
||||
|
||||
O<yaxis> if [#<_Probe_Axis> eq 1]
|
||||
|
||||
G91 G38.3 y#<_Probe_Distance> F#<_Probe_Speed>
|
||||
O<yresult> if [#5070]
|
||||
(MSG, probe succeeded)
|
||||
G10 L20 P#<_Probe_System> y#<_Probe_Diameter>
|
||||
G90 G0 y#<_Probe_Retract>
|
||||
O<yresult> else
|
||||
(MSG,probe failed)
|
||||
G91 G0 y[0 - #<_Probe_Distance>]
|
||||
O<yresult> endif
|
||||
G90
|
||||
O<yaxis> endif
|
||||
|
||||
O<zaxis> if [#<_Probe_Axis> eq 2]
|
||||
|
||||
G91 G38.3 z#<_Probe_Distance> F#<_Probe_Speed>
|
||||
O<zresult> if [#5070]
|
||||
(MSG, probe succeeded)
|
||||
G10 L20 P#<_Probe_System> z#<_Probe_Diameter>
|
||||
G90 G0 z#<_Probe_Retract>
|
||||
O<zresult> else
|
||||
(MSG,probe failed)
|
||||
G91 G0 z[0 - #<_Probe_Distance>]
|
||||
O<zresult> endif
|
||||
G90
|
||||
O<zaxis> endif
|
||||
|
||||
O<probe> endsub
|
||||
|
||||
M2
|
||||
4
wasm-port/vendor/linuxcnc/configs/sim/axis/gladevcp/sim.tbl
vendored
Normal file
4
wasm-port/vendor/linuxcnc/configs/sim/axis/gladevcp/sim.tbl
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
T1 P1 D0.125000 Z+0.511000 ;1/8 end mill
|
||||
T2 P2 D0.062500 Z+0.100000 ;1/16 end mill
|
||||
T3 P3 D0.201000 Z+1.273000 ;#7 tap drill
|
||||
T98876 P543 Z+0.100000 ;big tool number
|
||||
27
wasm-port/vendor/linuxcnc/configs/sim/axis/rose_engine/rcone.ngc
vendored
Normal file
27
wasm-port/vendor/linuxcnc/configs/sim/axis/rose_engine/rcone.ngc
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
o<rcone> sub
|
||||
#<rmax> = #1 (= 10)
|
||||
#<rmin> = #2 (= 1)
|
||||
#<del_r> = #3 (= 0.001)
|
||||
#<del_z> = #4 (= -0.001)
|
||||
#<del_theta> = #5 (= +1 1:CCW -1:CW)
|
||||
#<frate> = #6 (=100)
|
||||
|
||||
#<r> = #<rmax>
|
||||
#<z> = 0
|
||||
#<theta> = 0
|
||||
|
||||
f #<frate>
|
||||
|
||||
g0 x#<r> y0
|
||||
o<10wh> while [#<r> gt #<rmin>]
|
||||
#<r> = [#<r> - #<del_r>]
|
||||
#<theta> = [#<theta> + #<del_theta>]
|
||||
#<x> = [#<r> * cos[#<theta>]]
|
||||
#<y> = [#<r> * sin[#<theta>]]
|
||||
#<z> = [#<z> + #<del_z>]
|
||||
;#<x> = [#<rmax> * cos[#<theta>]]
|
||||
;#<y> = [#<rmax> * sin[#<theta>]]
|
||||
;#<z> = [0 + #<del_z>]
|
||||
g1 x#<x> y#<y> z#<z>
|
||||
o<10wh> endwhile
|
||||
o<rcone> endsub
|
||||
11
wasm-port/vendor/linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc
vendored
Normal file
11
wasm-port/vendor/linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
f1000
|
||||
#<v> = 10
|
||||
g0 x #<v> y 0 z 0
|
||||
g1 x #<v> y #<v>
|
||||
g1 x -#<v> y #<v>
|
||||
g1 x -#<v> y -#<v>
|
||||
g1 x #<v> y -#<v>
|
||||
g1 x #<v> y 0
|
||||
g0 z 0
|
||||
o<rcone> call [#<v>][1][0.001][-0.001][+1][100]
|
||||
m2
|
||||
84
wasm-port/vendor/linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini
vendored
Normal file
84
wasm-port/vendor/linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
[APPLICATIONS]
|
||||
APP = halshow rose_engine.halshow
|
||||
[HAL]
|
||||
HALUI = halui
|
||||
HALFILE = LIB:basic_sim.tcl
|
||||
|
||||
[TRAJ]
|
||||
COORDINATES = XYZ
|
||||
LINEAR_UNITS = inch
|
||||
ANGULAR_UNITS = degree
|
||||
DEFAULT_LINEAR_VELOCITY = 10
|
||||
MAX_LINEAR_VELOCITY = 10
|
||||
|
||||
[KINS]
|
||||
KINEMATICS = rosekins
|
||||
JOINTS = 3
|
||||
|
||||
[EMC]
|
||||
VERSION = 1.1
|
||||
MACHINE = Roseengine
|
||||
|
||||
[DISPLAY]
|
||||
DISPLAY = axis
|
||||
OPEN_FILE = ./rcone_demo.ngc
|
||||
POSITION_OFFSET = RELATIVE
|
||||
MAX_LINEAR_VELOCITY = 10
|
||||
DEFAULT_LINEAR_VELOCITY = 10
|
||||
MAX_ANGULAR_VELOCITY = 72
|
||||
DEFAULT_ANGULAR_VELOCITY = 72
|
||||
MAX_FEED_OVERRIDE = 2
|
||||
TOOL_EDITOR = tooledit
|
||||
INCREMENTS = 1 in,0.1in,10mil,1mil
|
||||
TKPKG = Ngcgui 1.0
|
||||
NGCGUI_SUBFILE = rcone.ngc
|
||||
NGCGUI_FONT = Helvetica -14 normal
|
||||
|
||||
[RS274NGC]
|
||||
PARAMETER_FILE = /tmp/rose.var
|
||||
SUBROUTINE_PATH = .
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.001
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[EMCIO]
|
||||
TOOL_TABLE = rose.tbl
|
||||
|
||||
[AXIS_X]
|
||||
MAX_VELOCITY = 3
|
||||
MAX_ACCELERATION = 30
|
||||
[AXIS_Y]
|
||||
MAX_VELOCITY = 3
|
||||
MAX_ACCELERATION = 30
|
||||
[AXIS_Z]
|
||||
MAX_VELOCITY = 3
|
||||
MAX_ACCELERATION = 30
|
||||
|
||||
# Notes:
|
||||
# HOME_SEARCH_VEL=0 for immediate homing
|
||||
# HOME_SEQUENCE=0 for homeall in gui
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
MAX_VELOCITY = 3
|
||||
MAX_ACCELERATION = 30
|
||||
HOME_SEARCH_VEL = 0
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
MAX_VELOCITY = 3
|
||||
MAX_ACCELERATION = 30
|
||||
HOME_SEARCH_VEL = 0
|
||||
HOME_SEQUENCE = 0
|
||||
|
||||
[JOINT_2]
|
||||
TYPE = ANGULAR
|
||||
MAX_VELOCITY = 72
|
||||
MAX_ACCELERATION = 360
|
||||
HOME_SEARCH_VEL = 0
|
||||
HOME_SEQUENCE = 0
|
||||
5
wasm-port/vendor/linuxcnc/configs/sim/axis/sim.tbl
vendored
Normal file
5
wasm-port/vendor/linuxcnc/configs/sim/axis/sim.tbl
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
T1 P1 Z0.511 D0.125 ;1/8 end mill
|
||||
T2 P2 Z0.1 D0.0625 ;1/16 end mill
|
||||
T3 P3 Z1.273 D0.201 ;#7 tap drill
|
||||
T99999 P99999 Z0.1 ;big tool number
|
||||
T4 P4 Z0 D.005 ;Added 20131029
|
||||
93
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/melfa-sim/example.ngc
vendored
Normal file
93
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/melfa-sim/example.ngc
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
(Preamble)
|
||||
G90 G64 P0.1 Q1
|
||||
|
||||
(Switch to Joint mode trivial kinematics)
|
||||
M429
|
||||
|
||||
(When using "Joint" mode we need to make sure that)
|
||||
(the offsets are set correctly for the pose we used)
|
||||
(when we set up the Modified DH-Parameters for the )
|
||||
("genserkins" kinematic where we cannot define 'Theta')
|
||||
(-values.)
|
||||
("M429" sets offsets to "G59.2" and resets those)
|
||||
(to "G10 L2 P8 X0 Y-90 Z0 A0 B0 C0" so we match the DH-)
|
||||
(Parameter model used in the genserkins kinematics)
|
||||
(Note that this pose leaves Joint_3 and Joint_5 collinear)
|
||||
(and that will cause the inverse kinematics to fail so we)
|
||||
(need to make sure that the wrist does not start out at)
|
||||
(zero degrees.)
|
||||
(Note that we also change speed settings through HAL when)
|
||||
(we switch kinematics)
|
||||
|
||||
(Set the start pose to make sure the kinematics can handle it)
|
||||
G01 X0 Y0 Z0 A0 B0 C0 F1000
|
||||
|
||||
(Switch to Coordinated, i.e. cartesian, world mode)
|
||||
M428
|
||||
("M428" sets offsets to "G59.1" and resets those)
|
||||
(to "G10 L2 P7 X0 Y0 Z0 A0 B0 C0")
|
||||
(Origin is at bottom center of the robot base)
|
||||
(So to use other, nonzero, offsets we need to declare after)
|
||||
(each switch of the kinematics)
|
||||
|
||||
|
||||
|
||||
G0 X450 Y200 Z350 B10 C45
|
||||
|
||||
(Draw a cube of 150mm)
|
||||
G91
|
||||
G0 Y150
|
||||
X150
|
||||
Y-150
|
||||
X-150
|
||||
Z150
|
||||
Y150
|
||||
Z-150
|
||||
Z150
|
||||
X150
|
||||
Z-150
|
||||
Z150
|
||||
Y-150
|
||||
Z-150
|
||||
Z150
|
||||
X-150
|
||||
|
||||
(Switch to Joint mode trivial kinematics)
|
||||
M429
|
||||
|
||||
(Go different pose)
|
||||
G90
|
||||
G0 X90 Y0 Z0 A0
|
||||
|
||||
(Switch to Coordinated, i.e. cartesian, world mode)
|
||||
M428
|
||||
|
||||
(Draw a cube of 150mm)
|
||||
G91
|
||||
G0
|
||||
Z-150
|
||||
Y150
|
||||
X150
|
||||
Y-150
|
||||
X-150
|
||||
Z150
|
||||
Y150
|
||||
Z-150
|
||||
Z150
|
||||
X150
|
||||
Z-150
|
||||
Z150
|
||||
Y-150
|
||||
Z-150
|
||||
Z150
|
||||
X-150
|
||||
|
||||
|
||||
(Switch to Joint mode trivial kinematics)
|
||||
M429
|
||||
|
||||
(Go to start pose)
|
||||
G90
|
||||
G0 X0 Y0 Z0 A0
|
||||
|
||||
M2
|
||||
143
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini
vendored
Normal file
143
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
[EMC]
|
||||
VERSION = 1.1
|
||||
MACHINE = melfa (mm)
|
||||
DEBUG = 0
|
||||
|
||||
[KINS]
|
||||
KINEMATICS= genserkins
|
||||
JOINTS= 6
|
||||
|
||||
[HAL]
|
||||
HALUI = halui
|
||||
HALFILE = LIB:basic_sim.tcl
|
||||
HALFILE = melfa_dh.hal
|
||||
HALCMD = loadusr -W melfagui
|
||||
HALCMD = net :kinstype-select <= motion.analog-out-03 => motion.switchkins-type
|
||||
POSTGUI_HALFILE = melfa-postgui.hal
|
||||
|
||||
[RS274NGC]
|
||||
PARAMETER_FILE = melfa.var
|
||||
SUBROUTINE_PATH = ./remap_subs
|
||||
HAL_PIN_VARS = 1
|
||||
REMAP = M428 modalgroup=10 ngc=428remap
|
||||
REMAP = M429 modalgroup=10 ngc=429remap
|
||||
REMAP = M430 modalgroup=10 ngc=430remap
|
||||
RS274NGC_STARTUP_CODE = G10 L2 P7 X0 Y0 Z0 A-180 B0 C0 G59.1 (debug, ini: startup offsets)
|
||||
|
||||
[HALUI]
|
||||
MDI_COMMAND = M429
|
||||
MDI_COMMAND = M428
|
||||
MDI_COMMAND = M430
|
||||
|
||||
|
||||
[DISPLAY]
|
||||
DISPLAY = axis
|
||||
GEOMETRY = XYZABC
|
||||
CYCLE_TIME = 0.200
|
||||
POSITION_OFFSET = RELATIVE
|
||||
POSITION_FEEDBACK = ACTUAL
|
||||
DEFAULT_LINEAR_VELOCITY = 60.0
|
||||
DEFAULT_ANGULAR_VELOCITY = 40.0
|
||||
MAX_FEED_OVERRIDE = 2.0
|
||||
PROGRAM_PREFIX = ./
|
||||
INTRO_GRAPHIC = linuxcnc.gif
|
||||
INTRO_TIME = 5
|
||||
PYVCP = melfa.xml
|
||||
EDITOR = geany
|
||||
OPEN_FILE = example.ngc
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.010
|
||||
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
COMM_TIMEOUT = 1.0
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[TRAJ]
|
||||
COORDINATES = X Y Z A B C
|
||||
LINEAR_UNITS = mm
|
||||
ANGULAR_UNITS = deg
|
||||
DEFAULT_LINEAR_VELOCITY = 60.0
|
||||
DEFAULT_ANGULAR_VELOCITY = 60.0
|
||||
MAX_LINEAR_VELOCITY = 200.0
|
||||
MAX_ANGULAR_VELOCITY = 100.0
|
||||
DEFAULT_LINEAR_ACCELERATION = 200.0
|
||||
MAX_LINEAR_ACCELERATION = 400.0
|
||||
|
||||
[EMCIO]
|
||||
TOOL_TABLE = melfa.tbl
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = ANGULAR
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
MIN_LIMIT = -170.0
|
||||
MAX_LIMIT = 170.0
|
||||
HOME_SEQUENCE = 0
|
||||
HOME_OFFSET = 0
|
||||
HOME = 0
|
||||
[JOINT_1]
|
||||
TYPE = ANGULAR
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
MIN_LIMIT = -182.0
|
||||
MAX_LIMIT = 45.0
|
||||
HOME_OFFSET = 0.0
|
||||
HOME_SEQUENCE = 0
|
||||
HOME = -90.001
|
||||
[JOINT_2]
|
||||
TYPE = ANGULAR
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
MIN_LIMIT = -219.0
|
||||
MAX_LIMIT = 76.0
|
||||
HOME_OFFSET = 0.0
|
||||
HOME_SEQUENCE = 0
|
||||
HOME = 0.001
|
||||
[JOINT_3]
|
||||
TYPE = ANGULAR
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
MIN_LIMIT = -160.0
|
||||
MAX_LIMIT = 160.0
|
||||
HOME_OFFSET = 0.0
|
||||
HOME_SEQUENCE = 0
|
||||
HOME = 0.001
|
||||
[JOINT_4]
|
||||
TYPE = ANGULAR
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
MIN_LIMIT = -120.0
|
||||
MAX_LIMIT = 120.0
|
||||
HOME_OFFSET = 0.0
|
||||
HOME_SEQUENCE = 0
|
||||
HOME = 90.001
|
||||
[JOINT_5]
|
||||
TYPE = ANGULAR
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
HOME_OFFSET = 0.0
|
||||
HOME_SEQUENCE = 0
|
||||
HOME = 0.001
|
||||
|
||||
[AXIS_X]
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
[AXIS_Y]
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
[AXIS_Z]
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
[AXIS_A]
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
[AXIS_B]
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
[AXIS_C]
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
4
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.tbl
vendored
Normal file
4
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.tbl
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
T1 P1 Z100 D0.125 ;1/8 end mill
|
||||
T2 P2 Z0.1 D0.0625 ;1/16 end mill
|
||||
T3 P3 Z1.273 D0.201 ;#7 tap drill
|
||||
T99999 P99999 Z0.1 ;big tool number
|
||||
28
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc
vendored
Normal file
28
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
;M428 by remap: select genserkins
|
||||
o<428remap>sub
|
||||
#<SWITCHKINS_PIN> = 3 ; set N as required: motion.analog-out-0N
|
||||
#<kinstype> = 0 ; genserkins
|
||||
|
||||
o1 if [exists [#<_hal[motion.switchkins-type]>]]
|
||||
o1 else
|
||||
(debug,M428:Missing [RS274NGC]FEATURE==8)
|
||||
(debug,STOP)
|
||||
M2
|
||||
o1 endif
|
||||
|
||||
M66 E0 L0 ; force synch
|
||||
M68 E#<SWITCHKINS_PIN> Q#<kinstype> ; set kinstype value
|
||||
G10 L2 P7 X0 Y0 Z0 A-180 B0 C0
|
||||
G59.1
|
||||
M66 E0 L0 ; force synch
|
||||
; (debug, M428:genserkins)
|
||||
|
||||
o2 if [[#<_task> EQ 1] AND [#<_hal[motion.switchkins-type]> NE 0]]
|
||||
(debug,M428: Wrong motion.switchkins-type)
|
||||
(debug,or missing hal net to analog-out-0x)
|
||||
(debug,STOP)
|
||||
M2
|
||||
o2 else
|
||||
o2 endif
|
||||
|
||||
o<428remap>endsub
|
||||
28
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/429remap.ngc
vendored
Normal file
28
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/429remap.ngc
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
;M429 by remap: select identity kins
|
||||
o<429remap>sub
|
||||
#<SWITCHKINS_PIN> = 3 ; set N as required: motion.analog-out-0N
|
||||
#<kinstype> = 1 ; identity kins
|
||||
|
||||
o1 if [exists [#<_hal[motion.switchkins-type]>]]
|
||||
o1 else
|
||||
(debug,M429:Missing [RS274NGC]FEATURE==8)
|
||||
(debug,STOP)
|
||||
M2
|
||||
o1 endif
|
||||
|
||||
M66 E0 L0 ; force synch
|
||||
M68 E#<SWITCHKINS_PIN> Q#<kinstype> ; set kinstype value
|
||||
G10 L2 P8 X0 Y-90 Z0 A0 B90 C0
|
||||
G59.2
|
||||
M66 E0 L0 ; force synch
|
||||
; (debug, M429:identity kins)
|
||||
|
||||
o2 if [[#<_task> EQ 1] AND [#<_hal[motion.switchkins-type]> NE 1]]
|
||||
(debug,M429:Wrong motion.switchkins-type)
|
||||
(debug,or missing hal net to analog-out-0x)
|
||||
(debug,STOP)
|
||||
M2
|
||||
o2 else
|
||||
o2 endif
|
||||
|
||||
o<429remap>endsub
|
||||
26
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/430remap.ngc
vendored
Normal file
26
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/430remap.ngc
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
;M430 by remap: select gensertool kins
|
||||
o<430remap>sub
|
||||
#<SWITCHKINS_PIN> = 3 ; set N as required: motion.analog-out-0N
|
||||
#<kinstype> = 2 ; gensertool kins
|
||||
|
||||
o1 if [exists [#<_hal[motion.switchkins-type]>]]
|
||||
o1 else
|
||||
(debug,M430:Missing [RS274NGC]FEATURE==8)
|
||||
(debug,STOP)
|
||||
M2
|
||||
o1 endif
|
||||
|
||||
M66 E0 L0 ; force synch
|
||||
M68 E#<SWITCHKINS_PIN> Q#<kinstype> ; set kinstype value
|
||||
M66 E0 L0 ; force synch
|
||||
; (debug, M429:identity kins)
|
||||
|
||||
o2 if [[#<_task> EQ 1] AND [#<_hal[motion.switchkins-type]> NE 2]]
|
||||
(debug,M430:Wrong motion.switchkins-type)
|
||||
(debug,or missing hal net to analog-out-0x)
|
||||
(debug,STOP)
|
||||
M2
|
||||
o2 else
|
||||
o2 endif
|
||||
|
||||
o<430remap>endsub
|
||||
212
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/millturn/example.ngc
vendored
Normal file
212
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/millturn/example.ngc
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
(setup work offsets to run sim w/o .var file)
|
||||
G10 L2 P1 X-160 Y0 Z-230 (setup G54 for mill)
|
||||
G10 L2 P2 X-275 Y0 Z-140 (setup G55 for turn)
|
||||
|
||||
(actual code starts here)
|
||||
G21
|
||||
G40
|
||||
G64
|
||||
|
||||
M429 (turning)
|
||||
G18 G8
|
||||
G54
|
||||
T05 M6 G43
|
||||
G00 X20 Z25
|
||||
G00 X13.5 Z1.0 S1000 M3
|
||||
Z0.488
|
||||
G94 G01 X-1.0 F1000.0
|
||||
Z0.975
|
||||
G00 X0.383 Z1.269
|
||||
X13.5
|
||||
Z0.0
|
||||
G01 X-1.0
|
||||
Z0.488
|
||||
G00 X0.383 Z0.782
|
||||
X13.5
|
||||
Z1.0
|
||||
Z2.0
|
||||
X11.237
|
||||
G01 Z-34.973 F1000.0
|
||||
X12.2 Z-35.95
|
||||
Z-37.95
|
||||
X12.625
|
||||
G00 X13.625 Z-36.95
|
||||
Z2.0
|
||||
X9.849
|
||||
G01 Z-19.832
|
||||
G00 X10.849 Z-18.832
|
||||
Z2.0
|
||||
X8.461
|
||||
G01 Z-19.832
|
||||
X9.849
|
||||
G00 X10.849 Z-18.832
|
||||
Z2.0
|
||||
X7.073
|
||||
G01 Z-10.296
|
||||
G00 X8.073 Z-9.296
|
||||
Z2.0
|
||||
X5.685
|
||||
G01 Z-8.784
|
||||
G03 X7.073 Z-10.296 I-0.98513 K-2.29772
|
||||
G00 X8.073 Z-9.296
|
||||
Z2.0
|
||||
X4.297
|
||||
G01 Z-8.582
|
||||
X4.7
|
||||
G03 X5.685 Z-8.784 I-0.0 K-2.5
|
||||
G00 X6.685 Z-7.784
|
||||
Z2.0
|
||||
X2.909
|
||||
G01 Z-0.559
|
||||
G00 X3.909 Z0.441
|
||||
Z2.0
|
||||
X1.521
|
||||
G01 Z0.829
|
||||
X2.909 Z-0.559
|
||||
G00 X4.909 Z1.441
|
||||
Z0.441
|
||||
X2.909
|
||||
G01 Z-0.559
|
||||
X3.2 Z-0.85
|
||||
Z-4.15
|
||||
X2.909 Z-4.654
|
||||
Z-8.11
|
||||
G02 X4.2 Z-8.582 I1.29082 K1.52767
|
||||
G01 X4.297
|
||||
G00 X5.297 Z-7.582
|
||||
Z-4.654
|
||||
X3.909
|
||||
G01 X2.909
|
||||
X2.468 Z-5.418
|
||||
G02 X2.2 Z-6.418 I1.73205 K-1.0
|
||||
G01 Z-6.582
|
||||
G02 X2.909 Z-8.11 I2.0 K0.0
|
||||
G00 X3.909 Z-7.11
|
||||
X7.073
|
||||
Z-9.296
|
||||
G01 Z-10.296
|
||||
G03 X7.2 Z-11.082 I-2.3731 K-0.78638
|
||||
G01 Z-11.382
|
||||
G03 X7.073 Z-12.168 I-2.5 K0.0
|
||||
G01 Z-19.832
|
||||
X8.461
|
||||
G00 X9.461 Z-18.832
|
||||
Z-12.168
|
||||
X8.073
|
||||
G01 X7.073
|
||||
G03 X6.485 Z-13.132 I-2.3731 K0.78638
|
||||
G02 X5.685 Z-14.204 I3.57071 K-3.5
|
||||
G01 Z-18.76
|
||||
G02 X6.485 Z-19.832 I4.37094 K2.42793
|
||||
G01 X7.073
|
||||
G00 X8.073 Z-18.832
|
||||
Z-14.204
|
||||
X6.685
|
||||
G01 X5.685
|
||||
G02 X5.058 Z-16.482 I4.37075 K-2.42793
|
||||
G02 X5.685 Z-18.76 I4.99775 K0.15
|
||||
G00 X6.685 Z-17.76
|
||||
X9.849
|
||||
Z-18.832
|
||||
G01 Z-19.832
|
||||
X10.2
|
||||
Z-20.132
|
||||
X9.849 Z-20.74
|
||||
Z-24.42
|
||||
G00 X10.849 Z-23.42
|
||||
Z-20.74
|
||||
G01 X9.849
|
||||
X8.787 Z-22.58
|
||||
X9.849 Z-24.42
|
||||
G00 X10.849 Z-23.42
|
||||
X9.849
|
||||
G01 Z-24.42
|
||||
X10.2 Z-25.028
|
||||
Z-27.328
|
||||
G02 X9.849 Z-28.029 I5.70735 K-3.29514
|
||||
G01 Z-32.917
|
||||
G02 X11.214 Z-34.95 I6.0583 K2.594
|
||||
G01 X11.237 Z-34.973
|
||||
G00 X12.237 Z-33.973
|
||||
Z-28.029
|
||||
X10.849
|
||||
G01 X9.849
|
||||
G02 X9.319 Z-30.473 I6.05858 K-2.594
|
||||
G02 X9.849 Z-32.917 I6.58858 K0.15
|
||||
G00 X10.849 Z-31.917
|
||||
Z-29.029
|
||||
X11.237
|
||||
Z2.0
|
||||
X0.534 Z3.241
|
||||
G01 X0.202 Z3.041 F150.0
|
||||
G02 X1.081 Z0.919 I3.0 K0.0
|
||||
G01 X3.0 Z-1.0
|
||||
Z-4.0
|
||||
X2.268 Z-5.268
|
||||
G02 X2.0 Z-6.268 I1.73205 K-1.0
|
||||
G01 Z-6.732
|
||||
G02 X4.0 Z-8.732 I2.0 K0.0
|
||||
G01 X4.5
|
||||
G03 X6.285 Z-12.982 I-0.0 K-2.5
|
||||
G02 Z-19.982 I3.57071 K-3.5
|
||||
G01 X10.0
|
||||
X8.5 Z-22.58
|
||||
X10.0 Z-25.178
|
||||
Z-27.178
|
||||
G02 X11.014 Z-35.1 I5.70735 K-3.29514
|
||||
G01 X12.0 Z-36.1
|
||||
Z-38.1
|
||||
G00 X15.0
|
||||
Z10.0
|
||||
|
||||
M428 (milling)
|
||||
g55
|
||||
T10 m6 G43
|
||||
g21 g17 g64 g90
|
||||
s3400 m3
|
||||
|
||||
g0 z10
|
||||
g0 x5 y0
|
||||
g2 x-5 y0 i-5 j0 z-1 f500
|
||||
g2 x5 y0 i5 j0 z-2
|
||||
g2 x-5 y0 i-5 j0 z-3
|
||||
g2 x5 y0 i5 j0 z-4
|
||||
g2 x-5 y0 i-5 j0 z-4
|
||||
|
||||
g0 z10
|
||||
g0 a90
|
||||
g0 x5 y0
|
||||
g2 x-5 y0 i-5 j0 z-1
|
||||
g2 x5 y0 i5 j0 z-2
|
||||
g2 x-5 y0 i-5 j0 z-3
|
||||
g2 x5 y0 i5 j0 z-4
|
||||
g2 x-5 y0 i-5 j0 z-4
|
||||
|
||||
|
||||
g0 z10
|
||||
g0 a180
|
||||
g0 x5 y0
|
||||
g2 x-5 y0 i-5 j0 z-1
|
||||
g2 x5 y0 i5 j0 z-2
|
||||
g2 x-5 y0 i-5 j0 z-3
|
||||
g2 x5 y0 i5 j0 z-4
|
||||
g2 x-5 y0 i-5 j0 z-4
|
||||
|
||||
g0 z10
|
||||
g0 a270
|
||||
g0 x5 y0
|
||||
g2 x-5 y0 i-5 j0 z-1
|
||||
g2 x5 y0 i5 j0 z-2
|
||||
g2 x-5 y0 i-5 j0 z-3
|
||||
g2 x5 y0 i5 j0 z-4
|
||||
g2 x-5 y0 i-5 j0 z-4
|
||||
|
||||
g0 z10
|
||||
g0 a360
|
||||
|
||||
M428 (milling)
|
||||
G54
|
||||
G0 X0 Z100 A0
|
||||
|
||||
M2
|
||||
%
|
||||
145
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/millturn/millturn.ini
vendored
Normal file
145
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/millturn/millturn.ini
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
[APPLICATIONS]
|
||||
APP = halshow ./millturn.halshow
|
||||
|
||||
[EMC]
|
||||
VERSION = 1.1
|
||||
MACHINE = millturn (mm)
|
||||
DEBUG = 0
|
||||
|
||||
[KINS]
|
||||
KINEMATICS = millturn
|
||||
JOINTS= 4
|
||||
|
||||
[HAL]
|
||||
HALUI = halui
|
||||
HALFILE = LIB:basic_sim.tcl
|
||||
HALFILE = millturn.hal
|
||||
HALCMD = net :kinstype-select <= motion.analog-out-03 => motion.switchkins-type
|
||||
POSTGUI_HALFILE = millturn-postgui.hal
|
||||
|
||||
[RS274NGC]
|
||||
USER_M_PATH = ./mcodes
|
||||
PARAMETER_FILE = millturn.var
|
||||
SUBROUTINE_PATH = ./remap_subs
|
||||
HAL_PIN_VARS = 1
|
||||
REMAP = M428 modalgroup=10 ngc=428remap
|
||||
REMAP = M429 modalgroup=10 ngc=429remap
|
||||
# Set startup offsets
|
||||
RS274NGC_STARTUP_CODE = G10 L2 P7 X-290 Y0 Z-160 A0 G59.1
|
||||
|
||||
|
||||
[HALUI]
|
||||
# MDI-COMMANDS 00, 01 (remapped) for switching kinematics and limits:
|
||||
# M428: mill (kinstype==0 startupDEFAULT)
|
||||
# M429: turn (kinstype==1)
|
||||
MDI_COMMAND = M428
|
||||
MDI_COMMAND = M429
|
||||
# MDI-COMMANDS 02, 03 are for altering limits when switching
|
||||
# Note that M129 and M129 are not meant to be called directly.
|
||||
MDI_COMMAND = M128
|
||||
MDI_COMMAND = M129
|
||||
|
||||
[DISPLAY]
|
||||
DISPLAY = axis
|
||||
GEOMETRY = XYZA
|
||||
CYCLE_TIME = 0.200
|
||||
POSITION_OFFSET = RELATIVE
|
||||
POSITION_FEEDBACK = ACTUAL
|
||||
DEFAULT_LINEAR_VELOCITY = 60.0
|
||||
DEFAULT_ANGULAR_VELOCITY = 40.0
|
||||
MAX_FEED_OVERRIDE = 2.0
|
||||
PROGRAM_PREFIX = ./
|
||||
INTRO_GRAPHIC = linuxcnc.gif
|
||||
INTRO_TIME = 5
|
||||
PYVCP = millturn.xml
|
||||
EDITOR = geany
|
||||
OPEN_FILE = example.ngc
|
||||
|
||||
[TASK]
|
||||
TASK = milltask
|
||||
CYCLE_TIME = 0.010
|
||||
|
||||
[EMCMOT]
|
||||
EMCMOT = motmod
|
||||
COMM_TIMEOUT = 1.0
|
||||
SERVO_PERIOD = 1000000
|
||||
|
||||
[TRAJ]
|
||||
COORDINATES = X Y Z A
|
||||
LINEAR_UNITS = mm
|
||||
ANGULAR_UNITS = deg
|
||||
DEFAULT_LINEAR_VELOCITY = 60.0
|
||||
DEFAULT_ANGULAR_VELOCITY = 60.0
|
||||
MAX_LINEAR_VELOCITY = 200.0
|
||||
MAX_ANGULAR_VELOCITY = 100.0
|
||||
DEFAULT_LINEAR_ACCELERATION = 200.0
|
||||
MAX_LINEAR_ACCELERATION = 400.0
|
||||
|
||||
[EMCIO]
|
||||
TOOL_TABLE = millturn.tbl
|
||||
|
||||
[JOINT_0]
|
||||
TYPE = LINEAR
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
MIN_LIMIT = -300
|
||||
MAX_LIMIT = 300
|
||||
HOME_SEQUENCE = 0
|
||||
HOME_OFFSET = 0
|
||||
HOME = 0
|
||||
[JOINT_1]
|
||||
TYPE = LINEAR
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
MIN_LIMIT = -100
|
||||
MAX_LIMIT = 100
|
||||
HOME_OFFSET = 0.0
|
||||
HOME_SEQUENCE = 0
|
||||
HOME = 0
|
||||
[JOINT_2]
|
||||
TYPE = LINEAR
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
MIN_LIMIT = -240
|
||||
MAX_LIMIT = 0
|
||||
HOME_OFFSET = 0.0
|
||||
HOME_SEQUENCE = 0
|
||||
HOME = 0
|
||||
[JOINT_3]
|
||||
TYPE = ANGULAR
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
HOME_OFFSET = 0.0
|
||||
HOME_SEQUENCE = 0
|
||||
HOME = 0.001
|
||||
|
||||
[AXIS_X]
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
# define softlimits for default kinematic (mill)
|
||||
MIN_LIMIT = -300
|
||||
MAX_LIMIT = 300
|
||||
# define softlimits for alternate kinematic (turn)
|
||||
MIN_LIMIT_TURN = -240
|
||||
MAX_LIMIT_TURN = 0
|
||||
[AXIS_Y]
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
# define softlimits for default kinematic (mill)
|
||||
MIN_LIMIT = -100
|
||||
MAX_LIMIT = 100
|
||||
# define softlimits for alternate kinematic (turn)
|
||||
MIN_LIMIT_TURN = -100
|
||||
MAX_LIMIT_TURN = 100
|
||||
[AXIS_Z]
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
# define softlimits for default kinematic (mill)
|
||||
MIN_LIMIT = -240
|
||||
MAX_LIMIT = 0
|
||||
# define softlimits for alternate kinematic (turn)
|
||||
MIN_LIMIT_TURN = -300
|
||||
MAX_LIMIT_TURN = 300
|
||||
[AXIS_A]
|
||||
MAX_VELOCITY = 60.0
|
||||
MAX_ACCELERATION = 400.0
|
||||
11
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/millturn/millturn.tbl
vendored
Normal file
11
wasm-port/vendor/linuxcnc/configs/sim/axis/vismach/millturn/millturn.tbl
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
T1 P1 X30 Z30 D0.1 I95.000000 J155.000000 Q1
|
||||
T2 P2 X40 Z40 D0.1 I85.000000 J25.000000 Q2
|
||||
T3 P3 X50 Z50 D0.1 I275.000000 J335.000000 Q3
|
||||
T4 P4 X30 Z30 D0.1 I265.000000 J205.000000 Q4
|
||||
T5 P5 X40 Z40 D0.1 I210.000000 J150.000000 Q5
|
||||
T6 P6 X50 Z50 D0.1 I120.000000 J60.000000 Q6
|
||||
T7 P7 X30 Z30 D0.1 I-30.000000 J30.000000 Q7
|
||||
T8 P8 X40 Z40 D0.1 I240.000000 J300.000000 Q8
|
||||
T9 P9 X50 Z50 D0.1 Q9
|
||||
T10 P10 Z50 D5 ;5 mm end mill
|
||||
T11 P11 Z80 D10
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user