Compare commits

...

355 Commits

Author SHA1 Message Date
cnc
c81f9d0111 按源验证API百分号文件模式
源依据:LinuxCNC src/emc/rs274ngc/rs274ngc_pre.cc 的 Interp::open() 在首个非空行是 % 时启用 percent-file mode,src/emc/rs274ngc/interp_read.cc read_text() 在匹配 % 处结束读取并忽略后续文本。

结论:native API source-linked smoke 增加 CNC_SIM_RS274_FILE_MODE 下的百分号文件断言,确认 cnc_sim API 文件模式走 LinuxCNC open/read 语义而非逐行桥接或 smoke fallback。

检查:./test-linuxcnc-api-native.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-06 13:10:02 +08:00
cnc
c75eec1057 按源验证刀库DB参数拆分
源依据:LinuxCNC src/emc/tooldata/tooldata_db.cc 的 tooldata_db_init() 使用 strtok_r 原地拆分 progname_plus_args,再检查 argv[0] 是否可执行。

结论:wasm-safe tooldata runtime probe 增加多参数输入副作用断言,确认缺失 DB 程序返回失败前仍保留 LinuxCNC 的参数拆分边界,减少外部 tooldata DB 替换风险。

检查:./test-linuxcnc-wasm-tooldata-link.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-06 13:03:48 +08:00
cnc
23e67bb6cd 按源验证非随机刀表保存边界
源依据:LinuxCNC src/emc/tooldata/tooldata_common.cc 的 tooldata_save() 在 DB_NOTUSED 下按 is_random_toolchanger 选择起始 pocket;非随机换刀器从 pocket 1 开始写出,跳过 spindle pocket 0。

结论:wasm-safe tooldata_common standalone probe 增加非随机保存断言,确认 pocket 0 的 spindle-only 数据不会写入普通刀表,同时 pocket 1 的真实刀具仍会保存,减少 tooldata_common/mmap 后端替换风险。

检查:./test-linuxcnc-wasm-tooldata-common-link.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-06 12:58:57 +08:00
cnc
42e5b0784d 按源验证Python插件路径节边界
源依据:LinuxCNC src/emc/pythonplugin/python_plugin.cc 的 configure() 按调用方 section 查找 TOPLEVEL,但 PATH_APPEND 固定从 PYTHON section 逐项读取并在 tilde expansion 失败时返回 PLUGIN_EXCEPTION_DURING_PATH_APPEND。

结论:wasm-safe python_plugin standalone probe 增加 REMAP/PYTHON section 分离边界,确认禁用 Python 时仍保留 LinuxCNC 的 INI 查找顺序和失败状态,不引入浏览器端 Python 执行。

检查:./test-linuxcnc-wasm-python-plugin-link.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-06 12:53:40 +08:00
cnc
64f336d6f2 按源验证tooldata低索引写入
源依据:LinuxCNC src/emc/tooldata/tooldata_mmap.cc 的 tooldata_put() 仅在 idx > last_index 时返回 IDX_NEW 并更新 last_index,否则返回 IDX_OK。

结论:wasm-safe tooldata mmap probe 增加低于当前 last_index 的写入断言,确认返回 IDX_OK、last_index 不变且数据可读回,减少 tooldata_mmap 后端替换风险。

检查:./test-linuxcnc-wasm-tooldata-link.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-06 12:48:33 +08:00
cnc
fc56b11d8f 按源链接Python绑定探针
结论:依据 LinuxCNC canonmodule.cc、interpmodule.cc 与 py*.cc,新增 Python binding link probe,证明 7 个 non-gcodemodule blocked binding 对象可与 inert Python/runtime shims 链接;同时负向锁定 gcodemodule.cc 与浏览器 runtime canon bridge 的 Canon 回调重定义冲突,Python/remap 执行仍禁用,未扩展 smoke。

检查:./test-linuxcnc-wasm-python-binding-link.sh;./test-linuxcnc-wasm-python-c-api-symbols.sh;./test-linuxcnc-wasm-runtime-link.sh;git diff --check;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-06 12:40:49 +08:00
cnc
fb9561ccc7 按源标记Python绑定编译覆盖
结论:依据 LinuxCNC RS274 Python/Boost.Python 绑定源 canonmodule.cc、gcodemodule.cc、interpmodule.cc 与 py*.cc,blocker 分析新增 python-compile-covered 分组,锁定这些 blocker 已由 inert Python C API symbol probe 编译覆盖;Python/remap 执行仍保持禁用,未扩展 smoke。

检查:./test-linuxcnc-wasm-python-c-api-symbols.sh;./test-linuxcnc-wasm-blockers.sh linuxcnc-rs274-wasm-source-files.txt;CNC_SIM_WASM_BLOCKERS_SELF_CHECKS=1 ./test-linuxcnc-wasm-blockers.sh linuxcnc-rs274-wasm-source-files.txt;./check-linuxcnc-wasm-blockers-source-map.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-06 12:27:22 +08:00
cnc
4e246738ac 按源清理OPFS参数scratch
结论:依据 LinuxCNC rs274ngc_pre.cc restore_parameters() 只读取配置参数文件、interp_internal.hh 默认 rs274ngc.var/.bak 常量,浏览器 OPFS loadParameterFile 在加载存在的参数文件前清理 rs274ngc.var.new/.bak scratch,避免旧 WASM mirror 污染;缺失文件路径继续清空 scratch 并返回 null。

检查:./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-06 11:47:33 +08:00
cnc
92562db29f 按源验证当前位置命名参数 2026-06-06 08:51:20 +08:00
cnc
de5f28f7ee 按源扩展坐标偏置UVW覆盖 2026-06-06 08:45:25 +08:00
cnc
82f222ba44 按源验证G10坐标UVW偏置 2026-06-06 08:38:23 +08:00
cnc
cdc30dda7e 按源验证G92单位切换UVW偏置 2026-06-06 08:35:11 +08:00
cnc
1d96aa8a55 按源验证单位切换UVW线性轴 2026-06-06 08:31:42 +08:00
cnc
25cb6885bf 按源修正外部单位换算
结论:依据 LinuxCNC emccanon.cc 与 units.h,GET_EXTERNAL_LENGTH_UNITS 返回 user-units-per-mm 而非当前程序单位;新增 G20/G21 后 G92.3 恢复覆盖,native/source-link/test-native 均通过。
2026-06-06 08:26:21 +08:00
cnc
ccd51ff373 按源同步单位切换当前位置
结论:依据 LinuxCNC interp_convert.cc convert_length_units 与 unit_tests/interp/test_interp_basics.cc,G20/G21 切换时事件桥同步重标定当前线性坐标,native/source-link 覆盖通过。
2026-06-06 08:15:59 +08:00
cnc
10bf0a5897 验证 INI G64 默认容差
提示词:快速推进,非纯 source-map-only,保留源依据、自动测试、本地提交且不自动推送。

结论:基于 LinuxCNC rs274ngc_pre.cc 读取 G64_DEFAULT_TOLERANCE/G64_DEFAULT_NAIVETOLERANCE 以及 interp_convert.cc convert_control_mode 在 G64 省略 P/Q 时使用默认值,新增 INI/G-code fixture 并接入 native/source-link 事件断言;./test-native.sh 和 ./test-linuxcnc-source-link.sh 通过。
2026-06-06 07:12:42 +08:00
cnc
ced3e5de0e 验证 INI 圆弧容差收紧
提示词:快速推进,非纯 source-map-only,保留源依据、自动测试、本地提交且不自动推送。

结论:基于 LinuxCNC rs274ngc_pre.cc 读取 CENTER_ARC_RADIUS_TOLERANCE_MM 以及 interp_convert.cc/interp_arc.cc IJK 圆弧半径误差检查,新增 metric arc fixture 和 tighter INI 覆盖默认通过、INI 收紧失败;./test-native.sh 和 ./test-linuxcnc-source-link.sh 通过。
2026-06-06 07:05:29 +08:00
cnc
7ba3a22441 验证 Fanuc 子程序禁用
提示词:快速推进,非纯 source-map-only,保留源依据、自动测试、本地提交且不自动推送。

结论:基于 LinuxCNC rs274ngc_pre.cc 读取 DISABLE_FANUC_STYLE_SUB 以及 interp_read.cc 对 M98、Fanuc-style M99、bare O-word 的禁用分支,新增 INI/G-code fixture 并接入 native/source-link 错误断言;./test-native.sh 和 ./test-linuxcnc-source-link.sh 通过。
2026-06-06 06:48:17 +08:00
cnc
5aa328b413 验证 G92 禁用持久化
提示词:快速推进,非纯 source-map-only,保留源依据、自动测试、本地提交且不自动推送。

结论:基于 LinuxCNC rs274ngc_pre.cc 的 DISABLE_G92_PERSISTENCE 初始化清理和 interp_convert.cc convert_stop 的 M30 清理逻辑,新增 INI/G-code fixture 并接入 native/source-link 参数持久化断言;./test-native.sh 和 ./test-linuxcnc-source-link.sh 通过。
2026-06-06 06:38:42 +08:00
cnc
586c28656d 验证 OPFS rename 持久清理
提示词:快速推进,避免纯 source-map-only,不自动推送。

结论:基于 LinuxCNC rs274ngc_pre.cc save_parameters() 的 filename + .new rename 语义,补强浏览器 OPFS moveFile 在新 WASM 实例中持久删除源文件并恢复目标 mirror 的覆盖;./test-native.sh 和 ./test-linuxcnc-source-link.sh 通过。
2026-06-06 06:31:30 +08:00
cnc
6efb77b8e4 验证 tooldata reset 保留索引边界 2026-06-06 06:26:46 +08:00
cnc
ebb29f8cb6 补齐 G5.3 非 G5.2 运动错误覆盖 2026-06-06 06:15:12 +08:00
cnc
8544872198 按源保护 OPFS 参数备份失败边界 2026-06-06 06:06:03 +08:00
cnc
e320d6d420 按源保护 PythonPlugin INI 节解析边界 2026-06-06 05:56:10 +08:00
cnc
ba2d680500 按源保护 PythonPlugin inittab 初始化边界 2026-06-06 05:49:45 +08:00
cnc
9af3443d95 按源保护 PythonPlugin 路径列表边界 2026-06-06 05:34:33 +08:00
cnc
4adcd82b74 按源保护 PythonPlugin TOPLEVEL 路径边界 2026-06-06 05:26:33 +08:00
cnc
66ea34a76d 验证 Boost.Python shim 惰性边界 2026-06-06 05:20:23 +08:00
cnc
5bc568eaa9 加强 PythonPlugin 负状态探针 2026-06-06 05:12:39 +08:00
cnc
23a4679c28 验证 tooldata NML 非空表注册路径 2026-06-06 05:08:00 +08:00
cnc
3c05202097 补齐刀补下偏置平面错误覆盖
结论:基于 LinuxCNC interp_convert.cc convert_axis_offsets()、convert_straight() 和 convert_set_plane(),native/source-link 现在覆盖刀补打开时 G92、G53、G18 被拒绝的失败分支。
2026-06-05 23:52:34 +08:00
cnc
ba0d973f4f 补齐刀补下模式切换错误覆盖
结论:基于 LinuxCNC interp_convert.cc convert_control_mode()、convert_coordinate_system() 和 convert_home(),native/source-link 现在覆盖刀补打开时 G64、G55、G28 被拒绝的失败分支。
2026-06-05 23:46:15 +08:00
cnc
f69e10bedf 补齐刀补下工具偏置错误覆盖
结论:基于 LinuxCNC interp_convert.cc convert_tool_length_offset(),native/source-link 现在覆盖 cutter radius comp 打开时 G43 工具长度偏置被拒绝的失败分支。
2026-06-05 23:41:09 +08:00
cnc
a8f6a9d907 补齐 probe 刀补错误覆盖
结论:基于 LinuxCNC interp_convert.cc convert_probe(),native/source-link 现在覆盖 cutter radius comp 打开时 G38 probe 被拒绝的失败分支。
2026-06-05 23:36:27 +08:00
cnc
0850954ca8 补齐 probe 转换错误覆盖
结论:基于 LinuxCNC interp_convert.cc convert_probe(),native/source-link 现在覆盖零进给、G95 feed-per-rev 和起点等于终点三类 G38 probe 失败分支。
2026-06-05 23:32:43 +08:00
cnc
6bd1c6a429 接入 WASM blocker 自检
结论:native/source-link 必跑入口已执行 blocker 分析器负路径自检,防止坏 manifest、缺失源根、重复条目和缺失替换映射退化。
2026-06-05 23:26:02 +08:00
cnc
e928b32e69 补齐 OPFS 镜像冲突覆盖 2026-06-05 23:20:06 +08:00
cnc
36763c6bcd 补齐 cutter-comp override M码覆盖 2026-06-05 23:15:13 +08:00
cnc
03080afe24 补齐 cutter-comp 剩余M码覆盖 2026-06-05 23:10:46 +08:00
cnc
120a294f62 补齐 cutter-comp M码 source-linked 覆盖 2026-06-05 23:06:13 +08:00
cnc
a3daa829fc 补齐 switchkins 输出路径检查 2026-06-05 22:46:15 +08:00
cnc
71c61fca84 补齐 tooldata 非随机同步 probe 2026-06-05 22:40:34 +08:00
cnc
ff2980569c 锁定 ABORT 热注释 source-linked 错误 2026-06-05 22:34:46 +08:00
cnc
695186ddfc 补齐 LinuxCNC 注释 source-linked 覆盖 2026-06-05 22:31:09 +08:00
cnc
8394042d18 补齐 OPFS 参数跨实例恢复覆盖 2026-06-05 22:20:10 +08:00
cnc
7e18c6cd40 补齐默认 M3M4 单主轴 source-linked 覆盖 2026-06-05 22:07:39 +08:00
cnc
09e2259669 补齐默认 M5 全主轴 source-linked 覆盖 2026-06-05 22:03:14 +08:00
cnc
867bf09c5d 补齐 M51 全主轴 source-linked 覆盖 2026-06-05 21:59:26 +08:00
cnc
d35766e387 补齐多主轴 G76 source-linked 覆盖 2026-06-05 21:55:31 +08:00
cnc
9c73577ade 补齐多主轴 M19 source-linked 覆盖 2026-06-05 21:50:59 +08:00
cnc
55149b0a0a 覆盖多主轴全选 spindle source-linked 路径 2026-06-05 21:47:28 +08:00
cnc
6a886be52d 保留多主轴 spindle canon selector 2026-06-05 21:43:22 +08:00
cnc
acbf5b4686 多主轴 G95/G96/G97 source-linked 覆盖 2026-06-05 21:34:43 +08:00
cnc
77c6674905 提示词:补齐 G33 多主轴源链接覆盖
结论:新增 [TRAJ] SPINDLES=2 的 source-linked fixture,验证 LinuxCNC interp_convert.cc 中 G33/G33.1 带  会把 active_spindle 传入 START_SPEED_FEED_SYNCH;INI 来源为 rs274ngc_pre.cc init 读取 INI_FILE_NAME。未扩展 smoke 语义。检查通过:./test-linuxcnc-source-link.sh;./test-native.sh。
2026-06-05 21:18:27 +08:00
cnc
eb91ced792 提示词:补齐 M51 多主轴源链接覆盖
结论:新增 [TRAJ] SPINDLES=2 的 source-linked fixture,验证 LinuxCNC interp_convert.cc 中 M51 带  只切换选定主轴的 speed override;INI 来源为 rs274ngc_pre.cc init 读取 INI_FILE_NAME。未扩展 smoke 语义。检查通过:./test-linuxcnc-source-link.sh;./test-native.sh。
2026-06-05 21:09:07 +08:00
cnc
1b60bc2b2e 提示词:验证 OPFS 程序跨独立挂载恢复
结论:新增浏览器 OPFS section,使用同一 workspace 但不同 WASM mount point 的新 simulator 解析已持久化程序,证明程序来自 OPFS 而非旧 WASM FS mirror。LinuxCNC 来源依据为 rs274ngc_pre.cc open()/read_text() 文件读取路径;未扩展 smoke 语义。检查通过:./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-05 20:59:43 +08:00
cnc
37879f2a67 提示词:锁定 naivecam tolerance Canon 事件 2026-06-05 16:04:23 +08:00
cnc
a7cd889b3a 提示词:补齐 G64 naivecam tolerance 源链接覆盖 2026-06-05 15:59:51 +08:00
cnc
4887b2f64e 提示词:补齐 PythonPlugin C API WASM shim 符号
结论:按 LinuxCNC src/emc/pythonplugin/python_plugin.cc 的初始化、导入、配置和 callable 调用路径,把 PyConfig/PyStatus、PyImport_*、PyDict_SetItemString、Py_InitializeFromConfig、PyRun_SimpleString、Py_GetVersion、PyObject_Call 等符号纳入 inert WASM Python C API shim;真实 Python 执行仍保持禁用。

检查:./test-linuxcnc-wasm-python-c-api-symbols.sh 通过;./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-05 15:51:51 +08:00
cnc
cb315ce1cd 提示词:验证 OPFS 参数备份跨实例恢复
结论:按 LinuxCNC src/emc/rs274ngc/rs274ngc_pre.cc save_parameters() 的 .bak 保存语义,强化浏览器 smoke,在新 WASM 实例中读取 OPFS 主参数文件和备份文件,证明备份不是当前实例镜像残留。

检查:./check-linuxcnc-opfs-source-map.sh 通过;./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-05 15:37:03 +08:00
cnc
9a4e0e3712 提示词:补齐 rtapi_vsnprintf WASM shim
结论:按 LinuxCNC src/rtapi/uspace_common.h 和 src/rtapi/rtapi.h,将 userspace rtapi_vsnprintf 适配为 vsnprintf 包装,并在 WASM runtime probe 覆盖 snprintf、va_list 和截断返回值。

检查:./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-05 15:29:41 +08:00
cnc
98755528e4 使用刚更新的 AGENTS.md 和 align-linuxcnc 加速规则执行
结论:按 align-linuxcnc 先确认干净基线,避免 source-map-only 和 smoke 扩展;增强 WASM runtime link probe,覆盖 LinuxCNC src/emc/rs274ngc/gcodemodule.cc 与 src/emc/task/taskclass.cc 的 _task=0 preview 边界和 builtin_modules interpreter/emccanon 表结构。\n\n测试结论:./test-linuxcnc-wasm-runtime-link.sh、./test-native.sh、./test-linuxcnc-source-link.sh 均通过。
2026-06-05 15:21:06 +08:00
cnc
504b9b6d23 使用刚更新的 AGENTS.md 和 align-linuxcnc 加速规则执行
结论:按 align-linuxcnc 先确认干净基线,避免 source-map-only 和 smoke 扩展;增强 WASM Python C API symbol probe,覆盖 LinuxCNC src/emc/rs274ngc/gcodemodule.cc、interp_python.cc、interp_namedparams.cc 等 RS274 Python/remap 源已使用的 inert API 返回边界,包括 PyObject_Str、Py*_Check、PySequence_*、PyObject_*、PyArg_ParseTuple 和 Py_BuildValue。\n\n测试结论:./test-linuxcnc-wasm-python-c-api-symbols.sh、./test-native.sh、./test-linuxcnc-source-link.sh 均通过。
2026-06-05 15:15:42 +08:00
cnc
284afa1c64 使用刚更新的 AGENTS.md 和 align-linuxcnc 加速规则执行
结论:按 align-linuxcnc 先确认干净基线,避免 source-map-only 和 smoke 扩展;增强 WASM PythonPlugin link probe,锁定 LinuxCNC src/emc/pythonplugin/python_plugin.cc / python_plugin.hh 暴露的 last_errmsg()/last_exception() 状态面,在 Python 禁用边界下仍保持插件 API 诊断一致。\n\n测试结论:./test-linuxcnc-wasm-python-plugin-link.sh、./test-native.sh、./test-linuxcnc-source-link.sh 均通过。
2026-06-05 15:11:32 +08:00
cnc
428fc46f4e 使用刚更新的 AGENTS.md 和 align-linuxcnc 加速规则执行
结论:按 align-linuxcnc 先确认干净基线,避免 source-map-only 和 smoke 扩展;增强 WASM interp_base link probe,锁定 LinuxCNC src/emc/rs274ngc/interp_base.cc 的绝对/相对 interpreter 路径失败诊断均到达 wasm-safe dlfcn unavailable 边界。\n\n测试结论:./test-linuxcnc-wasm-interp-base-link.sh、./test-native.sh、./test-linuxcnc-source-link.sh 均通过。
2026-06-05 15:07:26 +08:00
cnc
d5c6b7555a 使用刚更新的 AGENTS.md 和 align-linuxcnc 加速规则执行
结论:按 align-linuxcnc 先确认干净基线,避免 source-map-only 和 smoke 扩展;新增 WASM tooldata 运行探针对 LinuxCNC src/emc/tooldata/tooldata_mmap.cc 随机换刀 T0 查找分支的覆盖,确认随机换刀扫描会越过 pocket 0 并返回非零 T0 pocket。\n\n测试结论:./test-linuxcnc-wasm-tooldata-link.sh、./test-linuxcnc-wasm-tooldata-runtime-symbols.sh、./test-native.sh、./test-linuxcnc-source-link.sh 均通过。
2026-06-05 15:03:41 +08:00
cnc
b773946c45 执行 AGENTS align-linuxcnc 加速规则
结论:新增 LinuxCNC source-backed G95 zero-spindle-speed 错误覆盖,源码依据为 src/emc/rs274ngc/interp_convert.cc convert_straight()。

测试:./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-05 11:15:05 +08:00
cnc
7a2a80ea71 执行 AGENTS align-linuxcnc 加速规则
结论:新增 LinuxCNC source-backed arc inverse-time missing-F 错误覆盖,源码依据为 src/emc/rs274ngc/interp_convert.cc convert_arc()。

测试:./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-05 11:11:54 +08:00
cnc
41db47f440 执行 AGENTS align-linuxcnc 加速规则
结论:新增 LinuxCNC source-backed G1 inverse-time missing-F 错误覆盖,源码依据为 src/emc/rs274ngc/interp_convert.cc convert_straight()。

测试:./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-05 11:08:11 +08:00
cnc
27ac0bfb25 执行 AGENTS align-linuxcnc 加速规则
结论:新增 LinuxCNC source-backed G1 zero-feed 错误覆盖,源码依据为 src/emc/rs274ngc/interp_convert.cc convert_straight()。

测试:./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-05 11:04:55 +08:00
cnc
cd71eff8a5 执行 AGENTS align-linuxcnc 加速规则
结论:新增 LinuxCNC source-backed canned cycle cutter compensation 错误覆盖,源码依据为 src/emc/rs274ngc/interp_cycles.cc convert_cycle()。

测试:./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-05 11:01:07 +08:00
cnc
eb1d31bfee 执行 AGENTS align-linuxcnc 加速规则
结论:新增 LinuxCNC source-backed canned cycle inverse-time feed 错误覆盖,源码依据为 src/emc/rs274ngc/interp_cycles.cc convert_cycle()。

测试:./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-05 10:57:38 +08:00
cnc
2892f2ca9d 执行 AGENTS align-linuxcnc 加速规则
结论:补齐 WASM NML status shim 并增加可执行 runtime 探针,依据 LinuxCNC src/libnml/nml/nmlmsg.cc 和 stat_msg.cc 中 RCS_GENERIC_STATUS/RCS_STAT_MSG 构造初始化。验证通过:./test-linuxcnc-wasm-runtime-link.sh、./test-native.sh、./test-linuxcnc-source-link.sh。
2026-06-05 10:44:31 +08:00
cnc
a301965452 执行 AGENTS align-linuxcnc 加速规则
结论:补强 WASM runtime 可执行探针,依据 LinuxCNC src/emc/rs274ngc/rs274ngc_pre.cc 中程序/子程序路径解析对 wordexp() 的使用,覆盖  形式的环境变量展开。验证通过:./test-linuxcnc-wasm-runtime-link.sh、./test-native.sh、./test-linuxcnc-source-link.sh。
2026-06-05 10:31:33 +08:00
cnc
0737d5ea65 执行 AGENTS align-linuxcnc 加速规则
结论:补强 WASM Python C API 可执行探针,依据 LinuxCNC src/emc/rs274ngc/gcodemodule.cc、interpmodule.cc 和 py*.cc 对 PyObject_New、Py_TYPE、Py_None、PyList_Type 等 Python C API 的绑定用法。验证通过:./test-linuxcnc-wasm-python-c-api-symbols.sh、./test-native.sh、./test-linuxcnc-source-link.sh。
2026-06-05 10:23:49 +08:00
cnc
3a478ca335 执行 AGENTS align-linuxcnc 加速规则
结论:补强 WASM interp_base 可执行探针,依据 LinuxCNC src/emc/rs274ngc/interp_base.cc 中绝对解释器路径直接 dlopen、相对路径经 EMC2_HOME/lib/linuxcnc 解析后 dlopen 的分支。验证通过:./test-linuxcnc-wasm-interp-base-link.sh、./test-native.sh、./test-linuxcnc-source-link.sh。
2026-06-05 10:13:27 +08:00
cnc
a144576e90 执行 AGENTS align-linuxcnc 加速规则
结论:补强 WASM PythonPlugin 可执行探针,依据 LinuxCNC src/emc/pythonplugin/python_plugin.cc 中 configure() 对 INI_FILE_NAME 缺失和环境变量 INI 文件的分支。验证通过:./test-linuxcnc-wasm-python-plugin-link.sh、./test-native.sh、./test-linuxcnc-source-link.sh。
2026-06-05 10:06:39 +08:00
cnc
60745adeef 执行 AGENTS align-linuxcnc 加速规则
结论:补强 WASM tooldata 可执行探针,依据 LinuxCNC src/emc/tooldata/tooldata_mmap.cc 非随机刀库 T0 查找优先返回 pocket 0 的源行为。验证通过:./test-linuxcnc-wasm-tooldata-link.sh、./test-native.sh、./test-linuxcnc-source-link.sh。
2026-06-05 09:59:06 +08:00
cnc
6506d23dc2 继续按提示词推进:补tooldata mmap单创建者覆盖 2026-06-05 09:51:37 +08:00
cnc
440a61be32 尽快推进:收紧Python C API shim源码锚点 2026-06-05 09:38:13 +08:00
cnc
9b9e942dd6 尽快推进:收紧tooldata mmap shim源码锚点 2026-06-05 09:33:08 +08:00
cnc
3c72356c92 尽快推进:收紧runtime shim源码锚点 2026-06-05 09:28:22 +08:00
cnc
a89c107676 尽快推进:收紧EMC状态shim源码锚点 2026-06-05 09:19:23 +08:00
cnc
035b25862a 尽快推进:收紧dlfcn shim源码锚点 2026-06-05 09:15:41 +08:00
cnc
167703b87a 继续按提示词推进:收紧NML状态shim源码锚点 2026-06-05 09:11:30 +08:00
cnc
b600d6a516 尽快推进:收紧gettext shim源码锚点 2026-06-05 09:03:41 +08:00
cnc
0a2d37fae8 尽快推进:收紧RTAPI格式化shim源码锚点 2026-06-05 08:58:58 +08:00
cnc
111cb5f9b9 尽快推进:收紧RCS打印shim源码锚点 2026-06-05 08:54:01 +08:00
cnc
218ac523b8 尽快推进:收紧OPFS程序读取源码锚点 2026-06-05 08:48:46 +08:00
cnc
abaebe7940 尽快推进:收紧tooldata工具源码锚点 2026-06-05 08:43:29 +08:00
cnc
77e92b0116 尽快推进:收紧WASM blocker参数文件源码锚点 2026-06-05 08:38:29 +08:00
cnc
cf3154b043 尽快推进:同步WASM smoke OPFS边界 2026-06-05 08:30:47 +08:00
cnc
9da6ffcd6a 尽快推进:收紧浏览器后端边界 2026-06-05 08:25:48 +08:00
cnc
c71d2964ee 尽快推进:收紧OPFS smoke边界 2026-06-05 08:20:31 +08:00
cnc
c1a130f5fb 尽快推进:收紧Python shim禁执行边界 2026-06-05 08:15:17 +08:00
cnc
baf8429736 尽快推进:收紧tooldata外部DB边界 2026-06-05 08:09:06 +08:00
cnc
2e2fb34cc3 尽快推进:收紧HAL运行时shim边界 2026-06-05 08:02:20 +08:00
cnc
810bd92ddc 尽快推进:锁定dlopen shim映射 2026-06-05 07:56:33 +08:00
cnc
6d99ca67a9 尽快推进:收紧PythonPlugin禁用边界
结论:对齐LinuxCNC src/emc/pythonplugin/python_plugin.cc initialize() 负状态语义,WASM安全shim在Python不可用时返回PLUGIN_PYTHON_NOT_INITIALIZED并保持负状态短路;未实现Python/remap执行,未扩展smoke parser。
2026-06-05 07:50:13 +08:00
cnc
6a2f63c894 尽快推进:加强OPFS参数备份目录边界验证
结论:真实浏览器app smoke继续使用LinuxCNC rs274ngc_pre.cc save_parameters() 语义,验证OPFS下备份link失败非致命时主参数仍持久化且目录不被误删;未扩展smoke parser功能。
2026-06-05 07:39:12 +08:00
cnc
bc80d74d1f 参考所有分组,完成尽量多的内容:收紧build source-map边界守卫 2026-06-05 07:29:30 +08:00
cnc
71676ac1be 参考所有分组,完成尽量多的内容:收紧WASM smoke source-map边界守卫 2026-06-05 07:24:49 +08:00
cnc
89af73298e 参考所有分组,完成尽量多的内容:收紧WASM blockers source-map边界守卫 2026-06-05 07:20:05 +08:00
cnc
dc5cdeacc2 参考所有分组,完成尽量多的内容:收紧WASM shims source-map边界守卫 2026-06-05 07:15:05 +08:00
cnc
b170ccd95a 参考所有分组,完成尽量多的内容:收紧tooldata source-map边界守卫 2026-06-05 07:10:30 +08:00
cnc
29197b25cc 参考所有分组,完成尽量多的内容:收紧OPFS source-map边界守卫 2026-06-05 07:06:14 +08:00
cnc
5ce3c18103 参考所有分组,完成尽量多的内容:收紧browser-app source-map边界守卫 2026-06-05 07:01:37 +08:00
cnc
79077fa695 参考所有分组,完成尽量多的内容:收紧switchkins source-map边界守卫 2026-06-05 06:57:11 +08:00
cnc
f64db24060 参考所有分组,完成尽量多的内容:收紧kinematics source-map边界守卫 2026-06-05 06:52:42 +08:00
cnc
fb2b5c4920 参考所有分组,完成尽量多的内容:收紧RS274 source-map边界守卫 2026-06-05 06:48:36 +08:00
cnc
9c52e426f7 参考所有分组,完成尽量多的内容:闭环OPFS类型source-map守卫 2026-06-05 06:40:58 +08:00
cnc
d80405ccb3 参考所有分组,完成尽量多的内容:收紧 switchkins source-only API 守卫
结论:未扩展 smoke 功能行为,仅把 switchkins/remap source-map guard 的负向 API 检查从 TOOL_TABLE 扩展到 asset、metadata、tooldata 等 source-only 输入。

LinuxCNC 来源依据:configs/sim/**.ini 的 M428/M429/M430 REMAP、remap_subs/{428,429,430}remap.ngc 的 #<kinstype>、src/hal/utils/halcmd_commands.cc do_loadusr_cmd() 与 halcmd_completion.c 的 loadusr 选项模型。

检查通过:./check-linuxcnc-switchkins-remap-source-map.sh;./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt;./check-linuxcnc-kinematics-source-map.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-05 06:32:36 +08:00
cnc
a48c02f5cc 参考所有分组,完成尽量多的内容:收紧 tooldata WASM probe 顺序守卫
结论:未扩展 smoke 功能行为,仅让 tooldata source-map guard 校验 build-wasm.sh 中四个 tooldata link/symbol probe 都在复制浏览器 artifact 前运行。

LinuxCNC 来源依据:src/emc/tooldata/Submakefile、tooldata_common.cc、tooldata_mmap.cc、tooldata_db.cc、tooldata_nml.cc、tool_mmap_read.cc、tool_watch.cc 与 tooldata.hh 的 tooldata 构建/导出边界。

检查通过:./check-linuxcnc-tooldata-source-map.sh;./check-linuxcnc-build-source-map.sh;./check-linuxcnc-workstreams-source-map.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-05 06:25:33 +08:00
cnc
f56468b664 参考所有分组,完成尽量多的内容:闭环 workstreams source-map 索引
结论:未扩展 smoke 功能行为,仅让 architecture、source-policy 与 workstreams source-map 形成双向索引,并由 check-linuxcnc-workstreams-source-map.sh 守卫。

LinuxCNC 来源依据:configs/sim/axis/vismach/5axis/bridgemill/5axis.ini、remap_subs/428remap.ngc、src/emc/kinematics/Submakefile、kinematics.h、src/emc/rs274ngc/interp_internal.hh、rs274ngc_pre.cc、rs274ngc/Submakefile、tooldata/Submakefile、ini/Submakefile、pythonplugin/Submakefile 的六分组边界。

检查通过:./check-linuxcnc-workstreams-source-map.sh;./check-linuxcnc-build-source-map.sh;./test-native.sh;./test-linuxcnc-source-link.sh;CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh。
2026-06-05 06:19:46 +08:00
cnc
ba2d576f8b 参考所有分组,完成尽量多的内容:收紧构建入口 source-map 守卫
结论:未扩展 smoke 功能行为,仅把 build-policy guard 扩展为同时校验 test-native.sh、test-linuxcnc-source-link.sh、build-wasm.sh 运行同一组 source-map guards。

LinuxCNC 来源依据:src/emc/rs274ngc/Submakefile、src/emc/tooldata/Submakefile、src/emc/ini/Submakefile、src/emc/pythonplugin/Submakefile、src/emc/kinematics/Submakefile 与 kinematics.h 的构建/源码链接边界。

检查通过:./check-linuxcnc-build-source-map.sh;./check-linuxcnc-workstreams-source-map.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-05 06:12:59 +08:00
cnc
7f45abe93c 参考所有分组,完成尽量多的内容:同步 OPFS 与 smoke 映射
结论:未扩展 smoke 功能行为,仅收紧 OPFS workspace、directory、policy 浏览器烟测和 source-map 守卫。

LinuxCNC 来源依据:src/emc/rs274ngc/rs274ngc_pre.cc 的 restore_parameters/save_parameters/read_text 参数文件与文件读取语义,以及 interp_internal.hh 的 rs274ngc.var/.bak 常量边界。

检查通过:./check-linuxcnc-opfs-source-map.sh;./check-linuxcnc-wasm-smoke-source-map.sh;./check-linuxcnc-workstreams-source-map.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-05 06:07:14 +08:00
cnc
ae7de73a7c 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:补强 WASM runtime shim 的 LinuxCNC HAL source-map 与 probe,确认 HAL lookup 在浏览器安全 shim 中保持不可用边界,不扩展 smoke 行为。
2026-06-04 20:23:50 +08:00
cnc
7471444781 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定dlfcn禁用边界
结论:按 LinuxCNC src/emc/rs274ngc/interp_base.cc 的 dlopen/dlsym/dlerror 调用点,补强 WASM dlfcn shim 与 interp_base probe,确认浏览器安全探针中动态加载保持不可用;不扩展 smoke 解析。
2026-06-04 19:59:14 +08:00
cnc
f36e2a96c4 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定PythonPlugin负状态短路
结论:按 LinuxCNC src/emc/pythonplugin/python_plugin.cc 的 status < PLUGIN_OK 分支,补齐 WASM PythonPlugin shim 的 call/call_method 负状态短路探针;保持 Python 执行禁用,不扩展 smoke 解析。
2026-06-04 19:53:50 +08:00
cnc
aaa6dce99a 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定tooldata未激活DB语义
结论:按 LinuxCNC src/emc/tooldata/tooldata_db.cc 锁定 tooldata_db_getall() 未激活返回 -1、tooldata_db_notify() 未激活返回 0 的 WASM shim 探针与 source-map 守卫;不启用外部 DB 服务,不扩展 smoke 解析。
2026-06-04 19:48:47 +08:00
cnc
b89d6311ff 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定OPFS参数保存顺序
结论:按 LinuxCNC rs274ngc_pre.cc save_parameters() 的 .new、.bak、unlink/link/rename 顺序补强 OPFS/app/browser smoke 守卫,并验证临时参数文件不会残留在 OPFS。
2026-06-04 19:42:49 +08:00
cnc
a12c0166b5 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定OPFS覆盖条目 2026-06-04 19:22:34 +08:00
cnc
9fd23192dd 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定kinematics源码锚点 2026-06-04 19:15:54 +08:00
cnc
a46910b6c0 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定WASM阻塞替换集合 2026-06-04 19:07:21 +08:00
cnc
0b387db8ca 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定浏览器OPFS段名守卫 2026-06-04 18:57:28 +08:00
cnc
22c1c311ff 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定build-wasm源码映射前置
结论:build-wasm.sh 现在与 native/source-link 一样先运行六分组 source-map guards,再消费生成配置别名和浏览器 artifacts;同步 build/workstreams source map 与 aggregate 元检查。未新增 CNC 行为,未扩展 smoke fallback。\n\n测试:./check-linuxcnc-workstreams-source-map.sh;./check-linuxcnc-build-source-map.sh;git diff --check;CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-04 18:48:46 +08:00
cnc
3079656cfb 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:接入WASM边界源码映射
结论:将 WASM shims/blockers source-map guard 接入 native/source-link 主检查,并取消 workstreams guard 对这两个六分组 guard 的例外;仅加强源映射边界检查,未新增 CNC 行为,未扩展 smoke fallback。\n\n测试:./check-linuxcnc-workstreams-source-map.sh;./check-linuxcnc-wasm-shims-source-map.sh;./check-linuxcnc-wasm-blockers-source-map.sh;git diff --check;CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-04 18:39:06 +08:00
cnc
7502684780 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定六分组策略边界
结论:把六分组 workstreams 总索引与 docs/linuxcnc-source-policy.md 的单上下文推进、OPFS-only、smoke 仅验证等边界做机器检查绑定;未新增 CNC 行为,未扩展 smoke fallback。\n\n测试:./check-linuxcnc-workstreams-source-map.sh;git diff --check;CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-04 18:14:23 +08:00
cnc
340966b6f7 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:补齐 LinuxCNC tooldata 源映射 guard,并接入 native/source-link/workstreams 检查;仅验证源覆盖、WASM-safe shim/probe 边界和 TOOL_TABLE source-only 策略,未扩展 smoke 行为。\n\n测试:./check-linuxcnc-tooldata-source-map.sh;./check-linuxcnc-workstreams-source-map.sh;git diff --check;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-04 18:07:35 +08:00
cnc
1f314b7e9e 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定六分组源码映射总索引结论 2026-06-04 16:54:49 +08:00
cnc
1bfb7426d4 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定浏览器应用源码映射结论 2026-06-04 16:45:01 +08:00
cnc
abd96dfdde 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定构建源码映射结论 2026-06-04 16:37:24 +08:00
cnc
7b171c605b 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke:锁定WASM smoke源码映射结论 2026-06-04 16:26:51 +08:00
cnc
cb22ecd8ef 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:补齐 LinuxCNC WASM-safe shim source-map,锁定 linuxcnc_wasm_safe_probe_shims、RS274/WASM manifest、每个 shim 的 LinuxCNC 源依据与聚合 guardrails;未扩展 smoke 行为。验证通过:./check-linuxcnc-wasm-shims-source-map.sh linuxcnc-rs274-wasm-source-files.txt、./check-linuxcnc-wasm-blockers-source-map.sh linuxcnc-rs274-wasm-source-files.txt、./test-linuxcnc-wasm-cmake-safe-probe.sh、./test-native.sh、./test-linuxcnc-source-link.sh、./test-linuxcnc-source-syntax.sh、CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh。
2026-06-04 16:08:32 +08:00
cnc
a0f6228908 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:补齐 LinuxCNC WASM blocker source-map,锁定 RS274/WASM manifest、Python/Boost.Python、dlopen、tooldata mmap、native-fs 分类与 wasm-safe shim replacement;未扩展 smoke 行为。验证通过:./check-linuxcnc-wasm-blockers-source-map.sh linuxcnc-rs274-wasm-source-files.txt、./test-linuxcnc-wasm-blockers.sh linuxcnc-rs274-wasm-source-files.txt、./test-native.sh、./test-linuxcnc-source-link.sh、CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh、./test-linuxcnc-source-syntax.sh、./test-linuxcnc-wasm-cmake-safe-probe.sh。
2026-06-04 15:54:57 +08:00
cnc
8fe468ba3c 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:补齐 switchkins/remap 源映射守卫,接入 native/source-link/聚合检查;未扩展 smoke 行为。验证通过:./check-linuxcnc-switchkins-remap-source-map.sh、./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt、./test-native.sh、./test-linuxcnc-source-link.sh、CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh。
2026-06-04 15:36:19 +08:00
cnc
b3b1117ec9 参考所有分组,完成尽量多的内容:锁定OPFS源码映射结论
结论:新增 OPFS source-map 文档和同步守卫,绑定 interp_internal.hh 默认参数文件、rs274ngc_pre.cc restore/save 语义与浏览器 OPFS 边界;未扩展 smoke 功能。
2026-06-04 14:48:07 +08:00
cnc
4eb694164f 参考所有分组,完成尽量多的内容:锁定kinematics源码映射结论
结论:新增 kinematics source-map 文档和同步检查,固定 manifest 分组计数与 source-coverage 边界,并接入 native/all-native guardrail;未扩展 smoke 功能。
2026-06-04 14:33:52 +08:00
cnc
59b8e2a832 参考所有分组,完成尽量多的内容:锁定RS274源码映射结论
结论:补齐 WASM header、blocked-header、metadata 的 source map 文档闭环,并把 manifest/source-map 同步检查接入 native 必跑路径;未扩展 smoke 功能。
2026-06-04 14:20:29 +08:00
cnc
f9ac38251b 参考所有分组,完成尽量多的内容
结论:source-map纳入WASM-safe core和blocked源清单,并由聚合守护比对linuxcnc-rs274-wasm-source-files.txt,防止WASM源码链接文档与manifest漂移。
2026-06-04 06:11:41 +08:00
cnc
002c8a2d53 参考所有分组,完成尽量多的内容
结论:OPFS负向守护补充showDirectoryPicker,禁止浏览器目录选择绕过OPFS持久化路径;未扩展smoke功能行为。
2026-06-04 05:59:49 +08:00
cnc
decc008609 参考所有分组,完成尽量多的内容
结论:聚合守护锁住LinuxCNC loadusr源依据,要求switchkins/remap生成器按do_loadusr_cmd getopt(+wWin:)和补全表跳过等待名后再记录实际程序。
2026-06-04 05:48:54 +08:00
cnc
3c2df90872 按提示词完善loadusr解析
结论:对齐LinuxCNC halcmd loadusr getopt(+wWin:)和补全表,生成器能正确跳过-Wn/-n等待名并提取实际用户态程序。
2026-06-04 05:37:51 +08:00
cnc
81c7b061a8 参考所有分组,完成尽量多的内容:结论为 native 守住 OPFS 选项不透传 2026-06-04 05:27:46 +08:00
cnc
2223e6d601 参考所有分组,完成尽量多的内容:结论为 manifest 校验覆盖 LinuxCNC loadusr 等待选项 2026-06-04 05:16:19 +08:00
cnc
708b2bee17 参考所有分组,完成尽量多的内容:结论为 binding manifest 纳入 source-map 守护 2026-06-03 23:39:09 +08:00
cnc
0a270c73a0 参考所有分组,完成尽量多的内容:结论为 source-map 守住 RS274 core manifest 2026-06-03 23:20:46 +08:00
cnc
52d7a49861 参考所有分组,完成尽量多的内容:结论为 porting 文档守住 LinuxCNC fixture 覆盖 2026-06-03 23:07:37 +08:00
cnc
802e85cd63 参考所有分组,完成尽量多的内容:结论为动态守住 LinuxCNC fixture 双路径覆盖 2026-06-03 22:55:55 +08:00
cnc
0de612651e 参考所有分组,完成尽量多的内容:结论为 guardrail 锁住 native 注释百分号覆盖 2026-06-03 22:45:45 +08:00
cnc
6f21d22912 参考所有分组,完成尽量多的内容:结论为 native RS274 补齐注释百分号错误覆盖 2026-06-03 22:37:13 +08:00
cnc
f8902b3f8d 参考所有分组,完成尽量多的内容:结论为 build-wasm 显式关闭 smoke 后端 2026-06-03 22:24:57 +08:00
cnc
3d368b2564 参考所有分组,完成尽量多的内容:结论为 WASM source probe 禁止夹带 smoke 后端 2026-06-03 22:07:57 +08:00
cnc
8b89b5b4c6 参考所有分组,完成尽量多的内容:结论为 switchkins 表检查同步守住 kinematics manifest 完整性 2026-06-03 21:44:41 +08:00
cnc
21c242c9a4 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:按 LinuxCNC rs274ngc_pre.cc save_parameters()/restore_parameters() 的参数文件流程,收紧 OPFS 参数持久化后 WASM 根目录 scratch 文件清理;未扩展临时 smoke 行为。
2026-06-03 21:32:27 +08:00
cnc
945e839e9d 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:对齐 LinuxCNC tooldata_nml.cc 与 tooldata_db.cc 的失败诊断输出,收紧 WASM tooldata link probe;未扩展临时 smoke 行为。
2026-06-03 21:19:24 +08:00
cnc
feb04102e7 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:补齐 cached LinuxCNC input 与 switchkins/remap 表检查器的 filesystem-root cache-dir 拒绝守卫,不新增 CNC 行为或 smoke 功能。
2026-06-03 21:08:35 +08:00
cnc
aaa2573bba 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:补齐 check-linuxcnc-inputs-cached.sh 的 cache-dir 入口拒绝守卫,锁住缺失 cache-dir 值和空白预检缓存目录,不新增 CNC 行为或 smoke 功能。
2026-06-03 20:57:15 +08:00
cnc
d7b9006ba5 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:补齐 LinuxCNC source file/CXX flag helper 的入口拒绝守卫,锁住缺失参数、未知选项、逃逸 source path 和未知 WASM profile,不新增 CNC 行为或 smoke 功能。
2026-06-03 20:47:14 +08:00
cnc
51b415a701 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:补齐 Puma560 DH 参数生成器的可执行入口和 LinuxCNC-root 相对 HAL 源约束,锁住 puma560_dh.hal 中 genserkins DH 值到生成输出的映射,不新增 CNC 行为或 smoke 功能。
2026-06-03 20:36:09 +08:00
cnc
856b526acb 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:补齐 switchkins/remap 表 generator 的 CLI 拒绝守卫,锁住未知选项、缺失 all-output-dir、过多参数和空白输出目录,不新增 CNC 行为或 smoke 功能。
2026-06-03 20:03:43 +08:00
cnc
bc54b5f8f8 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:补齐 switchkins/remap 表 checker 与 cached checker 的 CLI 拒绝守卫,锁住未知选项、多余参数、缺失 cache-dir 和空白缓存目录,不新增 CNC 行为或 smoke 功能。
2026-06-03 19:55:53 +08:00
cnc
8151829643 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:补齐 kinematics manifest lister 对复合过滤器 core,header 的拒绝守卫,保持与通用 LinuxCNC manifest 组名校验一致。
2026-06-03 19:46:40 +08:00
cnc
5fb041f1a5 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:收敛第 5/6 组 Node/browser smoke wrapper 约束,all-native guardrail 验证 WASM_NODE_SMOKE_MIN_STEPS 和 BROWSER_SMOKE_MIN_SECTIONS 非法阈值会被 wrapper 直接拒绝,避免 smoke 覆盖门槛被误配置;未扩展 smoke 功能。验证:CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh、./test-native.sh、./test-linuxcnc-source-link.sh 均通过。
2026-06-03 19:37:23 +08:00
cnc
bba306ccfe 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:收敛第 6 组 WASM manifest/source-link 约束,WASM manifest source lister 明确拒绝 core,header 这类复合 filter,避免 source coverage 查询静默误用;未扩展 smoke 功能。验证:./test-linuxcnc-wasm-cmake-safe-probe.sh linuxcnc-rs274-wasm-source-files.txt、./test-native.sh、./test-linuxcnc-source-link.sh 均通过。
2026-06-03 19:28:43 +08:00
cnc
4a3d87045e 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:收敛第 3/5/6 组 OPFS/browser 约束,test-native guardrail 明确要求 wasm-core OPFS 路径拒绝反斜杠和 NUL 字符,防止 browser WASM filesystem 路径逃逸;未扩展 smoke 功能。验证:./test-native.sh、./test-linuxcnc-source-link.sh 均通过。
2026-06-03 19:24:13 +08:00
cnc
951dcc7980 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:收敛第 1 组 switchkins/remap 生成器约束,--all-output-dir 拒绝含空白路径和文件系统根目录,避免生成表/配置 case 误写到不受控位置;未扩展 smoke 功能。验证:./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt、CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh、./test-native.sh、./test-linuxcnc-source-link.sh 均通过。
2026-06-03 19:17:18 +08:00
cnc
aa6e79ac64 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:收敛第 2/6 组 manifest/source-link 约束,公共 manifest source lister 只接受单个 manifest group 名称或 all,拒绝 core,header 这类复合 filter,避免 source coverage 查询静默误用;未扩展 smoke 功能。验证:CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh、./test-native.sh、./test-linuxcnc-source-link.sh 均通过。
2026-06-03 19:11:22 +08:00
cnc
39f6f9133c 参考所有分组,完成尽量多的内容:收紧源列表过滤器 basename 守卫,结论测试通过 2026-06-03 18:54:36 +08:00
cnc
bc7b947d14 参考所有分组,完成尽量多的内容:收紧 CMake 源列表参数守卫,结论测试通过 2026-06-03 18:46:10 +08:00
cnc
1ce460d1d5 参考所有分组,完成尽量多的内容:收紧 CMake 源列表缺失守卫,结论测试通过 2026-06-03 18:36:59 +08:00
cnc
96b5973e1b 参考所有分组,完成尽量多的内容:收紧项目源列表路径守卫,结论测试通过 2026-06-03 18:27:29 +08:00
cnc
c8b834e36b 参考所有分组,完成尽量多的内容:补齐 switchkins 缓存签名输入
结论:switchkins/remap 表缓存签名现在包含 manifest 输入校验脚本,输入校验规则变化会使缓存失效;未扩展 smoke 功能。
2026-06-03 18:13:42 +08:00
cnc
32b4a96a9f 参考所有分组,完成尽量多的内容:收紧 switchkins 生成器 manifest 入口校验
结论:switchkins/remap 生成器现在先复用 LinuxCNC manifest 输入校验,坏行、未知组、重复源等会在生成 table/config/json 前被拦截;未扩展 smoke 功能。
2026-06-03 18:06:21 +08:00
cnc
ee0a3130bf 参考所有分组,完成尽量多的内容:收紧 switchkins 表 manifest 入口校验
结论:switchkins/remap 表 checker 现在先复用 LinuxCNC manifest 输入校验,坏行、未知组、重复源等会在表扫描前被拦截;未扩展 smoke 功能。
2026-06-03 17:49:42 +08:00
cnc
c031bc0a2c 参考所有分组,完成尽量多的内容:收紧 wasm blocker manifest 校验
结论:wasm blocker 分析器现在与 manifest 输入校验一致拒绝缺少 note 或字段数量异常的坏行,并在 blocker self-check 中覆盖该负向路径;未扩展 smoke 功能。
2026-06-03 17:43:51 +08:00
cnc
dcf246c0a0 参考所有分组,完成尽量多的内容:收紧 manifest 坏行校验
结论:通用 LinuxCNC manifest lister 现在与输入校验一致拒绝缺少 note 或字段数量异常的坏行,并在 all-native guardrail 中覆盖通用 lister 与 native/source-link 调用链;未扩展 smoke 功能。
2026-06-03 17:37:58 +08:00
cnc
1f4044efe2 参考所有分组:锁定 OPFS parseFileWithParameterFile 路径守卫,测试通过 2026-06-03 17:08:42 +08:00
cnc
3cc7590121 参考所有分组:锁定loadusr资产清单校验,测试通过 2026-06-03 17:00:15 +08:00
cnc
180c0f6dbe 收紧TRSRN资产清单校验 2026-06-03 16:44:33 +08:00
cnc
702f94cc52 参考 OPFS 参数桥接:锁定 LinuxCNC 临时参数目录守卫,测试通过 2026-06-03 16:42:05 +08:00
cnc
b366d220f5 参考所有分组:锁定浏览器入口和应用恢复守卫,测试通过 2026-06-03 16:28:11 +08:00
cnc
3090b9a79a 参考所有分组:锁定浏览器 switchkins 和应用 OPFS 守卫,测试通过 2026-06-03 16:19:04 +08:00
cnc
c851476007 参考所有分组:锁定 OPFS 参数失败清理守卫,测试通过 2026-06-03 16:09:22 +08:00
cnc
ddc20eeb98 参考所有分组:锁定 OPFS 目录和参数浏览器守卫,测试通过 2026-06-03 16:03:04 +08:00
cnc
1ef3f2c7df 参考所有分组:锁定 OPFS basic 浏览器守卫,测试通过 2026-06-03 15:56:39 +08:00
cnc
eef30e1831 参考所有分组:锁定 OPFS policy 负向守卫,测试通过 2026-06-03 15:49:36 +08:00
cnc
1433435121 参考所有分组:锁定 OPFS 和烟测覆盖守卫,测试通过 2026-06-03 15:43:23 +08:00
cnc
46807f05a5 继续按 align-linuxcnc 约束:补 OPFS 镜像跨实例浏览器验证,测试通过 2026-06-03 15:31:05 +08:00
cnc
77f9754833 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:补强 tracked switchkins/remap 输出的 TOOL_TABLE source-only 守护,防止仓库内已生成表绕过 LinuxCNC source policy;表校验、native、source-link 均通过。
2026-06-03 15:20:57 +08:00
cnc
457557dee8 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:按 LinuxCNC source policy 将 TOOL_TABLE 恢复为 source coverage only,撤回生成 API/browser switchkins cases,并增加 checker/native 守护;表校验、native、source-link 均通过。
2026-06-03 15:13:01 +08:00
cnc
aff6f85d8a 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:按 LinuxCNC INI TOOL_TABLE 来源扩展 switchkins/remap 配置别名和生成案例,未增加 smoke 功能行为;表校验、native、source-link 均通过。
2026-06-03 14:53:37 +08:00
cnc
4086445900 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:按 align-linuxcnc 约束补齐 switchkins TOOL_TABLE/tooldata 与 kinematics manifest lister 的源码政策说明,并用 all-native guardrail 锁定文档,避免规则和脚本脱节;不扩展 smoke 行为。

源依据:LinuxCNC M428/M429/M430 INI 的 [EMCIO] TOOL_TABLE 条目、linuxcnc-kinematics-source-files.txt 的 asset/tooldata/generated 分组,以及 check-linuxcnc-switchkins-remap-table-cached.sh 对引用源的签名约束。

验证:CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh 通过;./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-03 13:47:51 +08:00
cnc
18f14ca111 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:按 align-linuxcnc 约束增强 kinematics manifest lister 的 all-native 守卫,覆盖 asset/tooldata/generated 分组、full 输出以及未知 filter/output 负例;只收紧源清单约束,不扩展 smoke 行为。

源依据:linuxcnc-kinematics-source-files.txt 中由 ../linuxcnc/configs/sim、src/hal/user_comps、src/objects/hal/components 等 LinuxCNC 源/生成源组成的 kinematics、switchkins、TOOL_TABLE 覆盖。

验证:CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 ./test-all-native.sh 通过;./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-03 13:42:26 +08:00
cnc
7d28d0a025 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:按 align-linuxcnc 约束让 switchkins/remap 生成器读取 LinuxCNC INI [EMCIO] TOOL_TABLE,仅用于校验 linuxcnc-kinematics-source-files.txt 的 tooldata 源覆盖;不把 TOOL_TABLE 输出为新 API case,不扩展 smoke 行为。

源依据:../linuxcnc/configs/sim 下 M428/M429/M430 switchkins INI 的 [EMCIO] TOOL_TABLE 条目,以及现有 kinematics manifest 的 tooldata 条目。

验证:./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt 通过;./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-03 13:36:25 +08:00
cnc
0d4e248811 参考所有分组,完成尽量多的内容。禁止顺手扩功能 smoke
结论:按 align-linuxcnc 约束收窄 OPFS removeFile 和 LinuxCNC 参数 .new/.bak 清理为文件专用语义,避免把同名目录递归删除。依据 ../linuxcnc/src/emc/rs274ngc/rs274ngc_pre.cc save_parameters() 的 fopen(filename.new)、unlink(filename.bak)、link、rename 行为。

验证:./test-native.sh 通过;./test-linuxcnc-source-link.sh 通过。
2026-06-03 13:28:15 +08:00
cnc
5481ab3bff 参考所有分组:锁定kinematics源约束守护
结论:按 align-linuxcnc 约束把 generated component manpage metadata 和已知不可解析 TOOL_TABLE 基线纳入 native/all-native guardrail,防止 kinematics manifest/source 校验被削弱。

验证:check-linuxcnc-inputs --require-kinematics-complete、check-linuxcnc-switchkins-remap-table、CNC_SIM_ALL_NATIVE_GUARDRAILS_ONLY=1 test-all-native、test-native、test-linuxcnc-source-link 均通过。
2026-06-03 11:36:09 +08:00
cnc
8ff22065dd 参考所有分组:补生成组件manpage源约束
结论:按 align-linuxcnc 约束补齐 kinematics manifest 的 halcompile generated manpage metadata,并让 M428/M429/M430 INI TOOL_TABLE 的不可解析 LinuxCNC 源现状成为显式基线。

验证:check-linuxcnc-inputs --require-kinematics-complete、check-linuxcnc-switchkins-remap-table、test-native、test-linuxcnc-source-link 均通过。
2026-06-03 11:29:47 +08:00
cnc
5c2d5cfa05 参考所有分组:保持OPFS备份失败非致命
结论:按 LinuxCNC rs274ngc_pre.cc save_parameters() 中 unlink/link 失败不阻止 rename 主参数文件的语义,OPFS 参数 .bak 写入失败时不让解析保存失败,并避免把目录当作可 unlink 的备份文件删除;未扩展 smoke 解析行为。

验证:./test-native.sh;./test-linuxcnc-source-link.sh;./test-web-wasm-browser-smoke.sh。
2026-06-03 11:22:08 +08:00
cnc
f98a715ed7 参考所有分组:替换陈旧OPFS参数备份
结论:按 LinuxCNC rs274ngc_pre.cc save_parameters() 先 unlink 旧 .bak 再 link 当前参数文件的语义,浏览器 OPFS 成功保存时总是用解析前主参数文件刷新 .bak;未扩展 smoke 解析行为。

验证:./test-native.sh;./test-linuxcnc-source-link.sh;./test-web-wasm-browser-smoke.sh。
2026-06-03 11:11:40 +08:00
cnc
1928ff3b6a 参考所有分组:补充OPFS失败解析备份断言
结论:按 LinuxCNC rs274ngc_pre.cc 仅成功解释后执行 save_parameters() 的语义,验证浏览器 OPFS 参数 .bak 不会在失败解析后被替换;未扩展 smoke 解析行为。

验证:./test-native.sh;./test-linuxcnc-source-link.sh;./test-web-wasm-browser-smoke.sh。
2026-06-03 11:04:35 +08:00
cnc
6cf7c73392 参考所有分组:完善OPFS参数备份桥接
结论:按 LinuxCNC rs274ngc_pre.cc save_parameters()/restore_parameters() 语义,补齐浏览器 OPFS 参数文件 .bak 持久化;未扩展 smoke 解析行为。

验证:./test-native.sh;./test-linuxcnc-source-link.sh;./test-web-wasm-node-smoke.sh;./test-web-wasm-browser-smoke.sh。
2026-06-03 10:58:05 +08:00
cnc
028825cd9c 参考所有分组:补 source 覆盖文档登记,测试通过 2026-06-03 10:21:39 +08:00
cnc
e778a37fdb 参考所有分组:补 native numeric O-word source 覆盖,测试通过 2026-06-03 10:17:10 +08:00
cnc
6cc07c2950 参考所有分组:补 native read path source 覆盖,测试通过 2026-06-03 10:10:45 +08:00
cnc
c5a1d655b0 参考所有分组:补 program end source 覆盖,测试通过 2026-06-03 10:04:46 +08:00
cnc
0895c04ab1 参考所有分组:补 G4 dwell source 边界覆盖,测试通过 2026-06-03 09:58:44 +08:00
cnc
c932e10801 参考所有分组:补 modal group source 覆盖,测试通过 2026-06-03 09:41:26 +08:00
cnc
d1e0152eb8 参考所有分组:补 G10 tool source 覆盖,测试通过 2026-06-03 09:36:47 +08:00
cnc
a134dcef7d 参考所有分组:补 G10 source 覆盖,测试通过 2026-06-03 09:32:16 +08:00
cnc
bbd5739e7a 参考所有分组:补 polar 和 NURBS source 覆盖,测试通过 2026-06-03 09:27:04 +08:00
cnc
611e6417b4 参考所有分组:补 NURBS source 覆盖,测试通过 2026-06-03 09:22:26 +08:00
cnc
fa4296d730 参考所有分组:补 percent 错误 source 覆盖,测试通过 2026-06-03 09:12:17 +08:00
cnc
2dd9847a7b 参考所有分组:补 comment 错误 source 覆盖,测试通过 2026-06-03 09:04:26 +08:00
cnc
9bf9206869 参考所有分组:补 percent 文件 source 覆盖,测试通过 2026-06-03 08:56:30 +08:00
cnc
090a5fb89c 参考所有分组:补 close_and_downcase word source 覆盖,测试通过 2026-06-03 08:49:10 +08:00
cnc
2f7a46758b 参考所有分组:补 extended N 行号 source 覆盖,测试通过 2026-06-03 08:41:44 +08:00
cnc
1b16d3e0b5 参考所有分组:补 fractional N 行号 source 覆盖,测试通过 2026-06-03 08:33:33 +08:00
cnc
d0a7a9fcec 参考所有分组:补 G73 G85 G86 G89 source 覆盖,测试通过 2026-06-03 06:30:24 +08:00
cnc
dbf27a4e6b 参考所有分组:补 native 参数持久化和 threading 覆盖,测试通过 2026-06-03 06:21:35 +08:00
cnc
e71b9146e1 参考所有分组:补 G76 和 NURBS native 覆盖,测试通过 2026-06-03 06:09:20 +08:00
cnc
59287b9920 参考所有分组:补只读命名参数 native 覆盖,测试通过 2026-06-03 06:00:29 +08:00
cnc
33e1ee5a06 参考所有分组:补执行错误 native 覆盖,测试通过 2026-06-03 00:55:59 +08:00
cnc
b26620dc32 参考所有分组:补 read_text native 覆盖,测试通过 2026-06-03 00:48:18 +08:00
cnc
74cb5f826c 参考所有分组:补读取和圆弧 native 覆盖,测试通过 2026-06-03 00:35:07 +08:00
cnc
2ba8cd7fb8 参考所有分组:补编号参数 native 覆盖,测试通过 2026-06-03 00:26:07 +08:00
cnc
e3d47e8267 参考所有分组:补 O-word 状态参数 native 覆盖,测试通过 2026-06-03 00:19:16 +08:00
cnc
3df61e8da5 参考所有分组:补齐 O-word native 覆盖,测试通过 2026-06-03 00:12:28 +08:00
cnc
28aa1956b7 继续按 align-linuxcnc 约束:补块读取 native 覆盖,测试通过
结论:补齐 LinuxCNC interp_read.cc read_items()/read_one_item() 坏字符路径,以及 interp_check.cc check_m_codes() M 码数量错误的 native 校验;./test-native.sh 与 ./test-linuxcnc-source-link.sh 均通过,未扩展 smoke。
2026-06-03 00:01:25 +08:00
cnc
4e3df07b8a 继续按 align-linuxcnc 约束:补参数表达式 native 覆盖,测试通过
结论:补齐 LinuxCNC interp_read.cc 参数读写、ATAN/unary/binary 表达式错误路径,以及 interp_execute.cc sqrt/divide/power 执行错误的 native 校验;./test-native.sh 与 ./test-linuxcnc-source-link.sh 均通过,未扩展 smoke。
2026-06-02 23:56:34 +08:00
cnc
b7610d1b96 继续按 align-linuxcnc 约束:补极坐标 native 覆盖,测试通过
结论:补齐 LinuxCNC interp_read.cc、interp_internal.cc、interp_find.cc 极坐标路径,以及 interp_convert.cc 扩展平面 arc/NURBS 错误 fixture 的 native 校验;./test-native.sh 与 ./test-linuxcnc-source-link.sh 均通过,未扩展 smoke。
2026-06-02 23:51:22 +08:00
cnc
55c75ffe80 继续按 align-linuxcnc 约束:补固定循环 native 覆盖,测试通过
结论:补齐 LinuxCNC interp_cycles.cc 固定循环错误 fixture 的 native 校验,覆盖重复次数、平面轴/R、Q/P、主轴状态和 G87 必需字;./test-native.sh 与 ./test-linuxcnc-source-link.sh 均通过,未扩展 smoke。
2026-06-02 23:47:02 +08:00
cnc
ad3720713e 继续按 align-linuxcnc 约束:补 M 码和刀具 native 覆盖,测试通过
结论:补齐 LinuxCNC interp_convert.cc convert_m()、convert_tool_select()、convert_tool_length_offset() 以及 interp_find.cc find_tool_index() 相关错误 fixture 的 native 校验;./test-native.sh 与 ./test-linuxcnc-source-link.sh 均通过,未扩展 smoke。
2026-06-02 23:42:05 +08:00
cnc
ba526a6042 继续按 align-linuxcnc 约束:补轴运动 native 覆盖,测试通过
结论:补齐 LinuxCNC interp_internal.cc enhance_block() 轴/运动错误 fixture 的 native 校验,并同步 check_other_codes() native 覆盖文档;./test-native.sh 与 ./test-linuxcnc-source-link.sh 均通过。
2026-06-02 23:34:56 +08:00
cnc
93f2a903b5 继续按 align-linuxcnc 约束:补未使用词 native 覆盖,测试通过 2026-06-02 23:25:47 +08:00
cnc
c52ecc09eb 继续按 align-linuxcnc 约束:补词读取 native 覆盖,测试通过 2026-06-02 23:20:55 +08:00
cnc
cc05532528 继续按 align-linuxcnc 约束:补 GM 读码 native 覆盖,测试通过 2026-06-02 23:16:15 +08:00
cnc
ecec27aa64 继续按 align-linuxcnc 约束:补 G53 校验 native 覆盖,测试通过 2026-06-02 23:11:47 +08:00
cnc
c25b0eff22 继续按 align-linuxcnc 约束:补 M98 与增量圆弧源码覆盖,测试通过 2026-06-02 23:07:27 +08:00
cnc
777d29651c 继续按 align-linuxcnc 约束:补 G 代码校验错误源码覆盖,测试通过 2026-06-02 22:34:52 +08:00
cnc
138fe862ae 继续按 align-linuxcnc 约束:补工具号查找错误源码覆盖,测试通过 2026-06-02 22:29:47 +08:00
cnc
d985b13a8e 继续按 align-linuxcnc 约束:补工具长度错误源码覆盖,测试通过 2026-06-02 22:25:05 +08:00
cnc
030074b8c5 继续按 align-linuxcnc 约束:补极坐标变体源码覆盖,测试通过 2026-06-02 22:20:03 +08:00
cnc
84fdc8a1c5 继续按 align-linuxcnc 约束:补增强块轴值错误源码覆盖,测试通过 2026-06-02 22:15:29 +08:00
cnc
9c03f91fa8 继续按 align-linuxcnc 约束:补极坐标源码覆盖,测试通过 2026-06-02 22:08:26 +08:00
cnc
868f20382a 继续按 align-linuxcnc 约束:补螺纹转换错误源码覆盖,测试通过 2026-06-02 20:15:04 +08:00
cnc
cde0bdaba6 继续按 align-linuxcnc 约束:补螺纹和恒线速校验源码覆盖,测试通过 2026-06-02 20:08:39 +08:00
cnc
3a60624108 继续按 align-linuxcnc 约束:补未使用字地址源码覆盖,测试通过 2026-06-02 19:53:28 +08:00
cnc
4d0cf788b1 继续按 align-linuxcnc 约束:补主轴选择和 M19 校验源码覆盖,测试通过 2026-06-02 19:47:52 +08:00
cnc
e875496fe0 继续按 align-linuxcnc 约束:补 convert_m 参数错误源码覆盖,测试通过 2026-06-02 19:36:22 +08:00
cnc
7e15a99d28 继续按 align-linuxcnc 约束:补控制字位置错误源码覆盖,测试通过 2026-06-02 18:49:53 +08:00
cnc
3435c3556a 继续按 align-linuxcnc 约束:补 reader 裸项错误源码覆盖,测试通过 2026-06-02 18:43:54 +08:00
cnc
318220b766 继续按 align-linuxcnc 约束:补 O-word 调用错误源码覆盖,测试通过 2026-06-02 18:33:15 +08:00
cnc
43a3e66106 继续按 align-linuxcnc 约束:补注释前缀控制字源码覆盖,测试通过 2026-06-02 18:27:50 +08:00
cnc
4f1042c548 继续按 align-linuxcnc 约束:补 O-word 匹配错误源码覆盖
结论:新增 undefined/no-matching O-word label 与子程序闭合方式错误的 source-link 断言;依据 LinuxCNC interp_o_word.cc,未扩展 smoke。
2026-06-02 18:01:07 +08:00
cnc
cc708b1ccc 继续按 align-linuxcnc 约束:补 O-word 执行错误源码覆盖
结论:新增重复/未匹配 O-word label 和 out-of-context return/endsub 的 source-link 断言;依据 LinuxCNC interp_o_word.cc,未扩展 smoke。
2026-06-02 17:55:14 +08:00
cnc
25519b5bc4 继续按 align-linuxcnc 约束:补文件读取错误源码覆盖
结论:新增 read_text 和 read_n_number 的 EOF、百分号文件、过长行和 N 行号错误 source-link 断言;依据 LinuxCNC interp_read.cc,未扩展 smoke。
2026-06-02 17:47:24 +08:00
cnc
a44199eb48 继续按 align-linuxcnc 约束:补 O-word 读取错误源码覆盖
结论:新增 malformed O-word、缺少控制表达式括号、O-call 参数过多和 M98 缺 P 的 source-link 断言;依据 LinuxCNC interp_read.cc,未扩展 smoke。
2026-06-02 17:35:18 +08:00
cnc
9a9b931982 继续按 align-linuxcnc 约束:补参数读取错误源码覆盖
结论:新增参数索引、命名参数终止和实数读取错误 fixture,全部绑定 LinuxCNC interp_read.cc 的 source-link 断言;未扩展 smoke。
2026-06-02 17:20:42 +08:00
cnc
7bd1dc640f 继续按 align-linuxcnc 约束:补 block reader 源码错误覆盖
结论:覆盖 LinuxCNC interp_read.cc/read_dollar()/read_one_item() 的重复 $、非法起始字符和中途 N word 错误,以及 interp_check.cc/check_m_codes() 的 M 码数量错误;未扩展 smoke。

验证:./test-linuxcnc-source-link.sh;./test-native.sh。
2026-06-02 17:00:55 +08:00
cnc
b83bc47c45 继续按 align-linuxcnc 约束:补 G/M reader 源码错误覆盖
结论:覆盖 LinuxCNC interp_read.cc/read_g()/read_m() 的范围、未知代码和同 modal group 冲突错误,代码表依据 interp_array.cc;未扩展 smoke。

验证:./test-linuxcnc-source-link.sh;./test-native.sh。
2026-06-02 16:35:58 +08:00
cnc
6ffcada028 继续按 align-linuxcnc 约束:补齐重复 word reader 源码覆盖
结论:覆盖 LinuxCNC interp_read.cc 中剩余重复 A/B/C/D/E/I/J/K/P/Q/R/U/V/W word reader 错误;未扩展 smoke。

验证:./test-linuxcnc-source-link.sh;./test-native.sh。
2026-06-02 16:30:39 +08:00
cnc
b2945cf57f 继续按 align-linuxcnc 约束:补 unary 和参数读取源码错误覆盖
结论:覆盖 LinuxCNC interp_read.cc 中 read_operation_unary() 未知词分支、read_parameter_setting() 缺少等号和 read_real_value() 空值错误;未扩展 smoke。

验证:./test-linuxcnc-source-link.sh;./test-native.sh。
2026-06-02 16:25:57 +08:00
cnc
05ece027c4 继续按 align-linuxcnc 约束:补 word reader 源码错误覆盖
结论:覆盖 LinuxCNC interp_read.cc 中负数 F/G/H/L/M/S/T 和重复 F/H/L/S/T/X/Y/Z word reader 错误;未扩展 smoke。

验证:./test-linuxcnc-source-link.sh;./test-native.sh。
2026-06-02 16:17:19 +08:00
cnc
b4d0bfae46 继续按 align-linuxcnc 约束:补齐 read_operation 源码错误覆盖
结论:覆盖 LinuxCNC interp_read.cc read_operation() 的未知二元操作名前缀、未知操作字符和未闭合表达式错误;未扩展 smoke。

验证:./test-native.sh;./test-linuxcnc-source-link.sh。
2026-06-02 16:10:03 +08:00
cnc
3f5c3faf25 继续按 align-linuxcnc 约束:补 unary 错误源码回归 2026-06-02 16:01:55 +08:00
cnc
dd4c62fce6 继续按 align-linuxcnc 约束:补 ATAN 错误源码回归 2026-06-02 15:58:01 +08:00
cnc
4861287138 继续按 align-linuxcnc 约束:补表达式错误源码回归 2026-06-02 15:54:17 +08:00
cnc
7319e18810 继续按 align-linuxcnc 约束:补表达式运算源码回归 2026-06-02 15:50:37 +08:00
cnc
df5c459b1d 继续按 align-linuxcnc 约束:补只读命名参数源回归
结论:新增 LinuxCNC-backed readonly named parameter fixture,依据 interp_namedparams.cc::lookup_named_param 与 init_readonly_param,在 native librs274 与 source-link runner 中验证 #<_metric>/#<_imperial>/#<_absolute>/#<_incremental>/#<_feed>/#<_rpm>/#<_spindle_on>/#<_spindle_cw>;未扩展 smoke,test-native 与 test-linuxcnc-source-link 均通过。
2026-06-02 15:40:23 +08:00
cnc
23284e528f 继续按 align-linuxcnc 约束:补 G7G76 源链接回归
结论:新增 LinuxCNC-backed G7 G76 fixture,依据 interp_convert.cc::convert_lathe_diameter_mode 和 convert_threading_cycle 验证直径模式下 G76 I/J/K 几何缩放、最终 threading pass 与 taper sync pitch;未扩展 smoke,test-native 与 test-linuxcnc-source-link 均通过。
2026-06-02 15:33:17 +08:00
cnc
952d3ecfd2 继续按 align-linuxcnc 约束:同步只读参数覆盖文档
结论:文档矩阵已同步 G7/G8 提交结果,#<_lathe_diameter_mode> 与 #<_lathe_radius_mode> 标记为 LinuxCNC native/source-backed 覆盖;仅文档改动,未运行测试。
2026-06-02 15:28:42 +08:00
cnc
6e0a5627f0 继续按 align-linuxcnc 约束:补 G7G8 源链接回归
结论:新增 LinuxCNC-backed lathe diameter/radius fixture,依据 interp_convert.cc::convert_lathe_diameter_mode 与 interp_namedparams.cc readonly 参数,在 native librs274 与 source-link runner 中验证 G7/G8 同块 X 缩放和 #<_lathe_diameter_mode>/#<_lathe_radius_mode> 分支;未扩展 smoke,test-native 与 test-linuxcnc-source-link 均通过。
2026-06-02 15:27:31 +08:00
cnc
6266887fec 继续按 align-linuxcnc 约束:锁定 INI remap smoke 过滤依据
结论:Node/browser generated switchkins smoke 的 INI_FILE_NAME 过滤已用 LinuxCNC rs274ngc_pre.cc ini_load 来源注释标明,并由 native/all-native guardrail 锁定;native/source-link/表检查均通过。
2026-06-02 15:17:28 +08:00
cnc
b8ad3a0346 继续按 align-linuxcnc 约束:闭合 switchkins 构建预检
结论:build-wasm 入口纳入 cached switchkins/remap 表校验,LinuxCNC RS274 INI 加载按 rs274ngc_pre.cc 保留真实 ini_load 路径,Node/browser generated switchkins smoke 跳过会触发 REMAP 依赖加载的 INI_FILE_NAME 别名;native/source-link/表检查均通过。
2026-06-02 15:13:37 +08:00
cnc
0abf801856 按提示词结论补 switchkins 完整覆盖锁 2026-06-02 14:40:52 +08:00
cnc
31e1e4499e 按提示词结论补构建校验依据锁 2026-06-02 14:26:40 +08:00
cnc
4780dd8621 按提示词结论补源链接脚本依据锁 2026-06-02 14:19:08 +08:00
cnc
303b6bfa36 按提示词结论补项目源集依据锁 2026-06-02 14:11:22 +08:00
cnc
854cc682b3 按提示词结论补源路径依据锁 2026-06-02 14:06:59 +08:00
cnc
dda5b4973e 按提示词结论补 flags helper 源依据锁 2026-06-02 14:02:36 +08:00
cnc
ce0605d20d 按提示词结论统一 wasm 源集依据锁 2026-06-02 13:58:02 +08:00
cnc
67154a70fa 按提示词结论补 wasm 探针源依据锁 2026-06-02 13:46:52 +08:00
cnc
5c7093ba39 按提示词结论补 INI 命名参数桥 2026-06-02 13:30:54 +08:00
cnc
0e1e22a864 按提示词结论接入 INI 参数文件桥 2026-06-02 13:26:09 +08:00
cnc
cee1bf3a3f 按提示词结论补参数文件默认回落锁 2026-06-02 13:10:32 +08:00
cnc
d807464cf7 按提示词结论补 native source 边界依据锁 2026-06-02 13:04:38 +08:00
cnc
303b7f7a72 按提示词结论补 wasm 探针源码依据锁 2026-06-02 13:00:01 +08:00
cnc
154fc7578a 按提示词结论补 wasm guardrail 源码依据 2026-06-02 12:55:36 +08:00
cnc
08978b82de 按提示词结论补 wasm blocker 源码依据 2026-06-02 12:33:30 +08:00
cnc
dfd41bb7fc 按提示词结论补锁 gcode 模块阻塞依据 2026-06-02 12:05:02 +08:00
cnc
1f37c979e9 按提示词结论补 tooldata API 源码依据 2026-06-02 12:01:25 +08:00
cnc
23f88a619a 按提示词结论补 emcStatus shim 源码依据 2026-06-02 11:57:58 +08:00
cnc
34aa884a22 按提示词结论补 runtime shim 任务源码依据 2026-06-02 11:54:37 +08:00
cnc
56f78b3054 按提示词结论补锁 standalone 探针源码依据 2026-06-02 11:50:06 +08:00
cnc
c3ec85196f 按提示词结论补锁 interp_find 探针源码依据 2026-06-02 11:47:24 +08:00
cnc
c2e5e8f2c8 按提示词结论修正 RTAPI shim 源码依据 2026-06-02 11:43:39 +08:00
cnc
10c1b70ed8 按 align-linuxcnc 约束补 runtime probe 源码依据 2026-06-02 11:35:55 +08:00
cnc
68b6c19392 按 align-linuxcnc 约束补 tooldata probe 源码依据 2026-06-02 11:15:09 +08:00
cnc
e1fe284078 按 align-linuxcnc 约束补 Python plugin probe 源码依据 2026-06-02 11:11:10 +08:00
cnc
082495d3ad 按 align-linuxcnc 约束补 RS274 probe 源码依据 2026-06-02 11:07:18 +08:00
cnc
e117ac8a5b 按 align-linuxcnc 约束补 tooldata shim 源码依据 2026-06-02 11:01:22 +08:00
cnc
518628bfea 按 align-linuxcnc 约束补状态 shim 源码依据 2026-06-02 10:57:04 +08:00
cnc
89f9930ec4 Document LinuxCNC Boost.Python shim source basis 2026-06-02 10:51:56 +08:00
cnc
7bf4300c84 按 align-linuxcnc 约束补 Boost Python facade 源码依据 2026-06-02 10:41:18 +08:00
cnc
16ac5cbfef 按 align-linuxcnc 约束补 Boost shim 源码依据 2026-06-02 10:30:14 +08:00
cnc
820ac7e72b 按 align-linuxcnc 约束补平台 shim 源码依据 2026-06-02 10:24:59 +08:00
cnc
5f4e918663 按 align-linuxcnc 约束补 Python C API shim 源码依据 2026-06-02 10:19:50 +08:00
cnc
de13d8efeb 按 align-linuxcnc 约束补 PythonPlugin shim 源码依据 2026-06-02 10:15:29 +08:00
cnc
8826f2ee73 按 align-linuxcnc 约束补 runtime shim 源码依据 2026-06-02 10:10:56 +08:00
cnc
e1708c96ee Document dlfcn shim source basis 2026-06-02 10:05:22 +08:00
cnc
cc7faef3a7 Track RTAPI source basis for wasm shim 2026-06-02 10:01:56 +08:00
cnc
d8c2364816 Track LinuxCNC RCS print source for wasm 2026-06-02 09:57:26 +08:00
cnc
93e83d52bf Track native emc status headers 2026-06-02 09:46:42 +08:00
cnc
c4e174f011 Track wasm emc status headers 2026-06-02 09:42:58 +08:00
cnc
00d62bd61a Track native nml support headers 2026-06-02 09:38:39 +08:00
cnc
c9c5db8e9c Track wasm nml status headers 2026-06-02 09:24:05 +08:00
cnc
26da1e6d9d Track native source-link support sources 2026-06-02 09:19:25 +08:00
cnc
d5c99f99c0 Track wasm ini source boundary 2026-06-02 09:15:18 +08:00
cnc
158fd343c6 Complete native tooldata manifest coverage 2026-06-02 09:09:33 +08:00
cnc
35d341de70 Track native tooldata source boundary 2026-06-02 08:42:18 +08:00
cnc
d6cae8bf56 Bind wasm tooldata runtime sources 2026-06-02 08:33:53 +08:00
cnc
0a0083b2d3 继续推进
结论:扩大 LinuxCNC source-link 覆盖,新增 basic motion 与普通注释事件验证,未扩展 smoke parser。
2026-06-02 08:26:25 +08:00
cnc
ad7a461b3e Link wasm runtime status constructors 2026-06-02 08:20:17 +08:00
cnc
dab7b9e2ef Guard browser storage behind OPFS 2026-06-02 07:11:41 +08:00
cnc
0db23b4a5d Tighten cached guardrail signatures 2026-06-02 06:47:10 +08:00
cnc
fbe1c31961 Stabilize browser smoke profile cleanup 2026-06-02 06:47:02 +08:00
cnc
87fcf74135 Strengthen LinuxCNC switchkins remap generation 2026-06-02 06:46:54 +08:00
cnc
f5013ec745 全方位推进合理、高效、快速处理上下文、禁止顺手扩功能 smoke:同步WASM烟测文档入口 2026-06-02 00:10:07 +08:00
cnc
1250638f22 全方位推进合理、高效、快速处理上下文、禁止顺手扩功能 smoke:同步WASM自检守卫 2026-06-02 00:04:19 +08:00
cnc
37c2f08dfb 全方位推进合理、高效、快速处理上下文、禁止顺手扩功能 smoke:收紧WASM烟测守卫 2026-06-01 23:59:41 +08:00
cnc
c474d45cdc 第2组:补充AXIS XYZB锁定分度源清单 2026-06-01 23:20:59 +08:00
cnc
04b3bc3a47 第2组:补充AXIS trivkins XZ源清单 2026-06-01 23:14:16 +08:00
cnc
110c3ba76e 第2组:补充Scorbot运动学源清单 2026-06-01 23:09:10 +08:00
cnc
57733f424a 第2组:补充TkLinuxCNC trivkins源清单 2026-06-01 23:03:18 +08:00
cnc
f98aa9ebb1 每轮只推进合理适量的上下文、禁止顺手扩功能 smoke:补充gantry运动学源清单
结论:第2组 kinematics manifest 补充 LinuxCNC gantry_mm 配置、HAL、tool table、README,以及 gantry HAL component 源/生成源/元数据,并在轻量 manifest 校验中固定这些 source coverage;native/source-link 均复用增量构建,没有触发慢编译。
2026-06-01 21:08:21 +08:00
cnc
8387f48130 每轮只推进合理适量的上下文、禁止顺手扩功能 smoke:补充tripod运动学源清单
结论:第2组 kinematics manifest 补充 LinuxCNC tripod.ini、tripodsim.hal、TkLinuxCNC README 和 tripodkins manpage 源,并在轻量 manifest 校验中固定这些 source coverage;native/source-link 均复用增量构建,没有触发慢编译。
2026-06-01 21:01:50 +08:00
cnc
d8909b5d8a 每轮只推进合理适量的上下文、禁止顺手扩功能 smoke:补充rosekins运动学源清单
结论:第2组 kinematics manifest 补充 LinuxCNC rose_engine 配置、NGC demo、halshow、README 和 rosekins manpage 源,并在轻量 manifest 校验中固定这些 source coverage;rose_engine.ini 引用的 rose.tbl 在 LinuxCNC 树中不存在,本轮不发明 tooldata;native/source-link 均复用增量构建,没有触发慢编译。
2026-06-01 20:52:15 +08:00
cnc
2c24909a47 每轮只推进合理适量的上下文、禁止顺手扩功能 smoke:补充CoreXY运动学源清单
结论:第2组 kinematics manifest 补充 LinuxCNC CoreXY 配置、HAL、说明、corexy_by_hal 组件源/生成源/元数据,并在轻量 manifest 校验中固定这些 source coverage;native/source-link 均复用增量构建,没有触发慢编译。
2026-06-01 20:45:32 +08:00
cnc
d7cac10c3b 每轮只推进合理适量的上下文、禁止顺手扩功能 smoke:补充delta运动学源清单
结论:第2组 kinematics manifest 补充 LinuxCNC ldelta/rdelta 配置、hallib HAL 和 demo notes,并在轻量 manifest 校验中固定这些 source coverage;native/source-link 均复用增量构建,没有触发慢编译。
2026-06-01 20:37:36 +08:00
cnc
bbaf9f4da6 每轮只推进合理适量的上下文、禁止顺手扩功能 smoke:补充pentapod运动学源清单 2026-06-01 20:26:26 +08:00
cnc
89fd49c604 每轮只推进合理适量的上下文、禁止顺手扩功能 smoke:补充max5运动学源清单 2026-06-01 20:16:56 +08:00
cnc
5ec10abf38 每轮只推进合理适量的上下文、禁止顺手扩功能 smoke:源码链接构建约束 2026-06-01 19:54:04 +08:00
cnc
6f9431a389 第6组:启用ccache加速重复编译并保持闭环 2026-06-01 06:42:35 +08:00
cnc
4c55ab3435 第 6 组:源码链接、构建和文档约束闭环完成 2026-06-01 06:12:50 +08:00
cnc
1498d9830d 继续推进:浏览器批量验证LinuxCNC配置入口
结论:浏览器 WASM smoke 已读取 LinuxCNC 生成的 64 个 switchkins 配置用例,覆盖 config/MACHINE/KINEMATICS/HALFILE 入口;native 与 source-link 验证通过。
2026-05-30 10:19:32 +08:00
cnc
c2d1aa6fe3 继续推进:用LinuxCNC配置生成switchkins测试
结论:M428/M429/M430 的配置匹配测试已从手写列表改为由 LinuxCNC INI/MACHINE/KINEMATICS/HALFILE 生成,native 与 source-link 验证通过。
2026-05-30 10:10:49 +08:00
cnc
de273bf830 接续上一轮:推进浏览器验证与M428配置收拢
结论:已完成浏览器侧真实加载验证,M428/M429/M430 的配置入口进一步从 LinuxCNC INI/HALFILE/REMAP 来源生成,native 与 source-link 验证通过。
2026-05-30 10:03:21 +08:00
cnc
b40914747d 满庭芳·源流归一
结论:LinuxCNC rs274ngc 源码移植边界继续收拢。已将 rs274ngc 头文件纳入 manifest 跟踪,core 编译组仍只包含可编译源文件;启用 CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND 时 API 默认使用 LinuxCNC rs274 后端;M428/M429/M430 与 5axis/TRT/TDR/RTCP 相关路径继续保持来自 LinuxCNC 源码或 remap 配置的薄桥接。

验证:./test-linuxcnc-source-syntax.sh;./test-linuxcnc-api-native.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
2026-05-28 18:00:53 +08:00
cnc
40a63c76f7 高效率推进 LinuxCNC 源码对齐流程
说明:集中 LinuxCNC 源码清单与 wasm/native probe 的路径解析,补强 manifest、shim、source-link 检查,并保持 LinuxCNC 源码后端对齐验证流程可复用。

验证:./test-native.sh;./test-linuxcnc-source-link.sh;./test-all-native.sh;wasm source/probe 相关脚本。
2026-05-27 11:14:06 +08:00
cnc
ce2f3f3769 Harden LinuxCNC wasm and native probe workflows 2026-05-26 17:00:58 +08:00
cnc
86734622d7 对齐 LinuxCNC NURBS 事件行号 2026-05-24 22:02:46 +08:00
cnc
dd4b7d9314 覆盖 LinuxCNC G6.2 XZ NURBS 映射 2026-05-24 21:55:45 +08:00
cnc
efc92d0380 修正 LinuxCNC NURBS 平面映射 2026-05-24 21:52:21 +08:00
cnc
b4861fd618 对齐 LinuxCNC 解释器行为覆盖 2026-05-24 21:47:29 +08:00
660 changed files with 58661 additions and 1524 deletions

View File

@@ -116,19 +116,13 @@ The first useful target is:
公共 API 使用同一个入口解析程序,后端通过 JSON 配置选择: 公共 API 使用同一个入口解析程序,后端通过 JSON 配置选择:
```json
{"backend":"smoke"}
```
`smoke` 是默认后端,用于 wasm 和 UI 联调。
native 构建启用 LinuxCNC 后端后,可以使用:
```json ```json
{"backend":"linuxcnc-rs274"} {"backend":"linuxcnc-rs274"}
``` ```
网页面板右上角的 `BACKEND` 下拉框会把该配置传给 wasm/core。当前默认 wasm 构建不包含 LinuxCNC 后端,选择 `LINUXCNC` 会返回明确错误native API 测试已验证该后端可用 `linuxcnc-rs274` 是默认后端wasm 和 UI 默认通过 LinuxCNC RS274 源码路径解析;`smoke` 仅用于显式编译/选择的 legacy 联调路径
网页面板右上角的 `BACKEND` 下拉框会把该配置传给 wasm/core。`./build-wasm.sh` 会生成 `web/public/cnc_sim.js``web/public/cnc_sim.wasm`,随后运行 `test-web-wasm-node-smoke.cjs``test-web-wasm-browser-smoke.sh`;这些 smoke 会验证 wasm 中的 LinuxCNC RS274 后端可在 Node 和 Chromium 浏览器中加载、可解析基础运动,并保留 `M428/M429/M430` remap 触发的运动学切换事件。
## RTCP 配置 ## RTCP 配置
@@ -143,11 +137,19 @@ RTCP 默认关闭。开启后,原有 `rapid`、`linear-feed`、`arc-feed` 事
"toolLengths": { "toolLengths": {
"7": 125 "7": 125
} }
},
"xyzbcTrt": {
"xRotPoint": 0,
"yRotPoint": 0,
"zRotPoint": 0,
"xOffset": -20,
"zOffset": -15,
"conventionalDirections": false
} }
} }
``` ```
当前 RTCP 内核是第一版头头/摆头类几何模型:编程 XYZ 视为刀尖点,刀长沿本地 `-Z`A/B/C 按 `Rz(C) * Ry(B) * Rx(A)` 旋转。后续会继续加入机型拓扑、旋转中心偏置、刀表刀长和不同五轴构型 RTCP pivot 事件只在已知 LinuxCNC switchkins 类型下输出。`M428` 切换到 `XYZBC_TRT` 后,`xyzbcTrt` 对应 LinuxCNC `xyzbc-trt-kins` 的 HAL pin`x-rot-point``y-rot-point``z-rot-point``x-offset``z-offset``conventional-directions`;刀长仍来自 `toolLength` / `toolLengths``M430` 使用 LinuxCNC `userkfuncs.c``kins_util.c` identity 运动学路径。未支持或未知的 switchkins 类型不会回退到自研 RTCP 几何
RTCP G 码控制已开始标准化: RTCP G 码控制已开始标准化:
@@ -161,11 +163,11 @@ RTCP G 码控制已开始标准化:
core 已对原型中的运动学切换 M 码做标准化解析: core 已对原型中的运动学切换 M 码做标准化解析:
- `M429`:切换到三轴恒等运动学 `IDENTITY`,关闭 RTCP - `M429`:切换到三轴恒等运动学 `IDENTITY`
- `M428`恢复到原始五轴运动学,开启 RTCP - `M428`切换到 LinuxCNC `xyzbc-trt-kins` 的 XYZBC TRT 运动学
- `M430`:切换到 `FIVEAXIS_BC`,开启 RTCP - `M430`:切换到 LinuxCNC 默认 `userk` 运动学;当前对齐 `userkfuncs.c` 的 identity 正/逆解
解析后会发出 `kinematics-switch` 事件。事件中的 `reserved` 字段表示运动学类型:`0=IDENTITY``1=ORIGINAL``2=FIVEAXIS_BC``feed` 字段临时表示 RTCP 状态:`0=关闭``1=开启`。同一行中的 `M428 M429 M430` 会按出现顺序产生三个切换事件。 解析后会发出 `kinematics-switch` 事件。事件中的 `reserved` 字段表示运动学类型:`0=IDENTITY``1=XYZBC_TRT``2=USERK_IDENTITY`。这些 M 码对齐 LinuxCNC `remap_subs/428remap.ngc``429remap.ngc``430remap.ngc`,只设置 `motion.switchkins-type`,不隐式开启或关闭 RTCPRTCP 状态仍由 `G43.4``G43.5``G49` 控制。同一行中的 `M428 M429 M430` 会按出现顺序产生三个切换事件。
`linuxcnc-rs274` native API backend 会在送入 LinuxCNC 解释器前拦截只包含这些切换 M 码的行,避免 LinuxCNC 去查找外部用户 M-code 文件。 `linuxcnc-rs274` native API backend 会在送入 LinuxCNC 解释器前拦截只包含这些切换 M 码的行,避免 LinuxCNC 去查找外部用户 M-code 文件。

214
analyze-linuxcnc-wasm-blockers.sh Executable file
View File

@@ -0,0 +1,214 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: this blocker scan is constrained to the RS274/WASM
# manifest. Python and Boost.Python blockers come from
# src/emc/rs274ngc/gcodemodule.cc, interp_namedparams.cc, interp_o_word.cc,
# and the py*.cc module bindings; tooldata/native-backend blockers come from
# src/emc/tooldata/tooldata_mmap.cc and tooldata_db.cc; native filesystem
# coverage comes from LinuxCNC RS274 sources such as interp_o_word.cc
# directory traversal and rs274ngc_pre.cc parameter-file replacement.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh "$manifest"
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
python_blockers=()
python_compile_covered=()
core_python_sources=()
dlopen_blockers=()
core_dlopen_sources=()
tooldata_blockers=()
core_tooldata_sources=()
shimmed_tooldata_users=()
core_tooldata_users=()
native_backend_blockers=()
native_fs_blockers=()
core_native_fs_sources=()
core_count=0
blocked_count=0
blocked_replacements=()
shim_source_output=$(./list-linuxcnc-wasm-safe-shims.sh)
mapfile -t wasm_safe_shims <<<"$shim_source_output"
blocked_replacement_for() {
case "$1" in
src/emc/tooldata/tooldata_mmap.cc)
./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc
;;
src/emc/rs274ngc/canonmodule.cc|src/emc/rs274ngc/gcodemodule.cc|src/emc/rs274ngc/interpmodule.cc)
./list-linuxcnc-wasm-safe-shims.sh linuxcnc_runtime_shim.cc
;;
src/emc/rs274ngc/pyarrays.cc|src/emc/rs274ngc/pyblock.cc|src/emc/rs274ngc/pyemctypes.cc|src/emc/rs274ngc/pyinterp1.cc|src/emc/rs274ngc/pyparamclass.cc)
./list-linuxcnc-wasm-safe-shims.sh python_c_api_shim.cc
;;
*)
return 1
;;
esac
}
wasm_safe_shim_in_build() {
local shim=$1
local candidate
for candidate in "${wasm_safe_shims[@]}"; do
if [[ "$candidate" == "$shim" ]]; then
return 0
fi
done
return 1
}
python_compile_probe_covers() {
grep -F "$1" test-linuxcnc-wasm-python-c-api-symbols.sh >/dev/null
}
core_sources=()
blocked_sources=()
manifest_line_number=0
while IFS= read -r manifest_line || [[ -n "$manifest_line" ]]; do
manifest_line_number=$((manifest_line_number + 1))
case "$manifest_line" in
""|\#*)
continue
;;
esac
IFS=: read -r group path note extra <<<"$manifest_line"
if [[ -z "$group" || -z "$path" || -z "$note" || -n "${extra:-}" ]]; then
echo "bad manifest line $manifest_line_number: $manifest_line" >&2
exit 1
fi
case "$group" in
core)
core_sources+=("$path")
;;
blocked)
blocked_sources+=("$path")
;;
header|metadata|blocked-header)
;;
*)
echo "unknown manifest group: $group" >&2
exit 1
;;
esac
done < "$manifest"
core_count=${#core_sources[@]}
blocked_count=${#blocked_sources[@]}
scan_source() {
local group=$1
local path=$2
full_path="$linuxcnc_root/$path"
if [[ ! -f "$full_path" ]]; then
echo "missing manifest source: $path" >&2
exit 1
fi
if [[ "$group" == "blocked" ]]; then
if ! replacement=$(blocked_replacement_for "$path"); then
echo "missing blocked replacement mapping: $path" >&2
exit 1
fi
if [[ ! -f "$replacement" ]]; then
echo "missing blocked replacement source: $replacement" >&2
exit 1
fi
if ! wasm_safe_shim_in_build "$replacement"; then
echo "blocked replacement is not in wasm shim build set: $path -> $replacement" >&2
exit 1
fi
blocked_replacements+=("$path -> $replacement")
fi
if [[ "$path" == src/emc/tooldata/* ]]; then
if [[ "$group" == "blocked" ]]; then
tooldata_blockers+=("$path")
else
core_tooldata_sources+=("$path")
fi
fi
if [[ "$path" == "src/emc/tooldata/tooldata_mmap.cc" ]]; then
native_backend_blockers+=("$path")
fi
if grep -Eq 'Python\.h|boost/python|pythonplugin|PyObject|PyInit_' "$full_path"; then
if [[ "$group" == "blocked" ]]; then
python_blockers+=("$path")
if python_compile_probe_covers "$path"; then
python_compile_covered+=("$path")
fi
else
core_python_sources+=("$path")
fi
fi
if grep -Eq 'dlopen|dlsym|RTLD_' "$full_path"; then
if [[ "$group" == "blocked" ]]; then
dlopen_blockers+=("$path")
else
core_dlopen_sources+=("$path")
fi
fi
if grep -Eq 'mkstemp|mkstemps|dirent\.h|opendir|readdir|unistd\.h' "$full_path"; then
if [[ "$group" == "blocked" ]]; then
native_fs_blockers+=("$path")
else
core_native_fs_sources+=("$path")
fi
fi
if [[ "$path" != src/emc/tooldata/* ]] && grep -Eq 'tooldata/tooldata.hh|tooldata_' "$full_path"; then
if [[ "$group" == "blocked" ]]; then
shimmed_tooldata_users+=("$path")
else
core_tooldata_users+=("$path")
fi
fi
}
for path in "${core_sources[@]}"; do
[[ -z "$path" ]] && continue
scan_source core "$path"
done
for path in "${blocked_sources[@]}"; do
[[ -z "$path" ]] && continue
scan_source blocked "$path"
done
print_group() {
local title=$1
shift
echo "[$title]"
if [[ $# -eq 0 ]]; then
echo "(none)"
else
printf '%s\n' "$@" | LC_ALL=C sort -u
fi
echo
}
echo "[manifest]"
echo "core=$core_count"
echo "blocked=$blocked_count"
echo
print_group "python" "${python_blockers[@]}"
print_group "python-compile-covered" "${python_compile_covered[@]}"
print_group "dlopen" "${dlopen_blockers[@]}"
print_group "tooldata" "${tooldata_blockers[@]}"
print_group "tooldata-users" "${shimmed_tooldata_users[@]}"
print_group "native-backend" "${native_backend_blockers[@]}"
print_group "native-fs" "${native_fs_blockers[@]}"
print_group "blocked-replacements" "${blocked_replacements[@]}"
print_group "core-python-shimmed" "${core_python_sources[@]}"
print_group "core-dlopen-shimmed" "${core_dlopen_sources[@]}"
print_group "core-tooldata-shimmed" "${core_tooldata_sources[@]}"
print_group "core-tooldata-users-shimmed" "${core_tooldata_users[@]}"
print_group "core-native-fs-shimmed" "${core_native_fs_sources[@]}"

View File

@@ -0,0 +1,105 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: native bridge object caches compile only project
# canon/kinematics bridge sources that wrap LinuxCNC src/emc/nml_intf/canon.hh
# and src/emc/kinematics implementations; G-code behavior remains in
# LinuxCNC RS274 objects linked by the caller.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then
cxx="ccache $cxx"
fi
build_jobs=${CNC_SIM_BUILD_JOBS:-8}
if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then
echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2
exit 1
fi
cache_dir=${CNC_SIM_NATIVE_BRIDGE_OBJECT_CACHE_DIR-build/native-bridge-object-cache}
if [[ -z "$cache_dir" ]]; then
echo "CNC_SIM_NATIVE_BRIDGE_OBJECT_CACHE_DIR must not be empty" >&2
exit 1
fi
if [[ "$cache_dir" =~ [[:space:]] ]]; then
echo "CNC_SIM_NATIVE_BRIDGE_OBJECT_CACHE_DIR must not contain whitespace: $cache_dir" >&2
exit 1
fi
mkdir -p "$cache_dir"
cache_dir=$(cd "$cache_dir" && pwd)
if [[ "$cache_dir" == "/" ]]; then
echo "CNC_SIM_NATIVE_BRIDGE_OBJECT_CACHE_DIR must not be the filesystem root" >&2
exit 1
fi
exec 8>"$cache_dir/native-bridge-objects.lock"
flock 8
preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$cache_dir/preflight-cache"}
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
source_output=$(./list-linuxcnc-bridge-smoke-project-sources.sh)
mapfile -t sources <<<"$source_output"
common_flag_output=$(
CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" \
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh \
--no-ulapi \
--core-include \
--core-src
)
mapfile -t common_flags <<<"$common_flag_output"
objects=()
source_index=0
for source in "${sources[@]}"; do
objects+=("$cache_dir/source_${source_index}.o")
source_index=$((source_index + 1))
done
signature="$cache_dir/native-bridge-objects.signature"
next_signature="$cache_dir/native-bridge-objects.signature.next"
{
printf 'CXX=%s\n' "$cxx"
printf 'PWD=%s\n' "$PWD"
printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root"
printf 'BUILD_RULE_VERSION=1\n'
printf 'COMMON_FLAGS='
printf ' %s' "${common_flags[@]}"
printf '\n'
printf 'SOURCES:\n'
printf '%s\n' "${sources[@]}"
} > "$next_signature"
if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then
rm -f "$cache_dir"/source_*.o "$cache_dir"/source_*.d
fi
mv "$next_signature" "$signature"
makefile="$cache_dir/native-bridge-objects.mk"
{
printf 'CXX := %s\n' "$cxx"
printf 'COMMON_FLAGS :='
printf ' %s' "${common_flags[@]}"
printf '\n\n'
printf 'OBJECTS :='
printf ' %s' "${objects[@]}"
printf '\n\n'
printf 'DEP_FILES :=\n'
for object in "${objects[@]}"; do
printf 'DEP_FILES += %s.d\n' "$object"
done
printf '\n.PHONY: all\n'
printf 'all: $(OBJECTS)\n\n'
source_index=0
for source in "${sources[@]}"; do
printf '%s: %s\n' "${objects[$source_index]}" "$source"
printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' \
"${objects[$source_index]}" "$source" "${objects[$source_index]}"
source_index=$((source_index + 1))
done
printf '\n-include $(DEP_FILES)\n'
} > "$makefile"
make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null
printf '%s\n' "${objects[@]}"

152
build-linuxcnc-wasm-link-probe.sh Executable file
View File

@@ -0,0 +1,152 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: link probe modes reuse wasm-safe objects built from
# linuxcnc-rs274-wasm-source-files.txt and add only a narrow probe main that
# asserts LinuxCNC rs274ngc_pre/runtime symbols stay linked.
usage() {
echo "usage: $0 --mode rs274ngc-pre|runtime [manifest]" >&2
}
mode=
manifest=linuxcnc-rs274-wasm-source-files.txt
while [[ "$#" -gt 0 ]]; do
case "$1" in
--mode)
if [[ "$#" -lt 2 ]]; then
echo "missing --mode value" >&2
exit 1
fi
mode=$2
shift 2
;;
--help|-h)
usage
exit 0
;;
--*)
usage
echo "unknown wasm link probe option: $1" >&2
exit 1
;;
*)
manifest=$1
shift
;;
esac
done
case "$mode" in
rs274ngc-pre|runtime)
;;
"")
usage
echo "missing --mode" >&2
exit 1
;;
*)
usage
echo "unknown wasm link probe mode: $mode" >&2
exit 1
;;
esac
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then
cxx="ccache $cxx"
fi
build_jobs=${CNC_SIM_BUILD_JOBS:-8}
if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then
echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2
exit 1
fi
cache_dir=${CNC_SIM_WASM_LINK_PROBE_CACHE_DIR-build/wasm-link-probe-cache}
if [[ -z "$cache_dir" ]]; then
echo "CNC_SIM_WASM_LINK_PROBE_CACHE_DIR must not be empty" >&2
exit 1
fi
if [[ "$cache_dir" =~ [[:space:]] ]]; then
echo "CNC_SIM_WASM_LINK_PROBE_CACHE_DIR must not contain whitespace: $cache_dir" >&2
exit 1
fi
mkdir -p "$cache_dir"
cache_dir=$(cd "$cache_dir" && pwd)
if [[ "$cache_dir" == "/" ]]; then
echo "CNC_SIM_WASM_LINK_PROBE_CACHE_DIR must not be the filesystem root" >&2
exit 1
fi
preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$cache_dir/preflight-cache"}
object_mode=$mode
main_source=core/wasm_shims/rs274ngc_pre_probe_main.cc
target_name=rs274ngc_pre_probe
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20-sections)
mapfile -t common_flags <<<"$common_flag_output"
case "$mode" in
rs274ngc-pre)
grep -F 'LinuxCNC source basis: src/emc/rs274ngc/rs274ngc_pre.cc provides' "$main_source" >/dev/null
;;
runtime)
object_mode=runtime
main_source=core/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc
target_name=cnc_sim_wasm_runtime_probe
common_flags+=(-DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
grep -F 'LinuxCNC source basis: src/emc/rs274ngc/rs274ngc_pre.cc provides read(),' "$main_source" >/dev/null
;;
esac
object_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./build-linuxcnc-wasm-probe-objects.sh --mode "$object_mode" "$manifest")
mapfile -t objects <<<"$object_output"
manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
mode_dir="$cache_dir/$mode"
mkdir -p "$mode_dir"
exec 8>"$mode_dir/link.lock"
flock 8
main_object="$mode_dir/main.o"
target="$mode_dir/$target_name"
signature="$mode_dir/link.signature"
next_signature="$mode_dir/link.signature.next"
{
printf 'CXX=%s\n' "$cxx"
printf 'PWD=%s\n' "$PWD"
printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root"
printf 'MANIFEST=%s\n' "$manifest"
printf 'MODE=%s\n' "$mode"
printf 'BUILD_RULE_VERSION=1\n'
printf 'COMMON_FLAGS='
printf ' %s' "${common_flags[@]}"
printf '\n'
printf 'MAIN_SOURCE=%s\n' "$main_source"
printf 'OBJECTS:\n'
printf '%s\n' "${objects[@]}"
} > "$next_signature"
if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then
rm -f "$main_object" "$main_object.d" "$target" "$mode_dir/runtime.symbols"
fi
mv "$next_signature" "$signature"
makefile="$mode_dir/link.mk"
{
printf 'CXX := %s\n' "$cxx"
printf 'COMMON_FLAGS :='
printf ' %s' "${common_flags[@]}"
printf '\n\n'
printf 'OBJECTS :='
printf ' %s' "${objects[@]}"
printf '\n\n'
printf '.PHONY: all\n'
printf 'all: %s\n\n' "$target"
printf '%s: %s\n' "$main_object" "$main_source"
printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' "$main_object" "$main_source" "$main_object"
printf '%s: $(OBJECTS) %s\n' "$target" "$main_object"
printf '\t@$(CXX) $(OBJECTS) %s -Wl,--gc-sections -lfmt -o %s\n\n' "$main_object" "$target"
printf '\n-include %s.d\n' "$main_object"
} > "$makefile"
make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null
printf '%s\n' "$target"

View File

@@ -0,0 +1,197 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: object probes compile the core entries selected from
# linuxcnc-rs274-wasm-source-files.txt, including src/emc/rs274ngc,
# src/emc/tooldata/tooldata_common.cc, src/emc/ini/inifile.cc, and
# src/emc/nml_intf/emcops.cc, together with CMake-listed wasm-safe project
# bridge sources and shims that replace the manifest's blocked Python, dlopen,
# tooldata mmap, HAL, and runtime edges.
usage() {
echo "usage: $0 --mode source-objects|rs274ngc-pre|runtime [manifest]" >&2
}
mode=
manifest=linuxcnc-rs274-wasm-source-files.txt
while [[ "$#" -gt 0 ]]; do
case "$1" in
--mode)
if [[ "$#" -lt 2 ]]; then
echo "missing --mode value" >&2
exit 1
fi
mode=$2
shift 2
;;
--help|-h)
usage
exit 0
;;
--*)
usage
echo "unknown wasm probe object cache option: $1" >&2
exit 1
;;
*)
manifest=$1
shift
;;
esac
done
case "$mode" in
source-objects|rs274ngc-pre|runtime)
;;
"")
usage
echo "missing --mode" >&2
exit 1
;;
*)
usage
echo "unknown wasm probe object cache mode: $mode" >&2
exit 1
;;
esac
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then
cxx="ccache $cxx"
fi
build_jobs=${CNC_SIM_BUILD_JOBS:-8}
if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then
echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2
exit 1
fi
cache_dir=${CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR-build/wasm-probe-object-cache}
if [[ -z "$cache_dir" ]]; then
echo "CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not be empty" >&2
exit 1
fi
if [[ "$cache_dir" =~ [[:space:]] ]]; then
echo "CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not contain whitespace: $cache_dir" >&2
exit 1
fi
mkdir -p "$cache_dir"
cache_dir=$(cd "$cache_dir" && pwd)
if [[ "$cache_dir" == "/" ]]; then
echo "CNC_SIM_WASM_PROBE_OBJECT_CACHE_DIR must not be the filesystem root" >&2
exit 1
fi
preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$cache_dir/preflight-cache"}
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" --require-rs274-complete
manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
common_flag_profile=core20-sections
case "$mode" in
source-objects)
common_flag_profile=core20
;;
esac
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile "$common_flag_profile")
mapfile -t common_flags <<<"$common_flag_output"
case "$mode" in
runtime)
common_flags+=(-DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
;;
esac
grep -F 'LinuxCNC source basis: wasm-safe project probe sources are the thin bridge' list-linuxcnc-wasm-safe-project-sources.sh >/dev/null
grep -F 'LinuxCNC source basis: wasm-safe shim probe sources are limited to the' list-linuxcnc-wasm-safe-shims.sh >/dev/null
sources=()
shim_output=$(./list-linuxcnc-wasm-safe-shims.sh)
mapfile -t shim_sources <<<"$shim_output"
sources+=("${shim_sources[@]}")
case "$mode" in
source-objects)
;;
rs274ngc-pre|runtime)
project_source_output=$(./list-linuxcnc-wasm-safe-project-sources.sh)
if [[ -n "$project_source_output" ]]; then
mapfile -t project_sources <<<"$project_source_output"
sources+=("${project_sources[@]}")
fi
;;
esac
source_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full)
mapfile -t linuxcnc_sources <<<"$source_output"
sources+=("${linuxcnc_sources[@]}")
for source in "${sources[@]}"; do
if [[ -z "$source" ]]; then
echo "wasm probe object source list contains an empty path" >&2
exit 1
fi
done
mode_dir="$cache_dir/$mode"
mkdir -p "$mode_dir"
exec 8>"$mode_dir/objects.lock"
flock 8
objects=()
object_index=0
for source in "${sources[@]}"; do
objects+=("$mode_dir/source_${object_index}.o")
object_index=$((object_index + 1))
done
signature="$mode_dir/objects.signature"
next_signature="$mode_dir/objects.signature.next"
{
printf 'CXX=%s\n' "$cxx"
printf 'PWD=%s\n' "$PWD"
printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root"
printf 'MANIFEST=%s\n' "$manifest"
printf 'MODE=%s\n' "$mode"
printf 'BUILD_RULE_VERSION=1\n'
printf 'COMMON_FLAGS='
printf ' %s' "${common_flags[@]}"
printf '\n'
printf 'SOURCES:\n'
printf '%s\n' "${sources[@]}"
} > "$next_signature"
if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then
rm -f "$mode_dir"/source_*.o "$mode_dir"/source_*.d
fi
mv "$next_signature" "$signature"
makefile="$mode_dir/objects.mk"
{
printf 'CXX := %s\n' "$cxx"
printf 'COMMON_FLAGS :='
printf ' %s' "${common_flags[@]}"
printf '\n\n'
printf 'OBJECTS :='
printf ' %s' "${objects[@]}"
printf '\n\n'
printf 'DEP_FILES :=\n'
for object in "${objects[@]}"; do
printf 'DEP_FILES += %s.d\n' "$object"
done
printf '\n.PHONY: all\n'
printf 'all: $(OBJECTS)\n\n'
object_index=0
for source in "${sources[@]}"; do
source_flags=()
case "$source" in
core/src/linuxcnc_corexykins_adapter.c|core/src/linuxcnc_genserfuncs_adapter.c|core/src/linuxcnc_xyzab_tdr_kins_adapter.c)
source_flags+=(-fpermissive)
;;
esac
printf '%s: %s\n' "${objects[$object_index]}" "$source"
printf '\t@$(CXX) $(COMMON_FLAGS)'
printf ' %s' "${source_flags[@]}"
printf ' -MMD -MP -MF %s.d -c %s -o %s\n\n' "${objects[$object_index]}" "$source" "${objects[$object_index]}"
object_index=$((object_index + 1))
done
printf '\n-include $(DEP_FILES)\n'
} > "$makefile"
make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null
printf '%s\n' "${objects[@]}"

View File

@@ -0,0 +1,211 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: standalone probe modes either compile LinuxCNC
# rs274/tooldata sources directly or link wasm-safe shims whose source basis is
# checked against LinuxCNC source comments before building.
usage() {
echo "usage: $0 --mode tooldata-link|tooldata-common-link|interp-base-link|python-plugin-link|python-plugin-inittab-link|rs274ngc-pre-object" >&2
}
mode=
while [[ "$#" -gt 0 ]]; do
case "$1" in
--mode)
if [[ "$#" -lt 2 ]]; then
echo "missing --mode value" >&2
exit 1
fi
mode=$2
shift 2
;;
--help|-h)
usage
exit 0
;;
*)
usage
echo "unknown wasm standalone probe option: $1" >&2
exit 1
;;
esac
done
case "$mode" in
tooldata-link|tooldata-common-link|interp-base-link|python-plugin-link|python-plugin-inittab-link|rs274ngc-pre-object)
;;
"")
usage
echo "missing --mode" >&2
exit 1
;;
*)
usage
echo "unknown wasm standalone probe mode: $mode" >&2
exit 1
;;
esac
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then
cxx="ccache $cxx"
fi
build_jobs=${CNC_SIM_BUILD_JOBS:-8}
if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then
echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2
exit 1
fi
cache_dir=${CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR-build/wasm-standalone-probe-cache}
if [[ -z "$cache_dir" ]]; then
echo "CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR must not be empty" >&2
exit 1
fi
if [[ "$cache_dir" =~ [[:space:]] ]]; then
echo "CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR must not contain whitespace: $cache_dir" >&2
exit 1
fi
mkdir -p "$cache_dir"
cache_dir=$(cd "$cache_dir" && pwd)
if [[ "$cache_dir" == "/" ]]; then
echo "CNC_SIM_WASM_STANDALONE_PROBE_CACHE_DIR must not be the filesystem root" >&2
exit 1
fi
preflight_cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-"$cache_dir/preflight-cache"}
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" --root-only
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
common_flag_args=()
sources=()
target_name=
case "$mode" in
tooldata-link|tooldata-common-link)
if [[ "$mode" == "tooldata-common-link" ]]; then
common_flag_args+=(--tooldata-include)
fi
tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc)
tooldata_runtime_stubs_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc)
tooldata_common_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/tooldata/tooldata_common.cc)
grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_common.cc provides' core/wasm_shims/tooldata/tooldata_common_probe_main.cc >/dev/null
grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_mmap.cc, tooldata_nml.cc,' core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
sources+=(
"$tooldata_common_source"
"$tooldata_mmap_backend_shim"
"$tooldata_runtime_stubs_shim"
)
if [[ "$mode" == "tooldata-link" ]]; then
sources+=(core/wasm_shims/tooldata/tooldata_probe_main.cc)
target_name=tooldata_probe
else
sources+=(core/wasm_shims/tooldata/tooldata_common_probe_main.cc)
target_name=tooldata_common_probe
fi
;;
interp-base-link)
dlfcn_shim=$(./list-linuxcnc-wasm-safe-shims.sh dlfcn.cc)
interp_base_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/rs274ngc/interp_base.cc)
grep -F 'LinuxCNC source basis: src/emc/rs274ngc/interp_base.cc provides' core/wasm_shims/interp_base_probe_main.cc >/dev/null
sources+=(
"$dlfcn_shim"
"$interp_base_source"
core/wasm_shims/interp_base_probe_main.cc
)
target_name=interp_base_probe
;;
python-plugin-link|python-plugin-inittab-link)
python_plugin_shim=$(./list-linuxcnc-wasm-safe-shims.sh python_plugin.cc)
if [[ "$mode" == "python-plugin-link" ]]; then
probe_source=core/wasm_shims/pythonplugin/python_plugin_probe_main.cc
target_name=python_plugin_probe
grep -F 'LinuxCNC source basis: src/emc/pythonplugin/python_plugin.cc and' "$probe_source" >/dev/null
else
probe_source=core/wasm_shims/pythonplugin/python_plugin_inittab_probe_main.cc
target_name=python_plugin_inittab_probe
grep -F 'LinuxCNC source basis: src/emc/pythonplugin/python_plugin.cc' "$probe_source" >/dev/null
fi
sources+=(
"$python_plugin_shim"
"$probe_source"
)
;;
rs274ngc-pre-object)
rs274ngc_pre_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/rs274ngc/rs274ngc_pre.cc)
grep -F 'core:src/emc/rs274ngc/rs274ngc_pre.cc:Interp implementation covered by rs274ngc_pre link probe' linuxcnc-rs274-wasm-source-files.txt >/dev/null
sources+=("$rs274ngc_pre_source")
target_name=rs274ngc_pre.o
;;
esac
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh "${common_flag_args[@]}")
mapfile -t common_flags <<<"$common_flag_output"
mode_dir="$cache_dir/$mode"
mkdir -p "$mode_dir"
exec 8>"$mode_dir/probe.lock"
flock 8
objects=()
object_index=0
for source in "${sources[@]}"; do
objects+=("$mode_dir/source_${object_index}.o")
object_index=$((object_index + 1))
done
target="$mode_dir/$target_name"
if [[ "$mode" == "rs274ngc-pre-object" ]]; then
target=${objects[0]}
fi
signature="$mode_dir/probe.signature"
next_signature="$mode_dir/probe.signature.next"
{
printf 'CXX=%s\n' "$cxx"
printf 'PWD=%s\n' "$PWD"
printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root"
printf 'MODE=%s\n' "$mode"
printf 'BUILD_RULE_VERSION=1\n'
printf 'COMMON_FLAGS='
printf ' %s' "${common_flags[@]}"
printf '\n'
printf 'SOURCES:\n'
printf '%s\n' "${sources[@]}"
} > "$next_signature"
if [[ ! -f "$signature" ]] || ! cmp -s "$signature" "$next_signature"; then
rm -f "$mode_dir"/source_*.o "$mode_dir"/source_*.d "$target"
fi
mv "$next_signature" "$signature"
makefile="$mode_dir/probe.mk"
{
printf 'CXX := %s\n' "$cxx"
printf 'COMMON_FLAGS :='
printf ' %s' "${common_flags[@]}"
printf '\n\n'
printf 'OBJECTS :='
printf ' %s' "${objects[@]}"
printf '\n\n'
printf 'DEP_FILES :=\n'
for object in "${objects[@]}"; do
printf 'DEP_FILES += %s.d\n' "$object"
done
printf '\n.PHONY: all\n'
printf 'all: %s\n\n' "$target"
object_index=0
for source in "${sources[@]}"; do
printf '%s: %s\n' "${objects[$object_index]}" "$source"
printf '\t@$(CXX) $(COMMON_FLAGS) -MMD -MP -MF %s.d -c %s -o %s\n\n' \
"${objects[$object_index]}" "$source" "${objects[$object_index]}"
object_index=$((object_index + 1))
done
if [[ "$mode" != "rs274ngc-pre-object" ]]; then
printf '%s: $(OBJECTS)\n' "$target"
printf '\t@$(CXX) $(OBJECTS) -o %s\n\n' "$target"
fi
printf '\n-include $(DEP_FILES)\n'
} > "$makefile"
make --output-sync=target -j"$build_jobs" -f "$makefile" >/dev/null
printf '%s\n' "$target"

View File

@@ -3,14 +3,208 @@ set -euo pipefail
cd "$(dirname "$0")" cd "$(dirname "$0")"
if ! command -v emcmake >/dev/null 2>&1; then # LinuxCNC source basis: the wasm build preflights and compiles the
echo "Emscripten is required. Install/activate emsdk so emcmake and emcc are in PATH." >&2 # linuxcnc-rs274-wasm-source-files.txt manifest entries from
# src/emc/rs274ngc/tooldata plus source-basis-checked shims before copying the
# browser artifacts; it must not add independent interpreter behavior.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
build_jobs=${CNC_SIM_BUILD_JOBS:-8}
if ! [[ "$build_jobs" =~ ^[1-9][0-9]*$ ]]; then
echo "CNC_SIM_BUILD_JOBS must be a positive integer: $build_jobs" >&2
exit 1
fi
cmake_compiler_launcher_mode=none
cmake_compiler_launcher_args=()
if [[ -z ${CXX:-} ]] && command -v ccache >/dev/null 2>&1; then
cmake_compiler_launcher_mode=ccache
cmake_compiler_launcher_args+=(
-DCMAKE_C_COMPILER_LAUNCHER=ccache
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
)
fi
preflight_cache_dir=build/wasm-preflight-cache
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest"
./check-linuxcnc-rs274-source-map.sh
./check-linuxcnc-kinematics-source-map.sh
./check-linuxcnc-opfs-source-map.sh
./check-linuxcnc-switchkins-remap-source-map.sh
./check-linuxcnc-wasm-smoke-source-map.sh
./check-linuxcnc-build-source-map.sh
./check-linuxcnc-browser-app-source-map.sh
./check-linuxcnc-workstreams-source-map.sh
./check-linuxcnc-tooldata-source-map.sh
./check-linuxcnc-wasm-shims-source-map.sh
./check-linuxcnc-wasm-blockers-source-map.sh
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table-cached.sh --cache-dir "$preflight_cache_dir" linuxcnc-kinematics-source-files.txt
manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
default_manifest="$PWD/linuxcnc-rs274-wasm-source-files.txt"
count_nonempty_lines() {
local text=${1-}
local count=0
local line
while IFS= read -r line; do
if [[ -n "$line" ]]; then
count=$((count + 1))
fi
done <<<"$text"
printf '%s\n' "$count"
}
parallel_probe_labels=()
parallel_probe_logs=()
parallel_probe_pids=()
run_parallel_wasm_probe() {
local label=$1
shift
local probe_index=${#parallel_probe_pids[@]}
local log_dir=build/wasm-preflight-cache/parallel-probe-logs
local log="$log_dir/probe_${probe_index}.log"
mkdir -p "$log_dir"
(
CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" "$@"
) >"$log" 2>&1 &
parallel_probe_labels+=("$label")
parallel_probe_logs+=("$log")
parallel_probe_pids+=("$!")
}
wait_parallel_wasm_probes() {
local failed=0
local probe_index
local status
for probe_index in "${!parallel_probe_pids[@]}"; do
echo "${parallel_probe_labels[$probe_index]}"
if wait "${parallel_probe_pids[$probe_index]}"; then
cat "${parallel_probe_logs[$probe_index]}"
else
status=$?
cat "${parallel_probe_logs[$probe_index]}" >&2 || true
echo "${parallel_probe_labels[$probe_index]} failed with status $status" >&2
failed=1
fi
done
parallel_probe_labels=()
parallel_probe_logs=()
parallel_probe_pids=()
if [[ "$failed" -ne 0 ]]; then
exit 1
fi
}
# This script writes fixed build and public artifact paths, so serialize it.
if [[ "${CNC_SIM_BUILD_WASM_SKIP_LOCK:-0}" != "1" ]]; then
exec 9>"${TMPDIR:-/tmp}/cnc_sim_build_wasm.lock"
flock 9
fi
manifest_core_count=0
manifest_blocked_count=0
manifest_core_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core)
manifest_blocked_output=$(CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked)
manifest_core_count=$(count_nonempty_lines "$manifest_core_output")
manifest_blocked_count=$(count_nonempty_lines "$manifest_blocked_output")
if [[ "$manifest_core_count" -eq 0 || "$manifest_blocked_count" -eq 0 ]]; then
echo "unexpected wasm manifest partition: core=$manifest_core_count blocked=$manifest_blocked_count" >&2
exit 1
fi
if [[ "$manifest" == "$default_manifest" ]] && [[ "$manifest_core_count" -ne 26 || "$manifest_blocked_count" -ne 9 ]]; then
echo "unexpected wasm manifest partition: core=$manifest_core_count blocked=$manifest_blocked_count" >&2
exit 1
fi
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh --cache-dir "$preflight_cache_dir" "$manifest" --require-rs274-complete
echo "LinuxCNC wasm blocker scan"
CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-blockers.sh "$manifest"
echo "LinuxCNC wasm-safe source syntax probe"
CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-source-syntax.sh "$manifest"
echo "LinuxCNC wasm-safe source object probe"
CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-source-objects.sh "$manifest"
echo "LinuxCNC wasm-safe CMake probe target"
CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-cmake-safe-probe.sh "$manifest"
run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe" ./test-linuxcnc-wasm-tooldata-link.sh
run_parallel_wasm_probe "LinuxCNC wasm tooldata_common link probe" ./test-linuxcnc-wasm-tooldata-common-link.sh
run_parallel_wasm_probe "LinuxCNC wasm tooldata_mmap symbol probe" ./test-linuxcnc-wasm-tooldata-mmap-symbols.sh
run_parallel_wasm_probe "LinuxCNC wasm tooldata runtime symbol probe" ./test-linuxcnc-wasm-tooldata-runtime-symbols.sh
run_parallel_wasm_probe "LinuxCNC wasm interp_base link probe" ./test-linuxcnc-wasm-interp-base-link.sh
run_parallel_wasm_probe "LinuxCNC wasm interp_find link probe" ./test-linuxcnc-wasm-interp-find-link.sh
run_parallel_wasm_probe "LinuxCNC wasm python_plugin link probe" ./test-linuxcnc-wasm-python-plugin-link.sh
run_parallel_wasm_probe "LinuxCNC wasm Python C API symbol probe" ./test-linuxcnc-wasm-python-c-api-symbols.sh
run_parallel_wasm_probe "LinuxCNC wasm Python binding link probe" ./test-linuxcnc-wasm-python-binding-link.sh
run_parallel_wasm_probe "LinuxCNC wasm rs274ngc_pre object probe" ./test-linuxcnc-wasm-rs274ngc-pre-object.sh
wait_parallel_wasm_probes
echo "LinuxCNC wasm rs274ngc_pre link blocker scan"
CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh "$manifest"
echo "LinuxCNC wasm rs274ngc_pre link probe"
CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-rs274ngc-pre-link.sh "$manifest"
echo "LinuxCNC wasm runtime link probe"
CNC_SIM_PREFLIGHT_CACHE_DIR="$preflight_cache_dir" ./test-linuxcnc-wasm-runtime-link.sh "$manifest"
missing_tools=()
for required_tool in cmake emcmake emcc node; do
if ! command -v "$required_tool" >/dev/null 2>&1; then
missing_tools+=("$required_tool")
fi
done
if ((${#missing_tools[@]} > 0)); then
echo "WASM build tools are required. Missing: ${missing_tools[*]}." >&2
echo "Install/activate cmake, emsdk, and Node.js so cmake, emcmake, emcc, and node are in PATH." >&2
exit 1 exit 1
fi fi
emcmake cmake -S core -B build/wasm -DCMAKE_BUILD_TYPE=Release wasm_build_signature=build/wasm/wasm-build.signature
cmake --build build/wasm wasm_build_next_signature=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_build.signature.XXXXXX")
{
printf 'CXX=%s\n' "${CXX:-g++}"
printf 'PWD=%s\n' "$PWD"
printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root"
printf 'MANIFEST=%s\n' "$manifest"
printf 'MANIFEST_HASH='
sha256sum "$manifest"
printf 'COMPILER_LAUNCHER=%s\n' "$cmake_compiler_launcher_mode"
printf 'CMAKE_LISTS='
sha256sum core/CMakeLists.txt
printf 'CONFIGURE_RULE_VERSION=2\n'
} > "$wasm_build_next_signature"
configure_wasm=1
if [[ ! -f "$wasm_build_signature" ]] || ! cmp -s "$wasm_build_signature" "$wasm_build_next_signature"; then
mkdir -p build/wasm
elif [[ -f build/wasm/CMakeCache.txt ]]; then
configure_wasm=0
fi
if [[ "$configure_wasm" == "1" ]]; then
emcmake cmake -S core -B build/wasm \
-DCMAKE_BUILD_TYPE=Release \
-DCNC_SIM_LINUXCNC_ROOT="$linuxcnc_root" \
-DCNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST="$manifest" \
-DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON \
-DCNC_SIM_ENABLE_SMOKE_BACKEND=OFF \
"${cmake_compiler_launcher_args[@]}"
fi
mv "$wasm_build_next_signature" "$wasm_build_signature"
cmake --build build/wasm --target cnc_sim_wasm_runtime_probe cnc_sim_wasm --parallel "$build_jobs"
mkdir -p web/public mkdir -p web/public
cp build/wasm/cnc_sim.js web/public/ cp build/wasm/cnc_sim.js web/public/cnc_sim.js
cp build/wasm/cnc_sim.wasm web/public/ cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm
cmp -s build/wasm/cnc_sim.js web/public/cnc_sim.js
cmp -s build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm
./test-web-wasm-node-smoke.sh
./test-web-wasm-browser-smoke.sh

View File

@@ -0,0 +1,130 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: this guard ties the browser app entry point to
# LinuxCNC RS274 parameter-file defaults, rs274ngc_pre.cc restore/save/read
# file semantics, and the existing OPFS-backed wasm-core bridge. It validates
# app integration policy only; it does not add CNC behavior or expand smoke
# parsing, or interpret G-code.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
grep -F '#define RS274NGC_PARAMETER_FILE_NAME_DEFAULT "rs274ngc.var"' \
"$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null
grep -F '#define RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX ".bak"' \
"$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null
grep -F 'int Interp::restore_parameters(const char *filename)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'int Interp::save_parameters(const char *filename,' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'std::string tempfile = std::string(filename) + ".new";' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F '+ RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX;' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'unlink(bakfile.c_str());' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'if(link(filename, bakfile.c_str()) < 0)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'if(rename(tempfile.c_str(), filename) < 0)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'read_text(command, _setup.file_pointer, _setup.linetext,' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'LinuxCNC browser app source map' docs/linuxcnc-browser-app-source-map.md >/dev/null
grep -F 'must not add CNC behavior' docs/linuxcnc-browser-app-source-map.md >/dev/null
grep -F 'must not expand the temporary smoke parser' docs/linuxcnc-browser-app-source-map.md >/dev/null
grep -F '`RS274NGC_PARAMETER_FILE_NAME_DEFAULT` as `rs274ngc.var`' \
docs/linuxcnc-browser-app-source-map.md >/dev/null
grep -F '`parseFileWithParameterFile(programPath, parameterPath, "linuxcnc", ...)`' \
docs/linuxcnc-browser-app-source-map.md >/dev/null
grep -F 'orders parameter saves as backup unlink' \
docs/linuxcnc-browser-app-source-map.md >/dev/null
grep -F '<script type="module" src="/src/app.js"></script>' web/index.html >/dev/null
grep -F '<option value="linuxcnc-rs274">LINUXCNC</option>' web/index.html >/dev/null
if [[ "$(grep -Fc '<option value=' web/index.html)" -ne 1 ]]; then
echo "browser app backend selector must expose only the LinuxCNC RS274 backend" >&2
exit 1
fi
grep -F 'import { createWasmSimulator } from "./wasm-core.js";' web/src/app.js >/dev/null
grep -F 'const programPath = "programs/current.ngc";' web/src/app.js >/dev/null
grep -F 'const linuxCncParameterFileName = "rs274ngc.var";' web/src/app.js >/dev/null
grep -F 'const parameterPath = `parameters/${linuxCncParameterFileName}`;' web/src/app.js >/dev/null
grep -F 'const linuxCncBackend = "linuxcnc-rs274";' web/src/app.js >/dev/null
grep -F 'const opfsOptions = { required: true, mountPoint: "/cnc", workspacePath: "cnc-simulator" };' \
web/src/app.js >/dev/null
grep -F 'simulatorPromise = createWasmSimulator({' web/src/app.js >/dev/null
grep -F 'opfs: opfsOptions,' web/src/app.js >/dev/null
grep -F 'OPFS workspace is required for browser program parsing' web/src/app.js >/dev/null
grep -F 'await simulator.fs.opfs.writeFile(programPath, elements.input.value);' web/src/app.js >/dev/null
grep -F 'const events = await simulator.parseFileWithParameterFile(programPath, parameterPath, "linuxcnc", parseOptions);' \
web/src/app.js >/dev/null
grep -F 'elements.backendSelect.value = linuxCncBackend;' web/src/app.js >/dev/null
grep -F 'globalThis.__cncSimulatorForTest = simulatorPromise;' web/src/app.js >/dev/null
grep -F 'app frame did not persist current program into OPFS' \
web/test-browser-wasm-smoke-app-sections.js >/dev/null
grep -F 'app frame did not persist LinuxCNC parameter state into OPFS' \
web/test-browser-wasm-smoke-app-sections.js >/dev/null
grep -F 'app frame did not persist LinuxCNC parameter backup into OPFS' \
web/test-browser-wasm-smoke-app-sections.js >/dev/null
grep -F 'app frame did not clean up LinuxCNC temporary parameter file in OPFS' \
web/test-browser-wasm-smoke-app-sections.js >/dev/null
grep -F 'OPFS PROGRAM RESTORED' web/test-browser-wasm-smoke-app-sections.js >/dev/null
grep -F 'app frame did not reload LinuxCNC parameter state from OPFS after reload' \
web/test-browser-wasm-smoke-app-sections.js >/dev/null
grep -F 'runSection("browser app opfs save"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("browser app opfs restore"' test-web-wasm-browser-smoke.sh >/dev/null
if grep -R -n -E 'localStorage|sessionStorage|indexedDB|showOpenFilePicker|showSaveFilePicker|showDirectoryPicker|webkitRequestFileSystem|FileReader' web/src; then
echo "browser app/wasm code must not add filesystem persistence outside OPFS" >&2
exit 1
fi
python3 - <<'PY'
from pathlib import Path
source_map = Path("docs/linuxcnc-browser-app-source-map.md").read_text(encoding="utf-8")
for entry in [
"web/index.html",
"web/src/app.js",
"web/test-browser-wasm-smoke-app-sections.js",
]:
if f"| `{entry}` |" not in source_map:
raise SystemExit(f"docs/linuxcnc-browser-app-source-map.md missing app entry: {entry}")
for phrase in [
"must not add CNC behavior",
"must not expand the temporary smoke parser",
"existing `createWasmSimulator()` OPFS API",
"programs/current.ngc",
"parameters/rs274ngc.var",
"visible backend selector must stay limited",
"./check-linuxcnc-browser-app-source-map.sh",
"does not interpret G-code",
]:
if phrase not in source_map:
raise SystemExit(f"docs/linuxcnc-browser-app-source-map.md missing boundary phrase: {phrase}")
for entry in [
"test-native.sh",
"test-linuxcnc-source-link.sh",
"build-wasm.sh",
]:
text = Path(entry).read_text(encoding="utf-8")
if "./check-linuxcnc-browser-app-source-map.sh" not in text:
raise SystemExit(f"{entry} must run ./check-linuxcnc-browser-app-source-map.sh")
app = Path("web/src/app.js").read_text(encoding="utf-8")
if "parse(" in app.replace("parseProgram", ""):
raise SystemExit("browser app should route program execution through parseFileWithParameterFile, not direct parse")
if app.count("elements.backendSelect.value = linuxCncBackend;") != 2:
raise SystemExit("browser app backend selector must only be forced to the LinuxCNC backend")
if "backendSelect.value" in app.replace("elements.backendSelect.value = linuxCncBackend;", ""):
raise SystemExit("browser app must not read the backend selector as a dynamic backend source")
if 'backend: linuxCncBackend' not in app:
raise SystemExit("browser app parse options must force the LinuxCNC backend")
PY

View File

@@ -0,0 +1,202 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: this guard ties native/source-link/WASM build entry
# points to LinuxCNC Submakefile source ownership, source manifests, persistent
# build signatures, and source-backed WASM artifact ordering. It validates
# build policy only; it does not add CNC behavior, expand smoke parsing, or
# interpret G-code.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
grep -F 'LIBRS274SRCS := $(addprefix emc/rs274ngc/,' \
"$linuxcnc_root/src/emc/rs274ngc/Submakefile" >/dev/null
grep -F 'rs274ngc_pre.cc \' "$linuxcnc_root/src/emc/rs274ngc/Submakefile" >/dev/null
grep -F '../lib/libtooldata.so.0' "$linuxcnc_root/src/emc/rs274ngc/Submakefile" >/dev/null
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_common.cc' \
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_mmap.cc' \
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_db.cc' \
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
grep -F 'LIBLCNCINISRCS := emc/ini/inifile.cc' \
"$linuxcnc_root/src/emc/ini/Submakefile" >/dev/null
grep -F 'LIBPPSRCS := $(addprefix emc/pythonplugin/,' \
"$linuxcnc_root/src/emc/pythonplugin/Submakefile" >/dev/null
grep -F 'GENSERKINSSRCS := emc/kinematics/ugenserkins.c' \
"$linuxcnc_root/src/emc/kinematics/Submakefile" >/dev/null
grep -F 'extern int kinematicsInverse(const struct EmcPose * world,' \
"$linuxcnc_root/src/emc/kinematics/kinematics.h" >/dev/null
grep -F 'LinuxCNC build source map' docs/linuxcnc-build-source-map.md >/dev/null
grep -F 'must not add CNC' docs/linuxcnc-build-source-map.md >/dev/null
grep -F 'must not expand the temporary smoke parser' docs/linuxcnc-build-source-map.md >/dev/null
grep -F '`src/emc/rs274ngc/Submakefile` defines `LIBRS274SRCS`' \
docs/linuxcnc-build-source-map.md >/dev/null
grep -F '`src/emc/tooldata/Submakefile` defines the LinuxCNC tooldata' \
docs/linuxcnc-build-source-map.md >/dev/null
grep -F 'Default WASM manifest partition must remain 26 wasm-safe `core` sources and' \
docs/linuxcnc-build-source-map.md >/dev/null
grep -F '`build-wasm.sh` must configure with `CNC_SIM_ENABLE_SMOKE_BACKEND=OFF`' \
docs/linuxcnc-build-source-map.md >/dev/null
grep -F '`test-native.sh`, `test-linuxcnc-source-link.sh`, and `build-wasm.sh` must' \
docs/linuxcnc-build-source-map.md >/dev/null
grep -F 'LinuxCNC source basis: the wasm build preflights and compiles the' build-wasm.sh >/dev/null
source_map_guards=(
'./check-linuxcnc-rs274-source-map.sh'
'./check-linuxcnc-kinematics-source-map.sh'
'./check-linuxcnc-opfs-source-map.sh'
'./check-linuxcnc-switchkins-remap-source-map.sh'
'./check-linuxcnc-wasm-smoke-source-map.sh'
'./check-linuxcnc-build-source-map.sh'
'./check-linuxcnc-browser-app-source-map.sh'
'./check-linuxcnc-workstreams-source-map.sh'
'./check-linuxcnc-tooldata-source-map.sh'
'./check-linuxcnc-wasm-shims-source-map.sh'
'./check-linuxcnc-wasm-blockers-source-map.sh'
)
for guarded_entry in test-native.sh test-linuxcnc-source-link.sh build-wasm.sh; do
for source_map_guard in "${source_map_guards[@]}"; do
grep -F "$source_map_guard" "$guarded_entry" >/dev/null
done
done
grep -F 'manifest_core_count=$(count_nonempty_lines "$manifest_core_output")' build-wasm.sh >/dev/null
grep -F 'manifest_blocked_count=$(count_nonempty_lines "$manifest_blocked_output")' build-wasm.sh >/dev/null
grep -F 'manifest_core_count" -ne 26 || "$manifest_blocked_count" -ne 9' build-wasm.sh >/dev/null
grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe" ./test-linuxcnc-wasm-tooldata-link.sh' build-wasm.sh >/dev/null
grep -F 'run_parallel_wasm_probe "LinuxCNC wasm rs274ngc_pre object probe" ./test-linuxcnc-wasm-rs274ngc-pre-object.sh' build-wasm.sh >/dev/null
grep -F 'wait_parallel_wasm_probes' build-wasm.sh >/dev/null
grep -F 'for required_tool in cmake emcmake emcc node; do' build-wasm.sh >/dev/null
grep -F 'CONFIGURE_RULE_VERSION=2' build-wasm.sh >/dev/null
grep -F -- '-DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON' build-wasm.sh >/dev/null
grep -F -- '-DCNC_SIM_ENABLE_SMOKE_BACKEND=OFF' build-wasm.sh >/dev/null
grep -F 'cmake --build build/wasm --target cnc_sim_wasm_runtime_probe cnc_sim_wasm --parallel "$build_jobs"' build-wasm.sh >/dev/null
grep -F 'cmp -s build/wasm/cnc_sim.js web/public/cnc_sim.js' build-wasm.sh >/dev/null
grep -F 'cmp -s build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm' build-wasm.sh >/dev/null
grep -F './test-web-wasm-node-smoke.sh' build-wasm.sh >/dev/null
grep -F './test-web-wasm-browser-smoke.sh' build-wasm.sh >/dev/null
grep -F 'LinuxCNC source basis: this source-link test compiles the manifest-selected' \
test-linuxcnc-source-link.sh >/dev/null
grep -F 'BUILD_RULE_VERSION=1' test-linuxcnc-source-link.sh >/dev/null
grep -F 'make --output-sync=target -j"$build_jobs" -f "$source_link_makefile"' \
test-linuxcnc-source-link.sh >/dev/null
grep -F 'LinuxCNC source basis: this syntax probe checks the native RS274 manifest' \
test-linuxcnc-source-syntax.sh >/dev/null
grep -F 'LinuxCNC source basis: the wasm syntax probe compiles the' \
test-linuxcnc-wasm-source-syntax.sh >/dev/null
grep -F "printf 'CONFIGURE_RULE_VERSION=1\n'" \
test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
grep -F 'cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe cnc_sim_wasm_runtime_probe --parallel "$build_jobs"' \
test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
if grep -F 'printf '\''BUILD_JOBS=%s\n'\'' "$build_jobs"' build-wasm.sh >/dev/null; then
echo "build-wasm configure signature must not depend on CNC_SIM_BUILD_JOBS" >&2
exit 1
fi
for hot_signature_script in \
build-linuxcnc-wasm-link-probe.sh \
build-linuxcnc-wasm-probe-objects.sh \
build-linuxcnc-wasm-standalone-probe.sh \
build-linuxcnc-native-bridge-objects.sh \
test-linuxcnc-api-native.sh \
test-linuxcnc-bridge-native.sh \
test-linuxcnc-rs274-native.sh \
test-linuxcnc-source-link.sh \
test-linuxcnc-source-objects.sh \
test-linuxcnc-source-syntax.sh \
test-native.sh; do
if grep -F "sha256sum $hot_signature_script" "$hot_signature_script" >/dev/null; then
echo "$hot_signature_script hot object signature must use BUILD_RULE_VERSION instead of the whole script hash" >&2
exit 1
fi
done
python3 - <<'PY'
from pathlib import Path
import subprocess
source_map = Path("docs/linuxcnc-build-source-map.md").read_text(encoding="utf-8")
wasm_manifest = Path("linuxcnc-rs274-wasm-source-files.txt")
core_count = len(subprocess.check_output(
["./list-linuxcnc-wasm-manifest-sources.sh", str(wasm_manifest), "core"],
text=True,
).splitlines())
blocked_count = len(subprocess.check_output(
["./list-linuxcnc-wasm-manifest-sources.sh", str(wasm_manifest), "blocked"],
text=True,
).splitlines())
if core_count != 26 or blocked_count != 9:
raise SystemExit(f"unexpected default WASM manifest partition: core={core_count} blocked={blocked_count}")
for entry in [
"test-native.sh",
"test-linuxcnc-source-link.sh",
"test-linuxcnc-source-syntax.sh",
"test-linuxcnc-wasm-cmake-safe-probe.sh",
"build-wasm.sh",
]:
if f"| `{entry}` |" not in source_map:
raise SystemExit(f"docs/linuxcnc-build-source-map.md missing build entry: {entry}")
for phrase in [
"must not add CNC",
"must not expand the temporary smoke parser",
"CNC_SIM_BUILD_JOBS",
"BUILD_RULE_VERSION",
"CONFIGURE_RULE_VERSION",
"CNC_SIM_ENABLE_SMOKE_BACKEND=OFF",
"Node smoke before browser smoke",
"does not interpret",
"G-code",
]:
if phrase not in source_map:
raise SystemExit(f"docs/linuxcnc-build-source-map.md missing boundary phrase: {phrase}")
build_wasm = Path("build-wasm.sh").read_text(encoding="utf-8").splitlines()
positions = {}
for index, line in enumerate(build_wasm, start=1):
if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe"' in line:
positions["parallel_start"] = index
if "wait_parallel_wasm_probes" in line and "parallel_start" in positions and "wait" not in positions:
positions["wait"] = index
if "LinuxCNC wasm rs274ngc_pre link blocker scan" in line:
positions["pre_link"] = index
if "for required_tool in cmake emcmake emcc node; do" in line:
positions["tools"] = index
if "emcmake cmake -S core -B build/wasm" in line:
positions["configure"] = index
if "cmake --build build/wasm --target cnc_sim_wasm_runtime_probe cnc_sim_wasm" in line:
positions["build"] = index
if "cp build/wasm/cnc_sim.js web/public/cnc_sim.js" in line:
positions["cp_js"] = index
if "cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm" in line:
positions["cp_wasm"] = index
if "cmp -s build/wasm/cnc_sim.js web/public/cnc_sim.js" in line:
positions["cmp_js"] = index
if "cmp -s build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm" in line:
positions["cmp_wasm"] = index
if "./test-web-wasm-node-smoke.sh" in line:
positions["node"] = index
if "./test-web-wasm-browser-smoke.sh" in line:
positions["browser"] = index
required = [
"parallel_start", "wait", "pre_link", "tools", "configure", "build",
"cp_js", "cp_wasm", "cmp_js", "cmp_wasm", "node", "browser",
]
missing = [name for name in required if name not in positions]
if missing:
raise SystemExit(f"build-wasm.sh missing ordered build anchors: {', '.join(missing)}")
if not (positions["parallel_start"] < positions["wait"] < positions["pre_link"]):
raise SystemExit("build-wasm.sh must wait for parallel probes before rs274ngc_pre link checks")
if not (positions["tools"] < positions["configure"] < positions["build"]):
raise SystemExit("build-wasm.sh must check tools, then configure, then build")
if not (positions["cp_js"] < positions["cmp_js"] < positions["node"] < positions["browser"]):
raise SystemExit("build-wasm.js artifact JS copy/compare or smoke ordering changed")
if not (positions["cp_wasm"] < positions["cmp_wasm"] < positions["node"] < positions["browser"]):
raise SystemExit("build-wasm.wasm artifact copy/compare or smoke ordering changed")
PY

136
check-linuxcnc-inputs-cached.sh Executable file
View File

@@ -0,0 +1,136 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: the cached input checker keys check-linuxcnc-inputs.sh
# results by manifest contents and LinuxCNC source mtimes so source-backed
# rs274/kinematics coverage is not silently reused after upstream changes.
cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-}
check_args=("$@")
if [[ "${1:-}" == "--cache-dir" ]]; then
if [[ "$#" -lt 2 ]]; then
echo "missing --cache-dir value" >&2
exit 1
fi
cache_dir=$2
shift 2
check_args=("$@")
fi
manifest=${1:-}
require_complete=${2:-}
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
if [[ -z "$cache_dir" ]]; then
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "${check_args[@]}"
exit 0
fi
if [[ "$cache_dir" =~ [[:space:]] ]]; then
echo "CNC_SIM_PREFLIGHT_CACHE_DIR must not contain whitespace: $cache_dir" >&2
exit 1
fi
mkdir -p "$cache_dir"
cache_dir=$(cd "$cache_dir" && pwd)
if [[ "$cache_dir" == "/" ]]; then
echo "CNC_SIM_PREFLIGHT_CACHE_DIR must not be the filesystem root" >&2
exit 1
fi
exec 8>"$cache_dir/check-linuxcnc-inputs.lock"
flock 8
if [[ ! -d "$linuxcnc_root" ]]; then
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "${check_args[@]}"
exit 0
fi
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
manifest_key=ROOT_ONLY
manifest_path=
if [[ -n "$manifest" && "$manifest" != "--root-only" ]]; then
if [[ ! -f "$manifest" ]]; then
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "${check_args[@]}"
exit 0
fi
manifest_path=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")
manifest_key=$(printf '%s' "$manifest_path" | sha256sum | awk '{ print $1 }')
fi
safe_require=${require_complete:-none}
safe_require=${safe_require//[^A-Za-z0-9_.-]/_}
args_key=$(
{
printf 'ARGS='
printf ' %q' "${check_args[@]}"
printf '\n'
} | sha256sum | awk '{ print $1 }'
)
cache_file="$cache_dir/check-linuxcnc-inputs.${manifest_key}.${safe_require}.${args_key}.signature"
next_signature=$(mktemp "$cache_file.next.XXXXXX")
write_source_stat_signature() {
local source_path
[[ -z "$manifest_path" ]] && return 0
while IFS=: read -r group path note extra; do
case "$group" in
""|\#*)
continue
;;
esac
[[ -z "${path:-}" ]] && continue
source_path="$linuxcnc_root/$path"
if [[ ! -f "$source_path" ]]; then
return 1
fi
stat -c 'SOURCE=%n:%s:%y' "$source_path"
done < "$manifest_path"
}
write_complete_mode_signature() {
case "$require_complete" in
--require-rs274-complete)
find "$linuxcnc_root/src/emc/rs274ngc" -maxdepth 1 -type f \
-printf 'RS274=%P:%s:%T@\n' | sort
;;
--require-kinematics-complete)
for source_dir in \
"$linuxcnc_root/src/emc/kinematics" \
"$linuxcnc_root/src/libnml/posemath" \
"$linuxcnc_root/configs/sim"; do
[[ -d "$source_dir" ]] || return 1
find "$source_dir" -type f -printf "KIN=$source_dir/%P:%s:%T@\n" | sort
done
;;
esac
}
if ! {
printf 'CACHED_SCRIPT='
sha256sum check-linuxcnc-inputs-cached.sh
printf 'CHECK_SCRIPT='
sha256sum check-linuxcnc-inputs.sh
printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root"
printf 'ARGS='
printf ' %q' "${check_args[@]}"
printf '\n'
if [[ -n "$manifest_path" ]]; then
printf 'MANIFEST_PATH=%s\n' "$manifest_path"
printf 'MANIFEST='
sha256sum "$manifest_path"
write_source_stat_signature
fi
write_complete_mode_signature
} > "$next_signature"; then
rm -f "$next_signature"
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "${check_args[@]}"
exit 0
fi
if [[ -f "$cache_file" ]] && cmp -s "$cache_file" "$next_signature"; then
rm -f "$next_signature"
exit 0
fi
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "${check_args[@]}"
mv "$next_signature" "$cache_file"

1443
check-linuxcnc-inputs.sh Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,97 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: this guard keeps the kinematics source map aligned
# with linuxcnc-kinematics-source-files.txt, which enumerates LinuxCNC
# kinematics, posemath, switchkins INI/HAL/remap/tooldata/assets, and adjacent
# build metadata. It validates source coverage documentation only; it does not
# add CNC behavior, expand smoke parsing, or interpret G-code.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
grep -F 'GENSERKINSSRCS := emc/kinematics/ugenserkins.c' \
"$linuxcnc_root/src/emc/kinematics/Submakefile" >/dev/null
grep -F 'extern int kinematicsInverse(const struct EmcPose * world,' \
"$linuxcnc_root/src/emc/kinematics/kinematics.h" >/dev/null
grep -F 'extern int kinematicsSwitch(int switchkins_type);' \
"$linuxcnc_root/src/emc/kinematics/kinematics.h" >/dev/null
grep -F 'motion.switchkins-type' \
"$linuxcnc_root/configs/sim/axis/vismach/5axis/bridgemill/5axis.ini" >/dev/null
grep -F '#<kinstype>' \
"$linuxcnc_root/configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc" >/dev/null
python3 - <<'PY'
from pathlib import Path
import re
manifest = Path("linuxcnc-kinematics-source-files.txt").read_text(encoding="utf-8").splitlines()
source_map = Path("docs/linuxcnc-kinematics-source-map.md").read_text(encoding="utf-8")
expected_groups = [
"core",
"config",
"remap",
"component",
"generated",
"header",
"metadata",
"asset",
"tooldata",
]
counts = {group: 0 for group in expected_groups}
for line in manifest:
if not line or line.startswith("#"):
continue
group = line.split(":", 1)[0]
if group not in counts:
raise SystemExit(f"unexpected kinematics manifest group in source map guard: {group}")
counts[group] += 1
for group, count in counts.items():
pattern = rf"\| `{re.escape(group)}` \| {count} \|"
if not re.search(pattern, source_map):
raise SystemExit(
f"docs/linuxcnc-kinematics-source-map.md {group} count no longer matches linuxcnc-kinematics-source-files.txt"
)
for group in expected_groups:
if f"`{group}`" not in source_map:
raise SystemExit(
f"docs/linuxcnc-kinematics-source-map.md is missing kinematics manifest group {group}"
)
required_phrases = [
"LinuxCNC Source Anchors",
"must not add CNC behavior",
"must not",
"expand the temporary smoke parser",
"`src/emc/kinematics/Submakefile`",
"`src/emc/kinematics/kinematics.h`",
"motion.switchkins-type",
"`#<kinstype>` remap assignment",
"not browser API surface",
"OPFS-only",
"derive M428/M429/M430 cases from",
"RTCP and five-axis coverage",
"./check-linuxcnc-kinematics-source-map.sh",
"./check-linuxcnc-inputs.sh linuxcnc-kinematics-source-files.txt --require-kinematics-complete",
"./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt",
"does not interpret G-code",
]
for phrase in required_phrases:
if phrase not in source_map:
raise SystemExit(
f"docs/linuxcnc-kinematics-source-map.md is missing source-coverage boundary phrase: {phrase}"
)
for entry in [
"test-native.sh",
"test-linuxcnc-source-link.sh",
"build-wasm.sh",
]:
text = Path(entry).read_text(encoding="utf-8")
if "./check-linuxcnc-kinematics-source-map.sh" not in text:
raise SystemExit(f"{entry} must run ./check-linuxcnc-kinematics-source-map.sh")
PY

182
check-linuxcnc-opfs-source-map.sh Executable file
View File

@@ -0,0 +1,182 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: this guard ties browser OPFS parameter persistence to
# interp_internal.hh defaults and rs274ngc_pre.cc restore_parameters() /
# save_parameters() file semantics. It validates bridge policy only; it does not
# add CNC behavior, expand smoke parsing, or interpret G-code.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
grep -F '#define RS274NGC_PARAMETER_FILE_NAME_DEFAULT "rs274ngc.var"' \
"$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null
grep -F '#define RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX ".bak"' \
"$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null
grep -F 'int Interp::restore_parameters(const char *filename)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'int Interp::open(const char *filename)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F '_setup.file_pointer = fopen(filename, "r");' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'read_text(command, _setup.file_pointer, _setup.linetext,' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'int Interp::save_parameters(const char *filename,' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'std::string tempfile = std::string(filename) + ".new";' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F '+ RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX;' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'unlink(bakfile.c_str());' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'if(link(filename, bakfile.c_str()) < 0)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'if(rename(tempfile.c_str(), filename) < 0)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'RS274NGC_PARAMETER_FILE_NAME_DEFAULT` as `rs274ngc.var`' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F 'RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX` as `.bak`' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '`Interp::restore_parameters()` for parameter-file reads' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '`Interp::open()` by opening' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '`fopen(filename, "r")`, then `Interp::_read()`' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '`read_text()`' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '`Interp::save_parameters()` for `filename + ".new"`' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F 'unlinks `filename + ".bak"`, links the' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F 'must not add independent filesystem behavior or expand the temporary smoke parser' \
docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F 'parseFileWithParameterFile()' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F 'OPFS Coverage' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/src/wasm-core.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/src/wasm-core.d.ts` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/src/app.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-opfs-basic-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-opfs-parameter-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-opfs-workspace-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-opfs-mirror-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-opfs-directory-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-opfs-policy-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F '| `web/test-browser-wasm-smoke-app-sections.js` |' docs/linuxcnc-opfs-source-map.md >/dev/null
grep -F 'const LINUXCNC_DEFAULT_PARAMETER_FILE = "rs274ngc.var";' web/src/wasm-core.js >/dev/null
grep -F 'async parseFile(path, dialect = "linuxcnc", options = {})' web/src/wasm-core.js >/dev/null
grep -F 'OPFS workspace is not available for program file parsing' web/src/wasm-core.js >/dev/null
grep -F 'const program = new TextDecoder().decode(await opfs.readFile(path));' web/src/wasm-core.js >/dev/null
grep -F 'async parseFileWithParameterFile(path, parameterPath, dialect = "linuxcnc", options = {})' \
web/src/wasm-core.js >/dev/null
grep -F 'OPFS workspace is not available for program and parameter file parsing' web/src/wasm-core.js >/dev/null
grep -F '`${LINUXCNC_DEFAULT_PARAMETER_FILE}.new`' web/src/wasm-core.js >/dev/null
grep -F '`${LINUXCNC_DEFAULT_PARAMETER_FILE}.bak`' web/src/wasm-core.js >/dev/null
grep -F 'LinuxCNC source basis: rs274ngc_pre.cc restore_parameters() reads the' web/src/wasm-core.js >/dev/null
grep -F 'before replacing the main file and managing filename + ".bak".' web/src/wasm-core.js >/dev/null
grep -F 'app frame did not clean up LinuxCNC temporary parameter file in OPFS' \
web/test-browser-wasm-smoke-app-sections.js >/dev/null
grep -F 'OPFS cannot be disabled in browser contexts with OPFS support' web/src/wasm-core.js >/dev/null
grep -F 'OPFS path must stay inside the CNC workspace' web/src/wasm-core.js >/dev/null
grep -F 'OPFS mount point must stay inside the Emscripten filesystem' web/src/wasm-core.js >/dev/null
grep -F 'async persistDirectory(path)' web/src/wasm-core.js >/dev/null
grep -F 'async readDirectory(path)' web/src/wasm-core.js >/dev/null
grep -F 'export type WasmOpfsWorkspace = {' web/src/wasm-core.d.ts >/dev/null
grep -F 'readDirectory(path: string): Promise<string[]>;' web/src/wasm-core.d.ts >/dev/null
grep -F 'persistDirectory(path: string): Promise<string[]>;' web/src/wasm-core.d.ts >/dev/null
grep -F 'loadParameterFile(path: string): Promise<Uint8Array | null>;' web/src/wasm-core.d.ts >/dev/null
grep -F 'persistParameterFile(path: string): Promise<Uint8Array>;' web/src/wasm-core.d.ts >/dev/null
grep -F 'opfs?: false | {' web/src/wasm-core.d.ts >/dev/null
grep -F 'mountPoint?: string;' web/src/wasm-core.d.ts >/dev/null
grep -F 'workspacePath?: string;' web/src/wasm-core.d.ts >/dev/null
grep -F 'const events = await simulator.parseFileWithParameterFile(programPath, parameterPath, "linuxcnc", parseOptions);' \
web/src/app.js >/dev/null
grep -F 'OPFS workspace is required for browser program parsing' web/src/app.js >/dev/null
grep -F 'runBrowserOpfsBasicSections(context, simulator)' \
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
grep -F 'runBrowserOpfsParameterSections(context, simulator)' \
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
grep -F 'runBrowserOpfsMirrorSections(context, simulator)' \
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
grep -F 'runBrowserOpfsDirectorySections(context, simulator)' \
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
grep -F 'runBrowserOpfsPolicySections(context)' web/test-browser-wasm-smoke-sections.js >/dev/null
grep -F 'runSection("opfs directory persist and load"' \
web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
grep -F 'runSection("opfs metadata queries avoid wasm mirror pollution"' \
web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
grep -F 'runSection("opfs copy and move mirror files"' \
web/test-browser-wasm-smoke-opfs-mirror-sections.js >/dev/null
grep -F 'runSection("browser opfs policy"' \
web/test-browser-wasm-smoke-opfs-policy-sections.js >/dev/null
grep -F 'runSection("opfs parameter restore in new instance"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("opfs invalid parameter restore failures"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("opfs stale parameter state cleanup"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("opfs program parse and restore"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("browser app opfs restore"' test-web-wasm-browser-smoke.sh >/dev/null
if grep -R -n -E 'localStorage|sessionStorage|indexedDB|showOpenFilePicker|showSaveFilePicker|showDirectoryPicker|webkitRequestFileSystem|FileReader' web/src; then
echo "browser app/wasm code must not add filesystem persistence outside OPFS" >&2
exit 1
fi
if grep -n -E 'localStorage|sessionStorage|indexedDB|showOpenFilePicker|showSaveFilePicker|showDirectoryPicker|webkitRequestFileSystem|FileReader' \
web/test-browser-wasm-smoke-*.js \
web/test-browser-wasm-smoke.html \
test-web-wasm-browser-smoke.sh; then
echo "browser OPFS smoke harness must not add filesystem persistence outside OPFS" >&2
exit 1
fi
python3 - <<'PY'
from pathlib import Path
source_map = Path("docs/linuxcnc-opfs-source-map.md").read_text(encoding="utf-8")
coverage_entries = [
"web/src/wasm-core.js",
"web/src/wasm-core.d.ts",
"web/src/app.js",
"web/test-browser-wasm-smoke-opfs-basic-sections.js",
"web/test-browser-wasm-smoke-opfs-parameter-sections.js",
"web/test-browser-wasm-smoke-opfs-workspace-sections.js",
"web/test-browser-wasm-smoke-opfs-mirror-sections.js",
"web/test-browser-wasm-smoke-opfs-directory-sections.js",
"web/test-browser-wasm-smoke-opfs-policy-sections.js",
"web/test-browser-wasm-smoke-app-sections.js",
]
for entry in coverage_entries:
if f"| `{entry}` |" not in source_map:
raise SystemExit(f"docs/linuxcnc-opfs-source-map.md missing OPFS coverage entry: {entry}")
required_phrases = [
"programs/current.ngc",
"parameters/rs274ngc.var",
'fopen(filename, "r")',
"LinuxCNC `read_text()`",
"program reads and parameter restore/save bridge files under the OPFS workspace",
"`.new`, `.bak`, failure, and stale-state semantics",
'unlinks `filename + ".bak"`',
"OPFS/WASM mirror persistence",
"OPFS directory persistence and WASM mirror refresh",
"OPFS workspace and mount policy",
"Browser OPFS smoke harness files must also stay free of Local Storage",
"they may only verify OPFS-backed browser persistence",
"OPFS-backed workspace, directory, parameter-file, and mount/workspace option types",
"app-level OPFS program, parameter, backup, and reload behavior",
"./check-linuxcnc-opfs-source-map.sh",
"does not interpret G-code",
]
for phrase in required_phrases:
if phrase not in source_map:
raise SystemExit(f"docs/linuxcnc-opfs-source-map.md missing OPFS coverage phrase: {phrase}")
for entry in [
"test-native.sh",
"test-linuxcnc-source-link.sh",
"build-wasm.sh",
]:
text = Path(entry).read_text(encoding="utf-8")
if "./check-linuxcnc-opfs-source-map.sh" not in text:
raise SystemExit(f"{entry} must run ./check-linuxcnc-opfs-source-map.sh")
PY

View File

@@ -0,0 +1,102 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: this guard keeps the local source map aligned with the
# LinuxCNC-root relative rs274 manifests that drive native and WASM source-link
# probes. It validates documentation/manifest drift only; it does not add CNC
# behavior, expand smoke parsing, or interpret G-code.
python3 - <<'PY'
from pathlib import Path
source_map = Path("docs/linuxcnc-rs274-source-map.md").read_text(encoding="utf-8")
native_manifest = Path("linuxcnc-rs274-source-files.txt").read_text(encoding="utf-8").splitlines()
wasm_manifest = Path("linuxcnc-rs274-wasm-source-files.txt").read_text(encoding="utf-8").splitlines()
expected = {
"### Interpreter core": [
Path(line.split(":", 2)[1]).name
for line in native_manifest
if line.startswith("core:")
],
"### Python binding modules": [
Path(line.split(":", 2)[1]).name
for line in native_manifest
if line.startswith("binding:")
],
"### WASM-safe source core": [
line.split(":", 2)[1]
for line in wasm_manifest
if line.startswith("core:")
],
"### WASM-blocked sources": [
line.split(":", 2)[1]
for line in wasm_manifest
if line.startswith("blocked:")
],
"### WASM-tracked headers": [
line.split(":", 2)[1]
for line in wasm_manifest
if line.startswith("header:")
],
"### WASM-blocked headers": [
line.split(":", 2)[1]
for line in wasm_manifest
if line.startswith("blocked-header:")
],
"### WASM metadata sources": [
line.split(":", 2)[1]
for line in wasm_manifest
if line.startswith("metadata:")
],
}
actual = {section: [] for section in expected}
active_section = None
for line in source_map.splitlines():
if line in expected:
active_section = line
continue
if active_section and line.startswith("### "):
active_section = None
if active_section and line.startswith("- `"):
actual[active_section].append(line.split("`", 2)[1])
labels = {
"### Interpreter core": "interpreter core",
"### Python binding modules": "Python binding module",
"### WASM-safe source core": "WASM-safe source core",
"### WASM-blocked sources": "WASM-blocked source",
"### WASM-tracked headers": "WASM-tracked header",
"### WASM-blocked headers": "WASM-blocked header",
"### WASM metadata sources": "WASM metadata source",
}
for section, expected_entries in expected.items():
if actual[section] != expected_entries:
raise SystemExit(
f"docs/linuxcnc-rs274-source-map.md {labels[section]} list no longer matches source manifest"
)
for phrase in [
"must not add CNC behavior",
"must not expand the temporary smoke parser",
"source coverage only",
"must not become browser API fields",
"temporary smoke parser fallback behavior",
"Browser-hostile dependencies must remain tracked blockers",
"./check-linuxcnc-rs274-source-map.sh",
"does not interpret G-code",
]:
if phrase not in source_map:
raise SystemExit(f"docs/linuxcnc-rs274-source-map.md missing boundary phrase: {phrase}")
for entry in [
"test-native.sh",
"test-linuxcnc-source-link.sh",
"build-wasm.sh",
]:
text = Path(entry).read_text(encoding="utf-8")
if "./check-linuxcnc-rs274-source-map.sh" not in text:
raise SystemExit(f"{entry} must run ./check-linuxcnc-rs274-source-map.sh")
PY

View File

@@ -0,0 +1,107 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: this guard ties generated switchkins/remap aliases and
# config cases to LinuxCNC configs/sim INI REMAP entries, remap_subs
# #<kinstype> assignments, and halcmd loadusr parsing sources. It validates
# generator/table policy only; it does not add CNC behavior, expand smoke
# parsing, or interpret G-code.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
grep -F 'LinuxCNC source basis: generated switchkins/remap tables are extracted from' \
generate-linuxcnc-switchkins-remap-table.sh >/dev/null
grep -F 'src/hal/utils/halcmd_commands.cc do_loadusr_cmd()' \
generate-linuxcnc-switchkins-remap-table.sh >/dev/null
grep -F 'getopt("+wWin:")' generate-linuxcnc-switchkins-remap-table.sh >/dev/null
grep -F 'src/hal/utils/halcmd_completion.c lists the common -W/-Wn/-w/-iw forms' \
generate-linuxcnc-switchkins-remap-table.sh >/dev/null
grep -F 'LinuxCNC TOOL_TABLE source is not listed as tooldata' \
generate-linuxcnc-switchkins-remap-table.sh >/dev/null
grep -F 'LinuxCNC source basis: switchkins/remap table validation derives M428/M429/M430' \
check-linuxcnc-switchkins-remap-table.sh >/dev/null
grep -F 'validate_tool_table_source_coverage_only' \
check-linuxcnc-switchkins-remap-table.sh >/dev/null
grep -F 'exposes TOOL_TABLE through switchkins API/web bridge' \
check-linuxcnc-switchkins-remap-table.sh >/dev/null
grep -F 'int do_loadusr_cmd(const char *args[])' "$linuxcnc_root/src/hal/utils/halcmd_commands.cc" >/dev/null
grep -F 'getopt(argc,' "$linuxcnc_root/src/hal/utils/halcmd_commands.cc" >/dev/null
grep -F '"+wWin:"' "$linuxcnc_root/src/hal/utils/halcmd_commands.cc" >/dev/null
grep -F -- '-Wn' "$linuxcnc_root/src/hal/utils/halcmd_completion.c" >/dev/null
python3 - "$linuxcnc_root" <<'PY'
from pathlib import Path
import json
import re
import sys
linuxcnc_root = Path(sys.argv[1])
source_map = Path("docs/linuxcnc-switchkins-remap-source-map.md").read_text(encoding="utf-8")
table = Path("core/src/linuxcnc_switchkins_remap_table.inc").read_text(encoding="utf-8")
cpp_cases = Path("core/tests/linuxcnc_switchkins_remap_config_cases.inc").read_text(encoding="utf-8")
json_cases = json.loads(Path("web/public/linuxcnc_switchkins_remap_config_cases.json").read_text(encoding="utf-8"))
counts = {
"core/src/linuxcnc_switchkins_remap_table.inc": len(re.findall(r'^\s*\{"', table, re.M)),
"core/tests/linuxcnc_switchkins_remap_config_cases.inc": len(re.findall(r'^\s*\{"', cpp_cases, re.M)),
"web/public/linuxcnc_switchkins_remap_config_cases.json": len(json_cases),
}
for path, count in counts.items():
if f"| `{path}` | {count} |" not in source_map:
raise SystemExit(f"docs/linuxcnc-switchkins-remap-source-map.md count mismatch for {path}")
remap_sources = [
path
for path in (linuxcnc_root / "configs/sim").rglob("*remap.ngc")
if path.name in {"428remap.ngc", "429remap.ngc", "430remap.ngc"}
]
remap_ini_re = re.compile(r"REMAP\s*=\s*M(428|429|430)(?:\D|$)", re.I)
remap_inis = [
path
for path in (linuxcnc_root / "configs/sim").rglob("*.ini")
if remap_ini_re.search(path.read_text(encoding="utf-8", errors="ignore"))
]
if f"| LinuxCNC M428/M429/M430 remap sources | {len(remap_sources)} |" not in source_map:
raise SystemExit("docs/linuxcnc-switchkins-remap-source-map.md remap source count mismatch")
if f"| LinuxCNC switchkins REMAP INI sources | {len(remap_inis)} |" not in source_map:
raise SystemExit("docs/linuxcnc-switchkins-remap-source-map.md REMAP INI source count mismatch")
required_phrases = [
"must not add CNC behavior",
"must not expand the temporary smoke parser",
"`#<kinstype>` assignments",
"[EMCIO] TOOL_TABLE` entries are source coverage only",
"must not become generated switchkins aliases",
"./check-linuxcnc-switchkins-remap-source-map.sh",
"./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt",
"does not interpret G-code",
]
for phrase in required_phrases:
if phrase not in source_map:
raise SystemExit(
f"docs/linuxcnc-switchkins-remap-source-map.md is missing boundary phrase: {phrase}"
)
for entry in [
"test-native.sh",
"test-linuxcnc-source-link.sh",
"build-wasm.sh",
]:
text = Path(entry).read_text(encoding="utf-8")
if "./check-linuxcnc-switchkins-remap-source-map.sh" not in text:
raise SystemExit(f"{entry} must run ./check-linuxcnc-switchkins-remap-source-map.sh")
case_fields = {case["field"] for case in json_cases}
source_only_field_re = re.compile(r"asset|metadata|tool|tooldata|tool_table|TOOL_TABLE", re.I)
bad_fields = [field for field in case_fields if source_only_field_re.search(field)]
if bad_fields:
raise SystemExit(f"generated switchkins config cases expose source-only fields: {bad_fields}")
api_surface_re = re.compile(r'\b(?:asset|metadata|toolData|tooldata|toolTable|tooltable|tool_table|TOOL_TABLE)\b')
for api_path in ["web/src/wasm-core.js", "web/src/index.ts", "core/include/cnc_sim_api.h"]:
if api_surface_re.search(Path(api_path).read_text(encoding="utf-8")):
raise SystemExit(f"{api_path} exposes source-only switchkins inputs through API surface")
PY

View File

@@ -0,0 +1,123 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: cached switchkins/remap validation hashes the LinuxCNC
# kinematics manifest, referenced configs/remaps, generator script, and bridge
# outputs before reusing table-check results.
cache_dir=${CNC_SIM_PREFLIGHT_CACHE_DIR:-}
check_args=("$@")
if [[ "${1:-}" == "--cache-dir" ]]; then
if [[ "$#" -lt 2 ]]; then
echo "missing --cache-dir value" >&2
exit 1
fi
cache_dir=$2
shift 2
check_args=("$@")
fi
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
manifest=${1:-linuxcnc-kinematics-source-files.txt}
if [[ -z "$cache_dir" ]]; then
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table.sh "${check_args[@]}"
exit 0
fi
if [[ "$cache_dir" =~ [[:space:]] ]]; then
echo "CNC_SIM_PREFLIGHT_CACHE_DIR must not contain whitespace: $cache_dir" >&2
exit 1
fi
mkdir -p "$cache_dir"
cache_dir=$(cd "$cache_dir" && pwd)
if [[ "$cache_dir" == "/" ]]; then
echo "CNC_SIM_PREFLIGHT_CACHE_DIR must not be the filesystem root" >&2
exit 1
fi
exec 8>"$cache_dir/check-linuxcnc-switchkins-remap-table.lock"
flock 8
if [[ ! -d "$linuxcnc_root" || ! -f "$manifest" ]]; then
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table.sh "${check_args[@]}"
exit 0
fi
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
manifest_path=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")
manifest_key=$(printf '%s' "$manifest_path" | sha256sum | awk '{ print $1 }')
args_key=$(
{
printf 'ARGS='
printf ' %q' "${check_args[@]}"
printf '\n'
} | sha256sum | awk '{ print $1 }'
)
cache_file="$cache_dir/check-linuxcnc-switchkins-remap-table.${manifest_key}.${args_key}.signature"
next_signature=$(mktemp "$cache_file.next.XXXXXX")
write_manifest_source_stats() {
local source_path
while IFS=: read -r group path note extra; do
case "$group" in
""|\#*)
continue
;;
esac
[[ -z "${path:-}" ]] && continue
source_path="$linuxcnc_root/$path"
if [[ ! -f "$source_path" ]]; then
return 1
fi
stat -c 'SOURCE=%n:%s:%y' "$source_path"
done < "$manifest_path"
}
write_project_output_hashes() {
local project_output
for project_output in \
core/src/cnc_sim_api.cpp \
core/src/linuxcnc_switchkins_remap_table.inc \
core/tests/linuxcnc_switchkins_remap_config_cases.inc \
web/public/linuxcnc_switchkins_remap_config_cases.json \
web/src/index.ts \
web/src/wasm-core.js; do
[[ -f "$project_output" ]] || return 1
sha256sum "$project_output"
done
}
if ! {
printf 'CACHED_SCRIPT='
sha256sum check-linuxcnc-switchkins-remap-table-cached.sh
printf 'CHECK_SCRIPT='
sha256sum check-linuxcnc-switchkins-remap-table.sh
printf 'INPUT_CACHED_SCRIPT='
sha256sum check-linuxcnc-inputs-cached.sh
printf 'INPUT_CHECK_SCRIPT='
sha256sum check-linuxcnc-inputs.sh
printf 'GENERATOR_SCRIPT='
sha256sum generate-linuxcnc-switchkins-remap-table.sh
printf 'LINUXCNC_ROOT=%s\n' "$linuxcnc_root"
printf 'ARGS='
printf ' %q' "${check_args[@]}"
printf '\n'
printf 'MANIFEST_PATH=%s\n' "$manifest_path"
printf 'MANIFEST='
sha256sum "$manifest_path"
write_manifest_source_stats
printf 'PROJECT_OUTPUTS:\n'
write_project_output_hashes
} > "$next_signature"; then
rm -f "$next_signature"
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table.sh "${check_args[@]}"
exit 0
fi
if [[ -f "$cache_file" ]] && cmp -s "$cache_file" "$next_signature"; then
rm -f "$next_signature"
exit 0
fi
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table.sh "${check_args[@]}"
mv "$next_signature" "$cache_file"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,333 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: this guard ties LinuxCNC tooldata/Submakefile,
# src/emc/tooldata sources, native/WASM manifests, and wasm-safe tooldata shims
# to the tooldata source map. It validates source coverage only; it does not
# add CNC behavior, expand smoke parsing, or interpret G-code.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_nml.cc' \
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_mmap.cc' \
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_common.cc' \
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_db.cc' \
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
grep -F 'TOOL_MMAP_READ_SRCS = emc/tooldata/tool_mmap_read.cc' \
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
grep -F 'TOOL_WATCH_SRCS = emc/tooldata/tool_watch.cc' \
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
grep -F 'int main (int argc,char**argv)' \
"$linuxcnc_root/src/emc/tooldata/tool_mmap_read.cc" >/dev/null
grep -F 'if (tool_mmap_user())' \
"$linuxcnc_root/src/emc/tooldata/tool_mmap_read.cc" >/dev/null
grep -F 'idx_last = tooldata_last_index_get();' \
"$linuxcnc_root/src/emc/tooldata/tool_mmap_read.cc" >/dev/null
grep -F 'if (tooldata_get(&tdata,idx) != IDX_OK)' \
"$linuxcnc_root/src/emc/tooldata/tool_mmap_read.cc" >/dev/null
grep -F 'RCS_STAT_CHANNEL *stat = 0;' \
"$linuxcnc_root/src/emc/tooldata/tool_watch.cc" >/dev/null
grep -F 'stat = new RCS_STAT_CHANNEL(emcFormat, "emcStatus", "xemc", nmlfile);' \
"$linuxcnc_root/src/emc/tooldata/tool_watch.cc" >/dev/null
grep -F 'EMC_STAT *emcStatus = static_cast<EMC_STAT*>(stat->get_address());' \
"$linuxcnc_root/src/emc/tooldata/tool_watch.cc" >/dev/null
grep -F 'emcStatus->io.tool.pocketPrepped' \
"$linuxcnc_root/src/emc/tooldata/tool_watch.cc" >/dev/null
grep -F '#define TOOL_MMAP_FILENAME ".tool.mmap"' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'bool tool_mmap_is_random_toolchanger(void)' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'ans = hptr->is_random_toolchanger;' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'int tool_mmap_creator(EMC_TOOL_STAT const * ptr,int random_toolchanger)' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'static int inited=0;' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'if (inited) {' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'inited = 1;' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'hptr->is_random_toolchanger = random_toolchanger;' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'hptr->last_index = 0;' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'int tool_mmap_user()' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'fprintf(stderr,"tool_mmap_user(): tool mmap not available\n");' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'return(-1);' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'void tool_mmap_close()' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'if (!tool_mmap_base) { return; }' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'int tooldata_last_index_get(void)' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'tool_mmap_mutex_give(); return -1;' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'int tooldata_find_index_for_tool(int toolno)' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'if (toolno == -1) {tool_mmap_mutex_give(); return -1;}' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'if (!hptr->is_random_toolchanger && toolno == 0)' \
"$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'int tool_nml_register(CANON_TOOL_TABLE *tblptr)' \
"$linuxcnc_root/src/emc/tooldata/tooldata_nml.cc" >/dev/null
grep -F 'int tooldata_db_init(char progname_plus_args[]' \
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
grep -F '#define MAX_DB_PROGRAM_ARGS 10' \
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
grep -F 'char* token = strtok_r(progname_plus_args, " ", &saveptr);' \
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
grep -F 'if (child_argc >= MAX_DB_PROGRAM_ARGS)' \
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
grep -F 'if (access(child_argv[0],X_OK))' \
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
grep -F 'int tooldata_db_getall() {' \
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
grep -F 'if (!db_live) {return -1;}' \
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
grep -F 'int tooldata_db_notify(tool_notify_t ntype,' \
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
grep -F 'if (!db_live) return 0;' \
"$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
grep -F 'int tooldata_read_entry(const char *input_line)' \
"$linuxcnc_root/src/emc/tooldata/tooldata_common.cc" >/dev/null
grep -F 'LinuxCNC tooldata source map' docs/linuxcnc-tooldata-source-map.md >/dev/null
grep -F 'must not add CNC behavior' docs/linuxcnc-tooldata-source-map.md >/dev/null
grep -F 'must not expand the temporary smoke parser' docs/linuxcnc-tooldata-source-map.md >/dev/null
grep -F '`src/emc/tooldata/Submakefile` selects `tooldata_mmap.cc`' \
docs/linuxcnc-tooldata-source-map.md >/dev/null
grep -F 'Tool table files referenced by switchkins INI files are source coverage only' \
docs/linuxcnc-tooldata-source-map.md >/dev/null
grep -F '`src/emc/tooldata/tool_mmap_read.cc` is the mmap tool table reader utility' \
docs/linuxcnc-tooldata-source-map.md >/dev/null
grep -F '`tool_mmap_user()`, `tooldata_last_index_get()`, and `tooldata_get()`' \
docs/linuxcnc-tooldata-source-map.md >/dev/null
grep -F "single-creator process boundary from" docs/linuxcnc-tooldata-source-map.md >/dev/null
grep -F "including after" docs/linuxcnc-tooldata-source-map.md >/dev/null
grep -F '`src/emc/tooldata/tool_watch.cc` is the NML/mmap tool table watcher utility' \
docs/linuxcnc-tooldata-source-map.md >/dev/null
grep -F '`EMC_STAT::io.tool` fields through `RCS_STAT_CHANNEL`' \
docs/linuxcnc-tooldata-source-map.md >/dev/null
grep -F 'tooldata:src/emc/tooldata/tooldata_common.cc:shared tool table parser/format logic' \
linuxcnc-rs274-source-files.txt >/dev/null
grep -F 'tooldata:src/emc/tooldata/tooldata_mmap.cc:native mmap implementation; replace for wasm' \
linuxcnc-rs274-source-files.txt >/dev/null
grep -F 'tooldata:src/emc/tooldata/tooldata_db.cc:external database integration; exclude from wasm core' \
linuxcnc-rs274-source-files.txt >/dev/null
grep -F 'tooldata:src/emc/tooldata/tooldata_nml.cc:NML tool table backend selected by LinuxCNC TOOL_NML_FLAG' \
linuxcnc-rs274-source-files.txt >/dev/null
grep -F 'header:src/emc/tooldata/tooldata.hh:LinuxCNC tooldata declarations used by RS274 and wasm-safe tooldata shims' \
linuxcnc-rs274-source-files.txt >/dev/null
grep -F 'metadata:src/emc/tooldata/Submakefile:LinuxCNC tooldata build metadata defining mmap and NML backend source selection' \
linuxcnc-rs274-source-files.txt >/dev/null
grep -F 'core:src/emc/tooldata/tooldata_common.cc:shared tool table parser/format logic linked with wasm-safe mmap backend' \
linuxcnc-rs274-wasm-source-files.txt >/dev/null
grep -F 'blocked:src/emc/tooldata/tooldata_mmap.cc:native mmap implementation' \
linuxcnc-rs274-wasm-source-files.txt >/dev/null
grep -F 'metadata:src/emc/tooldata/tooldata_db.cc:source basis for wasm-safe tooldata runtime export shim' \
linuxcnc-rs274-wasm-source-files.txt >/dev/null
grep -F 'metadata:src/emc/tooldata/tooldata_nml.cc:source basis for wasm-safe tooldata runtime NML export shim' \
linuxcnc-rs274-wasm-source-files.txt >/dev/null
grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_mmap.cc provides these' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'bool mmap_created = false;' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'bool mmap_random_toolchanger = false;' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'bool mmap_creator_called = false;' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'int tool_mmap_creator(EMC_TOOL_STAT const *ptr, int random_toolchanger)' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'Error: tool_mmap_creator already called BYE' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'mmap_created = true;' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'mmap_random_toolchanger = random_toolchanger;' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'last_index = 0;' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'int tool_mmap_user(void)' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'tool_mmap_user(): tool mmap not available' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'void tool_mmap_close(void)' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'mmap_created = false;' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'bool tool_mmap_is_random_toolchanger(void)' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'return mmap_random_toolchanger;' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'int tooldata_last_index_get(void)' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'return -1;' \
core/wasm_shims/tooldata/tooldata_mmap_backend.cc >/dev/null
grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_nml.cc and tooldata_db.cc' \
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null
grep -F 'constexpr int MAX_DB_PROGRAM_ARGS = 10;' \
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null
grep -F 'strtok_r(progname_plus_args, " ", &saveptr)' \
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null
grep -F 'argc exceeds MAX_DB_PROGRAM_ARGS=%d' \
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null
grep -F 'access(program, X_OK) != 0' \
core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null
grep -F 'returns 0 when' core/wasm_shims/tooldata/tooldata_runtime_stubs.cc >/dev/null
grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_common.cc provides' \
core/wasm_shims/tooldata/tooldata_common_probe_main.cc >/dev/null
grep -F 'LinuxCNC source basis: src/emc/tooldata/tooldata_mmap.cc, tooldata_nml.cc,' \
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
grep -F 'tooldata_db_notify() returns 0 without side effects while db_live is' \
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
grep -F 'tooldata_db_init(missing_db_program_with_arg.data(), 1) != -1' \
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
grep -F 'missing_db_program_with_arg[separator] != 0' \
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
grep -F 'tooldata_db_init(too_many_db_args, 1) != -1' \
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
grep -F 'tooldata_db_init(non_db_program, 1) != -1' \
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
grep -F 'tool_mmap_creator(nullptr, 1);' \
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
grep -F 'WEXITSTATUS(recreate_status) == 0' \
core/wasm_shims/tooldata/tooldata_probe_main.cc >/dev/null
grep -F 'core/wasm_shims/tooldata/tooldata_mmap_backend.cc' docs/linuxcnc-wasm-shims-source-map.md >/dev/null
grep -F 'core/wasm_shims/tooldata/tooldata_runtime_stubs.cc' docs/linuxcnc-wasm-shims-source-map.md >/dev/null
grep -F 'LinuxCNC source basis: this link probe runs the wasm-safe tooldata shim' \
test-linuxcnc-wasm-tooldata-link.sh >/dev/null
grep -F 'LinuxCNC source basis: this link probe runs tooldata_common through the' \
test-linuxcnc-wasm-tooldata-common-link.sh >/dev/null
grep -F 'LinuxCNC source basis: this symbol probe compares the wasm mmap backend shim' \
test-linuxcnc-wasm-tooldata-mmap-symbols.sh >/dev/null
grep -F 'LinuxCNC source basis: this probe compares wasm-safe tooldata runtime stubs' \
test-linuxcnc-wasm-tooldata-runtime-symbols.sh >/dev/null
grep -F '!!!!db_init: argc exceeds MAX_DB_PROGRAM_ARGS=10' \
test-linuxcnc-wasm-tooldata-link.sh >/dev/null
grep -F 'not executable' test-linuxcnc-wasm-tooldata-link.sh >/dev/null
grep -F 'Error: tool_mmap_creator already called BYE' \
test-linuxcnc-wasm-tooldata-link.sh >/dev/null
grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe" ./test-linuxcnc-wasm-tooldata-link.sh' \
build-wasm.sh >/dev/null
grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata_common link probe" ./test-linuxcnc-wasm-tooldata-common-link.sh' \
build-wasm.sh >/dev/null
grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata_mmap symbol probe" ./test-linuxcnc-wasm-tooldata-mmap-symbols.sh' \
build-wasm.sh >/dev/null
grep -F 'run_parallel_wasm_probe "LinuxCNC wasm tooldata runtime symbol probe" ./test-linuxcnc-wasm-tooldata-runtime-symbols.sh' \
build-wasm.sh >/dev/null
python3 - <<'PY'
from pathlib import Path
import subprocess
doc = Path("docs/linuxcnc-tooldata-source-map.md").read_text(encoding="utf-8")
build_wasm_lines = Path("build-wasm.sh").read_text(encoding="utf-8").splitlines()
native_tooldata = subprocess.check_output(
["./list-linuxcnc-source-manifest-sources.sh", "linuxcnc-rs274-source-files.txt", "tooldata"],
text=True,
).splitlines()
expected_native = [
"src/emc/tooldata/tooldata_common.cc",
"src/emc/tooldata/tooldata_mmap.cc",
"src/emc/tooldata/tooldata_db.cc",
"src/emc/tooldata/tooldata_nml.cc",
"src/emc/tooldata/tool_mmap_read.cc",
"src/emc/tooldata/tool_watch.cc",
]
for source in expected_native:
if source not in native_tooldata:
raise SystemExit(f"native RS274 manifest missing LinuxCNC tooldata source: {source}")
kin_tooldata = subprocess.check_output(
["./list-linuxcnc-kinematics-manifest-sources.sh", "linuxcnc-kinematics-source-files.txt", "tooldata"],
text=True,
).splitlines()
if len([source for source in kin_tooldata if source.endswith(".tbl")]) != 10:
raise SystemExit("kinematics manifest must track exactly 10 LinuxCNC TOOL_TABLE sources")
positions = {}
for index, line in enumerate(build_wasm_lines, start=1):
if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata shim link probe"' in line:
positions["tooldata_link"] = index
if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata_common link probe"' in line:
positions["tooldata_common"] = index
if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata_mmap symbol probe"' in line:
positions["tooldata_mmap"] = index
if 'run_parallel_wasm_probe "LinuxCNC wasm tooldata runtime symbol probe"' in line:
positions["tooldata_runtime"] = index
if "cp build/wasm/cnc_sim.js web/public/cnc_sim.js" in line:
positions["copy_js"] = index
if "cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm" in line:
positions["copy_wasm"] = index
missing = [
name for name in (
"tooldata_link",
"tooldata_common",
"tooldata_mmap",
"tooldata_runtime",
"copy_js",
"copy_wasm",
)
if name not in positions
]
if missing:
raise SystemExit(f"build-wasm.sh missing tooldata/copy ordering anchor: {', '.join(missing)}")
for probe in ("tooldata_link", "tooldata_common", "tooldata_mmap", "tooldata_runtime"):
if not (positions[probe] < positions["copy_js"] and positions[probe] < positions["copy_wasm"]):
raise SystemExit("build-wasm.sh must run all tooldata link/symbol probes before copying browser artifacts")
for path in [
"core/wasm_shims/tooldata/tooldata_mmap_backend.cc",
"core/wasm_shims/tooldata/tooldata_runtime_stubs.cc",
"test-linuxcnc-wasm-tooldata-link.sh",
"test-linuxcnc-wasm-tooldata-common-link.sh",
"test-linuxcnc-wasm-tooldata-mmap-symbols.sh",
"test-linuxcnc-wasm-tooldata-runtime-symbols.sh",
]:
if path not in doc:
raise SystemExit(f"docs/linuxcnc-tooldata-source-map.md missing coverage path: {path}")
for phrase in [
"must not add CNC behavior",
"must not expand the temporary smoke parser",
"source coverage only",
"must not become browser API fields",
"WASM-safe tooldata shims must preserve the LinuxCNC export surface",
"must keep external DB services unavailable",
"must not fork, exec, poll",
"MAX_DB_PROGRAM_ARGS=10",
"`strtok_r()` argument splitting",
"access(..., X_OK)",
"mmap tool table reader utility",
"NML/mmap tool table watcher utility",
"mmap creator/user/close lifecycle",
"random-toolchanger flag",
"last-index unavailable return",
"in-memory replacement for native mmap",
"inactive-DB return values",
"./check-linuxcnc-tooldata-source-map.sh",
"does not interpret G-code",
]:
if phrase not in doc:
raise SystemExit(f"docs/linuxcnc-tooldata-source-map.md missing boundary phrase: {phrase}")
for entry in [
"test-native.sh",
"test-linuxcnc-source-link.sh",
"build-wasm.sh",
]:
text = Path(entry).read_text(encoding="utf-8")
if "./check-linuxcnc-tooldata-source-map.sh" not in text:
raise SystemExit(f"{entry} must run ./check-linuxcnc-tooldata-source-map.sh")
PY

View File

@@ -0,0 +1,194 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: this guard ties the documented WASM blocker map to
# LinuxCNC RS274/Python/remap/tooldata sources, the rs274 WASM manifest, and
# wasm-safe shim replacement sources. It validates source coverage only; it
# does not add CNC behavior, expand smoke parsing, or interpret G-code.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh "$manifest"
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
grep -F 'LinuxCNC source basis: this blocker scan is constrained to the RS274/WASM' \
analyze-linuxcnc-wasm-blockers.sh >/dev/null
grep -F 'blocked replacement is not in wasm shim build set' \
analyze-linuxcnc-wasm-blockers.sh >/dev/null
grep -F 'shim_source_output=$(./list-linuxcnc-wasm-safe-shims.sh)' \
analyze-linuxcnc-wasm-blockers.sh >/dev/null
grep -F 'LinuxCNC source basis: this guardrail locks the blocker categories reported' \
test-linuxcnc-wasm-blockers.sh >/dev/null
grep -F 'Cache wasm blocker analyzer output' docs/linuxcnc-source-policy.md >/dev/null
grep -F '#include <Python.h>' "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
grep -F 'PyInit_gcode' "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
grep -F '#include <boost/python/module.hpp>' "$linuxcnc_root/src/emc/rs274ngc/interpmodule.cc" >/dev/null
grep -F '#include "pythonplugin/python_plugin.hh"' "$linuxcnc_root/src/emc/rs274ngc/interp_python.cc" >/dev/null
grep -F 'dlopen' "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null
grep -F 'opendir(direct)' "$linuxcnc_root/src/emc/rs274ngc/interp_o_word.cc" >/dev/null
grep -F 'readdir(aDir)' "$linuxcnc_root/src/emc/rs274ngc/interp_o_word.cc" >/dev/null
grep -F 'std::string tempfile = std::string(filename) + ".new";' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F '+ RS274NGC_PARAMETER_FILE_BACKUP_SUFFIX;' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'unlink(bakfile.c_str());' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'if(link(filename, bakfile.c_str()) < 0)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'if(rename(tempfile.c_str(), filename) < 0)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F '#define TOOL_MMAP_FILENAME ".tool.mmap"' "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'int tool_mmap_creator(' "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
python3 - "$linuxcnc_root" "$manifest" <<'PY'
from pathlib import Path
import re
import subprocess
import sys
linuxcnc_root = Path(sys.argv[1])
manifest = Path(sys.argv[2])
source_map = Path("docs/linuxcnc-wasm-blockers-source-map.md").read_text(encoding="utf-8")
manifest_lines = [
line for line in manifest.read_text(encoding="utf-8").splitlines()
if line and not line.startswith("#")
]
manifest_counts = {"core": 0, "blocked": 0, "header": 0, "blocked-header": 0, "metadata": 0}
for line in manifest_lines:
group = line.split(":", 1)[0]
if group not in manifest_counts:
raise SystemExit(f"unexpected WASM manifest group in blocker source map guard: {group}")
manifest_counts[group] += 1
for group, count in manifest_counts.items():
if f"| `{group}` | {count} |" not in source_map:
raise SystemExit(f"docs/linuxcnc-wasm-blockers-source-map.md count mismatch for {group}")
output = subprocess.check_output(
["./analyze-linuxcnc-wasm-blockers.sh", str(manifest)],
text=True,
env={"LINUXCNC_ROOT": str(linuxcnc_root)},
)
sections = {}
current = None
for line in output.splitlines():
if line.startswith("[") and line.endswith("]"):
current = line[1:-1]
sections[current] = []
elif current and line:
sections[current].append(line)
expected_counts = {
"manifest-core": "core=26",
"manifest-blocked": "blocked=9",
"python": 8,
"python-compile-covered": 8,
"tooldata": 1,
"native-backend": 1,
"native-fs": 1,
"blocked-replacements": 9,
"core-python-shimmed": 6,
"core-dlopen-shimmed": 1,
"core-tooldata-shimmed": 1,
"core-tooldata-users-shimmed": 2,
"core-native-fs-shimmed": 16,
}
manifest_section = sections.get("manifest", [])
if expected_counts["manifest-core"] not in manifest_section:
raise SystemExit("WASM blocker analyzer core count changed")
if expected_counts["manifest-blocked"] not in manifest_section:
raise SystemExit("WASM blocker analyzer blocked count changed")
for section, count in expected_counts.items():
if section.startswith("manifest-"):
continue
entries = [entry for entry in sections.get(section, []) if entry != "(none)"]
if len(entries) != count:
raise SystemExit(f"WASM blocker analyzer section {section} count changed: {len(entries)}")
if f"| `{section}` | {count} |" not in source_map:
raise SystemExit(f"docs/linuxcnc-wasm-blockers-source-map.md analyzer count mismatch for {section}")
if sections.get("dlopen") != ["(none)"]:
raise SystemExit("blocked dlopen section is no longer empty")
if sections.get("tooldata-users") != ["(none)"]:
raise SystemExit("blocked tooldata-users section is no longer empty")
if sections.get("python") != sections.get("python-compile-covered"):
raise SystemExit("blocked Python sources are no longer fully compile-covered by the inert Python C API probe")
shim_paths = set(Path(line).as_posix() for line in subprocess.check_output(
["./list-linuxcnc-wasm-safe-shims.sh"],
text=True,
).splitlines() if line)
required_tracked_entries = [
"src/emc/rs274ngc/interp_base.cc -> core/wasm_shims/dlfcn.cc",
"src/emc/rs274ngc/gcodemodule.cc",
"src/emc/rs274ngc/interpmodule.cc",
"src/emc/tooldata/tooldata_mmap.cc",
"src/emc/rs274ngc/interp_base.cc",
"src/emc/rs274ngc/interp_o_word.cc",
"src/emc/rs274ngc/rs274ngc_pre.cc",
]
required_replacements = [
"src/emc/rs274ngc/canonmodule.cc -> core/wasm_shims/linuxcnc_runtime_shim.cc",
"src/emc/rs274ngc/gcodemodule.cc -> core/wasm_shims/linuxcnc_runtime_shim.cc",
"src/emc/rs274ngc/interpmodule.cc -> core/wasm_shims/linuxcnc_runtime_shim.cc",
"src/emc/rs274ngc/pyarrays.cc -> core/wasm_shims/python_c_api_shim.cc",
"src/emc/rs274ngc/pyblock.cc -> core/wasm_shims/python_c_api_shim.cc",
"src/emc/rs274ngc/pyemctypes.cc -> core/wasm_shims/python_c_api_shim.cc",
"src/emc/rs274ngc/pyinterp1.cc -> core/wasm_shims/python_c_api_shim.cc",
"src/emc/rs274ngc/pyparamclass.cc -> core/wasm_shims/python_c_api_shim.cc",
"src/emc/tooldata/tooldata_mmap.cc -> core/wasm_shims/tooldata/tooldata_mmap_backend.cc",
]
for entry in required_tracked_entries:
if " -> " in entry:
source, shim = entry.split(" -> ", 1)
if source not in sections.get("core-dlopen-shimmed", []):
raise SystemExit(f"WASM blocker analyzer output missing core dlopen shimmed source: {source}")
if shim not in shim_paths:
raise SystemExit(f"WASM blocker analyzer core dlopen shim is absent from wasm-safe shim set: {shim}")
elif entry not in output:
raise SystemExit(f"WASM blocker analyzer output missing required entry: {entry}")
if entry not in source_map:
raise SystemExit(f"docs/linuxcnc-wasm-blockers-source-map.md missing required entry: {entry}")
replacement_entries = [entry for entry in sections.get("blocked-replacements", []) if entry != "(none)"]
if replacement_entries != required_replacements:
raise SystemExit(f"WASM blocker replacement set changed: {replacement_entries}")
if "Required replacement set:" not in source_map:
raise SystemExit("docs/linuxcnc-wasm-blockers-source-map.md must document the complete replacement set")
for entry in required_replacements:
if entry not in source_map:
raise SystemExit(f"docs/linuxcnc-wasm-blockers-source-map.md missing required replacement: {entry}")
for replacement in re.findall(r"-> (core/wasm_shims/[^\s]+)", output):
if replacement not in shim_paths:
raise SystemExit(f"blocked replacement is absent from wasm-safe shim set: {replacement}")
for phrase in [
"does not add CNC behavior",
"must not become smoke parser behavior",
"blocked `dlopen` and blocked tooldata-users remain empty",
'parameter-file replacement through `filename + ".new"`',
"O-word lookup and parameter-file replacement",
"Python/Boost.Python remap paths remain tracked blockers",
"Python/Boost.Python blockers are compile-covered by the inert Python C API probe",
"./check-linuxcnc-wasm-blockers-source-map.sh",
"does not interpret G-code",
]:
if phrase not in source_map:
raise SystemExit(f"docs/linuxcnc-wasm-blockers-source-map.md missing boundary phrase: {phrase}")
for entry in [
"test-native.sh",
"test-linuxcnc-source-link.sh",
"build-wasm.sh",
]:
text = Path(entry).read_text(encoding="utf-8")
if "./check-linuxcnc-wasm-blockers-source-map.sh" not in text:
raise SystemExit(f"{entry} must run ./check-linuxcnc-wasm-blockers-source-map.sh")
for entry in [
"test-native.sh",
"test-linuxcnc-source-link.sh",
]:
text = Path(entry).read_text(encoding="utf-8")
if "CNC_SIM_WASM_BLOCKERS_SELF_CHECKS=1" not in text:
raise SystemExit(f"{entry} must run wasm blocker analyzer self-checks")
PY

View File

@@ -0,0 +1,499 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: this guard ties the documented wasm-safe shim map to
# the linuxcnc_wasm_safe_probe_shims CMake set, the RS274/WASM source manifest,
# and the LinuxCNC sources each shim cites. It validates source coverage only;
# it does not add CNC behavior, expand smoke parsing, or interpret G-code.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs-cached.sh "$manifest"
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
grep -F 'LinuxCNC source basis: wasm-safe shim probe sources are limited to the' \
list-linuxcnc-wasm-safe-shims.sh >/dev/null
grep -F 'set(linuxcnc_wasm_safe_probe_shims' core/CMakeLists.txt >/dev/null
grep -F 'LinuxCNC source basis: this guard ties the documented WASM blocker map' \
check-linuxcnc-wasm-blockers-source-map.sh >/dev/null
grep -F 'dlopen(NULL, RTLD_GLOBAL);' "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null
grep -F 'interp_lib = dlopen(interp_path, RTLD_NOW);' "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null
grep -F 'Constructor constructor = (Constructor)dlsym(interp_lib, "makeInterp");' \
"$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null
grep -F 'dlerror()' "$linuxcnc_root/src/emc/rs274ngc/interp_base.cc" >/dev/null
grep -F '#include "libintl.h"' "$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null
grep -F '#define _(s) gettext(s)' "$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null
grep -F 'CHKS((!_readers[(int)' "$linuxcnc_root/src/emc/rs274ngc/interp_internal.cc" >/dev/null
grep -F 'Cannot use polar coordinate on a machine lacking X or Y axes' \
"$linuxcnc_root/src/emc/rs274ngc/interp_internal.cc" >/dev/null
grep -F 'int _task = 0;' "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
grep -F 'int _task = 1;' "$linuxcnc_root/src/emc/task/emctaskmain.cc" >/dev/null
grep -F '{ "interpreter", PyInit_interpreter },' "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
grep -F '{ "emccanon", PyInit_emccanon },' "$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
grep -F 'struct _inittab builtin_modules[]' "$linuxcnc_root/src/emc/task/taskclass.cc" >/dev/null
grep -F 'int Interp::fetch_hal_param(' "$linuxcnc_root/src/emc/rs274ngc/interp_namedparams.cc" >/dev/null
grep -F 'hal_get_pin_value_by_name(hal_name, &type, &ptr, &conn)' \
"$linuxcnc_root/src/emc/rs274ngc/interp_namedparams.cc" >/dev/null
grep -F 'hal_get_signal_value_by_name(hal_name, &type, &ptr, &conn)' \
"$linuxcnc_root/src/emc/rs274ngc/interp_namedparams.cc" >/dev/null
grep -F 'hal_get_param_value_by_name(hal_name, &type, &ptr)' \
"$linuxcnc_root/src/emc/rs274ngc/interp_namedparams.cc" >/dev/null
grep -F 'HAL_TYPE_UNINITIALIZED = 0,' "$linuxcnc_root/include/hal.h" >/dev/null
grep -F 'extern int hal_get_pin_value_by_name(' "$linuxcnc_root/include/hal.h" >/dev/null
grep -F 'extern int hal_get_signal_value_by_name(' "$linuxcnc_root/include/hal.h" >/dev/null
grep -F 'extern int hal_get_param_value_by_name(' "$linuxcnc_root/include/hal.h" >/dev/null
grep -F 'int hal_get_pin_value_by_name(' "$linuxcnc_root/src/hal/hal_lib.c" >/dev/null
grep -F 'int hal_get_signal_value_by_name(' "$linuxcnc_root/src/hal/hal_lib.c" >/dev/null
grep -F 'int hal_get_param_value_by_name(' "$linuxcnc_root/src/hal/hal_lib.c" >/dev/null
grep -F 'wordexp(basename, &exp_result, 0);' "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'wordexp(settings->program_prefix, &exp_result, 0);' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'wordexp(settings->subroutines[dct], &exp_result, 0);' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'wordfree(&exp_result);' "$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F '#include "nml_intf/emc_nml.hh"' "$linuxcnc_root/src/emc/sai/dummyemcstat.cc" >/dev/null
grep -F 'EMC_STAT *emcStatus = new EMC_STAT;' "$linuxcnc_root/src/emc/sai/dummyemcstat.cc" >/dev/null
grep -F 'int NMLmsg::automatically_clear = 1;' \
"$linuxcnc_root/src/libnml/nml/nmlmsg.cc" >/dev/null
grep -F 'NMLmsg::NMLmsg' "$linuxcnc_root/src/libnml/nml/nmlmsg.cc" >/dev/null
grep -F 'void NMLmsg::clear()' "$linuxcnc_root/src/libnml/nml/nmlmsg.cc" >/dev/null
grep -F 'memset((void *) this, 0, size);' \
"$linuxcnc_root/src/libnml/nml/nmlmsg.cc" >/dev/null
grep -F 'RCS_STAT_MSG::RCS_STAT_MSG' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null
grep -F 'command_type = -1;' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null
grep -F 'echo_serial_number = -1;' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null
grep -F 'status = RCS_STATUS::UNINITIALIZED;' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null
grep -F '_state = -1;' "$linuxcnc_root/src/libnml/nml/stat_msg.cc" >/dev/null
grep -F 'extern int set_print_rcs_error_info(const char *file, int line);' \
"$linuxcnc_root/src/libnml/rcs/rcs_print.hh" >/dev/null
grep -F 'extern int print_rcs_error_new(const char *_fmt, ...)' \
"$linuxcnc_root/src/libnml/rcs/rcs_print.hh" >/dev/null
grep -F '#define rcs_print_error set_print_rcs_error_info( __FILE__, __LINE__); print_rcs_error_new' \
"$linuxcnc_root/src/libnml/rcs/rcs_print.hh" >/dev/null
grep -F 'int set_print_rcs_error_info(const char *file, int line)' \
"$linuxcnc_root/src/libnml/rcs/rcs_print.cc" >/dev/null
grep -F 'print_rcs_error_new' "$linuxcnc_root/src/libnml/rcs/rcs_print.cc" >/dev/null
grep -F 'retval = rcs_vprint(_fmt, args, 1);' \
"$linuxcnc_root/src/libnml/rcs/rcs_print.cc" >/dev/null
grep -F 'extern int rtapi_snprintf(char *buf, unsigned long int size,' \
"$linuxcnc_root/src/rtapi/rtapi.h" >/dev/null
grep -F 'extern int rtapi_vsnprintf(char *buf, unsigned long size,' \
"$linuxcnc_root/src/rtapi/rtapi.h" >/dev/null
grep -F 'int rtapi_snprintf(char *buffer, unsigned long int size, const char *msg, ...) {' \
"$linuxcnc_root/src/rtapi/uspace_common.h" >/dev/null
grep -F 'result = vsnprintf(buffer, size, msg, args);' \
"$linuxcnc_root/src/rtapi/uspace_common.h" >/dev/null
grep -F 'int rtapi_vsnprintf(char *buffer, unsigned long int size, const char *fmt,' \
"$linuxcnc_root/src/rtapi/uspace_common.h" >/dev/null
grep -F 'return vsnprintf(buffer, size, fmt, args);' \
"$linuxcnc_root/src/rtapi/uspace_common.h" >/dev/null
grep -F 'class PythonPlugin' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
grep -F 'PLUGIN_PYTHON_NOT_INITIALIZED = -12,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
grep -F 'PLUGIN_BAD_PATH = -5,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
grep -F 'PLUGIN_INITTAB_FAILED = -7 ,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
grep -F 'PLUGIN_EXCEPTION_DURING_PATH_PREPEND = -9,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
grep -F 'PLUGIN_EXCEPTION_DURING_PATH_APPEND = -10,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
grep -F 'PLUGIN_NO_CALLABLE = 1,' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
grep -F 'PLUGIN_EXCEPTION = 2' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.hh" >/dev/null
grep -F 'PythonPlugin::instantiate' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'for (int i = 0; inittab[i].name != NULL; i++) {' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'if (module == NULL) {' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'status = PLUGIN_INITTAB_FAILED;' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'if (status < PLUGIN_OK)' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'if (auto inistring = inifile.findString("TOPLEVEL", section)) {' \
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'if (realpath(toplevel, real_path) == NULL) {' \
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'status = PLUGIN_BAD_PATH;' "$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'while (auto inistring = inifile.findString(n, "PATH_PREPEND", "PYTHON")) {' \
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'while (auto inistring = inifile.findString(n, "PATH_APPEND", "PYTHON")) {' \
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'status = PLUGIN_EXCEPTION_DURING_PATH_PREPEND;' \
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'status = PLUGIN_EXCEPTION_DURING_PATH_APPEND;' \
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'int IniFile::tildeExpand(const std::string &path, std::string &result)' \
"$linuxcnc_root/src/emc/ini/inifile.cc" >/dev/null
grep -F "if(!home)" "$linuxcnc_root/src/emc/ini/inifile.cc" >/dev/null
grep -F 'IniFile::trim(sect);' "$linuxcnc_root/src/emc/ini/inifile.cc" >/dev/null
grep -F 'Section header has trailing content, ignored' "$linuxcnc_root/src/emc/ini/inifile.cc" >/dev/null
grep -F 'retval = bp::exec_file(cmd, main_namespace, main_namespace);' \
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'retval = bp::exec(cmd, main_namespace, main_namespace);' \
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'PyObject *rv = PyObject_Call(function.ptr(), tupleargs.ptr(), kwargs.ptr());' \
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F 'PyErr_Fetch(&exc,&val,&tb);' \
"$linuxcnc_root/src/emc/rs274ngc/interp_python.cc" >/dev/null
grep -F 'PyErr_NormalizeException(&exc,&val,&tb);' \
"$linuxcnc_root/src/emc/rs274ngc/interp_python.cc" >/dev/null
grep -F 'if (Py_IsInitialized()) {' \
"$linuxcnc_root/src/emc/pythonplugin/python_plugin.cc" >/dev/null
grep -F '#define callmethod(o, m, f, ...) PyObject_CallMethod((o), (char*)(m), (char*)(f), ## __VA_ARGS__)' \
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
grep -F 'PyObject *result = PyObject_GetAttrString(callback, "parameter_file");' \
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
grep -F 'PyErr_Format(PyExc_TypeError,' \
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
grep -F 'return Py_BuildValue("[ddd][ddd][ddd][ddd]",' \
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
grep -F 'PyObject *m = PyModule_Create(&gcode_moduledef);' \
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
grep -F 'PyType_Ready(&LineCodeType);' \
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
grep -F 'PyModule_AddObject(m, "linecode", (PyObject*)&LineCodeType);' \
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
grep -F 'PyObject_SetAttrString(m, "MAX_ERROR", PyLong_FromLong(maxerror));' \
"$linuxcnc_root/src/emc/rs274ngc/gcodemodule.cc" >/dev/null
grep -F '#define TOOL_MMAP_FILENAME ".tool.mmap"' "$linuxcnc_root/src/emc/tooldata/tooldata_mmap.cc" >/dev/null
grep -F 'int tool_nml_register(CANON_TOOL_TABLE *tblptr)' "$linuxcnc_root/src/emc/tooldata/tooldata_nml.cc" >/dev/null
grep -F 'int tooldata_db_init(char progname_plus_args[]' "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
grep -F 'if (!db_live) {return -1;}' "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
grep -F 'if (!db_live) return 0;' "$linuxcnc_root/src/emc/tooldata/tooldata_db.cc" >/dev/null
python3 - "$manifest" <<'PY'
from pathlib import Path
import re
import subprocess
import sys
manifest = Path(sys.argv[1])
source_map = Path("docs/linuxcnc-wasm-shims-source-map.md").read_text(encoding="utf-8")
manifest_text = manifest.read_text(encoding="utf-8")
shim_paths = [
line for line in subprocess.check_output(["./list-linuxcnc-wasm-safe-shims.sh"], text=True).splitlines()
if line
]
if len(shim_paths) != 11:
raise SystemExit(f"expected 11 wasm-safe CMake shim sources, found {len(shim_paths)}")
if f"| CMake `linuxcnc_wasm_safe_probe_shims` sources | {len(shim_paths)} |" not in source_map:
raise SystemExit("docs/linuxcnc-wasm-shims-source-map.md shim count mismatch")
expected_shims = {
"core/wasm_shims/dlfcn.cc": ["src/emc/rs274ngc/interp_base.cc"],
"core/wasm_shims/emc_status_shim.cc": ["src/emc/sai/dummyemcstat.cc"],
"core/wasm_shims/gettext_shim.cc": ["src/emc/rs274ngc/interp_internal.hh"],
"core/wasm_shims/linuxcnc_runtime_shim.cc": [
"src/emc/rs274ngc/gcodemodule.cc",
"src/emc/task/emctaskmain.cc",
"src/emc/task/taskclass.cc",
"src/emc/rs274ngc/interp_namedparams.cc",
"include/hal.h",
"src/hal/hal_lib.c",
"src/emc/rs274ngc/rs274ngc_pre.cc",
],
"core/wasm_shims/nml_status_shim.cc": [
"src/libnml/nml/nmlmsg.cc",
"src/libnml/nml/stat_msg.cc",
],
"core/wasm_shims/python_c_api_shim.cc": [
"src/emc/rs274ngc/interp_python.cc",
"src/emc/rs274ngc/interp_namedparams.cc",
"src/emc/rs274ngc/gcodemodule.cc",
"src/emc/pythonplugin/python_plugin.cc",
],
"core/wasm_shims/rcs_print_shim.cc": [
"src/libnml/rcs/rcs_print.cc",
"src/libnml/rcs/rcs_print.hh",
],
"core/wasm_shims/rtapi_compat.cc": [
"src/rtapi/uspace_common.h",
"src/rtapi/rtapi.h",
],
"core/wasm_shims/tooldata/tooldata_mmap_backend.cc": [
"src/emc/tooldata/tooldata_mmap.cc",
],
"core/wasm_shims/tooldata/tooldata_runtime_stubs.cc": [
"src/emc/tooldata/tooldata_nml.cc",
"src/emc/tooldata/tooldata_db.cc",
],
"core/wasm_shims/pythonplugin/python_plugin.cc": [
"src/emc/pythonplugin/python_plugin.cc",
"src/emc/pythonplugin/python_plugin.hh",
"src/emc/ini/inifile.cc",
],
}
if shim_paths != list(expected_shims):
raise SystemExit("wasm-safe shim CMake order no longer matches docs/linuxcnc-wasm-shims-source-map.md")
for shim_path, linuxcnc_sources in expected_shims.items():
shim_text = Path(shim_path).read_text(encoding="utf-8")
if "LinuxCNC source basis:" not in shim_text:
raise SystemExit(f"{shim_path} is missing a LinuxCNC source basis comment")
if f"| `{shim_path}` |" not in source_map:
raise SystemExit(f"docs/linuxcnc-wasm-shims-source-map.md missing shim row: {shim_path}")
for source in linuxcnc_sources:
if source not in source_map:
raise SystemExit(f"docs/linuxcnc-wasm-shims-source-map.md missing source {source} for {shim_path}")
if source not in manifest_text:
raise SystemExit(f"{source} is not tracked in {manifest}")
runtime_shim = Path("core/wasm_shims/linuxcnc_runtime_shim.cc").read_text(encoding="utf-8")
runtime_probe = Path("core/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc").read_text(encoding="utf-8")
dlfcn_shim = Path("core/wasm_shims/dlfcn.cc").read_text(encoding="utf-8")
dlfcn_header = Path("core/wasm_shims/dlfcn.h").read_text(encoding="utf-8")
emc_status_shim = Path("core/wasm_shims/emc_status_shim.cc").read_text(encoding="utf-8")
gettext_shim = Path("core/wasm_shims/gettext_shim.cc").read_text(encoding="utf-8")
for needle in [
"#define RTLD_GLOBAL 0x00100",
"#define RTLD_NOW 0x00002",
"void *dlopen(const char *filename, int flags);",
"void *dlsym(void *handle, const char *symbol);",
"char *dlerror(void);",
"int dlclose(void *handle);",
]:
if needle not in dlfcn_header:
raise SystemExit(f"dlfcn shim header missing LinuxCNC loader boundary: {needle}")
for needle in [
"dynamic loading is unavailable in wasm-safe probe",
"void *dlopen(const char *, int)",
"return nullptr;",
"void *dlsym(void *, const char *)",
"char *dlerror(void)",
"return last_error;",
"int dlclose(void *)",
"return 0;",
]:
if needle not in dlfcn_shim:
raise SystemExit(f"dlfcn shim missing unavailable-loader boundary: {needle}")
for needle in [
'#include "nml_intf/emc_nml.hh"',
"EMC_STAT *emcStatus = new EMC_STAT;",
]:
if needle not in emc_status_shim:
raise SystemExit(f"EMC status shim missing detached LinuxCNC status singleton: {needle}")
for needle in [
"extern \"C\" char *gettext(const char *msgid)",
"return const_cast<char *>(msgid ? msgid : \"\");",
]:
if needle not in gettext_shim:
raise SystemExit(f"gettext shim missing diagnostic passthrough boundary: {needle}")
nml_status_shim = Path("core/wasm_shims/nml_status_shim.cc").read_text(encoding="utf-8")
for needle in [
"int NMLmsg::automatically_clear = 1;",
"void NMLmsg::clear()",
"std::memset(static_cast<void *>(this), 0, static_cast<size_t>(size));",
"command_type = -1;",
"echo_serial_number = -1;",
"status = RCS_STATUS::UNINITIALIZED;",
"_state = -1;",
]:
if needle not in nml_status_shim:
raise SystemExit(f"NML status shim missing LinuxCNC constructor boundary: {needle}")
for needle in [
"int _task = 0;",
'extern "C" PyObject *PyInit_interpreter(void)',
'extern "C" PyObject *PyInit_emccanon(void)',
'extern "C" PyObject *PyInit_gcode(void)',
"return nullptr;",
'struct _inittab builtin_modules[] = {',
'{"interpreter", PyInit_interpreter}',
'{"emccanon", PyInit_emccanon}',
]:
if needle not in runtime_shim:
raise SystemExit(f"linuxcnc runtime shim missing preview/builtin module boundary: {needle}")
for needle in [
"std::string expand_home_directory(const char *words)",
"std::string expand_environment_variables(const std::string &words)",
"extern \"C\" int wordexp(const char *words, wordexp_t *pwordexp, int)",
"return WRDE_BADVAL;",
"return WRDE_NOSPACE;",
"pwordexp->we_wordc = 1;",
"pwordexp->we_offs = 0;",
"extern \"C\" void wordfree(wordexp_t *pwordexp)",
"pwordexp->we_wordc = 0;",
"pwordexp->we_wordv = nullptr;",
]:
if needle not in runtime_shim:
raise SystemExit(f"linuxcnc runtime shim missing limited wordexp boundary: {needle}")
for needle in [
'extern "C" int hal_get_pin_value_by_name',
'extern "C" int hal_get_signal_value_by_name',
'extern "C" int hal_get_param_value_by_name',
"*type = HAL_TYPE_UNINITIALIZED;",
"*data = nullptr;",
"return -1;",
]:
if needle not in runtime_shim:
raise SystemExit(f"linuxcnc runtime shim missing HAL unavailable boundary: {needle}")
for needle in [
"bool hal_lookup_is_unavailable()",
'hal_get_pin_value_by_name("cnc-sim.missing-pin", &type, &data, &connected) != -1',
'hal_get_signal_value_by_name("cnc-sim.missing-signal", &type, &data, &has_writers) != -1',
'hal_get_param_value_by_name("cnc-sim.missing-param", &type, &data) != -1',
"type != HAL_TYPE_UNINITIALIZED",
"data != nullptr",
]:
if needle not in runtime_probe:
raise SystemExit(f"runtime probe missing HAL unavailable assertion: {needle}")
python_plugin_shim = Path("core/wasm_shims/pythonplugin/python_plugin.cc").read_text(encoding="utf-8")
python_plugin_probe = Path("core/wasm_shims/pythonplugin/python_plugin_probe_main.cc").read_text(encoding="utf-8")
python_plugin_inittab_probe = Path("core/wasm_shims/pythonplugin/python_plugin_inittab_probe_main.cc").read_text(encoding="utf-8")
python_c_api_shim = Path("core/wasm_shims/python_c_api_shim.cc").read_text(encoding="utf-8")
for forbidden in [
"bp::exec(",
"bp::exec_file(",
"PyObject_Call(",
]:
if forbidden in python_plugin_shim:
raise SystemExit(f"PythonPlugin wasm shim must not execute Python: {forbidden}")
for needle in [
"python disabled in wasm-safe probe",
'find_ini_value(iniFilename, section, "TOPLEVEL", toplevel)',
"realpath(toplevel.c_str(), resolved_path) == nullptr",
"status = PLUGIN_BAD_PATH;",
"status = PLUGIN_INITTAB_FAILED;",
"failed to initialize built-in module",
"read_ini_section(text, section_name)",
"section_name = trim_ini_value(text.substr(1, close - 1));",
'check_python_path_entries(iniFilename,',
"PLUGIN_EXCEPTION_DURING_PATH_PREPEND",
"PLUGIN_EXCEPTION_DURING_PATH_APPEND",
"tilde_expand_path(entry, expanded)",
"status = PLUGIN_PYTHON_NOT_INITIALIZED;",
"return PLUGIN_NO_CALLABLE;",
"status = PLUGIN_EXCEPTION;",
"if (status < PLUGIN_OK)",
]:
if needle not in python_plugin_shim:
raise SystemExit(f"PythonPlugin wasm shim missing disabled-python boundary: {needle}")
for needle in [
"PythonPlugin::PythonPlugin(struct _inittab*) reports PLUGIN_INITTAB_FAILED",
"PythonPlugin *plugin = PythonPlugin::instantiate(bad_modules);",
"plugin != nullptr",
"python_plugin->plugin_status() != PLUGIN_INITTAB_FAILED",
"python_plugin->usable()",
'python_plugin->last_errmsg() != "failed to initialize built-in module"',
]:
if needle not in python_plugin_inittab_probe:
raise SystemExit(f"PythonPlugin inittab probe missing first-caller status assertion: {needle}")
for needle in [
"[ PYTHON ] ; section comment",
"PLUGIN_NO_SECTION",
"PLUGIN_BAD_INIFILE",
"PLUGIN_BAD_PATH",
"PLUGIN_EXCEPTION_DURING_PATH_PREPEND",
"PLUGIN_EXCEPTION_DURING_PATH_APPEND",
"PLUGIN_PYTHON_NOT_INITIALIZED",
"PLUGIN_NO_CALLABLE",
"PLUGIN_EXCEPTION",
"handle_pyerror().empty()",
]:
if needle not in python_plugin_probe:
raise SystemExit(f"PythonPlugin probe missing disabled-python status assertion: {needle}")
for needle in [
"static PyTypeObject none_type = {\"NoneType\"};",
"static PyObject none_object = {&none_type};",
"PyObject *_Py_NoneStructPtr = &none_object;",
"int Py_IsInitialized(void)",
"return 1;",
"PyObject *PyErr_Occurred(void)",
"return nullptr;",
"void PyErr_Fetch(PyObject **exc, PyObject **val, PyObject **tb)",
"*exc = nullptr;",
"*val = nullptr;",
"*tb = nullptr;",
"int PyCallable_Check(PyObject *)",
"return 0;",
"PyObject *PyObject_CallMethod(PyObject *, char *, char *, ...)",
"PyObject *PyObject_GetAttrString(PyObject *, const char *)",
"return &none_object;",
"PyObject *Py_BuildValue(const char *, ...)",
"PyObject *PyModule_Create(PyModuleDef *)",
"int PyModule_AddObject(PyObject *, const char *, PyObject *)",
"int PyType_Ready(PyTypeObject *)",
"void *PyObject_NewShim(std::size_t size, PyTypeObject *type)",
"std::calloc(1, size)",
]:
if needle not in python_c_api_shim:
raise SystemExit(f"Python C API shim missing symbol boundary: {needle}")
rcs_print_shim = Path("core/wasm_shims/rcs_print_shim.cc").read_text(encoding="utf-8")
for needle in [
"set_print_rcs_error_info",
"print_rcs_error_new",
"std::vfprintf(stderr, format, args)",
]:
if needle not in rcs_print_shim:
raise SystemExit(f"RCS print shim missing split print-error boundary: {needle}")
rtapi_compat_shim = Path("core/wasm_shims/rtapi_compat.cc").read_text(encoding="utf-8")
for needle in [
"rtapi_snprintf",
"std::vsnprintf(buf, static_cast<std::size_t>(size), fmt, ap)",
]:
if needle not in rtapi_compat_shim:
raise SystemExit(f"RTAPI compatibility shim missing userspace snprintf boundary: {needle}")
for phrase in [
"must not add CNC behavior",
"must not expand the temporary smoke parser",
"CMake `linuxcnc_wasm_safe_probe_shims` set",
"RS274/WASM manifest",
"browser-safe LinuxCNC adaptations",
"LinuxCNC `interp_base.cc` dynamic loader calls",
"unavailable-loader returns",
"detached LinuxCNC `EMC_STAT` singleton",
"LinuxCNC `_()` diagnostic call sites",
"browser gettext catalogs unavailable",
"LinuxCNC NML status constructors",
"`RCS_STAT_MSG` default status fields",
"dynamic loading unavailable",
"unavailable HAL lookup",
"must leave HAL values unavailable",
"preview `_task=0`",
"builtin module stubs",
"must not switch browser-safe probes into task mode",
"single-word `wordexp()` path expansion",
"`wordfree()` cleanup",
"must not become a shell expansion feature",
"must not become Python execution",
"Python C API shim must return inert objects",
"must not call into CPython runtime",
"keeping Python execution disabled",
"must not execute `bp::exec`, `bp::exec_file`, or",
"inactive-DB return values",
"negative-status short-circuit behavior",
"first-caller `_inittab` `PLUGIN_INITTAB_FAILED` edge",
"preserve the `TOPLEVEL` missing-path `PLUGIN_BAD_PATH` edge",
"preserve `PATH_PREPEND` and `PATH_APPEND` tilde-expansion failure statuses",
"preserve LinuxCNC INI section-name trimming and trailing section-comment handling",
"LinuxCNC `rcs_print_error` split entry points",
"without native print routing",
"LinuxCNC userspace `rtapi_snprintf()` formatting",
"`uspace_common.h` `vsnprintf()` implementation path",
"./check-linuxcnc-wasm-shims-source-map.sh",
"does not interpret G-code",
]:
if phrase not in source_map:
raise SystemExit(f"docs/linuxcnc-wasm-shims-source-map.md missing boundary phrase: {phrase}")
for entry in [
"test-native.sh",
"test-linuxcnc-source-link.sh",
"build-wasm.sh",
]:
text = Path(entry).read_text(encoding="utf-8")
if "./check-linuxcnc-wasm-shims-source-map.sh" not in text:
raise SystemExit(f"{entry} must run ./check-linuxcnc-wasm-shims-source-map.sh")
cmake_text = Path("core/CMakeLists.txt").read_text(encoding="utf-8")
for shim_path in shim_paths:
cmake_suffix = shim_path.removeprefix("core/")
if cmake_suffix not in cmake_text:
raise SystemExit(f"{shim_path} is not listed in linuxcnc_wasm_safe_probe_shims")
blocker_map = Path("docs/linuxcnc-wasm-blockers-source-map.md").read_text(encoding="utf-8")
for shim_path in [
"core/wasm_shims/linuxcnc_runtime_shim.cc",
"core/wasm_shims/python_c_api_shim.cc",
"core/wasm_shims/tooldata/tooldata_mmap_backend.cc",
]:
if shim_path not in blocker_map:
raise SystemExit(f"docs/linuxcnc-wasm-blockers-source-map.md no longer references {shim_path}")
PY

View File

@@ -0,0 +1,204 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: this guard ties Node/browser WASM smoke coverage to
# LinuxCNC RS274 parameter/INI handling, M-code canon side effects, generated
# switchkins/remap config cases, kinematics sources, and OPFS-only browser
# persistence. It validates smoke-test source coverage only; it does not add
# CNC behavior, expand smoke parsing, or interpret G-code.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
grep -F 'int Interp::ini_load(const char *filename)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'int Interp::restore_parameters(const char *filename)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'int Interp::save_parameters(const char *filename,' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'std::string tempfile = std::string(filename) + ".new";' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'unlink(bakfile.c_str());' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'if(link(filename, bakfile.c_str()) < 0)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'if(rename(tempfile.c_str(), filename) < 0)' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'read_text(command, _setup.file_pointer, _setup.linetext,' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'int read_text(const char *command, FILE * inport, char *raw_line,' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_interp.hh" >/dev/null
grep -F 'int Interp::convert_m(block_pointer block' \
"$linuxcnc_root/src/emc/rs274ngc/interp_convert.cc" >/dev/null
grep -F 'int ret = WAIT(round_to_int(block->p_number), DIGITAL_INPUT, type, timeout);' \
"$linuxcnc_root/src/emc/rs274ngc/interp_convert.cc" >/dev/null
grep -F 'SET_AUX_OUTPUT_VALUE(round_to_int(block->e_number), block->q_number);' \
"$linuxcnc_root/src/emc/rs274ngc/interp_convert.cc" >/dev/null
grep -F 'extern int WAIT(int index' "$linuxcnc_root/src/emc/nml_intf/canon.hh" >/dev/null
grep -F 'extern void SET_AUX_OUTPUT_VALUE(int index, double value);' \
"$linuxcnc_root/src/emc/nml_intf/canon.hh" >/dev/null
grep -F 'kp->kinsname = "5axiskins";' "$linuxcnc_root/src/emc/kinematics/5axiskins.c" >/dev/null
grep -F 'int xyzbcKinematicsInverse(const EmcPose * pos' \
"$linuxcnc_root/src/emc/kinematics/trtfuncs.c" >/dev/null
grep -F 'int xyzacKinematicsInverse(const EmcPose * pos' \
"$linuxcnc_root/src/emc/kinematics/trtfuncs.c" >/dev/null
grep -F '*kinv1 = xyzacKinematicsInverse;' \
"$linuxcnc_root/src/emc/kinematics/xyzac-trt-kins.c" >/dev/null
grep -F '*kinv1 = xyzbcKinematicsInverse;' \
"$linuxcnc_root/src/emc/kinematics/xyzbc-trt-kins.c" >/dev/null
grep -F 'int userkKinematicsInverse(const EmcPose * pos' \
"$linuxcnc_root/src/emc/kinematics/userkfuncs.c" >/dev/null
grep -F 'LinuxCNC WASM smoke source map' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null
grep -F 'must not expand the temporary smoke parser' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null
grep -F 'OPFS-only' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null
grep -F '`src/emc/rs274ngc/interp_convert.cc` provides `convert_m()` coverage' \
docs/linuxcnc-wasm-smoke-source-map.md >/dev/null
grep -F 'unlinking `filename + ".bak"`' docs/linuxcnc-wasm-smoke-source-map.md >/dev/null
grep -F '`src/emc/kinematics/5axiskins.c`, `src/emc/kinematics/trtfuncs.c`' \
docs/linuxcnc-wasm-smoke-source-map.md >/dev/null
grep -F 'Generated switchkins config smoke cases must continue to come from' \
docs/linuxcnc-wasm-smoke-source-map.md >/dev/null
grep -F 'LinuxCNC source basis: Node smoke only verifies the wasm/API bridge around' \
test-web-wasm-node-smoke.sh >/dev/null
grep -F 'minimum_steps=${WASM_NODE_SMOKE_MIN_STEPS:-50}' test-web-wasm-node-smoke.sh >/dev/null
grep -F 'WASM Node smoke covered $passed_steps steps, expected at least $minimum_steps' \
test-web-wasm-node-smoke.sh >/dev/null
grep -F 'expected Node wasm-core wrapper to leave OPFS disabled' test-web-wasm-node-smoke.cjs >/dev/null
grep -F 'expected Node wasm-core parseFile to require OPFS' test-web-wasm-node-smoke.cjs >/dev/null
grep -F 'linuxcnc_switchkins_remap_config_cases.json' test-web-wasm-node-smoke.cjs >/dev/null
grep -F 'expected LinuxCNC WASM M68 E3 Q${kinstype} remap side effect' \
test-web-wasm-node-smoke.cjs >/dev/null
grep -F 'expected LinuxCNC WASM M66 E0 L0 remap side effect' \
test-web-wasm-node-smoke.cjs >/dev/null
grep -F 'expected LinuxCNC WASM 5axiskins RTCP pivot from LinuxCNC 5axiskins inverse' \
test-web-wasm-node-smoke.cjs >/dev/null
grep -F 'expected LinuxCNC WASM configured xyzbc-trt RTCP pivot from LinuxCNC trtfuncs inverse' \
test-web-wasm-node-smoke.cjs >/dev/null
grep -F 'expected LinuxCNC WASM configured xyzac-trt RTCP pivot from LinuxCNC xyzab_tdr inverse' \
test-web-wasm-node-smoke.cjs >/dev/null
grep -F 'LinuxCNC source basis: browser smoke verifies OPFS-backed wasm/API/app flows' \
test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'minimum_sections=${BROWSER_SMOKE_MIN_SECTIONS:-50}' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'BROWSER_SMOKE_MIN_SECTIONS must be a positive integer' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'browser WASM smoke covered $passed_sections sections, expected at least $minimum_sections' \
test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'required_browser_sections = {' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("linuxcnc generated switchkins cases load"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("opfs parameter restore in new instance"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("opfs directory persist and load"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("browser opfs policy"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runSection("browser app opfs restore"' test-web-wasm-browser-smoke.sh >/dev/null
grep -F 'runLinuxCncBrowserSections(context, simulator)' web/test-browser-wasm-smoke-sections.js >/dev/null
grep -F 'runBrowserOpfsWorkspaceSections(context, simulator)' web/test-browser-wasm-smoke-sections.js >/dev/null
grep -F 'runBrowserOpfsPolicySections(context)' web/test-browser-wasm-smoke-sections.js >/dev/null
grep -F 'runBrowserAppOpfsSections(context)' web/test-browser-wasm-smoke-sections.js >/dev/null
grep -F 'runBrowserOpfsBasicSections(context, simulator)' \
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
grep -F 'runBrowserOpfsParameterSections(context, simulator)' \
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
grep -F 'runBrowserOpfsMirrorSections(context, simulator)' \
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
grep -F 'runBrowserOpfsDirectorySections(context, simulator)' \
web/test-browser-wasm-smoke-opfs-workspace-sections.js >/dev/null
grep -F 'linuxcnc_switchkins_remap_config_cases.json' \
web/test-browser-wasm-smoke-linuxcnc-sections.js >/dev/null
grep -F 'missing LinuxCNC switchkins config cases after INI_FILE_NAME remap-load filtering' \
web/test-browser-wasm-smoke-linuxcnc-sections.js >/dev/null
grep -F 'LinuxCNC source basis: rs274ngc_pre.cc restore_parameters()' \
web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
grep -F 'LinuxCNC source basis: rs274ngc_pre.cc save_parameters()' \
web/test-browser-wasm-smoke-opfs-parameter-sections.js >/dev/null
grep -F 'LinuxCNC source basis: rs274ngc_pre.cc read_text() reads' \
web/test-browser-wasm-smoke-opfs-basic-sections.js >/dev/null
grep -F 'runSection("opfs directory persist and load"' \
web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
grep -F 'runSection("opfs metadata queries avoid wasm mirror pollution"' \
web/test-browser-wasm-smoke-opfs-directory-sections.js >/dev/null
grep -F 'runSection("browser opfs policy"' \
web/test-browser-wasm-smoke-opfs-policy-sections.js >/dev/null
grep -F 'app frame did not persist current program into OPFS' \
web/test-browser-wasm-smoke-app-sections.js >/dev/null
grep -F 'app frame did not persist LinuxCNC parameter backup into OPFS' \
web/test-browser-wasm-smoke-app-sections.js >/dev/null
grep -F 'app frame did not clean up LinuxCNC temporary parameter file in OPFS' \
web/test-browser-wasm-smoke-app-sections.js >/dev/null
if grep -R -n -E 'localStorage|sessionStorage|indexedDB|showOpenFilePicker|showSaveFilePicker|showDirectoryPicker|webkitRequestFileSystem|FileReader' web/src; then
echo "browser app/wasm code must not add filesystem persistence outside OPFS" >&2
exit 1
fi
if grep -n -E 'localStorage|sessionStorage|indexedDB|showOpenFilePicker|showSaveFilePicker|showDirectoryPicker|webkitRequestFileSystem|FileReader' \
web/test-browser-wasm-smoke-*.js \
web/test-browser-wasm-smoke.html \
test-web-wasm-browser-smoke.sh; then
echo "browser WASM smoke harness must not add filesystem persistence outside OPFS" >&2
exit 1
fi
python3 - <<'PY'
from pathlib import Path
import json
import re
source_map = Path("docs/linuxcnc-wasm-smoke-source-map.md").read_text(encoding="utf-8")
json_cases = json.loads(Path("web/public/linuxcnc_switchkins_remap_config_cases.json").read_text(encoding="utf-8"))
if not isinstance(json_cases, list) or len(json_cases) == 0:
raise SystemExit("generated LinuxCNC switchkins browser config cases are missing")
node_wrapper = Path("test-web-wasm-node-smoke.sh").read_text(encoding="utf-8")
browser_wrapper = Path("test-web-wasm-browser-smoke.sh").read_text(encoding="utf-8")
node_min = re.search(r"minimum_steps=\$\{WASM_NODE_SMOKE_MIN_STEPS:-(\d+)\}", node_wrapper)
browser_min = re.search(r"minimum_sections=\$\{BROWSER_SMOKE_MIN_SECTIONS:-(\d+)\}", browser_wrapper)
if not node_min or int(node_min.group(1)) < 50:
raise SystemExit("Node WASM smoke minimum step threshold is missing or below 50")
if not browser_min or int(browser_min.group(1)) < 50:
raise SystemExit("browser WASM smoke minimum section threshold is missing or below 50")
for path in [
"test-web-wasm-node-smoke.sh",
"test-web-wasm-node-smoke.cjs",
"test-web-wasm-browser-smoke.sh",
"web/test-browser-wasm-smoke-linuxcnc-sections.js",
"web/test-browser-wasm-smoke-opfs-workspace-sections.js",
"web/test-browser-wasm-smoke-opfs-basic-sections.js",
"web/test-browser-wasm-smoke-opfs-parameter-sections.js",
"web/test-browser-wasm-smoke-opfs-mirror-sections.js",
"web/test-browser-wasm-smoke-opfs-directory-sections.js",
"web/test-browser-wasm-smoke-opfs-policy-sections.js",
"web/test-browser-wasm-smoke-app-sections.js",
]:
if f"| `{path}` |" not in source_map:
raise SystemExit(f"docs/linuxcnc-wasm-smoke-source-map.md missing smoke entry: {path}")
required_phrases = [
"must not add CNC behavior",
"must not expand the temporary smoke parser",
"persistence OPFS-only",
"Node smoke must leave OPFS disabled",
"Browser smoke harness files must not use Local Storage",
"Generated switchkins config smoke cases",
"browser smoke shell guard also pins required section names",
"OPFS workspace/policy/directory handling",
'unlinking `filename + ".bak"`',
"./check-linuxcnc-wasm-smoke-source-map.sh",
"does not interpret G-code",
]
for phrase in required_phrases:
if phrase not in source_map:
raise SystemExit(f"docs/linuxcnc-wasm-smoke-source-map.md missing boundary phrase: {phrase}")
for entry in [
"test-native.sh",
"test-linuxcnc-source-link.sh",
"build-wasm.sh",
]:
text = Path(entry).read_text(encoding="utf-8")
if "./check-linuxcnc-wasm-smoke-source-map.sh" not in text:
raise SystemExit(f"{entry} must run ./check-linuxcnc-wasm-smoke-source-map.sh")
PY

View File

@@ -0,0 +1,132 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# LinuxCNC source basis: this guard ties the six active workstream contexts to
# their source-map guards and to LinuxCNC source/config anchors. It validates
# coordination policy only; it does not add CNC behavior or expand smoke
# parsing.
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
grep -F '1. `switchkins/remap` generator and tables' docs/architecture.md >/dev/null
grep -F '2. `linuxcnc-kinematics` source manifest' docs/architecture.md >/dev/null
grep -F '3. WASM filesystem and OPFS persistence' docs/architecture.md >/dev/null
grep -F '4. browser app integration' docs/architecture.md >/dev/null
grep -F '5. Node and browser smoke coverage' docs/architecture.md >/dev/null
grep -F '6. source-link, build, and documentation constraints' docs/architecture.md >/dev/null
grep -F 'The synchronized source-map guard for these contexts is' docs/architecture.md >/dev/null
grep -F '`docs/linuxcnc-workstreams-source-map.md`.' docs/architecture.md >/dev/null
grep -F 'REMAP = M428' "$linuxcnc_root/configs/sim/axis/vismach/5axis/bridgemill/5axis.ini" >/dev/null
grep -F '#<kinstype>' "$linuxcnc_root/configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc" >/dev/null
grep -F 'GENSERKINSSRCS := emc/kinematics/ugenserkins.c' \
"$linuxcnc_root/src/emc/kinematics/Submakefile" >/dev/null
grep -F 'extern int kinematicsInverse(const struct EmcPose * world,' \
"$linuxcnc_root/src/emc/kinematics/kinematics.h" >/dev/null
grep -F '#define RS274NGC_PARAMETER_FILE_NAME_DEFAULT "rs274ngc.var"' \
"$linuxcnc_root/src/emc/rs274ngc/interp_internal.hh" >/dev/null
grep -F 'int Interp::save_parameters(const char *filename,' \
"$linuxcnc_root/src/emc/rs274ngc/rs274ngc_pre.cc" >/dev/null
grep -F 'LIBRS274SRCS := $(addprefix emc/rs274ngc/,' \
"$linuxcnc_root/src/emc/rs274ngc/Submakefile" >/dev/null
grep -F 'LIBTOOLDATA_SRCS += emc/tooldata/tooldata_common.cc' \
"$linuxcnc_root/src/emc/tooldata/Submakefile" >/dev/null
grep -F 'LIBLCNCINISRCS := emc/ini/inifile.cc' \
"$linuxcnc_root/src/emc/ini/Submakefile" >/dev/null
grep -F 'LIBPPSRCS := $(addprefix emc/pythonplugin/,' \
"$linuxcnc_root/src/emc/pythonplugin/Submakefile" >/dev/null
grep -F 'LinuxCNC workstreams source map' docs/linuxcnc-workstreams-source-map.md >/dev/null
grep -F 'must not add CNC' docs/linuxcnc-workstreams-source-map.md >/dev/null
grep -F 'temporary smoke parser' docs/linuxcnc-workstreams-source-map.md >/dev/null
grep -F 'Every active workstream must have at least one source-map document and guard' \
docs/linuxcnc-workstreams-source-map.md >/dev/null
grep -F 'docs/linuxcnc-source-policy.md` must keep the same one-context workflow' \
docs/linuxcnc-workstreams-source-map.md >/dev/null
grep -F 'Browser filesystem persistence remains OPFS-only.' docs/linuxcnc-workstreams-source-map.md >/dev/null
grep -F 'Smoke coverage remains verification only' docs/linuxcnc-workstreams-source-map.md >/dev/null
grep -F 'Keep the change inside one active workstream context unless a dependency is' \
docs/linuxcnc-source-policy.md >/dev/null
grep -F 'Each pass should advance one reasonable, reviewable context.' \
docs/linuxcnc-source-policy.md >/dev/null
grep -F 'Keep the six active workstream contexts synchronized with' \
docs/linuxcnc-source-policy.md >/dev/null
grep -F '`docs/linuxcnc-workstreams-source-map.md` so source-map guards' \
docs/linuxcnc-source-policy.md >/dev/null
grep -F 'Smoke tests are verification guardrails. They must not be expanded with new' \
docs/linuxcnc-source-policy.md >/dev/null
grep -F 'Do not mix generator/table work, manifests, OPFS/browser work, and build' \
docs/linuxcnc-source-policy.md >/dev/null
grep -F 'Browser-side wasm filesystem behavior must use OPFS-backed storage.' \
docs/linuxcnc-source-policy.md >/dev/null
python3 - <<'PY'
from pathlib import Path
doc = Path("docs/linuxcnc-workstreams-source-map.md").read_text(encoding="utf-8")
policy = Path("docs/linuxcnc-source-policy.md").read_text(encoding="utf-8")
expected = {
"switchkins/remap": [
"docs/linuxcnc-switchkins-remap-source-map.md",
"check-linuxcnc-switchkins-remap-source-map.sh",
],
"linuxcnc-kinematics": [
"docs/linuxcnc-kinematics-source-map.md",
"check-linuxcnc-kinematics-source-map.sh",
],
"WASM filesystem and OPFS persistence": [
"docs/linuxcnc-opfs-source-map.md",
"docs/linuxcnc-wasm-shims-source-map.md",
"docs/linuxcnc-wasm-blockers-source-map.md",
"docs/linuxcnc-tooldata-source-map.md",
"check-linuxcnc-opfs-source-map.sh",
"check-linuxcnc-wasm-shims-source-map.sh",
"check-linuxcnc-wasm-blockers-source-map.sh",
"check-linuxcnc-tooldata-source-map.sh",
],
"browser app integration": [
"docs/linuxcnc-browser-app-source-map.md",
"check-linuxcnc-browser-app-source-map.sh",
],
"Node and browser smoke coverage": [
"docs/linuxcnc-wasm-smoke-source-map.md",
"check-linuxcnc-wasm-smoke-source-map.sh",
],
"source-link, build, and documentation constraints": [
"docs/linuxcnc-rs274-source-map.md",
"docs/linuxcnc-build-source-map.md",
"docs/linuxcnc-tooldata-source-map.md",
"check-linuxcnc-rs274-source-map.sh",
"check-linuxcnc-build-source-map.sh",
"check-linuxcnc-tooldata-source-map.sh",
],
}
for workstream, paths in expected.items():
if workstream not in doc:
raise SystemExit(f"missing workstream row in docs/linuxcnc-workstreams-source-map.md: {workstream}")
policy_terms = {
"switchkins/remap": ["switchkins/remap", "M428/M429/M430"],
"linuxcnc-kinematics": ["kinematics manifest", "RTCP coverage"],
"WASM filesystem and OPFS persistence": ["OPFS", "filesystem"],
"browser app integration": ["Browser app", "wasm bridge"],
"Node and browser smoke coverage": ["Smoke tests are verification guardrails", "temporary smoke parser"],
"source-link, build, and documentation constraints": ["source-link", "build-policy"],
}[workstream]
if not any(term in policy for term in policy_terms):
raise SystemExit(f"docs/linuxcnc-source-policy.md no longer references workstream boundary: {workstream}")
for path in paths:
if path not in doc:
raise SystemExit(f"missing source-map path in workstream index: {path}")
if not Path(path).exists():
raise SystemExit(f"source-map path listed in workstream index does not exist: {path}")
if path.startswith("check-") and not Path(path).stat().st_mode & 0o111:
raise SystemExit(f"source-map guard is not executable: {path}")
for test_entry in ["test-native.sh", "test-linuxcnc-source-link.sh", "build-wasm.sh"]:
text = Path(test_entry).read_text(encoding="utf-8")
for guard in sorted({path for paths in expected.values() for path in paths if path.startswith("check-")}):
if f"./{guard}" not in text:
raise SystemExit(f"{test_entry} does not run workstream source-map guard: {guard}")
PY

View File

@@ -1,31 +1,224 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
project(cnc_sim_wasm_core LANGUAGES CXX) project(cnc_sim_wasm_core LANGUAGES C CXX)
option(CNC_SIM_ENABLE_LINUXCNC_BRIDGE "Build the experimental LinuxCNC canon bridge" OFF) option(CNC_SIM_ENABLE_LINUXCNC_BRIDGE "Build the experimental LinuxCNC canon bridge" OFF)
option(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND "Build the native LinuxCNC librs274 backend" OFF) option(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND "Build the native LinuxCNC librs274 backend" OFF)
option(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE "Compile the browser-safe subset of LinuxCNC rs274 sources" OFF)
option(CNC_SIM_ENABLE_SMOKE_BACKEND "Build the legacy explicit smoke G-code backend test harness" OFF)
if(CNC_SIM_ENABLE_SMOKE_BACKEND AND CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE)
message(FATAL_ERROR "CNC_SIM_ENABLE_SMOKE_BACKEND cannot be combined with CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE")
endif()
set(CNC_SIM_LINUXCNC_ROOT "" CACHE PATH "LinuxCNC source root for the experimental bridge") set(CNC_SIM_LINUXCNC_ROOT "" CACHE PATH "LinuxCNC source root for the experimental bridge")
set(CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST
"${CMAKE_CURRENT_SOURCE_DIR}/../linuxcnc-rs274-wasm-source-files.txt"
CACHE FILEPATH
"Manifest that partitions LinuxCNC rs274 sources into wasm-safe core and blocked groups"
)
set(cnc_sim_core_sources set(cnc_sim_core_sources
src/canon_event_sink.cpp src/canon_event_sink.cpp
src/cnc_sim_api.cpp src/cnc_sim_api.cpp
src/gcode_backend.cpp src/gcode_backend.cpp
src/linuxcnc_5axiskins_adapter.c
src/linuxcnc_5axiskins_adapter.cpp
src/linuxcnc_corexykins_adapter.c
src/linuxcnc_corexykins_adapter.cpp
src/linuxcnc_cubic_adapter.c
src/linuxcnc_cubic_adapter.cpp
src/linuxcnc_genserfuncs_adapter.c
src/linuxcnc_genserfuncs_adapter.cpp
src/linuxcnc_genhexkins_adapter.c
src/linuxcnc_genhexkins_adapter.cpp
src/linuxcnc_kins_util_adapter.c
src/linuxcnc_kins_util_adapter.cpp
src/linuxcnc_lineardeltakins_adapter.c
src/linuxcnc_lineardeltakins_adapter.cpp
src/linuxcnc_maxkins_adapter.c
src/linuxcnc_maxkins_adapter.cpp
src/linuxcnc_pentakins_adapter.c
src/linuxcnc_pentakins_adapter.cpp
src/linuxcnc_pumakins_adapter.c
src/linuxcnc_pumakins_adapter.cpp
src/linuxcnc_rotatekins_adapter.c
src/linuxcnc_rotatekins_adapter.cpp
src/linuxcnc_rosekins_adapter.c
src/linuxcnc_rosekins_adapter.cpp
src/linuxcnc_rotarydeltakins_adapter.c
src/linuxcnc_rotarydeltakins_adapter.cpp
src/linuxcnc_scarakins_adapter.c
src/linuxcnc_scarakins_adapter.cpp
src/linuxcnc_scorbot_kins_adapter.c
src/linuxcnc_scorbot_kins_adapter.cpp
src/linuxcnc_tripodkins_adapter.c
src/linuxcnc_tripodkins_adapter.cpp
src/linuxcnc_trtfuncs_adapter.c
src/linuxcnc_trtfuncs_adapter.cpp
src/linuxcnc_userkfuncs_adapter.c
src/linuxcnc_userkfuncs_adapter.cpp
src/linuxcnc_xyzab_tdr_kins_adapter.c
src/linuxcnc_xyzab_tdr_kins_adapter.cpp
src/rtcp_kinematics.cpp src/rtcp_kinematics.cpp
src/simulator_gcode_controls.cpp src/simulator_gcode_controls.cpp
)
set(cnc_sim_smoke_backend_sources
src/smoke_gcode_parser.cpp src/smoke_gcode_parser.cpp
) )
set(cnc_sim_linuxcnc_rs274_backend_sources
src/linuxcnc_canon_bridge.cpp
src/linuxcnc_rs274_backend.cpp
src/linuxcnc_tooldata_fixture.cpp
)
if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND) if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
if(CNC_SIM_ENABLE_SMOKE_BACKEND)
message(FATAL_ERROR "CNC_SIM_ENABLE_SMOKE_BACKEND cannot be combined with CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND")
endif()
if(EMSCRIPTEN) if(EMSCRIPTEN)
message(FATAL_ERROR "CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND is native-only; it links LinuxCNC librs274") message(FATAL_ERROR "CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND is native-only; it links LinuxCNC librs274")
endif() endif()
if(NOT CNC_SIM_LINUXCNC_ROOT) if(NOT CNC_SIM_LINUXCNC_ROOT)
message(FATAL_ERROR "Set CNC_SIM_LINUXCNC_ROOT to the LinuxCNC source root") message(FATAL_ERROR "Set CNC_SIM_LINUXCNC_ROOT to the LinuxCNC source root")
endif() endif()
list(APPEND cnc_sim_core_sources list(APPEND cnc_sim_core_sources ${cnc_sim_linuxcnc_rs274_backend_sources})
src/linuxcnc_canon_bridge.cpp elseif(CNC_SIM_ENABLE_SMOKE_BACKEND)
src/linuxcnc_rs274_backend.cpp list(APPEND cnc_sim_core_sources ${cnc_sim_smoke_backend_sources})
src/linuxcnc_tooldata_fixture.cpp endif()
if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE)
if(NOT CNC_SIM_LINUXCNC_ROOT)
message(FATAL_ERROR "Set CNC_SIM_LINUXCNC_ROOT to the LinuxCNC source root")
endif()
get_filename_component(CNC_SIM_LINUXCNC_ROOT_ABS
"${CNC_SIM_LINUXCNC_ROOT}"
ABSOLUTE
BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.."
) )
if(NOT IS_DIRECTORY "${CNC_SIM_LINUXCNC_ROOT_ABS}")
message(FATAL_ERROR "LinuxCNC source root does not exist: ${CNC_SIM_LINUXCNC_ROOT_ABS}")
endif()
if(NOT EXISTS "${CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST}")
message(FATAL_ERROR "Missing LinuxCNC wasm source manifest: ${CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST}")
endif()
if(IS_DIRECTORY "${CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST}")
message(FATAL_ERROR "LinuxCNC wasm source manifest is not a file: ${CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST}")
endif()
set(linuxcnc_wasm_safe_probe_sources)
set(linuxcnc_wasm_blocked_sources)
file(STRINGS "${CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST}" linuxcnc_wasm_manifest_lines)
foreach(manifest_line IN LISTS linuxcnc_wasm_manifest_lines)
string(STRIP "${manifest_line}" manifest_line)
if(manifest_line STREQUAL "" OR manifest_line MATCHES "^#")
continue()
endif()
if(NOT manifest_line MATCHES "^([^:]+):([^:]+):(.*)$")
message(FATAL_ERROR "Bad LinuxCNC wasm source manifest line: ${manifest_line}")
endif()
set(manifest_group "${CMAKE_MATCH_1}")
set(manifest_path "${CMAKE_MATCH_2}")
if(NOT manifest_group STREQUAL "core"
AND NOT manifest_group STREQUAL "blocked"
AND NOT manifest_group STREQUAL "header"
AND NOT manifest_group STREQUAL "metadata"
AND NOT manifest_group STREQUAL "blocked-header")
message(FATAL_ERROR "Unknown LinuxCNC wasm source manifest group: ${manifest_group}")
endif()
set(manifest_source "${CNC_SIM_LINUXCNC_ROOT_ABS}/${manifest_path}")
if(NOT EXISTS "${manifest_source}")
message(FATAL_ERROR "LinuxCNC wasm manifest source does not exist: ${manifest_source}")
endif()
if(IS_DIRECTORY "${manifest_source}")
message(FATAL_ERROR "LinuxCNC wasm manifest source is not a file: ${manifest_source}")
endif()
if(manifest_group STREQUAL "core")
list(APPEND linuxcnc_wasm_safe_probe_sources "${manifest_source}")
elseif(manifest_group STREQUAL "blocked")
list(APPEND linuxcnc_wasm_blocked_sources "${manifest_source}")
endif()
endforeach()
list(LENGTH linuxcnc_wasm_safe_probe_sources linuxcnc_wasm_safe_probe_source_count)
list(LENGTH linuxcnc_wasm_blocked_sources linuxcnc_wasm_blocked_source_count)
if(linuxcnc_wasm_safe_probe_source_count EQUAL 0)
message(FATAL_ERROR "LinuxCNC wasm source manifest has no core entries")
endif()
if(linuxcnc_wasm_blocked_source_count EQUAL 0)
message(FATAL_ERROR "LinuxCNC wasm source manifest has no blocked entries")
endif()
message(STATUS "LinuxCNC wasm-safe core sources: ${linuxcnc_wasm_safe_probe_source_count}")
message(STATUS "LinuxCNC wasm blocked sources: ${linuxcnc_wasm_blocked_source_count}")
set(cnc_sim_wasm_safe_preinclude_options "SHELL:-include wctype.h")
set(cnc_sim_wasm_safe_section_options -ffunction-sections -fdata-sections)
set(cnc_sim_wasm_safe_link_options "-Wl,--gc-sections")
if(EMSCRIPTEN)
list(APPEND cnc_sim_wasm_safe_preinclude_options "SHELL:-include libgen.h")
find_path(CNC_SIM_FMT_INCLUDE_DIR fmt/format.h
PATHS /usr/include /usr/local/include
NO_CMAKE_FIND_ROOT_PATH
REQUIRED
)
list(APPEND cnc_sim_wasm_safe_preinclude_options "SHELL:-idirafter ${CNC_SIM_FMT_INCLUDE_DIR}")
endif()
set(linuxcnc_wasm_safe_probe_shims
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/dlfcn.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/emc_status_shim.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/gettext_shim.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/linuxcnc_runtime_shim.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/nml_status_shim.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/python_c_api_shim.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/rcs_print_shim.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/rtapi_compat.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/tooldata/tooldata_mmap_backend.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/tooldata/tooldata_runtime_stubs.cc
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/pythonplugin/python_plugin.cc
)
add_library(linuxcnc_rs274_wasm_safe_probe_objects OBJECT
${linuxcnc_wasm_safe_probe_shims}
${linuxcnc_wasm_safe_probe_sources}
)
target_compile_features(linuxcnc_rs274_wasm_safe_probe_objects PUBLIC cxx_std_20)
target_compile_definitions(linuxcnc_rs274_wasm_safe_probe_objects PRIVATE ULAPI)
target_compile_options(linuxcnc_rs274_wasm_safe_probe_objects PRIVATE
${cnc_sim_wasm_safe_preinclude_options}
${cnc_sim_wasm_safe_section_options}
)
target_include_directories(linuxcnc_rs274_wasm_safe_probe_objects
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims
${CNC_SIM_LINUXCNC_ROOT_ABS}/src
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/nml_intf
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/rs274ngc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/motion
${CNC_SIM_LINUXCNC_ROOT_ABS}/include
)
if(EMSCRIPTEN)
target_compile_definitions(linuxcnc_rs274_wasm_safe_probe_objects PRIVATE FMT_HEADER_ONLY=1)
endif()
set(cnc_sim_wasm_runtime_probe_main
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc
)
if(linuxcnc_wasm_blocked_sources)
add_custom_target(linuxcnc_rs274_wasm_blocked_sources
SOURCES ${linuxcnc_wasm_blocked_sources}
)
endif()
endif()
if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE)
list(APPEND cnc_sim_core_sources ${cnc_sim_linuxcnc_rs274_backend_sources})
endif() endif()
add_library(cnc_sim_objects OBJECT add_library(cnc_sim_objects OBJECT
@@ -37,9 +230,36 @@ target_include_directories(cnc_sim_objects
${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/../../linuxcnc/include
) )
target_compile_features(cnc_sim_objects PUBLIC cxx_std_17) target_compile_features(cnc_sim_objects PUBLIC cxx_std_17)
set(cnc_sim_linuxcnc_kinematics_c_sources
src/linuxcnc_5axiskins_adapter.c
src/linuxcnc_corexykins_adapter.c
src/linuxcnc_cubic_adapter.c
src/linuxcnc_genserfuncs_adapter.c
src/linuxcnc_genhexkins_adapter.c
src/linuxcnc_kins_util_adapter.c
src/linuxcnc_lineardeltakins_adapter.c
src/linuxcnc_maxkins_adapter.c
src/linuxcnc_pentakins_adapter.c
src/linuxcnc_pumakins_adapter.c
src/linuxcnc_rotatekins_adapter.c
src/linuxcnc_rosekins_adapter.c
src/linuxcnc_rotarydeltakins_adapter.c
src/linuxcnc_scarakins_adapter.c
src/linuxcnc_scorbot_kins_adapter.c
src/linuxcnc_tripodkins_adapter.c
src/linuxcnc_trtfuncs_adapter.c
src/linuxcnc_userkfuncs_adapter.c
src/linuxcnc_xyzab_tdr_kins_adapter.c
)
set_source_files_properties(
${cnc_sim_linuxcnc_kinematics_c_sources}
PROPERTIES
COMPILE_OPTIONS "$<$<COMPILE_LANGUAGE:CXX>:-fpermissive>"
)
if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND) if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
find_package(Python3 REQUIRED COMPONENTS Development) find_package(Python3 REQUIRED COMPONENTS Development)
@@ -59,6 +279,97 @@ if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
) )
endif() endif()
if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE)
target_compile_definitions(cnc_sim_objects
PRIVATE
CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND
CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE
ULAPI
)
target_compile_options(cnc_sim_objects PRIVATE ${cnc_sim_wasm_safe_preinclude_options})
target_include_directories(cnc_sim_objects
PRIVATE
${CNC_SIM_LINUXCNC_ROOT_ABS}/src
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/nml_intf
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/rs274ngc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/motion
${CNC_SIM_LINUXCNC_ROOT_ABS}/include
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims
)
if(EMSCRIPTEN)
target_compile_definitions(cnc_sim_objects PRIVATE FMT_HEADER_ONLY=1)
endif()
endif()
if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE)
add_executable(linuxcnc_rs274_wasm_safe_probe EXCLUDE_FROM_ALL
$<TARGET_OBJECTS:linuxcnc_rs274_wasm_safe_probe_objects>
$<TARGET_OBJECTS:cnc_sim_objects>
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims/rs274ngc_pre_probe_main.cc
)
target_compile_features(linuxcnc_rs274_wasm_safe_probe PRIVATE cxx_std_20)
target_compile_definitions(linuxcnc_rs274_wasm_safe_probe
PRIVATE
CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND
CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE
ULAPI
)
target_compile_options(linuxcnc_rs274_wasm_safe_probe PRIVATE ${cnc_sim_wasm_safe_preinclude_options})
target_link_options(linuxcnc_rs274_wasm_safe_probe PRIVATE ${cnc_sim_wasm_safe_link_options})
target_include_directories(linuxcnc_rs274_wasm_safe_probe
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims
${CNC_SIM_LINUXCNC_ROOT_ABS}/src
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/nml_intf
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/rs274ngc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/motion
${CNC_SIM_LINUXCNC_ROOT_ABS}/include
)
if(EMSCRIPTEN)
target_compile_definitions(linuxcnc_rs274_wasm_safe_probe PRIVATE FMT_HEADER_ONLY=1)
else()
target_link_libraries(linuxcnc_rs274_wasm_safe_probe PRIVATE fmt)
endif()
add_executable(cnc_sim_wasm_runtime_probe EXCLUDE_FROM_ALL
$<TARGET_OBJECTS:linuxcnc_rs274_wasm_safe_probe_objects>
$<TARGET_OBJECTS:cnc_sim_objects>
${cnc_sim_wasm_runtime_probe_main}
)
target_compile_features(cnc_sim_wasm_runtime_probe PRIVATE cxx_std_20)
target_compile_definitions(cnc_sim_wasm_runtime_probe PRIVATE ULAPI)
target_compile_options(cnc_sim_wasm_runtime_probe PRIVATE ${cnc_sim_wasm_safe_preinclude_options})
target_link_options(cnc_sim_wasm_runtime_probe PRIVATE ${cnc_sim_wasm_safe_link_options})
target_include_directories(cnc_sim_wasm_runtime_probe
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims
${CNC_SIM_LINUXCNC_ROOT_ABS}/src
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/nml_intf
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/rs274ngc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/motion
${CNC_SIM_LINUXCNC_ROOT_ABS}/include
)
if(EMSCRIPTEN)
target_compile_definitions(cnc_sim_wasm_runtime_probe PRIVATE FMT_HEADER_ONLY=1)
else()
target_link_libraries(cnc_sim_wasm_runtime_probe PRIVATE fmt)
endif()
endif()
if(CNC_SIM_ENABLE_SMOKE_BACKEND)
target_compile_definitions(cnc_sim_objects
PRIVATE
CNC_SIM_ENABLE_SMOKE_BACKEND
)
endif()
add_library(cnc_sim_core STATIC add_library(cnc_sim_core STATIC
$<TARGET_OBJECTS:cnc_sim_objects> $<TARGET_OBJECTS:cnc_sim_objects>
) )
@@ -88,11 +399,13 @@ if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
endif() endif()
if(NOT EMSCRIPTEN) if(NOT EMSCRIPTEN)
if(CNC_SIM_ENABLE_SMOKE_BACKEND)
add_executable(cnc_sim_api_smoke add_executable(cnc_sim_api_smoke
tests/cnc_sim_api_smoke.cpp tests/cnc_sim_api_smoke.cpp
) )
target_link_libraries(cnc_sim_api_smoke PRIVATE cnc_sim_core) target_link_libraries(cnc_sim_api_smoke PRIVATE cnc_sim_core)
target_include_directories(cnc_sim_api_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(cnc_sim_api_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
endif()
add_executable(canon_event_sink_smoke add_executable(canon_event_sink_smoke
tests/canon_event_sink_smoke.cpp tests/canon_event_sink_smoke.cpp
@@ -106,17 +419,24 @@ if(NOT EMSCRIPTEN)
target_link_libraries(rtcp_kinematics_smoke PRIVATE cnc_sim_core) target_link_libraries(rtcp_kinematics_smoke PRIVATE cnc_sim_core)
target_include_directories(rtcp_kinematics_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(rtcp_kinematics_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
add_executable(linuxcnc_gees_table_smoke
tests/linuxcnc_gees_table_smoke.cpp
)
target_include_directories(linuxcnc_gees_table_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
add_executable(simulator_gcode_controls_smoke add_executable(simulator_gcode_controls_smoke
tests/simulator_gcode_controls_smoke.cpp tests/simulator_gcode_controls_smoke.cpp
) )
target_link_libraries(simulator_gcode_controls_smoke PRIVATE cnc_sim_core) target_link_libraries(simulator_gcode_controls_smoke PRIVATE cnc_sim_core)
target_include_directories(simulator_gcode_controls_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(simulator_gcode_controls_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
if(CNC_SIM_ENABLE_SMOKE_BACKEND)
add_executable(cnc_sim_dump add_executable(cnc_sim_dump
tools/cnc_sim_dump.cpp tools/cnc_sim_dump.cpp
) )
target_link_libraries(cnc_sim_dump PRIVATE cnc_sim_core) target_link_libraries(cnc_sim_dump PRIVATE cnc_sim_core)
target_include_directories(cnc_sim_dump PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(cnc_sim_dump PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
endif()
if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND) if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND)
add_executable(cnc_sim_api_linuxcnc_rs274_smoke add_executable(cnc_sim_api_linuxcnc_rs274_smoke
@@ -156,7 +476,11 @@ if(CNC_SIM_ENABLE_LINUXCNC_BRIDGE)
endif() endif()
if(EMSCRIPTEN) if(EMSCRIPTEN)
if(NOT CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE)
message(FATAL_ERROR "WASM builds must enable CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE so LinuxCNC rs274 sources are linked")
endif()
add_executable(cnc_sim_wasm add_executable(cnc_sim_wasm
$<TARGET_OBJECTS:linuxcnc_rs274_wasm_safe_probe_objects>
$<TARGET_OBJECTS:cnc_sim_objects> $<TARGET_OBJECTS:cnc_sim_objects>
) )
@@ -164,7 +488,15 @@ if(EMSCRIPTEN)
PRIVATE PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/wasm_shims
${CNC_SIM_LINUXCNC_ROOT_ABS}/src
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/nml_intf
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/rs274ngc
${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/motion
${CNC_SIM_LINUXCNC_ROOT_ABS}/include
) )
target_compile_definitions(cnc_sim_wasm PRIVATE FMT_HEADER_ONLY=1)
set_target_properties(cnc_sim_wasm PROPERTIES set_target_properties(cnc_sim_wasm PROPERTIES
OUTPUT_NAME "cnc_sim" OUTPUT_NAME "cnc_sim"
@@ -172,12 +504,14 @@ if(EMSCRIPTEN)
) )
target_link_options(cnc_sim_wasm PRIVATE target_link_options(cnc_sim_wasm PRIVATE
${cnc_sim_wasm_safe_link_options}
"--no-entry" "--no-entry"
"-sMODULARIZE=1" "-sMODULARIZE=1"
"-sEXPORT_NAME=createCncSimModule" "-sEXPORT_NAME=createCncSimModule"
"-sALLOW_MEMORY_GROWTH=1" "-sALLOW_MEMORY_GROWTH=1"
"-sALLOW_TABLE_GROWTH=1" "-sALLOW_TABLE_GROWTH=1"
"-sFORCE_FILESYSTEM=1"
"-sEXPORTED_FUNCTIONS=['_malloc','_free','_cnc_sim_create','_cnc_sim_destroy','_cnc_sim_reset','_cnc_sim_set_dialect','_cnc_sim_set_event_callback','_cnc_sim_load_config_json','_cnc_sim_parse_program','_cnc_sim_last_error']" "-sEXPORTED_FUNCTIONS=['_malloc','_free','_cnc_sim_create','_cnc_sim_destroy','_cnc_sim_reset','_cnc_sim_set_dialect','_cnc_sim_set_event_callback','_cnc_sim_load_config_json','_cnc_sim_parse_program','_cnc_sim_last_error']"
"-sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','addFunction','removeFunction']" "-sEXPORTED_RUNTIME_METHODS=['FS','ccall','cwrap','UTF8ToString','stringToUTF8','lengthBytesUTF8','addFunction','removeFunction']"
) )
endif() endif()

View File

@@ -1,6 +1,23 @@
#include "canon_event_sink.h" #include "canon_event_sink.h"
#include "rtcp_kinematics.h" #include <cmath>
namespace {
constexpr int kCssSpindleModeEvent = 960;
constexpr int kConstantRpmSpindleModeEvent = 970;
void rotate_xy(double *x, double *y, double degrees) {
const double radians = degrees * std::acos(-1.0) / 180.0;
const double c = std::cos(radians);
const double s = std::sin(radians);
const double nx = *x * c - *y * s;
const double ny = *x * s + *y * c;
*x = nx;
*y = ny;
}
} // namespace
void CanonEventSink::reset() { void CanonEventSink::reset() {
position_ = {}; position_ = {};
@@ -14,14 +31,19 @@ void CanonEventSink::reset() {
probe_position_ = {}; probe_position_ = {};
probe_tripped_ = false; probe_tripped_ = false;
tool_length_offset_ = {}; tool_length_offset_ = {};
xy_rotation_degrees_ = 0.0;
callback_status_ = 0; callback_status_ = 0;
kinematics_type_ = 1; // LinuxCNC xyzbc-trt-kins with sparm=identityfirst starts at switchkins-type 0.
// Source: linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini.
kinematics_type_ = 0;
feed_override_ = false; feed_override_ = false;
speed_override_ = false; speed_override_ = false;
adaptive_feed_ = false; adaptive_feed_ = false;
feed_hold_ = false; feed_hold_ = false;
mist_on_ = false; mist_on_ = false;
flood_on_ = false; flood_on_ = false;
has_pending_motion_start_ = false;
pending_motion_start_ = {};
} }
void CanonEventSink::set_callback(CncSimEventCallback callback, void *user_data) { void CanonEventSink::set_callback(CncSimEventCallback callback, void *user_data) {
@@ -38,12 +60,41 @@ bool CanonEventSink::callback_aborted() const {
return callback_status_ != 0; return callback_status_ != 0;
} }
void CanonEventSink::set_block_delete(bool enabled) {
block_delete_ = enabled;
}
void CanonEventSink::configure_rtcp(bool enabled, double tool_length) { void CanonEventSink::configure_rtcp(bool enabled, double tool_length) {
rtcp_enabled_ = enabled; rtcp_enabled_ = enabled;
default_rtcp_tool_length_ = tool_length; default_rtcp_tool_length_ = tool_length;
rtcp_tool_length_ = tool_length; rtcp_tool_length_ = tool_length;
} }
void CanonEventSink::configure_xyzbc_trt(const LinuxCncXyzbcTrtParameters &parameters) {
xyzbc_trt_parameters_ = parameters;
xyzbc_trt_parameters_configured_ = true;
use_xyzac_trt_ = false;
}
void CanonEventSink::configure_xyzac_trt(const LinuxCncXyzbcTrtParameters &parameters) {
xyzbc_trt_parameters_ = parameters;
xyzbc_trt_parameters_configured_ = true;
use_xyzac_trt_ = true;
}
void CanonEventSink::configure_fiveaxis(double pivot_length, int kinematics_type) {
fiveaxis_configured_ = true;
fiveaxis_pivot_length_ = pivot_length;
fiveaxis_kinematics_type_ = kinematics_type;
}
void CanonEventSink::configure_switchkins_remap(int m428_type, int m429_type, int m430_type) {
switchkins_remap_configured_ = true;
switchkins_m428_type_ = m428_type;
switchkins_m429_type_ = m429_type;
switchkins_m430_type_ = m430_type;
}
void CanonEventSink::set_tool_length(int h_code, double tool_length) { void CanonEventSink::set_tool_length(int h_code, double tool_length) {
if (h_code <= 0) { if (h_code <= 0) {
default_rtcp_tool_length_ = tool_length; default_rtcp_tool_length_ = tool_length;
@@ -62,6 +113,26 @@ void CanonEventSink::set_tool_length_offset(const CncSimPose &offset) {
tool_length_offset_ = offset; tool_length_offset_ = offset;
} }
void CanonEventSink::apply_tool_length_offset(const CncSimPose &offset) {
if (!has_pending_motion_start_) {
pending_motion_start_ = position_;
has_pending_motion_start_ = true;
}
double dx = tool_length_offset_.x - offset.x;
double dy = tool_length_offset_.y - offset.y;
rotate_xy(&dx, &dy, -xy_rotation_degrees_);
position_.x += dx;
position_.y += dy;
position_.z += tool_length_offset_.z - offset.z;
position_.a += tool_length_offset_.a - offset.a;
position_.b += tool_length_offset_.b - offset.b;
position_.c += tool_length_offset_.c - offset.c;
position_.u += tool_length_offset_.u - offset.u;
position_.v += tool_length_offset_.v - offset.v;
position_.w += tool_length_offset_.w - offset.w;
tool_length_offset_ = offset;
}
void CanonEventSink::set_rtcp_state(bool enabled, int h_code, int line) { void CanonEventSink::set_rtcp_state(bool enabled, int h_code, int line) {
rtcp_enabled_ = enabled; rtcp_enabled_ = enabled;
rtcp_h_code_ = enabled ? h_code : 0; rtcp_h_code_ = enabled ? h_code : 0;
@@ -76,15 +147,72 @@ void CanonEventSink::set_rtcp_state(bool enabled, int h_code, int line) {
emit(event); emit(event);
} }
void CanonEventSink::switch_kinematics(int kinematics_type, bool rtcp_enabled, int line) { void CanonEventSink::switch_kinematics(int kinematics_type, int line) {
kinematics_type_ = kinematics_type; kinematics_type_ = kinematics_type;
rtcp_enabled_ = rtcp_enabled; // Source: linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/{428,429,430}remap.ngc.
// M68 E3 Q#<kinstype>, then M66 E0 L0 to force synchronization.
set_analog_output(3, static_cast<double>(kinematics_type_), false, line);
wait_input(0, 0, 0, 0.0, line);
CncSimEvent event = base_event(CNC_SIM_EVENT_KINEMATICS_SWITCH, line); CncSimEvent event = base_event(CNC_SIM_EVENT_KINEMATICS_SWITCH, line);
event.reserved = kinematics_type_; event.reserved = kinematics_type_;
event.feed = rtcp_enabled_ ? 1.0 : 0.0;
emit(event); emit(event);
} }
bool CanonEventSink::can_switch_m_code_kinematics(int m_code) const {
if (!switchkins_remap_configured_) {
return true;
}
if (m_code == 428) {
return switchkins_m428_type_ >= 0;
}
if (m_code == 429) {
return switchkins_m429_type_ >= 0;
}
if (m_code == 430) {
return switchkins_m430_type_ >= 0;
}
return true;
}
bool CanonEventSink::switch_m_code_kinematics(int m_code, int default_kinematics_type, int line) {
if (switchkins_remap_configured_) {
int kinematics_type = -1;
if (m_code == 428) {
kinematics_type = switchkins_m428_type_;
} else if (m_code == 429) {
kinematics_type = switchkins_m429_type_;
} else if (m_code == 430) {
kinematics_type = switchkins_m430_type_;
}
if (kinematics_type >= 0) {
switch_kinematics(kinematics_type, line);
return true;
}
return false;
}
if (fiveaxis_configured_) {
// Sources:
// - linuxcnc/configs/sim/axis/vismach/5axis/bridgemill/remap_subs/{428,429,430}remap.ngc
// - linuxcnc/configs/sim/axis/vismach/scara/remap_subs/{428,429,430}remap.ngc
// - linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/{428,429,430}remap.ngc
// These remaps select kinstype 0, 1, and 2 respectively.
if (m_code == 428) {
switch_kinematics(0, line);
return true;
}
if (m_code == 429) {
switch_kinematics(1, line);
return true;
}
if (m_code == 430) {
switch_kinematics(2, line);
return true;
}
}
switch_kinematics(default_kinematics_type, line);
return true;
}
void CanonEventSink::set_g5x_offset(int index, const CncSimPose &offset, int line) { void CanonEventSink::set_g5x_offset(int index, const CncSimPose &offset, int line) {
CncSimEvent event = base_event(CNC_SIM_EVENT_SET_G5X_OFFSET, line); CncSimEvent event = base_event(CNC_SIM_EVENT_SET_G5X_OFFSET, line);
event.tool = index; event.tool = index;
@@ -99,12 +227,48 @@ void CanonEventSink::set_g92_offset(const CncSimPose &offset, int line) {
} }
void CanonEventSink::set_xy_rotation(double angle_degrees, int line) { void CanonEventSink::set_xy_rotation(double angle_degrees, int line) {
xy_rotation_degrees_ = angle_degrees;
CncSimEvent event = base_event(CNC_SIM_EVENT_SET_XY_ROTATION, line); CncSimEvent event = base_event(CNC_SIM_EVENT_SET_XY_ROTATION, line);
event.feed = angle_degrees; event.feed = angle_degrees;
emit(event); emit(event);
} }
void CanonEventSink::use_length_units(double scale, int line) { void CanonEventSink::use_length_units(double scale, int line) {
use_length_units(scale, line, false);
}
void CanonEventSink::use_length_units(double scale, int line, bool rescale_linear_state) {
if (rescale_linear_state && unit_scale_ != 0.0 && scale != unit_scale_) {
const double factor = unit_scale_ / scale;
// LinuxCNC src/emc/rs274ngc/interp_convert.cc convert_length_units()
// rescales linear current axes on G20/G21; A/B/C remain angular.
position_.x *= factor;
position_.y *= factor;
position_.z *= factor;
position_.u *= factor;
position_.v *= factor;
position_.w *= factor;
tool_length_offset_.x *= factor;
tool_length_offset_.y *= factor;
tool_length_offset_.z *= factor;
tool_length_offset_.u *= factor;
tool_length_offset_.v *= factor;
tool_length_offset_.w *= factor;
probe_position_.x *= factor;
probe_position_.y *= factor;
probe_position_.z *= factor;
probe_position_.u *= factor;
probe_position_.v *= factor;
probe_position_.w *= factor;
if (has_pending_motion_start_) {
pending_motion_start_.x *= factor;
pending_motion_start_.y *= factor;
pending_motion_start_.z *= factor;
pending_motion_start_.u *= factor;
pending_motion_start_.v *= factor;
pending_motion_start_.w *= factor;
}
}
unit_scale_ = scale; unit_scale_ = scale;
CncSimEvent event = base_event(CNC_SIM_EVENT_SET_UNITS, line); CncSimEvent event = base_event(CNC_SIM_EVENT_SET_UNITS, line);
event.feed = scale; event.feed = scale;
@@ -138,8 +302,13 @@ void CanonEventSink::set_feed_mode(int spindle, int mode, int line) {
} }
void CanonEventSink::set_spindle_speed(double spindle, int line) { void CanonEventSink::set_spindle_speed(double spindle, int line) {
spindle_ = spindle; set_spindle_speed(0, spindle, line);
}
void CanonEventSink::set_spindle_speed(int spindle, double speed, int line) {
spindle_ = speed;
CncSimEvent event = base_event(CNC_SIM_EVENT_SET_SPINDLE, line); CncSimEvent event = base_event(CNC_SIM_EVENT_SET_SPINDLE, line);
event.tool = spindle;
event.spindle = spindle_; event.spindle = spindle_;
event.reserved = spindle_direction_; event.reserved = spindle_direction_;
emit(event); emit(event);
@@ -149,7 +318,7 @@ void CanonEventSink::set_spindle_mode(int spindle, double mode, int line) {
CncSimEvent event = base_event(CNC_SIM_EVENT_COMMENT, line); CncSimEvent event = base_event(CNC_SIM_EVENT_COMMENT, line);
event.tool = spindle; event.tool = spindle;
event.feed = mode; event.feed = mode;
event.reserved = mode == 0.0 ? 970 : 960; event.reserved = mode == 0.0 ? kConstantRpmSpindleModeEvent : kCssSpindleModeEvent;
emit(event); emit(event);
} }
@@ -229,16 +398,26 @@ void CanonEventSink::set_override_control(int control, bool enabled, int spindle
} }
void CanonEventSink::start_spindle(int direction, int line) { void CanonEventSink::start_spindle(int direction, int line) {
start_spindle(0, direction, line);
}
void CanonEventSink::start_spindle(int spindle, int direction, int line) {
spindle_direction_ = direction < 0 ? 2 : 1; spindle_direction_ = direction < 0 ? 2 : 1;
CncSimEvent event = base_event(CNC_SIM_EVENT_SET_SPINDLE, line); CncSimEvent event = base_event(CNC_SIM_EVENT_SET_SPINDLE, line);
event.tool = spindle;
event.spindle = spindle_; event.spindle = spindle_;
event.reserved = spindle_direction_; event.reserved = spindle_direction_;
emit(event); emit(event);
} }
void CanonEventSink::stop_spindle(int line) { void CanonEventSink::stop_spindle(int line) {
stop_spindle(0, line);
}
void CanonEventSink::stop_spindle(int spindle, int line) {
spindle_direction_ = 0; spindle_direction_ = 0;
CncSimEvent event = base_event(CNC_SIM_EVENT_SET_SPINDLE, line); CncSimEvent event = base_event(CNC_SIM_EVENT_SET_SPINDLE, line);
event.tool = spindle;
event.spindle = 0.0; event.spindle = 0.0;
event.reserved = spindle_direction_; event.reserved = spindle_direction_;
emit(event); emit(event);
@@ -256,41 +435,45 @@ void CanonEventSink::change_tool(int line) {
void CanonEventSink::straight_traverse(int line, const CncSimPose &end) { void CanonEventSink::straight_traverse(int line, const CncSimPose &end) {
CncSimEvent event = base_event(CNC_SIM_EVENT_RAPID, line); CncSimEvent event = base_event(CNC_SIM_EVENT_RAPID, line);
event.start = position_; event.start = has_pending_motion_start_ ? pending_motion_start_ : position_;
event.end = end; event.end = end;
emit(event); emit(event);
emit_rtcp_pivot(line, position_, end); emit_rtcp_pivot(line, event.start, end);
has_pending_motion_start_ = false;
position_ = end; position_ = end;
} }
void CanonEventSink::straight_feed(int line, const CncSimPose &end) { void CanonEventSink::straight_feed(int line, const CncSimPose &end) {
CncSimEvent event = base_event(CNC_SIM_EVENT_LINEAR_FEED, line); CncSimEvent event = base_event(CNC_SIM_EVENT_LINEAR_FEED, line);
event.start = position_; event.start = has_pending_motion_start_ ? pending_motion_start_ : position_;
event.end = end; event.end = end;
emit(event); emit(event);
emit_rtcp_pivot(line, position_, end); emit_rtcp_pivot(line, event.start, end);
has_pending_motion_start_ = false;
position_ = end; position_ = end;
} }
void CanonEventSink::arc_feed(int line, const CncSimPose &end, const CncSimPose &center, int turns) { void CanonEventSink::arc_feed(int line, const CncSimPose &end, const CncSimPose &center, int turns) {
CncSimEvent event = base_event(CNC_SIM_EVENT_ARC_FEED, line); CncSimEvent event = base_event(CNC_SIM_EVENT_ARC_FEED, line);
event.start = position_; event.start = has_pending_motion_start_ ? pending_motion_start_ : position_;
event.end = end; event.end = end;
event.center = center; event.center = center;
event.arc_turns = turns; event.arc_turns = turns;
emit(event); emit(event);
emit_rtcp_pivot(line, position_, end); emit_rtcp_pivot(line, event.start, end);
has_pending_motion_start_ = false;
position_ = end; position_ = end;
} }
void CanonEventSink::straight_probe(int line, const CncSimPose &end, int probe_type) { void CanonEventSink::straight_probe(int line, const CncSimPose &end, int probe_type) {
CncSimEvent event = base_event(CNC_SIM_EVENT_PROBE, line); CncSimEvent event = base_event(CNC_SIM_EVENT_PROBE, line);
event.start = position_; event.start = has_pending_motion_start_ ? pending_motion_start_ : position_;
event.end = end; event.end = end;
event.reserved = probe_type; event.reserved = probe_type;
emit(event); emit(event);
probe_position_ = end; probe_position_ = end;
probe_tripped_ = (probe_type & 2) == 0; probe_tripped_ = (probe_type & 2) == 0;
has_pending_motion_start_ = false;
position_ = end; position_ = end;
} }
@@ -397,6 +580,10 @@ bool CanonEventSink::flood_on() const {
return flood_on_; return flood_on_;
} }
bool CanonEventSink::block_delete() const {
return block_delete_;
}
void CanonEventSink::set_mist_on(bool enabled) { void CanonEventSink::set_mist_on(bool enabled) {
mist_on_ = enabled; mist_on_ = enabled;
} }
@@ -412,16 +599,67 @@ void CanonEventSink::emit(CncSimEvent event) {
} }
void CanonEventSink::emit_rtcp_pivot(int line, const CncSimPose &start, const CncSimPose &end) { void CanonEventSink::emit_rtcp_pivot(int line, const CncSimPose &start, const CncSimPose &end) {
if (!rtcp_enabled_ || rtcp_tool_length_ == 0.0 || callback_status_ != 0) { if (!rtcp_enabled_ || callback_status_ != 0) {
return; return;
} }
CncSimEvent event = base_event(CNC_SIM_EVENT_RTCP_PIVOT, line); CncSimEvent event = base_event(CNC_SIM_EVENT_RTCP_PIVOT, line);
event.start = rtcp_pivot_from_tool_tip(start, rtcp_tool_length_); if (fiveaxis_configured_ && kinematics_type_ == fiveaxis_kinematics_type_) {
event.end = rtcp_pivot_from_tool_tip(end, rtcp_tool_length_); const LinuxCncFiveAxisJoints start_joints =
const RtcpVector tool = rtcp_tool_vector_from_pose(end, rtcp_tool_length_); linuxcnc_5axis_inverse(start, fiveaxis_pivot_length_);
event.center.x = tool.x; const LinuxCncFiveAxisJoints end_joints =
event.center.y = tool.y; linuxcnc_5axis_inverse(end, fiveaxis_pivot_length_);
event.center.z = tool.z; event.start.x = start_joints.x;
event.start.y = start_joints.y;
event.start.z = start_joints.z;
event.start.b = start_joints.b;
event.start.c = start_joints.c;
event.start.w = start_joints.w;
event.end.x = end_joints.x;
event.end.y = end_joints.y;
event.end.z = end_joints.z;
event.end.b = end_joints.b;
event.end.c = end_joints.c;
event.end.w = end_joints.w;
event.reserved = kinematics_type_;
event.dwell_seconds = fiveaxis_pivot_length_;
} else if (kinematics_type_ == 1 || kinematics_type_ == 2) {
LinuxCncFiveAxisJoints start_joints{};
LinuxCncFiveAxisJoints end_joints{};
if (kinematics_type_ == 1) {
LinuxCncXyzbcTrtParameters parameters = xyzbc_trt_parameters_configured_
? xyzbc_trt_parameters_
: linuxcnc_xyzbc_trt_default_parameters(rtcp_tool_length_);
parameters.tool_offset = rtcp_tool_length_;
if (use_xyzac_trt_) {
start_joints = linuxcnc_xyzac_trt_inverse(start, parameters);
end_joints = linuxcnc_xyzac_trt_inverse(end, parameters);
} else {
start_joints = linuxcnc_xyzbc_trt_inverse(start, parameters);
end_joints = linuxcnc_xyzbc_trt_inverse(end, parameters);
}
} else {
start_joints = linuxcnc_xyzbc_userk_inverse(start);
end_joints = linuxcnc_xyzbc_userk_inverse(end);
}
event.start.x = start_joints.x;
event.start.y = start_joints.y;
event.start.z = start_joints.z;
event.start.a = start_joints.a;
event.start.b = start_joints.b;
event.start.c = start_joints.c;
event.start.w = start_joints.w;
event.end.x = end_joints.x;
event.end.y = end_joints.y;
event.end.z = end_joints.z;
event.end.a = end_joints.a;
event.end.b = end_joints.b;
event.end.c = end_joints.c;
event.end.w = end_joints.w;
event.reserved = kinematics_type_;
event.dwell_seconds = rtcp_tool_length_;
} else {
return;
}
emit(event); emit(event);
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "cnc_sim_api.h" #include "cnc_sim_api.h"
#include "rtcp_kinematics.h"
#include <unordered_map> #include <unordered_map>
@@ -10,21 +11,31 @@ public:
void set_callback(CncSimEventCallback callback, void *user_data); void set_callback(CncSimEventCallback callback, void *user_data);
void clear_callback_status(); void clear_callback_status();
bool callback_aborted() const; bool callback_aborted() const;
void set_block_delete(bool enabled);
void configure_rtcp(bool enabled, double tool_length); void configure_rtcp(bool enabled, double tool_length);
void configure_xyzbc_trt(const LinuxCncXyzbcTrtParameters &parameters);
void configure_xyzac_trt(const LinuxCncXyzbcTrtParameters &parameters);
void configure_fiveaxis(double pivot_length, int kinematics_type);
void configure_switchkins_remap(int m428_type, int m429_type, int m430_type);
void set_tool_length(int h_code, double tool_length); void set_tool_length(int h_code, double tool_length);
void set_tool_length_offset(const CncSimPose &offset); void set_tool_length_offset(const CncSimPose &offset);
void apply_tool_length_offset(const CncSimPose &offset);
void set_rtcp_state(bool enabled, int h_code, int line); void set_rtcp_state(bool enabled, int h_code, int line);
void switch_kinematics(int kinematics_type, bool rtcp_enabled, int line); void switch_kinematics(int kinematics_type, int line);
bool can_switch_m_code_kinematics(int m_code) const;
bool switch_m_code_kinematics(int m_code, int default_kinematics_type, int line);
void set_g5x_offset(int index, const CncSimPose &offset, int line); void set_g5x_offset(int index, const CncSimPose &offset, int line);
void set_g92_offset(const CncSimPose &offset, int line); void set_g92_offset(const CncSimPose &offset, int line);
void set_xy_rotation(double angle_degrees, int line); void set_xy_rotation(double angle_degrees, int line);
void use_length_units(double scale, int line); void use_length_units(double scale, int line);
void use_length_units(double scale, int line, bool rescale_linear_state);
void select_plane(int plane, int line); void select_plane(int plane, int line);
void set_traverse_rate(double rate); void set_traverse_rate(double rate);
void set_feed_rate(double feed, int line); void set_feed_rate(double feed, int line);
void set_feed_mode(int spindle, int mode, int line); void set_feed_mode(int spindle, int mode, int line);
void set_spindle_speed(double spindle, int line); void set_spindle_speed(double spindle, int line);
void set_spindle_speed(int spindle, double speed, int line);
void set_spindle_mode(int spindle, double mode, int line); void set_spindle_mode(int spindle, double mode, int line);
void set_speed_feed_sync(int spindle, double feed_per_revolution, bool velocity_mode, bool enabled, int line); void set_speed_feed_sync(int spindle, double feed_per_revolution, bool velocity_mode, bool enabled, int line);
void orient_spindle(int spindle, double orientation, int mode, int line); void orient_spindle(int spindle, double orientation, int mode, int line);
@@ -34,7 +45,9 @@ public:
void wait_input(int index, int input_type, int wait_type, double timeout, int line); void wait_input(int index, int input_type, int wait_type, double timeout, int line);
void set_override_control(int control, bool enabled, int spindle, int line); void set_override_control(int control, bool enabled, int spindle, int line);
void start_spindle(int direction, int line); void start_spindle(int direction, int line);
void start_spindle(int spindle, int direction, int line);
void stop_spindle(int line); void stop_spindle(int line);
void stop_spindle(int spindle, int line);
void select_tool(int tool); void select_tool(int tool);
void change_tool(int line); void change_tool(int line);
void straight_traverse(int line, const CncSimPose &end); void straight_traverse(int line, const CncSimPose &end);
@@ -66,6 +79,7 @@ public:
bool feed_hold() const; bool feed_hold() const;
bool mist_on() const; bool mist_on() const;
bool flood_on() const; bool flood_on() const;
bool block_delete() const;
void set_mist_on(bool enabled); void set_mist_on(bool enabled);
void set_flood_on(bool enabled); void set_flood_on(bool enabled);
@@ -91,13 +105,27 @@ private:
double default_rtcp_tool_length_ = 0.0; double default_rtcp_tool_length_ = 0.0;
double rtcp_tool_length_ = 0.0; double rtcp_tool_length_ = 0.0;
int rtcp_h_code_ = 0; int rtcp_h_code_ = 0;
LinuxCncXyzbcTrtParameters xyzbc_trt_parameters_{};
bool xyzbc_trt_parameters_configured_ = false;
bool use_xyzac_trt_ = false;
bool fiveaxis_configured_ = false;
double fiveaxis_pivot_length_ = 250.0;
int fiveaxis_kinematics_type_ = 0;
bool switchkins_remap_configured_ = false;
int switchkins_m428_type_ = 1;
int switchkins_m429_type_ = 0;
int switchkins_m430_type_ = 2;
CncSimPose tool_length_offset_{}; CncSimPose tool_length_offset_{};
int kinematics_type_ = 1; double xy_rotation_degrees_ = 0.0;
int kinematics_type_ = 0;
bool feed_override_ = false; bool feed_override_ = false;
bool speed_override_ = false; bool speed_override_ = false;
bool adaptive_feed_ = false; bool adaptive_feed_ = false;
bool feed_hold_ = false; bool feed_hold_ = false;
bool mist_on_ = false; bool mist_on_ = false;
bool flood_on_ = false; bool flood_on_ = false;
bool block_delete_ = false;
std::unordered_map<int, double> tool_lengths_; std::unordered_map<int, double> tool_lengths_;
bool has_pending_motion_start_ = false;
CncSimPose pending_motion_start_{};
}; };

View File

@@ -4,12 +4,14 @@
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <cstring>
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
struct CncSimHandle { struct CncSimHandle {
CncSimDialect dialect = CNC_SIM_DIALECT_LINUXCNC; CncSimDialect dialect = CNC_SIM_DIALECT_LINUXCNC;
GcodeBackendKind backend = GcodeBackendKind::Smoke; GcodeBackendKind backend = GcodeBackendKind::LinuxCncRs274;
GcodeBackendOptions backend_options;
std::string last_error; std::string last_error;
CanonEventSink sink; CanonEventSink sink;
}; };
@@ -73,6 +75,116 @@ bool contains_bool_value(const std::string &compact, const char *key, bool *valu
return false; return false;
} }
bool contains_string_value(const std::string &compact, const char *key, const char *value) {
const std::string quoted_needle = std::string("\"") + key + "\":\"" + value + "\"";
if (compact.find(quoted_needle) != std::string::npos) {
return true;
}
const std::string prefix_needle = std::string("\"") + key + "\":\"";
size_t pos = 0;
while ((pos = compact.find(prefix_needle, pos)) != std::string::npos) {
pos += prefix_needle.size();
const size_t end = compact.find('"', pos);
if (end == std::string::npos) {
return false;
}
const std::string field_value = compact.substr(pos, end - pos);
if (field_value.find(value) != std::string::npos) {
return true;
}
pos = end + 1;
}
return false;
}
bool find_json_string_value(const std::string &json, const char *key, std::string *value) {
const std::string needle = std::string("\"") + key + "\"";
size_t pos = 0;
while ((pos = json.find(needle, pos)) != std::string::npos) {
pos += needle.size();
while (pos < json.size() && std::isspace(static_cast<unsigned char>(json[pos]))) {
++pos;
}
if (pos >= json.size() || json[pos] != ':') {
continue;
}
++pos;
while (pos < json.size() && std::isspace(static_cast<unsigned char>(json[pos]))) {
++pos;
}
if (pos >= json.size() || json[pos] != '"') {
continue;
}
++pos;
std::string parsed;
bool escaped = false;
for (; pos < json.size(); ++pos) {
const char ch = json[pos];
if (escaped) {
switch (ch) {
case '"':
case '\\':
case '/':
parsed.push_back(ch);
break;
case 'n':
parsed.push_back('\n');
break;
case 'r':
parsed.push_back('\r');
break;
case 't':
parsed.push_back('\t');
break;
default:
parsed.push_back(ch);
break;
}
escaped = false;
} else if (ch == '\\') {
escaped = true;
} else if (ch == '"') {
*value = parsed;
return true;
} else {
parsed.push_back(ch);
}
}
}
return false;
}
bool find_json_string_any(const std::string &json, std::string *value, const char *first) {
return find_json_string_value(json, first, value);
}
template <typename... Keys>
bool find_json_string_any(const std::string &json, std::string *value, const char *first, Keys... rest) {
return find_json_string_value(json, first, value) || find_json_string_any(json, value, rest...);
}
bool contains_switchkins_config_alias(const std::string &compact, const char *alias) {
return contains_string_value(compact, "switchkins", alias) ||
contains_string_value(compact, "remap", alias) ||
contains_string_value(compact, "machine", alias) ||
contains_string_value(compact, "kinematics", alias) ||
contains_string_value(compact, "halfile", alias) ||
contains_string_value(compact, "hal_file", alias) ||
contains_string_value(compact, "postguihalfile", alias) ||
contains_string_value(compact, "postgui_halfile", alias) ||
contains_string_value(compact, "postgui_hal_file", alias) ||
contains_string_value(compact, "subroutinepath", alias) ||
contains_string_value(compact, "subroutine_path", alias) ||
contains_string_value(compact, "config", alias) ||
contains_string_value(compact, "configpath", alias) ||
contains_string_value(compact, "config_path", alias) ||
contains_string_value(compact, "ini", alias) ||
contains_string_value(compact, "inifile", alias) ||
contains_string_value(compact, "inifilename", alias) ||
contains_string_value(compact, "ini_file", alias) ||
contains_string_value(compact, "ini_file_name", alias);
}
bool find_number_value(const std::string &compact, const char *key, double *value) { bool find_number_value(const std::string &compact, const char *key, double *value) {
const std::string needle = std::string("\"") + key + "\":"; const std::string needle = std::string("\"") + key + "\":";
const size_t pos = compact.find(needle); const size_t pos = compact.find(needle);
@@ -128,6 +240,118 @@ void apply_tool_length_table(CanonEventSink &sink, const std::string &compact) {
} }
} }
bool find_number_any(const std::string &compact, double *value, const char *first) {
return find_number_value(compact, first, value);
}
template <typename... Keys>
bool find_number_any(const std::string &compact, double *value, const char *first, Keys... rest) {
return find_number_value(compact, first, value) || find_number_any(compact, value, rest...);
}
bool contains_bool_any(const std::string &compact, bool *value, const char *first) {
return contains_bool_value(compact, first, value);
}
template <typename... Keys>
bool contains_bool_any(const std::string &compact, bool *value, const char *first, Keys... rest) {
return contains_bool_value(compact, first, value) || contains_bool_any(compact, value, rest...);
}
void apply_xyzbc_trt_config(CanonEventSink &sink, const std::string &compact) {
LinuxCncXyzbcTrtParameters parameters = linuxcnc_xyzbc_trt_default_parameters(0.0);
bool configured = false;
const bool xyzac = compact.find("\"trt\":\"xyzac\"") != std::string::npos ||
compact.find("\"kinematics\":\"xyzac-trt\"") != std::string::npos ||
compact.find("\"switchkins\":\"xyzac-trt\"") != std::string::npos;
const bool xyzbc = compact.find("\"trt\":\"xyzbc\"") != std::string::npos ||
compact.find("\"kinematics\":\"xyzbc-trt\"") != std::string::npos ||
compact.find("\"switchkins\":\"xyzbc-trt\"") != std::string::npos;
configured = find_number_any(compact, &parameters.x_rot_point, "xrotpoint", "x_rot_point", "x-rot-point") ||
configured;
configured = find_number_any(compact, &parameters.y_rot_point, "yrotpoint", "y_rot_point", "y-rot-point") ||
configured;
configured = find_number_any(compact, &parameters.z_rot_point, "zrotpoint", "z_rot_point", "z-rot-point") ||
configured;
configured = find_number_any(compact, &parameters.x_offset, "xoffset", "x_offset", "x-offset") ||
configured;
configured = find_number_any(compact, &parameters.y_offset, "yoffset", "y_offset", "y-offset") ||
configured;
configured = find_number_any(compact, &parameters.z_offset, "zoffset", "z_offset", "z-offset") ||
configured;
configured = contains_bool_any(compact,
&parameters.conventional_directions,
"conventionaldirections",
"conventional_directions",
"conventional-directions") ||
configured;
if (configured || xyzac || xyzbc) {
configured = true;
}
if (configured && xyzac) {
sink.configure_xyzac_trt(parameters);
} else if (configured) {
sink.configure_xyzbc_trt(parameters);
}
}
void apply_fiveaxis_config(CanonEventSink &sink, const std::string &compact) {
const bool fiveaxis = compact.find("\"kinematics\":\"5axiskins\"") != std::string::npos ||
compact.find("\"switchkins\":\"5axiskins\"") != std::string::npos ||
compact.find("\"kinematics\":\"5axis\"") != std::string::npos;
double pivot_length = 250.0;
const bool has_pivot_length = find_number_any(compact,
&pivot_length,
"pivotlength",
"pivot_length",
"pivot-length");
double kinematics_type = 0.0;
const bool has_kinematics_type = find_number_any(compact,
&kinematics_type,
"fiveaxiskinematicstype",
"fiveaxis_kinematics_type",
"fiveaxis-kinematics-type",
"kinematicstype",
"kinematics_type");
if (fiveaxis || has_pivot_length || has_kinematics_type) {
sink.configure_fiveaxis(pivot_length, static_cast<int>(kinematics_type));
}
}
void apply_switchkins_remap_config(CanonEventSink &sink, const std::string &compact) {
struct RemapSource {
const char *alias;
int m428_type;
int m429_type;
int m430_type;
};
// Generated from LinuxCNC INI REMAP/SUBROUTINE_PATH entries and the
// adjacent remap_subs #<kinstype> assignments.
static constexpr RemapSource kSources[] = {
#include "linuxcnc_switchkins_remap_table.inc"
};
const RemapSource *matched_source = nullptr;
size_t matched_alias_length = 0;
for (const RemapSource &source : kSources) {
if (contains_switchkins_config_alias(compact, source.alias)) {
const size_t alias_length = std::strlen(source.alias);
if (alias_length > matched_alias_length) {
matched_source = &source;
matched_alias_length = alias_length;
}
}
}
if (matched_source) {
sink.configure_switchkins_remap(matched_source->m428_type,
matched_source->m429_type,
matched_source->m430_type);
}
}
int apply_config(CncSimHandle *handle, const std::string &json) { int apply_config(CncSimHandle *handle, const std::string &json) {
if (json.empty()) { if (json.empty()) {
return 0; return 0;
@@ -143,6 +367,20 @@ int apply_config(CncSimHandle *handle, const std::string &json) {
return -1; return -1;
} }
std::string ini_file_name;
if (find_json_string_any(json,
&ini_file_name,
"ini",
"iniFile",
"inifile",
"iniFileName",
"inifilename",
"ini_file",
"ini_file_name",
"INI_FILE_NAME")) {
handle->backend_options.ini_file_name = ini_file_name;
}
bool rtcp_enabled = false; bool rtcp_enabled = false;
double tool_length = 0.0; double tool_length = 0.0;
const bool has_rtcp_enabled = contains_bool_value(compact, "rtcp", &rtcp_enabled) || const bool has_rtcp_enabled = contains_bool_value(compact, "rtcp", &rtcp_enabled) ||
@@ -152,7 +390,15 @@ int apply_config(CncSimHandle *handle, const std::string &json) {
if (has_rtcp_enabled || has_tool_length) { if (has_rtcp_enabled || has_tool_length) {
handle->sink.configure_rtcp(rtcp_enabled, tool_length); handle->sink.configure_rtcp(rtcp_enabled, tool_length);
} }
bool block_delete = false;
if (contains_bool_value(compact, "blockdelete", &block_delete) ||
contains_bool_value(compact, "block_delete", &block_delete)) {
handle->sink.set_block_delete(block_delete);
}
apply_tool_length_table(handle->sink, compact); apply_tool_length_table(handle->sink, compact);
apply_switchkins_remap_config(handle->sink, compact);
apply_xyzbc_trt_config(handle->sink, compact);
apply_fiveaxis_config(handle->sink, compact);
return 0; return 0;
} }
@@ -218,7 +464,12 @@ int cnc_sim_parse_program(CncSimHandle *handle, const char *program, size_t prog
} }
handle->last_error.clear(); handle->last_error.clear();
const int rc = parse_gcode_with_backend(handle->backend, handle->sink, program, program_len, &handle->last_error); const int rc = parse_gcode_with_backend(handle->backend,
handle->sink,
program,
program_len,
handle->backend_options,
&handle->last_error);
if (rc != 0 && handle->last_error.empty()) { if (rc != 0 && handle->last_error.empty()) {
handle->last_error = "program parse failed"; handle->last_error = "program parse failed";
} }

View File

@@ -3,7 +3,9 @@
#ifdef CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND #ifdef CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND
#include "linuxcnc_rs274_backend.h" #include "linuxcnc_rs274_backend.h"
#endif #endif
#ifdef CNC_SIM_ENABLE_SMOKE_BACKEND
#include "smoke_gcode_parser.h" #include "smoke_gcode_parser.h"
#endif
const char *gcode_backend_name(GcodeBackendKind backend) { const char *gcode_backend_name(GcodeBackendKind backend) {
switch (backend) { switch (backend) {
@@ -20,15 +22,23 @@ int parse_gcode_with_backend(GcodeBackendKind backend,
CanonEventSink &sink, CanonEventSink &sink,
const char *program, const char *program,
size_t program_len, size_t program_len,
const GcodeBackendOptions &options,
std::string *error) { std::string *error) {
switch (backend) { switch (backend) {
case GcodeBackendKind::Smoke: { case GcodeBackendKind::Smoke: {
#ifdef CNC_SIM_ENABLE_SMOKE_BACKEND
SmokeGcodeParser parser(sink); SmokeGcodeParser parser(sink);
return parser.parse(program, program_len, error); return parser.parse(program, program_len, error);
#else
if (error) {
*error = "smoke backend is not compiled into this build";
}
return -1;
#endif
} }
case GcodeBackendKind::LinuxCncRs274: case GcodeBackendKind::LinuxCncRs274:
#ifdef CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND #ifdef CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND
return parse_linuxcnc_rs274_backend(sink, program, program_len, error); return parse_linuxcnc_rs274_backend(sink, program, program_len, options, error);
#else #else
if (error) { if (error) {
*error = "linuxcnc-rs274 backend is not compiled into this build"; *error = "linuxcnc-rs274 backend is not compiled into this build";

View File

@@ -10,11 +10,15 @@ enum class GcodeBackendKind {
LinuxCncRs274, LinuxCncRs274,
}; };
struct GcodeBackendOptions {
std::string ini_file_name;
};
const char *gcode_backend_name(GcodeBackendKind backend); const char *gcode_backend_name(GcodeBackendKind backend);
int parse_gcode_with_backend(GcodeBackendKind backend, int parse_gcode_with_backend(GcodeBackendKind backend,
CanonEventSink &sink, CanonEventSink &sink,
const char *program, const char *program,
size_t program_len, size_t program_len,
const GcodeBackendOptions &options,
std::string *error); std::string *error);

View File

@@ -0,0 +1,266 @@
#include <ctype.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_STRING_H
#define __LINUXCNC_RTAPI_CTYPE_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_EMCMOTCFG_H
#define __LINUXCNC_KINEMATICS_H
#define SWITCHKINS_H
#define RTAPI_MSG_ERR 1
#define PM_PI 3.1415926535897932384626433832795029
#define TO_RAD (PM_PI / 180.0)
#define EMCMOT_MAX_JOINTS 16
#define EMCMOT_MAX_AXIS 9
typedef double real_t;
typedef double hal_float_t;
typedef int hal_s32_t;
typedef bool hal_bit_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
typedef int (*KF)(const double *joint,
EmcPose *pos,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags);
typedef int (*KI)(const struct EmcPose *world,
double *joint,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags);
typedef int (*KS)(const int comp_id, const char *coordinates, kparms *ksetup_parms);
#ifdef __cplusplus
extern "C" {
#endif
static int cnc_sim_axis_idx_for_jno[EMCMOT_MAX_JOINTS];
static int cnc_sim_max_joints;
void cnc_sim_5axis_rtapi_print(const char *fmt, ...) {
(void)fmt;
}
void cnc_sim_5axis_rtapi_print_msg(int level, const char *fmt, ...) {
(void)level;
(void)fmt;
}
void *cnc_sim_5axis_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_5axis_hal_pin_float_newf(hal_pin_dir_t dir,
hal_float_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_float_t *)calloc(1, sizeof(hal_float_t));
return *data_ptr_addr ? 0 : -1;
}
int cnc_sim_5axis_map_coordinates_to_jnumbers(const char *coordinates,
const int max_joints,
const int allow_duplicates,
int axis_idx_for_jno[]) {
(void)allow_duplicates;
const char *letters = "XYZABCUVW";
int jno = 0;
for (int i = 0; i < EMCMOT_MAX_JOINTS; ++i) {
axis_idx_for_jno[i] = -1;
cnc_sim_axis_idx_for_jno[i] = -1;
}
while (*coordinates) {
const char *found = strchr(letters, toupper((unsigned char)*coordinates));
if (!found || jno >= max_joints) {
return -1;
}
axis_idx_for_jno[jno++] = (int)(found - letters);
++coordinates;
}
cnc_sim_max_joints = jno;
for (int i = 0; i < EMCMOT_MAX_JOINTS; ++i) {
cnc_sim_axis_idx_for_jno[i] = axis_idx_for_jno[i];
}
return 0;
}
int cnc_sim_5axis_position_to_mapped_joints(const int max_joints, const EmcPose *pos, double *joints) {
for (int jno = 0; jno < max_joints; ++jno) {
switch (cnc_sim_axis_idx_for_jno[jno]) {
case 0: joints[jno] = pos->tran.x; break;
case 1: joints[jno] = pos->tran.y; break;
case 2: joints[jno] = pos->tran.z; break;
case 3: joints[jno] = pos->a; break;
case 4: joints[jno] = pos->b; break;
case 5: joints[jno] = pos->c; break;
case 6: joints[jno] = pos->u; break;
case 7: joints[jno] = pos->v; break;
case 8: joints[jno] = pos->w; break;
default: joints[jno] = 0.0; break;
}
}
return 0;
}
int cnc_sim_5axis_identityKinematicsSetup(const int comp_id, const char *coordinates, kparms *ksetup_parms) {
(void)comp_id;
(void)coordinates;
(void)ksetup_parms;
return 0;
}
int cnc_sim_5axis_identityKinematicsForward(const double *joint,
EmcPose *world,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags) {
(void)joint;
(void)world;
(void)fflags;
(void)iflags;
return -1;
}
int cnc_sim_5axis_identityKinematicsInverse(const EmcPose *world,
double *joint,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags) {
(void)world;
(void)joint;
(void)iflags;
(void)fflags;
return -1;
}
int cnc_sim_5axis_userkKinematicsSetup(const int comp_id, const char *coordinates, kparms *ksetup_parms) {
(void)comp_id;
(void)coordinates;
(void)ksetup_parms;
return -1;
}
int cnc_sim_5axis_userkKinematicsForward(const double *joint,
EmcPose *world,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags) {
(void)joint;
(void)world;
(void)fflags;
(void)iflags;
return -1;
}
int cnc_sim_5axis_userkKinematicsInverse(const EmcPose *pos,
double *joint,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags) {
(void)pos;
(void)joint;
(void)iflags;
(void)fflags;
return -1;
}
#define rtapi_print cnc_sim_5axis_rtapi_print
#define rtapi_print_msg cnc_sim_5axis_rtapi_print_msg
#define hal_pin_float_newf cnc_sim_5axis_hal_pin_float_newf
#define map_coordinates_to_jnumbers cnc_sim_5axis_map_coordinates_to_jnumbers
#define position_to_mapped_joints cnc_sim_5axis_position_to_mapped_joints
#define identityKinematicsSetup cnc_sim_5axis_identityKinematicsSetup
#define identityKinematicsForward cnc_sim_5axis_identityKinematicsForward
#define identityKinematicsInverse cnc_sim_5axis_identityKinematicsInverse
#define userkKinematicsSetup cnc_sim_5axis_userkKinematicsSetup
#define userkKinematicsForward cnc_sim_5axis_userkKinematicsForward
#define userkKinematicsInverse cnc_sim_5axis_userkKinematicsInverse
#define switchkinsSetup cnc_sim_5axis_switchkinsSetup
#define haldata cnc_sim_5axis_haldata
#define hal_malloc(size) ((struct haldata *)cnc_sim_5axis_hal_malloc(size))
#include "../../../linuxcnc/src/emc/kinematics/5axiskins.c"
#undef hal_malloc
#undef haldata
#undef switchkinsSetup
#undef userkKinematicsInverse
#undef userkKinematicsForward
#undef userkKinematicsSetup
#undef identityKinematicsInverse
#undef identityKinematicsForward
#undef identityKinematicsSetup
#undef position_to_mapped_joints
#undef map_coordinates_to_jnumbers
#undef hal_pin_float_newf
#undef rtapi_print_msg
#undef rtapi_print
int cnc_sim_linuxcnc_5axiskins_setup(void) {
kparms kp = {0};
static char kinsname[] = "5axiskins";
static char halprefix[] = "5axiskins";
static char required_coordinates[] = "XYZBCW";
kp.kinsname = kinsname;
kp.halprefix = halprefix;
kp.required_coordinates = required_coordinates;
kp.max_joints = EMCMOT_MAX_JOINTS;
kp.allow_duplicates = 1;
return fiveaxis_KinematicsSetup(0, "XYZBCW", &kp);
}
void cnc_sim_linuxcnc_5axiskins_set_pivot_length(double pivot_length) {
*cnc_sim_5axis_haldata->pivot_length = pivot_length;
}
int cnc_sim_linuxcnc_5axiskins_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return fiveaxis_KinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_5axiskins_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return fiveaxis_KinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,122 @@
#include "linuxcnc_5axiskins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_5axiskins_setup(void);
void cnc_sim_linuxcnc_5axiskins_set_pivot_length(double pivot_length);
int cnc_sim_linuxcnc_5axiskins_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_5axiskins_inverse(const EmcPose *pos, double *joints);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_5axiskins_setup() == 0;
return initialized;
}
void to_joints(const LinuxCncFiveAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.b;
out[4] = joints.c;
out[5] = joints.w;
}
LinuxCncFiveAxisJoints from_joints(const double joints[16]) {
LinuxCncFiveAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.b = joints[3];
out.c = joints[4];
out.w = joints[5];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_5axiskins_forward(const LinuxCncFiveAxisJoints &joints,
double pivot_length,
CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
cnc_sim_linuxcnc_5axiskins_set_pivot_length(pivot_length);
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
if (cnc_sim_linuxcnc_5axiskins_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_5axiskins_inverse(const CncSimPose &pose,
double pivot_length,
LinuxCncFiveAxisJoints *joints) {
if (!ensure_initialized()) {
return false;
}
cnc_sim_linuxcnc_5axiskins_set_pivot_length(pivot_length);
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
if (cnc_sim_linuxcnc_5axiskins_inverse(&in, raw_joints) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_5axiskins_forward(const LinuxCncFiveAxisJoints &joints,
double pivot_length,
CncSimPose *pose);
bool linuxcnc_5axiskins_inverse(const CncSimPose &pose,
double pivot_length,
LinuxCncFiveAxisJoints *joints);

View File

@@ -15,6 +15,8 @@
namespace { namespace {
constexpr int kToolLengthOffsetEvent = 430;
CanonEventSink *active_sink = nullptr; CanonEventSink *active_sink = nullptr;
int active_line = 0; int active_line = 0;
bool flood_on = false; bool flood_on = false;
@@ -26,6 +28,19 @@ double motion_control_tolerance = 0.0;
double naivecam_tolerance = 0.0; double naivecam_tolerance = 0.0;
std::string parameter_file_name = "rs274ngc.var"; std::string parameter_file_name = "rs274ngc.var";
void load_nonrandom_tool_to_spindle(int pocket) {
// LinuxCNC Task::load_tool() uses pocket 0 as the non-random
// toolchanger unload handshake. Do not eagerly mirror nonzero
// loads here; the source-linked interpreter observes its own
// post-canonical state before external task-side load completion.
if (pocket == 0) {
CANON_TOOL_TABLE unloaded = tooldata_entry_init();
unloaded.toolno = 0;
unloaded.pocketno = 0;
tooldata_put(unloaded, 0);
}
}
CncSimPose make_pose(double x, double y, double z, CncSimPose make_pose(double x, double y, double z,
double a, double b, double c, double a, double b, double c,
double u, double v, double w) { double u, double v, double w) {
@@ -50,6 +65,12 @@ int plane_to_g_code(CANON_PLANE plane) {
return 18; return 18;
case CANON_PLANE::YZ: case CANON_PLANE::YZ:
return 19; return 19;
case CANON_PLANE::UV:
return 171;
case CANON_PLANE::UW:
return 181;
case CANON_PLANE::VW:
return 191;
default: default:
return 17; return 17;
} }
@@ -73,6 +94,12 @@ CANON_PLANE g_code_to_plane(int plane) {
return CANON_PLANE::XZ; return CANON_PLANE::XZ;
case 19: case 19:
return CANON_PLANE::YZ; return CANON_PLANE::YZ;
case 171:
return CANON_PLANE::UV;
case 181:
return CANON_PLANE::UW;
case 191:
return CANON_PLANE::VW;
case 17: case 17:
default: default:
return CANON_PLANE::XY; return CANON_PLANE::XY;
@@ -84,6 +111,27 @@ const CncSimPose &current_position() {
return active_sink ? active_sink->position() : zero; return active_sink ? active_sink->position() : zero;
} }
template <typename NurbsPoint>
CncSimPose nurbs_plane_point_to_pose(const NurbsPoint &point, CANON_PLANE plane) {
CncSimPose end = current_position();
switch (plane) {
case CANON_PLANE::YZ:
end.y = point.NURBS_X;
end.z = point.NURBS_Y;
break;
case CANON_PLANE::XZ:
end.x = point.NURBS_Y;
end.z = point.NURBS_X;
break;
case CANON_PLANE::XY:
default:
end.x = point.NURBS_X;
end.y = point.NURBS_Y;
break;
}
return end;
}
void trace_call(const char *name) { void trace_call(const char *name) {
if (std::getenv("CNC_SIM_TRACE_CANON")) { if (std::getenv("CNC_SIM_TRACE_CANON")) {
std::fprintf(stderr, "canon:%s\n", name); std::fprintf(stderr, "canon:%s\n", name);
@@ -145,7 +193,7 @@ void INIT_CANON() {
void USE_LENGTH_UNITS(CANON_UNITS units) { void USE_LENGTH_UNITS(CANON_UNITS units) {
trace_call("USE_LENGTH_UNITS"); trace_call("USE_LENGTH_UNITS");
if (active_sink) { if (active_sink) {
active_sink->use_length_units(units_to_scale(units), event_line()); active_sink->use_length_units(units_to_scale(units), event_line(), true);
} }
} }
@@ -163,10 +211,10 @@ void SET_FEED_RATE(double rate) {
} }
} }
void SET_SPINDLE_SPEED(int, double speed) { void SET_SPINDLE_SPEED(int spindle, double speed) {
trace_call("SET_SPINDLE_SPEED"); trace_call("SET_SPINDLE_SPEED");
if (active_sink) { if (active_sink) {
active_sink->set_spindle_speed(speed, event_line()); active_sink->set_spindle_speed(spindle, speed, event_line());
} }
} }
@@ -180,6 +228,10 @@ void SELECT_TOOL(int tool) {
void CHANGE_TOOL() { void CHANGE_TOOL() {
trace_call("CHANGE_TOOL"); trace_call("CHANGE_TOOL");
if (active_sink) { if (active_sink) {
const int selected_tool = active_sink->selected_tool();
if (selected_tool == 0) {
load_nonrandom_tool_to_spindle(0);
}
active_sink->change_tool(event_line()); active_sink->change_tool(event_line());
} }
} }
@@ -350,6 +402,15 @@ void SET_MOTION_CONTROL_MODE(CANON_MOTION_MODE mode, double tolerance) {
void SET_NAIVECAM_TOLERANCE(double tolerance) { void SET_NAIVECAM_TOLERANCE(double tolerance) {
naivecam_tolerance = tolerance; naivecam_tolerance = tolerance;
if (active_sink) {
CncSimEvent event{};
event.version = 1;
event.type = CNC_SIM_EVENT_COMMENT;
event.line = event_line();
event.feed = tolerance;
event.reserved = 641;
active_sink->emit_raw(event);
}
} }
void SET_CUTTER_RADIUS_COMPENSATION(double) { void SET_CUTTER_RADIUS_COMPENSATION(double) {
@@ -373,27 +434,21 @@ void STOP_SPEED_FEED_SYNCH() {
} }
} }
void NURBS_G5_FEED(int lineno, const std::vector<NURBS_CONTROL_POINT> &points, unsigned int, CANON_PLANE) { void NURBS_G5_FEED(int lineno, const std::vector<NURBS_CONTROL_POINT> &points, unsigned int, CANON_PLANE plane) {
if (!active_sink) { if (!active_sink) {
return; return;
} }
for (const auto &point : points) { for (const auto &point : points) {
CncSimPose end = active_sink->position(); active_sink->straight_feed(event_line(lineno), nurbs_plane_point_to_pose(point, plane));
end.x = point.NURBS_X;
end.y = point.NURBS_Y;
active_sink->straight_feed(lineno, end);
} }
} }
void NURBS_G6_FEED(int lineno, const std::vector<NURBS_G6_CONTROL_POINT> &points, unsigned int, double, int, CANON_PLANE) { void NURBS_G6_FEED(int lineno, const std::vector<NURBS_G6_CONTROL_POINT> &points, unsigned int, double, int, CANON_PLANE plane) {
if (!active_sink) { if (!active_sink) {
return; return;
} }
for (const auto &point : points) { for (const auto &point : points) {
CncSimPose end = active_sink->position(); active_sink->straight_feed(event_line(lineno), nurbs_plane_point_to_pose(point, plane));
end.x = point.NURBS_X;
end.y = point.NURBS_Y;
active_sink->straight_feed(lineno, end);
} }
} }
@@ -431,21 +486,21 @@ void SET_SPINDLE_MODE(int spindle, double mode) {
void SPINDLE_RETRACT_TRAVERSE() { void SPINDLE_RETRACT_TRAVERSE() {
} }
void START_SPINDLE_CLOCKWISE(int, int) { void START_SPINDLE_CLOCKWISE(int spindle, int) {
if (active_sink) { if (active_sink) {
active_sink->start_spindle(1, event_line()); active_sink->start_spindle(spindle, 1, event_line());
} }
} }
void START_SPINDLE_COUNTERCLOCKWISE(int, int) { void START_SPINDLE_COUNTERCLOCKWISE(int spindle, int) {
if (active_sink) { if (active_sink) {
active_sink->start_spindle(-1, event_line()); active_sink->start_spindle(spindle, -1, event_line());
} }
} }
void STOP_SPINDLE_TURNING(int) { void STOP_SPINDLE_TURNING(int spindle) {
if (active_sink) { if (active_sink) {
active_sink->stop_spindle(event_line()); active_sink->stop_spindle(spindle, event_line());
} }
} }
@@ -488,13 +543,14 @@ void USE_TOOL_LENGTH_OFFSET(const EmcPose &offset) {
event.version = 1; event.version = 1;
event.type = CNC_SIM_EVENT_COMMENT; event.type = CNC_SIM_EVENT_COMMENT;
event.line = event_line(); event.line = event_line();
event.reserved = 430; event.reserved = kToolLengthOffsetEvent;
event.start = tool_offset; event.start = tool_offset;
active_sink->emit_raw(event); active_sink->emit_raw(event);
} }
void CHANGE_TOOL_NUMBER(int number) { void CHANGE_TOOL_NUMBER(int number) {
if (active_sink) { if (active_sink) {
load_nonrandom_tool_to_spindle(number);
active_sink->select_tool(number); active_sink->select_tool(number);
active_sink->change_tool(event_line()); active_sink->change_tool(event_line());
} }
@@ -726,11 +782,16 @@ int GET_EXTERNAL_FLOOD() {
} }
CANON_UNITS GET_EXTERNAL_LENGTH_UNIT_TYPE() { CANON_UNITS GET_EXTERNAL_LENGTH_UNIT_TYPE() {
return active_sink && active_sink->unit_scale() == 25.4 ? CANON_UNITS_INCHES : CANON_UNITS_MM; // LinuxCNC src/emc/task/emccanon.cc derives this from INI machine/user
// units, not from the active G20/G21 program units.
return CANON_UNITS_MM;
} }
double GET_EXTERNAL_LENGTH_UNITS() { double GET_EXTERNAL_LENGTH_UNITS() {
return active_sink ? active_sink->unit_scale() : 1.0; // LinuxCNC src/emc/task/emccanon.cc returns traj.linearUnits here:
// external/user units per internal millimeter. The simulator has no INI
// machine-units bridge yet, so the source-linked default is metric.
return 1.0;
} }
double GET_EXTERNAL_ANGLE_UNITS() { double GET_EXTERNAL_ANGLE_UNITS() {

View File

@@ -0,0 +1,127 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_APP_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_STRING_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_EMCMOTCFG_H
#define __LINUXCNC_KINEMATICS_H
#define MODULE_LICENSE(...)
#define EXPORT_SYMBOL(symbol)
#define RTAPI_MSG_ERR 1
#define EMCMOT_MAX_JOINTS 16
typedef int hal_s32_t;
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef enum {
KINEMATICS_IDENTITY = 1,
KINEMATICS_FORWARD_ONLY,
KINEMATICS_INVERSE_ONLY,
KINEMATICS_BOTH
} KINEMATICS_TYPE;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#define KINS_NOT_SWITCHABLE \
extern int kinematicsSwitchable() { return 0; } \
extern int kinematicsSwitch(int switchkins_type) { (void)switchkins_type; return 0; }
#ifdef __cplusplus
extern "C" {
#endif
int cnc_sim_corexykins_hal_init(const char *name) {
(void)name;
return 1;
}
void *cnc_sim_corexykins_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_corexykins_hal_ready(int comp_id) {
(void)comp_id;
return 0;
}
void cnc_sim_corexykins_hal_exit(int comp_id) {
(void)comp_id;
}
#define hal_init cnc_sim_corexykins_hal_init
#define hal_malloc(size) ((struct data *)cnc_sim_corexykins_hal_malloc(size))
#define hal_ready cnc_sim_corexykins_hal_ready
#define hal_exit cnc_sim_corexykins_hal_exit
#define kinematicsType cnc_sim_corexykins_kinematicsType
#define kinematicsForward cnc_sim_corexykins_kinematicsForward
#define kinematicsInverse cnc_sim_corexykins_kinematicsInverse
#define kinematicsHome cnc_sim_corexykins_kinematicsHome
#define kinematicsSwitchable cnc_sim_corexykins_kinematicsSwitchable
#define kinematicsSwitch cnc_sim_corexykins_kinematicsSwitch
#define rtapi_app_main cnc_sim_corexykins_rtapi_app_main
#define rtapi_app_exit cnc_sim_corexykins_rtapi_app_exit
#define comp_id cnc_sim_corexykins_comp_id
#define data cnc_sim_corexykins_data
#include "../../../linuxcnc/src/emc/kinematics/corexykins.c"
#undef data
#undef comp_id
#undef rtapi_app_exit
#undef rtapi_app_main
#undef kinematicsSwitch
#undef kinematicsSwitchable
#undef kinematicsHome
#undef kinematicsInverse
#undef kinematicsForward
#undef kinematicsType
#undef hal_exit
#undef hal_ready
#undef hal_malloc
#undef hal_init
int cnc_sim_linuxcnc_corexykins_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return cnc_sim_corexykins_kinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_corexykins_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return cnc_sim_corexykins_kinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,104 @@
#include "linuxcnc_corexykins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_corexykins_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_corexykins_inverse(const EmcPose *pos, double *joints);
}
namespace {
void to_joints(const LinuxCncAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.a;
out[4] = joints.b;
out[5] = joints.c;
out[6] = joints.u;
out[7] = joints.v;
out[8] = joints.w;
}
LinuxCncAxisJoints from_joints(const double joints[16]) {
LinuxCncAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.a = joints[3];
out.b = joints[4];
out.c = joints[5];
out.u = joints[6];
out.v = joints[7];
out.w = joints[8];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_corexykins_source_forward(const LinuxCncAxisJoints &joints, CncSimPose *pose) {
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
if (cnc_sim_linuxcnc_corexykins_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_corexykins_source_inverse(const CncSimPose &pose, LinuxCncAxisJoints *joints) {
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
if (cnc_sim_linuxcnc_corexykins_inverse(&in, raw_joints) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_corexykins_source_forward(const LinuxCncAxisJoints &joints, CncSimPose *pose);
bool linuxcnc_corexykins_source_inverse(const CncSimPose &pose, LinuxCncAxisJoints *joints);

View File

@@ -0,0 +1,117 @@
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#define __LINUXCNC_RTAPI_MATH_H
typedef struct {
double a;
double b;
double c;
double d;
} CUBIC_COEFF;
typedef struct {
int configured;
double segmentTime;
int interpolationRate;
double interpolationTime;
double interpolationIncrement;
double x0, x1, x2, x3;
double wp0, wp1;
double velp0, velp1;
int filled;
int needNextPoint;
CUBIC_COEFF coeff;
} CUBIC_STRUCT;
#define CUBIC_H
#define cubicInit cnc_sim_cubic_cubicInit
#define cubicSetSegmentTime cnc_sim_cubic_cubicSetSegmentTime
#define cubicGetSegmentTime cnc_sim_cubic_cubicGetSegmentTime
#define cubicSetInterpolationRate cnc_sim_cubic_cubicSetInterpolationRate
#define cubicGetInterpolationRate cnc_sim_cubic_cubicGetInterpolationRate
#define cubicAddPoint cnc_sim_cubic_cubicAddPoint
#define cubicOffset cnc_sim_cubic_cubicOffset
#define cubicGetInterpolationIncrement cnc_sim_cubic_cubicGetInterpolationIncrement
#define cubicGetCubicCoeff cnc_sim_cubic_cubicGetCubicCoeff
#define cubicFilled cnc_sim_cubic_cubicFilled
#define cubicInterpolate cnc_sim_cubic_cubicInterpolate
#define cubicNeedNextPoint cnc_sim_cubic_cubicNeedNextPoint
#define cubicDrain cnc_sim_cubic_cubicDrain
int cubicInit(CUBIC_STRUCT *ci);
int cubicSetSegmentTime(CUBIC_STRUCT *ci, double time);
double cubicGetSegmentTime(CUBIC_STRUCT *ci);
int cubicSetInterpolationRate(CUBIC_STRUCT *ci, int rate);
int cubicGetInterpolationRate(CUBIC_STRUCT *ci);
int cubicAddPoint(CUBIC_STRUCT *ci, double point);
int cubicOffset(CUBIC_STRUCT *ci, double offset);
double cubicGetInterpolationIncrement(CUBIC_STRUCT *ci);
CUBIC_COEFF cubicGetCubicCoeff(CUBIC_STRUCT *ci);
int cubicFilled(CUBIC_STRUCT *ci);
double cubicInterpolate(CUBIC_STRUCT *ci, double *x, double *v, double *a, double *j);
int cubicNeedNextPoint(CUBIC_STRUCT *ci);
int cubicDrain(CUBIC_STRUCT *ci);
#include "../../../linuxcnc/src/emc/kinematics/cubic.c"
#undef cubicDrain
#undef cubicNeedNextPoint
#undef cubicInterpolate
#undef cubicFilled
#undef cubicGetCubicCoeff
#undef cubicGetInterpolationIncrement
#undef cubicOffset
#undef cubicAddPoint
#undef cubicGetInterpolationRate
#undef cubicSetInterpolationRate
#undef cubicGetSegmentTime
#undef cubicSetSegmentTime
#undef cubicInit
#undef CUBIC_H
#ifdef __cplusplus
extern "C" {
#endif
int cnc_sim_linuxcnc_cubic_second_segment_samples(double *x,
double *v,
double *a,
double *j,
int sample_count) {
if (!x || !v || !a || !j || sample_count != 4) {
return -1;
}
CUBIC_STRUCT cubic;
if (cnc_sim_cubic_cubicInit(&cubic) != 0 ||
cnc_sim_cubic_cubicSetSegmentTime(&cubic, 1.0) != 0 ||
cnc_sim_cubic_cubicSetInterpolationRate(&cubic, 4) != 0 ||
cnc_sim_cubic_cubicAddPoint(&cubic, 0.0) != 0) {
return -1;
}
for (int i = 0; i < 4; ++i) {
cnc_sim_cubic_cubicInterpolate(&cubic, NULL, NULL, NULL, NULL);
}
if (!cnc_sim_cubic_cubicNeedNextPoint(&cubic) ||
cnc_sim_cubic_cubicAddPoint(&cubic, 10.0) != 0) {
return -1;
}
for (int i = 0; i < sample_count; ++i) {
x[i] = cnc_sim_cubic_cubicInterpolate(&cubic, &x[i], &v[i], &a[i], &j[i]);
}
return cnc_sim_cubic_cubicNeedNextPoint(&cubic) ? 0 : -1;
}
int cnc_sim_linuxcnc_cubic_rejects_invalid_configuration(void) {
CUBIC_STRUCT cubic;
return cnc_sim_cubic_cubicInit(&cubic) == 0 &&
cnc_sim_cubic_cubicSetSegmentTime(&cubic, 0.0) != 0 &&
cnc_sim_cubic_cubicSetInterpolationRate(&cubic, 0) != 0 &&
cnc_sim_cubic_cubicAddPoint(&cubic, 1.0) != 0;
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,36 @@
#include "linuxcnc_cubic_adapter.h"
extern "C" {
int cnc_sim_linuxcnc_cubic_second_segment_samples(double *x,
double *v,
double *a,
double *j,
int sample_count);
int cnc_sim_linuxcnc_cubic_rejects_invalid_configuration(void);
}
bool linuxcnc_cubic_source_second_segment_samples(LinuxCncCubicSample *samples, int sample_count) {
if (!samples || sample_count != 4) {
return false;
}
double x[4] = {};
double v[4] = {};
double a[4] = {};
double j[4] = {};
if (cnc_sim_linuxcnc_cubic_second_segment_samples(x, v, a, j, sample_count) != 0) {
return false;
}
for (int i = 0; i < sample_count; ++i) {
samples[i].x = x[i];
samples[i].v = v[i];
samples[i].a = a[i];
samples[i].j = j[i];
}
return true;
}
bool linuxcnc_cubic_source_rejects_invalid_configuration() {
return cnc_sim_linuxcnc_cubic_rejects_invalid_configuration() != 0;
}

View File

@@ -0,0 +1,11 @@
#pragma once
struct LinuxCncCubicSample {
double x;
double v;
double a;
double j;
};
bool linuxcnc_cubic_source_second_segment_samples(LinuxCncCubicSample *samples, int sample_count);
bool linuxcnc_cubic_source_rejects_invalid_configuration();

View File

@@ -0,0 +1,223 @@
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_STRING_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_EMCMOTCFG_H
#define __LINUXCNC_KINEMATICS_H
#define SWITCHKINS_H
#define RTAPI_MSG_ERR 1
#define RTAPI_MSG_INFO 2
#define EMCMOT_MAX_JOINTS 16
typedef double real_t;
typedef double hal_float_t;
typedef int hal_bit_t;
typedef uint32_t hal_u32_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
#include "../../../linuxcnc/src/libnml/posemath/posemath.h"
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
typedef int (*KF)(const double *joint,
EmcPose *pos,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags);
typedef int (*KI)(const EmcPose *world,
double *joint,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags);
typedef int (*KS)(const int comp_id, const char *coordinates, kparms *ksetup_parms);
#ifdef __cplusplus
extern "C" {
#endif
void cnc_sim_genhex_rtapi_print_msg(int level, const char *fmt, ...) {
(void)level;
(void)fmt;
}
void *cnc_sim_genhex_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_genhex_hal_pin_float_newf(hal_pin_dir_t dir,
hal_float_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_float_t *)calloc(1, sizeof(hal_float_t));
return *data_ptr_addr ? 0 : -1;
}
int cnc_sim_genhex_hal_pin_u32_newf(hal_pin_dir_t dir,
hal_u32_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_u32_t *)calloc(1, sizeof(hal_u32_t));
return *data_ptr_addr ? 0 : -1;
}
int cnc_sim_genhex_hal_pin_bit_newf(hal_pin_dir_t dir,
hal_bit_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_bit_t *)calloc(1, sizeof(hal_bit_t));
return *data_ptr_addr ? 0 : -1;
}
int cnc_sim_genhex_identityKinematicsSetup(const int comp_id, const char *coordinates, kparms *ksetup_parms) {
(void)comp_id;
(void)coordinates;
(void)ksetup_parms;
return 0;
}
int cnc_sim_genhex_identityKinematicsForward(const double *joint,
EmcPose *world,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags) {
(void)joint;
(void)world;
(void)fflags;
(void)iflags;
return -1;
}
int cnc_sim_genhex_identityKinematicsInverse(const EmcPose *world,
double *joint,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags) {
(void)world;
(void)joint;
(void)iflags;
(void)fflags;
return -1;
}
int cnc_sim_genhex_userkKinematicsSetup(const int comp_id, const char *coordinates, kparms *ksetup_parms) {
(void)comp_id;
(void)coordinates;
(void)ksetup_parms;
return -1;
}
int cnc_sim_genhex_userkKinematicsForward(const double *joint,
EmcPose *world,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags) {
(void)joint;
(void)world;
(void)fflags;
(void)iflags;
return -1;
}
int cnc_sim_genhex_userkKinematicsInverse(const EmcPose *world,
double *joint,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags) {
(void)world;
(void)joint;
(void)iflags;
(void)fflags;
return -1;
}
#define rtapi_print_msg cnc_sim_genhex_rtapi_print_msg
#define hal_pin_float_newf cnc_sim_genhex_hal_pin_float_newf
#define hal_pin_u32_newf cnc_sim_genhex_hal_pin_u32_newf
#define hal_pin_bit_newf cnc_sim_genhex_hal_pin_bit_newf
#define identityKinematicsSetup cnc_sim_genhex_identityKinematicsSetup
#define identityKinematicsForward cnc_sim_genhex_identityKinematicsForward
#define identityKinematicsInverse cnc_sim_genhex_identityKinematicsInverse
#define userkKinematicsSetup cnc_sim_genhex_userkKinematicsSetup
#define userkKinematicsForward cnc_sim_genhex_userkKinematicsForward
#define userkKinematicsInverse cnc_sim_genhex_userkKinematicsInverse
#define switchkinsSetup cnc_sim_genhex_switchkinsSetup
#define haldata cnc_sim_genhex_haldata
#define hal_malloc(size) ((struct haldata *)cnc_sim_genhex_hal_malloc(size))
#include "../../../linuxcnc/src/emc/kinematics/genhexkins.c"
#undef hal_malloc
#undef haldata
#undef switchkinsSetup
#undef userkKinematicsInverse
#undef userkKinematicsForward
#undef userkKinematicsSetup
#undef identityKinematicsInverse
#undef identityKinematicsForward
#undef identityKinematicsSetup
#undef hal_pin_bit_newf
#undef hal_pin_u32_newf
#undef hal_pin_float_newf
#undef rtapi_print_msg
int cnc_sim_linuxcnc_genhexkins_setup(void) {
kparms kp;
memset(&kp, 0, sizeof(kp));
static char kinsname[] = "genhexkins";
static char halprefix[] = "genhexkins";
kp.kinsname = kinsname;
kp.halprefix = halprefix;
kp.max_joints = NUM_STRUTS;
return genhexKinematicsSetup(0, "XYZABC", &kp);
}
int cnc_sim_linuxcnc_genhexkins_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return genhexKinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_genhexkins_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return genhexKinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,96 @@
#include "linuxcnc_genhexkins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_genhexkins_setup(void);
int cnc_sim_linuxcnc_genhexkins_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_genhexkins_inverse(const EmcPose *pos, double *joints);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_genhexkins_setup() == 0;
return initialized;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_genhexkins_source_forward(const LinuxCncGenhexJoints &joints,
const CncSimPose &initial_pose,
CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
double raw_joints[6];
std::memcpy(raw_joints, joints.strut, sizeof(raw_joints));
EmcPose emc_pose = to_emc_pose(initial_pose);
if (cnc_sim_linuxcnc_genhexkins_forward(raw_joints, &emc_pose) != 0) {
return false;
}
*pose = from_emc_pose(emc_pose);
return true;
}
bool linuxcnc_genhexkins_source_inverse(const CncSimPose &pose, LinuxCncGenhexJoints *joints) {
if (!ensure_initialized()) {
return false;
}
const EmcPose emc_pose = to_emc_pose(pose);
double raw_joints[6] = {};
if (cnc_sim_linuxcnc_genhexkins_inverse(&emc_pose, raw_joints) != 0) {
return false;
}
std::memcpy(joints->strut, raw_joints, sizeof(raw_joints));
return true;
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_genhexkins_source_forward(const LinuxCncGenhexJoints &joints,
const CncSimPose &initial_pose,
CncSimPose *pose);
bool linuxcnc_genhexkins_source_inverse(const CncSimPose &pose, LinuxCncGenhexJoints *joints);

View File

@@ -0,0 +1,206 @@
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_STRING_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_KINEMATICS_H
#define RTAPI_MSG_ERR 1
#define PM_PI 3.1415926535897932384626433832795029
typedef double real_t;
typedef double hal_float_t;
typedef unsigned int hal_u32_t;
typedef int hal_s32_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#ifdef __cplusplus
extern "C" {
#endif
void cnc_sim_genser_rtapi_print(const char *fmt, ...) {
(void)fmt;
}
void cnc_sim_genser_rtapi_print_msg(int level, const char *fmt, ...) {
(void)level;
(void)fmt;
}
void *cnc_sim_genser_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_genser_hal_pin_float_newf(hal_pin_dir_t dir,
hal_float_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_float_t *)calloc(1, sizeof(hal_float_t));
return *data_ptr_addr ? 0 : -1;
}
int cnc_sim_genser_hal_pin_u32_newf(hal_pin_dir_t dir,
hal_u32_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_u32_t *)calloc(1, sizeof(hal_u32_t));
return *data_ptr_addr ? 0 : -1;
}
int cnc_sim_genser_hal_pin_s32_newf(hal_pin_dir_t dir,
hal_s32_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_s32_t *)calloc(1, sizeof(hal_s32_t));
return *data_ptr_addr ? 0 : -1;
}
#define rtapi_print cnc_sim_genser_rtapi_print
#define rtapi_print_msg cnc_sim_genser_rtapi_print_msg
#define hal_pin_float_newf cnc_sim_genser_hal_pin_float_newf
#define hal_pin_u32_newf cnc_sim_genser_hal_pin_u32_newf
#define hal_pin_s32_newf cnc_sim_genser_hal_pin_s32_newf
#define pm_sincos cnc_sim_genser_pm_sincos
#include "../../../linuxcnc/src/libnml/posemath/sincos.c"
#include "../../../linuxcnc/src/libnml/posemath/gomath.c"
#define hal_malloc(size) cnc_sim_genser_hal_malloc(size)
#include "../../../linuxcnc/src/emc/kinematics/genserfuncs.c"
#undef hal_malloc
#undef pm_sincos
#undef hal_pin_s32_newf
#undef hal_pin_u32_newf
#undef hal_pin_float_newf
#undef rtapi_print_msg
#undef rtapi_print
int cnc_sim_linuxcnc_genserfuncs_setup(void) {
kparms kp = {0};
static char kinsname[] = "genserkins";
static char halprefix[] = "genserkins";
kp.kinsname = kinsname;
kp.halprefix = halprefix;
kp.max_joints = 6;
return genserKinematicsSetup(0, "XYZABC", &kp);
}
void cnc_sim_linuxcnc_genserfuncs_set_parameters(const double *a,
const double *alpha,
const double *d,
const int *unrotate,
int max_iterations) {
for (int i = 0; i < GENSER_MAX_JOINTS; ++i) {
*haldata->a[i] = a[i];
*haldata->alpha[i] = alpha[i];
*haldata->d[i] = d[i];
*haldata->unrotate[i] = unrotate[i];
}
*haldata->max_iterations = (hal_u32_t)max_iterations;
}
void cnc_sim_linuxcnc_genserfuncs_default_parameters(double *a,
double *alpha,
double *d,
int *unrotate,
int *max_iterations) {
const double default_a[GENSER_MAX_JOINTS] = {
DEFAULT_A1,
DEFAULT_A2,
DEFAULT_A3,
DEFAULT_A4,
DEFAULT_A5,
DEFAULT_A6,
};
const double default_alpha[GENSER_MAX_JOINTS] = {
DEFAULT_ALPHA1,
DEFAULT_ALPHA2,
DEFAULT_ALPHA3,
DEFAULT_ALPHA4,
DEFAULT_ALPHA5,
DEFAULT_ALPHA6,
};
const double default_d[GENSER_MAX_JOINTS] = {
DEFAULT_D1,
DEFAULT_D2,
DEFAULT_D3,
DEFAULT_D4,
DEFAULT_D5,
DEFAULT_D6,
};
for (int i = 0; i < GENSER_MAX_JOINTS; ++i) {
a[i] = default_a[i];
alpha[i] = default_alpha[i];
d[i] = default_d[i];
unrotate[i] = 0;
}
*max_iterations = GENSER_DEFAULT_MAX_ITERATIONS;
}
int cnc_sim_linuxcnc_genserfuncs_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return genserKinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_genserfuncs_inverse(const EmcPose *pos, double *joints, int *iterations) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
const int result = genserKinematicsInverse(pos, joints, &iflags, &fflags);
if (iterations) {
*iterations = genser_kin_inv_iterations(KINS_PTR);
}
return result;
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,166 @@
#include "linuxcnc_genserfuncs_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_genserfuncs_setup(void);
void cnc_sim_linuxcnc_genserfuncs_set_parameters(const double *a,
const double *alpha,
const double *d,
const int *unrotate,
int max_iterations);
void cnc_sim_linuxcnc_genserfuncs_default_parameters(double *a,
double *alpha,
double *d,
int *unrotate,
int *max_iterations);
int cnc_sim_linuxcnc_genserfuncs_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_genserfuncs_inverse(const EmcPose *pos, double *joints, int *iterations);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_genserfuncs_setup() == 0;
return initialized;
}
void apply_parameters(const LinuxCncGenserParameters &parameters, int max_iterations) {
cnc_sim_linuxcnc_genserfuncs_set_parameters(parameters.a,
parameters.alpha,
parameters.d,
parameters.unrotate,
max_iterations);
}
void to_joints(const LinuxCncAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.a;
out[4] = joints.b;
out[5] = joints.c;
out[6] = joints.u;
out[7] = joints.v;
out[8] = joints.w;
}
LinuxCncAxisJoints from_joints(const double joints[16]) {
LinuxCncAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.a = joints[3];
out.b = joints[4];
out.c = joints[5];
out.u = joints[6];
out.v = joints[7];
out.w = joints[8];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_genserfuncs_forward(const LinuxCncAxisJoints &joints,
const LinuxCncGenserParameters &parameters,
CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
apply_parameters(parameters, 100);
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
if (cnc_sim_linuxcnc_genserfuncs_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_genserfuncs_source_default_parameters(LinuxCncGenserParameters *parameters,
int *max_iterations) {
if (!parameters) {
return false;
}
int raw_max_iterations = 0;
cnc_sim_linuxcnc_genserfuncs_default_parameters(parameters->a,
parameters->alpha,
parameters->d,
parameters->unrotate,
&raw_max_iterations);
if (max_iterations) {
*max_iterations = raw_max_iterations;
}
return true;
}
bool linuxcnc_genserfuncs_inverse(const CncSimPose &pose,
const LinuxCncAxisJoints &joint_estimate,
const LinuxCncGenserParameters &parameters,
int max_iterations,
LinuxCncAxisJoints *joints,
int *iterations) {
if (!ensure_initialized()) {
return false;
}
apply_parameters(parameters, max_iterations);
double raw_joints[16];
to_joints(joint_estimate, raw_joints);
const EmcPose in = to_emc_pose(pose);
if (cnc_sim_linuxcnc_genserfuncs_inverse(&in, raw_joints, iterations) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_genserfuncs_forward(const LinuxCncAxisJoints &joints,
const LinuxCncGenserParameters &parameters,
CncSimPose *pose);
bool linuxcnc_genserfuncs_source_default_parameters(LinuxCncGenserParameters *parameters,
int *max_iterations);
bool linuxcnc_genserfuncs_inverse(const CncSimPose &pose,
const LinuxCncAxisJoints &joint_estimate,
const LinuxCncGenserParameters &parameters,
int max_iterations,
LinuxCncAxisJoints *joints,
int *iterations);

View File

@@ -0,0 +1,116 @@
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_STRING_H
#define __LINUXCNC_EMCMOTCFG_H
#define __LINUXCNC_EMCPOS_H
#define __LINUXCNC_KINEMATICS_H
#define RTAPI_MSG_ERR 1
#define EMCMOT_MAX_JOINTS 16
#define EMCMOT_MAX_AXIS 9
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef enum {
KINEMATICS_IDENTITY = 1,
KINEMATICS_FORWARD_ONLY,
KINEMATICS_INVERSE_ONLY,
KINEMATICS_BOTH
} KINEMATICS_TYPE;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#ifdef __cplusplus
extern "C" {
#endif
void cnc_sim_kins_util_rtapi_print(const char *fmt, ...) {
(void)fmt;
}
void cnc_sim_kins_util_rtapi_print_msg(int level, const char *fmt, ...) {
(void)level;
(void)fmt;
}
KINEMATICS_TYPE kinematicsType(void) {
return KINEMATICS_BOTH;
}
#define rtapi_print cnc_sim_kins_util_rtapi_print
#define rtapi_print_msg cnc_sim_kins_util_rtapi_print_msg
#define map_coordinates_to_jnumbers cnc_sim_kins_util_map_coordinates_to_jnumbers
#define mapped_joints_to_position cnc_sim_kins_util_mapped_joints_to_position
#define position_to_mapped_joints cnc_sim_kins_util_position_to_mapped_joints
#define identityKinematicsSetup cnc_sim_kins_util_identityKinematicsSetup
#define identityKinematicsForward cnc_sim_kins_util_identityKinematicsForward
#define identityKinematicsInverse cnc_sim_kins_util_identityKinematicsInverse
#include "../../../linuxcnc/src/emc/kinematics/kins_util.c"
#undef identityKinematicsInverse
#undef identityKinematicsForward
#undef identityKinematicsSetup
#undef position_to_mapped_joints
#undef mapped_joints_to_position
#undef map_coordinates_to_jnumbers
#undef rtapi_print_msg
#undef rtapi_print
int cnc_sim_linuxcnc_kins_util_setup(void) {
kparms kp = {0};
static char kinsname[] = "kins_util_identity";
static char halprefix[] = "identity";
kp.kinsname = kinsname;
kp.halprefix = halprefix;
kp.max_joints = EMCMOT_MAX_JOINTS;
kp.allow_duplicates = 0;
return cnc_sim_kins_util_identityKinematicsSetup(0, "XYZABCUVW", &kp);
}
int cnc_sim_linuxcnc_kins_util_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return cnc_sim_kins_util_identityKinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_kins_util_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return cnc_sim_kins_util_identityKinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,121 @@
#include "linuxcnc_kins_util_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_kins_util_setup(void);
int cnc_sim_linuxcnc_kins_util_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_kins_util_inverse(const EmcPose *pos, double *joints);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_kins_util_setup() == 0;
return initialized;
}
void to_joints(const LinuxCncAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.a;
out[4] = joints.b;
out[5] = joints.c;
out[6] = joints.u;
out[7] = joints.v;
out[8] = joints.w;
}
LinuxCncAxisJoints from_joints(const double joints[16]) {
LinuxCncAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.a = joints[3];
out.b = joints[4];
out.c = joints[5];
out.u = joints[6];
out.v = joints[7];
out.w = joints[8];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_kins_util_identity_forward(const LinuxCncAxisJoints &joints, CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
if (cnc_sim_linuxcnc_kins_util_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_kins_util_identity_inverse(const CncSimPose &pose, LinuxCncAxisJoints *joints) {
if (!ensure_initialized()) {
return false;
}
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
if (cnc_sim_linuxcnc_kins_util_inverse(&in, raw_joints) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_kins_util_identity_forward(const LinuxCncAxisJoints &joints, CncSimPose *pose);
bool linuxcnc_kins_util_identity_inverse(const CncSimPose &pose, LinuxCncAxisJoints *joints);

View File

@@ -0,0 +1,155 @@
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_APP_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_KINEMATICS_H
#define __LINUXCNC_EMCPOS_H
#define MODULE_LICENSE(...)
#define EXPORT_SYMBOL(symbol)
#define EMCMOT_MAX_JOINTS 16
typedef double real_t;
typedef double hal_float_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef enum {
KINEMATICS_IDENTITY = 1,
KINEMATICS_FORWARD_ONLY,
KINEMATICS_INVERSE_ONLY,
KINEMATICS_BOTH
} KINEMATICS_TYPE;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#define KINS_NOT_SWITCHABLE \
extern int kinematicsSwitchable() { return 0; } \
extern int kinematicsSwitch(int switchkins_type) { (void)switchkins_type; return 0; }
#ifdef __cplusplus
extern "C" {
#endif
int cnc_sim_lineardelta_hal_init(const char *name) {
(void)name;
return 1;
}
void *cnc_sim_lineardelta_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_lineardelta_hal_ready(int comp_id) {
(void)comp_id;
return 0;
}
void cnc_sim_lineardelta_hal_exit(int comp_id) {
(void)comp_id;
}
int cnc_sim_lineardelta_hal_pin_float_newf(hal_pin_dir_t dir,
hal_float_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_float_t *)calloc(1, sizeof(hal_float_t));
return *data_ptr_addr ? 0 : -1;
}
#define hal_init cnc_sim_lineardelta_hal_init
#define hal_malloc(size) ((struct haldata *)cnc_sim_lineardelta_hal_malloc(size))
#define hal_pin_float_newf cnc_sim_lineardelta_hal_pin_float_newf
#define hal_ready cnc_sim_lineardelta_hal_ready
#define hal_exit cnc_sim_lineardelta_hal_exit
#define kinematicsType cnc_sim_lineardelta_kinematicsType
#define kinematicsForward cnc_sim_lineardelta_kinematicsForward
#define kinematicsInverse cnc_sim_lineardelta_kinematicsInverse
#define kinematicsSwitchable cnc_sim_lineardelta_kinematicsSwitchable
#define kinematicsSwitch cnc_sim_lineardelta_kinematicsSwitch
#define rtapi_app_main cnc_sim_lineardelta_rtapi_app_main
#define rtapi_app_exit cnc_sim_lineardelta_rtapi_app_exit
#define comp_id cnc_sim_lineardelta_comp_id
#define haldata cnc_sim_lineardelta_haldata
#include "../../../linuxcnc/src/emc/kinematics/lineardeltakins.c"
#undef haldata
#undef comp_id
#undef rtapi_app_exit
#undef rtapi_app_main
#undef kinematicsSwitch
#undef kinematicsSwitchable
#undef kinematicsInverse
#undef kinematicsForward
#undef kinematicsType
#undef hal_exit
#undef hal_ready
#undef hal_pin_float_newf
#undef hal_malloc
#undef hal_init
int cnc_sim_linuxcnc_lineardeltakins_setup(void) {
return cnc_sim_lineardelta_rtapi_app_main();
}
void cnc_sim_linuxcnc_lineardeltakins_default_parameters(double *r, double *l) {
*r = DELTA_RADIUS;
*l = DELTA_DIAGONAL_ROD;
}
void cnc_sim_linuxcnc_lineardeltakins_set_parameters(double r, double l) {
*cnc_sim_lineardelta_haldata->r = r;
*cnc_sim_lineardelta_haldata->l = l;
}
int cnc_sim_linuxcnc_lineardeltakins_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return cnc_sim_lineardelta_kinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_lineardeltakins_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return cnc_sim_lineardelta_kinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,138 @@
#include "linuxcnc_lineardeltakins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_lineardeltakins_setup(void);
void cnc_sim_linuxcnc_lineardeltakins_default_parameters(double *r, double *l);
void cnc_sim_linuxcnc_lineardeltakins_set_parameters(double r, double l);
int cnc_sim_linuxcnc_lineardeltakins_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_lineardeltakins_inverse(const EmcPose *pos, double *joints);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_lineardeltakins_setup() == 0;
return initialized;
}
void to_joints(const LinuxCncAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.a;
out[4] = joints.b;
out[5] = joints.c;
out[6] = joints.u;
out[7] = joints.v;
out[8] = joints.w;
}
LinuxCncAxisJoints from_joints(const double joints[16]) {
LinuxCncAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.a = joints[3];
out.b = joints[4];
out.c = joints[5];
out.u = joints[6];
out.v = joints[7];
out.w = joints[8];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_lineardeltakins_source_default_parameters(LinuxCncLinearDeltaParameters *parameters) {
double r = 0.0;
double l = 0.0;
cnc_sim_linuxcnc_lineardeltakins_default_parameters(&r, &l);
parameters->r = r;
parameters->l = l;
return true;
}
bool linuxcnc_lineardeltakins_source_forward(const LinuxCncAxisJoints &joints,
const LinuxCncLinearDeltaParameters &parameters,
CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
cnc_sim_linuxcnc_lineardeltakins_set_parameters(parameters.r, parameters.l);
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
if (cnc_sim_linuxcnc_lineardeltakins_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_lineardeltakins_source_inverse(const CncSimPose &pose,
const LinuxCncLinearDeltaParameters &parameters,
LinuxCncAxisJoints *joints) {
if (!ensure_initialized()) {
return false;
}
cnc_sim_linuxcnc_lineardeltakins_set_parameters(parameters.r, parameters.l);
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
if (cnc_sim_linuxcnc_lineardeltakins_inverse(&in, raw_joints) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_lineardeltakins_source_default_parameters(LinuxCncLinearDeltaParameters *parameters);
bool linuxcnc_lineardeltakins_source_forward(const LinuxCncAxisJoints &joints,
const LinuxCncLinearDeltaParameters &parameters,
CncSimPose *pose);
bool linuxcnc_lineardeltakins_source_inverse(const CncSimPose &pose,
const LinuxCncLinearDeltaParameters &parameters,
LinuxCncAxisJoints *joints);

View File

@@ -0,0 +1,168 @@
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_APP_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_KINEMATICS_H
#define __LINUXCNC_EMCPOS_H
#define MODULE_LICENSE(...)
#define EXPORT_SYMBOL(symbol)
#define EMCMOT_MAX_JOINTS 16
#define PM_PI 3.1415926535897932384626433832795029
typedef double real_t;
typedef double hal_float_t;
typedef bool hal_bit_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef enum {
KINEMATICS_IDENTITY = 1,
KINEMATICS_FORWARD_ONLY,
KINEMATICS_INVERSE_ONLY,
KINEMATICS_BOTH
} KINEMATICS_TYPE;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#define KINS_NOT_SWITCHABLE \
extern int kinematicsSwitchable() { return 0; } \
extern int kinematicsSwitch(int switchkins_type) { (void)switchkins_type; return 0; }
#ifdef __cplusplus
extern "C" {
#endif
int cnc_sim_maxkins_hal_init(const char *name) {
(void)name;
return 1;
}
void *cnc_sim_maxkins_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_maxkins_hal_ready(int comp_id) {
(void)comp_id;
return 0;
}
void cnc_sim_maxkins_hal_exit(int comp_id) {
(void)comp_id;
}
int cnc_sim_maxkins_hal_pin_float_new(const char *name,
hal_pin_dir_t dir,
hal_float_t **data_ptr_addr,
int comp_id) {
(void)name;
(void)dir;
(void)comp_id;
*data_ptr_addr = (hal_float_t *)calloc(1, sizeof(hal_float_t));
return *data_ptr_addr ? 0 : -1;
}
int cnc_sim_maxkins_hal_pin_bit_new(const char *name,
hal_pin_dir_t dir,
hal_bit_t **data_ptr_addr,
int comp_id) {
(void)name;
(void)dir;
(void)comp_id;
*data_ptr_addr = (hal_bit_t *)calloc(1, sizeof(hal_bit_t));
return *data_ptr_addr ? 0 : -1;
}
#define hal_init cnc_sim_maxkins_hal_init
#define hal_malloc(size) ((struct haldata *)cnc_sim_maxkins_hal_malloc(size))
#define hal_pin_float_new cnc_sim_maxkins_hal_pin_float_new
#define hal_pin_bit_new cnc_sim_maxkins_hal_pin_bit_new
#define hal_ready cnc_sim_maxkins_hal_ready
#define hal_exit cnc_sim_maxkins_hal_exit
#define kinematicsType cnc_sim_maxkins_kinematicsType
#define kinematicsForward cnc_sim_maxkins_kinematicsForward
#define kinematicsInverse cnc_sim_maxkins_kinematicsInverse
#define kinematicsSwitchable cnc_sim_maxkins_kinematicsSwitchable
#define kinematicsSwitch cnc_sim_maxkins_kinematicsSwitch
#define rtapi_app_main cnc_sim_maxkins_rtapi_app_main
#define rtapi_app_exit cnc_sim_maxkins_rtapi_app_exit
#define comp_id cnc_sim_maxkins_comp_id
#define haldata cnc_sim_maxkins_haldata
#include "../../../linuxcnc/src/emc/kinematics/maxkins.c"
#undef haldata
#undef comp_id
#undef rtapi_app_exit
#undef rtapi_app_main
#undef kinematicsSwitch
#undef kinematicsSwitchable
#undef kinematicsInverse
#undef kinematicsForward
#undef kinematicsType
#undef hal_exit
#undef hal_ready
#undef hal_pin_bit_new
#undef hal_pin_float_new
#undef hal_malloc
#undef hal_init
int cnc_sim_linuxcnc_maxkins_setup(void) {
return cnc_sim_maxkins_rtapi_app_main();
}
void cnc_sim_linuxcnc_maxkins_default_parameters(double *pivot_length, int *conventional_directions) {
*pivot_length = 0.666;
*conventional_directions = 0;
}
void cnc_sim_linuxcnc_maxkins_set_parameters(double pivot_length, int conventional_directions) {
*cnc_sim_maxkins_haldata->pivot_length = pivot_length;
*cnc_sim_maxkins_haldata->conventional_directions = conventional_directions != 0;
}
int cnc_sim_linuxcnc_maxkins_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return cnc_sim_maxkins_kinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_maxkins_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return cnc_sim_maxkins_kinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,140 @@
#include "linuxcnc_maxkins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_maxkins_setup(void);
void cnc_sim_linuxcnc_maxkins_default_parameters(double *pivot_length, int *conventional_directions);
void cnc_sim_linuxcnc_maxkins_set_parameters(double pivot_length, int conventional_directions);
int cnc_sim_linuxcnc_maxkins_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_maxkins_inverse(const EmcPose *pos, double *joints);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_maxkins_setup() == 0;
return initialized;
}
void to_joints(const LinuxCncAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.a;
out[4] = joints.b;
out[5] = joints.c;
out[6] = joints.u;
out[7] = joints.v;
out[8] = joints.w;
}
LinuxCncAxisJoints from_joints(const double joints[16]) {
LinuxCncAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.a = joints[3];
out.b = joints[4];
out.c = joints[5];
out.u = joints[6];
out.v = joints[7];
out.w = joints[8];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_maxkins_source_default_parameters(LinuxCncMaxkinsParameters *parameters) {
double pivot_length = 0.0;
int conventional_directions = 0;
cnc_sim_linuxcnc_maxkins_default_parameters(&pivot_length, &conventional_directions);
parameters->pivot_length = pivot_length;
parameters->conventional_directions = conventional_directions != 0;
return true;
}
bool linuxcnc_maxkins_source_forward(const LinuxCncAxisJoints &joints,
const LinuxCncMaxkinsParameters &parameters,
CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
cnc_sim_linuxcnc_maxkins_set_parameters(parameters.pivot_length,
parameters.conventional_directions ? 1 : 0);
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
if (cnc_sim_linuxcnc_maxkins_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_maxkins_source_inverse(const CncSimPose &pose,
const LinuxCncMaxkinsParameters &parameters,
LinuxCncAxisJoints *joints) {
if (!ensure_initialized()) {
return false;
}
cnc_sim_linuxcnc_maxkins_set_parameters(parameters.pivot_length,
parameters.conventional_directions ? 1 : 0);
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
if (cnc_sim_linuxcnc_maxkins_inverse(&in, raw_joints) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_maxkins_source_default_parameters(LinuxCncMaxkinsParameters *parameters);
bool linuxcnc_maxkins_source_forward(const LinuxCncAxisJoints &joints,
const LinuxCncMaxkinsParameters &parameters,
CncSimPose *pose);
bool linuxcnc_maxkins_source_inverse(const CncSimPose &pose,
const LinuxCncMaxkinsParameters &parameters,
LinuxCncAxisJoints *joints);

View File

@@ -0,0 +1,172 @@
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_APP_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_KINEMATICS_H
#define __LINUXCNC_EMCPOS_H
#define MODULE_LICENSE(...)
#define EXPORT_SYMBOL(symbol)
#define EMCMOT_MAX_JOINTS 16
typedef double real_t;
typedef double hal_float_t;
typedef uint32_t hal_u32_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef enum { HAL_RO = 64, HAL_RW = (HAL_RO | 128) } hal_param_dir_t;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
#include "../../../linuxcnc/src/libnml/posemath/posemath.h"
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef enum {
KINEMATICS_IDENTITY = 1,
KINEMATICS_FORWARD_ONLY,
KINEMATICS_INVERSE_ONLY,
KINEMATICS_BOTH
} KINEMATICS_TYPE;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#define KINS_NOT_SWITCHABLE \
extern int kinematicsSwitchable() { return 0; } \
extern int kinematicsSwitch(int switchkins_type) { (void)switchkins_type; return 0; }
#ifdef __cplusplus
extern "C" {
#endif
int cnc_sim_pentakins_hal_init(const char *name) {
(void)name;
return 1;
}
void *cnc_sim_pentakins_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_pentakins_hal_ready(int comp_id) {
(void)comp_id;
return 0;
}
void cnc_sim_pentakins_hal_exit(int comp_id) {
(void)comp_id;
}
int cnc_sim_pentakins_hal_param_float_newf(hal_param_dir_t dir,
hal_float_t *data_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)data_addr;
(void)comp_id;
(void)fmt;
return 0;
}
int cnc_sim_pentakins_hal_pin_float_newf(hal_pin_dir_t dir,
hal_float_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_float_t *)calloc(1, sizeof(hal_float_t));
return *data_ptr_addr ? 0 : -1;
}
int cnc_sim_pentakins_hal_pin_u32_newf(hal_pin_dir_t dir,
hal_u32_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_u32_t *)calloc(1, sizeof(hal_u32_t));
return *data_ptr_addr ? 0 : -1;
}
#define hal_init cnc_sim_pentakins_hal_init
#define hal_malloc(size) ((struct haldata *)cnc_sim_pentakins_hal_malloc(size))
#define hal_param_float_newf cnc_sim_pentakins_hal_param_float_newf
#define hal_pin_float_newf cnc_sim_pentakins_hal_pin_float_newf
#define hal_pin_u32_newf cnc_sim_pentakins_hal_pin_u32_newf
#define hal_ready cnc_sim_pentakins_hal_ready
#define hal_exit cnc_sim_pentakins_hal_exit
#define kinematicsType cnc_sim_pentakins_kinematicsType
#define kinematicsForward cnc_sim_pentakins_kinematicsForward
#define kinematicsInverse cnc_sim_pentakins_kinematicsInverse
#define kinematicsSwitchable cnc_sim_pentakins_kinematicsSwitchable
#define kinematicsSwitch cnc_sim_pentakins_kinematicsSwitch
#define rtapi_app_main cnc_sim_pentakins_rtapi_app_main
#define rtapi_app_exit cnc_sim_pentakins_rtapi_app_exit
#define comp_id cnc_sim_pentakins_comp_id
#define haldata cnc_sim_pentakins_haldata
#include "../../../linuxcnc/src/emc/kinematics/pentakins.c"
#undef haldata
#undef comp_id
#undef rtapi_app_exit
#undef rtapi_app_main
#undef kinematicsSwitch
#undef kinematicsSwitchable
#undef kinematicsInverse
#undef kinematicsForward
#undef kinematicsType
#undef hal_exit
#undef hal_ready
#undef hal_pin_u32_newf
#undef hal_pin_float_newf
#undef hal_param_float_newf
#undef hal_malloc
#undef hal_init
int cnc_sim_linuxcnc_pentakins_setup(void) {
return cnc_sim_pentakins_rtapi_app_main();
}
int cnc_sim_linuxcnc_pentakins_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return cnc_sim_pentakins_kinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_pentakins_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return cnc_sim_pentakins_kinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,97 @@
#include "linuxcnc_pentakins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_pentakins_setup(void);
int cnc_sim_linuxcnc_pentakins_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_pentakins_inverse(const EmcPose *pos, double *joints);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_pentakins_setup() == 0;
return initialized;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_pentakins_source_forward(const LinuxCncPentakinsJoints &joints,
const CncSimPose &initial_pose,
CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
double raw_joints[5];
std::memcpy(raw_joints, joints.strut, sizeof(raw_joints));
EmcPose emc_pose = to_emc_pose(initial_pose);
if (cnc_sim_linuxcnc_pentakins_forward(raw_joints, &emc_pose) != 0) {
return false;
}
*pose = from_emc_pose(emc_pose);
return true;
}
bool linuxcnc_pentakins_source_inverse(const CncSimPose &pose,
LinuxCncPentakinsJoints *joints) {
if (!ensure_initialized()) {
return false;
}
const EmcPose emc_pose = to_emc_pose(pose);
double raw_joints[5] = {};
if (cnc_sim_linuxcnc_pentakins_inverse(&emc_pose, raw_joints) != 0) {
return false;
}
std::memcpy(joints->strut, raw_joints, sizeof(raw_joints));
return true;
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_pentakins_source_forward(const LinuxCncPentakinsJoints &joints,
const CncSimPose &initial_pose,
CncSimPose *pose);
bool linuxcnc_pentakins_source_inverse(const CncSimPose &pose,
LinuxCncPentakinsJoints *joints);

View File

@@ -0,0 +1,218 @@
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_STRING_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_KINEMATICS_H
#define SWITCHKINS_H
#ifdef __cplusplus
#define CNC_SIM_EXTERN_C extern "C"
#else
#define CNC_SIM_EXTERN_C
#endif
typedef double real_t;
typedef double hal_float_t;
typedef int hal_s32_t;
typedef bool hal_bit_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
#include "../../../linuxcnc/src/libnml/posemath/sincos.c"
#include "../../../linuxcnc/src/libnml/posemath/_posemath.c"
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
typedef int (*KF)(const double *joint,
EmcPose *pos,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags);
typedef int (*KI)(const EmcPose *world,
double *joint,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags);
typedef int (*KS)(const int comp_id, const char *coordinates, kparms *ksetup_parms);
void cnc_sim_puma_rtapi_print(const char *fmt, ...) {
(void)fmt;
}
void cnc_sim_puma_rtapi_print_msg(int level, const char *fmt, ...) {
(void)level;
(void)fmt;
}
void *cnc_sim_puma_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_puma_hal_pin_float_newf(hal_pin_dir_t dir,
hal_float_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_float_t *)calloc(1, sizeof(hal_float_t));
return *data_ptr_addr ? 0 : -1;
}
int cnc_sim_puma_identityKinematicsSetup(const int comp_id, const char *coordinates, kparms *ksetup_parms) {
(void)comp_id;
(void)coordinates;
(void)ksetup_parms;
return 0;
}
int cnc_sim_puma_identityKinematicsForward(const double *joint,
EmcPose *world,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags) {
(void)joint;
(void)world;
(void)fflags;
(void)iflags;
return -1;
}
int cnc_sim_puma_identityKinematicsInverse(const EmcPose *world,
double *joint,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags) {
(void)world;
(void)joint;
(void)iflags;
(void)fflags;
return -1;
}
int cnc_sim_puma_userkKinematicsSetup(const int comp_id, const char *coordinates, kparms *ksetup_parms) {
(void)comp_id;
(void)coordinates;
(void)ksetup_parms;
return -1;
}
int cnc_sim_puma_userkKinematicsForward(const double *joint,
EmcPose *world,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags) {
(void)joint;
(void)world;
(void)fflags;
(void)iflags;
return -1;
}
int cnc_sim_puma_userkKinematicsInverse(const EmcPose *world,
double *joint,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags) {
(void)world;
(void)joint;
(void)iflags;
(void)fflags;
return -1;
}
#define rtapi_print cnc_sim_puma_rtapi_print
#define rtapi_print_msg cnc_sim_puma_rtapi_print_msg
#define hal_pin_float_newf cnc_sim_puma_hal_pin_float_newf
#define identityKinematicsSetup cnc_sim_puma_identityKinematicsSetup
#define identityKinematicsForward cnc_sim_puma_identityKinematicsForward
#define identityKinematicsInverse cnc_sim_puma_identityKinematicsInverse
#define userkKinematicsSetup cnc_sim_puma_userkKinematicsSetup
#define userkKinematicsForward cnc_sim_puma_userkKinematicsForward
#define userkKinematicsInverse cnc_sim_puma_userkKinematicsInverse
#define switchkinsSetup cnc_sim_puma_switchkinsSetup
#define haldata cnc_sim_puma_haldata
#define hal_malloc(size) ((struct haldata *)cnc_sim_puma_hal_malloc(size))
#include "../../../linuxcnc/src/emc/kinematics/pumakins.c"
#undef hal_malloc
#undef haldata
#undef switchkinsSetup
#undef userkKinematicsInverse
#undef userkKinematicsForward
#undef userkKinematicsSetup
#undef identityKinematicsInverse
#undef identityKinematicsForward
#undef identityKinematicsSetup
#undef hal_pin_float_newf
#undef rtapi_print_msg
#undef rtapi_print
CNC_SIM_EXTERN_C int cnc_sim_linuxcnc_pumakins_setup(void) {
kparms kp = {0};
static char kinsname[] = "pumakins";
static char halprefix[] = "pumakins";
kp.kinsname = kinsname;
kp.halprefix = halprefix;
return pumaKinematicsSetup(0, "XYZABC", &kp);
}
CNC_SIM_EXTERN_C void cnc_sim_linuxcnc_pumakins_set_parameters(double a2,
double a3,
double d3,
double d4,
double d6) {
*cnc_sim_puma_haldata->a2 = a2;
*cnc_sim_puma_haldata->a3 = a3;
*cnc_sim_puma_haldata->d3 = d3;
*cnc_sim_puma_haldata->d4 = d4;
*cnc_sim_puma_haldata->d6 = d6;
}
CNC_SIM_EXTERN_C int cnc_sim_linuxcnc_pumakins_forward(const double *joints,
struct EmcPose *pos,
int *iflags) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS raw_iflags = 0;
const int result = pumaKinematicsForward(joints, pos, &fflags, &raw_iflags);
if (iflags) {
*iflags = (int)raw_iflags;
}
return result;
}
CNC_SIM_EXTERN_C int cnc_sim_linuxcnc_pumakins_inverse(const struct EmcPose *pos,
double *joints,
int iflags,
int *fflags) {
KINEMATICS_INVERSE_FLAGS raw_iflags = (KINEMATICS_INVERSE_FLAGS)iflags;
KINEMATICS_FORWARD_FLAGS raw_fflags = 0;
const int result = pumaKinematicsInverse(pos, joints, &raw_iflags, &raw_fflags);
if (fflags) {
*fflags = (int)raw_fflags;
}
return result;
}

View File

@@ -0,0 +1,156 @@
#include "linuxcnc_pumakins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_pumakins_setup(void);
void cnc_sim_linuxcnc_pumakins_set_parameters(double a2,
double a3,
double d3,
double d4,
double d6);
int cnc_sim_linuxcnc_pumakins_forward(const double *joints, EmcPose *pos, int *iflags);
int cnc_sim_linuxcnc_pumakins_inverse(const EmcPose *pos,
double *joints,
int iflags,
int *fflags);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_pumakins_setup() == 0;
return initialized;
}
void apply_parameters(const LinuxCncPumaParameters &parameters) {
cnc_sim_linuxcnc_pumakins_set_parameters(parameters.a2,
parameters.a3,
parameters.d3,
parameters.d4,
parameters.d6);
}
void to_joints(const LinuxCncAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.a;
out[4] = joints.b;
out[5] = joints.c;
out[6] = joints.u;
out[7] = joints.v;
out[8] = joints.w;
}
LinuxCncAxisJoints from_joints(const double joints[16]) {
LinuxCncAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.a = joints[3];
out.b = joints[4];
out.c = joints[5];
out.u = joints[6];
out.v = joints[7];
out.w = joints[8];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_pumakins_forward(const LinuxCncAxisJoints &joints,
const LinuxCncPumaParameters &parameters,
CncSimPose *pose,
int *iflags) {
if (!ensure_initialized()) {
return false;
}
apply_parameters(parameters);
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
int raw_iflags = 0;
if (cnc_sim_linuxcnc_pumakins_forward(raw_joints, &out, &raw_iflags) != 0) {
return false;
}
if (iflags) {
*iflags = raw_iflags;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_pumakins_inverse(const CncSimPose &pose,
const LinuxCncAxisJoints &current_joints,
const LinuxCncPumaParameters &parameters,
int iflags,
LinuxCncAxisJoints *joints,
int *fflags) {
if (!ensure_initialized()) {
return false;
}
apply_parameters(parameters);
double raw_joints[16];
to_joints(current_joints, raw_joints);
const EmcPose in = to_emc_pose(pose);
int raw_fflags = 0;
if (cnc_sim_linuxcnc_pumakins_inverse(&in, raw_joints, iflags, &raw_fflags) != 0) {
return false;
}
if (fflags) {
*fflags = raw_fflags;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_pumakins_forward(const LinuxCncAxisJoints &joints,
const LinuxCncPumaParameters &parameters,
CncSimPose *pose,
int *iflags);
bool linuxcnc_pumakins_inverse(const CncSimPose &pose,
const LinuxCncAxisJoints &current_joints,
const LinuxCncPumaParameters &parameters,
int iflags,
LinuxCncAxisJoints *joints,
int *fflags);

View File

@@ -0,0 +1,147 @@
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_APP_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_KINEMATICS_H
#define __LINUXCNC_EMCPOS_H
#define MODULE_LICENSE(...)
#define EXPORT_SYMBOL(symbol)
#define EMCMOT_MAX_JOINTS 16
#define PM_PI 3.1415926535897932384626433832795029
#define PM_2_PI (2.0 * PM_PI)
#define TO_RAD (PM_PI / 180.0)
#define TO_DEG (180.0 / PM_PI)
typedef double real_t;
typedef double hal_float_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef enum {
KINEMATICS_IDENTITY = 1,
KINEMATICS_FORWARD_ONLY,
KINEMATICS_INVERSE_ONLY,
KINEMATICS_BOTH
} KINEMATICS_TYPE;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#define KINS_NOT_SWITCHABLE \
extern int kinematicsSwitchable() { return 0; } \
extern int kinematicsSwitch(int switchkins_type) { (void)switchkins_type; return 0; }
#ifdef __cplusplus
extern "C" {
#endif
int cnc_sim_rosekins_hal_init(const char *name) {
(void)name;
return 1;
}
void *cnc_sim_rosekins_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_rosekins_hal_ready(int comp_id) {
(void)comp_id;
return 0;
}
void cnc_sim_rosekins_hal_exit(int comp_id) {
(void)comp_id;
}
int cnc_sim_rosekins_hal_pin_float_new(const char *name,
hal_pin_dir_t dir,
hal_float_t **data_ptr_addr,
int comp_id) {
(void)name;
(void)dir;
(void)comp_id;
*data_ptr_addr = (hal_float_t *)calloc(1, sizeof(hal_float_t));
return *data_ptr_addr ? 0 : -1;
}
#define hal_init cnc_sim_rosekins_hal_init
#define hal_malloc(size) ((struct haldata *)cnc_sim_rosekins_hal_malloc(size))
#define hal_pin_float_new cnc_sim_rosekins_hal_pin_float_new
#define hal_ready cnc_sim_rosekins_hal_ready
#define hal_exit cnc_sim_rosekins_hal_exit
#define kinematicsType cnc_sim_rosekins_kinematicsType
#define kinematicsForward cnc_sim_rosekins_kinematicsForward
#define kinematicsInverse cnc_sim_rosekins_kinematicsInverse
#define kinematicsSwitchable cnc_sim_rosekins_kinematicsSwitchable
#define kinematicsSwitch cnc_sim_rosekins_kinematicsSwitch
#define rtapi_app_main cnc_sim_rosekins_rtapi_app_main
#define rtapi_app_exit cnc_sim_rosekins_rtapi_app_exit
#define comp_id cnc_sim_rosekins_comp_id
#define haldata cnc_sim_rosekins_haldata
#include "../../../linuxcnc/src/emc/kinematics/rosekins.c"
#undef haldata
#undef comp_id
#undef rtapi_app_exit
#undef rtapi_app_main
#undef kinematicsSwitch
#undef kinematicsSwitchable
#undef kinematicsInverse
#undef kinematicsForward
#undef kinematicsType
#undef hal_exit
#undef hal_ready
#undef hal_pin_float_new
#undef hal_malloc
#undef hal_init
int cnc_sim_linuxcnc_rosekins_setup(void) {
return cnc_sim_rosekins_rtapi_app_main();
}
int cnc_sim_linuxcnc_rosekins_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return cnc_sim_rosekins_kinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_rosekins_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return cnc_sim_rosekins_kinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,121 @@
#include "linuxcnc_rosekins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_rosekins_setup(void);
int cnc_sim_linuxcnc_rosekins_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_rosekins_inverse(const EmcPose *pos, double *joints);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_rosekins_setup() == 0;
return initialized;
}
void to_joints(const LinuxCncAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.a;
out[4] = joints.b;
out[5] = joints.c;
out[6] = joints.u;
out[7] = joints.v;
out[8] = joints.w;
}
LinuxCncAxisJoints from_joints(const double joints[16]) {
LinuxCncAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.a = joints[3];
out.b = joints[4];
out.c = joints[5];
out.u = joints[6];
out.v = joints[7];
out.w = joints[8];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_rosekins_source_forward(const LinuxCncAxisJoints &joints, CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
if (cnc_sim_linuxcnc_rosekins_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_rosekins_source_inverse(const CncSimPose &pose, LinuxCncAxisJoints *joints) {
if (!ensure_initialized()) {
return false;
}
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
if (cnc_sim_linuxcnc_rosekins_inverse(&in, raw_joints) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_rosekins_source_forward(const LinuxCncAxisJoints &joints, CncSimPose *pose);
bool linuxcnc_rosekins_source_inverse(const CncSimPose &pose, LinuxCncAxisJoints *joints);

View File

@@ -0,0 +1,165 @@
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_APP_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_KINEMATICS_H
#define __LINUXCNC_EMCPOS_H
#define MODULE_LICENSE(...)
#define EXPORT_SYMBOL(symbol)
#define EMCMOT_MAX_JOINTS 16
typedef double real_t;
typedef double hal_float_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef enum {
KINEMATICS_IDENTITY = 1,
KINEMATICS_FORWARD_ONLY,
KINEMATICS_INVERSE_ONLY,
KINEMATICS_BOTH
} KINEMATICS_TYPE;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#define KINS_NOT_SWITCHABLE \
extern int kinematicsSwitchable() { return 0; } \
extern int kinematicsSwitch(int switchkins_type) { (void)switchkins_type; return 0; }
#ifdef __cplusplus
extern "C" {
#endif
int cnc_sim_rotarydelta_hal_init(const char *name) {
(void)name;
return 1;
}
void *cnc_sim_rotarydelta_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_rotarydelta_hal_ready(int comp_id) {
(void)comp_id;
return 0;
}
void cnc_sim_rotarydelta_hal_exit(int comp_id) {
(void)comp_id;
}
int cnc_sim_rotarydelta_hal_pin_float_newf(hal_pin_dir_t dir,
hal_float_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_float_t *)calloc(1, sizeof(hal_float_t));
return *data_ptr_addr ? 0 : -1;
}
#define hal_init cnc_sim_rotarydelta_hal_init
#define hal_malloc(size) ((struct haldata *)cnc_sim_rotarydelta_hal_malloc(size))
#define hal_pin_float_newf cnc_sim_rotarydelta_hal_pin_float_newf
#define hal_ready cnc_sim_rotarydelta_hal_ready
#define hal_exit cnc_sim_rotarydelta_hal_exit
#define kinematicsType cnc_sim_rotarydelta_kinematicsType
#define kinematicsForward cnc_sim_rotarydelta_kinematicsForward
#define kinematicsInverse cnc_sim_rotarydelta_kinematicsInverse
#define kinematicsSwitchable cnc_sim_rotarydelta_kinematicsSwitchable
#define kinematicsSwitch cnc_sim_rotarydelta_kinematicsSwitch
#define rtapi_app_main cnc_sim_rotarydelta_rtapi_app_main
#define rtapi_app_exit cnc_sim_rotarydelta_rtapi_app_exit
#define comp_id cnc_sim_rotarydelta_comp_id
#define haldata cnc_sim_rotarydelta_haldata
#include "../../../linuxcnc/src/emc/kinematics/rotarydeltakins.c"
#undef haldata
#undef comp_id
#undef rtapi_app_exit
#undef rtapi_app_main
#undef kinematicsSwitch
#undef kinematicsSwitchable
#undef kinematicsInverse
#undef kinematicsForward
#undef kinematicsType
#undef hal_exit
#undef hal_ready
#undef hal_pin_float_newf
#undef hal_malloc
#undef hal_init
int cnc_sim_linuxcnc_rotarydeltakins_setup(void) {
return cnc_sim_rotarydelta_rtapi_app_main();
}
void cnc_sim_linuxcnc_rotarydeltakins_default_parameters(double *platformradius,
double *thighlength,
double *shinlength,
double *footradius) {
*platformradius = RDELTA_PFR;
*thighlength = RDELTA_TL;
*shinlength = RDELTA_SL;
*footradius = RDELTA_FR;
}
void cnc_sim_linuxcnc_rotarydeltakins_set_parameters(double platformradius,
double thighlength,
double shinlength,
double footradius) {
*cnc_sim_rotarydelta_haldata->pfr = platformradius;
*cnc_sim_rotarydelta_haldata->tl = thighlength;
*cnc_sim_rotarydelta_haldata->sl = shinlength;
*cnc_sim_rotarydelta_haldata->fr = footradius;
}
int cnc_sim_linuxcnc_rotarydeltakins_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return cnc_sim_rotarydelta_kinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_rotarydeltakins_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return cnc_sim_rotarydelta_kinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,157 @@
#include "linuxcnc_rotarydeltakins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_rotarydeltakins_setup(void);
void cnc_sim_linuxcnc_rotarydeltakins_default_parameters(double *platformradius,
double *thighlength,
double *shinlength,
double *footradius);
void cnc_sim_linuxcnc_rotarydeltakins_set_parameters(double platformradius,
double thighlength,
double shinlength,
double footradius);
int cnc_sim_linuxcnc_rotarydeltakins_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_rotarydeltakins_inverse(const EmcPose *pos, double *joints);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_rotarydeltakins_setup() == 0;
return initialized;
}
void to_joints(const LinuxCncAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.a;
out[4] = joints.b;
out[5] = joints.c;
out[6] = joints.u;
out[7] = joints.v;
out[8] = joints.w;
}
LinuxCncAxisJoints from_joints(const double joints[16]) {
LinuxCncAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.a = joints[3];
out.b = joints[4];
out.c = joints[5];
out.u = joints[6];
out.v = joints[7];
out.w = joints[8];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_rotarydeltakins_source_default_parameters(LinuxCncRotaryDeltaParameters *parameters) {
double platformradius = 0.0;
double thighlength = 0.0;
double shinlength = 0.0;
double footradius = 0.0;
cnc_sim_linuxcnc_rotarydeltakins_default_parameters(&platformradius,
&thighlength,
&shinlength,
&footradius);
parameters->platformradius = platformradius;
parameters->thighlength = thighlength;
parameters->shinlength = shinlength;
parameters->footradius = footradius;
return true;
}
bool linuxcnc_rotarydeltakins_source_forward(const LinuxCncAxisJoints &joints,
const LinuxCncRotaryDeltaParameters &parameters,
CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
cnc_sim_linuxcnc_rotarydeltakins_set_parameters(parameters.platformradius,
parameters.thighlength,
parameters.shinlength,
parameters.footradius);
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
if (cnc_sim_linuxcnc_rotarydeltakins_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_rotarydeltakins_source_inverse(const CncSimPose &pose,
const LinuxCncRotaryDeltaParameters &parameters,
LinuxCncAxisJoints *joints) {
if (!ensure_initialized()) {
return false;
}
cnc_sim_linuxcnc_rotarydeltakins_set_parameters(parameters.platformradius,
parameters.thighlength,
parameters.shinlength,
parameters.footradius);
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
if (cnc_sim_linuxcnc_rotarydeltakins_inverse(&in, raw_joints) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_rotarydeltakins_source_default_parameters(LinuxCncRotaryDeltaParameters *parameters);
bool linuxcnc_rotarydeltakins_source_forward(const LinuxCncAxisJoints &joints,
const LinuxCncRotaryDeltaParameters &parameters,
CncSimPose *pose);
bool linuxcnc_rotarydeltakins_source_inverse(const CncSimPose &pose,
const LinuxCncRotaryDeltaParameters &parameters,
LinuxCncAxisJoints *joints);

View File

@@ -0,0 +1,118 @@
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_APP_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_KINEMATICS_H
#define MODULE_LICENSE(...)
#define EXPORT_SYMBOL(symbol)
#define RTAPI_MSG_ERR 1
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef enum {
KINEMATICS_IDENTITY = 1,
KINEMATICS_FORWARD_ONLY,
KINEMATICS_INVERSE_ONLY,
KINEMATICS_BOTH
} KINEMATICS_TYPE;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#define KINS_NOT_SWITCHABLE \
extern int kinematicsSwitchable() { return 0; } \
extern int kinematicsSwitch(int switchkins_type) { (void)switchkins_type; return 0; }
#ifdef __cplusplus
extern "C" {
#endif
int cnc_sim_rotatekins_hal_init(const char *name) {
(void)name;
return 1;
}
int cnc_sim_rotatekins_hal_ready(int comp_id) {
(void)comp_id;
return 0;
}
void cnc_sim_rotatekins_hal_exit(int comp_id) {
(void)comp_id;
}
#define hal_init cnc_sim_rotatekins_hal_init
#define hal_ready cnc_sim_rotatekins_hal_ready
#define hal_exit cnc_sim_rotatekins_hal_exit
#define kinematicsType cnc_sim_rotatekins_kinematicsType
#define kinematicsForward cnc_sim_rotatekins_kinematicsForward
#define kinematicsInverse cnc_sim_rotatekins_kinematicsInverse
#define kinematicsHome cnc_sim_rotatekins_kinematicsHome
#define kinematicsSwitchable cnc_sim_rotatekins_kinematicsSwitchable
#define kinematicsSwitch cnc_sim_rotatekins_kinematicsSwitch
#define rtapi_app_main cnc_sim_rotatekins_rtapi_app_main
#define rtapi_app_exit cnc_sim_rotatekins_rtapi_app_exit
#define comp_id cnc_sim_rotatekins_comp_id
#include "../../../linuxcnc/src/emc/kinematics/rotatekins.c"
#undef comp_id
#undef rtapi_app_exit
#undef rtapi_app_main
#undef kinematicsSwitch
#undef kinematicsSwitchable
#undef kinematicsHome
#undef kinematicsInverse
#undef kinematicsForward
#undef kinematicsType
#undef hal_exit
#undef hal_ready
#undef hal_init
int cnc_sim_linuxcnc_rotatekins_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return cnc_sim_rotatekins_kinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_rotatekins_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return cnc_sim_rotatekins_kinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,104 @@
#include "linuxcnc_rotatekins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_rotatekins_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_rotatekins_inverse(const EmcPose *pos, double *joints);
}
namespace {
void to_joints(const LinuxCncAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.a;
out[4] = joints.b;
out[5] = joints.c;
out[6] = joints.u;
out[7] = joints.v;
out[8] = joints.w;
}
LinuxCncAxisJoints from_joints(const double joints[16]) {
LinuxCncAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.a = joints[3];
out.b = joints[4];
out.c = joints[5];
out.u = joints[6];
out.v = joints[7];
out.w = joints[8];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_rotatekins_source_forward(const LinuxCncAxisJoints &joints, CncSimPose *pose) {
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
if (cnc_sim_linuxcnc_rotatekins_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_rotatekins_source_inverse(const CncSimPose &pose, LinuxCncAxisJoints *joints) {
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
if (cnc_sim_linuxcnc_rotatekins_inverse(&in, raw_joints) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_rotatekins_source_forward(const LinuxCncAxisJoints &joints, CncSimPose *pose);
bool linuxcnc_rotatekins_source_inverse(const CncSimPose &pose, LinuxCncAxisJoints *joints);

View File

@@ -1,6 +1,10 @@
#include "linuxcnc_rs274_backend.h" #include "linuxcnc_rs274_backend.h"
#ifdef CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE
#include "boost/python/object.hpp"
#else
#include <Python.h> #include <Python.h>
#endif
#include "linuxcnc_canon_bridge.h" #include "linuxcnc_canon_bridge.h"
#include "linuxcnc_tooldata_fixture.h" #include "linuxcnc_tooldata_fixture.h"
@@ -16,11 +20,15 @@
#include <vector> #include <vector>
#include <cstdlib> #include <cstdlib>
#include <fstream> #include <fstream>
#ifndef CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE
#include <unistd.h> #include <unistd.h>
#endif
int _task = 0;
char _parameter_file_name[LINELEN]; char _parameter_file_name[LINELEN];
#ifndef CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE
int _task = 0;
extern "C" PyObject *PyInit_interpreter(void); extern "C" PyObject *PyInit_interpreter(void);
extern "C" PyObject *PyInit_emccanon(void); extern "C" PyObject *PyInit_emccanon(void);
extern "C" struct _inittab builtin_modules[]; extern "C" struct _inittab builtin_modules[];
@@ -29,6 +37,7 @@ struct _inittab builtin_modules[] = {
{"emccanon", PyInit_emccanon}, {"emccanon", PyInit_emccanon},
{nullptr, nullptr}, {nullptr, nullptr},
}; };
#endif
namespace { namespace {
@@ -73,12 +82,48 @@ bool file_mode_enabled() {
return std::getenv("CNC_SIM_RS274_FILE_MODE") != nullptr; return std::getenv("CNC_SIM_RS274_FILE_MODE") != nullptr;
} }
class ScopedEnvironmentVariable {
public:
ScopedEnvironmentVariable(const char *name, const std::string &value) : name_(name), active_(!value.empty()) {
if (!active_) {
return;
}
if (const char *old = std::getenv(name_)) {
had_old_ = true;
old_ = old;
}
setenv(name_, value.c_str(), 1);
}
~ScopedEnvironmentVariable() {
if (!active_) {
return;
}
if (had_old_) {
setenv(name_, old_.c_str(), 1);
} else {
unsetenv(name_);
}
}
private:
const char *name_;
bool active_;
bool had_old_ = false;
std::string old_;
};
#ifndef CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE
bool write_temp_program(const char *program, bool write_temp_program(const char *program,
size_t program_len, size_t program_len,
std::string *path, std::string *path,
std::string *error) { std::string *error) {
char tmpl[] = "/tmp/cnc_sim_api_XXXXXX.ngc"; const char *tmpdir = std::getenv("TMPDIR");
const int fd = mkstemps(tmpl, 4); std::string tmpl = std::string(tmpdir ? tmpdir : "/tmp") + "/cnc_sim_api_XXXXXX.ngc";
std::vector<char> tmpl_buffer(tmpl.begin(), tmpl.end());
tmpl_buffer.push_back('\0');
const int fd = mkstemps(tmpl_buffer.data(), 4);
if (fd < 0) { if (fd < 0) {
if (error) { if (error) {
*error = "failed to create temporary ngc file"; *error = "failed to create temporary ngc file";
@@ -100,15 +145,16 @@ bool write_temp_program(const char *program,
ok = false; ok = false;
} }
if (!ok) { if (!ok) {
unlink(tmpl); unlink(tmpl_buffer.data());
if (error) { if (error) {
*error = "failed to write temporary ngc file"; *error = "failed to write temporary ngc file";
} }
return false; return false;
} }
*path = tmpl; *path = tmpl_buffer.data();
return true; return true;
} }
#endif
int execute_file_mode(InterpBase *interp, int execute_file_mode(InterpBase *interp,
CanonEventSink &sink, CanonEventSink &sink,
@@ -160,6 +206,7 @@ int execute_file_mode(InterpBase *interp,
int parse_linuxcnc_rs274_backend(CanonEventSink &sink, int parse_linuxcnc_rs274_backend(CanonEventSink &sink,
const char *program, const char *program,
size_t program_len, size_t program_len,
const GcodeBackendOptions &options,
std::string *error) { std::string *error) {
if (!program && program_len != 0) { if (!program && program_len != 0) {
if (error) { if (error) {
@@ -168,14 +215,36 @@ int parse_linuxcnc_rs274_backend(CanonEventSink &sink,
return -1; return -1;
} }
// LinuxCNC source basis: rs274ngc_pre.cc uses
// RS274NGC_PARAMETER_FILE_NAME_DEFAULT when the external parameter file
// name is empty. Reset the bridge every parse so a prior native env
// override does not leak into later default parses.
if (const char *parameter_file = std::getenv("CNC_SIM_RS274_VAR")) { if (const char *parameter_file = std::getenv("CNC_SIM_RS274_VAR")) {
SET_PARAMETER_FILE_NAME(parameter_file); SET_PARAMETER_FILE_NAME(parameter_file);
} else {
SET_PARAMETER_FILE_NAME("rs274ngc.var");
} }
cnc_sim_init_minimal_linuxcnc_tooldata(); cnc_sim_init_minimal_linuxcnc_tooldata();
sink.clear_callback_status(); sink.clear_callback_status();
cnc_sim_linuxcnc_set_canon_sink(&sink); cnc_sim_linuxcnc_set_canon_sink(&sink);
// LinuxCNC source basis: rs274ngc_pre.cc and interp_namedparams.cc read
// INI_FILE_NAME for startup and #<_ini[section]key> named parameters.
ScopedEnvironmentVariable ini_file_env("INI_FILE_NAME", options.ini_file_name);
InterpBase *interp = makeInterp(); InterpBase *interp = makeInterp();
if (!options.ini_file_name.empty()) {
// LinuxCNC source basis: emctask.cc calls Interp::ini_load() before
// Interp::init(), and rs274ngc_pre.cc ini_load() maps
// [RS274NGC] PARAMETER_FILE through SET_PARAMETER_FILE_NAME().
if (interp->ini_load(options.ini_file_name.c_str()) != INTERP_OK) {
if (error) {
*error = "ini_load failed: " + options.ini_file_name;
}
delete interp;
cnc_sim_linuxcnc_set_canon_sink(nullptr);
return -1;
}
}
int status = interp->init(); int status = interp->init();
if (status != INTERP_OK) { if (status != INTERP_OK) {
if (error) { if (error) {
@@ -185,8 +254,18 @@ int parse_linuxcnc_rs274_backend(CanonEventSink &sink,
cnc_sim_linuxcnc_set_canon_sink(nullptr); cnc_sim_linuxcnc_set_canon_sink(nullptr);
return -1; return -1;
} }
SET_BLOCK_DELETE(sink.block_delete());
if (file_mode_enabled()) { if (file_mode_enabled()) {
#ifdef CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE
if (error) {
*error = "linuxcnc-rs274 file mode is not available in wasm-safe builds";
}
interp->exit();
delete interp;
cnc_sim_linuxcnc_set_canon_sink(nullptr);
return -1;
#else
std::string temp_path; std::string temp_path;
if (!write_temp_program(program, program_len, &temp_path, error)) { if (!write_temp_program(program, program_len, &temp_path, error)) {
interp->exit(); interp->exit();
@@ -202,6 +281,7 @@ int parse_linuxcnc_rs274_backend(CanonEventSink &sink,
cnc_sim_linuxcnc_set_canon_sink(nullptr); cnc_sim_linuxcnc_set_canon_sink(nullptr);
return -1; return -1;
} }
#endif
} else { } else {
std::string source(program, program + program_len); std::string source(program, program + program_len);
std::istringstream input(source); std::istringstream input(source);
@@ -212,21 +292,47 @@ int parse_linuxcnc_rs274_backend(CanonEventSink &sink,
++line_number; ++line_number;
cnc_sim_linuxcnc_set_current_line(line_number); cnc_sim_linuxcnc_set_current_line(line_number);
std::vector<SimulatorGcodeControlAction> control_actions; std::vector<SimulatorGcodeControlAction> control_actions;
if (parse_simulator_gcode_control_line(line, &control_actions)) { if (parse_simulator_gcode_control_line(line, sink.block_delete(), &control_actions)) {
bool simulator_only_line = true; bool simulator_only_line = true;
bool all_control_actions_handled = true;
for (const auto &action : control_actions) { for (const auto &action : control_actions) {
emit_simulator_gcode_control_action(sink, action, line_number); if (!can_emit_simulator_gcode_control_action(sink, action)) {
all_control_actions_handled = false;
simulator_only_line = false;
}
if (action.kind == SimulatorGcodeControlKind::RtcpState && if (action.kind == SimulatorGcodeControlKind::RtcpState &&
!action.rtcp_enabled) { !action.rtcp_enabled) {
simulator_only_line = false; simulator_only_line = false;
} }
if (stop_if_callback_aborted(sink, error)) { }
interp->exit(); if (!all_control_actions_handled && control_actions.size() > 1) {
delete interp; for (const auto &action : control_actions) {
cnc_sim_linuxcnc_set_canon_sink(nullptr); if (!can_emit_simulator_gcode_control_action(sink, action) &&
return -1; action.kind == SimulatorGcodeControlKind::KinematicsSwitch) {
if (error) {
*error = "M-code greater than 199: M" + std::to_string(action.m_code);
}
interp->exit();
delete interp;
cnc_sim_linuxcnc_set_canon_sink(nullptr);
return -1;
}
} }
} }
if (all_control_actions_handled) {
for (const auto &action : control_actions) {
emit_simulator_gcode_control_action(sink, action, line_number);
if (stop_if_callback_aborted(sink, error)) {
interp->exit();
delete interp;
cnc_sim_linuxcnc_set_canon_sink(nullptr);
return -1;
}
}
}
if (!all_control_actions_handled) {
simulator_only_line = false;
}
if (simulator_only_line) { if (simulator_only_line) {
continue; continue;
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "canon_event_sink.h" #include "canon_event_sink.h"
#include "gcode_backend.h"
#include <cstddef> #include <cstddef>
#include <string> #include <string>
@@ -8,5 +9,5 @@
int parse_linuxcnc_rs274_backend(CanonEventSink &sink, int parse_linuxcnc_rs274_backend(CanonEventSink &sink,
const char *program, const char *program,
size_t program_len, size_t program_len,
const GcodeBackendOptions &options,
std::string *error); std::string *error);

View File

@@ -0,0 +1,224 @@
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_STRING_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_KINEMATICS_H
#define SWITCHKINS_H
#ifdef __cplusplus
#define CNC_SIM_EXTERN_C extern "C"
#else
#define CNC_SIM_EXTERN_C
#endif
#define PM_PI 3.1415926535897932384626433832795029
typedef double real_t;
typedef double hal_float_t;
typedef int hal_s32_t;
typedef bool hal_bit_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
typedef int (*KF)(const double *joint,
EmcPose *pos,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags);
typedef int (*KI)(const struct EmcPose *world,
double *joint,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags);
typedef int (*KS)(const int comp_id, const char *coordinates, kparms *ksetup_parms);
void cnc_sim_scara_rtapi_print(const char *fmt, ...) {
(void)fmt;
}
void cnc_sim_scara_rtapi_print_msg(int level, const char *fmt, ...) {
(void)level;
(void)fmt;
}
void *cnc_sim_scara_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_scara_hal_pin_float_newf(hal_pin_dir_t dir,
hal_float_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_float_t *)calloc(1, sizeof(hal_float_t));
return *data_ptr_addr ? 0 : -1;
}
int cnc_sim_scara_identityKinematicsSetup(const int comp_id,
const char *coordinates,
kparms *ksetup_parms) {
(void)comp_id;
(void)coordinates;
(void)ksetup_parms;
return 0;
}
int cnc_sim_scara_identityKinematicsForward(const double *joint,
struct EmcPose *world,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags) {
(void)joint;
(void)world;
(void)fflags;
(void)iflags;
return -1;
}
int cnc_sim_scara_identityKinematicsInverse(const struct EmcPose *world,
double *joint,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags) {
(void)world;
(void)joint;
(void)iflags;
(void)fflags;
return -1;
}
int cnc_sim_scara_userkKinematicsSetup(const int comp_id,
const char *coordinates,
kparms *ksetup_parms) {
(void)comp_id;
(void)coordinates;
(void)ksetup_parms;
return -1;
}
int cnc_sim_scara_userkKinematicsForward(const double *joint,
struct EmcPose *world,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags) {
(void)joint;
(void)world;
(void)fflags;
(void)iflags;
return -1;
}
int cnc_sim_scara_userkKinematicsInverse(const struct EmcPose *world,
double *joint,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags) {
(void)world;
(void)joint;
(void)iflags;
(void)fflags;
return -1;
}
#define rtapi_print cnc_sim_scara_rtapi_print
#define rtapi_print_msg cnc_sim_scara_rtapi_print_msg
#define hal_pin_float_newf cnc_sim_scara_hal_pin_float_newf
#define identityKinematicsSetup cnc_sim_scara_identityKinematicsSetup
#define identityKinematicsForward cnc_sim_scara_identityKinematicsForward
#define identityKinematicsInverse cnc_sim_scara_identityKinematicsInverse
#define userkKinematicsSetup cnc_sim_scara_userkKinematicsSetup
#define userkKinematicsForward cnc_sim_scara_userkKinematicsForward
#define userkKinematicsInverse cnc_sim_scara_userkKinematicsInverse
#define switchkinsSetup cnc_sim_scara_switchkinsSetup
#define haldata cnc_sim_scara_haldata
#define hal_malloc(size) ((struct scara_data *)cnc_sim_scara_hal_malloc(size))
#include "../../../linuxcnc/src/emc/kinematics/scarakins.c"
#undef hal_malloc
#undef haldata
#undef switchkinsSetup
#undef userkKinematicsInverse
#undef userkKinematicsForward
#undef userkKinematicsSetup
#undef identityKinematicsInverse
#undef identityKinematicsForward
#undef identityKinematicsSetup
#undef hal_pin_float_newf
#undef rtapi_print_msg
#undef rtapi_print
CNC_SIM_EXTERN_C int cnc_sim_linuxcnc_scarakins_setup(void) {
kparms kp = {0};
static char kinsname[] = "scarakins";
static char halprefix[] = "scarakins";
kp.kinsname = kinsname;
kp.halprefix = halprefix;
return scaraKinematicsSetup(0, "XYZABC", &kp);
}
CNC_SIM_EXTERN_C void cnc_sim_linuxcnc_scarakins_set_parameters(double d1,
double d2,
double d3,
double d4,
double d5,
double d6) {
*cnc_sim_scara_haldata->d1 = d1;
*cnc_sim_scara_haldata->d2 = d2;
*cnc_sim_scara_haldata->d3 = d3;
*cnc_sim_scara_haldata->d4 = d4;
*cnc_sim_scara_haldata->d5 = d5;
*cnc_sim_scara_haldata->d6 = d6;
}
CNC_SIM_EXTERN_C int cnc_sim_linuxcnc_scarakins_forward(const double *joints,
struct EmcPose *pos,
int *iflags) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS raw_iflags = 0;
const int result = scaraKinematicsForward(joints, pos, &fflags, &raw_iflags);
if (iflags) {
*iflags = (int)raw_iflags;
}
return result;
}
CNC_SIM_EXTERN_C int cnc_sim_linuxcnc_scarakins_inverse(const struct EmcPose *pos,
double *joints,
int iflags) {
KINEMATICS_INVERSE_FLAGS raw_iflags = (KINEMATICS_INVERSE_FLAGS)iflags;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return scaraKinematicsInverse(pos, joints, &raw_iflags, &fflags);
}

View File

@@ -0,0 +1,144 @@
#include "linuxcnc_scarakins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_scarakins_setup(void);
void cnc_sim_linuxcnc_scarakins_set_parameters(double d1,
double d2,
double d3,
double d4,
double d5,
double d6);
int cnc_sim_linuxcnc_scarakins_forward(const double *joints, EmcPose *pos, int *iflags);
int cnc_sim_linuxcnc_scarakins_inverse(const EmcPose *pos,
double *joints,
int iflags);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_scarakins_setup() == 0;
return initialized;
}
void apply_parameters(const LinuxCncScaraParameters &parameters) {
cnc_sim_linuxcnc_scarakins_set_parameters(parameters.d1,
parameters.d2,
parameters.d3,
parameters.d4,
parameters.d5,
parameters.d6);
}
void to_joints(const LinuxCncScaraJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.j0;
out[1] = joints.j1;
out[2] = joints.j2;
out[3] = joints.j3;
out[4] = joints.j4;
out[5] = joints.j5;
}
LinuxCncScaraJoints from_joints(const double joints[16]) {
LinuxCncScaraJoints out{};
out.j0 = joints[0];
out.j1 = joints[1];
out.j2 = joints[2];
out.j3 = joints[3];
out.j4 = joints[4];
out.j5 = joints[5];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_scarakins_forward(const LinuxCncScaraJoints &joints,
const LinuxCncScaraParameters &parameters,
CncSimPose *pose,
int *iflags) {
if (!ensure_initialized()) {
return false;
}
apply_parameters(parameters);
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
int raw_iflags = 0;
if (cnc_sim_linuxcnc_scarakins_forward(raw_joints, &out, &raw_iflags) != 0) {
return false;
}
if (iflags) {
*iflags = raw_iflags;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_scarakins_inverse(const CncSimPose &pose,
const LinuxCncScaraParameters &parameters,
int iflags,
LinuxCncScaraJoints *joints) {
if (!ensure_initialized()) {
return false;
}
apply_parameters(parameters);
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
if (cnc_sim_linuxcnc_scarakins_inverse(&in, raw_joints, iflags) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,12 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_scarakins_forward(const LinuxCncScaraJoints &joints,
const LinuxCncScaraParameters &parameters,
CncSimPose *pose,
int *iflags);
bool linuxcnc_scarakins_inverse(const CncSimPose &pose,
const LinuxCncScaraParameters &parameters,
int iflags,
LinuxCncScaraJoints *joints);

View File

@@ -0,0 +1,121 @@
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_APP_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_GO_TYPES_H
#define __LINUXCNC_KINEMATICS_H
#define __LINUXCNC_EMCPOS_H
#define MODULE_LICENSE(...)
#define EXPORT_SYMBOL(symbol)
#define PM_PI 3.1415926535897932384626433832795029
#define TO_RAD (PM_PI / 180.0)
#define TO_DEG (180.0 / PM_PI)
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef enum {
KINEMATICS_IDENTITY = 1,
KINEMATICS_FORWARD_ONLY,
KINEMATICS_INVERSE_ONLY,
KINEMATICS_BOTH
} KINEMATICS_TYPE;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#define KINS_NOT_SWITCHABLE \
extern int kinematicsSwitchable() { return 0; } \
extern int kinematicsSwitch(int switchkins_type) { (void)switchkins_type; return 0; }
#ifdef __cplusplus
extern "C" {
#endif
int cnc_sim_scorbot_kins_hal_init(const char *name) {
(void)name;
return 1;
}
int cnc_sim_scorbot_kins_hal_ready(int comp_id) {
(void)comp_id;
return 0;
}
void cnc_sim_scorbot_kins_hal_exit(int comp_id) {
(void)comp_id;
}
#define hal_init cnc_sim_scorbot_kins_hal_init
#define hal_ready cnc_sim_scorbot_kins_hal_ready
#define hal_exit cnc_sim_scorbot_kins_hal_exit
#define kinematicsType cnc_sim_scorbot_kins_kinematicsType
#define kinematicsForward cnc_sim_scorbot_kins_kinematicsForward
#define kinematicsInverse cnc_sim_scorbot_kins_kinematicsInverse
#define kinematicsSwitchable cnc_sim_scorbot_kins_kinematicsSwitchable
#define kinematicsSwitch cnc_sim_scorbot_kins_kinematicsSwitch
#define rtapi_app_main cnc_sim_scorbot_kins_rtapi_app_main
#define rtapi_app_exit cnc_sim_scorbot_kins_rtapi_app_exit
#define comp_id cnc_sim_scorbot_kins_comp_id
#include "../../../linuxcnc/src/emc/kinematics/scorbot-kins.c"
#undef comp_id
#undef rtapi_app_exit
#undef rtapi_app_main
#undef kinematicsSwitch
#undef kinematicsSwitchable
#undef kinematicsInverse
#undef kinematicsForward
#undef kinematicsType
#undef hal_exit
#undef hal_ready
#undef hal_init
int cnc_sim_linuxcnc_scorbot_kins_setup(void) {
return cnc_sim_scorbot_kins_rtapi_app_main();
}
int cnc_sim_linuxcnc_scorbot_kins_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return cnc_sim_scorbot_kins_kinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_scorbot_kins_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return cnc_sim_scorbot_kins_kinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,110 @@
#include "linuxcnc_scorbot_kins_adapter.h"
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_scorbot_kins_setup(void);
int cnc_sim_linuxcnc_scorbot_kins_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_scorbot_kins_inverse(const EmcPose *pos, double *joints);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_scorbot_kins_setup() == 0;
return initialized;
}
void to_joints(const LinuxCncScorbotJoints &joints, double out[5]) {
out[0] = joints.j0;
out[1] = joints.j1;
out[2] = joints.j2;
out[3] = joints.j3;
out[4] = joints.j4;
}
LinuxCncScorbotJoints from_joints(const double joints[5]) {
LinuxCncScorbotJoints out{};
out.j0 = joints[0];
out.j1 = joints[1];
out.j2 = joints[2];
out.j3 = joints[3];
out.j4 = joints[4];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_scorbot_kins_source_forward(const LinuxCncScorbotJoints &joints, CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
double raw_joints[5] = {};
to_joints(joints, raw_joints);
EmcPose out{};
if (cnc_sim_linuxcnc_scorbot_kins_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_scorbot_kins_source_inverse(const CncSimPose &pose, LinuxCncScorbotJoints *joints) {
if (!ensure_initialized()) {
return false;
}
const EmcPose in = to_emc_pose(pose);
double raw_joints[5] = {};
if (cnc_sim_linuxcnc_scorbot_kins_inverse(&in, raw_joints) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_scorbot_kins_source_forward(const LinuxCncScorbotJoints &joints, CncSimPose *pose);
bool linuxcnc_scorbot_kins_source_inverse(const CncSimPose &pose, LinuxCncScorbotJoints *joints);

View File

@@ -0,0 +1,180 @@
// Generated by ./generate-linuxcnc-switchkins-remap-table.sh.
// Source: LinuxCNC INI MACHINE/KINEMATICS/HALFILE/POSTGUI_HALFILE/SUBROUTINE_PATH/REMAP entries and
// adjacent remap_subs/{428,429,430}remap.ngc #<kinstype> assignments.
{"configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/bridgemill", 0, 1, 2},
{"bridgemill", 0, 1, 2},
{"5axis", 0, 1, 2},
{"sim-5axisbridgemill(xyzbcw)", 0, 1, 2},
{"sim-5axisbridgemill", 0, 1, 2},
{"5axiskinscoordinates=xyzbcwy", 0, 1, 2},
{"5axiskins", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/bridgemill/5axisgui.hal", 0, 1, 2},
{"5axisgui.hal", 0, 1, 2},
{"5axisgui", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/bridgemill/5axis_postgui.hal", 0, 1, 2},
{"5axis_postgui.hal", 0, 1, 2},
{"5axis_postgui", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/bridgemill/remap_subs", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/bridgemill/remap_subs/429remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/bridgemill/remap_subs/430remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1},
{"configs/sim/axis/vismach/5axis/table-dual-rotary", 1, 0, -1},
{"table-dual-rotary", 1, 0, -1},
{"xyzab-tdr", 1, 0, -1},
{"sim-xyzab-tdr-kins(switchkins)", 1, 0, -1},
{"sim-xyzab-tdr-kins", 1, 0, -1},
{"xyzab_tdr_kins", 1, 0, -1},
{"xyzab_tdr", 1, 0, -1},
{"xyzab-tdr-kins", 1, 0, -1},
{"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr-postgui.hal", 1, 0, -1},
{"xyzab-tdr-postgui.hal", 1, 0, -1},
{"xyzab-tdr-postgui", 1, 0, -1},
{"configs/sim/axis/vismach/5axis/table-dual-rotary/remap_subs/428remap.ngc", 1, 0, -1},
{"configs/sim/axis/vismach/5axis/table-dual-rotary/remap_subs", 1, 0, -1},
{"configs/sim/axis/vismach/5axis/table-dual-rotary/remap_subs/429remap.ngc", 1, 0, -1},
{"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2},
{"configs/sim/axis/vismach/5axis/table-rotary-tilting", 1, 0, 2},
{"table-rotary-tilting", 1, 0, 2},
{"xyzac-trt", 1, 0, 2},
{"sim-xyzac-trt-kins(switchkins)", 1, 0, 2},
{"sim-xyzac-trt-kins", 1, 0, 2},
{"xyzac-trt-kinssparm=identityfirst", 1, 0, 2},
{"xyzac-trt-kins", 1, 0, 2},
{"configs/sim/axis/vismach/5axis/table-rotary-tilting/switchkins_postgui.hal", 1, 0, 2},
{"switchkins_postgui.hal", 1, 0, 2},
{"switchkins_postgui", 1, 0, 2},
{"configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/428remap.ngc", 1, 0, 2},
{"configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs", 1, 0, 2},
{"configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/429remap.ngc", 1, 0, 2},
{"configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/430remap.ngc", 1, 0, 2},
{"configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2},
{"xyzbc-trt", 1, 0, 2},
{"sim-xyzbc-trt-kins(switchkins)", 1, 0, 2},
{"sim-xyzbc-trt-kins", 1, 0, 2},
{"xyzbc-trt-kinssparm=identityfirst", 1, 0, 2},
{"xyzbc-trt-kins", 1, 0, 2},
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp", 0, 1, 2},
{"xyzacb-trsrn_twp", 0, 1, 2},
{"xyzacb-trsrn", 0, 1, 2},
{"xyzacb-trsrn(switchkins)", 0, 1, 2},
{"xyzacb_trsrn", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn_postgui.hal", 0, 1, 2},
{"xyzacb-trsrn_postgui.hal", 0, 1, 2},
{"xyzacb-trsrn_postgui", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/428remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating", 0, 1, 2},
{"table-rotary_spindle-rotary-nutating", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/429remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/430remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp", 0, 1, 2},
{"xyzbca-trsrn_twp", 0, 1, 2},
{"xyzbca-trsrn", 0, 1, 2},
{"xyzbca-trsrn(switchkins)", 0, 1, 2},
{"xyzbca_trsrn", 0, 1, 2},
{"configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn_postgui.hal", 0, 1, 2},
{"xyzbca-trsrn_postgui.hal", 0, 1, 2},
{"xyzbca-trsrn_postgui", 0, 1, 2},
{"configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2},
{"configs/sim/axis/vismach/hexapod-sim", 0, 1, 2},
{"hexapod-sim", 0, 1, 2},
{"hexapod", 0, 1, 2},
{"hexapod(switchkins)", 0, 1, 2},
{"genhexkins", 0, 1, 2},
{"configs/sim/axis/vismach/hexapod-sim/kinematics.hal", 0, 1, 2},
{"kinematics.hal", 0, 1, 2},
{"kinematics", 0, 1, 2},
{"configs/sim/axis/vismach/hexapod-sim/hexapod_postgui.hal", 0, 1, 2},
{"hexapod_postgui.hal", 0, 1, 2},
{"hexapod_postgui", 0, 1, 2},
{"configs/sim/axis/vismach/hexapod-sim/remap_subs/428remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/hexapod-sim/remap_subs", 0, 1, 2},
{"configs/sim/axis/vismach/hexapod-sim/remap_subs/429remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/hexapod-sim/remap_subs/430remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2},
{"configs/sim/axis/vismach/melfa-sim", 0, 1, 2},
{"melfa-sim", 0, 1, 2},
{"melfa", 0, 1, 2},
{"melfa(mm)", 0, 1, 2},
{"genserkins", 0, 1, 2},
{"configs/sim/axis/vismach/melfa-sim/melfa_dh.hal", 0, 1, 2},
{"melfa_dh.hal", 0, 1, 2},
{"melfa_dh", 0, 1, 2},
{"configs/sim/axis/vismach/melfa-sim/melfa-postgui.hal", 0, 1, 2},
{"melfa-postgui.hal", 0, 1, 2},
{"melfa-postgui", 0, 1, 2},
{"configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/melfa-sim/remap_subs", 0, 1, 2},
{"configs/sim/axis/vismach/melfa-sim/remap_subs/429remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/melfa-sim/remap_subs/430remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1},
{"configs/sim/axis/vismach/millturn", 0, 1, -1},
{"millturn", 0, 1, -1},
{"millturn(mm)", 0, 1, -1},
{"configs/sim/axis/vismach/millturn/millturn.hal", 0, 1, -1},
{"millturn.hal", 0, 1, -1},
{"configs/sim/axis/vismach/millturn/millturn-postgui.hal", 0, 1, -1},
{"millturn-postgui.hal", 0, 1, -1},
{"millturn-postgui", 0, 1, -1},
{"configs/sim/axis/vismach/millturn/remap_subs/428remap.ngc", 0, 1, -1},
{"configs/sim/axis/vismach/millturn/remap_subs", 0, 1, -1},
{"configs/sim/axis/vismach/millturn/remap_subs/429remap.ngc", 0, 1, -1},
{"configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2},
{"configs/sim/axis/vismach/puma", 0, 1, 2},
{"puma", 0, 1, 2},
{"puma(pumakins,switchkins)", 0, 1, 2},
{"pumakins", 0, 1, 2},
{"configs/sim/axis/vismach/puma/puma_dh.hal", 0, 1, 2},
{"puma_dh.hal", 0, 1, 2},
{"puma_dh", 0, 1, 2},
{"configs/sim/axis/vismach/puma/puma_postgui.hal", 0, 1, 2},
{"puma_postgui.hal", 0, 1, 2},
{"puma_postgui", 0, 1, 2},
{"configs/sim/axis/vismach/puma/remap_subs/428remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/puma/remap_subs", 0, 1, 2},
{"configs/sim/axis/vismach/puma/remap_subs/429remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/puma/remap_subs/430remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2},
{"puma_cube", 0, 1, 2},
{"puma_cube.ini(pumakins)", 0, 1, 2},
{"puma_cube.ini", 0, 1, 2},
{"configs/sim/axis/vismach/puma/puma560.ini", 0, 1, 2},
{"puma560", 0, 1, 2},
{"puma560(switchkins)(inch)", 0, 1, 2},
{"configs/sim/axis/vismach/puma/puma560_dh.hal", 0, 1, 2},
{"puma560_dh.hal", 0, 1, 2},
{"puma560_dh", 0, 1, 2},
{"configs/sim/axis/vismach/puma/puma560_postgui.hal", 0, 1, 2},
{"puma560_postgui.hal", 0, 1, 2},
{"puma560_postgui", 0, 1, 2},
{"configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2},
{"puma560_uvw", 0, 1, 2},
{"configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2},
{"configs/sim/axis/vismach/scara", 0, 1, 2},
{"scara", 0, 1, 2},
{"scara(genserkins,switchkins)", 0, 1, 2},
{"scarakinscoordinates=xyzcab", 0, 1, 2},
{"scarakins", 0, 1, 2},
{"configs/sim/axis/vismach/scara/scara_postgui.hal", 0, 1, 2},
{"scara_postgui.hal", 0, 1, 2},
{"scara_postgui", 0, 1, 2},
{"configs/sim/axis/vismach/scara/remap_subs/428remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/scara/remap_subs", 0, 1, 2},
{"configs/sim/axis/vismach/scara/remap_subs/429remap.ngc", 0, 1, 2},
{"configs/sim/axis/vismach/scara/remap_subs/430remap.ngc", 0, 1, 2},
{"configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2},
{"configs/sim/qtaxis/non-trivial/scara", 0, 1, 2},
{"configs/sim/qtaxis/non-trivial/scara/remap_subs/428remap.ngc", 0, 1, 2},
{"configs/sim/qtaxis/non-trivial/scara/remap_subs", 0, 1, 2},
{"configs/sim/qtaxis/non-trivial/scara/remap_subs/429remap.ngc", 0, 1, 2},
{"configs/sim/qtaxis/non-trivial/scara/remap_subs/430remap.ngc", 0, 1, 2},
{"configs/sim/qtvcp_screens/non-trivial/scara/scara.ini", 0, 1, 2},
{"configs/sim/qtvcp_screens/non-trivial/scara", 0, 1, 2},
{"configs/sim/qtvcp_screens/non-trivial/scara/remap_subs/428remap.ngc", 0, 1, 2},
{"configs/sim/qtvcp_screens/non-trivial/scara/remap_subs", 0, 1, 2},
{"configs/sim/qtvcp_screens/non-trivial/scara/remap_subs/429remap.ngc", 0, 1, 2},
{"configs/sim/qtvcp_screens/non-trivial/scara/remap_subs/430remap.ngc", 0, 1, 2},

View File

@@ -2,6 +2,8 @@
#include "emc/tooldata/tooldata.hh" #include "emc/tooldata/tooldata.hh"
#include <cstdlib>
void cnc_sim_init_minimal_linuxcnc_tooldata() { void cnc_sim_init_minimal_linuxcnc_tooldata() {
static bool created = false; static bool created = false;
if (!created) { if (!created) {
@@ -22,5 +24,29 @@ void cnc_sim_init_minimal_linuxcnc_tooldata() {
tool.diameter = 6.0; tool.diameter = 6.0;
tool.offset.tran.z = 12.5; tool.offset.tran.z = 12.5;
tooldata_put(tool, 1); tooldata_put(tool, 1);
tooldata_last_index_set(1);
CANON_TOOL_TABLE thread_tool = tooldata_entry_init();
thread_tool.toolno = 4;
thread_tool.pocketno = 4;
thread_tool.diameter = 1.0;
thread_tool.offset.tran.z = 0.7;
tooldata_put(thread_tool, 4);
if (const char *spindle_tool = std::getenv("CNC_SIM_TOOL_IN_SPINDLE")) {
const int tool_id = std::atoi(spindle_tool);
if (tool_id == 1) {
CANON_TOOL_TABLE spindle_tooldata = tool;
spindle_tooldata.pocketno = 0;
spindle_tooldata.frontangle = 12.0;
spindle_tooldata.backangle = 34.0;
spindle_tooldata.orientation = 5;
tooldata_put(spindle_tooldata, 0);
} else if (tool_id == 4) {
CANON_TOOL_TABLE spindle_tooldata = thread_tool;
spindle_tooldata.pocketno = 0;
tooldata_put(spindle_tooldata, 0);
}
}
tooldata_last_index_set(4);
} }

View File

@@ -0,0 +1,142 @@
#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_APP_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_KINEMATICS_H
#define __LINUXCNC_EMCPOS_H
#define MODULE_LICENSE(...)
#define EXPORT_SYMBOL(symbol)
typedef double real_t;
typedef double hal_float_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef enum {
KINEMATICS_IDENTITY = 1,
KINEMATICS_FORWARD_ONLY,
KINEMATICS_INVERSE_ONLY,
KINEMATICS_BOTH
} KINEMATICS_TYPE;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#define KINS_NOT_SWITCHABLE \
extern int kinematicsSwitchable() { return 0; } \
extern int kinematicsSwitch(int switchkins_type) { (void)switchkins_type; return 0; }
#ifdef __cplusplus
extern "C" {
#endif
int cnc_sim_tripodkins_hal_init(const char *name) {
(void)name;
return 1;
}
void *cnc_sim_tripodkins_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_tripodkins_hal_ready(int comp_id) {
(void)comp_id;
return 0;
}
void cnc_sim_tripodkins_hal_exit(int comp_id) {
(void)comp_id;
}
int cnc_sim_tripodkins_hal_pin_float_new(const char *name,
hal_pin_dir_t dir,
hal_float_t **data_ptr_addr,
int comp_id) {
(void)name;
(void)dir;
(void)comp_id;
*data_ptr_addr = (hal_float_t *)calloc(1, sizeof(hal_float_t));
return *data_ptr_addr ? 0 : -1;
}
#define hal_init cnc_sim_tripodkins_hal_init
#define hal_malloc(size) ((struct haldata *)cnc_sim_tripodkins_hal_malloc(size))
#define hal_pin_float_new cnc_sim_tripodkins_hal_pin_float_new
#define hal_ready cnc_sim_tripodkins_hal_ready
#define hal_exit cnc_sim_tripodkins_hal_exit
#define kinematicsType cnc_sim_tripodkins_kinematicsType
#define kinematicsForward cnc_sim_tripodkins_kinematicsForward
#define kinematicsInverse cnc_sim_tripodkins_kinematicsInverse
#define kinematicsSwitchable cnc_sim_tripodkins_kinematicsSwitchable
#define kinematicsSwitch cnc_sim_tripodkins_kinematicsSwitch
#define rtapi_app_main cnc_sim_tripodkins_rtapi_app_main
#define rtapi_app_exit cnc_sim_tripodkins_rtapi_app_exit
#define comp_id cnc_sim_tripodkins_comp_id
#define haldata cnc_sim_tripodkins_haldata
#include "../../../linuxcnc/src/emc/kinematics/tripodkins.c"
#undef haldata
#undef comp_id
#undef rtapi_app_exit
#undef rtapi_app_main
#undef kinematicsSwitch
#undef kinematicsSwitchable
#undef kinematicsInverse
#undef kinematicsForward
#undef kinematicsType
#undef hal_exit
#undef hal_ready
#undef hal_pin_float_new
#undef hal_malloc
#undef hal_init
int cnc_sim_linuxcnc_tripodkins_setup(void) {
return cnc_sim_tripodkins_rtapi_app_main();
}
int cnc_sim_linuxcnc_tripodkins_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return cnc_sim_tripodkins_kinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_tripodkins_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return cnc_sim_tripodkins_kinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,94 @@
#include "linuxcnc_tripodkins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_tripodkins_setup(void);
int cnc_sim_linuxcnc_tripodkins_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_tripodkins_inverse(const EmcPose *pos, double *joints);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_tripodkins_setup() == 0;
return initialized;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_tripodkins_source_forward(const LinuxCncTripodJoints &joints, CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
double raw_joints[3];
std::memcpy(raw_joints, joints.strut, sizeof(raw_joints));
EmcPose out{};
if (cnc_sim_linuxcnc_tripodkins_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_tripodkins_source_inverse(const CncSimPose &pose, LinuxCncTripodJoints *joints) {
if (!ensure_initialized()) {
return false;
}
const EmcPose emc_pose = to_emc_pose(pose);
double raw_joints[3] = {};
if (cnc_sim_linuxcnc_tripodkins_inverse(&emc_pose, raw_joints) != 0) {
return false;
}
std::memcpy(joints->strut, raw_joints, sizeof(raw_joints));
return true;
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_tripodkins_source_forward(const LinuxCncTripodJoints &joints, CncSimPose *pose);
bool linuxcnc_tripodkins_source_inverse(const CncSimPose &pose, LinuxCncTripodJoints *joints);

View File

@@ -0,0 +1,163 @@
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_APP_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_STRING_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_EMCMOTCFG_H
#define __LINUXCNC_EMCPOS_H
#define __LINUXCNC_KINEMATICS_H
#define MODULE_LICENSE(...)
#define EXPORT_SYMBOL(symbol)
#define RTAPI_MP_STRING(name, desc)
#define RTAPI_MSG_ERR 1
#define EMCMOT_MAX_JOINTS 16
#define EMCMOT_MAX_AXIS 9
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef enum {
KINEMATICS_IDENTITY = 1,
KINEMATICS_FORWARD_ONLY,
KINEMATICS_INVERSE_ONLY,
KINEMATICS_BOTH
} KINEMATICS_TYPE;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#define KINS_NOT_SWITCHABLE \
extern int kinematicsSwitchable() { return 0; } \
extern int kinematicsSwitch(int switchkins_type) { (void)switchkins_type; return 0; }
#ifdef __cplusplus
extern "C" {
#endif
void cnc_sim_trivkins_rtapi_print(const char *fmt, ...) {
(void)fmt;
}
void cnc_sim_trivkins_rtapi_print_msg(int level, const char *fmt, ...) {
(void)level;
(void)fmt;
}
int cnc_sim_trivkins_hal_init(const char *name) {
(void)name;
return 1;
}
int cnc_sim_trivkins_hal_ready(int comp_id) {
(void)comp_id;
return 0;
}
void cnc_sim_trivkins_hal_exit(int comp_id) {
(void)comp_id;
}
#define rtapi_print cnc_sim_trivkins_rtapi_print
#define rtapi_print_msg cnc_sim_trivkins_rtapi_print_msg
#define kinematicsType cnc_sim_trivkins_kinematicsType
KINEMATICS_TYPE kinematicsType(void);
#define map_coordinates_to_jnumbers cnc_sim_trivkins_map_coordinates_to_jnumbers
#define mapped_joints_to_position cnc_sim_trivkins_mapped_joints_to_position
#define position_to_mapped_joints cnc_sim_trivkins_position_to_mapped_joints
#define identityKinematicsSetup cnc_sim_trivkins_identityKinematicsSetup
#define identityKinematicsForward cnc_sim_trivkins_identityKinematicsForward
#define identityKinematicsInverse cnc_sim_trivkins_identityKinematicsInverse
#include "../../../linuxcnc/src/emc/kinematics/kins_util.c"
#define hal_init cnc_sim_trivkins_hal_init
#define hal_ready cnc_sim_trivkins_hal_ready
#define hal_exit cnc_sim_trivkins_hal_exit
#define kinematicsForward cnc_sim_trivkins_kinematicsForward
#define kinematicsInverse cnc_sim_trivkins_kinematicsInverse
#define kinematicsSwitchable cnc_sim_trivkins_kinematicsSwitchable
#define kinematicsSwitch cnc_sim_trivkins_kinematicsSwitch
#define rtapi_app_main cnc_sim_trivkins_rtapi_app_main
#define rtapi_app_exit cnc_sim_trivkins_rtapi_app_exit
#define comp_id cnc_sim_trivkins_comp_id
#define coordinates cnc_sim_trivkins_coordinates
#define kinstype cnc_sim_trivkins_kinstype
#include "../../../linuxcnc/src/emc/kinematics/trivkins.c"
#undef kinstype
#undef coordinates
#undef comp_id
#undef rtapi_app_exit
#undef rtapi_app_main
#undef kinematicsSwitch
#undef kinematicsSwitchable
#undef kinematicsInverse
#undef kinematicsForward
#undef hal_exit
#undef hal_ready
#undef hal_init
#undef identityKinematicsInverse
#undef identityKinematicsForward
#undef identityKinematicsSetup
#undef position_to_mapped_joints
#undef mapped_joints_to_position
#undef map_coordinates_to_jnumbers
#undef kinematicsType
#undef rtapi_print_msg
#undef rtapi_print
int cnc_sim_linuxcnc_trivkins_setup(void) {
return cnc_sim_trivkins_rtapi_app_main();
}
int cnc_sim_linuxcnc_trivkins_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return cnc_sim_trivkins_kinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_trivkins_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return cnc_sim_trivkins_kinematicsInverse(pos, joints, &iflags, &fflags);
}
int cnc_sim_linuxcnc_trivkins_type(void) {
return (int)cnc_sim_trivkins_kinematicsType();
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,129 @@
#include "linuxcnc_trivkins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_trivkins_setup(void);
int cnc_sim_linuxcnc_trivkins_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_trivkins_inverse(const EmcPose *pos, double *joints);
int cnc_sim_linuxcnc_trivkins_type(void);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_trivkins_setup() == 0;
return initialized;
}
void to_joints(const LinuxCncAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.a;
out[4] = joints.b;
out[5] = joints.c;
out[6] = joints.u;
out[7] = joints.v;
out[8] = joints.w;
}
LinuxCncAxisJoints from_joints(const double joints[16]) {
LinuxCncAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.a = joints[3];
out.b = joints[4];
out.c = joints[5];
out.u = joints[6];
out.v = joints[7];
out.w = joints[8];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_trivkins_source_forward(const LinuxCncAxisJoints &joints, CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
if (cnc_sim_linuxcnc_trivkins_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_trivkins_source_inverse(const CncSimPose &pose, LinuxCncAxisJoints *joints) {
if (!ensure_initialized()) {
return false;
}
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
if (cnc_sim_linuxcnc_trivkins_inverse(&in, raw_joints) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}
int linuxcnc_trivkins_source_kinematics_type() {
if (!ensure_initialized()) {
return 0;
}
return cnc_sim_linuxcnc_trivkins_type();
}

View File

@@ -0,0 +1,7 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_trivkins_source_forward(const LinuxCncAxisJoints &joints, CncSimPose *pose);
bool linuxcnc_trivkins_source_inverse(const CncSimPose &pose, LinuxCncAxisJoints *joints);
int linuxcnc_trivkins_source_kinematics_type();

View File

@@ -0,0 +1,207 @@
#include <ctype.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_STRING_H
#define __LINUXCNC_RTAPI_CTYPE_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_EMCMOTCFG_H
#define __LINUXCNC_KINEMATICS_H
#define RTAPI_MSG_ERR 1
#define PM_PI 3.1415926535897932384626433832795029
#define TO_RAD (PM_PI / 180.0)
#define EMCMOT_MAX_JOINTS 16
#define EMCMOT_MAX_AXIS 9
typedef double real_t;
typedef double hal_float_t;
typedef bool hal_bit_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
static int cnc_sim_axis_idx_for_jno[EMCMOT_MAX_JOINTS];
static int cnc_sim_max_joints;
#ifdef __cplusplus
extern "C" {
#endif
void rtapi_print(const char *fmt, ...) {
(void)fmt;
}
void rtapi_print_msg(int level, const char *fmt, ...) {
(void)level;
(void)fmt;
}
void *cnc_sim_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int hal_pin_float_newf(hal_pin_dir_t dir, hal_float_t **data_ptr_addr, int comp_id, const char *fmt, ...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_float_t *)calloc(1, sizeof(hal_float_t));
return *data_ptr_addr ? 0 : -1;
}
int hal_pin_bit_newf(hal_pin_dir_t dir, hal_bit_t **data_ptr_addr, int comp_id, const char *fmt, ...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_bit_t *)calloc(1, sizeof(hal_bit_t));
return *data_ptr_addr ? 0 : -1;
}
int map_coordinates_to_jnumbers(const char *coordinates,
const int max_joints,
const int allow_duplicates,
int axis_idx_for_jno[]) {
(void)allow_duplicates;
const char *letters = "XYZABCUVW";
int jno = 0;
for (int i = 0; i < EMCMOT_MAX_JOINTS; ++i) {
axis_idx_for_jno[i] = -1;
cnc_sim_axis_idx_for_jno[i] = -1;
}
while (*coordinates) {
const char *found = strchr(letters, toupper((unsigned char)*coordinates));
if (!found || jno >= max_joints) {
return -1;
}
axis_idx_for_jno[jno++] = (int)(found - letters);
++coordinates;
}
cnc_sim_max_joints = jno;
for (int i = 0; i < EMCMOT_MAX_JOINTS; ++i) {
cnc_sim_axis_idx_for_jno[i] = axis_idx_for_jno[i];
}
return 0;
}
int position_to_mapped_joints(const int max_joints, const EmcPose *pos, double *joints) {
for (int jno = 0; jno < max_joints; ++jno) {
switch (cnc_sim_axis_idx_for_jno[jno]) {
case 0: joints[jno] = pos->tran.x; break;
case 1: joints[jno] = pos->tran.y; break;
case 2: joints[jno] = pos->tran.z; break;
case 3: joints[jno] = pos->a; break;
case 4: joints[jno] = pos->b; break;
case 5: joints[jno] = pos->c; break;
case 6: joints[jno] = pos->u; break;
case 7: joints[jno] = pos->v; break;
case 8: joints[jno] = pos->w; break;
default: joints[jno] = 0.0; break;
}
}
return 0;
}
#define hal_malloc(size) ((struct haldata *)cnc_sim_hal_malloc(size))
#include "../../../linuxcnc/src/emc/kinematics/trtfuncs.c"
#undef hal_malloc
void cnc_sim_linuxcnc_trtfuncs_reset_setup_state(void) {
trtfuncs_max_joints = 0;
JX = -1;
JY = -1;
JZ = -1;
JA = -1;
JB = -1;
JC = -1;
JU = -1;
JV = -1;
JW = -1;
haldata = NULL;
cnc_sim_max_joints = 0;
for (int i = 0; i < EMCMOT_MAX_JOINTS; ++i) {
cnc_sim_axis_idx_for_jno[i] = -1;
}
}
int cnc_sim_linuxcnc_trtfuncs_setup_xyzbc(void) {
kparms kp = {0};
static char kinsname[] = "xyzbc-trt-kins";
static char halprefix[] = "xyzbc-trt-kins";
static char required_coordinates[] = "XYZBC";
kp.kinsname = kinsname;
kp.halprefix = halprefix;
kp.required_coordinates = required_coordinates;
kp.max_joints = EMCMOT_MAX_JOINTS;
kp.allow_duplicates = 1;
return trtKinematicsSetup(0, "XYZBC", &kp);
}
int cnc_sim_linuxcnc_trtfuncs_setup_xyzac(void) {
kparms kp = {0};
static char kinsname[] = "xyzac-trt-kins";
static char halprefix[] = "xyzac-trt-kins";
static char required_coordinates[] = "XYZAC";
kp.kinsname = kinsname;
kp.halprefix = halprefix;
kp.required_coordinates = required_coordinates;
kp.max_joints = EMCMOT_MAX_JOINTS;
kp.allow_duplicates = 1;
return trtKinematicsSetup(0, "XYZAC", &kp);
}
void cnc_sim_linuxcnc_trtfuncs_set_pins(double x_rot_point,
double y_rot_point,
double z_rot_point,
double x_offset,
double y_offset,
double z_offset,
double tool_offset,
bool conventional_directions) {
*haldata->x_rot_point = x_rot_point;
*haldata->y_rot_point = y_rot_point;
*haldata->z_rot_point = z_rot_point;
*haldata->x_offset = x_offset;
*haldata->y_offset = y_offset;
*haldata->z_offset = z_offset;
*haldata->tool_offset = tool_offset;
*haldata->conventional_directions = conventional_directions;
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,241 @@
#include "linuxcnc_trtfuncs_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
int cnc_sim_linuxcnc_trtfuncs_setup_xyzbc(void);
int cnc_sim_linuxcnc_trtfuncs_setup_xyzac(void);
void cnc_sim_linuxcnc_trtfuncs_set_pins(double x_rot_point,
double y_rot_point,
double z_rot_point,
double x_offset,
double y_offset,
double z_offset,
double tool_offset,
bool conventional_directions);
void cnc_sim_linuxcnc_trtfuncs_reset_setup_state(void);
int xyzbcKinematicsForward(const double *joints,
EmcPose *pos,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags);
int xyzbcKinematicsInverse(const EmcPose *pos,
double *joints,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags);
int xyzacKinematicsForward(const double *joints,
EmcPose *pos,
const KINEMATICS_FORWARD_FLAGS *fflags,
KINEMATICS_INVERSE_FLAGS *iflags);
int xyzacKinematicsInverse(const EmcPose *pos,
double *joints,
const KINEMATICS_INVERSE_FLAGS *iflags,
KINEMATICS_FORWARD_FLAGS *fflags);
}
namespace {
enum class TrtSetup {
None,
Xyzbc,
Xyzac,
};
TrtSetup current_setup = TrtSetup::None;
bool ensure_xyzbc() {
if (current_setup == TrtSetup::Xyzbc) {
return true;
}
cnc_sim_linuxcnc_trtfuncs_reset_setup_state();
if (cnc_sim_linuxcnc_trtfuncs_setup_xyzbc() != 0) {
current_setup = TrtSetup::None;
return false;
}
current_setup = TrtSetup::Xyzbc;
return true;
}
bool ensure_xyzac() {
if (current_setup == TrtSetup::Xyzac) {
return true;
}
cnc_sim_linuxcnc_trtfuncs_reset_setup_state();
if (cnc_sim_linuxcnc_trtfuncs_setup_xyzac() != 0) {
current_setup = TrtSetup::None;
return false;
}
current_setup = TrtSetup::Xyzac;
return true;
}
void apply_parameters(const LinuxCncXyzbcTrtParameters &parameters) {
cnc_sim_linuxcnc_trtfuncs_set_pins(parameters.x_rot_point,
parameters.y_rot_point,
parameters.z_rot_point,
parameters.x_offset,
parameters.y_offset,
parameters.z_offset,
parameters.tool_offset,
parameters.conventional_directions);
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
void to_xyzbc_joints(const LinuxCncFiveAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.b;
out[4] = joints.c;
}
void to_xyzac_joints(const LinuxCncFiveAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.a;
out[4] = joints.c;
}
LinuxCncFiveAxisJoints from_xyzbc_joints(const double joints[16]) {
LinuxCncFiveAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.b = joints[3];
out.c = joints[4];
return out;
}
LinuxCncFiveAxisJoints from_xyzac_joints(const double joints[16]) {
LinuxCncFiveAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.a = joints[3];
out.c = joints[4];
return out;
}
} // namespace
bool linuxcnc_trtfuncs_xyzbc_forward(const LinuxCncFiveAxisJoints &joints,
const LinuxCncXyzbcTrtParameters &parameters,
CncSimPose *pose) {
if (!ensure_xyzbc()) {
return false;
}
apply_parameters(parameters);
double raw_joints[16];
to_xyzbc_joints(joints, raw_joints);
EmcPose out{};
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
if (xyzbcKinematicsForward(raw_joints, &out, &fflags, &iflags) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_trtfuncs_xyzbc_inverse(const CncSimPose &pose,
const LinuxCncXyzbcTrtParameters &parameters,
LinuxCncFiveAxisJoints *joints) {
if (!ensure_xyzbc()) {
return false;
}
apply_parameters(parameters);
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
if (xyzbcKinematicsInverse(&in, raw_joints, &iflags, &fflags) != 0) {
return false;
}
*joints = from_xyzbc_joints(raw_joints);
return true;
}
bool linuxcnc_trtfuncs_xyzac_forward(const LinuxCncFiveAxisJoints &joints,
const LinuxCncXyzbcTrtParameters &parameters,
CncSimPose *pose) {
if (!ensure_xyzac()) {
return false;
}
apply_parameters(parameters);
double raw_joints[16];
to_xyzac_joints(joints, raw_joints);
EmcPose out{};
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
if (xyzacKinematicsForward(raw_joints, &out, &fflags, &iflags) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_trtfuncs_xyzac_inverse(const CncSimPose &pose,
const LinuxCncXyzbcTrtParameters &parameters,
LinuxCncFiveAxisJoints *joints) {
if (!ensure_xyzac()) {
return false;
}
apply_parameters(parameters);
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
if (xyzacKinematicsInverse(&in, raw_joints, &iflags, &fflags) != 0) {
return false;
}
*joints = from_xyzac_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_trtfuncs_xyzbc_forward(const LinuxCncFiveAxisJoints &joints,
const LinuxCncXyzbcTrtParameters &parameters,
CncSimPose *pose);
bool linuxcnc_trtfuncs_xyzbc_inverse(const CncSimPose &pose,
const LinuxCncXyzbcTrtParameters &parameters,
LinuxCncFiveAxisJoints *joints);
bool linuxcnc_trtfuncs_xyzac_forward(const LinuxCncFiveAxisJoints &joints,
const LinuxCncXyzbcTrtParameters &parameters,
CncSimPose *pose);
bool linuxcnc_trtfuncs_xyzac_inverse(const CncSimPose &pose,
const LinuxCncXyzbcTrtParameters &parameters,
LinuxCncFiveAxisJoints *joints);

View File

@@ -0,0 +1,154 @@
#include <ctype.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_STRING_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_EMCMOTCFG_H
#define __LINUXCNC_EMCPOS_H
#define __LINUXCNC_KINEMATICS_H
#define RTAPI_MSG_ERR 1
#define EMCMOT_MAX_JOINTS 16
#define EMCMOT_MAX_AXIS 9
typedef double hal_float_t;
typedef int hal_s32_t;
typedef bool hal_bit_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef enum {
KINEMATICS_IDENTITY = 1,
KINEMATICS_FORWARD_ONLY,
KINEMATICS_INVERSE_ONLY,
KINEMATICS_BOTH
} KINEMATICS_TYPE;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#ifdef __cplusplus
extern "C" {
#endif
void cnc_sim_userk_rtapi_print(const char *fmt, ...) {
(void)fmt;
}
void cnc_sim_userk_rtapi_print_msg(int level, const char *fmt, ...) {
(void)level;
(void)fmt;
}
void *cnc_sim_userk_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_userk_hal_pin_s32_new(const char *name,
hal_pin_dir_t dir,
hal_s32_t **data_ptr_addr,
int comp_id) {
(void)name;
(void)dir;
(void)comp_id;
*data_ptr_addr = (hal_s32_t *)calloc(1, sizeof(hal_s32_t));
return *data_ptr_addr ? 0 : -1;
}
KINEMATICS_TYPE cnc_sim_userk_kinematicsType(void) {
return KINEMATICS_BOTH;
}
#define rtapi_print cnc_sim_userk_rtapi_print
#define rtapi_print_msg cnc_sim_userk_rtapi_print_msg
#define kinematicsType cnc_sim_userk_kinematicsType
#define hal_pin_s32_new cnc_sim_userk_hal_pin_s32_new
#define map_coordinates_to_jnumbers cnc_sim_userk_map_coordinates_to_jnumbers
#define mapped_joints_to_position cnc_sim_userk_mapped_joints_to_position
#define position_to_mapped_joints cnc_sim_userk_position_to_mapped_joints
#define identityKinematicsSetup cnc_sim_userk_identityKinematicsSetup
#define identityKinematicsForward cnc_sim_userk_identityKinematicsForward
#define identityKinematicsInverse cnc_sim_userk_identityKinematicsInverse
#define userkKinematicsSetup cnc_sim_userk_userkKinematicsSetup
#define userkKinematicsForward cnc_sim_userk_userkKinematicsForward
#define userkKinematicsInverse cnc_sim_userk_userkKinematicsInverse
#include "../../../linuxcnc/src/emc/kinematics/kins_util.c"
#define hal_malloc(size) ((struct udata *)cnc_sim_userk_hal_malloc(size))
#include "../../../linuxcnc/src/emc/kinematics/userkfuncs.c"
#undef hal_malloc
#undef userkKinematicsInverse
#undef userkKinematicsForward
#undef userkKinematicsSetup
#undef identityKinematicsInverse
#undef identityKinematicsForward
#undef identityKinematicsSetup
#undef position_to_mapped_joints
#undef mapped_joints_to_position
#undef map_coordinates_to_jnumbers
#undef hal_pin_s32_new
#undef kinematicsType
#undef rtapi_print_msg
#undef rtapi_print
int cnc_sim_linuxcnc_userkfuncs_setup(void) {
kparms kp = {0};
static char kinsname[] = "userkfuncs";
static char halprefix[] = "userk";
kp.kinsname = kinsname;
kp.halprefix = halprefix;
kp.max_joints = EMCMOT_MAX_JOINTS;
kp.allow_duplicates = 1;
if (cnc_sim_userk_identityKinematicsSetup(0, "XYZBC", &kp) != 0) {
return -1;
}
return cnc_sim_userk_userkKinematicsSetup(0, "XYZBC", &kp);
}
int cnc_sim_linuxcnc_userkfuncs_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return cnc_sim_userk_userkKinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_userkfuncs_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return cnc_sim_userk_userkKinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,113 @@
#include "linuxcnc_userkfuncs_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_userkfuncs_setup(void);
int cnc_sim_linuxcnc_userkfuncs_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_userkfuncs_inverse(const EmcPose *pos, double *joints);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_userkfuncs_setup() == 0;
return initialized;
}
void to_joints(const LinuxCncFiveAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.b;
out[4] = joints.c;
}
LinuxCncFiveAxisJoints from_joints(const double joints[16]) {
LinuxCncFiveAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.b = joints[3];
out.c = joints[4];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_userkfuncs_forward(const LinuxCncFiveAxisJoints &joints, CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
if (cnc_sim_linuxcnc_userkfuncs_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_userkfuncs_inverse(const CncSimPose &pose, LinuxCncFiveAxisJoints *joints) {
if (!ensure_initialized()) {
return false;
}
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
if (cnc_sim_linuxcnc_userkfuncs_inverse(&in, raw_joints) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_userkfuncs_forward(const LinuxCncFiveAxisJoints &joints, CncSimPose *pose);
bool linuxcnc_userkfuncs_inverse(const CncSimPose &pose, LinuxCncFiveAxisJoints *joints);

View File

@@ -0,0 +1,241 @@
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define __LINUXCNC_RTAPI_H
#define __LINUXCNC_RTAPI_APP_H
#define __LINUXCNC_RTAPI_STRING_H
#define __LINUXCNC_RTAPI_ERRNO_H
#define __LINUXCNC_RTAPI_MATH_H
#define __LINUXCNC_RTAPI_MATH64_H
#define __LINUXCNC_HAL_H
#define __LINUXCNC_KINEMATICS_H
#define MODULE_INFO(...)
#define MODULE_LICENSE(...)
#define EXPORT_SYMBOL(symbol)
#define RTAPI_MP_INT(name, description)
#define RTAPI_MP_STRING(name, description)
#define RTAPI_MSG_ERR 1
#define RTAPI_MSG_INFO 2
#define HAL_NAME_LEN 47
#define EINVAL 22
#define PM_PI 3.1415926535897932384626433832795029
#define TO_RAD (PM_PI / 180.0)
typedef double real_t;
typedef double hal_float_t;
typedef int hal_s32_t;
typedef unsigned int hal_u32_t;
typedef bool hal_bit_t;
typedef enum { HAL_IN = 16, HAL_OUT = 32, HAL_IO = (HAL_IN | HAL_OUT) } hal_pin_dir_t;
typedef struct {
double x;
double y;
double z;
} PmCartesian;
typedef struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
} EmcPose;
typedef enum {
KINEMATICS_IDENTITY = 1,
KINEMATICS_FORWARD_ONLY,
KINEMATICS_INVERSE_ONLY,
KINEMATICS_BOTH
} KINEMATICS_TYPE;
typedef unsigned long int KINEMATICS_FORWARD_FLAGS;
typedef unsigned long int KINEMATICS_INVERSE_FLAGS;
typedef struct kinematics_parms {
char *sparm;
char *kinsname;
char *halprefix;
char *required_coordinates;
int max_joints;
int allow_duplicates;
int fwd_iterates_mask;
int gui_kinstype;
} kparms;
#ifdef __cplusplus
extern "C" {
#endif
int __attribute__((weak)) rtapi_snprintf(char *str, unsigned long int size, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
const int result = vsnprintf(str, (size_t)size, fmt, args);
va_end(args);
return result;
}
void cnc_sim_xyzab_tdr_rtapi_print(const char *fmt, ...) {
(void)fmt;
}
void cnc_sim_xyzab_tdr_rtapi_print_msg(int level, const char *fmt, ...) {
(void)level;
(void)fmt;
}
void *cnc_sim_xyzab_tdr_hal_malloc(long int size) {
return calloc(1, (size_t)size);
}
int cnc_sim_xyzab_tdr_hal_init(const char *name) {
(void)name;
return 1;
}
void cnc_sim_xyzab_tdr_hal_exit(int comp_id) {
(void)comp_id;
}
int cnc_sim_xyzab_tdr_hal_ready(int comp_id) {
(void)comp_id;
return 0;
}
int cnc_sim_xyzab_tdr_hal_set_unready(int comp_id) {
(void)comp_id;
return 0;
}
int cnc_sim_xyzab_tdr_hal_pin_s32_newf(hal_pin_dir_t dir,
hal_s32_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_s32_t *)calloc(1, sizeof(hal_s32_t));
return *data_ptr_addr ? 0 : -1;
}
int cnc_sim_xyzab_tdr_hal_pin_float_newf(hal_pin_dir_t dir,
hal_float_t **data_ptr_addr,
int comp_id,
const char *fmt,
...) {
(void)dir;
(void)comp_id;
(void)fmt;
*data_ptr_addr = (hal_float_t *)calloc(1, sizeof(hal_float_t));
return *data_ptr_addr ? 0 : -1;
}
int cnc_sim_xyzab_tdr_hal_pin_bit_new(const char *name,
hal_pin_dir_t dir,
hal_bit_t **data_ptr_addr,
int comp_id) {
(void)name;
(void)dir;
(void)comp_id;
*data_ptr_addr = (hal_bit_t *)calloc(1, sizeof(hal_bit_t));
return *data_ptr_addr ? 0 : -1;
}
#define rtapi_print cnc_sim_xyzab_tdr_rtapi_print
#define rtapi_print_msg cnc_sim_xyzab_tdr_rtapi_print_msg
#define hal_malloc(size) cnc_sim_xyzab_tdr_hal_malloc(size)
#define hal_init cnc_sim_xyzab_tdr_hal_init
#define hal_exit cnc_sim_xyzab_tdr_hal_exit
#define hal_ready cnc_sim_xyzab_tdr_hal_ready
#define hal_set_unready cnc_sim_xyzab_tdr_hal_set_unready
#define hal_pin_s32_newf cnc_sim_xyzab_tdr_hal_pin_s32_newf
#define hal_pin_float_newf cnc_sim_xyzab_tdr_hal_pin_float_newf
#define hal_pin_bit_new cnc_sim_xyzab_tdr_hal_pin_bit_new
#define kinematicsType cnc_sim_xyzab_tdr_kinematicsType
#define kinematicsSwitchable cnc_sim_xyzab_tdr_kinematicsSwitchable
#define kinematicsSwitch cnc_sim_xyzab_tdr_kinematicsSwitch
#define kinematicsForward cnc_sim_xyzab_tdr_kinematicsForward
#define kinematicsInverse cnc_sim_xyzab_tdr_kinematicsInverse
#define rtapi_app_main cnc_sim_xyzab_tdr_rtapi_app_main
#define rtapi_app_exit cnc_sim_xyzab_tdr_rtapi_app_exit
#define export cnc_sim_xyzab_tdr_export
#include "../../../linuxcnc/src/objects/hal/components/xyzab_tdr_kins.c"
#undef export
#undef rtapi_app_exit
#undef rtapi_app_main
#undef kinematicsInverse
#undef kinematicsForward
#undef kinematicsSwitch
#undef kinematicsSwitchable
#undef kinematicsType
#undef hal_pin_bit_new
#undef hal_pin_float_newf
#undef hal_pin_s32_newf
#undef hal_set_unready
#undef hal_ready
#undef hal_exit
#undef hal_init
#undef hal_malloc
#undef rtapi_print_msg
#undef rtapi_print
int cnc_sim_linuxcnc_xyzab_tdr_kins_setup(void) {
return cnc_sim_xyzab_tdr_kinematicsType() == KINEMATICS_BOTH ? 0 : -1;
}
void cnc_sim_linuxcnc_xyzab_tdr_kins_set_parameters(double tool_offset_z,
double x_offset,
double z_offset,
double x_rot_point,
double y_rot_point,
double z_rot_point) {
*haldata->tool_offset_z = tool_offset_z;
*haldata->x_offset = x_offset;
*haldata->z_offset = z_offset;
*haldata->x_rot_point = x_rot_point;
*haldata->y_rot_point = y_rot_point;
*haldata->z_rot_point = z_rot_point;
}
int cnc_sim_linuxcnc_xyzab_tdr_kins_switch(int switchkins_type) {
return cnc_sim_xyzab_tdr_kinematicsSwitch(switchkins_type);
}
int cnc_sim_linuxcnc_xyzab_tdr_kins_switch_state(int *kinstype_is_0, int *kinstype_is_1) {
if (!haldata || !haldata->kinstype_is_0 || !haldata->kinstype_is_1) {
return -1;
}
if (kinstype_is_0) {
*kinstype_is_0 = *haldata->kinstype_is_0 ? 1 : 0;
}
if (kinstype_is_1) {
*kinstype_is_1 = *haldata->kinstype_is_1 ? 1 : 0;
}
return 0;
}
int cnc_sim_linuxcnc_xyzab_tdr_kins_forward(const double *joints, EmcPose *pos) {
KINEMATICS_FORWARD_FLAGS fflags = 0;
KINEMATICS_INVERSE_FLAGS iflags = 0;
return cnc_sim_xyzab_tdr_kinematicsForward(joints, pos, &fflags, &iflags);
}
int cnc_sim_linuxcnc_xyzab_tdr_kins_inverse(const EmcPose *pos, double *joints) {
KINEMATICS_INVERSE_FLAGS iflags = 0;
KINEMATICS_FORWARD_FLAGS fflags = 0;
return cnc_sim_xyzab_tdr_kinematicsInverse(pos, joints, &iflags, &fflags);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,177 @@
#include "linuxcnc_xyzab_tdr_kins_adapter.h"
#include <cstring>
extern "C" {
struct PmCartesian {
double x;
double y;
double z;
};
struct EmcPose {
PmCartesian tran;
double a;
double b;
double c;
double u;
double v;
double w;
};
int cnc_sim_linuxcnc_xyzab_tdr_kins_setup(void);
void cnc_sim_linuxcnc_xyzab_tdr_kins_set_parameters(double tool_offset_z,
double x_offset,
double z_offset,
double x_rot_point,
double y_rot_point,
double z_rot_point);
int cnc_sim_linuxcnc_xyzab_tdr_kins_switch(int switchkins_type);
int cnc_sim_linuxcnc_xyzab_tdr_kins_switch_state(int *kinstype_is_0, int *kinstype_is_1);
int cnc_sim_linuxcnc_xyzab_tdr_kins_forward(const double *joints, EmcPose *pos);
int cnc_sim_linuxcnc_xyzab_tdr_kins_inverse(const EmcPose *pos, double *joints);
}
namespace {
bool initialized = false;
bool ensure_initialized() {
if (initialized) {
return true;
}
initialized = cnc_sim_linuxcnc_xyzab_tdr_kins_setup() == 0;
return initialized;
}
void apply_parameters(const LinuxCncXyzabTdrParameters &parameters) {
cnc_sim_linuxcnc_xyzab_tdr_kins_set_parameters(parameters.tool_offset_z,
parameters.x_offset,
parameters.z_offset,
parameters.x_rot_point,
parameters.y_rot_point,
parameters.z_rot_point);
}
void to_joints(const LinuxCncAxisJoints &joints, double out[16]) {
std::memset(out, 0, sizeof(double) * 16);
out[0] = joints.x;
out[1] = joints.y;
out[2] = joints.z;
out[3] = joints.a;
out[4] = joints.b;
out[5] = joints.c;
out[6] = joints.u;
out[7] = joints.v;
out[8] = joints.w;
}
LinuxCncAxisJoints from_joints(const double joints[16]) {
LinuxCncAxisJoints out{};
out.x = joints[0];
out.y = joints[1];
out.z = joints[2];
out.a = joints[3];
out.b = joints[4];
out.c = joints[5];
out.u = joints[6];
out.v = joints[7];
out.w = joints[8];
return out;
}
EmcPose to_emc_pose(const CncSimPose &pose) {
EmcPose emc{};
emc.tran.x = pose.x;
emc.tran.y = pose.y;
emc.tran.z = pose.z;
emc.a = pose.a;
emc.b = pose.b;
emc.c = pose.c;
emc.u = pose.u;
emc.v = pose.v;
emc.w = pose.w;
return emc;
}
CncSimPose from_emc_pose(const EmcPose &emc) {
CncSimPose pose{};
pose.x = emc.tran.x;
pose.y = emc.tran.y;
pose.z = emc.tran.z;
pose.a = emc.a;
pose.b = emc.b;
pose.c = emc.c;
pose.u = emc.u;
pose.v = emc.v;
pose.w = emc.w;
return pose;
}
} // namespace
bool linuxcnc_xyzab_tdr_kins_switch(int switchkins_type) {
if (!ensure_initialized()) {
return false;
}
return cnc_sim_linuxcnc_xyzab_tdr_kins_switch(switchkins_type) == 0;
}
bool linuxcnc_xyzab_tdr_kins_switch_state(bool *kinstype_is_0, bool *kinstype_is_1) {
if (!ensure_initialized()) {
return false;
}
int raw_kinstype_is_0 = 0;
int raw_kinstype_is_1 = 0;
if (cnc_sim_linuxcnc_xyzab_tdr_kins_switch_state(&raw_kinstype_is_0, &raw_kinstype_is_1) != 0) {
return false;
}
if (kinstype_is_0) {
*kinstype_is_0 = raw_kinstype_is_0 != 0;
}
if (kinstype_is_1) {
*kinstype_is_1 = raw_kinstype_is_1 != 0;
}
return true;
}
bool linuxcnc_xyzab_tdr_kins_forward(const LinuxCncAxisJoints &joints,
const LinuxCncXyzabTdrParameters &parameters,
int switchkins_type,
CncSimPose *pose) {
if (!ensure_initialized()) {
return false;
}
apply_parameters(parameters);
if (!linuxcnc_xyzab_tdr_kins_switch(switchkins_type)) {
return false;
}
double raw_joints[16];
to_joints(joints, raw_joints);
EmcPose out{};
if (cnc_sim_linuxcnc_xyzab_tdr_kins_forward(raw_joints, &out) != 0) {
return false;
}
*pose = from_emc_pose(out);
return true;
}
bool linuxcnc_xyzab_tdr_kins_inverse(const CncSimPose &pose,
const LinuxCncXyzabTdrParameters &parameters,
int switchkins_type,
LinuxCncAxisJoints *joints) {
if (!ensure_initialized()) {
return false;
}
apply_parameters(parameters);
if (!linuxcnc_xyzab_tdr_kins_switch(switchkins_type)) {
return false;
}
const EmcPose in = to_emc_pose(pose);
double raw_joints[16] = {};
if (cnc_sim_linuxcnc_xyzab_tdr_kins_inverse(&in, raw_joints) != 0) {
return false;
}
*joints = from_joints(raw_joints);
return true;
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include "rtcp_kinematics.h"
bool linuxcnc_xyzab_tdr_kins_switch(int switchkins_type);
bool linuxcnc_xyzab_tdr_kins_switch_state(bool *kinstype_is_0, bool *kinstype_is_1);
bool linuxcnc_xyzab_tdr_kins_forward(const LinuxCncAxisJoints &joints,
const LinuxCncXyzabTdrParameters &parameters,
int switchkins_type,
CncSimPose *pose);
bool linuxcnc_xyzab_tdr_kins_inverse(const CncSimPose &pose,
const LinuxCncXyzabTdrParameters &parameters,
int switchkins_type,
LinuxCncAxisJoints *joints);

View File

@@ -1,72 +1,497 @@
#include "rtcp_kinematics.h" #include "rtcp_kinematics.h"
#include <cmath> #include "linuxcnc_5axiskins_adapter.h"
#include "linuxcnc_corexykins_adapter.h"
#include "linuxcnc_genhexkins_adapter.h"
#include "linuxcnc_genserfuncs_adapter.h"
#include "linuxcnc_kins_util_adapter.h"
#include "linuxcnc_lineardeltakins_adapter.h"
#include "linuxcnc_maxkins_adapter.h"
#include "linuxcnc_pentakins_adapter.h"
#include "linuxcnc_pumakins_adapter.h"
#include "linuxcnc_rotatekins_adapter.h"
#include "linuxcnc_rosekins_adapter.h"
#include "linuxcnc_rotarydeltakins_adapter.h"
#include "linuxcnc_scarakins_adapter.h"
#include "linuxcnc_scorbot_kins_adapter.h"
#include "linuxcnc_tripodkins_adapter.h"
#include "linuxcnc_trtfuncs_adapter.h"
#include "linuxcnc_userkfuncs_adapter.h"
#include "linuxcnc_xyzab_tdr_kins_adapter.h"
namespace { LinuxCncAxisJoints linuxcnc_identity_default_joints() {
return {};
constexpr double kPi = 3.141592653589793238462643383279502884;
double radians(double degrees) {
return degrees * kPi / 180.0;
} }
RtcpVector rotate_x(RtcpVector vector, double angle) { // Source: linuxcnc/src/emc/kinematics/kins_util.c
const double c = std::cos(angle); // Mirrors identityKinematicsForward() with default XYZABCUVW coordinate mapping.
const double s = std::sin(angle); CncSimPose linuxcnc_identity_forward(const LinuxCncAxisJoints &joints) {
return { CncSimPose pose{};
vector.x, linuxcnc_kins_util_identity_forward(joints, &pose);
vector.y * c - vector.z * s, return pose;
vector.y * s + vector.z * c,
};
} }
RtcpVector rotate_y(RtcpVector vector, double angle) { // Source: linuxcnc/src/emc/kinematics/kins_util.c
const double c = std::cos(angle); // Mirrors identityKinematicsInverse() with default XYZABCUVW coordinate mapping.
const double s = std::sin(angle); LinuxCncAxisJoints linuxcnc_identity_inverse(const CncSimPose &pose) {
return { LinuxCncAxisJoints joints{};
vector.x * c + vector.z * s, linuxcnc_kins_util_identity_inverse(pose, &joints);
vector.y, return joints;
-vector.x * s + vector.z * c,
};
} }
RtcpVector rotate_z(RtcpVector vector, double angle) { // Source: linuxcnc/src/emc/kinematics/trivkins.c
const double c = std::cos(angle); // kinematicsForward()/kinematicsInverse() delegate to identityKinematics*().
const double s = std::sin(angle);
return { // Source: linuxcnc/src/emc/kinematics/corexykins.c
vector.x * c - vector.y * s, // Mirrors kinematicsForward().
vector.x * s + vector.y * c, CncSimPose linuxcnc_corexykins_forward(const LinuxCncAxisJoints &joints) {
vector.z, CncSimPose pose{};
}; linuxcnc_corexykins_source_forward(joints, &pose);
return pose;
} }
} // namespace // Source: linuxcnc/src/emc/kinematics/corexykins.c
// Mirrors kinematicsInverse().
RtcpVector rtcp_rotate_abc_degrees(RtcpVector vector, double a_deg, double b_deg, double c_deg) { LinuxCncAxisJoints linuxcnc_corexykins_inverse(const CncSimPose &pose) {
vector = rotate_x(vector, radians(a_deg)); LinuxCncAxisJoints joints{};
vector = rotate_y(vector, radians(b_deg)); linuxcnc_corexykins_source_inverse(pose, &joints);
vector = rotate_z(vector, radians(c_deg)); return joints;
return vector;
} }
RtcpVector rtcp_tool_vector_from_pose(const CncSimPose &pose, double tool_length) { // Source: linuxcnc/src/emc/kinematics/rotatekins.c
return rtcp_rotate_abc_degrees({0.0, 0.0, -tool_length}, pose.a, pose.b, pose.c); // Mirrors kinematicsForward().
CncSimPose linuxcnc_rotatekins_forward(const LinuxCncAxisJoints &joints) {
CncSimPose pose{};
linuxcnc_rotatekins_source_forward(joints, &pose);
return pose;
} }
CncSimPose rtcp_pivot_from_tool_tip(const CncSimPose &tool_tip, double tool_length) { // Source: linuxcnc/src/emc/kinematics/rotatekins.c
const RtcpVector tool = rtcp_tool_vector_from_pose(tool_tip, tool_length); // Mirrors kinematicsInverse().
CncSimPose pivot = tool_tip; LinuxCncAxisJoints linuxcnc_rotatekins_inverse(const CncSimPose &pose) {
pivot.x = tool_tip.x - tool.x; LinuxCncAxisJoints joints{};
pivot.y = tool_tip.y - tool.y; linuxcnc_rotatekins_source_inverse(pose, &joints);
pivot.z = tool_tip.z - tool.z; return joints;
return pivot;
} }
CncSimPose rtcp_tool_tip_from_pivot(const CncSimPose &pivot, double tool_length) { // Source: linuxcnc/src/emc/kinematics/genhexkins.c
const RtcpVector tool = rtcp_tool_vector_from_pose(pivot, tool_length); // Mirrors genhexKinematicsInverse().
CncSimPose tool_tip = pivot; LinuxCncGenhexJoints linuxcnc_genhex_inverse(const CncSimPose &pose) {
tool_tip.x = pivot.x + tool.x; LinuxCncGenhexJoints joints{};
tool_tip.y = pivot.y + tool.y; linuxcnc_genhexkins_source_inverse(pose, &joints);
tool_tip.z = pivot.z + tool.z; return joints;
return tool_tip; }
// Source: linuxcnc/src/emc/kinematics/genhexkins.c
// Mirrors genhexKinematicsForward().
CncSimPose linuxcnc_genhex_forward(const LinuxCncGenhexJoints &joints, const CncSimPose &initial_pose) {
CncSimPose pose{};
linuxcnc_genhexkins_source_forward(joints, initial_pose, &pose);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/pentakins.c
// Mirrors kinematicsInverse().
LinuxCncPentakinsJoints linuxcnc_pentakins_inverse(const CncSimPose &pose) {
LinuxCncPentakinsJoints joints{};
linuxcnc_pentakins_source_inverse(pose, &joints);
return joints;
}
// Source: linuxcnc/src/emc/kinematics/pentakins.c
// Mirrors kinematicsForward().
CncSimPose linuxcnc_pentakins_forward(const LinuxCncPentakinsJoints &joints,
const CncSimPose &initial_pose) {
CncSimPose pose{};
linuxcnc_pentakins_source_forward(joints, initial_pose, &pose);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/tripodkins.c
// Mirrors kinematicsInverse().
LinuxCncTripodJoints linuxcnc_tripodkins_inverse(const CncSimPose &pose) {
LinuxCncTripodJoints joints{};
linuxcnc_tripodkins_source_inverse(pose, &joints);
return joints;
}
// Source: linuxcnc/src/emc/kinematics/tripodkins.c
// Mirrors kinematicsForward() with the default positive-Z forward flag.
CncSimPose linuxcnc_tripodkins_forward(const LinuxCncTripodJoints &joints) {
CncSimPose pose{};
linuxcnc_tripodkins_source_forward(joints, &pose);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/scorbot-kins.c
// Mirrors kinematicsInverse().
LinuxCncScorbotJoints linuxcnc_scorbot_kins_inverse(const CncSimPose &pose) {
LinuxCncScorbotJoints joints{};
linuxcnc_scorbot_kins_source_inverse(pose, &joints);
return joints;
}
// Source: linuxcnc/src/emc/kinematics/scorbot-kins.c
// Mirrors kinematicsForward().
CncSimPose linuxcnc_scorbot_kins_forward(const LinuxCncScorbotJoints &joints) {
CncSimPose pose{};
linuxcnc_scorbot_kins_source_forward(joints, &pose);
return pose;
}
// Source defaults: linuxcnc/src/emc/kinematics/lineardeltakins-common.h
LinuxCncLinearDeltaParameters linuxcnc_lineardelta_default_parameters() {
LinuxCncLinearDeltaParameters parameters{};
linuxcnc_lineardeltakins_source_default_parameters(&parameters);
return parameters;
}
// Source: linuxcnc/src/emc/kinematics/lineardeltakins.c
// Mirrors kinematicsForward().
CncSimPose linuxcnc_lineardelta_forward(const LinuxCncAxisJoints &joints,
const LinuxCncLinearDeltaParameters &parameters) {
CncSimPose pose{};
linuxcnc_lineardeltakins_source_forward(joints, parameters, &pose);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/lineardeltakins.c
// Mirrors kinematicsInverse().
LinuxCncAxisJoints linuxcnc_lineardelta_inverse(const CncSimPose &pose,
const LinuxCncLinearDeltaParameters &parameters) {
LinuxCncAxisJoints joints{};
linuxcnc_lineardeltakins_source_inverse(pose, parameters, &joints);
return joints;
}
// Source defaults: linuxcnc/src/emc/kinematics/rotarydeltakins-common.h
LinuxCncRotaryDeltaParameters linuxcnc_rotarydelta_default_parameters() {
LinuxCncRotaryDeltaParameters parameters{};
linuxcnc_rotarydeltakins_source_default_parameters(&parameters);
return parameters;
}
// Source: linuxcnc/src/emc/kinematics/rotarydeltakins.c
// Mirrors kinematicsForward().
CncSimPose linuxcnc_rotarydelta_forward(const LinuxCncAxisJoints &joints,
const LinuxCncRotaryDeltaParameters &parameters) {
CncSimPose pose{};
linuxcnc_rotarydeltakins_source_forward(joints, parameters, &pose);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/rotarydeltakins.c
// Mirrors kinematicsInverse().
LinuxCncAxisJoints linuxcnc_rotarydelta_inverse(const CncSimPose &pose,
const LinuxCncRotaryDeltaParameters &parameters) {
LinuxCncAxisJoints joints{};
linuxcnc_rotarydeltakins_source_inverse(pose, parameters, &joints);
return joints;
}
// Source defaults: linuxcnc/src/emc/kinematics/maxkins.c
LinuxCncMaxkinsParameters linuxcnc_maxkins_default_parameters() {
LinuxCncMaxkinsParameters parameters{};
linuxcnc_maxkins_source_default_parameters(&parameters);
return parameters;
}
// Source: linuxcnc/src/emc/kinematics/maxkins.c
// Mirrors kinematicsForward().
CncSimPose linuxcnc_maxkins_forward(const LinuxCncAxisJoints &joints,
const LinuxCncMaxkinsParameters &parameters) {
CncSimPose pose{};
linuxcnc_maxkins_source_forward(joints, parameters, &pose);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/maxkins.c
// Mirrors kinematicsInverse().
LinuxCncAxisJoints linuxcnc_maxkins_inverse(const CncSimPose &pose,
const LinuxCncMaxkinsParameters &parameters) {
LinuxCncAxisJoints joints{};
linuxcnc_maxkins_source_inverse(pose, parameters, &joints);
return joints;
}
// Source: linuxcnc/src/emc/kinematics/rosekins.c
// Mirrors kinematicsForward().
CncSimPose linuxcnc_rosekins_forward(const LinuxCncAxisJoints &joints) {
CncSimPose pose{};
linuxcnc_rosekins_source_forward(joints, &pose);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/rosekins.c
// Mirrors kinematicsInverse().
LinuxCncAxisJoints linuxcnc_rosekins_inverse(const CncSimPose &pose) {
LinuxCncAxisJoints joints{};
linuxcnc_rosekins_source_inverse(pose, &joints);
return joints;
}
// Source: linuxcnc/src/emc/kinematics/5axiskins.c
// Mirrors s2r(), fiveaxis_KinematicsForward().
CncSimPose linuxcnc_5axis_forward(const LinuxCncFiveAxisJoints &joints, double pivot_length) {
CncSimPose pose{};
linuxcnc_5axiskins_forward(joints, pivot_length, &pose);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/5axiskins.c
// Mirrors s2r(), fiveaxis_KinematicsInverse().
LinuxCncFiveAxisJoints linuxcnc_5axis_inverse(const CncSimPose &pose, double pivot_length) {
LinuxCncFiveAxisJoints joints{};
linuxcnc_5axiskins_inverse(pose, pivot_length, &joints);
return joints;
}
// Source defaults: linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini
LinuxCncXyzbcTrtParameters linuxcnc_xyzbc_trt_default_parameters(double tool_offset) {
LinuxCncXyzbcTrtParameters parameters{};
parameters.x_offset = -20.0;
parameters.y_offset = 0.0;
parameters.z_offset = -15.0;
parameters.tool_offset = tool_offset;
parameters.conventional_directions = false;
return parameters;
}
// Source: linuxcnc/src/emc/kinematics/trtfuncs.c
// Mirrors xyzbcKinematicsForward().
CncSimPose linuxcnc_xyzbc_trt_forward(const LinuxCncFiveAxisJoints &joints,
const LinuxCncXyzbcTrtParameters &parameters) {
CncSimPose pose{};
linuxcnc_trtfuncs_xyzbc_forward(joints, parameters, &pose);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/trtfuncs.c
// Mirrors xyzbcKinematicsInverse().
LinuxCncFiveAxisJoints linuxcnc_xyzbc_trt_inverse(const CncSimPose &pose,
const LinuxCncXyzbcTrtParameters &parameters) {
LinuxCncFiveAxisJoints joints{};
linuxcnc_trtfuncs_xyzbc_inverse(pose, parameters, &joints);
return joints;
}
// Source: linuxcnc/src/emc/kinematics/trtfuncs.c
// Mirrors xyzacKinematicsForward().
CncSimPose linuxcnc_xyzac_trt_forward(const LinuxCncFiveAxisJoints &joints,
const LinuxCncXyzbcTrtParameters &parameters) {
CncSimPose pose{};
linuxcnc_trtfuncs_xyzac_forward(joints, parameters, &pose);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/trtfuncs.c
// Mirrors xyzacKinematicsInverse().
LinuxCncFiveAxisJoints linuxcnc_xyzac_trt_inverse(const CncSimPose &pose,
const LinuxCncXyzbcTrtParameters &parameters) {
LinuxCncFiveAxisJoints joints{};
linuxcnc_trtfuncs_xyzac_inverse(pose, parameters, &joints);
return joints;
}
// Source: linuxcnc/src/emc/kinematics/userkfuncs.c
// userkKinematicsForward() delegates to identityKinematicsForward(),
// implemented in linuxcnc/src/emc/kinematics/kins_util.c.
CncSimPose linuxcnc_xyzbc_userk_forward(const LinuxCncFiveAxisJoints &joints) {
CncSimPose pose{};
linuxcnc_userkfuncs_forward(joints, &pose);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/userkfuncs.c
// userkKinematicsInverse() delegates to identityKinematicsInverse(),
// implemented in linuxcnc/src/emc/kinematics/kins_util.c.
LinuxCncFiveAxisJoints linuxcnc_xyzbc_userk_inverse(const CncSimPose &pose) {
LinuxCncFiveAxisJoints joints{};
linuxcnc_userkfuncs_inverse(pose, &joints);
return joints;
}
// Source defaults: linuxcnc/src/emc/kinematics/scarakins.c
LinuxCncScaraParameters linuxcnc_scara_default_parameters() {
LinuxCncScaraParameters parameters{};
parameters.d1 = 490.0;
parameters.d2 = 340.0;
parameters.d3 = 50.0;
parameters.d4 = 250.0;
parameters.d5 = 50.0;
parameters.d6 = 50.0;
return parameters;
}
// Source: linuxcnc/src/emc/kinematics/scarakins.c
CncSimPose linuxcnc_scara_forward(const LinuxCncScaraJoints &joints,
const LinuxCncScaraParameters &parameters,
int *iflags) {
CncSimPose pose{};
linuxcnc_scarakins_forward(joints, parameters, &pose, iflags);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/scarakins.c
LinuxCncScaraJoints linuxcnc_scara_inverse(const CncSimPose &pose,
const LinuxCncScaraParameters &parameters,
int iflags) {
LinuxCncScaraJoints joints{};
linuxcnc_scarakins_inverse(pose, parameters, iflags, &joints);
return joints;
}
LinuxCncXyzabTdrParameters linuxcnc_xyzab_tdr_default_parameters(double tool_offset_z) {
LinuxCncXyzabTdrParameters parameters{};
parameters.tool_offset_z = tool_offset_z;
return parameters;
}
// Source: linuxcnc/src/hal/components/xyzab_tdr_kins.comp
// Mirrors kinematicsForward() switchkins type 0.
CncSimPose linuxcnc_xyzab_tdr_identity_forward(const LinuxCncAxisJoints &joints) {
CncSimPose pose{};
LinuxCncXyzabTdrParameters parameters{};
linuxcnc_xyzab_tdr_kins_forward(joints, parameters, 0, &pose);
return pose;
}
// Source: linuxcnc/src/hal/components/xyzab_tdr_kins.comp
// Mirrors kinematicsInverse() switchkins type 0.
LinuxCncAxisJoints linuxcnc_xyzab_tdr_identity_inverse(const CncSimPose &pose) {
LinuxCncAxisJoints joints{};
LinuxCncXyzabTdrParameters parameters{};
linuxcnc_xyzab_tdr_kins_inverse(pose, parameters, 0, &joints);
return joints;
}
// Source: linuxcnc/src/hal/components/xyzab_tdr_kins.comp
// Mirrors kinematicsForward() switchkins type 1.
CncSimPose linuxcnc_xyzab_tdr_tcp_forward(const LinuxCncAxisJoints &joints,
const LinuxCncXyzabTdrParameters &parameters) {
CncSimPose pose{};
linuxcnc_xyzab_tdr_kins_forward(joints, parameters, 1, &pose);
return pose;
}
// Source: linuxcnc/src/hal/components/xyzab_tdr_kins.comp
// Mirrors kinematicsInverse() switchkins type 1.
LinuxCncAxisJoints linuxcnc_xyzab_tdr_tcp_inverse(const CncSimPose &pose,
const LinuxCncXyzabTdrParameters &parameters) {
LinuxCncAxisJoints joints{};
linuxcnc_xyzab_tdr_kins_inverse(pose, parameters, 1, &joints);
return joints;
}
LinuxCncPumaParameters linuxcnc_puma_default_parameters() {
LinuxCncPumaParameters parameters{};
parameters.a2 = 300.0;
parameters.a3 = 50.0;
parameters.d3 = 70.0;
parameters.d4 = 400.0;
parameters.d6 = 70.0;
return parameters;
}
// Source: linuxcnc/src/emc/kinematics/pumakins.c
CncSimPose linuxcnc_puma_forward(const LinuxCncAxisJoints &joints,
const LinuxCncPumaParameters &parameters,
int *iflags) {
CncSimPose pose{};
linuxcnc_pumakins_forward(joints, parameters, &pose, iflags);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/pumakins.c
LinuxCncAxisJoints linuxcnc_puma_inverse(const CncSimPose &pose,
const LinuxCncAxisJoints &current_joints,
const LinuxCncPumaParameters &parameters,
int iflags,
int *fflags) {
LinuxCncAxisJoints joints{};
linuxcnc_pumakins_inverse(pose, current_joints, parameters, iflags, &joints, fflags);
return joints;
}
LinuxCncGenserParameters linuxcnc_genser_puma560_parameters() {
LinuxCncGenserParameters parameters{};
// Source: linuxcnc/configs/sim/axis/vismach/puma/puma560_dh.hal.
parameters.a[0] = 0.0;
parameters.a[1] = 0.0;
parameters.a[2] = 17.0;
parameters.a[3] = 0.0;
parameters.a[4] = 0.0;
parameters.a[5] = 0.0;
parameters.alpha[0] = 0.0;
parameters.alpha[1] = 1.570796326;
parameters.alpha[2] = 0.0;
parameters.alpha[3] = 1.570796326;
parameters.alpha[4] = -1.570796326;
parameters.alpha[5] = 1.570796326;
parameters.d[0] = 26.45;
parameters.d[1] = -5.5;
parameters.d[2] = 0.0;
parameters.d[3] = 17.05;
parameters.d[4] = 0.0;
parameters.d[5] = 2.2;
return parameters;
}
LinuxCncGenserParameters linuxcnc_genser_default_parameters(int *max_iterations) {
LinuxCncGenserParameters parameters{};
linuxcnc_genserfuncs_source_default_parameters(&parameters, max_iterations);
return parameters;
}
// Source: linuxcnc/src/emc/kinematics/genserfuncs.c
// Mirrors genserKinematicsForward() -> genser_kin_fwd() -> go_link_pose_build()
// for the six angular DH joints used by the LinuxCNC puma560 M428 configuration.
CncSimPose linuxcnc_genser_forward(const LinuxCncAxisJoints &joints,
const LinuxCncGenserParameters &parameters) {
CncSimPose pose{};
linuxcnc_genserfuncs_forward(joints, parameters, &pose);
return pose;
}
// Source: linuxcnc/src/emc/kinematics/genserfuncs.c
// Mirrors genserKinematicsInverse() for the six angular DH joints used by
// the LinuxCNC puma560 M428 configuration.
LinuxCncAxisJoints linuxcnc_genser_inverse(const CncSimPose &pose,
const LinuxCncAxisJoints &joint_estimate,
const LinuxCncGenserParameters &parameters,
int *iterations,
int max_iterations) {
if (iterations) {
*iterations = -1;
}
LinuxCncAxisJoints joints = joint_estimate;
linuxcnc_genserfuncs_inverse(pose, joint_estimate, parameters, max_iterations, &joints, iterations);
return joints;
}
// Source: linuxcnc/src/emc/kinematics/genserfuncs.c
// Exposes genserKinematicsInverse() success/failure without changing the
// LinuxCNC-computed joint buffer.
bool linuxcnc_genser_inverse_checked(const CncSimPose &pose,
const LinuxCncAxisJoints &joint_estimate,
const LinuxCncGenserParameters &parameters,
LinuxCncAxisJoints *joints,
int *iterations,
int max_iterations) {
if (iterations) {
*iterations = -1;
}
LinuxCncAxisJoints out = joint_estimate;
const bool ok = linuxcnc_genserfuncs_inverse(pose,
joint_estimate,
parameters,
max_iterations,
&out,
iterations);
if (joints) {
*joints = out;
}
return ok;
} }

View File

@@ -8,10 +8,207 @@ struct RtcpVector {
double z; double z;
}; };
// Rotation order is intrinsic tool orientation A then B then C, represented as struct LinuxCncFiveAxisJoints {
// Rz(C) * Ry(B) * Rx(A) applied to a local tool vector. double x;
RtcpVector rtcp_rotate_abc_degrees(RtcpVector vector, double a_deg, double b_deg, double c_deg); double y;
double z;
double a;
double b;
double c;
double w;
};
RtcpVector rtcp_tool_vector_from_pose(const CncSimPose &pose, double tool_length); struct LinuxCncAxisJoints {
CncSimPose rtcp_pivot_from_tool_tip(const CncSimPose &tool_tip, double tool_length); double x;
CncSimPose rtcp_tool_tip_from_pivot(const CncSimPose &pivot, double tool_length); double y;
double z;
double a;
double b;
double c;
double u;
double v;
double w;
};
struct LinuxCncXyzbcTrtParameters {
double x_rot_point;
double y_rot_point;
double z_rot_point;
double x_offset;
double y_offset;
double z_offset;
double tool_offset;
bool conventional_directions;
};
struct LinuxCncScaraParameters {
double d1;
double d2;
double d3;
double d4;
double d5;
double d6;
};
struct LinuxCncXyzabTdrParameters {
double tool_offset_z;
double x_offset;
double z_offset;
double x_rot_point;
double y_rot_point;
double z_rot_point;
};
struct LinuxCncPumaParameters {
double a2;
double a3;
double d3;
double d4;
double d6;
};
struct LinuxCncGenserParameters {
double a[6];
double alpha[6];
double d[6];
int unrotate[6];
};
struct LinuxCncScaraJoints {
double j0;
double j1;
double j2;
double j3;
double j4;
double j5;
};
struct LinuxCncGenhexJoints {
double strut[6];
};
struct LinuxCncPentakinsJoints {
double strut[5];
};
struct LinuxCncTripodJoints {
double strut[3];
};
struct LinuxCncScorbotJoints {
double j0;
double j1;
double j2;
double j3;
double j4;
};
struct LinuxCncLinearDeltaParameters {
double r;
double l;
};
struct LinuxCncRotaryDeltaParameters {
double platformradius;
double thighlength;
double shinlength;
double footradius;
};
struct LinuxCncMaxkinsParameters {
double pivot_length;
bool conventional_directions;
};
LinuxCncAxisJoints linuxcnc_identity_default_joints();
CncSimPose linuxcnc_identity_forward(const LinuxCncAxisJoints &joints);
LinuxCncAxisJoints linuxcnc_identity_inverse(const CncSimPose &pose);
CncSimPose linuxcnc_corexykins_forward(const LinuxCncAxisJoints &joints);
LinuxCncAxisJoints linuxcnc_corexykins_inverse(const CncSimPose &pose);
CncSimPose linuxcnc_rotatekins_forward(const LinuxCncAxisJoints &joints);
LinuxCncAxisJoints linuxcnc_rotatekins_inverse(const CncSimPose &pose);
LinuxCncGenhexJoints linuxcnc_genhex_inverse(const CncSimPose &pose);
CncSimPose linuxcnc_genhex_forward(const LinuxCncGenhexJoints &joints, const CncSimPose &initial_pose);
LinuxCncPentakinsJoints linuxcnc_pentakins_inverse(const CncSimPose &pose);
CncSimPose linuxcnc_pentakins_forward(const LinuxCncPentakinsJoints &joints,
const CncSimPose &initial_pose);
LinuxCncTripodJoints linuxcnc_tripodkins_inverse(const CncSimPose &pose);
CncSimPose linuxcnc_tripodkins_forward(const LinuxCncTripodJoints &joints);
LinuxCncScorbotJoints linuxcnc_scorbot_kins_inverse(const CncSimPose &pose);
CncSimPose linuxcnc_scorbot_kins_forward(const LinuxCncScorbotJoints &joints);
LinuxCncLinearDeltaParameters linuxcnc_lineardelta_default_parameters();
CncSimPose linuxcnc_lineardelta_forward(const LinuxCncAxisJoints &joints,
const LinuxCncLinearDeltaParameters &parameters);
LinuxCncAxisJoints linuxcnc_lineardelta_inverse(const CncSimPose &pose,
const LinuxCncLinearDeltaParameters &parameters);
LinuxCncRotaryDeltaParameters linuxcnc_rotarydelta_default_parameters();
CncSimPose linuxcnc_rotarydelta_forward(const LinuxCncAxisJoints &joints,
const LinuxCncRotaryDeltaParameters &parameters);
LinuxCncAxisJoints linuxcnc_rotarydelta_inverse(const CncSimPose &pose,
const LinuxCncRotaryDeltaParameters &parameters);
LinuxCncMaxkinsParameters linuxcnc_maxkins_default_parameters();
CncSimPose linuxcnc_maxkins_forward(const LinuxCncAxisJoints &joints,
const LinuxCncMaxkinsParameters &parameters);
LinuxCncAxisJoints linuxcnc_maxkins_inverse(const CncSimPose &pose,
const LinuxCncMaxkinsParameters &parameters);
CncSimPose linuxcnc_rosekins_forward(const LinuxCncAxisJoints &joints);
LinuxCncAxisJoints linuxcnc_rosekins_inverse(const CncSimPose &pose);
CncSimPose linuxcnc_5axis_forward(const LinuxCncFiveAxisJoints &joints, double pivot_length);
LinuxCncFiveAxisJoints linuxcnc_5axis_inverse(const CncSimPose &pose, double pivot_length);
LinuxCncXyzbcTrtParameters linuxcnc_xyzbc_trt_default_parameters(double tool_offset);
CncSimPose linuxcnc_xyzbc_trt_forward(const LinuxCncFiveAxisJoints &joints,
const LinuxCncXyzbcTrtParameters &parameters);
LinuxCncFiveAxisJoints linuxcnc_xyzbc_trt_inverse(const CncSimPose &pose,
const LinuxCncXyzbcTrtParameters &parameters);
CncSimPose linuxcnc_xyzac_trt_forward(const LinuxCncFiveAxisJoints &joints,
const LinuxCncXyzbcTrtParameters &parameters);
LinuxCncFiveAxisJoints linuxcnc_xyzac_trt_inverse(const CncSimPose &pose,
const LinuxCncXyzbcTrtParameters &parameters);
CncSimPose linuxcnc_xyzbc_userk_forward(const LinuxCncFiveAxisJoints &joints);
LinuxCncFiveAxisJoints linuxcnc_xyzbc_userk_inverse(const CncSimPose &pose);
LinuxCncScaraParameters linuxcnc_scara_default_parameters();
CncSimPose linuxcnc_scara_forward(const LinuxCncScaraJoints &joints,
const LinuxCncScaraParameters &parameters,
int *iflags);
LinuxCncScaraJoints linuxcnc_scara_inverse(const CncSimPose &pose,
const LinuxCncScaraParameters &parameters,
int iflags);
LinuxCncXyzabTdrParameters linuxcnc_xyzab_tdr_default_parameters(double tool_offset_z);
CncSimPose linuxcnc_xyzab_tdr_identity_forward(const LinuxCncAxisJoints &joints);
LinuxCncAxisJoints linuxcnc_xyzab_tdr_identity_inverse(const CncSimPose &pose);
CncSimPose linuxcnc_xyzab_tdr_tcp_forward(const LinuxCncAxisJoints &joints,
const LinuxCncXyzabTdrParameters &parameters);
LinuxCncAxisJoints linuxcnc_xyzab_tdr_tcp_inverse(const CncSimPose &pose,
const LinuxCncXyzabTdrParameters &parameters);
LinuxCncPumaParameters linuxcnc_puma_default_parameters();
CncSimPose linuxcnc_puma_forward(const LinuxCncAxisJoints &joints,
const LinuxCncPumaParameters &parameters,
int *iflags);
LinuxCncAxisJoints linuxcnc_puma_inverse(const CncSimPose &pose,
const LinuxCncAxisJoints &current_joints,
const LinuxCncPumaParameters &parameters,
int iflags,
int *fflags);
LinuxCncGenserParameters linuxcnc_genser_puma560_parameters();
LinuxCncGenserParameters linuxcnc_genser_default_parameters(int *max_iterations = nullptr);
CncSimPose linuxcnc_genser_forward(const LinuxCncAxisJoints &joints,
const LinuxCncGenserParameters &parameters);
LinuxCncAxisJoints linuxcnc_genser_inverse(const CncSimPose &pose,
const LinuxCncAxisJoints &joint_estimate,
const LinuxCncGenserParameters &parameters,
int *iterations,
int max_iterations = 100);
bool linuxcnc_genser_inverse_checked(const CncSimPose &pose,
const LinuxCncAxisJoints &joint_estimate,
const LinuxCncGenserParameters &parameters,
LinuxCncAxisJoints *joints,
int *iterations,
int max_iterations = 100);

View File

@@ -6,31 +6,155 @@
namespace { namespace {
std::string strip_line_comment(const std::string &line) { bool strip_line_comment(const std::string &line, bool block_delete, std::string *stripped) {
std::string out; std::string out;
bool in_paren = false; bool in_paren = false;
bool saw_code = false;
for (char raw : line) { for (char raw : line) {
const char ch = static_cast<char>(std::toupper(static_cast<unsigned char>(raw))); const char ch = static_cast<char>(std::toupper(static_cast<unsigned char>(raw)));
if (in_paren) { if (in_paren) {
if (ch == '(') {
return false;
}
if (ch == ')') { if (ch == ')') {
in_paren = false; in_paren = false;
} }
continue; continue;
} }
if (ch == '(') { if (ch == '(') {
saw_code = true;
in_paren = true; in_paren = true;
continue; continue;
} }
if (ch == ';') { if (ch == ';') {
break; break;
} }
if (!saw_code && std::isspace(static_cast<unsigned char>(ch))) {
out.push_back(ch);
continue;
}
if (!saw_code && ch == '/') {
if (block_delete) {
return false;
}
saw_code = true;
continue;
}
saw_code = true;
out.push_back(ch); out.push_back(ch);
} }
return out; if (in_paren) {
return false;
}
*stripped = out;
return true;
} }
bool g_code_is(double actual, double expected) { bool linuxcnc_integer_word_value(double value, int *integer) {
return std::fabs(actual - expected) < 0.0001; *integer = static_cast<int>(std::floor(value));
const double fraction = value - *integer;
if (fraction > 0.9999) {
*integer = static_cast<int>(std::ceil(value));
return true;
}
return fraction <= 0.0001;
}
bool linuxcnc_g_word_value(double value, int *g_code) {
const double scaled = value * 10.0;
int parsed = static_cast<int>(std::floor(scaled));
const double fraction = scaled - parsed;
if (fraction > 0.999) {
parsed = static_cast<int>(std::ceil(scaled));
} else if (fraction > 0.001) {
return false;
}
if (parsed < 0 || parsed > 999) {
return false;
}
*g_code = parsed;
return true;
}
bool read_linuxcnc_real_number(const char *text, size_t start, double *value, size_t *end_index) {
const char first = text[start];
const char next = text[start + 1];
if (first == '[') {
double nested = 0.0;
size_t nested_end = start + 1;
if (!read_linuxcnc_real_number(text, start + 1, &nested, &nested_end) ||
text[nested_end] != ']') {
return false;
}
*value = nested;
*end_index = nested_end + 1;
return true;
}
if ((first == '+' || first == '-') &&
next != '\0' &&
!std::isdigit(static_cast<unsigned char>(next)) &&
next != '.') {
double nested = 0.0;
size_t nested_end = start + 1;
if (!read_linuxcnc_real_number(text, start + 1, &nested, &nested_end)) {
return false;
}
*value = first == '-' ? -nested : nested;
*end_index = nested_end;
return true;
}
size_t i = start;
while (text[i] == '+' || text[i] == '-') {
++i;
}
const size_t number_start = i;
while (std::isdigit(static_cast<unsigned char>(text[i])) || text[i] == '.') {
++i;
}
if (i == start || i == number_start) {
return false;
}
std::string token(text + start, i - start);
char *end = nullptr;
const double parsed = std::strtod(token.c_str(), &end);
if (end != token.c_str() + token.size()) {
return false;
}
*value = parsed;
*end_index = i;
return true;
}
bool skip_linuxcnc_leading_n_number(const std::string &stripped, std::string *without_n) {
size_t i = 0;
while (std::isspace(static_cast<unsigned char>(stripped[i]))) {
++i;
}
if (stripped[i] != 'N') {
*without_n = stripped;
return true;
}
++i;
const size_t integer_start = i;
while (std::isdigit(static_cast<unsigned char>(stripped[i]))) {
++i;
}
if (i == integer_start) {
return false;
}
if (stripped[i] == '.') {
++i;
const size_t fraction_start = i;
while (std::isdigit(static_cast<unsigned char>(stripped[i]))) {
++i;
}
if (i == fraction_start) {
return false;
}
}
*without_n = stripped.substr(i);
return true;
} }
bool parse_m_control_line(const std::string &stripped, bool parse_m_control_line(const std::string &stripped,
@@ -46,9 +170,13 @@ bool parse_m_control_line(const std::string &stripped,
return false; return false;
} }
++i; ++i;
char *end = nullptr; double raw_value = 0.0;
const long value = std::strtol(text + i, &end, 10); size_t end = i;
if (end == text + i) { if (!read_linuxcnc_real_number(text, i, &raw_value, &end)) {
return false;
}
int value = 0;
if (!linuxcnc_integer_word_value(raw_value, &value) || value < 0) {
return false; return false;
} }
@@ -56,19 +184,19 @@ bool parse_m_control_line(const std::string &stripped,
action.kind = SimulatorGcodeControlKind::KinematicsSwitch; action.kind = SimulatorGcodeControlKind::KinematicsSwitch;
if (value == 428) { if (value == 428) {
action.value = 1; action.value = 1;
action.rtcp_enabled = true; action.m_code = 428;
} else if (value == 429) { } else if (value == 429) {
action.value = 0; action.value = 0;
action.rtcp_enabled = false; action.m_code = 429;
} else if (value == 430) { } else if (value == 430) {
action.value = 2; action.value = 2;
action.rtcp_enabled = true; action.m_code = 430;
} else { } else {
return false; return false;
} }
actions->push_back(action); actions->push_back(action);
saw_code = true; saw_code = true;
i = static_cast<size_t>(end - text); i = end;
} }
return saw_code; return saw_code;
} }
@@ -78,7 +206,9 @@ bool parse_rtcp_control_line(const std::string &stripped,
const char *text = stripped.c_str(); const char *text = stripped.c_str();
std::vector<SimulatorGcodeControlAction> parsed; std::vector<SimulatorGcodeControlAction> parsed;
int h_code = 0; int h_code = 0;
bool saw_h_word = false;
bool saw_rtcp_code = false; bool saw_rtcp_code = false;
bool saw_rtcp_g_word = false;
for (size_t i = 0; text[i] != '\0';) { for (size_t i = 0; text[i] != '\0';) {
if (std::isspace(static_cast<unsigned char>(text[i]))) { if (std::isspace(static_cast<unsigned char>(text[i]))) {
@@ -90,26 +220,44 @@ bool parse_rtcp_control_line(const std::string &stripped,
return false; return false;
} }
++i; ++i;
char *end = nullptr; double value = 0.0;
const double value = std::strtod(text + i, &end); size_t end = i;
if (end == text + i) { if (!read_linuxcnc_real_number(text, i, &value, &end)) {
return false; return false;
} }
if (letter == 'H') { if (letter == 'H') {
h_code = static_cast<int>(std::lround(value)); if (saw_h_word) {
} else if (g_code_is(value, 43.4) || g_code_is(value, 43.5) || g_code_is(value, 49.0)) { return false;
}
if (!linuxcnc_integer_word_value(value, &h_code) || h_code < -1) {
return false;
}
saw_h_word = true;
} else {
int g_code = 0;
if (!linuxcnc_g_word_value(value, &g_code) ||
(g_code != 434 && g_code != 435 && g_code != 490)) {
return false;
}
if (saw_rtcp_g_word) {
return false;
}
SimulatorGcodeControlAction action{}; SimulatorGcodeControlAction action{};
action.kind = SimulatorGcodeControlKind::RtcpState; action.kind = SimulatorGcodeControlKind::RtcpState;
action.value = static_cast<int>(std::lround(value * 10.0)); action.value = g_code;
action.rtcp_enabled = g_code_is(value, 43.4) || g_code_is(value, 43.5); action.rtcp_enabled = g_code == 434 || g_code == 435;
parsed.push_back(action); parsed.push_back(action);
saw_rtcp_code = true; saw_rtcp_code = true;
} else { saw_rtcp_g_word = true;
return false;
} }
i = static_cast<size_t>(end - text); i = end;
} }
for (const auto &action : parsed) {
if (!action.rtcp_enabled && saw_h_word) {
return false;
}
}
for (auto &action : parsed) { for (auto &action : parsed) {
action.h_code = action.rtcp_enabled ? h_code : 0; action.h_code = action.rtcp_enabled ? h_code : 0;
actions->push_back(action); actions->push_back(action);
@@ -121,25 +269,51 @@ bool parse_rtcp_control_line(const std::string &stripped,
bool parse_simulator_gcode_control_line(const std::string &line, bool parse_simulator_gcode_control_line(const std::string &line,
std::vector<SimulatorGcodeControlAction> *actions) { std::vector<SimulatorGcodeControlAction> *actions) {
return parse_simulator_gcode_control_line(line, false, actions);
}
bool parse_simulator_gcode_control_line(const std::string &line,
bool block_delete,
std::vector<SimulatorGcodeControlAction> *actions) {
actions->clear(); actions->clear();
const std::string stripped = strip_line_comment(line); std::string stripped;
if (parse_m_control_line(stripped, actions)) { if (!strip_line_comment(line, block_delete, &stripped)) {
return false;
}
std::string control_line;
if (!skip_linuxcnc_leading_n_number(stripped, &control_line)) {
return false;
}
if (parse_m_control_line(control_line, actions)) {
return true; return true;
} }
actions->clear(); actions->clear();
if (parse_rtcp_control_line(stripped, actions)) { if (parse_rtcp_control_line(control_line, actions)) {
return true; return true;
} }
actions->clear(); actions->clear();
return false; return false;
} }
void emit_simulator_gcode_control_action(CanonEventSink &sink, bool can_emit_simulator_gcode_control_action(const CanonEventSink &sink,
const SimulatorGcodeControlAction &action) {
if (action.kind == SimulatorGcodeControlKind::KinematicsSwitch) {
return sink.can_switch_m_code_kinematics(action.m_code);
}
if (action.kind == SimulatorGcodeControlKind::RtcpState) {
return true;
}
return false;
}
bool emit_simulator_gcode_control_action(CanonEventSink &sink,
const SimulatorGcodeControlAction &action, const SimulatorGcodeControlAction &action,
int line) { int line) {
if (action.kind == SimulatorGcodeControlKind::KinematicsSwitch) { if (action.kind == SimulatorGcodeControlKind::KinematicsSwitch) {
sink.switch_kinematics(action.value, action.rtcp_enabled, line); return sink.switch_m_code_kinematics(action.m_code, action.value, line);
} else if (action.kind == SimulatorGcodeControlKind::RtcpState) { } else if (action.kind == SimulatorGcodeControlKind::RtcpState) {
sink.set_rtcp_state(action.rtcp_enabled, action.h_code, line); sink.set_rtcp_state(action.rtcp_enabled, action.h_code, line);
return true;
} }
return false;
} }

View File

@@ -13,12 +13,18 @@ enum class SimulatorGcodeControlKind {
struct SimulatorGcodeControlAction { struct SimulatorGcodeControlAction {
SimulatorGcodeControlKind kind; SimulatorGcodeControlKind kind;
int value; int value;
int m_code;
bool rtcp_enabled; bool rtcp_enabled;
int h_code; int h_code;
}; };
bool parse_simulator_gcode_control_line(const std::string &line, bool parse_simulator_gcode_control_line(const std::string &line,
std::vector<SimulatorGcodeControlAction> *actions); std::vector<SimulatorGcodeControlAction> *actions);
void emit_simulator_gcode_control_action(CanonEventSink &sink, bool parse_simulator_gcode_control_line(const std::string &line,
bool block_delete,
std::vector<SimulatorGcodeControlAction> *actions);
bool can_emit_simulator_gcode_control_action(const CanonEventSink &sink,
const SimulatorGcodeControlAction &action);
bool emit_simulator_gcode_control_action(CanonEventSink &sink,
const SimulatorGcodeControlAction &action, const SimulatorGcodeControlAction &action,
int line); int line);

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@
#include "canon_event_sink.h" #include "canon_event_sink.h"
#include <cstddef> #include <cstddef>
#include <unordered_map>
#include <string> #include <string>
class SmokeGcodeParser { class SmokeGcodeParser {
@@ -12,14 +13,38 @@ public:
int parse(const char *program, size_t program_len, std::string *error); int parse(const char *program, size_t program_len, std::string *error);
private: private:
struct SmokeToolTableEntry {
int pocketno = 0;
CncSimPose offset{};
double diameter = 0.0;
double frontangle = 0.0;
double backangle = 0.0;
double orientation = 0.0;
};
CanonEventSink &sink_; CanonEventSink &sink_;
bool absolute_ = true; bool absolute_ = true;
bool arc_absolute_ = false; bool arc_absolute_ = false;
bool lathe_diameter_mode_ = false; bool lathe_diameter_mode_ = false;
bool cutter_comp_on_ = false; bool cutter_comp_on_ = false;
bool cutter_comp_exit_pending_ = false;
bool cutter_comp_has_prev_vector_ = false;
bool cutter_comp_has_prev_prev_vector_ = false;
double cutter_comp_radius_ = 0.0;
int spindle_tool_id_ = 0;
CncSimPose cutter_comp_prev_vector_{};
CncSimPose cutter_comp_prev_prev_vector_{};
int cutter_comp_mode_ = 40;
bool spindle_css_mode_ = false;
bool tool_selected_ = false;
bool nurbs_g5_collecting_ = false;
int nurbs_g5_control_points_ = 0;
int nurbs_g5_order_ = 3;
bool nurbs_g6_collecting_ = false;
int feed_mode_ = 0; int feed_mode_ = 0;
int canned_cycle_ = 0; int canned_cycle_ = 0;
bool canned_return_to_initial_ = false; bool canned_return_to_initial_ = false;
bool g92_applied_ = false;
bool canned_has_r_ = false; bool canned_has_r_ = false;
bool canned_has_z_ = false; bool canned_has_z_ = false;
bool canned_has_p_ = false; bool canned_has_p_ = false;
@@ -36,6 +61,11 @@ private:
double canned_i_ = 0.0; double canned_i_ = 0.0;
double canned_j_ = 0.0; double canned_j_ = 0.0;
double canned_k_ = 0.0; double canned_k_ = 0.0;
double m66_result_ = 0.0;
double oword_return_value_ = 0.0; double oword_return_value_ = 0.0;
bool oword_value_returned_ = false; bool oword_value_returned_ = false;
int selected_tool_id_ = 0;
int current_pocket_index_ = 0;
int selected_pocket_index_ = 0;
std::unordered_map<int, SmokeToolTableEntry> tool_table_;
}; };

View File

@@ -1,5 +1,6 @@
#include "canon_event_sink.h" #include "canon_event_sink.h"
#include <cmath>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
@@ -25,6 +26,18 @@ bool expect(bool value, const char *message) {
return true; return true;
} }
bool near(double actual, double expected) {
return std::fabs(actual - expected) < 1e-9;
}
bool expect_near(double actual, double expected, const char *message) {
if (!near(actual, expected)) {
std::cerr << "FAIL: " << message << " actual=" << actual << " expected=" << expected << '\n';
return false;
}
return true;
}
} // namespace } // namespace
int main() { int main() {
@@ -32,6 +45,8 @@ int main() {
std::vector<CncSimEvent> events; std::vector<CncSimEvent> events;
CanonEventSink sink; CanonEventSink sink;
sink.set_callback(collect_event, &events); sink.set_callback(collect_event, &events);
ok &= expect(sink.kinematics_type() == 0,
"expected LinuxCNC xyzbc-trt identityfirst startup kinematics type 0");
CncSimPose end{}; CncSimPose end{};
end.x = 10.0; end.x = 10.0;
@@ -49,6 +64,8 @@ int main() {
int abort_count = 0; int abort_count = 0;
sink.reset(); sink.reset();
ok &= expect(sink.kinematics_type() == 0,
"expected LinuxCNC xyzbc-trt identityfirst reset kinematics type 0");
sink.set_callback(abort_on_second_event, &abort_count); sink.set_callback(abort_on_second_event, &abort_count);
sink.select_plane(17, 1); sink.select_plane(17, 1);
sink.set_feed_rate(100.0, 2); sink.set_feed_rate(100.0, 2);
@@ -56,6 +73,290 @@ int main() {
ok &= expect(sink.callback_aborted(), "expected callback abort state"); ok &= expect(sink.callback_aborted(), "expected callback abort state");
ok &= expect(abort_count == 2, "expected no further callbacks after abort"); ok &= expect(abort_count == 2, "expected no further callbacks after abort");
events.clear();
sink.reset();
sink.set_callback(collect_event, &events);
sink.configure_rtcp(false, 250.0);
sink.switch_kinematics(2, 1);
CncSimPose five_axis_end{};
five_axis_end.x = 260.0;
five_axis_end.y = 20.0;
five_axis_end.z = 280.0;
five_axis_end.b = 90.0;
five_axis_end.c = 45.0;
five_axis_end.w = 12.0;
sink.straight_feed(2, five_axis_end);
bool saw_userk_before_g434 = false;
for (const auto &event : events) {
saw_userk_before_g434 = saw_userk_before_g434 || event.type == CNC_SIM_EVENT_RTCP_PIVOT;
}
ok &= expect(!saw_userk_before_g434, "expected M430 not to enable RTCP without G43.4/G43.5");
events.clear();
sink.set_rtcp_state(true, 0, 2);
sink.straight_feed(3, five_axis_end);
bool saw_userk_pivot = false;
for (const auto &event : events) {
saw_userk_pivot = saw_userk_pivot ||
(event.type == CNC_SIM_EVENT_RTCP_PIVOT &&
event.line == 3 &&
event.reserved == 2 &&
near(event.dwell_seconds, 250.0) &&
near(event.end.x, 260.0) &&
near(event.end.y, 20.0) &&
near(event.end.z, 280.0) &&
near(event.end.b, 90.0) &&
near(event.end.c, 45.0) &&
near(event.end.w, 0.0));
}
ok &= expect(saw_userk_pivot, "expected M430-style RTCP pivot to use LinuxCNC userk identity inverse");
events.clear();
sink.configure_rtcp(true, 0.0);
sink.switch_kinematics(2, 3);
bool saw_userk_remap_m68 = false;
bool saw_userk_remap_m66 = false;
bool saw_userk_switch_rtcp_feed = false;
for (const auto &event : events) {
saw_userk_remap_m68 = saw_userk_remap_m68 ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 3 &&
event.reserved == 68 &&
event.tool == 3 &&
near(event.feed, 2.0));
saw_userk_remap_m66 = saw_userk_remap_m66 ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 3 &&
event.reserved == 66 &&
event.tool == 0 &&
near(event.feed, 0.0) &&
near(event.dwell_seconds, 0.0));
saw_userk_switch_rtcp_feed = saw_userk_switch_rtcp_feed ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 3 &&
event.reserved == 2 &&
near(event.feed, 1.0));
}
ok &= expect(saw_userk_remap_m68, "expected M430 remap to emit LinuxCNC M68 E3 Q2 side effect");
ok &= expect(saw_userk_remap_m66, "expected M430 remap to emit LinuxCNC M66 E0 L0 side effect");
ok &= expect(!saw_userk_switch_rtcp_feed,
"expected kinematics switch event not to carry RTCP state");
events.clear();
CncSimPose zero_tool_end{};
zero_tool_end.x = 7.0;
zero_tool_end.y = 8.0;
zero_tool_end.z = 9.0;
zero_tool_end.b = 10.0;
zero_tool_end.c = 11.0;
sink.straight_feed(4, zero_tool_end);
bool saw_zero_tool_userk_pivot = false;
for (const auto &event : events) {
saw_zero_tool_userk_pivot = saw_zero_tool_userk_pivot ||
(event.type == CNC_SIM_EVENT_RTCP_PIVOT &&
event.line == 4 &&
event.reserved == 2 &&
near(event.dwell_seconds, 0.0) &&
near(event.end.x, 7.0) &&
near(event.end.y, 8.0) &&
near(event.end.z, 9.0) &&
near(event.end.b, 10.0) &&
near(event.end.c, 11.0));
}
ok &= expect(saw_zero_tool_userk_pivot, "expected M430 userk inverse to run with zero tool offset");
events.clear();
sink.configure_rtcp(false, 250.0);
sink.switch_kinematics(1, 3);
CncSimPose trt_end{};
trt_end.x = 225.0;
trt_end.y = 20.0;
trt_end.z = -205.0;
trt_end.b = 90.0;
sink.straight_feed(4, trt_end);
bool saw_trt_before_g434 = false;
for (const auto &event : events) {
saw_trt_before_g434 = saw_trt_before_g434 || event.type == CNC_SIM_EVENT_RTCP_PIVOT;
}
ok &= expect(!saw_trt_before_g434, "expected M428 not to enable RTCP without G43.4/G43.5");
events.clear();
sink.set_rtcp_state(true, 0, 4);
sink.straight_feed(5, trt_end);
bool saw_xyzbc_trt_pivot = false;
for (const auto &event : events) {
saw_xyzbc_trt_pivot = saw_xyzbc_trt_pivot ||
(event.type == CNC_SIM_EVENT_RTCP_PIVOT &&
event.line == 5 &&
event.reserved == 1 &&
near(event.dwell_seconds, 250.0) &&
near(event.end.x, 10.0) &&
near(event.end.y, 20.0) &&
near(event.end.z, 30.0) &&
near(event.end.b, 90.0));
}
ok &= expect(saw_xyzbc_trt_pivot, "expected M428-style RTCP pivot to use LinuxCNC xyzbc-trt inverse");
events.clear();
LinuxCncXyzbcTrtParameters trt_parameters = linuxcnc_xyzbc_trt_default_parameters(0.0);
trt_parameters.x_rot_point = 3.0;
trt_parameters.y_rot_point = -4.0;
trt_parameters.z_rot_point = 5.0;
trt_parameters.x_offset = 2.0;
trt_parameters.z_offset = 7.0;
trt_parameters.conventional_directions = true;
sink.configure_rtcp(false, 11.0);
sink.configure_xyzbc_trt(trt_parameters);
sink.switch_kinematics(1, 5);
sink.set_rtcp_state(true, 0, 5);
CncSimPose configured_trt_end{};
configured_trt_end.x = 30.0;
configured_trt_end.y = 40.0;
configured_trt_end.z = 50.0;
configured_trt_end.b = 20.0;
configured_trt_end.c = -30.0;
sink.straight_feed(6, configured_trt_end);
LinuxCncXyzbcTrtParameters expected_trt_parameters = trt_parameters;
expected_trt_parameters.tool_offset = 11.0;
const LinuxCncFiveAxisJoints expected_configured_trt =
linuxcnc_xyzbc_trt_inverse(configured_trt_end, expected_trt_parameters);
bool saw_configured_trt_pivot = false;
for (const auto &event : events) {
saw_configured_trt_pivot = saw_configured_trt_pivot ||
(event.type == CNC_SIM_EVENT_RTCP_PIVOT &&
event.line == 6 &&
event.reserved == 1 &&
near(event.dwell_seconds, 11.0) &&
near(event.end.x, expected_configured_trt.x) &&
near(event.end.y, expected_configured_trt.y) &&
near(event.end.z, expected_configured_trt.z) &&
near(event.end.b, expected_configured_trt.b) &&
near(event.end.c, expected_configured_trt.c));
}
ok &= expect(saw_configured_trt_pivot, "expected M428 xyzbc-trt inverse to use configured HAL-style pins");
events.clear();
LinuxCncXyzbcTrtParameters xyzac_parameters = linuxcnc_xyzbc_trt_default_parameters(0.0);
xyzac_parameters.y_offset = 20.0;
xyzac_parameters.z_offset = 10.0;
sink.configure_rtcp(false, 7.0);
sink.configure_xyzac_trt(xyzac_parameters);
sink.switch_kinematics(1, 6);
sink.set_rtcp_state(true, 0, 6);
CncSimPose xyzac_end{};
xyzac_end.x = 12.0;
xyzac_end.y = -8.0;
xyzac_end.z = 42.0;
xyzac_end.a = 35.0;
xyzac_end.c = -25.0;
sink.straight_feed(7, xyzac_end);
LinuxCncXyzbcTrtParameters expected_xyzac_parameters = xyzac_parameters;
expected_xyzac_parameters.tool_offset = 7.0;
const LinuxCncFiveAxisJoints expected_xyzac =
linuxcnc_xyzac_trt_inverse(xyzac_end, expected_xyzac_parameters);
bool saw_xyzac_trt_pivot = false;
for (const auto &event : events) {
saw_xyzac_trt_pivot = saw_xyzac_trt_pivot ||
(event.type == CNC_SIM_EVENT_RTCP_PIVOT &&
event.line == 7 &&
event.reserved == 1 &&
near(event.dwell_seconds, 7.0) &&
near(event.end.x, expected_xyzac.x) &&
near(event.end.y, expected_xyzac.y) &&
near(event.end.z, expected_xyzac.z) &&
near(event.end.a, expected_xyzac.a) &&
near(event.end.c, expected_xyzac.c));
}
ok &= expect(saw_xyzac_trt_pivot, "expected M428 xyzac-trt inverse to use LinuxCNC xyzacKinematicsInverse");
std::vector<CncSimEvent> fiveaxis_events;
CanonEventSink fiveaxis_sink;
fiveaxis_sink.set_callback(collect_event, &fiveaxis_events);
fiveaxis_sink.configure_rtcp(true, 250.0);
fiveaxis_sink.configure_fiveaxis(250.0, 0);
fiveaxis_sink.switch_m_code_kinematics(428, 1, 8);
CncSimPose bridgemill_end{};
bridgemill_end.x = 260.0;
bridgemill_end.y = 20.0;
bridgemill_end.z = 280.0;
bridgemill_end.b = 90.0;
bridgemill_end.c = 0.0;
fiveaxis_sink.straight_feed(9, bridgemill_end);
bool saw_fiveaxis_pivot = false;
for (const auto &event : fiveaxis_events) {
saw_fiveaxis_pivot = saw_fiveaxis_pivot ||
(event.type == CNC_SIM_EVENT_RTCP_PIVOT &&
event.line == 9 &&
event.reserved == 0 &&
near(event.dwell_seconds, 250.0) &&
near(event.end.x, 10.0) &&
near(event.end.y, 20.0) &&
near(event.end.z, 30.0) &&
near(event.end.b, 90.0) &&
near(event.end.c, 0.0));
}
ok &= expect(saw_fiveaxis_pivot, "expected M428 bridgemill RTCP pivot to use LinuxCNC 5axiskins inverse");
std::vector<CncSimEvent> switchkins_events;
CanonEventSink switchkins_sink;
switchkins_sink.set_callback(collect_event, &switchkins_events);
switchkins_sink.configure_switchkins_remap(0, 1, 2);
switchkins_sink.switch_m_code_kinematics(428, 1, 10);
switchkins_sink.switch_m_code_kinematics(429, 0, 11);
bool saw_scara_m428 = false;
bool saw_scara_m429 = false;
for (const auto &event : switchkins_events) {
saw_scara_m428 = saw_scara_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 10 &&
event.reserved == 0);
saw_scara_m429 = saw_scara_m429 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 11 &&
event.reserved == 1);
}
ok &= expect(saw_scara_m428,
"expected M428 scara/bridgemill/melfa remap to select LinuxCNC kinstype 0");
ok &= expect(saw_scara_m429,
"expected M429 scara/bridgemill/melfa remap to select LinuxCNC identity kinstype 1");
std::vector<CncSimEvent> table_dual_events;
CanonEventSink table_dual_sink;
table_dual_sink.set_callback(collect_event, &table_dual_events);
table_dual_sink.configure_switchkins_remap(1, 0, -1);
const bool table_dual_m430_handled = table_dual_sink.switch_m_code_kinematics(430, 2, 12);
bool saw_table_dual_m430 = false;
for (const auto &event : table_dual_events) {
saw_table_dual_m430 = saw_table_dual_m430 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 12);
}
ok &= expect(!table_dual_m430_handled,
"expected LinuxCNC table-dual-rotary config without 430remap.ngc to leave M430 unhandled");
ok &= expect(!saw_table_dual_m430,
"expected LinuxCNC table-dual-rotary config without 430remap.ngc to skip M430 switch");
events.clear();
sink.configure_rtcp(true, 250.0);
sink.switch_kinematics(0, 3);
five_axis_end.x = 300.0;
sink.straight_feed(4, five_axis_end);
bool saw_disabled_pivot = false;
for (const auto &event : events) {
saw_disabled_pivot = saw_disabled_pivot || event.type == CNC_SIM_EVENT_RTCP_PIVOT;
}
ok &= expect(!saw_disabled_pivot, "expected M429-style kinematics switch to disable RTCP pivot events");
events.clear();
sink.configure_rtcp(true, 250.0);
sink.switch_kinematics(3, 5);
sink.straight_feed(6, five_axis_end);
bool saw_unknown_switchkins_pivot = false;
for (const auto &event : events) {
saw_unknown_switchkins_pivot = saw_unknown_switchkins_pivot || event.type == CNC_SIM_EVENT_RTCP_PIVOT;
}
ok &= expect(!saw_unknown_switchkins_pivot,
"expected unsupported switchkins type to avoid generic RTCP fallback");
return ok ? 0 : 1; return ok ? 0 : 1;
} }

Some files were not shown because too many files have changed in this diff Show More