From de273bf83091904bf2fb860dc2dfe240a3bfd947 Mon Sep 17 00:00:00 2001 From: cnc Date: Sat, 30 May 2026 10:03:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E7=BB=AD=E4=B8=8A=E4=B8=80=E8=BD=AE?= =?UTF-8?q?=EF=BC=9A=E6=8E=A8=E8=BF=9B=E6=B5=8F=E8=A7=88=E5=99=A8=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E4=B8=8EM428=E9=85=8D=E7=BD=AE=E6=94=B6=E6=8B=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 结论:已完成浏览器侧真实加载验证,M428/M429/M430 的配置入口进一步从 LinuxCNC INI/HALFILE/REMAP 来源生成,native 与 source-link 验证通过。 --- README.zh-CN.md | 12 +- analyze-linuxcnc-wasm-blockers.sh | 23 + build-wasm.sh | 34 +- check-linuxcnc-inputs.sh | 147 ++ check-linuxcnc-switchkins-remap-table.sh | 13 + core/CMakeLists.txt | 249 ++- core/src/canon_event_sink.cpp | 54 +- core/src/canon_event_sink.h | 8 +- core/src/cnc_sim_api.cpp | 72 +- core/src/gcode_backend.cpp | 9 + core/src/linuxcnc_5axiskins_adapter.c | 266 ++++ core/src/linuxcnc_5axiskins_adapter.cpp | 122 ++ core/src/linuxcnc_5axiskins_adapter.h | 10 + core/src/linuxcnc_corexykins_adapter.c | 127 ++ core/src/linuxcnc_corexykins_adapter.cpp | 104 ++ core/src/linuxcnc_corexykins_adapter.h | 6 + core/src/linuxcnc_cubic_adapter.c | 117 ++ core/src/linuxcnc_cubic_adapter.cpp | 36 + core/src/linuxcnc_cubic_adapter.h | 11 + core/src/linuxcnc_genhexkins_adapter.c | 223 +++ core/src/linuxcnc_genhexkins_adapter.cpp | 96 ++ core/src/linuxcnc_genhexkins_adapter.h | 8 + core/src/linuxcnc_genserfuncs_adapter.c | 206 +++ core/src/linuxcnc_genserfuncs_adapter.cpp | 166 ++ core/src/linuxcnc_genserfuncs_adapter.h | 15 + core/src/linuxcnc_kins_util_adapter.c | 116 ++ core/src/linuxcnc_kins_util_adapter.cpp | 121 ++ core/src/linuxcnc_kins_util_adapter.h | 6 + core/src/linuxcnc_lineardeltakins_adapter.c | 155 ++ core/src/linuxcnc_lineardeltakins_adapter.cpp | 138 ++ core/src/linuxcnc_lineardeltakins_adapter.h | 11 + core/src/linuxcnc_maxkins_adapter.c | 168 ++ core/src/linuxcnc_maxkins_adapter.cpp | 140 ++ core/src/linuxcnc_maxkins_adapter.h | 11 + core/src/linuxcnc_pentakins_adapter.c | 172 +++ core/src/linuxcnc_pentakins_adapter.cpp | 97 ++ core/src/linuxcnc_pentakins_adapter.h | 9 + core/src/linuxcnc_pumakins_adapter.c | 218 +++ core/src/linuxcnc_pumakins_adapter.cpp | 156 ++ core/src/linuxcnc_pumakins_adapter.h | 14 + core/src/linuxcnc_rosekins_adapter.c | 147 ++ core/src/linuxcnc_rosekins_adapter.cpp | 121 ++ core/src/linuxcnc_rosekins_adapter.h | 6 + core/src/linuxcnc_rotarydeltakins_adapter.c | 165 ++ core/src/linuxcnc_rotarydeltakins_adapter.cpp | 157 ++ core/src/linuxcnc_rotarydeltakins_adapter.h | 11 + core/src/linuxcnc_rotatekins_adapter.c | 118 ++ core/src/linuxcnc_rotatekins_adapter.cpp | 104 ++ core/src/linuxcnc_rotatekins_adapter.h | 6 + core/src/linuxcnc_rs274_backend.cpp | 61 +- core/src/linuxcnc_scarakins_adapter.c | 224 +++ core/src/linuxcnc_scarakins_adapter.cpp | 144 ++ core/src/linuxcnc_scarakins_adapter.h | 12 + core/src/linuxcnc_scorbot_kins_adapter.c | 121 ++ core/src/linuxcnc_scorbot_kins_adapter.cpp | 110 ++ core/src/linuxcnc_scorbot_kins_adapter.h | 6 + core/src/linuxcnc_switchkins_remap_table.inc | 110 ++ core/src/linuxcnc_tripodkins_adapter.c | 142 ++ core/src/linuxcnc_tripodkins_adapter.cpp | 94 ++ core/src/linuxcnc_tripodkins_adapter.h | 6 + core/src/linuxcnc_trivkins_adapter.c | 163 ++ core/src/linuxcnc_trivkins_adapter.cpp | 129 ++ core/src/linuxcnc_trivkins_adapter.h | 7 + core/src/linuxcnc_trtfuncs_adapter.c | 207 +++ core/src/linuxcnc_trtfuncs_adapter.cpp | 241 +++ core/src/linuxcnc_trtfuncs_adapter.h | 16 + core/src/linuxcnc_userkfuncs_adapter.c | 154 ++ core/src/linuxcnc_userkfuncs_adapter.cpp | 113 ++ core/src/linuxcnc_userkfuncs_adapter.h | 6 + core/src/linuxcnc_xyzab_tdr_kins_adapter.c | 241 +++ core/src/linuxcnc_xyzab_tdr_kins_adapter.cpp | 177 +++ core/src/linuxcnc_xyzab_tdr_kins_adapter.h | 14 + core/src/rtcp_kinematics.cpp | 1355 +++-------------- core/src/rtcp_kinematics.h | 73 + core/src/simulator_gcode_controls.cpp | 17 +- core/src/simulator_gcode_controls.h | 4 +- core/src/smoke_gcode_parser.cpp | 139 +- core/src/smoke_gcode_parser.h | 3 + core/tests/canon_event_sink_smoke.cpp | 39 + .../cnc_sim_api_linuxcnc_rs274_smoke.cpp | 134 ++ core/tests/cnc_sim_api_smoke.cpp | 580 ++++++- core/tests/rtcp_kinematics_smoke.cpp | 332 ++++ core/tools/cnc_sim_dump.cpp | 2 + core/tools/linuxcnc_rs274_dump.cpp | 5 +- core/wasm_shims/Python.h | 3 + core/wasm_shims/boost/noncopyable.hpp | 18 + core/wasm_shims/boost/python/class.hpp | 10 + .../boost/python/converter/registry.hpp | 3 + core/wasm_shims/boost/python/def.hpp | 13 + .../wasm_shims/boost/python/detail/caller.hpp | 3 + core/wasm_shims/boost/python/enum.hpp | 25 + .../boost/python/exception_translator.hpp | 13 + core/wasm_shims/boost/python/module.hpp | 5 + core/wasm_shims/boost/python/object.hpp | 275 +++- .../boost/python/object/class_detail.hpp | 16 + .../python/return_internal_reference.hpp | 13 + .../suite/indexing/map_indexing_suite.hpp | 11 + .../cnc_sim_wasm_runtime_probe_main.cc | 99 ++ core/wasm_shims/libgen.h | 20 + core/wasm_shims/linuxcnc_runtime_shim.cc | 126 ++ core/wasm_shims/python_c_api_shim.cc | 129 +- core/wasm_shims/pythonplugin/python_plugin.cc | 37 +- .../pythonplugin/python_plugin_probe_main.cc | 25 +- core/wasm_shims/structmember.h | 7 + .../tooldata/tooldata_mmap_backend.cc | 7 +- docs/linuxcnc-porting.md | 10 +- docs/linuxcnc-source-policy.md | 2 +- generate-linuxcnc-puma560-dh-parameters.sh | 55 + generate-linuxcnc-switchkins-remap-table.sh | 238 +++ linuxcnc-kinematics-source-files.txt | 98 ++ linuxcnc-rs274-source-files.txt | 2 + linuxcnc-rs274-wasm-source-files.txt | 10 + list-cnc-sim-core-sources.sh | 34 +- list-linuxcnc-kinematics-manifest-sources.sh | 16 + list-linuxcnc-rs274-api-project-sources.sh | 11 +- list-linuxcnc-source-common-cxxflags.sh | 1 + list-linuxcnc-source-manifest-sources.sh | 2 +- list-linuxcnc-wasm-common-cxxflags.sh | 1 + list-linuxcnc-wasm-manifest-sources.sh | 2 +- list-project-source-set.sh | 76 + test-all-native.sh | 91 +- test-linuxcnc-source-link.sh | 2 + test-linuxcnc-source-syntax.sh | 4 +- test-linuxcnc-wasm-blockers.sh | 19 +- test-linuxcnc-wasm-cmake-safe-probe.sh | 181 ++- test-linuxcnc-wasm-python-c-api-symbols.sh | 128 ++ ...inuxcnc-wasm-rs274ngc-pre-link-blockers.sh | 10 +- test-linuxcnc-wasm-rs274ngc-pre-link.sh | 8 +- test-linuxcnc-wasm-runtime-link.sh | 65 + test-linuxcnc-wasm-tooldata-link.sh | 4 +- test-native.sh | 88 +- test-web-wasm-browser-smoke.sh | 110 ++ test-web-wasm-node-smoke.cjs | 507 ++++++ test-web-wasm-node-smoke.sh | 23 + web/index.html | 2 +- web/public/cnc_sim.js | 19 + web/src/index.ts | 47 +- web/src/wasm-core.d.ts | 12 +- web/src/wasm-core.js | 26 +- web/test-browser-wasm-smoke.html | 56 + web/tsconfig.json | 14 + 141 files changed, 11255 insertions(+), 1259 deletions(-) create mode 100755 check-linuxcnc-switchkins-remap-table.sh create mode 100644 core/src/linuxcnc_5axiskins_adapter.c create mode 100644 core/src/linuxcnc_5axiskins_adapter.cpp create mode 100644 core/src/linuxcnc_5axiskins_adapter.h create mode 100644 core/src/linuxcnc_corexykins_adapter.c create mode 100644 core/src/linuxcnc_corexykins_adapter.cpp create mode 100644 core/src/linuxcnc_corexykins_adapter.h create mode 100644 core/src/linuxcnc_cubic_adapter.c create mode 100644 core/src/linuxcnc_cubic_adapter.cpp create mode 100644 core/src/linuxcnc_cubic_adapter.h create mode 100644 core/src/linuxcnc_genhexkins_adapter.c create mode 100644 core/src/linuxcnc_genhexkins_adapter.cpp create mode 100644 core/src/linuxcnc_genhexkins_adapter.h create mode 100644 core/src/linuxcnc_genserfuncs_adapter.c create mode 100644 core/src/linuxcnc_genserfuncs_adapter.cpp create mode 100644 core/src/linuxcnc_genserfuncs_adapter.h create mode 100644 core/src/linuxcnc_kins_util_adapter.c create mode 100644 core/src/linuxcnc_kins_util_adapter.cpp create mode 100644 core/src/linuxcnc_kins_util_adapter.h create mode 100644 core/src/linuxcnc_lineardeltakins_adapter.c create mode 100644 core/src/linuxcnc_lineardeltakins_adapter.cpp create mode 100644 core/src/linuxcnc_lineardeltakins_adapter.h create mode 100644 core/src/linuxcnc_maxkins_adapter.c create mode 100644 core/src/linuxcnc_maxkins_adapter.cpp create mode 100644 core/src/linuxcnc_maxkins_adapter.h create mode 100644 core/src/linuxcnc_pentakins_adapter.c create mode 100644 core/src/linuxcnc_pentakins_adapter.cpp create mode 100644 core/src/linuxcnc_pentakins_adapter.h create mode 100644 core/src/linuxcnc_pumakins_adapter.c create mode 100644 core/src/linuxcnc_pumakins_adapter.cpp create mode 100644 core/src/linuxcnc_pumakins_adapter.h create mode 100644 core/src/linuxcnc_rosekins_adapter.c create mode 100644 core/src/linuxcnc_rosekins_adapter.cpp create mode 100644 core/src/linuxcnc_rosekins_adapter.h create mode 100644 core/src/linuxcnc_rotarydeltakins_adapter.c create mode 100644 core/src/linuxcnc_rotarydeltakins_adapter.cpp create mode 100644 core/src/linuxcnc_rotarydeltakins_adapter.h create mode 100644 core/src/linuxcnc_rotatekins_adapter.c create mode 100644 core/src/linuxcnc_rotatekins_adapter.cpp create mode 100644 core/src/linuxcnc_rotatekins_adapter.h create mode 100644 core/src/linuxcnc_scarakins_adapter.c create mode 100644 core/src/linuxcnc_scarakins_adapter.cpp create mode 100644 core/src/linuxcnc_scarakins_adapter.h create mode 100644 core/src/linuxcnc_scorbot_kins_adapter.c create mode 100644 core/src/linuxcnc_scorbot_kins_adapter.cpp create mode 100644 core/src/linuxcnc_scorbot_kins_adapter.h create mode 100644 core/src/linuxcnc_switchkins_remap_table.inc create mode 100644 core/src/linuxcnc_tripodkins_adapter.c create mode 100644 core/src/linuxcnc_tripodkins_adapter.cpp create mode 100644 core/src/linuxcnc_tripodkins_adapter.h create mode 100644 core/src/linuxcnc_trivkins_adapter.c create mode 100644 core/src/linuxcnc_trivkins_adapter.cpp create mode 100644 core/src/linuxcnc_trivkins_adapter.h create mode 100644 core/src/linuxcnc_trtfuncs_adapter.c create mode 100644 core/src/linuxcnc_trtfuncs_adapter.cpp create mode 100644 core/src/linuxcnc_trtfuncs_adapter.h create mode 100644 core/src/linuxcnc_userkfuncs_adapter.c create mode 100644 core/src/linuxcnc_userkfuncs_adapter.cpp create mode 100644 core/src/linuxcnc_userkfuncs_adapter.h create mode 100644 core/src/linuxcnc_xyzab_tdr_kins_adapter.c create mode 100644 core/src/linuxcnc_xyzab_tdr_kins_adapter.cpp create mode 100644 core/src/linuxcnc_xyzab_tdr_kins_adapter.h create mode 100644 core/wasm_shims/Python.h create mode 100644 core/wasm_shims/boost/noncopyable.hpp create mode 100644 core/wasm_shims/boost/python/class.hpp create mode 100644 core/wasm_shims/boost/python/converter/registry.hpp create mode 100644 core/wasm_shims/boost/python/def.hpp create mode 100644 core/wasm_shims/boost/python/detail/caller.hpp create mode 100644 core/wasm_shims/boost/python/enum.hpp create mode 100644 core/wasm_shims/boost/python/exception_translator.hpp create mode 100644 core/wasm_shims/boost/python/module.hpp create mode 100644 core/wasm_shims/boost/python/object/class_detail.hpp create mode 100644 core/wasm_shims/boost/python/return_internal_reference.hpp create mode 100644 core/wasm_shims/boost/python/suite/indexing/map_indexing_suite.hpp create mode 100644 core/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc create mode 100644 core/wasm_shims/libgen.h create mode 100644 core/wasm_shims/structmember.h create mode 100644 generate-linuxcnc-puma560-dh-parameters.sh create mode 100755 generate-linuxcnc-switchkins-remap-table.sh create mode 100644 linuxcnc-kinematics-source-files.txt create mode 100755 list-linuxcnc-kinematics-manifest-sources.sh create mode 100755 test-linuxcnc-wasm-python-c-api-symbols.sh create mode 100755 test-linuxcnc-wasm-runtime-link.sh create mode 100755 test-web-wasm-browser-smoke.sh create mode 100644 test-web-wasm-node-smoke.cjs create mode 100755 test-web-wasm-node-smoke.sh create mode 100644 web/public/cnc_sim.js create mode 100644 web/test-browser-wasm-smoke.html create mode 100644 web/tsconfig.json diff --git a/README.zh-CN.md b/README.zh-CN.md index aa3175e..a08a71b 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -116,19 +116,13 @@ The first useful target is: 公共 API 使用同一个入口解析程序,后端通过 JSON 配置选择: -```json -{"backend":"smoke"} -``` - -`smoke` 是默认后端,用于 wasm 和 UI 联调。 - -native 构建启用 LinuxCNC 后端后,可以使用: - ```json {"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 配置 diff --git a/analyze-linuxcnc-wasm-blockers.sh b/analyze-linuxcnc-wasm-blockers.sh index dcc84d7..1755ac7 100755 --- a/analyze-linuxcnc-wasm-blockers.sh +++ b/analyze-linuxcnc-wasm-blockers.sh @@ -22,18 +22,37 @@ 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 +} + core_source_output=$(./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core) mapfile -t core_sources <<<"$core_source_output" blocked_source_output=$(./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked) @@ -56,6 +75,10 @@ scan_source() { 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 diff --git a/build-wasm.sh b/build-wasm.sh index 43fb185..ebd311c 100755 --- a/build-wasm.sh +++ b/build-wasm.sh @@ -26,8 +26,10 @@ count_nonempty_lines() { } # 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 @@ -40,10 +42,11 @@ 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 25 || "$manifest_blocked_count" -ne 1 ]]; then +if [[ "$manifest" == "$default_manifest" ]] && [[ "$manifest_core_count" -ne 25 || "$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.sh "$manifest" --require-rs274-complete echo "LinuxCNC wasm blocker scan" ./test-linuxcnc-wasm-blockers.sh "$manifest" @@ -78,6 +81,9 @@ echo "LinuxCNC wasm interp_find link probe" echo "LinuxCNC wasm python_plugin link probe" ./test-linuxcnc-wasm-python-plugin-link.sh +echo "LinuxCNC wasm Python C API symbol probe" +./test-linuxcnc-wasm-python-c-api-symbols.sh + echo "LinuxCNC wasm rs274ngc_pre object probe" ./test-linuxcnc-wasm-rs274ngc-pre-object.sh @@ -87,8 +93,18 @@ echo "LinuxCNC wasm rs274ngc_pre link blocker scan" echo "LinuxCNC wasm rs274ngc_pre link probe" ./test-linuxcnc-wasm-rs274ngc-pre-link.sh "$manifest" -if ! command -v emcmake >/dev/null 2>&1; then - echo "Emscripten is required. Install/activate emsdk so emcmake and emcc are in PATH." >&2 +echo "LinuxCNC wasm runtime link probe" +./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 fi @@ -97,8 +113,12 @@ emcmake cmake -S core -B build/wasm \ -DCNC_SIM_LINUXCNC_ROOT="$linuxcnc_root" \ -DCNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST="$manifest" \ -DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON -cmake --build build/wasm - +cmake --build build/wasm --target cnc_sim_wasm_runtime_probe +cmake --build build/wasm --target cnc_sim_wasm mkdir -p web/public -cp build/wasm/cnc_sim.js web/public/ -cp build/wasm/cnc_sim.wasm web/public/ +cp build/wasm/cnc_sim.js web/public/cnc_sim.js +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 +node test-web-wasm-node-smoke.cjs +./test-web-wasm-browser-smoke.sh diff --git a/check-linuxcnc-inputs.sh b/check-linuxcnc-inputs.sh index 3f0c88b..e56ac96 100755 --- a/check-linuxcnc-inputs.sh +++ b/check-linuxcnc-inputs.sh @@ -5,10 +5,23 @@ cd "$(dirname "$0")" linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} manifest=${1:-} +require_complete=${2:-} if [[ "$manifest" == "--root-only" ]]; then manifest= fi +if [[ "$manifest" == "--require-rs274-complete" || "$manifest" == "--require-kinematics-complete" ]]; then + echo "usage: $0 [manifest] [--require-rs274-complete|--require-kinematics-complete]" >&2 + exit 1 +fi +case "$require_complete" in + ""|--require-rs274-complete|--require-kinematics-complete) + ;; + *) + echo "unknown check-linuxcnc-inputs option: $require_complete" >&2 + exit 1 + ;; +esac if [[ -n "$manifest" && ! -f "$manifest" ]]; then echo "missing manifest: $manifest" >&2 @@ -19,3 +32,137 @@ if [[ ! -d "$linuxcnc_root" ]]; then echo "missing LinuxCNC root: $linuxcnc_root" >&2 exit 1 fi + +if [[ -n "$manifest" && "$require_complete" == "--require-rs274-complete" ]]; then + rs274_dir="$linuxcnc_root/src/emc/rs274ngc" + if [[ ! -d "$rs274_dir" ]]; then + echo "missing LinuxCNC rs274ngc source directory: $rs274_dir" >&2 + exit 1 + fi + + mapfile -t rs274_sources < <( + find "$rs274_dir" -maxdepth 1 -type f \ + | sed "s#^$linuxcnc_root/##" \ + | sort + ) + mapfile -t manifest_rs274_sources < <( + awk -F: ' + $1 !~ /^($|#)/ && $2 ~ /^src\/emc\/rs274ngc\// { print $2 } + ' "$manifest" | sort -u + ) + + missing_rs274_sources=() + for source in "${rs274_sources[@]}"; do + if ! printf '%s\n' "${manifest_rs274_sources[@]}" | grep -Fx -- "$source" >/dev/null; then + missing_rs274_sources+=("$source") + fi + done + + if ((${#missing_rs274_sources[@]} > 0)); then + echo "manifest does not cover all LinuxCNC rs274ngc sources: $manifest" >&2 + printf 'missing rs274ngc source: %s\n' "${missing_rs274_sources[@]}" >&2 + exit 1 + fi +fi + +if [[ -n "$manifest" && "$require_complete" == "--require-kinematics-complete" ]]; then + kinematics_dir="$linuxcnc_root/src/emc/kinematics" + if [[ ! -d "$kinematics_dir" ]]; then + echo "missing LinuxCNC kinematics source directory: $kinematics_dir" >&2 + exit 1 + fi + + mapfile -t kinematics_sources < <( + find "$kinematics_dir" -maxdepth 1 -type f \ + | sed "s#^$linuxcnc_root/##" \ + | sort + ) + mapfile -t manifest_kinematics_sources < <( + awk -F: ' + $1 !~ /^($|#)/ && $2 ~ /^src\/emc\/kinematics\// { print $2 } + ' "$manifest" | sort -u + ) + + missing_kinematics_sources=() + for source in "${kinematics_sources[@]}"; do + if ! printf '%s\n' "${manifest_kinematics_sources[@]}" | grep -Fx -- "$source" >/dev/null; then + missing_kinematics_sources+=("$source") + fi + done + + if ((${#missing_kinematics_sources[@]} > 0)); then + echo "manifest does not cover all LinuxCNC kinematics sources: $manifest" >&2 + printf 'missing kinematics source: %s\n' "${missing_kinematics_sources[@]}" >&2 + exit 1 + fi + + mapfile -t m428_m430_remap_sources < <( + find "$linuxcnc_root/configs" -path '*/remap_subs/*' -type f \ + \( -name '428remap.ngc' -o -name '429remap.ngc' -o -name '430remap.ngc' \) \ + | sed "s#^$linuxcnc_root/##" \ + | sort + ) + mapfile -t manifest_m428_m430_remap_sources < <( + awk -F: ' + $1 !~ /^($|#)/ && $2 ~ /\/remap_subs\/(428|429|430)remap\.ngc$/ { print $2 } + ' "$manifest" | sort -u + ) + + missing_m428_m430_remap_sources=() + for source in "${m428_m430_remap_sources[@]}"; do + if ! printf '%s\n' "${manifest_m428_m430_remap_sources[@]}" | grep -Fx -- "$source" >/dev/null; then + missing_m428_m430_remap_sources+=("$source") + fi + done + + if ((${#missing_m428_m430_remap_sources[@]} > 0)); then + echo "manifest does not cover all LinuxCNC M428/M429/M430 remap sources: $manifest" >&2 + printf 'missing M428/M429/M430 remap source: %s\n' "${missing_m428_m430_remap_sources[@]}" >&2 + exit 1 + fi + + mismatched_m428_m430_remap_notes=() + for source in "${m428_m430_remap_sources[@]}"; do + kinstype=$(sed -n 's/^[[:space:]]*#[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$linuxcnc_root/$source" | head -n 1) + manifest_note=$(awk -F: -v path="$source" ' + $1 !~ /^($|#)/ && $2 == path { print $3; exit } + ' "$manifest") + if [[ -z "$kinstype" || "$manifest_note" != *"kinstype $kinstype"* ]]; then + mismatched_m428_m430_remap_notes+=("$source") + fi + done + + if ((${#mismatched_m428_m430_remap_notes[@]} > 0)); then + echo "manifest M428/M429/M430 remap notes do not match LinuxCNC # assignments: $manifest" >&2 + printf 'mismatched M428/M429/M430 remap note: %s\n' "${mismatched_m428_m430_remap_notes[@]}" >&2 + exit 1 + fi + + mapfile -t m428_m430_ini_sources < <( + find "$linuxcnc_root/configs" -name '*.ini' -type f -print0 \ + | while IFS= read -r -d '' source; do + if grep -Eiq 'REMAP[[:space:]]*=[[:space:]]*M(428|429|430)([^0-9]|$)' "$source"; then + printf '%s\n' "${source#"$linuxcnc_root/"}" + fi + done \ + | sort + ) + mapfile -t manifest_m428_m430_ini_sources < <( + awk -F: ' + $1 !~ /^($|#)/ && $1 == "config" && $2 ~ /\.ini$/ { print $2 } + ' "$manifest" | sort -u + ) + + missing_m428_m430_ini_sources=() + for source in "${m428_m430_ini_sources[@]}"; do + if ! printf '%s\n' "${manifest_m428_m430_ini_sources[@]}" | grep -Fx -- "$source" >/dev/null; then + missing_m428_m430_ini_sources+=("$source") + fi + done + + if ((${#missing_m428_m430_ini_sources[@]} > 0)); then + echo "manifest does not cover all LinuxCNC INI files that declare M428/M429/M430 remaps: $manifest" >&2 + printf 'missing M428/M429/M430 INI source: %s\n' "${missing_m428_m430_ini_sources[@]}" >&2 + exit 1 + fi +fi diff --git a/check-linuxcnc-switchkins-remap-table.sh b/check-linuxcnc-switchkins-remap-table.sh new file mode 100755 index 0000000..7ec6324 --- /dev/null +++ b/check-linuxcnc-switchkins-remap-table.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +manifest=${1:-linuxcnc-kinematics-source-files.txt} + +generated_table=$(mktemp "${TMPDIR:-/tmp}/linuxcnc_switchkins_remap_table.XXXXXX.inc") +trap 'rm -f "$generated_table"' EXIT + +LINUXCNC_ROOT="$linuxcnc_root" ./generate-linuxcnc-switchkins-remap-table.sh "$manifest" > "$generated_table" +diff -u core/src/linuxcnc_switchkins_remap_table.inc "$generated_table" diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 982443a..442b512 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -1,9 +1,10 @@ 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_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) 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" @@ -15,23 +16,71 @@ set(cnc_sim_core_sources src/canon_event_sink.cpp src/cnc_sim_api.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/simulator_gcode_controls.cpp +) + +set(cnc_sim_smoke_backend_sources 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_SMOKE_BACKEND) + message(FATAL_ERROR "CNC_SIM_ENABLE_SMOKE_BACKEND cannot be combined with CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND") + endif() if(EMSCRIPTEN) message(FATAL_ERROR "CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND is native-only; it links LinuxCNC librs274") endif() if(NOT CNC_SIM_LINUXCNC_ROOT) message(FATAL_ERROR "Set CNC_SIM_LINUXCNC_ROOT to the LinuxCNC source root") endif() - list(APPEND cnc_sim_core_sources - src/linuxcnc_canon_bridge.cpp - src/linuxcnc_rs274_backend.cpp - src/linuxcnc_tooldata_fixture.cpp - ) + list(APPEND cnc_sim_core_sources ${cnc_sim_linuxcnc_rs274_backend_sources}) +elseif(CNC_SIM_ENABLE_SMOKE_BACKEND) + list(APPEND cnc_sim_core_sources ${cnc_sim_smoke_backend_sources}) endif() if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE) @@ -67,7 +116,11 @@ if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE) set(manifest_group "${CMAKE_MATCH_1}") set(manifest_path "${CMAKE_MATCH_2}") - if(NOT manifest_group STREQUAL "core" AND NOT manifest_group STREQUAL "blocked") + 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() @@ -81,7 +134,7 @@ if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE) if(manifest_group STREQUAL "core") list(APPEND linuxcnc_wasm_safe_probe_sources "${manifest_source}") - else() + elseif(manifest_group STREQUAL "blocked") list(APPEND linuxcnc_wasm_blocked_sources "${manifest_source}") endif() endforeach() @@ -97,6 +150,17 @@ if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE) 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") + 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 @@ -115,7 +179,7 @@ if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE) ) 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 -include wctype.h) + target_compile_options(linuxcnc_rs274_wasm_safe_probe_objects PRIVATE ${cnc_sim_wasm_safe_preinclude_options}) target_include_directories(linuxcnc_rs274_wasm_safe_probe_objects PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include @@ -128,12 +192,55 @@ if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE) ${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(linuxcnc_rs274_wasm_safe_probe_project_sources ${CMAKE_CURRENT_SOURCE_DIR}/src/canon_event_sink.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_canon_bridge.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_rs274_backend.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_tooldata_fixture.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_5axiskins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_5axiskins_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_corexykins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_corexykins_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_cubic_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_cubic_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_genserfuncs_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_genserfuncs_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_genhexkins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_genhexkins_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_kins_util_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_kins_util_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_lineardeltakins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_lineardeltakins_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_maxkins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_maxkins_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_pentakins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_pentakins_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_pumakins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_pumakins_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_rotatekins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_rotatekins_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_rosekins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_rosekins_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_rotarydeltakins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_rotarydeltakins_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_scarakins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_scarakins_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_scorbot_kins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_scorbot_kins_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_tripodkins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_tripodkins_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_trtfuncs_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_trtfuncs_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_userkfuncs_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_userkfuncs_adapter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_xyzab_tdr_kins_adapter.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/linuxcnc_xyzab_tdr_kins_adapter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/rtcp_kinematics.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/simulator_gcode_controls.cpp ) add_executable(linuxcnc_rs274_wasm_safe_probe EXCLUDE_FROM_ALL @@ -142,8 +249,13 @@ if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE) ${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 ULAPI) - target_compile_options(linuxcnc_rs274_wasm_safe_probe PRIVATE -include wctype.h) + 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_include_directories(linuxcnc_rs274_wasm_safe_probe PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include @@ -156,7 +268,15 @@ if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE) ${CNC_SIM_LINUXCNC_ROOT_ABS}/src/emc/motion ${CNC_SIM_LINUXCNC_ROOT_ABS}/include ) - target_link_libraries(linuxcnc_rs274_wasm_safe_probe PRIVATE fmt) + 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() + + 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 @@ -165,6 +285,10 @@ if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE) endif() endif() +if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE) + list(APPEND cnc_sim_core_sources ${cnc_sim_linuxcnc_rs274_backend_sources}) +endif() + add_library(cnc_sim_objects OBJECT ${cnc_sim_core_sources} ) @@ -174,9 +298,36 @@ target_include_directories(cnc_sim_objects ${CMAKE_CURRENT_SOURCE_DIR}/include PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/../../linuxcnc/include ) 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 "$<$:-fpermissive>" +) if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND) find_package(Python3 REQUIRED COMPONENTS Development) @@ -196,6 +347,64 @@ if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND) ) 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(cnc_sim_wasm_runtime_probe EXCLUDE_FROM_ALL + $ + $ + ${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_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 $ ) @@ -225,11 +434,13 @@ if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND) endif() if(NOT EMSCRIPTEN) + if(CNC_SIM_ENABLE_SMOKE_BACKEND) add_executable(cnc_sim_api_smoke tests/cnc_sim_api_smoke.cpp ) target_link_libraries(cnc_sim_api_smoke PRIVATE cnc_sim_core) target_include_directories(cnc_sim_api_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) + endif() add_executable(canon_event_sink_smoke tests/canon_event_sink_smoke.cpp @@ -254,11 +465,13 @@ if(NOT EMSCRIPTEN) target_link_libraries(simulator_gcode_controls_smoke PRIVATE cnc_sim_core) target_include_directories(simulator_gcode_controls_smoke PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) + if(CNC_SIM_ENABLE_SMOKE_BACKEND) add_executable(cnc_sim_dump tools/cnc_sim_dump.cpp ) target_link_libraries(cnc_sim_dump PRIVATE cnc_sim_core) target_include_directories(cnc_sim_dump PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) + endif() if(CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND) add_executable(cnc_sim_api_linuxcnc_rs274_smoke @@ -298,7 +511,11 @@ if(CNC_SIM_ENABLE_LINUXCNC_BRIDGE) endif() 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 + $ $ ) @@ -306,7 +523,15 @@ if(EMSCRIPTEN) 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 ) + target_compile_definitions(cnc_sim_wasm PRIVATE FMT_HEADER_ONLY=1) set_target_properties(cnc_sim_wasm PROPERTIES OUTPUT_NAME "cnc_sim" diff --git a/core/src/canon_event_sink.cpp b/core/src/canon_event_sink.cpp index 74444ce..ed1a324 100644 --- a/core/src/canon_event_sink.cpp +++ b/core/src/canon_event_sink.cpp @@ -88,6 +88,13 @@ void CanonEventSink::configure_fiveaxis(double pivot_length, int kinematics_type 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) { if (h_code <= 0) { default_rtcp_tool_length_ = tool_length; @@ -151,24 +158,59 @@ void CanonEventSink::switch_kinematics(int kinematics_type, int line) { emit(event); } -void CanonEventSink::switch_m_code_kinematics(int m_code, int default_kinematics_type, int line) { +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_) { - // Source: linuxcnc/configs/sim/axis/vismach/5axis/bridgemill/remap_subs/{428,429,430}remap.ngc - // M428 selects 5axiskins type 0, M429 selects identity type 1, M430 selects userk type 2. + // 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; + return true; } if (m_code == 429) { switch_kinematics(1, line); - return; + return true; } if (m_code == 430) { switch_kinematics(2, line); - return; + return true; } } switch_kinematics(default_kinematics_type, line); + return true; } void CanonEventSink::set_g5x_offset(int index, const CncSimPose &offset, int line) { diff --git a/core/src/canon_event_sink.h b/core/src/canon_event_sink.h index 6f35278..6271530 100644 --- a/core/src/canon_event_sink.h +++ b/core/src/canon_event_sink.h @@ -16,12 +16,14 @@ public: void configure_xyzbc_trt(const LinuxCncXyzbcTrtParameters ¶meters); void configure_xyzac_trt(const LinuxCncXyzbcTrtParameters ¶meters); 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_offset(const CncSimPose &offset); void apply_tool_length_offset(const CncSimPose &offset); void set_rtcp_state(bool enabled, int h_code, int line); void switch_kinematics(int kinematics_type, int line); - void switch_m_code_kinematics(int m_code, int default_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_g92_offset(const CncSimPose &offset, int line); void set_xy_rotation(double angle_degrees, int line); @@ -108,6 +110,10 @@ private: 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_{}; double xy_rotation_degrees_ = 0.0; int kinematics_type_ = 0; diff --git a/core/src/cnc_sim_api.cpp b/core/src/cnc_sim_api.cpp index ed4fd02..40fe7a7 100644 --- a/core/src/cnc_sim_api.cpp +++ b/core/src/cnc_sim_api.cpp @@ -4,16 +4,13 @@ #include #include +#include #include #include struct CncSimHandle { CncSimDialect dialect = CNC_SIM_DIALECT_LINUXCNC; -#ifdef CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND GcodeBackendKind backend = GcodeBackendKind::LinuxCncRs274; -#else - GcodeBackendKind backend = GcodeBackendKind::Smoke; -#endif std::string last_error; CanonEventSink sink; }; @@ -77,6 +74,40 @@ bool contains_bool_value(const std::string &compact, const char *key, bool *valu 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 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, "config", alias) || + contains_string_value(compact, "configpath", alias) || + contains_string_value(compact, "config_path", alias); +} + bool find_number_value(const std::string &compact, const char *key, double *value) { const std::string needle = std::string("\"") + key + "\":"; const size_t pos = compact.find(needle); @@ -212,6 +243,38 @@ void apply_fiveaxis_config(CanonEventSink &sink, const std::string &compact) { } } +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 # 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) { if (json.empty()) { return 0; @@ -242,6 +305,7 @@ int apply_config(CncSimHandle *handle, const std::string &json) { handle->sink.set_block_delete(block_delete); } 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; diff --git a/core/src/gcode_backend.cpp b/core/src/gcode_backend.cpp index 7c4fc44..9b0b2c0 100644 --- a/core/src/gcode_backend.cpp +++ b/core/src/gcode_backend.cpp @@ -3,7 +3,9 @@ #ifdef CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND #include "linuxcnc_rs274_backend.h" #endif +#ifdef CNC_SIM_ENABLE_SMOKE_BACKEND #include "smoke_gcode_parser.h" +#endif const char *gcode_backend_name(GcodeBackendKind backend) { switch (backend) { @@ -23,8 +25,15 @@ int parse_gcode_with_backend(GcodeBackendKind backend, std::string *error) { switch (backend) { case GcodeBackendKind::Smoke: { +#ifdef CNC_SIM_ENABLE_SMOKE_BACKEND SmokeGcodeParser parser(sink); 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: #ifdef CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND diff --git a/core/src/linuxcnc_5axiskins_adapter.c b/core/src/linuxcnc_5axiskins_adapter.c new file mode 100644 index 0000000..64219d6 --- /dev/null +++ b/core/src/linuxcnc_5axiskins_adapter.c @@ -0,0 +1,266 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_5axiskins_adapter.cpp b/core/src/linuxcnc_5axiskins_adapter.cpp new file mode 100644 index 0000000..b93e276 --- /dev/null +++ b/core/src/linuxcnc_5axiskins_adapter.cpp @@ -0,0 +1,122 @@ +#include "linuxcnc_5axiskins_adapter.h" + +#include + +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; +} diff --git a/core/src/linuxcnc_5axiskins_adapter.h b/core/src/linuxcnc_5axiskins_adapter.h new file mode 100644 index 0000000..227d79a --- /dev/null +++ b/core/src/linuxcnc_5axiskins_adapter.h @@ -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); diff --git a/core/src/linuxcnc_corexykins_adapter.c b/core/src/linuxcnc_corexykins_adapter.c new file mode 100644 index 0000000..30e78a0 --- /dev/null +++ b/core/src/linuxcnc_corexykins_adapter.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_corexykins_adapter.cpp b/core/src/linuxcnc_corexykins_adapter.cpp new file mode 100644 index 0000000..4a34f7e --- /dev/null +++ b/core/src/linuxcnc_corexykins_adapter.cpp @@ -0,0 +1,104 @@ +#include "linuxcnc_corexykins_adapter.h" + +#include + +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; +} diff --git a/core/src/linuxcnc_corexykins_adapter.h b/core/src/linuxcnc_corexykins_adapter.h new file mode 100644 index 0000000..2cdd560 --- /dev/null +++ b/core/src/linuxcnc_corexykins_adapter.h @@ -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); diff --git a/core/src/linuxcnc_cubic_adapter.c b/core/src/linuxcnc_cubic_adapter.c new file mode 100644 index 0000000..ab31ab3 --- /dev/null +++ b/core/src/linuxcnc_cubic_adapter.c @@ -0,0 +1,117 @@ +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_cubic_adapter.cpp b/core/src/linuxcnc_cubic_adapter.cpp new file mode 100644 index 0000000..6e9e902 --- /dev/null +++ b/core/src/linuxcnc_cubic_adapter.cpp @@ -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; +} diff --git a/core/src/linuxcnc_cubic_adapter.h b/core/src/linuxcnc_cubic_adapter.h new file mode 100644 index 0000000..a41aa70 --- /dev/null +++ b/core/src/linuxcnc_cubic_adapter.h @@ -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(); diff --git a/core/src/linuxcnc_genhexkins_adapter.c b/core/src/linuxcnc_genhexkins_adapter.c new file mode 100644 index 0000000..2187e62 --- /dev/null +++ b/core/src/linuxcnc_genhexkins_adapter.c @@ -0,0 +1,223 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_genhexkins_adapter.cpp b/core/src/linuxcnc_genhexkins_adapter.cpp new file mode 100644 index 0000000..4b6ad4b --- /dev/null +++ b/core/src/linuxcnc_genhexkins_adapter.cpp @@ -0,0 +1,96 @@ +#include "linuxcnc_genhexkins_adapter.h" + +#include + +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; +} diff --git a/core/src/linuxcnc_genhexkins_adapter.h b/core/src/linuxcnc_genhexkins_adapter.h new file mode 100644 index 0000000..f213565 --- /dev/null +++ b/core/src/linuxcnc_genhexkins_adapter.h @@ -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); diff --git a/core/src/linuxcnc_genserfuncs_adapter.c b/core/src/linuxcnc_genserfuncs_adapter.c new file mode 100644 index 0000000..0d01001 --- /dev/null +++ b/core/src/linuxcnc_genserfuncs_adapter.c @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_genserfuncs_adapter.cpp b/core/src/linuxcnc_genserfuncs_adapter.cpp new file mode 100644 index 0000000..15086e0 --- /dev/null +++ b/core/src/linuxcnc_genserfuncs_adapter.cpp @@ -0,0 +1,166 @@ +#include "linuxcnc_genserfuncs_adapter.h" + +#include + +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 ¶meters, 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 ¶meters, + 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 ¶meters, + 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; +} diff --git a/core/src/linuxcnc_genserfuncs_adapter.h b/core/src/linuxcnc_genserfuncs_adapter.h new file mode 100644 index 0000000..1a4cef1 --- /dev/null +++ b/core/src/linuxcnc_genserfuncs_adapter.h @@ -0,0 +1,15 @@ +#pragma once + +#include "rtcp_kinematics.h" + +bool linuxcnc_genserfuncs_forward(const LinuxCncAxisJoints &joints, + const LinuxCncGenserParameters ¶meters, + 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 ¶meters, + int max_iterations, + LinuxCncAxisJoints *joints, + int *iterations); diff --git a/core/src/linuxcnc_kins_util_adapter.c b/core/src/linuxcnc_kins_util_adapter.c new file mode 100644 index 0000000..a1312cd --- /dev/null +++ b/core/src/linuxcnc_kins_util_adapter.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_kins_util_adapter.cpp b/core/src/linuxcnc_kins_util_adapter.cpp new file mode 100644 index 0000000..1ea5a05 --- /dev/null +++ b/core/src/linuxcnc_kins_util_adapter.cpp @@ -0,0 +1,121 @@ +#include "linuxcnc_kins_util_adapter.h" + +#include + +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; +} diff --git a/core/src/linuxcnc_kins_util_adapter.h b/core/src/linuxcnc_kins_util_adapter.h new file mode 100644 index 0000000..33b4a5f --- /dev/null +++ b/core/src/linuxcnc_kins_util_adapter.h @@ -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); diff --git a/core/src/linuxcnc_lineardeltakins_adapter.c b/core/src/linuxcnc_lineardeltakins_adapter.c new file mode 100644 index 0000000..76387fb --- /dev/null +++ b/core/src/linuxcnc_lineardeltakins_adapter.c @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_lineardeltakins_adapter.cpp b/core/src/linuxcnc_lineardeltakins_adapter.cpp new file mode 100644 index 0000000..c2f9e81 --- /dev/null +++ b/core/src/linuxcnc_lineardeltakins_adapter.cpp @@ -0,0 +1,138 @@ +#include "linuxcnc_lineardeltakins_adapter.h" + +#include + +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 ¶meters, + 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 ¶meters, + 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; +} diff --git a/core/src/linuxcnc_lineardeltakins_adapter.h b/core/src/linuxcnc_lineardeltakins_adapter.h new file mode 100644 index 0000000..9d6e9f3 --- /dev/null +++ b/core/src/linuxcnc_lineardeltakins_adapter.h @@ -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 ¶meters, + CncSimPose *pose); +bool linuxcnc_lineardeltakins_source_inverse(const CncSimPose &pose, + const LinuxCncLinearDeltaParameters ¶meters, + LinuxCncAxisJoints *joints); diff --git a/core/src/linuxcnc_maxkins_adapter.c b/core/src/linuxcnc_maxkins_adapter.c new file mode 100644 index 0000000..381853d --- /dev/null +++ b/core/src/linuxcnc_maxkins_adapter.c @@ -0,0 +1,168 @@ +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_maxkins_adapter.cpp b/core/src/linuxcnc_maxkins_adapter.cpp new file mode 100644 index 0000000..2bf81d2 --- /dev/null +++ b/core/src/linuxcnc_maxkins_adapter.cpp @@ -0,0 +1,140 @@ +#include "linuxcnc_maxkins_adapter.h" + +#include + +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 ¶meters, + 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 ¶meters, + 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; +} diff --git a/core/src/linuxcnc_maxkins_adapter.h b/core/src/linuxcnc_maxkins_adapter.h new file mode 100644 index 0000000..38e3fa6 --- /dev/null +++ b/core/src/linuxcnc_maxkins_adapter.h @@ -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 ¶meters, + CncSimPose *pose); +bool linuxcnc_maxkins_source_inverse(const CncSimPose &pose, + const LinuxCncMaxkinsParameters ¶meters, + LinuxCncAxisJoints *joints); diff --git a/core/src/linuxcnc_pentakins_adapter.c b/core/src/linuxcnc_pentakins_adapter.c new file mode 100644 index 0000000..df7e3ae --- /dev/null +++ b/core/src/linuxcnc_pentakins_adapter.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_pentakins_adapter.cpp b/core/src/linuxcnc_pentakins_adapter.cpp new file mode 100644 index 0000000..8b3a52a --- /dev/null +++ b/core/src/linuxcnc_pentakins_adapter.cpp @@ -0,0 +1,97 @@ +#include "linuxcnc_pentakins_adapter.h" + +#include + +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; +} diff --git a/core/src/linuxcnc_pentakins_adapter.h b/core/src/linuxcnc_pentakins_adapter.h new file mode 100644 index 0000000..de7a3a2 --- /dev/null +++ b/core/src/linuxcnc_pentakins_adapter.h @@ -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); diff --git a/core/src/linuxcnc_pumakins_adapter.c b/core/src/linuxcnc_pumakins_adapter.c new file mode 100644 index 0000000..a194a61 --- /dev/null +++ b/core/src/linuxcnc_pumakins_adapter.c @@ -0,0 +1,218 @@ +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/core/src/linuxcnc_pumakins_adapter.cpp b/core/src/linuxcnc_pumakins_adapter.cpp new file mode 100644 index 0000000..a4ecdd6 --- /dev/null +++ b/core/src/linuxcnc_pumakins_adapter.cpp @@ -0,0 +1,156 @@ +#include "linuxcnc_pumakins_adapter.h" + +#include + +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 ¶meters) { + 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 ¶meters, + 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 ¤t_joints, + const LinuxCncPumaParameters ¶meters, + 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; +} diff --git a/core/src/linuxcnc_pumakins_adapter.h b/core/src/linuxcnc_pumakins_adapter.h new file mode 100644 index 0000000..80ba72e --- /dev/null +++ b/core/src/linuxcnc_pumakins_adapter.h @@ -0,0 +1,14 @@ +#pragma once + +#include "rtcp_kinematics.h" + +bool linuxcnc_pumakins_forward(const LinuxCncAxisJoints &joints, + const LinuxCncPumaParameters ¶meters, + CncSimPose *pose, + int *iflags); +bool linuxcnc_pumakins_inverse(const CncSimPose &pose, + const LinuxCncAxisJoints ¤t_joints, + const LinuxCncPumaParameters ¶meters, + int iflags, + LinuxCncAxisJoints *joints, + int *fflags); diff --git a/core/src/linuxcnc_rosekins_adapter.c b/core/src/linuxcnc_rosekins_adapter.c new file mode 100644 index 0000000..0612e26 --- /dev/null +++ b/core/src/linuxcnc_rosekins_adapter.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_rosekins_adapter.cpp b/core/src/linuxcnc_rosekins_adapter.cpp new file mode 100644 index 0000000..f11856d --- /dev/null +++ b/core/src/linuxcnc_rosekins_adapter.cpp @@ -0,0 +1,121 @@ +#include "linuxcnc_rosekins_adapter.h" + +#include + +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; +} diff --git a/core/src/linuxcnc_rosekins_adapter.h b/core/src/linuxcnc_rosekins_adapter.h new file mode 100644 index 0000000..1b0e37d --- /dev/null +++ b/core/src/linuxcnc_rosekins_adapter.h @@ -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); diff --git a/core/src/linuxcnc_rotarydeltakins_adapter.c b/core/src/linuxcnc_rotarydeltakins_adapter.c new file mode 100644 index 0000000..ea55e6a --- /dev/null +++ b/core/src/linuxcnc_rotarydeltakins_adapter.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_rotarydeltakins_adapter.cpp b/core/src/linuxcnc_rotarydeltakins_adapter.cpp new file mode 100644 index 0000000..4bf50af --- /dev/null +++ b/core/src/linuxcnc_rotarydeltakins_adapter.cpp @@ -0,0 +1,157 @@ +#include "linuxcnc_rotarydeltakins_adapter.h" + +#include + +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 ¶meters, + 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 ¶meters, + 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; +} diff --git a/core/src/linuxcnc_rotarydeltakins_adapter.h b/core/src/linuxcnc_rotarydeltakins_adapter.h new file mode 100644 index 0000000..af8ec78 --- /dev/null +++ b/core/src/linuxcnc_rotarydeltakins_adapter.h @@ -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 ¶meters, + CncSimPose *pose); +bool linuxcnc_rotarydeltakins_source_inverse(const CncSimPose &pose, + const LinuxCncRotaryDeltaParameters ¶meters, + LinuxCncAxisJoints *joints); diff --git a/core/src/linuxcnc_rotatekins_adapter.c b/core/src/linuxcnc_rotatekins_adapter.c new file mode 100644 index 0000000..9fa11da --- /dev/null +++ b/core/src/linuxcnc_rotatekins_adapter.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_rotatekins_adapter.cpp b/core/src/linuxcnc_rotatekins_adapter.cpp new file mode 100644 index 0000000..113f299 --- /dev/null +++ b/core/src/linuxcnc_rotatekins_adapter.cpp @@ -0,0 +1,104 @@ +#include "linuxcnc_rotatekins_adapter.h" + +#include + +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; +} diff --git a/core/src/linuxcnc_rotatekins_adapter.h b/core/src/linuxcnc_rotatekins_adapter.h new file mode 100644 index 0000000..eb9a357 --- /dev/null +++ b/core/src/linuxcnc_rotatekins_adapter.h @@ -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); diff --git a/core/src/linuxcnc_rs274_backend.cpp b/core/src/linuxcnc_rs274_backend.cpp index f652a86..54c911a 100644 --- a/core/src/linuxcnc_rs274_backend.cpp +++ b/core/src/linuxcnc_rs274_backend.cpp @@ -1,6 +1,10 @@ #include "linuxcnc_rs274_backend.h" +#ifdef CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE +#include "boost/python/object.hpp" +#else #include +#endif #include "linuxcnc_canon_bridge.h" #include "linuxcnc_tooldata_fixture.h" @@ -16,11 +20,15 @@ #include #include #include +#ifndef CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE #include +#endif -int _task = 0; 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_emccanon(void); extern "C" struct _inittab builtin_modules[]; @@ -29,6 +37,7 @@ struct _inittab builtin_modules[] = { {"emccanon", PyInit_emccanon}, {nullptr, nullptr}, }; +#endif namespace { @@ -73,6 +82,7 @@ bool file_mode_enabled() { return std::getenv("CNC_SIM_RS274_FILE_MODE") != nullptr; } +#ifndef CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE bool write_temp_program(const char *program, size_t program_len, std::string *path, @@ -113,6 +123,7 @@ bool write_temp_program(const char *program, *path = tmpl_buffer.data(); return true; } +#endif int execute_file_mode(InterpBase *interp, CanonEventSink &sink, @@ -192,6 +203,15 @@ int parse_linuxcnc_rs274_backend(CanonEventSink &sink, SET_BLOCK_DELETE(sink.block_delete()); 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; if (!write_temp_program(program, program_len, &temp_path, error)) { interp->exit(); @@ -207,6 +227,7 @@ int parse_linuxcnc_rs274_backend(CanonEventSink &sink, cnc_sim_linuxcnc_set_canon_sink(nullptr); return -1; } +#endif } else { std::string source(program, program + program_len); std::istringstream input(source); @@ -219,19 +240,45 @@ int parse_linuxcnc_rs274_backend(CanonEventSink &sink, std::vector control_actions; if (parse_simulator_gcode_control_line(line, sink.block_delete(), &control_actions)) { bool simulator_only_line = true; + bool all_control_actions_handled = true; 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 && !action.rtcp_enabled) { simulator_only_line = false; } - 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 && control_actions.size() > 1) { + for (const auto &action : control_actions) { + if (!can_emit_simulator_gcode_control_action(sink, action) && + 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) { continue; } diff --git a/core/src/linuxcnc_scarakins_adapter.c b/core/src/linuxcnc_scarakins_adapter.c new file mode 100644 index 0000000..87833fe --- /dev/null +++ b/core/src/linuxcnc_scarakins_adapter.c @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/core/src/linuxcnc_scarakins_adapter.cpp b/core/src/linuxcnc_scarakins_adapter.cpp new file mode 100644 index 0000000..28b9adf --- /dev/null +++ b/core/src/linuxcnc_scarakins_adapter.cpp @@ -0,0 +1,144 @@ +#include "linuxcnc_scarakins_adapter.h" + +#include + +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 ¶meters) { + 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 ¶meters, + 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 ¶meters, + 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; +} diff --git a/core/src/linuxcnc_scarakins_adapter.h b/core/src/linuxcnc_scarakins_adapter.h new file mode 100644 index 0000000..d5f55a0 --- /dev/null +++ b/core/src/linuxcnc_scarakins_adapter.h @@ -0,0 +1,12 @@ +#pragma once + +#include "rtcp_kinematics.h" + +bool linuxcnc_scarakins_forward(const LinuxCncScaraJoints &joints, + const LinuxCncScaraParameters ¶meters, + CncSimPose *pose, + int *iflags); +bool linuxcnc_scarakins_inverse(const CncSimPose &pose, + const LinuxCncScaraParameters ¶meters, + int iflags, + LinuxCncScaraJoints *joints); diff --git a/core/src/linuxcnc_scorbot_kins_adapter.c b/core/src/linuxcnc_scorbot_kins_adapter.c new file mode 100644 index 0000000..9d80a0d --- /dev/null +++ b/core/src/linuxcnc_scorbot_kins_adapter.c @@ -0,0 +1,121 @@ +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_scorbot_kins_adapter.cpp b/core/src/linuxcnc_scorbot_kins_adapter.cpp new file mode 100644 index 0000000..fa39258 --- /dev/null +++ b/core/src/linuxcnc_scorbot_kins_adapter.cpp @@ -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; +} diff --git a/core/src/linuxcnc_scorbot_kins_adapter.h b/core/src/linuxcnc_scorbot_kins_adapter.h new file mode 100644 index 0000000..7ce6611 --- /dev/null +++ b/core/src/linuxcnc_scorbot_kins_adapter.h @@ -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); diff --git a/core/src/linuxcnc_switchkins_remap_table.inc b/core/src/linuxcnc_switchkins_remap_table.inc new file mode 100644 index 0000000..38090c7 --- /dev/null +++ b/core/src/linuxcnc_switchkins_remap_table.inc @@ -0,0 +1,110 @@ +// Generated by ./generate-linuxcnc-switchkins-remap-table.sh. +// Source: LinuxCNC INI MACHINE/KINEMATICS/HALFILE/SUBROUTINE_PATH/REMAP entries and +// adjacent remap_subs/{428,429,430}remap.ngc # 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}, + {"5axisgui.hal", 0, 1, 2}, + {"5axisgui", 0, 1, 2}, + {"configs/sim/axis/vismach/5axis/bridgemill/remap_subs", 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/remap_subs", 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/remap_subs", 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/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/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/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}, + {"kinematics.hal", 0, 1, 2}, + {"kinematics", 0, 1, 2}, + {"configs/sim/axis/vismach/hexapod-sim/remap_subs", 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}, + {"melfa_dh.hal", 0, 1, 2}, + {"melfa_dh", 0, 1, 2}, + {"configs/sim/axis/vismach/melfa-sim/remap_subs", 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}, + {"millturn.hal", 0, 1, -1}, + {"configs/sim/axis/vismach/millturn/remap_subs", 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}, + {"puma_dh.hal", 0, 1, 2}, + {"puma_dh", 0, 1, 2}, + {"configs/sim/axis/vismach/puma/remap_subs", 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}, + {"puma560_dh.hal", 0, 1, 2}, + {"puma560_dh", 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/remap_subs", 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", 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", 0, 1, 2}, diff --git a/core/src/linuxcnc_tripodkins_adapter.c b/core/src/linuxcnc_tripodkins_adapter.c new file mode 100644 index 0000000..7ff9551 --- /dev/null +++ b/core/src/linuxcnc_tripodkins_adapter.c @@ -0,0 +1,142 @@ +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_tripodkins_adapter.cpp b/core/src/linuxcnc_tripodkins_adapter.cpp new file mode 100644 index 0000000..6bcb6de --- /dev/null +++ b/core/src/linuxcnc_tripodkins_adapter.cpp @@ -0,0 +1,94 @@ +#include "linuxcnc_tripodkins_adapter.h" + +#include + +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; +} diff --git a/core/src/linuxcnc_tripodkins_adapter.h b/core/src/linuxcnc_tripodkins_adapter.h new file mode 100644 index 0000000..5d7e4c5 --- /dev/null +++ b/core/src/linuxcnc_tripodkins_adapter.h @@ -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); diff --git a/core/src/linuxcnc_trivkins_adapter.c b/core/src/linuxcnc_trivkins_adapter.c new file mode 100644 index 0000000..6c77409 --- /dev/null +++ b/core/src/linuxcnc_trivkins_adapter.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_trivkins_adapter.cpp b/core/src/linuxcnc_trivkins_adapter.cpp new file mode 100644 index 0000000..576c1e7 --- /dev/null +++ b/core/src/linuxcnc_trivkins_adapter.cpp @@ -0,0 +1,129 @@ +#include "linuxcnc_trivkins_adapter.h" + +#include + +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(); +} diff --git a/core/src/linuxcnc_trivkins_adapter.h b/core/src/linuxcnc_trivkins_adapter.h new file mode 100644 index 0000000..b3e94c1 --- /dev/null +++ b/core/src/linuxcnc_trivkins_adapter.h @@ -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(); diff --git a/core/src/linuxcnc_trtfuncs_adapter.c b/core/src/linuxcnc_trtfuncs_adapter.c new file mode 100644 index 0000000..3d9cc92 --- /dev/null +++ b/core/src/linuxcnc_trtfuncs_adapter.c @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_trtfuncs_adapter.cpp b/core/src/linuxcnc_trtfuncs_adapter.cpp new file mode 100644 index 0000000..95ca0f0 --- /dev/null +++ b/core/src/linuxcnc_trtfuncs_adapter.cpp @@ -0,0 +1,241 @@ +#include "linuxcnc_trtfuncs_adapter.h" + +#include + +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 ¶meters) { + 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 ¶meters, + 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 ¶meters, + 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 ¶meters, + 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 ¶meters, + 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; +} diff --git a/core/src/linuxcnc_trtfuncs_adapter.h b/core/src/linuxcnc_trtfuncs_adapter.h new file mode 100644 index 0000000..b67f874 --- /dev/null +++ b/core/src/linuxcnc_trtfuncs_adapter.h @@ -0,0 +1,16 @@ +#pragma once + +#include "rtcp_kinematics.h" + +bool linuxcnc_trtfuncs_xyzbc_forward(const LinuxCncFiveAxisJoints &joints, + const LinuxCncXyzbcTrtParameters ¶meters, + CncSimPose *pose); +bool linuxcnc_trtfuncs_xyzbc_inverse(const CncSimPose &pose, + const LinuxCncXyzbcTrtParameters ¶meters, + LinuxCncFiveAxisJoints *joints); +bool linuxcnc_trtfuncs_xyzac_forward(const LinuxCncFiveAxisJoints &joints, + const LinuxCncXyzbcTrtParameters ¶meters, + CncSimPose *pose); +bool linuxcnc_trtfuncs_xyzac_inverse(const CncSimPose &pose, + const LinuxCncXyzbcTrtParameters ¶meters, + LinuxCncFiveAxisJoints *joints); diff --git a/core/src/linuxcnc_userkfuncs_adapter.c b/core/src/linuxcnc_userkfuncs_adapter.c new file mode 100644 index 0000000..6e46b01 --- /dev/null +++ b/core/src/linuxcnc_userkfuncs_adapter.c @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_userkfuncs_adapter.cpp b/core/src/linuxcnc_userkfuncs_adapter.cpp new file mode 100644 index 0000000..22e429a --- /dev/null +++ b/core/src/linuxcnc_userkfuncs_adapter.cpp @@ -0,0 +1,113 @@ +#include "linuxcnc_userkfuncs_adapter.h" + +#include + +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; +} diff --git a/core/src/linuxcnc_userkfuncs_adapter.h b/core/src/linuxcnc_userkfuncs_adapter.h new file mode 100644 index 0000000..c5545dc --- /dev/null +++ b/core/src/linuxcnc_userkfuncs_adapter.h @@ -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); diff --git a/core/src/linuxcnc_xyzab_tdr_kins_adapter.c b/core/src/linuxcnc_xyzab_tdr_kins_adapter.c new file mode 100644 index 0000000..98e2681 --- /dev/null +++ b/core/src/linuxcnc_xyzab_tdr_kins_adapter.c @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/core/src/linuxcnc_xyzab_tdr_kins_adapter.cpp b/core/src/linuxcnc_xyzab_tdr_kins_adapter.cpp new file mode 100644 index 0000000..9ccfdbf --- /dev/null +++ b/core/src/linuxcnc_xyzab_tdr_kins_adapter.cpp @@ -0,0 +1,177 @@ +#include "linuxcnc_xyzab_tdr_kins_adapter.h" + +#include + +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 ¶meters) { + 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 ¶meters, + 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 ¶meters, + 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; +} diff --git a/core/src/linuxcnc_xyzab_tdr_kins_adapter.h b/core/src/linuxcnc_xyzab_tdr_kins_adapter.h new file mode 100644 index 0000000..b9419f3 --- /dev/null +++ b/core/src/linuxcnc_xyzab_tdr_kins_adapter.h @@ -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 ¶meters, + int switchkins_type, + CncSimPose *pose); +bool linuxcnc_xyzab_tdr_kins_inverse(const CncSimPose &pose, + const LinuxCncXyzabTdrParameters ¶meters, + int switchkins_type, + LinuxCncAxisJoints *joints); diff --git a/core/src/rtcp_kinematics.cpp b/core/src/rtcp_kinematics.cpp index 0030307..cb06e4f 100644 --- a/core/src/rtcp_kinematics.cpp +++ b/core/src/rtcp_kinematics.cpp @@ -1,540 +1,23 @@ #include "rtcp_kinematics.h" -#include - -namespace { - -constexpr double kPi = 3.141592653589793238462643383279502884; -constexpr double kRpyPitchFuzz = 1e-6; -constexpr double kGoRealEpsilon = 1e-6; -constexpr double kGoSingularEpsilon = 1.0e-15; - -struct RotationMatrix { - RtcpVector x; - RtcpVector y; - RtcpVector z; -}; - -struct HomogeneousTransform { - RotationMatrix rot; - RtcpVector tran; -}; - -struct Quaternion { - double s; - double x; - double y; - double z; -}; - -double radians(double degrees) { - return degrees * kPi / 180.0; -} - -double degrees(double radians_value) { - return radians_value * 180.0 / kPi; -} - -RtcpVector spherical_to_cartesian(double radius, double theta_deg, double phi_deg) { - const double theta = radians(theta_deg); - const double phi = radians(phi_deg); - return { - radius * std::sin(phi) * std::cos(theta), - radius * std::sin(phi) * std::sin(theta), - radius * std::cos(phi), - }; -} - -RotationMatrix multiply_rotation(const RotationMatrix &lhs, const RotationMatrix &rhs) { - RotationMatrix result{}; - result.x.x = lhs.x.x * rhs.x.x + lhs.y.x * rhs.x.y + lhs.z.x * rhs.x.z; - result.y.x = lhs.x.x * rhs.y.x + lhs.y.x * rhs.y.y + lhs.z.x * rhs.y.z; - result.z.x = lhs.x.x * rhs.z.x + lhs.y.x * rhs.z.y + lhs.z.x * rhs.z.z; - - result.x.y = lhs.x.y * rhs.x.x + lhs.y.y * rhs.x.y + lhs.z.y * rhs.x.z; - result.y.y = lhs.x.y * rhs.y.x + lhs.y.y * rhs.y.y + lhs.z.y * rhs.y.z; - result.z.y = lhs.x.y * rhs.z.x + lhs.y.y * rhs.z.y + lhs.z.y * rhs.z.z; - - result.x.z = lhs.x.z * rhs.x.x + lhs.y.z * rhs.x.y + lhs.z.z * rhs.x.z; - result.y.z = lhs.x.z * rhs.y.x + lhs.y.z * rhs.y.y + lhs.z.z * rhs.y.z; - result.z.z = lhs.x.z * rhs.z.x + lhs.y.z * rhs.z.y + lhs.z.z * rhs.z.z; - return result; -} - -RtcpVector rotate_vector(const RotationMatrix &matrix, const RtcpVector &vector) { - return { - matrix.x.x * vector.x + matrix.y.x * vector.y + matrix.z.x * vector.z, - matrix.x.y * vector.x + matrix.y.y * vector.y + matrix.z.y * vector.z, - matrix.x.z * vector.x + matrix.y.z * vector.y + matrix.z.z * vector.z, - }; -} - -HomogeneousTransform multiply_transform(const HomogeneousTransform &lhs, - const HomogeneousTransform &rhs) { - HomogeneousTransform result{}; - result.rot = multiply_rotation(lhs.rot, rhs.rot); - const RtcpVector rotated = rotate_vector(lhs.rot, rhs.tran); - result.tran.x = lhs.tran.x + rotated.x; - result.tran.y = lhs.tran.y + rotated.y; - result.tran.z = lhs.tran.z + rotated.z; - return result; -} - -double square(double value) { - return value * value; -} - -bool go_small(double value) { - return std::fabs(value) < kGoRealEpsilon; -} - -RtcpVector add_vector(const RtcpVector &lhs, const RtcpVector &rhs) { - return { - lhs.x + rhs.x, - lhs.y + rhs.y, - lhs.z + rhs.z, - }; -} - -RtcpVector cross_vector(const RtcpVector &lhs, const RtcpVector &rhs) { - return { - lhs.y * rhs.z - lhs.z * rhs.y, - lhs.z * rhs.x - lhs.x * rhs.z, - lhs.x * rhs.y - lhs.y * rhs.x, - }; -} - -// Source: linuxcnc/src/libnml/posemath/_posemath.c -// Mirrors pmRpyMatConvert(). -RotationMatrix posemath_rpy_to_matrix(double roll, double pitch, double yaw) { - const double sa = std::sin(yaw); - const double sb = std::sin(pitch); - const double sg = std::sin(roll); - const double ca = std::cos(yaw); - const double cb = std::cos(pitch); - const double cg = std::cos(roll); - - RotationMatrix matrix{}; - matrix.x.x = ca * cb; - matrix.y.x = ca * sb * sg - sa * cg; - matrix.z.x = ca * sb * cg + sa * sg; - - matrix.x.y = sa * cb; - matrix.y.y = sa * sb * sg + ca * cg; - matrix.z.y = sa * sb * cg - ca * sg; - - matrix.x.z = -sb; - matrix.y.z = cb * sg; - matrix.z.z = cb * cg; - return matrix; -} - -// Source: linuxcnc/src/libnml/posemath/_posemath.c -// Mirrors pmMatRpyConvert(). -CncSimPose posemath_matrix_to_pose(const RtcpVector &tran, const RotationMatrix &matrix) { - CncSimPose pose{}; - pose.x = tran.x; - pose.y = tran.y; - pose.z = tran.z; - - const double pitch = std::atan2(-matrix.x.z, - std::sqrt(square(matrix.x.x) + square(matrix.x.y))); - double roll = 0.0; - double yaw = 0.0; - if (std::fabs(pitch - kPi / 2.0) < kRpyPitchFuzz) { - roll = std::atan2(matrix.y.x, matrix.y.y); - pose.b = 90.0; - } else if (std::fabs(pitch + kPi / 2.0) < kRpyPitchFuzz) { - roll = -std::atan2(matrix.y.x, matrix.y.y); - pose.b = -90.0; - } else { - roll = std::atan2(matrix.y.z, matrix.z.z); - yaw = std::atan2(matrix.x.y, matrix.x.x); - pose.b = degrees(pitch); - } - pose.a = degrees(roll); - pose.c = degrees(yaw); - return pose; -} - -// Source: linuxcnc/src/libnml/posemath/gomath.c -// Mirrors go_mat_quat_convert() for normalized rotation matrices. -Quaternion linuxcnc_matrix_to_quaternion(const RotationMatrix &matrix) { - Quaternion q{}; - double discr = 1.0 + matrix.x.x + matrix.y.y + matrix.z.z; - if (discr < 0.0) { - discr = 0.0; - } - q.s = 0.5 * std::sqrt(discr); - if (go_small(q.s)) { - q.s = 0.0; - discr = 1.0 + matrix.x.x - matrix.y.y - matrix.z.z; - if (discr < 0.0) { - discr = 0.0; - } - q.x = std::sqrt(discr) / 2.0; - discr = 1.0 + matrix.y.y - matrix.x.x - matrix.z.z; - if (discr < 0.0) { - discr = 0.0; - } - q.y = std::sqrt(discr) / 2.0; - discr = 1.0 + matrix.z.z - matrix.y.y - matrix.x.x; - if (discr < 0.0) { - discr = 0.0; - } - q.z = std::sqrt(discr) / 2.0; - - if (q.x > q.y && q.x > q.z) { - if (matrix.x.y < 0.0) { - q.y *= -1.0; - } - if (matrix.x.z < 0.0) { - q.z *= -1.0; - } - } else if (q.y > q.z) { - if (matrix.x.y < 0.0) { - q.x *= -1.0; - } - if (matrix.y.z < 0.0) { - q.z *= -1.0; - } - } else { - if (matrix.x.z < 0.0) { - q.x *= -1.0; - } - if (matrix.y.z < 0.0) { - q.y *= -1.0; - } - } - } else { - const double a = 4.0 * q.s; - q.x = (matrix.y.z - matrix.z.y) / a; - q.y = (matrix.z.x - matrix.x.z) / a; - q.z = (matrix.x.y - matrix.y.x) / a; - } - - const double size = std::sqrt(square(q.s) + square(q.x) + square(q.y) + square(q.z)); - if (go_small(size)) { - return {1.0, 0.0, 0.0, 0.0}; - } - const double inv_size = 1.0 / size; - if (q.s >= 0.0) { - q.s *= inv_size; - q.x *= inv_size; - q.y *= inv_size; - q.z *= inv_size; - } else { - q.s *= -inv_size; - q.x *= -inv_size; - q.y *= -inv_size; - q.z *= -inv_size; - } - return q; -} - -// Source: linuxcnc/src/libnml/posemath/gomath.c -// Mirrors go_quat_mat_convert(). -RotationMatrix linuxcnc_quaternion_to_matrix(const Quaternion &q) { - RotationMatrix matrix{}; - matrix.x.x = 1.0 - 2.0 * (square(q.y) + square(q.z)); - matrix.y.x = 2.0 * (q.x * q.y - q.z * q.s); - matrix.z.x = 2.0 * (q.z * q.x + q.y * q.s); - - matrix.x.y = 2.0 * (q.x * q.y + q.z * q.s); - matrix.y.y = 1.0 - 2.0 * (square(q.z) + square(q.x)); - matrix.z.y = 2.0 * (q.y * q.z - q.x * q.s); - - matrix.x.z = 2.0 * (q.z * q.x - q.y * q.s); - matrix.y.z = 2.0 * (q.y * q.z + q.x * q.s); - matrix.z.z = 1.0 - 2.0 * (square(q.x) + square(q.y)); - return matrix; -} - -// Source: linuxcnc/src/libnml/posemath/gomath.c -// Mirrors go_quat_inv(). -Quaternion linuxcnc_quaternion_inverse(const Quaternion &q) { - return {q.s, -q.x, -q.y, -q.z}; -} - -// Source: linuxcnc/src/libnml/posemath/gomath.c -// Mirrors go_quat_quat_mult(). -Quaternion linuxcnc_multiply_quaternion(const Quaternion &lhs, const Quaternion &rhs) { - Quaternion q{}; - q.s = lhs.s * rhs.s - lhs.x * rhs.x - lhs.y * rhs.y - lhs.z * rhs.z; - if (q.s >= 0.0) { - q.x = lhs.s * rhs.x + lhs.x * rhs.s + lhs.y * rhs.z - lhs.z * rhs.y; - q.y = lhs.s * rhs.y - lhs.x * rhs.z + lhs.y * rhs.s + lhs.z * rhs.x; - q.z = lhs.s * rhs.z + lhs.x * rhs.y - lhs.y * rhs.x + lhs.z * rhs.s; - } else { - q.s = -q.s; - q.x = -lhs.s * rhs.x - lhs.x * rhs.s - lhs.y * rhs.z + lhs.z * rhs.y; - q.y = -lhs.s * rhs.y + lhs.x * rhs.z - lhs.y * rhs.s - lhs.z * rhs.x; - q.z = -lhs.s * rhs.z - lhs.x * rhs.y + lhs.y * rhs.x - lhs.z * rhs.s; - } - return q; -} - -// Source: linuxcnc/src/libnml/posemath/gomath.c -// Mirrors go_quat_cart_mult(). -RtcpVector linuxcnc_quaternion_rotate_vector(const Quaternion &q, const RtcpVector &vector) { - const RtcpVector c{ - q.y * vector.z - q.z * vector.y, - q.z * vector.x - q.x * vector.z, - q.x * vector.y - q.y * vector.x, - }; - return { - vector.x + 2.0 * (q.s * c.x + q.y * c.z - q.z * c.y), - vector.y + 2.0 * (q.s * c.y + q.z * c.x - q.x * c.z), - vector.z + 2.0 * (q.s * c.z + q.x * c.y - q.y * c.x), - }; -} - -// Source: linuxcnc/src/libnml/posemath/gomath.c -// Mirrors go_quat_rvec_convert(). -RtcpVector linuxcnc_quaternion_to_rvec(const Quaternion &q) { - const double sh = std::sqrt(square(q.x) + square(q.y) + square(q.z)); - if (go_small(sh)) { - return {}; - } - const double mag = 2.0 * std::atan2(sh, q.s) / sh; - return { - mag * q.x, - mag * q.y, - mag * q.z, - }; -} - -// Source: linuxcnc/src/libnml/posemath/gomath.c -// Mirrors go_dh_pose_convert(). -HomogeneousTransform linuxcnc_genser_dh_transform(double a, double alpha, double d, double theta) { - const double sth = std::sin(theta); - const double cth = std::cos(theta); - const double sal = std::sin(alpha); - const double cal = std::cos(alpha); - - HomogeneousTransform transform{}; - transform.rot.x.x = cth; - transform.rot.y.x = -sth; - transform.rot.z.x = 0.0; - transform.rot.x.y = sth * cal; - transform.rot.y.y = cth * cal; - transform.rot.z.y = -sal; - transform.rot.x.z = sth * sal; - transform.rot.y.z = cth * sal; - transform.rot.z.z = cal; - transform.tran.x = a; - transform.tran.y = -sal * d; - transform.tran.z = cal * d; - return transform; -} - -HomogeneousTransform identity_transform() { - HomogeneousTransform transform{}; - transform.rot.x.x = 1.0; - transform.rot.y.y = 1.0; - transform.rot.z.z = 1.0; - return transform; -} - -HomogeneousTransform linuxcnc_genser_build_pose(const double joint_radians[6], - const LinuxCncGenserParameters ¶meters) { - HomogeneousTransform total = identity_transform(); - for (int i = 0; i < 6; ++i) { - const HomogeneousTransform link = - linuxcnc_genser_dh_transform(parameters.a[i], - parameters.alpha[i], - parameters.d[i], - joint_radians[i]); - total = multiply_transform(total, link); - } - return total; -} - -// Source: linuxcnc/src/libnml/posemath/gomath.c -// Mirrors go_pose_inv(). -HomogeneousTransform linuxcnc_pose_inverse(const HomogeneousTransform &pose) { - HomogeneousTransform inverse{}; - const Quaternion q = linuxcnc_matrix_to_quaternion(pose.rot); - const Quaternion q_inv = linuxcnc_quaternion_inverse(q); - inverse.rot = linuxcnc_quaternion_to_matrix(q_inv); - const RtcpVector rotated = linuxcnc_quaternion_rotate_vector(q_inv, pose.tran); - inverse.tran.x = -rotated.x; - inverse.tran.y = -rotated.y; - inverse.tran.z = -rotated.z; - return inverse; -} - -bool invert_6x6(const double input[6][6], double inverse[6][6]) { - double lu[6][6] = {}; - double scratchrow[6] = {}; - int index[6] = {}; - double d = 1.0; - - // Source: linuxcnc/src/libnml/posemath/gomath.c - // Mirrors ludcmp(). - for (int i = 0; i < 6; ++i) { - double big = 0.0; - for (int j = 0; j < 6; ++j) { - lu[i][j] = input[i][j]; - const double temp = std::fabs(lu[i][j]); - if (temp > big) { - big = temp; - } - } - if (big < kGoSingularEpsilon) { - return false; - } - scratchrow[i] = 1.0 / big; - } - - for (int j = 0; j < 6; ++j) { - for (int i = 0; i < j; ++i) { - double sum = lu[i][j]; - for (int k = 0; k < i; ++k) { - sum -= lu[i][k] * lu[k][j]; - } - lu[i][j] = sum; - } - double big = 0.0; - int imax = 0; - for (int i = j; i < 6; ++i) { - double sum = lu[i][j]; - for (int k = 0; k < j; ++k) { - sum -= lu[i][k] * lu[k][j]; - } - lu[i][j] = sum; - const double dum = scratchrow[i] * std::fabs(sum); - if (dum >= big) { - big = dum; - imax = i; - } - } - if (j != imax) { - for (int k = 0; k < 6; ++k) { - const double dum = lu[imax][k]; - lu[imax][k] = lu[j][k]; - lu[j][k] = dum; - } - d = -d; - scratchrow[imax] = scratchrow[j]; - } - index[j] = imax; - if (std::fabs(lu[j][j]) < kGoSingularEpsilon) { - return false; - } - if (j != 5) { - const double dum = 1.0 / lu[j][j]; - for (int i = j + 1; i < 6; ++i) { - lu[i][j] *= dum; - } - } - } - (void)d; - - // Source: linuxcnc/src/libnml/posemath/gomath.c - // Mirrors go_matrix_inv() and lubksb(). - for (int col = 0; col < 6; ++col) { - double v[6] = {}; - v[col] = 1.0; - int ii = -1; - for (int i = 0; i < 6; ++i) { - const int ip = index[i]; - double sum = v[ip]; - v[ip] = v[i]; - if (ii != -1) { - for (int j = ii; j <= i - 1; ++j) { - sum -= lu[i][j] * v[j]; - } - } else if (sum) { - ii = i; - } - v[i] = sum; - } - for (int i = 5; i >= 0; --i) { - double sum = v[i]; - for (int j = i + 1; j < 6; ++j) { - sum -= lu[i][j] * v[j]; - } - if (std::fabs(lu[i][i]) < kGoSingularEpsilon) { - return false; - } - v[i] = sum / lu[i][i]; - } - for (int row = 0; row < 6; ++row) { - inverse[row][col] = v[row]; - } - } - return true; -} - -void multiply_6x6_vector(const double matrix[6][6], const double vector[6], double output[6]) { - // Source: linuxcnc/src/libnml/posemath/gomath.c - // Mirrors go_matrix_vector_mult(). - for (int row = 0; row < 6; ++row) { - output[row] = 0.0; - for (int col = 0; col < 6; ++col) { - output[row] += matrix[row][col] * vector[col]; - } - } -} - -bool linuxcnc_genser_compute_jfwd(const double joint_radians[6], - const LinuxCncGenserParameters ¶meters, - double jfwd[6][6], - HomogeneousTransform *t_l_0) { - // Source: linuxcnc/src/emc/kinematics/genserfuncs.c - // Mirrors compute_jfwd() for six DH angular joints. - RtcpVector jv[6] = {}; - RtcpVector jw[6] = {}; - jw[0].z = 1.0; - - HomogeneousTransform pose = - linuxcnc_genser_dh_transform(parameters.a[0], - parameters.alpha[0], - parameters.d[0], - joint_radians[0]); - *t_l_0 = pose; - - for (int col = 1; col < 6; ++col) { - pose = linuxcnc_genser_dh_transform(parameters.a[col], - parameters.alpha[col], - parameters.d[col], - joint_radians[col]); - const Quaternion q = linuxcnc_matrix_to_quaternion(pose.rot); - const RotationMatrix r_i_ip1 = linuxcnc_quaternion_to_matrix(linuxcnc_quaternion_inverse(q)); - - RtcpVector scratch[6] = {}; - for (int i = 0; i < 6; ++i) { - scratch[i] = add_vector(jv[i], cross_vector(jw[i], pose.tran)); - jv[i] = rotate_vector(r_i_ip1, scratch[i]); - jw[i] = rotate_vector(r_i_ip1, jw[i]); - } - jv[col] = {}; - jw[col] = {0.0, 0.0, 1.0}; - *t_l_0 = multiply_transform(*t_l_0, pose); - } - - const RotationMatrix r_inv = t_l_0->rot; - for (int col = 0; col < 6; ++col) { - jv[col] = rotate_vector(r_inv, jv[col]); - jw[col] = rotate_vector(r_inv, jw[col]); - jfwd[0][col] = jv[col].x; - jfwd[1][col] = jv[col].y; - jfwd[2][col] = jv[col].z; - jfwd[3][col] = jw[col].x; - jfwd[4][col] = jw[col].y; - jfwd[5][col] = jw[col].z; - } - return true; -} - -bool linuxcnc_genser_compute_jinv(const double jfwd[6][6], double jinv[6][6]) { - // Source: linuxcnc/src/emc/kinematics/genserfuncs.c - // compute_jinv() uses go_matrix_inv() directly for square 6x6 Jacobians. - return invert_6x6(jfwd, jinv); -} - -} // namespace +#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" LinuxCncAxisJoints linuxcnc_identity_default_joints() { return {}; @@ -544,15 +27,7 @@ LinuxCncAxisJoints linuxcnc_identity_default_joints() { // Mirrors identityKinematicsForward() with default XYZABCUVW coordinate mapping. CncSimPose linuxcnc_identity_forward(const LinuxCncAxisJoints &joints) { CncSimPose pose{}; - pose.x = joints.x; - pose.y = joints.y; - pose.z = joints.z; - pose.a = joints.a; - pose.b = joints.b; - pose.c = joints.c; - pose.u = joints.u; - pose.v = joints.v; - pose.w = joints.w; + linuxcnc_kins_util_identity_forward(joints, &pose); return pose; } @@ -560,84 +35,214 @@ CncSimPose linuxcnc_identity_forward(const LinuxCncAxisJoints &joints) { // Mirrors identityKinematicsInverse() with default XYZABCUVW coordinate mapping. LinuxCncAxisJoints linuxcnc_identity_inverse(const CncSimPose &pose) { LinuxCncAxisJoints joints{}; - joints.x = pose.x; - joints.y = pose.y; - joints.z = pose.z; - joints.a = pose.a; - joints.b = pose.b; - joints.c = pose.c; - joints.u = pose.u; - joints.v = pose.v; - joints.w = pose.w; + linuxcnc_kins_util_identity_inverse(pose, &joints); return joints; } // Source: linuxcnc/src/emc/kinematics/trivkins.c // kinematicsForward()/kinematicsInverse() delegate to identityKinematics*(). +// Source: linuxcnc/src/emc/kinematics/corexykins.c +// Mirrors kinematicsForward(). +CncSimPose linuxcnc_corexykins_forward(const LinuxCncAxisJoints &joints) { + CncSimPose pose{}; + linuxcnc_corexykins_source_forward(joints, &pose); + return pose; +} + +// Source: linuxcnc/src/emc/kinematics/corexykins.c +// Mirrors kinematicsInverse(). +LinuxCncAxisJoints linuxcnc_corexykins_inverse(const CncSimPose &pose) { + LinuxCncAxisJoints joints{}; + linuxcnc_corexykins_source_inverse(pose, &joints); + return joints; +} + // Source: linuxcnc/src/emc/kinematics/rotatekins.c // Mirrors kinematicsForward(). CncSimPose linuxcnc_rotatekins_forward(const LinuxCncAxisJoints &joints) { - const double c_rad = -radians(joints.c); CncSimPose pose{}; - pose.x = joints.x * std::cos(c_rad) - joints.y * std::sin(c_rad); - pose.y = joints.x * std::sin(c_rad) + joints.y * std::cos(c_rad); - pose.z = joints.z; - pose.a = joints.a; - pose.b = joints.b; - pose.c = joints.c; - pose.u = joints.u; - pose.v = joints.v; - pose.w = joints.w; + linuxcnc_rotatekins_source_forward(joints, &pose); return pose; } // Source: linuxcnc/src/emc/kinematics/rotatekins.c // Mirrors kinematicsInverse(). LinuxCncAxisJoints linuxcnc_rotatekins_inverse(const CncSimPose &pose) { - const double c_rad = radians(pose.c); LinuxCncAxisJoints joints{}; - joints.x = pose.x * std::cos(c_rad) - pose.y * std::sin(c_rad); - joints.y = pose.x * std::sin(c_rad) + pose.y * std::cos(c_rad); - joints.z = pose.z; - joints.a = pose.a; - joints.b = pose.b; - joints.c = pose.c; - joints.u = pose.u; - joints.v = pose.v; - joints.w = pose.w; + linuxcnc_rotatekins_source_inverse(pose, &joints); + return joints; +} + +// Source: linuxcnc/src/emc/kinematics/genhexkins.c +// Mirrors genhexKinematicsInverse(). +LinuxCncGenhexJoints linuxcnc_genhex_inverse(const CncSimPose &pose) { + LinuxCncGenhexJoints joints{}; + linuxcnc_genhexkins_source_inverse(pose, &joints); + return joints; +} + +// 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(¶meters); + return parameters; +} + +// Source: linuxcnc/src/emc/kinematics/lineardeltakins.c +// Mirrors kinematicsForward(). +CncSimPose linuxcnc_lineardelta_forward(const LinuxCncAxisJoints &joints, + const LinuxCncLinearDeltaParameters ¶meters) { + 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 ¶meters) { + 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(¶meters); + return parameters; +} + +// Source: linuxcnc/src/emc/kinematics/rotarydeltakins.c +// Mirrors kinematicsForward(). +CncSimPose linuxcnc_rotarydelta_forward(const LinuxCncAxisJoints &joints, + const LinuxCncRotaryDeltaParameters ¶meters) { + 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 ¶meters) { + 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(¶meters); + return parameters; +} + +// Source: linuxcnc/src/emc/kinematics/maxkins.c +// Mirrors kinematicsForward(). +CncSimPose linuxcnc_maxkins_forward(const LinuxCncAxisJoints &joints, + const LinuxCncMaxkinsParameters ¶meters) { + 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 ¶meters) { + 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) { - const RtcpVector radius = spherical_to_cartesian(pivot_length + joints.w, - joints.c, - 180.0 - joints.b); CncSimPose pose{}; - pose.x = joints.x + radius.x; - pose.y = joints.y + radius.y; - pose.z = joints.z + pivot_length + radius.z; - pose.b = joints.b; - pose.c = joints.c; - pose.w = joints.w; + 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) { - const RtcpVector radius = spherical_to_cartesian(pivot_length + pose.w, - pose.c, - 180.0 - pose.b); LinuxCncFiveAxisJoints joints{}; - joints.x = pose.x - radius.x; - joints.y = pose.y - radius.y; - joints.z = pose.z - pivot_length - radius.z; - joints.b = pose.b; - joints.c = pose.c; - joints.w = pose.w; + linuxcnc_5axiskins_inverse(pose, pivot_length, &joints); return joints; } @@ -656,35 +261,8 @@ LinuxCncXyzbcTrtParameters linuxcnc_xyzbc_trt_default_parameters(double tool_off // Mirrors xyzbcKinematicsForward(). CncSimPose linuxcnc_xyzbc_trt_forward(const LinuxCncFiveAxisJoints &joints, const LinuxCncXyzbcTrtParameters ¶meters) { - const double x_rot_point = parameters.x_rot_point; - const double y_rot_point = parameters.y_rot_point; - const double z_rot_point = parameters.z_rot_point; - const double dx = parameters.x_offset; - const double dz = parameters.z_offset + parameters.tool_offset; - const double b_rad = radians(joints.b); - const double c_rad = radians(joints.c); - const double con = parameters.conventional_directions ? 1.0 : -1.0; - CncSimPose pose{}; - pose.x = +std::cos(c_rad) * std::cos(b_rad) * (joints.x - dx - x_rot_point) - - con * std::sin(c_rad) * (joints.y - y_rot_point) - + con * std::cos(c_rad) * std::sin(b_rad) * (joints.z - dz - z_rot_point) - + std::cos(c_rad) * dx - + x_rot_point; - - pose.y = +con * std::sin(c_rad) * std::cos(b_rad) * (joints.x - dx - x_rot_point) - + std::cos(c_rad) * (joints.y - y_rot_point) - + std::sin(c_rad) * std::sin(b_rad) * (joints.z - dz - z_rot_point) - + con * std::sin(c_rad) * dx - + y_rot_point; - - pose.z = -con * std::sin(b_rad) * (joints.x - dx - x_rot_point) - + std::cos(b_rad) * (joints.z - dz - z_rot_point) - + dz - + z_rot_point; - - pose.b = joints.b; - pose.c = joints.c; + linuxcnc_trtfuncs_xyzbc_forward(joints, parameters, &pose); return pose; } @@ -692,36 +270,8 @@ CncSimPose linuxcnc_xyzbc_trt_forward(const LinuxCncFiveAxisJoints &joints, // Mirrors xyzbcKinematicsInverse(). LinuxCncFiveAxisJoints linuxcnc_xyzbc_trt_inverse(const CncSimPose &pose, const LinuxCncXyzbcTrtParameters ¶meters) { - const double x_rot_point = parameters.x_rot_point; - const double y_rot_point = parameters.y_rot_point; - const double z_rot_point = parameters.z_rot_point; - const double dx = parameters.x_offset; - const double dz = parameters.z_offset + parameters.tool_offset; - const double b_rad = radians(pose.b); - const double c_rad = radians(pose.c); - const double dpx = -std::cos(b_rad) * dx + std::sin(b_rad) * dz + dx; - const double dpz = -std::sin(b_rad) * dx - std::cos(b_rad) * dz + dz; - const double con = parameters.conventional_directions ? 1.0 : -1.0; - LinuxCncFiveAxisJoints joints{}; - joints.x = +std::cos(c_rad) * std::cos(b_rad) * (pose.x - x_rot_point) - + con * std::sin(c_rad) * std::cos(b_rad) * (pose.y - y_rot_point) - - con * std::sin(b_rad) * (pose.z - z_rot_point) - + dpx - + x_rot_point; - - joints.y = -con * std::sin(c_rad) * (pose.x - x_rot_point) - + std::cos(c_rad) * (pose.y - y_rot_point) - + y_rot_point; - - joints.z = +con * std::cos(c_rad) * std::sin(b_rad) * (pose.x - x_rot_point) - + std::sin(c_rad) * std::sin(b_rad) * (pose.y - y_rot_point) - + std::cos(b_rad) * (pose.z - z_rot_point) - + dpz - + z_rot_point; - - joints.b = pose.b; - joints.c = pose.c; + linuxcnc_trtfuncs_xyzbc_inverse(pose, parameters, &joints); return joints; } @@ -729,35 +279,8 @@ LinuxCncFiveAxisJoints linuxcnc_xyzbc_trt_inverse(const CncSimPose &pose, // Mirrors xyzacKinematicsForward(). CncSimPose linuxcnc_xyzac_trt_forward(const LinuxCncFiveAxisJoints &joints, const LinuxCncXyzbcTrtParameters ¶meters) { - const double x_rot_point = parameters.x_rot_point; - const double y_rot_point = parameters.y_rot_point; - const double z_rot_point = parameters.z_rot_point; - const double dy = parameters.y_offset; - const double dz = parameters.z_offset + parameters.tool_offset; - const double a_rad = radians(joints.a); - const double c_rad = radians(joints.c); - const double con = parameters.conventional_directions ? 1.0 : -1.0; - CncSimPose pose{}; - pose.x = +std::cos(c_rad) * (joints.x - x_rot_point) - - con * std::sin(c_rad) * std::cos(a_rad) * (joints.y - dy - y_rot_point) - + std::sin(c_rad) * std::sin(a_rad) * (joints.z - dz - z_rot_point) - - con * std::sin(c_rad) * dy - + x_rot_point; - - pose.y = +con * std::sin(c_rad) * (joints.x - x_rot_point) - + std::cos(c_rad) * std::cos(a_rad) * (joints.y - dy - y_rot_point) - - con * std::cos(c_rad) * std::sin(a_rad) * (joints.z - dz - z_rot_point) - + std::cos(c_rad) * dy - + y_rot_point; - - pose.z = +con * std::sin(a_rad) * (joints.y - dy - y_rot_point) - + std::cos(a_rad) * (joints.z - dz - z_rot_point) - + dz - + z_rot_point; - - pose.a = joints.a; - pose.c = joints.c; + linuxcnc_trtfuncs_xyzac_forward(joints, parameters, &pose); return pose; } @@ -765,38 +288,8 @@ CncSimPose linuxcnc_xyzac_trt_forward(const LinuxCncFiveAxisJoints &joints, // Mirrors xyzacKinematicsInverse(). LinuxCncFiveAxisJoints linuxcnc_xyzac_trt_inverse(const CncSimPose &pose, const LinuxCncXyzbcTrtParameters ¶meters) { - const double x_rot_point = parameters.x_rot_point; - const double y_rot_point = parameters.y_rot_point; - const double z_rot_point = parameters.z_rot_point; - const double dy = parameters.y_offset; - const double dz = parameters.z_offset + parameters.tool_offset; - const double a_rad = radians(pose.a); - const double c_rad = radians(pose.c); - const double con = parameters.conventional_directions ? 1.0 : -1.0; - LinuxCncFiveAxisJoints joints{}; - joints.x = +std::cos(c_rad) * (pose.x - x_rot_point) - + con * std::sin(c_rad) * (pose.y - y_rot_point) - + x_rot_point; - - joints.y = -con * std::sin(c_rad) * std::cos(a_rad) * (pose.x - x_rot_point) - + std::cos(c_rad) * std::cos(a_rad) * (pose.y - y_rot_point) - + con * std::sin(a_rad) * (pose.z - z_rot_point) - - std::cos(a_rad) * dy - - con * std::sin(a_rad) * dz - + dy - + y_rot_point; - - joints.z = +std::sin(c_rad) * std::sin(a_rad) * (pose.x - x_rot_point) - - con * std::cos(c_rad) * std::sin(a_rad) * (pose.y - y_rot_point) - + std::cos(a_rad) * (pose.z - z_rot_point) - + con * std::sin(a_rad) * dy - - std::cos(a_rad) * dz - + dz - + z_rot_point; - - joints.a = pose.a; - joints.c = pose.c; + linuxcnc_trtfuncs_xyzac_inverse(pose, parameters, &joints); return joints; } @@ -805,11 +298,7 @@ LinuxCncFiveAxisJoints linuxcnc_xyzac_trt_inverse(const CncSimPose &pose, // implemented in linuxcnc/src/emc/kinematics/kins_util.c. CncSimPose linuxcnc_xyzbc_userk_forward(const LinuxCncFiveAxisJoints &joints) { CncSimPose pose{}; - pose.x = joints.x; - pose.y = joints.y; - pose.z = joints.z; - pose.b = joints.b; - pose.c = joints.c; + linuxcnc_userkfuncs_forward(joints, &pose); return pose; } @@ -818,11 +307,7 @@ CncSimPose linuxcnc_xyzbc_userk_forward(const LinuxCncFiveAxisJoints &joints) { // implemented in linuxcnc/src/emc/kinematics/kins_util.c. LinuxCncFiveAxisJoints linuxcnc_xyzbc_userk_inverse(const CncSimPose &pose) { LinuxCncFiveAxisJoints joints{}; - joints.x = pose.x; - joints.y = pose.y; - joints.z = pose.z; - joints.b = pose.b; - joints.c = pose.c; + linuxcnc_userkfuncs_inverse(pose, &joints); return joints; } @@ -839,70 +324,20 @@ LinuxCncScaraParameters linuxcnc_scara_default_parameters() { } // Source: linuxcnc/src/emc/kinematics/scarakins.c -// Mirrors scaraKinematicsForward(). CncSimPose linuxcnc_scara_forward(const LinuxCncScaraJoints &joints, const LinuxCncScaraParameters ¶meters, int *iflags) { - double a0 = radians(joints.j0); - double a1 = radians(joints.j1); - double a3 = radians(joints.j3); - - a1 = a1 + a0; - a3 = a3 + a1; - CncSimPose pose{}; - pose.x = parameters.d2 * std::cos(a0) + - parameters.d4 * std::cos(a1) + - parameters.d6 * std::cos(a3); - pose.y = parameters.d2 * std::sin(a0) + - parameters.d4 * std::sin(a1) + - parameters.d6 * std::sin(a3); - pose.z = parameters.d1 + parameters.d3 - joints.j2 - parameters.d5; - pose.c = degrees(a3); - pose.a = joints.j4; - pose.b = joints.j5; - - if (iflags) { - *iflags = joints.j1 < 90.0 ? 1 : 0; - } + linuxcnc_scarakins_forward(joints, parameters, &pose, iflags); return pose; } // Source: linuxcnc/src/emc/kinematics/scarakins.c -// Mirrors scaraKinematicsInverse(). LinuxCncScaraJoints linuxcnc_scara_inverse(const CncSimPose &pose, const LinuxCncScaraParameters ¶meters, int iflags) { - const double a3 = radians(pose.c); - const double xt = pose.x - parameters.d6 * std::cos(a3); - const double yt = pose.y - parameters.d6 * std::sin(a3); - const double rsq = xt * xt + yt * yt; - double cc = (rsq - parameters.d2 * parameters.d2 - parameters.d4 * parameters.d4) / - (2.0 * parameters.d2 * parameters.d4); - if (cc < -1.0) { - cc = -1.0; - } - if (cc > 1.0) { - cc = 1.0; - } - - double q1 = std::acos(cc); - if (iflags) { - q1 = -q1; - } - - double q0 = std::atan2(yt, xt); - const double local_xt = parameters.d2 + parameters.d4 * std::cos(q1); - const double local_yt = parameters.d4 * std::sin(q1); - q0 = q0 - std::atan2(local_yt, local_xt); - LinuxCncScaraJoints joints{}; - joints.j0 = degrees(q0); - joints.j1 = degrees(q1); - joints.j2 = parameters.d1 + parameters.d3 - parameters.d5 - pose.z; - joints.j3 = pose.c - (joints.j0 + joints.j1); - joints.j4 = pose.a; - joints.j5 = pose.b; + linuxcnc_scarakins_inverse(pose, parameters, iflags, &joints); return joints; } @@ -916,11 +351,8 @@ LinuxCncXyzabTdrParameters linuxcnc_xyzab_tdr_default_parameters(double tool_off // Mirrors kinematicsForward() switchkins type 0. CncSimPose linuxcnc_xyzab_tdr_identity_forward(const LinuxCncAxisJoints &joints) { CncSimPose pose{}; - pose.x = joints.x; - pose.y = joints.y; - pose.z = joints.z; - pose.a = joints.a; - pose.b = joints.b; + LinuxCncXyzabTdrParameters parameters{}; + linuxcnc_xyzab_tdr_kins_forward(joints, parameters, 0, &pose); return pose; } @@ -928,11 +360,8 @@ CncSimPose linuxcnc_xyzab_tdr_identity_forward(const LinuxCncAxisJoints &joints) // Mirrors kinematicsInverse() switchkins type 0. LinuxCncAxisJoints linuxcnc_xyzab_tdr_identity_inverse(const CncSimPose &pose) { LinuxCncAxisJoints joints{}; - joints.x = pose.x; - joints.y = pose.y; - joints.z = pose.z; - joints.a = pose.a; - joints.b = pose.b; + LinuxCncXyzabTdrParameters parameters{}; + linuxcnc_xyzab_tdr_kins_inverse(pose, parameters, 0, &joints); return joints; } @@ -940,25 +369,8 @@ LinuxCncAxisJoints linuxcnc_xyzab_tdr_identity_inverse(const CncSimPose &pose) { // Mirrors kinematicsForward() switchkins type 1. CncSimPose linuxcnc_xyzab_tdr_tcp_forward(const LinuxCncAxisJoints &joints, const LinuxCncXyzabTdrParameters ¶meters) { - const double x_rot_point = parameters.x_rot_point; - const double y_rot_point = parameters.y_rot_point; - const double z_rot_point = parameters.z_rot_point; - const double dz = parameters.z_offset; - const double dt = parameters.tool_offset_z; - const double sa = std::sin(radians(joints.a)); - const double ca = std::cos(radians(joints.a)); - const double sb = std::sin(radians(joints.b)); - const double cb = std::cos(radians(joints.b)); - const double px = joints.x - x_rot_point; - const double py = joints.y - y_rot_point; - const double pz = joints.z - z_rot_point - dt; - CncSimPose pose{}; - pose.x = cb * px + sb * pz + x_rot_point; - pose.y = sa * sb * px + ca * py - cb * sa * pz + sa * dz + y_rot_point; - pose.z = -ca * sb * px + sa * py + ca * cb * pz - ca * dz + z_rot_point + dz + dt; - pose.a = joints.a; - pose.b = joints.b; + linuxcnc_xyzab_tdr_kins_forward(joints, parameters, 1, &pose); return pose; } @@ -966,26 +378,8 @@ CncSimPose linuxcnc_xyzab_tdr_tcp_forward(const LinuxCncAxisJoints &joints, // Mirrors kinematicsInverse() switchkins type 1. LinuxCncAxisJoints linuxcnc_xyzab_tdr_tcp_inverse(const CncSimPose &pose, const LinuxCncXyzabTdrParameters ¶meters) { - const double x_rot_point = parameters.x_rot_point; - const double y_rot_point = parameters.y_rot_point; - const double z_rot_point = parameters.z_rot_point; - const double dx = parameters.x_offset; - const double dz = parameters.z_offset; - const double dt = parameters.tool_offset_z; - const double sa = std::sin(radians(pose.a)); - const double ca = std::cos(radians(pose.a)); - const double sb = std::sin(radians(pose.b)); - const double cb = std::cos(radians(pose.b)); - const double qx = pose.x - x_rot_point - dx; - const double qy = pose.y - y_rot_point; - const double qz = pose.z - z_rot_point - dz - dt; - LinuxCncAxisJoints joints{}; - joints.x = cb * qx + sa * sb * qy - ca * sb * qz + cb * dx - sb * dz + x_rot_point; - joints.y = ca * qy + sa * qz + y_rot_point; - joints.z = sb * qx - sa * cb * qy + ca * cb * qz + sb * dx + cb * dz + z_rot_point + dt; - joints.a = pose.a; - joints.b = pose.b; + linuxcnc_xyzab_tdr_kins_inverse(pose, parameters, 1, &joints); return joints; } @@ -999,196 +393,23 @@ LinuxCncPumaParameters linuxcnc_puma_default_parameters() { return parameters; } -// Source: linuxcnc/src/emc/kinematics/pumakins.h -constexpr int kPumaShoulderRight = 0x01; -constexpr int kPumaElbowDown = 0x02; -constexpr int kPumaWristFlip = 0x04; -constexpr int kPumaSingular = 0x08; -constexpr int kPumaReach = 0x01; -constexpr double kPumaSingularFuzz = 0.000001; -constexpr double kPumaFlagFuzz = 0.000001; - // Source: linuxcnc/src/emc/kinematics/pumakins.c -// Mirrors pumaKinematicsForward(), with posemath RPY conversion from _posemath.c. CncSimPose linuxcnc_puma_forward(const LinuxCncAxisJoints &joints, const LinuxCncPumaParameters ¶meters, int *iflags) { - const double s1 = std::sin(radians(joints.x)); - const double s2 = std::sin(radians(joints.y)); - const double s3 = std::sin(radians(joints.z)); - const double s4 = std::sin(radians(joints.a)); - const double s5 = std::sin(radians(joints.b)); - const double s6 = std::sin(radians(joints.c)); - const double c1 = std::cos(radians(joints.x)); - const double c2 = std::cos(radians(joints.y)); - const double c3 = std::cos(radians(joints.z)); - const double c4 = std::cos(radians(joints.a)); - const double c5 = std::cos(radians(joints.b)); - const double c6 = std::cos(radians(joints.c)); - - const double s23 = c2 * s3 + s2 * c3; - const double c23 = c2 * c3 - s2 * s3; - - double t1 = c4 * c5 * c6 - s4 * s6; - double t2 = s23 * s5 * c6; - double t3 = s4 * c5 * c6 + c4 * s6; - double t4 = c23 * t1 - t2; - double t5 = c23 * s5 * c6; - - RotationMatrix hom_rot{}; - hom_rot.x.x = c1 * t4 + s1 * t3; - hom_rot.x.y = s1 * t4 - c1 * t3; - hom_rot.x.z = -s23 * t1 - t5; - - t1 = -c4 * c5 * s6 - s4 * c6; - t2 = s23 * s5 * s6; - t3 = c4 * c6 - s4 * c5 * s6; - t4 = c23 * t1 + t2; - t5 = c23 * s5 * s6; - - hom_rot.y.x = c1 * t4 + s1 * t3; - hom_rot.y.y = s1 * t4 - c1 * t3; - hom_rot.y.z = -s23 * t1 + t5; - - t1 = c23 * c4 * s5 + s23 * c5; - hom_rot.z.x = -c1 * t1 - s1 * s4 * s5; - hom_rot.z.y = -s1 * t1 + c1 * s4 * s5; - hom_rot.z.z = s23 * c4 * s5 - c23 * c5; - - t1 = parameters.a2 * c2 + parameters.a3 * c23 - parameters.d4 * s23; - RtcpVector tran{}; - tran.x = c1 * t1 - parameters.d3 * s1; - tran.y = s1 * t1 + parameters.d3 * c1; - tran.z = -parameters.a3 * s23 - parameters.a2 * s2 - parameters.d4 * c23; - - int flags = 0; - const double sum_sq = tran.x * tran.x + tran.y * tran.y - parameters.d3 * parameters.d3; - const double k = (sum_sq + tran.z * tran.z - parameters.a2 * parameters.a2 - - parameters.a3 * parameters.a3 - parameters.d4 * parameters.d4) / - (2.0 * parameters.a2); - if (std::fabs(radians(joints.x) - std::atan2(tran.y, tran.x) + - std::atan2(parameters.d3, -std::sqrt(sum_sq))) < kPumaFlagFuzz) { - flags |= kPumaShoulderRight; - } - if (std::fabs(radians(joints.z) - std::atan2(parameters.a3, parameters.d4) + - std::atan2(k, -std::sqrt(parameters.a3 * parameters.a3 + - parameters.d4 * parameters.d4 - k * k))) < kPumaFlagFuzz) { - flags |= kPumaElbowDown; - } - t1 = -hom_rot.z.x * s1 + hom_rot.z.y * c1; - t2 = -hom_rot.z.x * c1 * c23 - hom_rot.z.y * s1 * c23 + hom_rot.z.z * s23; - if (std::fabs(t1) < kPumaSingularFuzz && std::fabs(t2) < kPumaSingularFuzz) { - flags |= kPumaSingular; - } else if (!(std::fabs(radians(joints.a) - std::atan2(t1, t2)) < kPumaFlagFuzz)) { - flags |= kPumaWristFlip; - } - if (iflags) { - *iflags = flags; - } - - tran.x = tran.x + hom_rot.z.x * parameters.d6; - tran.y = tran.y + hom_rot.z.y * parameters.d6; - tran.z = tran.z + hom_rot.z.z * parameters.d6; - return posemath_matrix_to_pose(tran, hom_rot); + CncSimPose pose{}; + linuxcnc_pumakins_forward(joints, parameters, &pose, iflags); + return pose; } // Source: linuxcnc/src/emc/kinematics/pumakins.c -// Mirrors pumaKinematicsInverse(), with posemath RPY conversion from _posemath.c. LinuxCncAxisJoints linuxcnc_puma_inverse(const CncSimPose &pose, const LinuxCncAxisJoints ¤t_joints, const LinuxCncPumaParameters ¶meters, int iflags, int *fflags) { - if (fflags) { - *fflags = 0; - } - - RotationMatrix hom_rot = posemath_rpy_to_matrix(radians(pose.a), - radians(pose.b), - radians(pose.c)); - const double px = pose.x - parameters.d6 * hom_rot.z.x; - const double py = pose.y - parameters.d6 * hom_rot.z.y; - const double pz = pose.z - parameters.d6 * hom_rot.z.z; - - const double sum_sq = px * px + py * py - parameters.d3 * parameters.d3; - double th1 = 0.0; - if (iflags & kPumaShoulderRight) { - th1 = std::atan2(py, px) - std::atan2(parameters.d3, -std::sqrt(sum_sq)); - } else { - th1 = std::atan2(py, px) - std::atan2(parameters.d3, std::sqrt(sum_sq)); - } - const double s1 = std::sin(th1); - const double c1 = std::cos(th1); - - const double k = (sum_sq + pz * pz - parameters.a2 * parameters.a2 - - parameters.a3 * parameters.a3 - parameters.d4 * parameters.d4) / - (2.0 * parameters.a2); - double th3 = 0.0; - if (iflags & kPumaElbowDown) { - th3 = std::atan2(parameters.a3, parameters.d4) - - std::atan2(k, -std::sqrt(parameters.a3 * parameters.a3 + - parameters.d4 * parameters.d4 - k * k)); - } else { - th3 = std::atan2(parameters.a3, parameters.d4) - - std::atan2(k, std::sqrt(parameters.a3 * parameters.a3 + - parameters.d4 * parameters.d4 - k * k)); - } - - const double s3 = std::sin(th3); - const double c3 = std::cos(th3); - double t1 = (-parameters.a3 - parameters.a2 * c3) * pz + - (c1 * px + s1 * py) * (parameters.a2 * s3 - parameters.d4); - double t2 = (parameters.a2 * s3 - parameters.d4) * pz + - (parameters.a3 + parameters.a2 * c3) * (c1 * px + s1 * py); - const double t3 = pz * pz + (c1 * px + s1 * py) * (c1 * px + s1 * py); - const double th23 = std::atan2(t1, t2); - const double th2 = th23 - th3; - const double s23 = t1 / t3; - const double c23 = t2 / t3; - - t1 = -hom_rot.z.x * s1 + hom_rot.z.y * c1; - t2 = -hom_rot.z.x * c1 * c23 - hom_rot.z.y * s1 * c23 + hom_rot.z.z * s23; - double th4 = 0.0; - if (std::fabs(t1) < kPumaSingularFuzz && std::fabs(t2) < kPumaSingularFuzz) { - if (fflags) { - *fflags |= kPumaReach; - } - th4 = radians(current_joints.a); - } else { - th4 = std::atan2(t1, t2); - } - const double s4 = std::sin(th4); - const double c4 = std::cos(th4); - - const double s5 = hom_rot.z.z * (s23 * c4) - - hom_rot.z.x * (c1 * c23 * c4 + s1 * s4) - - hom_rot.z.y * (s1 * c23 * c4 - c1 * s4); - const double c5 = -hom_rot.z.x * (c1 * s23) - - hom_rot.z.y * (s1 * s23) - - hom_rot.z.z * c23; - double th5 = std::atan2(s5, c5); - - const double s6 = hom_rot.x.z * (s23 * s4) - - hom_rot.x.x * (c1 * c23 * s4 - s1 * c4) - - hom_rot.x.y * (s1 * c23 * s4 + c1 * c4); - const double c6 = hom_rot.x.x * ((c1 * c23 * c4 + s1 * s4) * c5 - c1 * s23 * s5) + - hom_rot.x.y * ((s1 * c23 * c4 - c1 * s4) * c5 - s1 * s23 * s5) - - hom_rot.x.z * (s23 * c4 * c5 + c23 * s5); - double th6 = std::atan2(s6, c6); - - if (iflags & kPumaWristFlip) { - th4 = th4 + kPi; - th5 = -th5; - th6 = th6 + kPi; - } - LinuxCncAxisJoints joints{}; - joints.x = degrees(th1); - joints.y = degrees(th2); - joints.z = degrees(th3); - joints.a = degrees(th4); - joints.b = degrees(th5); - joints.c = degrees(th6); + linuxcnc_pumakins_inverse(pose, current_joints, parameters, iflags, &joints, fflags); return joints; } @@ -1218,29 +439,20 @@ LinuxCncGenserParameters linuxcnc_genser_puma560_parameters() { return parameters; } +LinuxCncGenserParameters linuxcnc_genser_default_parameters(int *max_iterations) { + LinuxCncGenserParameters parameters{}; + linuxcnc_genserfuncs_source_default_parameters(¶meters, 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 ¶meters) { - const double raw_joints[6] = { - joints.x, - joints.y, - joints.z, - joints.a, - joints.b, - joints.c, - }; - double joint_radians[6] = {}; - for (int i = 0; i < 6; ++i) { - joint_radians[i] = radians(raw_joints[i]); - if (i != 0 && parameters.unrotate[i]) { - joint_radians[i] -= parameters.unrotate[i] * joint_radians[i - 1]; - } - } - - const HomogeneousTransform total = linuxcnc_genser_build_pose(joint_radians, parameters); - return posemath_matrix_to_pose(total.tran, total.rot); + CncSimPose pose{}; + linuxcnc_genserfuncs_forward(joints, parameters, &pose); + return pose; } // Source: linuxcnc/src/emc/kinematics/genserfuncs.c @@ -1251,100 +463,35 @@ LinuxCncAxisJoints linuxcnc_genser_inverse(const CncSimPose &pose, const LinuxCncGenserParameters ¶meters, int *iterations, int max_iterations) { - const double estimate_degrees[6] = { - joint_estimate.x, - joint_estimate.y, - joint_estimate.z, - joint_estimate.a, - joint_estimate.b, - joint_estimate.c, - }; - double jest[6] = {}; - for (int link = 0; link < 6; ++link) { - jest[link] = radians(estimate_degrees[link]); - } - - HomogeneousTransform target{}; - target.tran = {pose.x, pose.y, pose.z}; - target.rot = posemath_rpy_to_matrix(radians(pose.a), radians(pose.b), radians(pose.c)); - - int completed_iterations = 0; - for (; completed_iterations < max_iterations; ++completed_iterations) { - double jfwd[6][6] = {}; - double jinv[6][6] = {}; - HomogeneousTransform t_l_0{}; - if (!linuxcnc_genser_compute_jfwd(jest, parameters, jfwd, &t_l_0) || - !linuxcnc_genser_compute_jinv(jfwd, jinv)) { - break; - } - - const HomogeneousTransform pest = linuxcnc_genser_build_pose(jest, parameters); - const HomogeneousTransform pestinv = linuxcnc_pose_inverse(pest); - const HomogeneousTransform tdelta = multiply_transform(pestinv, target); - - const Quaternion pest_quat = linuxcnc_matrix_to_quaternion(pest.rot); - RtcpVector cart = linuxcnc_quaternion_rotate_vector(pest_quat, tdelta.tran); - double dvw[6] = {}; - dvw[0] = cart.x; - dvw[1] = cart.y; - dvw[2] = cart.z; - - const Quaternion tdelta_quat = linuxcnc_matrix_to_quaternion(tdelta.rot); - const RtcpVector rvec = linuxcnc_quaternion_to_rvec(tdelta_quat); - cart = linuxcnc_quaternion_rotate_vector(pest_quat, rvec); - dvw[3] = cart.x; - dvw[4] = cart.y; - dvw[5] = cart.z; - - double dj[6] = {}; - multiply_6x6_vector(jinv, dvw, dj); - - int smalls = 0; - for (int link = 0; link < 6; ++link) { - if (go_small(dj[link])) { - ++smalls; - } - } - if (smalls == 6) { - if (iterations) { - *iterations = completed_iterations; - } - LinuxCncAxisJoints joints{}; - double out_degrees[6] = {}; - for (int link = 0; link < 6; ++link) { - out_degrees[link] = degrees(jest[link]); - if (link != 0 && parameters.unrotate[link]) { - out_degrees[link] += parameters.unrotate[link] * out_degrees[link - 1]; - } - } - joints.x = out_degrees[0]; - joints.y = out_degrees[1]; - joints.z = out_degrees[2]; - joints.a = out_degrees[3]; - joints.b = out_degrees[4]; - joints.c = out_degrees[5]; - joints.u = pose.u; - joints.v = pose.v; - joints.w = pose.w; - return joints; - } - for (int link = 0; link < 6; ++link) { - jest[link] += dj[link]; - } - } - if (iterations) { - *iterations = completed_iterations; + *iterations = -1; } - LinuxCncAxisJoints joints{}; - joints.x = degrees(jest[0]); - joints.y = degrees(jest[1]); - joints.z = degrees(jest[2]); - joints.a = degrees(jest[3]); - joints.b = degrees(jest[4]); - joints.c = degrees(jest[5]); - joints.u = pose.u; - joints.v = pose.v; - joints.w = pose.w; + 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 ¶meters, + 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; +} diff --git a/core/src/rtcp_kinematics.h b/core/src/rtcp_kinematics.h index 232aef3..9f88061 100644 --- a/core/src/rtcp_kinematics.h +++ b/core/src/rtcp_kinematics.h @@ -83,12 +83,78 @@ struct LinuxCncScaraJoints { 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 ¶meters); +LinuxCncAxisJoints linuxcnc_lineardelta_inverse(const CncSimPose &pose, + const LinuxCncLinearDeltaParameters ¶meters); +LinuxCncRotaryDeltaParameters linuxcnc_rotarydelta_default_parameters(); +CncSimPose linuxcnc_rotarydelta_forward(const LinuxCncAxisJoints &joints, + const LinuxCncRotaryDeltaParameters ¶meters); +LinuxCncAxisJoints linuxcnc_rotarydelta_inverse(const CncSimPose &pose, + const LinuxCncRotaryDeltaParameters ¶meters); +LinuxCncMaxkinsParameters linuxcnc_maxkins_default_parameters(); +CncSimPose linuxcnc_maxkins_forward(const LinuxCncAxisJoints &joints, + const LinuxCncMaxkinsParameters ¶meters); +LinuxCncAxisJoints linuxcnc_maxkins_inverse(const CncSimPose &pose, + const LinuxCncMaxkinsParameters ¶meters); +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); @@ -132,6 +198,7 @@ LinuxCncAxisJoints linuxcnc_puma_inverse(const CncSimPose &pose, 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 ¶meters); LinuxCncAxisJoints linuxcnc_genser_inverse(const CncSimPose &pose, @@ -139,3 +206,9 @@ LinuxCncAxisJoints linuxcnc_genser_inverse(const CncSimPose &pose, const LinuxCncGenserParameters ¶meters, int *iterations, int max_iterations = 100); +bool linuxcnc_genser_inverse_checked(const CncSimPose &pose, + const LinuxCncAxisJoints &joint_estimate, + const LinuxCncGenserParameters ¶meters, + LinuxCncAxisJoints *joints, + int *iterations, + int max_iterations = 100); diff --git a/core/src/simulator_gcode_controls.cpp b/core/src/simulator_gcode_controls.cpp index 51a2980..10b41fb 100644 --- a/core/src/simulator_gcode_controls.cpp +++ b/core/src/simulator_gcode_controls.cpp @@ -295,12 +295,25 @@ bool parse_simulator_gcode_control_line(const std::string &line, 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, int line) { if (action.kind == SimulatorGcodeControlKind::KinematicsSwitch) { - sink.switch_m_code_kinematics(action.m_code, action.value, line); + return sink.switch_m_code_kinematics(action.m_code, action.value, line); } else if (action.kind == SimulatorGcodeControlKind::RtcpState) { sink.set_rtcp_state(action.rtcp_enabled, action.h_code, line); + return true; } + return false; } diff --git a/core/src/simulator_gcode_controls.h b/core/src/simulator_gcode_controls.h index 6660418..772994c 100644 --- a/core/src/simulator_gcode_controls.h +++ b/core/src/simulator_gcode_controls.h @@ -23,6 +23,8 @@ bool parse_simulator_gcode_control_line(const std::string &line, bool parse_simulator_gcode_control_line(const std::string &line, bool block_delete, std::vector *actions); -void emit_simulator_gcode_control_action(CanonEventSink &sink, +bool can_emit_simulator_gcode_control_action(const CanonEventSink &sink, + const SimulatorGcodeControlAction &action); +bool emit_simulator_gcode_control_action(CanonEventSink &sink, const SimulatorGcodeControlAction &action, int line); diff --git a/core/src/smoke_gcode_parser.cpp b/core/src/smoke_gcode_parser.cpp index a7565c4..da95e81 100644 --- a/core/src/smoke_gcode_parser.cpp +++ b/core/src/smoke_gcode_parser.cpp @@ -180,6 +180,7 @@ size_t leading_special_word_start(const std::string &line); bool is_numeric_m98_subprogram_header(const std::string &line); OwordKind oword_kind(const std::string &line); bool integer_word_value(const std::unordered_map &words, char letter, int *value); +bool smoke_ini_raw_value(const std::string §ion, const std::string &key, std::string *value); bool evaluate_expression(const std::string &text, const std::vector &call_stack, const std::unordered_map ¶meters, @@ -444,6 +445,20 @@ bool parse_smoke_hal_named_parameter(const std::string &name, std::string *pin) } bool smoke_ini_value(const std::string §ion, const std::string &key, double *value) { + std::string raw_value; + if (!smoke_ini_raw_value(section, key, &raw_value)) { + return false; + } + char *end = nullptr; + const double parsed = std::strtod(raw_value.c_str(), &end); + if (end == raw_value.c_str()) { + return false; + } + *value = parsed; + return true; +} + +bool smoke_ini_raw_value(const std::string §ion, const std::string &key, std::string *value) { const char *path = std::getenv("INI_FILE_NAME"); if (!path || !*path) { return false; @@ -474,18 +489,20 @@ bool smoke_ini_value(const std::string §ion, const std::string &key, double if (ini_lookup_key(text.substr(0, equals)) != key) { continue; } - char *end = nullptr; - const std::string raw_value = trim(text.substr(equals + 1)); - const double parsed = std::strtod(raw_value.c_str(), &end); - if (end == raw_value.c_str()) { - return false; - } - *value = parsed; + *value = trim(text.substr(equals + 1)); return true; } return false; } +double smoke_metric_machine_value() { + std::string linear_units; + if (!smoke_ini_raw_value("TRAJ", "LINEAR_UNITS", &linear_units)) { + return -1.0; + } + return linear_units == "inch" ? 0.0 : 1.0; +} + bool smoke_hal_value(const std::string &pin, double *value) { const char *path = std::getenv("HAL_PIN_FILE"); if (path && *path) { @@ -1735,6 +1752,18 @@ bool smoke_tool_number_exists(int tool_number) { return tool_number == 0 || tool_number == 1 || tool_number == 4; } +template +double smoke_pocket_named_parameter_value(const ToolTable &tool_table, int pocket_index, bool selected_pocket) { + const auto it = tool_table.find(pocket_index); + if (it == tool_table.end()) { + return -1.0; + } + if (selected_pocket && it->second.pocketno == 0) { + return -1.0; + } + return static_cast(it->second.pocketno); +} + int supported_linuxcnc_g_modal_group(double value) { const int code = linuxcnc_g_code_number(value); if (code < 0 || code >= kLinuxCncGCodeTableSize) { @@ -2333,13 +2362,7 @@ bool is_smoke_readonly_named_parameter(const std::string &name) { name == "_ABS_B" || name == "_abs_b" || name == "_ABS_C" || - name == "_abs_c" || - name == "_ABS_U" || - name == "_abs_u" || - name == "_ABS_V" || - name == "_abs_v" || - name == "_ABS_W" || - name == "_abs_w"; + name == "_abs_c"; } bool is_smoke_readonly_numbered_parameter(int index) { @@ -5408,6 +5431,8 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string spindle_css_mode_ = false; spindle_tool_id_ = 0; selected_tool_id_ = 0; + current_pocket_index_ = 0; + selected_pocket_index_ = 0; feed_mode_ = 0; canned_cycle_ = 0; canned_return_to_initial_ = false; @@ -5433,13 +5458,16 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string oword_value_returned_ = false; tool_table_.clear(); tool_table_[0] = {}; + tool_table_[0].pocketno = 0; tool_table_[1] = {}; + tool_table_[1].pocketno = 1; tool_table_[1].offset.z = 12.5; tool_table_[1].diameter = 6.0; tool_table_[1].frontangle = 12.0; tool_table_[1].backangle = 34.0; tool_table_[1].orientation = 5.0; tool_table_[4] = {}; + tool_table_[4].pocketno = 4; tool_table_[4].offset.z = 0.7; tool_table_[4].diameter = 1.0; @@ -5685,9 +5713,13 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string named_parameters["_CURRENT_TOOL"] = named_parameters["_current_tool"]; named_parameters["_selected_tool"] = static_cast(selected_tool_id_); named_parameters["_SELECTED_TOOL"] = named_parameters["_selected_tool"]; - named_parameters["_current_pocket"] = static_cast(spindle_tool_id_); + named_parameters["_current_pocket"] = smoke_pocket_named_parameter_value(tool_table_, + current_pocket_index_, + false); named_parameters["_CURRENT_POCKET"] = named_parameters["_current_pocket"]; - named_parameters["_selected_pocket"] = static_cast(selected_tool_id_); + named_parameters["_selected_pocket"] = smoke_pocket_named_parameter_value(tool_table_, + selected_pocket_index_, + true); named_parameters["_SELECTED_POCKET"] = named_parameters["_selected_pocket"]; named_parameters["_spindle_rpm_mode"] = spindle_css_mode_ ? 0.0 : 1.0; named_parameters["_SPINDLE_RPM_MODE"] = named_parameters["_spindle_rpm_mode"]; @@ -5707,8 +5739,8 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string named_parameters["_VMAJOR"] = 2.10; named_parameters["_vminor"] = 0.0; named_parameters["_VMINOR"] = 0.0; - named_parameters["_metric_machine"] = -1.0; - named_parameters["_METRIC_MACHINE"] = -1.0; + named_parameters["_metric_machine"] = smoke_metric_machine_value(); + named_parameters["_METRIC_MACHINE"] = named_parameters["_metric_machine"]; named_parameters["_task"] = 0.0; named_parameters["_TASK"] = 0.0; const bool has_tool_offset = tool_length_offset.x != 0.0 || @@ -5747,18 +5779,12 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string named_parameters["_abs_a"] = position.a; named_parameters["_abs_b"] = position.b; named_parameters["_abs_c"] = position.c; - named_parameters["_abs_u"] = position.u; - named_parameters["_abs_v"] = position.v; - named_parameters["_abs_w"] = position.w; named_parameters["_ABS_X"] = position.x; named_parameters["_ABS_Y"] = position.y; named_parameters["_ABS_Z"] = position.z; named_parameters["_ABS_A"] = position.a; named_parameters["_ABS_B"] = position.b; named_parameters["_ABS_C"] = position.c; - named_parameters["_ABS_U"] = position.u; - named_parameters["_ABS_V"] = position.v; - named_parameters["_ABS_W"] = position.w; const CncSimPose &probe_position = sink_.probe_position(); parameters[5061] = probe_position.x; @@ -5956,14 +5982,34 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string if (!has_keyword_oword && parse_simulator_gcode_control_line(line, sink_.block_delete(), &simulator_control_actions)) { bool simulator_only_line = true; + bool all_control_actions_handled = true; for (const auto &action : simulator_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 && !action.rtcp_enabled) { simulator_only_line = false; } - if (stop_if_callback_aborted(sink_, error)) { - return -1; + } + if (!all_control_actions_handled && simulator_control_actions.size() > 1) { + for (const auto &action : simulator_control_actions) { + if (!can_emit_simulator_gcode_control_action(sink_, action) && + action.kind == SimulatorGcodeControlKind::KinematicsSwitch) { + if (error) { + *error = "M-code greater than 199: M" + std::to_string(action.m_code); + } + return -1; + } + } + } + if (all_control_actions_handled) { + for (const auto &action : simulator_control_actions) { + emit_simulator_gcode_control_action(sink_, action, line_number); + if (stop_if_callback_aborted(sink_, error)) { + return -1; + } } } if (simulator_only_line) { @@ -7704,6 +7750,15 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string } return -1; } + for (int m : m_codes) { + if ((m == 428 || m == 429 || m == 430) && + !sink_.can_switch_m_code_kinematics(m)) { + if (error) { + *error = "M-code greater than 199: M" + std::to_string(m); + } + return -1; + } + } if (modal_motion_is_threading(modal_motion) && words.count('F')) { if (error) { @@ -7764,6 +7819,7 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string } sink_.select_tool(tool_id); selected_tool_id_ = tool_id; + selected_pocket_index_ = tool_id; tool_selected_ = true; } @@ -7910,6 +7966,10 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string if (stop_if_callback_aborted(sink_, error)) { return -1; } + if (selected_tool_id_ == 0) { + spindle_tool_id_ = 0; + } + current_pocket_index_ = selected_pocket_index_; } else if (m == 7) { sink_.set_mist_on(true); emit_comment_state(sink_, line_number, 11); @@ -8058,6 +8118,10 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string } sink_.select_tool(tool_number); sink_.change_tool(line_number); + if (tool_number == 0) { + spindle_tool_id_ = 0; + } + current_pocket_index_ = tool_number; if (stop_if_callback_aborted(sink_, error)) { return -1; } @@ -8192,17 +8256,32 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string return -1; } } else if (m == 428) { - sink_.switch_kinematics(1, line_number); + if (!sink_.switch_m_code_kinematics(428, 1, line_number)) { + if (error) { + *error = "M-code greater than 199: M428"; + } + return -1; + } if (stop_if_callback_aborted(sink_, error)) { return -1; } } else if (m == 429) { - sink_.switch_kinematics(0, line_number); + if (!sink_.switch_m_code_kinematics(429, 0, line_number)) { + if (error) { + *error = "M-code greater than 199: M429"; + } + return -1; + } if (stop_if_callback_aborted(sink_, error)) { return -1; } } else if (m == 430) { - sink_.switch_kinematics(2, line_number); + if (!sink_.switch_m_code_kinematics(430, 2, line_number)) { + if (error) { + *error = "M-code greater than 199: M430"; + } + return -1; + } if (stop_if_callback_aborted(sink_, error)) { return -1; } diff --git a/core/src/smoke_gcode_parser.h b/core/src/smoke_gcode_parser.h index b2d7649..7870bac 100644 --- a/core/src/smoke_gcode_parser.h +++ b/core/src/smoke_gcode_parser.h @@ -14,6 +14,7 @@ public: private: struct SmokeToolTableEntry { + int pocketno = 0; CncSimPose offset{}; double diameter = 0.0; double frontangle = 0.0; @@ -64,5 +65,7 @@ private: double oword_return_value_ = 0.0; bool oword_value_returned_ = false; int selected_tool_id_ = 0; + int current_pocket_index_ = 0; + int selected_pocket_index_ = 0; std::unordered_map tool_table_; }; diff --git a/core/tests/canon_event_sink_smoke.cpp b/core/tests/canon_event_sink_smoke.cpp index 5189282..cec31c9 100644 --- a/core/tests/canon_event_sink_smoke.cpp +++ b/core/tests/canon_event_sink_smoke.cpp @@ -297,6 +297,45 @@ int main() { } ok &= expect(saw_fiveaxis_pivot, "expected M428 bridgemill RTCP pivot to use LinuxCNC 5axiskins inverse"); + std::vector 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 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); diff --git a/core/tests/cnc_sim_api_linuxcnc_rs274_smoke.cpp b/core/tests/cnc_sim_api_linuxcnc_rs274_smoke.cpp index 392832b..a61dc0b 100644 --- a/core/tests/cnc_sim_api_linuxcnc_rs274_smoke.cpp +++ b/core/tests/cnc_sim_api_linuxcnc_rs274_smoke.cpp @@ -2,6 +2,7 @@ #include #include +#include #include namespace { @@ -55,6 +56,16 @@ bool saw_comment_state(const std::vector &events, return false; } +bool saw_any_kinematics_switch(const std::vector &events, int line) { + for (const auto &event : events) { + if (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == line) { + return true; + } + } + return false; +} + } // namespace int main() { @@ -521,6 +532,109 @@ int main() { ok &= expect(saw_g49_tool_offset_parameter_before && saw_g49_tool_offset_parameter_after, "expected LinuxCNC G49 to clear the applied tool offset parameter before the next motion"); + const char tool_pocket_named_parameter_program[] = + "G21 G90 G17\n" + "T1\n" + "O10 if [[#<_selected_tool> EQ 1] AND [#<_current_tool> EQ 0] AND [#<_selected_pocket> EQ 1] AND [#<_current_pocket> EQ 0]]\n" + "G1 X1 F100\n" + "O10 endif\n" + "M6\n" + "O20 if [[#<_selected_tool> EQ 1] AND [#<_current_tool> EQ 0] AND [#<_selected_pocket> EQ 1] AND [#<_current_pocket> EQ 1]]\n" + "G1 Y1 F100\n" + "O20 endif\n" + "M30\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + tool_pocket_named_parameter_program, + sizeof(tool_pocket_named_parameter_program) - 1) == 0, + cnc_sim_last_error(sim)); + bool saw_tool_pocket_before_change = false; + bool saw_tool_pocket_after_change = false; + for (const auto &event : events) { + saw_tool_pocket_before_change = saw_tool_pocket_before_change || + (event.type == CNC_SIM_EVENT_LINEAR_FEED && + event.line == 4 && + near(event.end.x, 1.0)); + saw_tool_pocket_after_change = saw_tool_pocket_after_change || + (event.type == CNC_SIM_EVENT_LINEAR_FEED && + event.line == 8 && + near(event.end.y, 1.0)); + } + ok &= expect(saw_tool_pocket_before_change && saw_tool_pocket_after_change, + "expected LinuxCNC tool pocket named parameters to follow selected/current pocket indexes"); + + const char m61_tool_pocket_named_parameter_program[] = + "G21 G90 G17\n" + "M61 Q1\n" + "O10 if [[#<_selected_tool> EQ 0] AND [#<_current_tool> EQ 0] AND [#<_selected_pocket> EQ -1] AND [#<_current_pocket> EQ 1]]\n" + "G1 X1 F100\n" + "O10 endif\n" + "M30\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + m61_tool_pocket_named_parameter_program, + sizeof(m61_tool_pocket_named_parameter_program) - 1) == 0, + cnc_sim_last_error(sim)); + bool saw_m61_tool_pocket_named_parameter = false; + for (const auto &event : events) { + saw_m61_tool_pocket_named_parameter = saw_m61_tool_pocket_named_parameter || + (event.type == CNC_SIM_EVENT_LINEAR_FEED && + event.line == 4 && + near(event.end.x, 1.0)); + } + ok &= expect(saw_m61_tool_pocket_named_parameter, + "expected LinuxCNC M61 to publish the current pocket without changing selected/current tool named parameters"); + + { + ScopedEnv spindle_tool_env("CNC_SIM_TOOL_IN_SPINDLE", "1"); + const char m61_unload_spindle_program[] = + "G21 G90 G17\n" + "M61 Q0\n" + "O10 if [#5400 EQ 0]\n" + "G1 X1 F100\n" + "O10 endif\n" + "M30\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + m61_unload_spindle_program, + sizeof(m61_unload_spindle_program) - 1) == 0, + cnc_sim_last_error(sim)); + bool saw_m61_unload_spindle = false; + for (const auto &event : events) { + saw_m61_unload_spindle = saw_m61_unload_spindle || + (event.type == CNC_SIM_EVENT_LINEAR_FEED && + event.line == 4 && + near(event.end.x, 1.0)); + } + ok &= expect(saw_m61_unload_spindle, + "expected LinuxCNC M61 Q0 to unload the spindle current tool number"); + } + + { + ScopedEnv spindle_tool_env("CNC_SIM_TOOL_IN_SPINDLE", "1"); + const char t0_m6_unload_program[] = + "G21 G90 G17\n" + "T0 M6\n" + "O10 if [#5400 EQ 0]\n" + "G1 X1 F100\n" + "O10 endif\n" + "M30\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + t0_m6_unload_program, + sizeof(t0_m6_unload_program) - 1) == 0, + cnc_sim_last_error(sim)); + bool saw_t0_m6_unload = false; + for (const auto &event : events) { + saw_t0_m6_unload = saw_t0_m6_unload || + (event.type == CNC_SIM_EVENT_LINEAR_FEED && + event.line == 4 && + near(event.end.x, 1.0)); + } + ok &= expect(saw_t0_m6_unload, + "expected LinuxCNC T0 M6 to unload the spindle current tool number"); + } + const char spindle_program[] = "G21 G90 G17\n" "S1200 M3\n" @@ -1443,6 +1557,26 @@ int main() { ok &= expect(saw_bracket_m428, "expected LinuxCNC source M[428] to execute as M428"); ok &= expect(saw_bracket_g434, "expected LinuxCNC source G[43.4] H[7] to execute as G43.4 H7"); + const char table_dual_config[] = + "{\"backend\":\"linuxcnc-rs274\"," + "\"config\":\"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini\"}"; + ok &= expect(cnc_sim_load_config_json(sim, table_dual_config, sizeof(table_dual_config) - 1) == 0, + cnc_sim_last_error(sim)); + const char mixed_unremapped_m430_program[] = + "G21 G90\n" + "M428 M430\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + mixed_unremapped_m430_program, + sizeof(mixed_unremapped_m430_program) - 1) != 0, + "expected LinuxCNC source-backed config without 430remap.ngc to reject mixed M428/M430"); + ok &= expect(std::string(cnc_sim_last_error(sim)) == "M-code greater than 199: M430", + "expected LinuxCNC source-backed mixed M428/M430 error to name unremapped M430"); + ok &= expect(!saw_any_kinematics_switch(events, 2), + "expected LinuxCNC source-backed mixed M428/M430 error to emit no partial switch"); + ok &= expect(cnc_sim_load_config_json(sim, config, sizeof(config) - 1) == 0, + cnc_sim_last_error(sim)); + const char exponent_m428_control_program[] = "G21 G90\n" "M4.28e2\n"; diff --git a/core/tests/cnc_sim_api_smoke.cpp b/core/tests/cnc_sim_api_smoke.cpp index eb96bf2..49d1bfd 100644 --- a/core/tests/cnc_sim_api_smoke.cpp +++ b/core/tests/cnc_sim_api_smoke.cpp @@ -109,11 +109,92 @@ bool saw_comment_state(const std::vector &events, return false; } +bool saw_kinematics_switch(const std::vector &events, int line, int kinstype) { + for (const auto &event : events) { + if (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == line && + event.reserved == kinstype) { + return true; + } + } + return false; +} + +bool saw_any_kinematics_switch(const std::vector &events, int line) { + for (const auto &event : events) { + if (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == line) { + return true; + } + } + return false; +} + +bool expect_switchkins_remap_config(const std::string &json, + int m428_type, + int m429_type, + int m430_type, + const char *message) { + std::vector events; + CncSimHandle *sim = cnc_sim_create(); + cnc_sim_set_event_callback(sim, collect_event, &events); + const char supported_program[] = + "M428\n" + "M429\n"; + const char full_program[] = + "M428\n" + "M429\n" + "M430\n"; + const char *program = m430_type >= 0 ? full_program : supported_program; + const size_t program_len = m430_type >= 0 ? sizeof(full_program) - 1 : sizeof(supported_program) - 1; + bool ok = true; + ok &= expect(cnc_sim_load_config_json(sim, json.c_str(), json.size()) == 0, cnc_sim_last_error(sim)); + ok &= expect(cnc_sim_parse_program(sim, program, program_len) == 0, cnc_sim_last_error(sim)); + ok &= expect(saw_kinematics_switch(events, 1, m428_type), message); + ok &= expect(saw_kinematics_switch(events, 2, m429_type), message); + if (m430_type >= 0) { + ok &= expect(saw_kinematics_switch(events, 3, m430_type), message); + } else { + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, "M430\n", 5) != 0, message); + ok &= expect(std::string(cnc_sim_last_error(sim)) == "M-code greater than 199: M430", message); + ok &= expect(!saw_kinematics_switch(events, 1, 2), message); + + std::vector mixed_events; + CncSimHandle *mixed_sim = cnc_sim_create(); + cnc_sim_set_event_callback(mixed_sim, collect_event, &mixed_events); + ok &= expect(cnc_sim_load_config_json(mixed_sim, json.c_str(), json.size()) == 0, + cnc_sim_last_error(mixed_sim)); + ok &= expect(cnc_sim_parse_program(mixed_sim, "M428 M430\n", 9) != 0, message); + ok &= expect(std::string(cnc_sim_last_error(mixed_sim)) == "M-code greater than 199: M430", message); + ok &= expect(!saw_any_kinematics_switch(mixed_events, 1), message); + cnc_sim_destroy(mixed_sim); + } + cnc_sim_destroy(sim); + return ok; +} + } // namespace int main() { ScopedTempDir temp_dir("cnc_sim_api_smoke"); +#ifndef CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND + { + CncSimHandle *default_sim = cnc_sim_create(); + const char default_program[] = "G21\nM30\n"; + const int default_rc = cnc_sim_parse_program(default_sim, + default_program, + sizeof(default_program) - 1); + const bool default_ok = expect(default_rc != 0, + "default LinuxCNC backend must not fall back to smoke parser"); + cnc_sim_destroy(default_sim); + if (!default_ok) { + return 1; + } + } +#endif + const char program[] = "G21 G90 G17\n" "T1 M6\n" @@ -1311,11 +1392,11 @@ int main() { const char selected_current_tool_named_param_program[] = "G21 G90 G17\n" "T1\n" - "O10 if [[#<_selected_tool> EQ 1] AND [#<_current_tool> EQ 0]]\n" + "O10 if [[#<_selected_tool> EQ 1] AND [#<_current_tool> EQ 0] AND [#<_selected_pocket> EQ 1] AND [#<_current_pocket> EQ 0]]\n" "G1 X1 F100\n" "O10 endif\n" "M6\n" - "O20 if [[#<_selected_tool> EQ 1] AND [#<_current_tool> EQ 0]]\n" + "O20 if [[#<_selected_tool> EQ 1] AND [#<_current_tool> EQ 0] AND [#<_selected_pocket> EQ 1] AND [#<_current_pocket> EQ 1]]\n" "G1 Y1\n" "O20 endif\n"; events.clear(); @@ -1336,14 +1417,14 @@ int main() { near(event.end.y, 1.0)); } ok &= expect(saw_selected_tool_before_change, - "expected LinuxCNC T word to update selected tool before current tool"); + "expected LinuxCNC T word to update selected tool/pocket before current tool/pocket"); ok &= expect(saw_current_tool_after_change, - "expected LinuxCNC M6 to keep current tool tied to #5400 in the nonrandom fixture"); + "expected LinuxCNC M6 to keep current tool tied to #5400 while updating current pocket in the nonrandom fixture"); const char m61_tool_named_param_program[] = "G21 G90 G17\n" "M61 Q1\n" - "O10 if [[#<_selected_tool> EQ 0] AND [#<_current_tool> EQ 0]]\n" + "O10 if [[#<_selected_tool> EQ 0] AND [#<_current_tool> EQ 0] AND [#<_selected_pocket> EQ -1] AND [#<_current_pocket> EQ 1]]\n" "G1 X1 F100\n" "O10 endif\n"; events.clear(); @@ -1359,7 +1440,7 @@ int main() { near(event.end.x, 1.0)); } ok &= expect(saw_m61_tool_named_params, - "expected LinuxCNC M61 to leave selected/current tool named parameters unchanged in nonrandom fixture"); + "expected LinuxCNC M61 to update current pocket while leaving selected/current tool named parameters unchanged in nonrandom fixture"); const char dynamic_tool_length_program[] = "G21 G90 G17\n" @@ -5404,7 +5485,7 @@ int main() { const char spindle_tool_info_parameter_program[] = "G21 G90 G17\n" "F100\n" - "O10 if [[#5400 EQ 1] AND [#5410 EQ 6] AND [#5411 EQ 12] AND [#5412 EQ 34] AND [#5413 EQ 5]]\n" + "O10 if [[#5400 EQ 1] AND [#5410 EQ 6] AND [#5411 EQ 12] AND [#5412 EQ 34] AND [#5413 EQ 5] AND [#<_current_pocket> EQ 0] AND [#<_selected_pocket> EQ -1]]\n" "G1 X1\n" "O10 endif\n"; events.clear(); @@ -5420,7 +5501,57 @@ int main() { near(event.end.x, 1.0)); } ok &= expect(saw_spindle_tool_info_parameter, - "expected LinuxCNC numbered tool info parameters for preloaded spindle tool"); + "expected LinuxCNC numbered tool info parameters and pocket named parameters for preloaded spindle tool"); + } + + { + ScopedEnv spindle_tool_env("CNC_SIM_TOOL_IN_SPINDLE", "1"); + const char m61_unload_spindle_program[] = + "G21 G90 G17\n" + "M61 Q0\n" + "O10 if [#5400 EQ 0]\n" + "G1 X1 F100\n" + "O10 endif\n" + "M30\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + m61_unload_spindle_program, + sizeof(m61_unload_spindle_program) - 1) == 0, + cnc_sim_last_error(sim)); + bool saw_m61_unload_spindle = false; + for (const auto &event : events) { + saw_m61_unload_spindle = saw_m61_unload_spindle || + (event.type == CNC_SIM_EVENT_LINEAR_FEED && + event.line == 4 && + near(event.end.x, 1.0)); + } + ok &= expect(saw_m61_unload_spindle, + "expected LinuxCNC M61 Q0 to unload the spindle current tool number"); + } + + { + ScopedEnv spindle_tool_env("CNC_SIM_TOOL_IN_SPINDLE", "1"); + const char t0_m6_unload_program[] = + "G21 G90 G17\n" + "T0 M6\n" + "O10 if [#5400 EQ 0]\n" + "G1 X1 F100\n" + "O10 endif\n" + "M30\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + t0_m6_unload_program, + sizeof(t0_m6_unload_program) - 1) == 0, + cnc_sim_last_error(sim)); + bool saw_t0_m6_unload = false; + for (const auto &event : events) { + saw_t0_m6_unload = saw_t0_m6_unload || + (event.type == CNC_SIM_EVENT_LINEAR_FEED && + event.line == 4 && + near(event.end.x, 1.0)); + } + ok &= expect(saw_t0_m6_unload, + "expected LinuxCNC T0 M6 to unload the spindle current tool number"); } const char g28_g30_numbered_parameter_program[] = @@ -6210,7 +6341,8 @@ int main() { const std::string ini_path = temp_dir.file("named_ini.ini"); { std::ofstream ini(ini_path); - ini << "[SETUP]\nXPOS = 3.25\n"; + ini << "[TRAJ]\nLINEAR_UNITS = inch\n" + << "[SETUP]\nXPOS = 3.25\n"; } { ScopedEnv ini_env("INI_FILE_NAME", ini_path.c_str()); @@ -6236,6 +6368,25 @@ int main() { event.line == 5 && event.end.y == 1.0); } + + const char metric_machine_named_parameter_program[] = + "G21 G90\n" + "F100\n" + "G1 X[2 + #<_metric_machine>]\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + metric_machine_named_parameter_program, + sizeof(metric_machine_named_parameter_program) - 1) == 0, + cnc_sim_last_error(sim)); + bool saw_inch_metric_machine_named_parameter = false; + for (const auto &event : events) { + saw_inch_metric_machine_named_parameter = saw_inch_metric_machine_named_parameter || + (event.type == CNC_SIM_EVENT_LINEAR_FEED && + event.line == 3 && + near(event.end.x, 2.0)); + } + ok &= expect(saw_inch_metric_machine_named_parameter, + "expected #<_metric_machine> to reflect inch INI LINEAR_UNITS"); } ok &= expect(saw_ini_named_parameter_motion, "expected #<_ini[section]key> named parameter lookup"); ok &= expect(saw_ini_named_parameter_exists_motion, "expected EXISTS for #<_ini[section]key>"); @@ -9881,6 +10032,16 @@ int main() { sizeof(missing_named_parameter_program) - 1) != 0, "expected undefined named parameter read to fail"); + const char missing_abs_uvw_named_parameter_program[] = + "G21 G90\n" + "F100\n" + "G1 X#<_abs_u>\n"; + events.clear(); + ok &= expect(cnc_sim_parse_program(sim, + missing_abs_uvw_named_parameter_program, + sizeof(missing_abs_uvw_named_parameter_program) - 1) != 0, + "expected LinuxCNC to reject undefined _abs_u named parameter"); + const char indexed_parameter_program[] = "#2 = 3\n" "#3 = 11\n" @@ -10832,6 +10993,407 @@ int main() { ok &= expect(config_rc == 0, cnc_sim_last_error(sim)); ok &= expect(rc != 0, "expected uncompiled linuxcnc-rs274 backend to fail clearly"); + { + std::vector scara_events; + CncSimHandle *scara_sim = cnc_sim_create(); + cnc_sim_set_event_callback(scara_sim, collect_event, &scara_events); + const char scara_config[] = "{\"backend\":\"smoke\",\"switchkins\":\"scara\"}"; + const char scara_program[] = + "M428\n" + "M429\n" + "M430\n"; + ok &= expect(cnc_sim_load_config_json(scara_sim, scara_config, sizeof(scara_config) - 1) == 0, + cnc_sim_last_error(scara_sim)); + ok &= expect(cnc_sim_parse_program(scara_sim, scara_program, sizeof(scara_program) - 1) == 0, + cnc_sim_last_error(scara_sim)); + bool saw_scara_m428 = false; + bool saw_scara_m429 = false; + bool saw_scara_m430 = false; + for (const auto &event : scara_events) { + saw_scara_m428 = saw_scara_m428 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 1 && + event.reserved == 0); + saw_scara_m429 = saw_scara_m429 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 2 && + event.reserved == 1); + saw_scara_m430 = saw_scara_m430 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 3 && + event.reserved == 2); + } + ok &= expect(saw_scara_m428, "expected scara M428 remap to select kinstype 0"); + ok &= expect(saw_scara_m429, "expected scara M429 remap to select kinstype 1"); + ok &= expect(saw_scara_m430, "expected scara M430 remap to select kinstype 2"); + cnc_sim_destroy(scara_sim); + } + + { + std::vector table_dual_events; + CncSimHandle *table_dual_sim = cnc_sim_create(); + cnc_sim_set_event_callback(table_dual_sim, collect_event, &table_dual_events); + const char table_dual_config[] = "{\"backend\":\"smoke\",\"switchkins\":\"table-dual-rotary\"}"; + const char table_dual_program[] = + "M428\n" + "M429\n"; + ok &= expect(cnc_sim_load_config_json(table_dual_sim, + table_dual_config, + sizeof(table_dual_config) - 1) == 0, + cnc_sim_last_error(table_dual_sim)); + ok &= expect(cnc_sim_parse_program(table_dual_sim, + table_dual_program, + sizeof(table_dual_program) - 1) == 0, + cnc_sim_last_error(table_dual_sim)); + bool saw_table_dual_m428 = false; + bool saw_table_dual_m429 = false; + for (const auto &event : table_dual_events) { + saw_table_dual_m428 = saw_table_dual_m428 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 1 && + event.reserved == 1); + saw_table_dual_m429 = saw_table_dual_m429 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 2 && + event.reserved == 0); + } + ok &= expect(saw_table_dual_m428, "expected table-dual-rotary M428 remap to select kinstype 1"); + ok &= expect(saw_table_dual_m429, "expected table-dual-rotary M429 remap to select kinstype 0"); + cnc_sim_destroy(table_dual_sim); + } + + { + std::vector xyzab_tdr_events; + CncSimHandle *xyzab_tdr_sim = cnc_sim_create(); + cnc_sim_set_event_callback(xyzab_tdr_sim, collect_event, &xyzab_tdr_events); + const char xyzab_tdr_config[] = "{\"backend\":\"smoke\",\"switchkins\":\"sim-xyzab-tdr-kins\"}"; + const char xyzab_tdr_program[] = + "M428\n" + "M429\n"; + ok &= expect(cnc_sim_load_config_json(xyzab_tdr_sim, + xyzab_tdr_config, + sizeof(xyzab_tdr_config) - 1) == 0, + cnc_sim_last_error(xyzab_tdr_sim)); + ok &= expect(cnc_sim_parse_program(xyzab_tdr_sim, + xyzab_tdr_program, + sizeof(xyzab_tdr_program) - 1) == 0, + cnc_sim_last_error(xyzab_tdr_sim)); + bool saw_xyzab_tdr_m428 = false; + bool saw_xyzab_tdr_m429 = false; + for (const auto &event : xyzab_tdr_events) { + saw_xyzab_tdr_m428 = saw_xyzab_tdr_m428 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 1 && + event.reserved == 1); + saw_xyzab_tdr_m429 = saw_xyzab_tdr_m429 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 2 && + event.reserved == 0); + } + ok &= expect(saw_xyzab_tdr_m428, "expected sim-xyzab-tdr-kins M428 remap to select kinstype 1"); + ok &= expect(saw_xyzab_tdr_m429, "expected sim-xyzab-tdr-kins M429 remap to select kinstype 0"); + cnc_sim_destroy(xyzab_tdr_sim); + } + + { + std::vector puma_events; + CncSimHandle *puma_sim = cnc_sim_create(); + cnc_sim_set_event_callback(puma_sim, collect_event, &puma_events); + const char puma_config[] = "{\"backend\":\"smoke\",\"switchkins\":\"puma\"}"; + const char puma_program[] = + "M428\n" + "M429\n" + "M430\n"; + ok &= expect(cnc_sim_load_config_json(puma_sim, puma_config, sizeof(puma_config) - 1) == 0, + cnc_sim_last_error(puma_sim)); + ok &= expect(cnc_sim_parse_program(puma_sim, puma_program, sizeof(puma_program) - 1) == 0, + cnc_sim_last_error(puma_sim)); + bool saw_puma_m428 = false; + bool saw_puma_m429 = false; + bool saw_puma_m430 = false; + for (const auto &event : puma_events) { + saw_puma_m428 = saw_puma_m428 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 1 && + event.reserved == 0); + saw_puma_m429 = saw_puma_m429 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 2 && + event.reserved == 1); + saw_puma_m430 = saw_puma_m430 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 3 && + event.reserved == 2); + } + ok &= expect(saw_puma_m428, "expected puma M428 remap to select kinstype 0"); + ok &= expect(saw_puma_m429, "expected puma M429 remap to select kinstype 1"); + ok &= expect(saw_puma_m430, "expected puma M430 remap to select kinstype 2"); + cnc_sim_destroy(puma_sim); + } + + { + std::vector melfa_events; + CncSimHandle *melfa_sim = cnc_sim_create(); + cnc_sim_set_event_callback(melfa_sim, collect_event, &melfa_events); + const char melfa_config[] = "{\"backend\":\"smoke\",\"switchkins\":\"melfa-sim\"}"; + const char melfa_program[] = + "M428\n" + "M429\n" + "M430\n"; + ok &= expect(cnc_sim_load_config_json(melfa_sim, melfa_config, sizeof(melfa_config) - 1) == 0, + cnc_sim_last_error(melfa_sim)); + ok &= expect(cnc_sim_parse_program(melfa_sim, melfa_program, sizeof(melfa_program) - 1) == 0, + cnc_sim_last_error(melfa_sim)); + bool saw_melfa_m428 = false; + bool saw_melfa_m429 = false; + bool saw_melfa_m430 = false; + for (const auto &event : melfa_events) { + saw_melfa_m428 = saw_melfa_m428 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 1 && + event.reserved == 0); + saw_melfa_m429 = saw_melfa_m429 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 2 && + event.reserved == 1); + saw_melfa_m430 = saw_melfa_m430 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 3 && + event.reserved == 2); + } + ok &= expect(saw_melfa_m428, "expected melfa-sim M428 remap to select kinstype 0"); + ok &= expect(saw_melfa_m429, "expected melfa-sim M429 remap to select kinstype 1"); + ok &= expect(saw_melfa_m430, "expected melfa-sim M430 remap to select kinstype 2"); + cnc_sim_destroy(melfa_sim); + } + + { + std::vector hexapod_events; + CncSimHandle *hexapod_sim = cnc_sim_create(); + cnc_sim_set_event_callback(hexapod_sim, collect_event, &hexapod_events); + const char hexapod_config[] = "{\"backend\":\"smoke\",\"remap\":\"hexapod-sim\"}"; + const char hexapod_program[] = + "M428\n" + "M429\n" + "M430\n"; + ok &= expect(cnc_sim_load_config_json(hexapod_sim, hexapod_config, sizeof(hexapod_config) - 1) == 0, + cnc_sim_last_error(hexapod_sim)); + ok &= expect(cnc_sim_parse_program(hexapod_sim, hexapod_program, sizeof(hexapod_program) - 1) == 0, + cnc_sim_last_error(hexapod_sim)); + bool saw_hexapod_m428 = false; + bool saw_hexapod_m429 = false; + bool saw_hexapod_m430 = false; + for (const auto &event : hexapod_events) { + saw_hexapod_m428 = saw_hexapod_m428 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 1 && + event.reserved == 0); + saw_hexapod_m429 = saw_hexapod_m429 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 2 && + event.reserved == 1); + saw_hexapod_m430 = saw_hexapod_m430 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 3 && + event.reserved == 2); + } + ok &= expect(saw_hexapod_m428, "expected hexapod-sim M428 remap to select kinstype 0"); + ok &= expect(saw_hexapod_m429, "expected hexapod-sim M429 remap to select kinstype 1"); + ok &= expect(saw_hexapod_m430, "expected hexapod-sim M430 remap to select kinstype 2"); + cnc_sim_destroy(hexapod_sim); + } + + { + std::vector trsrn_events; + CncSimHandle *trsrn_sim = cnc_sim_create(); + cnc_sim_set_event_callback(trsrn_sim, collect_event, &trsrn_events); + const char trsrn_config[] = "{\"backend\":\"smoke\",\"switchkins\":\"xyzacb-trsrn\"}"; + const char trsrn_program[] = + "M428\n" + "M429\n" + "M430\n"; + ok &= expect(cnc_sim_load_config_json(trsrn_sim, trsrn_config, sizeof(trsrn_config) - 1) == 0, + cnc_sim_last_error(trsrn_sim)); + ok &= expect(cnc_sim_parse_program(trsrn_sim, trsrn_program, sizeof(trsrn_program) - 1) == 0, + cnc_sim_last_error(trsrn_sim)); + bool saw_trsrn_m428 = false; + bool saw_trsrn_m429 = false; + bool saw_trsrn_m430 = false; + for (const auto &event : trsrn_events) { + saw_trsrn_m428 = saw_trsrn_m428 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 1 && + event.reserved == 0); + saw_trsrn_m429 = saw_trsrn_m429 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 2 && + event.reserved == 1); + saw_trsrn_m430 = saw_trsrn_m430 || + (event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH && + event.line == 3 && + event.reserved == 2); + } + ok &= expect(saw_trsrn_m428, "expected xyzacb-trsrn M428 remap to select kinstype 0"); + ok &= expect(saw_trsrn_m429, "expected xyzacb-trsrn M429 remap to select kinstype 1"); + ok &= expect(saw_trsrn_m430, "expected xyzacb-trsrn M430 remap to select kinstype 2"); + cnc_sim_destroy(trsrn_sim); + } + + { + std::vector machine_events; + CncSimHandle *machine_sim = cnc_sim_create(); + cnc_sim_set_event_callback(machine_sim, collect_event, &machine_events); + const char machine_config[] = "{\"backend\":\"smoke\",\"machine\":\"sim-xyzac-trt-kins (switchkins)\"}"; + const char machine_program[] = + "M428\n" + "M429\n" + "M430\n"; + ok &= expect(cnc_sim_load_config_json(machine_sim, machine_config, sizeof(machine_config) - 1) == 0, + cnc_sim_last_error(machine_sim)); + ok &= expect(cnc_sim_parse_program(machine_sim, machine_program, sizeof(machine_program) - 1) == 0, + cnc_sim_last_error(machine_sim)); + ok &= expect(saw_kinematics_switch(machine_events, 1, 1), + "expected LinuxCNC xyzac-trt MACHINE config M428 remap to select kinstype 1"); + ok &= expect(saw_kinematics_switch(machine_events, 2, 0), + "expected LinuxCNC xyzac-trt MACHINE config M429 remap to select kinstype 0"); + ok &= expect(saw_kinematics_switch(machine_events, 3, 2), + "expected LinuxCNC xyzac-trt MACHINE config M430 remap to select kinstype 2"); + cnc_sim_destroy(machine_sim); + } + + { + std::vector kinematics_events; + CncSimHandle *kinematics_sim = cnc_sim_create(); + cnc_sim_set_event_callback(kinematics_sim, collect_event, &kinematics_events); + const char kinematics_config[] = "{\"backend\":\"smoke\",\"kinematics\":\"genhexkins\"}"; + const char kinematics_program[] = + "M428\n" + "M429\n" + "M430\n"; + ok &= expect(cnc_sim_load_config_json(kinematics_sim, + kinematics_config, + sizeof(kinematics_config) - 1) == 0, + cnc_sim_last_error(kinematics_sim)); + ok &= expect(cnc_sim_parse_program(kinematics_sim, + kinematics_program, + sizeof(kinematics_program) - 1) == 0, + cnc_sim_last_error(kinematics_sim)); + ok &= expect(saw_kinematics_switch(kinematics_events, 1, 0), + "expected LinuxCNC genhexkins KINEMATICS config M428 remap to select kinstype 0"); + ok &= expect(saw_kinematics_switch(kinematics_events, 2, 1), + "expected LinuxCNC genhexkins KINEMATICS config M429 remap to select kinstype 1"); + ok &= expect(saw_kinematics_switch(kinematics_events, 3, 2), + "expected LinuxCNC genhexkins KINEMATICS config M430 remap to select kinstype 2"); + cnc_sim_destroy(kinematics_sim); + } + + { + std::vector config_path_events; + CncSimHandle *config_path_sim = cnc_sim_create(); + cnc_sim_set_event_callback(config_path_sim, collect_event, &config_path_events); + const char config_path_config[] = + "{\"backend\":\"smoke\"," + "\"config\":\"configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini\"}"; + const char config_path_program[] = + "M428\n" + "M429\n"; + ok &= expect(cnc_sim_load_config_json(config_path_sim, + config_path_config, + sizeof(config_path_config) - 1) == 0, + cnc_sim_last_error(config_path_sim)); + ok &= expect(cnc_sim_parse_program(config_path_sim, + config_path_program, + sizeof(config_path_program) - 1) == 0, + cnc_sim_last_error(config_path_sim)); + ok &= expect(saw_kinematics_switch(config_path_events, 1, 1), + "expected LinuxCNC xyzab-tdr config path M428 remap to select kinstype 1"); + ok &= expect(saw_kinematics_switch(config_path_events, 2, 0), + "expected LinuxCNC xyzab-tdr config path M429 remap to select kinstype 0"); + cnc_sim_destroy(config_path_sim); + } + + { + struct SourceBackedConfigCase { + const char *field; + const char *value; + int m428_type; + int m429_type; + int m430_type; + }; + const SourceBackedConfigCase source_cases[] = { + {"config", "configs/sim/axis/vismach/5axis/bridgemill/5axis.ini", 0, 1, 2}, + {"config", "configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini", 1, 0, -1}, + {"config", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini", 1, 0, 2}, + {"config", "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini", 1, 0, 2}, + {"config", + "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini", + 0, + 1, + 2}, + {"config", + "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini", + 0, + 1, + 2}, + {"config", "configs/sim/axis/vismach/hexapod-sim/hexapod.ini", 0, 1, 2}, + {"config", "configs/sim/axis/vismach/melfa-sim/melfa.ini", 0, 1, 2}, + {"config", "configs/sim/axis/vismach/millturn/millturn.ini", 0, 1, -1}, + {"config", "configs/sim/axis/vismach/puma/puma.ini", 0, 1, 2}, + {"config", "configs/sim/axis/vismach/puma/puma_cube.ini", 0, 1, 2}, + {"config", "configs/sim/axis/vismach/puma/puma560.ini", 0, 1, 2}, + {"config", "configs/sim/axis/vismach/puma/puma560_uvw.ini", 0, 1, 2}, + {"config", "configs/sim/axis/vismach/scara/scara.ini", 0, 1, 2}, + {"config", "configs/sim/qtaxis/non-trivial/scara/scara.ini", 0, 1, 2}, + {"config", "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini", 0, 1, 2}, + {"machine", "Sim-5Axis Bridge Mill (xyzbcw)", 0, 1, 2}, + {"machine", "sim-xyzab-tdr-kins (switchkins)", 1, 0, -1}, + {"machine", "sim-xyzac-trt-kins (switchkins)", 1, 0, 2}, + {"machine", "sim-xyzbc-trt-kins (switchkins)", 1, 0, 2}, + {"machine", "xyzacb-trsrn (switchkins)", 0, 1, 2}, + {"machine", "xyzbca-trsrn (switchkins)", 0, 1, 2}, + {"machine", "hexapod (switchkins)", 0, 1, 2}, + {"machine", "melfa (mm)", 0, 1, 2}, + {"machine", "millturn (mm)", 0, 1, -1}, + {"machine", "PUMA (pumakins,switchkins)", 0, 1, 2}, + {"machine", "puma_cube.ini (pumakins)", 0, 1, 2}, + {"machine", "puma560 (switchkins) (inch)", 0, 1, 2}, + {"machine", "SCARA (genserkins,switchkins)", 0, 1, 2}, + {"kinematics", "5axiskins", 0, 1, 2}, + {"kinematics", "xyzab_tdr_kins", 1, 0, -1}, + {"kinematics", "xyzac-trt-kins sparm=identityfirst", 1, 0, 2}, + {"kinematics", "xyzbc-trt-kins sparm=identityfirst", 1, 0, 2}, + {"kinematics", "xyzacb_trsrn", 0, 1, 2}, + {"kinematics", "xyzbca_trsrn", 0, 1, 2}, + {"kinematics", "genhexkins", 0, 1, 2}, + {"kinematics", "genserkins", 0, 1, 2}, + {"kinematics", "millturn", 0, 1, -1}, + {"kinematics", "pumakins", 0, 1, 2}, + {"kinematics", "scarakins coordinates=xyzcab", 0, 1, 2}, + {"halfile", "5axisgui.hal", 0, 1, 2}, + {"halfile", "5axisgui", 0, 1, 2}, + {"halfile", "kinematics.hal", 0, 1, 2}, + {"hal_file", "melfa_dh.hal", 0, 1, 2}, + {"hal_file", "melfa_dh", 0, 1, 2}, + {"hal_file", "millturn.hal", 0, 1, -1}, + {"halfile", "puma_dh.hal", 0, 1, 2}, + {"halfile", "puma_dh", 0, 1, 2}, + {"halfile", "puma560_dh.hal", 0, 1, 2}, + {"halfile", "puma560_dh", 0, 1, 2}, + }; + for (const SourceBackedConfigCase &source_case : source_cases) { + const std::string json = std::string("{\"backend\":\"smoke\",\"") + + source_case.field + + "\":\"" + + source_case.value + + "\"}"; + ok &= expect_switchkins_remap_config(json, + source_case.m428_type, + source_case.m429_type, + source_case.m430_type, + "expected LinuxCNC source-backed switchkins remap config"); + } + } + cnc_sim_destroy(sim); return ok ? 0 : 1; } diff --git a/core/tests/rtcp_kinematics_smoke.cpp b/core/tests/rtcp_kinematics_smoke.cpp index c1e3561..bff9ed2 100644 --- a/core/tests/rtcp_kinematics_smoke.cpp +++ b/core/tests/rtcp_kinematics_smoke.cpp @@ -1,5 +1,8 @@ #include "rtcp_kinematics.h" +#include "linuxcnc_cubic_adapter.h" +#include "linuxcnc_xyzab_tdr_kins_adapter.h" + #include #include @@ -63,6 +66,48 @@ bool expect_axis_joints_near(const LinuxCncAxisJoints &actual, return ok; } +bool expect_genhex_joints_near(const LinuxCncGenhexJoints &actual, + const LinuxCncGenhexJoints &expected, + const char *message) { + bool ok = true; + for (int i = 0; i < 6; ++i) { + ok &= expect_near_eps(actual.strut[i], expected.strut[i], 1e-6, message); + } + return ok; +} + +bool expect_pentakins_joints_near(const LinuxCncPentakinsJoints &actual, + const LinuxCncPentakinsJoints &expected, + const char *message) { + bool ok = true; + for (int i = 0; i < 5; ++i) { + ok &= expect_near_eps(actual.strut[i], expected.strut[i], 1e-6, message); + } + return ok; +} + +bool expect_tripod_joints_near(const LinuxCncTripodJoints &actual, + const LinuxCncTripodJoints &expected, + const char *message) { + bool ok = true; + for (int i = 0; i < 3; ++i) { + ok &= expect_near_eps(actual.strut[i], expected.strut[i], 1e-9, message); + } + return ok; +} + +bool expect_scorbot_joints_near(const LinuxCncScorbotJoints &actual, + const LinuxCncScorbotJoints &expected, + const char *message) { + bool ok = true; + ok &= expect_near_eps(actual.j0, expected.j0, 1e-9, message); + ok &= expect_near_eps(actual.j1, expected.j1, 1e-9, message); + ok &= expect_near_eps(actual.j2, expected.j2, 1e-9, message); + ok &= expect_near_eps(actual.j3, expected.j3, 1e-9, message); + ok &= expect_near_eps(actual.j4, expected.j4, 1e-9, message); + return ok; +} + } // namespace int main() { @@ -86,6 +131,46 @@ int main() { axis_joints, "identity/trivkins forward/inverse roundtrip"); + LinuxCncCubicSample cubic_samples[4]{}; + ok &= expect(linuxcnc_cubic_source_second_segment_samples(cubic_samples, 4), + "expected LinuxCNC cubic.c second segment sample generation"); + ok &= expect_near(cubic_samples[0].x, 0.0, "cubic second segment sample0 x"); + ok &= expect_near(cubic_samples[0].v, 0.0, "cubic second segment sample0 v"); + ok &= expect_near(cubic_samples[0].a, 0.0, "cubic second segment sample0 a"); + ok &= expect_near(cubic_samples[0].j, 10.0, "cubic second segment sample0 j"); + ok &= expect_near_eps(cubic_samples[1].x, 0.026041666666666668, 1e-12, "cubic second segment sample1 x"); + ok &= expect_near(cubic_samples[1].v, 0.3125, "cubic second segment sample1 v"); + ok &= expect_near(cubic_samples[1].a, 2.5, "cubic second segment sample1 a"); + ok &= expect_near(cubic_samples[1].j, 10.0, "cubic second segment sample1 j"); + ok &= expect_near_eps(cubic_samples[2].x, 0.20833333333333334, 1e-12, "cubic second segment sample2 x"); + ok &= expect_near(cubic_samples[2].v, 1.25, "cubic second segment sample2 v"); + ok &= expect_near(cubic_samples[2].a, 5.0, "cubic second segment sample2 a"); + ok &= expect_near_eps(cubic_samples[3].x, 0.703125, 1e-12, "cubic second segment sample3 x"); + ok &= expect_near(cubic_samples[3].v, 2.8125, "cubic second segment sample3 v"); + ok &= expect_near(cubic_samples[3].a, 7.5, "cubic second segment sample3 a"); + ok &= expect(linuxcnc_cubic_source_rejects_invalid_configuration(), + "expected LinuxCNC cubic.c to reject invalid configuration"); + + axis_joints = {}; + axis_joints.x = 12.0; + axis_joints.y = 4.0; + axis_joints.z = 3.0; + axis_joints.a = 10.0; + axis_joints.b = 20.0; + axis_joints.c = 30.0; + axis_joints.u = 7.0; + axis_joints.v = 8.0; + axis_joints.w = 9.0; + CncSimPose corexy_pose = linuxcnc_corexykins_forward(axis_joints); + ok &= expect_near(corexy_pose.x, 8.0, "corexykins forward x"); + ok &= expect_near(corexy_pose.y, 4.0, "corexykins forward y"); + ok &= expect_near(corexy_pose.z, 3.0, "corexykins forward z"); + ok &= expect_near(corexy_pose.a, 10.0, "corexykins forward a"); + ok &= expect_near(corexy_pose.w, 9.0, "corexykins forward w"); + ok &= expect_axis_joints_near(linuxcnc_corexykins_inverse(corexy_pose), + axis_joints, + "corexykins forward/inverse roundtrip"); + axis_joints = {}; axis_joints.x = 10.0; axis_joints.y = 0.0; @@ -104,6 +189,173 @@ int main() { axis_joints, "rotatekins forward/inverse roundtrip"); + CncSimPose genhex_pose{}; + genhex_pose.x = 1.25; + genhex_pose.y = -2.5; + genhex_pose.z = 3.75; + genhex_pose.a = 4.0; + genhex_pose.b = -5.0; + genhex_pose.c = 6.0; + const LinuxCncGenhexJoints genhex_joints = linuxcnc_genhex_inverse(genhex_pose); + CncSimPose genhex_roundtrip_pose = linuxcnc_genhex_forward(genhex_joints, genhex_pose); + ok &= expect_near_eps(genhex_roundtrip_pose.x, genhex_pose.x, 1e-6, "genhexkins forward/inverse roundtrip x"); + ok &= expect_near_eps(genhex_roundtrip_pose.y, genhex_pose.y, 1e-6, "genhexkins forward/inverse roundtrip y"); + ok &= expect_near_eps(genhex_roundtrip_pose.z, genhex_pose.z, 1e-6, "genhexkins forward/inverse roundtrip z"); + ok &= expect_near_eps(genhex_roundtrip_pose.a, genhex_pose.a, 1e-6, "genhexkins forward/inverse roundtrip a"); + ok &= expect_near_eps(genhex_roundtrip_pose.b, genhex_pose.b, 1e-6, "genhexkins forward/inverse roundtrip b"); + ok &= expect_near_eps(genhex_roundtrip_pose.c, genhex_pose.c, 1e-6, "genhexkins forward/inverse roundtrip c"); + ok &= expect_genhex_joints_near(linuxcnc_genhex_inverse(genhex_roundtrip_pose), + genhex_joints, + "genhexkins inverse deterministic strut lengths"); + + CncSimPose penta_pose{}; + penta_pose.x = 1.25; + penta_pose.y = -2.5; + penta_pose.z = 3.75; + penta_pose.a = 4.0; + penta_pose.b = -5.0; + const LinuxCncPentakinsJoints penta_joints = linuxcnc_pentakins_inverse(penta_pose); + CncSimPose penta_roundtrip_pose = linuxcnc_pentakins_forward(penta_joints, penta_pose); + ok &= expect_near_eps(penta_roundtrip_pose.x, penta_pose.x, 1e-6, "pentakins forward/inverse roundtrip x"); + ok &= expect_near_eps(penta_roundtrip_pose.y, penta_pose.y, 1e-6, "pentakins forward/inverse roundtrip y"); + ok &= expect_near_eps(penta_roundtrip_pose.z, penta_pose.z, 1e-6, "pentakins forward/inverse roundtrip z"); + ok &= expect_near_eps(penta_roundtrip_pose.a, penta_pose.a, 1e-6, "pentakins forward/inverse roundtrip a"); + ok &= expect_near_eps(penta_roundtrip_pose.b, penta_pose.b, 1e-6, "pentakins forward/inverse roundtrip b"); + ok &= expect_pentakins_joints_near(linuxcnc_pentakins_inverse(penta_roundtrip_pose), + penta_joints, + "pentakins inverse deterministic strut lengths"); + + CncSimPose tripod_pose{}; + tripod_pose.x = 0.2; + tripod_pose.y = 0.3; + tripod_pose.z = 0.4; + const LinuxCncTripodJoints tripod_joints = linuxcnc_tripodkins_inverse(tripod_pose); + const CncSimPose tripod_roundtrip_pose = linuxcnc_tripodkins_forward(tripod_joints); + ok &= expect_near_eps(tripod_roundtrip_pose.x, tripod_pose.x, 1e-12, "tripodkins forward/inverse roundtrip x"); + ok &= expect_near_eps(tripod_roundtrip_pose.y, tripod_pose.y, 1e-12, "tripodkins forward/inverse roundtrip y"); + ok &= expect_near_eps(tripod_roundtrip_pose.z, tripod_pose.z, 1e-12, "tripodkins forward/inverse roundtrip z"); + ok &= expect_near(tripod_roundtrip_pose.a, 0.0, "tripodkins forward clears a"); + ok &= expect_tripod_joints_near(linuxcnc_tripodkins_inverse(tripod_roundtrip_pose), + tripod_joints, + "tripodkins inverse deterministic strut lengths"); + + LinuxCncScorbotJoints scorbot_joints{}; + scorbot_joints.j0 = 30.0; + scorbot_joints.j1 = 20.0; + scorbot_joints.j2 = -10.0; + scorbot_joints.j3 = 7.0; + scorbot_joints.j4 = -8.0; + const CncSimPose scorbot_pose = linuxcnc_scorbot_kins_forward(scorbot_joints); + ok &= expect_near_eps(scorbot_pose.x, 382.1896396002526, 1e-9, "scorbot-kins forward x"); + ok &= expect_near_eps(scorbot_pose.y, 220.65729130469182, 1e-9, "scorbot-kins forward y"); + ok &= expect_near_eps(scorbot_pose.z, 177.2102044105812, 1e-9, "scorbot-kins forward z"); + ok &= expect_near(scorbot_pose.a, scorbot_joints.j3, "scorbot-kins forward wrist roll"); + ok &= expect_near(scorbot_pose.b, scorbot_joints.j4, "scorbot-kins forward wrist pitch"); + ok &= expect_scorbot_joints_near(linuxcnc_scorbot_kins_inverse(scorbot_pose), + scorbot_joints, + "scorbot-kins forward/inverse roundtrip"); + + const LinuxCncLinearDeltaParameters lineardelta_parameters = linuxcnc_lineardelta_default_parameters(); + ok &= expect_near(lineardelta_parameters.r, 130.25, "lineardeltakins default R"); + ok &= expect_near(lineardelta_parameters.l, 269.0, "lineardeltakins default L"); + CncSimPose lineardelta_pose{}; + lineardelta_pose.x = 1.25; + lineardelta_pose.y = -2.5; + lineardelta_pose.z = -20.0; + lineardelta_pose.a = 4.0; + lineardelta_pose.b = 5.0; + lineardelta_pose.c = 6.0; + lineardelta_pose.u = 7.0; + lineardelta_pose.v = 8.0; + lineardelta_pose.w = 9.0; + const LinuxCncAxisJoints lineardelta_joints = + linuxcnc_lineardelta_inverse(lineardelta_pose, lineardelta_parameters); + const CncSimPose lineardelta_roundtrip_pose = + linuxcnc_lineardelta_forward(lineardelta_joints, lineardelta_parameters); + ok &= expect_near_eps(lineardelta_roundtrip_pose.x, + lineardelta_pose.x, + 1e-6, + "lineardeltakins forward/inverse roundtrip x"); + ok &= expect_near_eps(lineardelta_roundtrip_pose.y, + lineardelta_pose.y, + 1e-6, + "lineardeltakins forward/inverse roundtrip y"); + ok &= expect_near_eps(lineardelta_roundtrip_pose.z, + lineardelta_pose.z, + 1e-6, + "lineardeltakins forward/inverse roundtrip z"); + ok &= expect_near(lineardelta_roundtrip_pose.a, lineardelta_pose.a, "lineardeltakins pass-through a"); + ok &= expect_near(lineardelta_roundtrip_pose.w, lineardelta_pose.w, "lineardeltakins pass-through w"); + + const LinuxCncRotaryDeltaParameters rotarydelta_parameters = linuxcnc_rotarydelta_default_parameters(); + ok &= expect_near(rotarydelta_parameters.platformradius, 10.0, "rotarydeltakins default platformradius"); + ok &= expect_near(rotarydelta_parameters.thighlength, 10.0, "rotarydeltakins default thighlength"); + ok &= expect_near(rotarydelta_parameters.shinlength, 14.0, "rotarydeltakins default shinlength"); + ok &= expect_near(rotarydelta_parameters.footradius, 6.0, "rotarydeltakins default footradius"); + LinuxCncAxisJoints rotarydelta_joints{}; + rotarydelta_joints.x = 20.0; + rotarydelta_joints.y = 25.0; + rotarydelta_joints.z = 30.0; + rotarydelta_joints.a = 4.0; + rotarydelta_joints.b = 5.0; + rotarydelta_joints.c = 6.0; + rotarydelta_joints.u = 7.0; + rotarydelta_joints.v = 8.0; + rotarydelta_joints.w = 9.0; + const CncSimPose rotarydelta_pose = + linuxcnc_rotarydelta_forward(rotarydelta_joints, rotarydelta_parameters); + const LinuxCncAxisJoints rotarydelta_roundtrip_joints = + linuxcnc_rotarydelta_inverse(rotarydelta_pose, rotarydelta_parameters); + const CncSimPose rotarydelta_roundtrip_pose = + linuxcnc_rotarydelta_forward(rotarydelta_roundtrip_joints, rotarydelta_parameters); + ok &= expect_near_eps(rotarydelta_roundtrip_pose.x, + rotarydelta_pose.x, + 1e-6, + "rotarydeltakins forward/inverse roundtrip x"); + ok &= expect_near_eps(rotarydelta_roundtrip_pose.y, + rotarydelta_pose.y, + 1e-6, + "rotarydeltakins forward/inverse roundtrip y"); + ok &= expect_near_eps(rotarydelta_roundtrip_pose.z, + rotarydelta_pose.z, + 1e-6, + "rotarydeltakins forward/inverse roundtrip z"); + ok &= expect_near(rotarydelta_roundtrip_pose.a, rotarydelta_pose.a, "rotarydeltakins pass-through a"); + ok &= expect_near(rotarydelta_roundtrip_pose.w, rotarydelta_pose.w, "rotarydeltakins pass-through w"); + + const LinuxCncMaxkinsParameters maxkins_parameters = linuxcnc_maxkins_default_parameters(); + ok &= expect_near(maxkins_parameters.pivot_length, 0.666, "maxkins default pivot-length"); + ok &= expect(!maxkins_parameters.conventional_directions, "maxkins default unconventional directions"); + LinuxCncAxisJoints maxkins_joints{}; + maxkins_joints.x = 10.0; + maxkins_joints.y = 4.0; + maxkins_joints.z = 3.0; + maxkins_joints.a = 1.0; + maxkins_joints.b = 0.0; + maxkins_joints.c = 30.0; + maxkins_joints.u = 0.0; + maxkins_joints.v = 0.0; + maxkins_joints.w = 0.0; + const CncSimPose maxkins_pose = linuxcnc_maxkins_forward(maxkins_joints, maxkins_parameters); + const LinuxCncAxisJoints maxkins_roundtrip_joints = + linuxcnc_maxkins_inverse(maxkins_pose, maxkins_parameters); + ok &= expect_axis_joints_near(maxkins_roundtrip_joints, + maxkins_joints, + "maxkins forward/inverse roundtrip"); + + LinuxCncAxisJoints rosekins_joints{}; + rosekins_joints.x = 5.0; + rosekins_joints.y = 2.0; + rosekins_joints.z = 45.0; + const CncSimPose rosekins_pose = linuxcnc_rosekins_forward(rosekins_joints); + ok &= expect_near_eps(rosekins_pose.x, 3.53553390593274, 1e-12, "rosekins forward x"); + ok &= expect_near_eps(rosekins_pose.y, 3.53553390593274, 1e-12, "rosekins forward y"); + ok &= expect_near(rosekins_pose.z, 2.0, "rosekins forward z"); + const LinuxCncAxisJoints rosekins_roundtrip_joints = linuxcnc_rosekins_inverse(rosekins_pose); + ok &= expect_near_eps(rosekins_roundtrip_joints.x, rosekins_joints.x, 1e-9, "rosekins inverse radius"); + ok &= expect_near(rosekins_roundtrip_joints.y, rosekins_joints.y, "rosekins inverse z joint"); + ok &= expect_near_eps(rosekins_roundtrip_joints.z, rosekins_joints.z, 1e-9, "rosekins inverse theta"); + LinuxCncFiveAxisJoints joints{}; joints.x = 10.0; joints.y = 20.0; @@ -257,6 +509,24 @@ int main() { ok &= expect_near(xyzac_roundtrip_joints.b, 0.0, "xyzac-trt inverse drops unconfigured B"); ok &= expect_near(xyzac_roundtrip_joints.w, 0.0, "xyzac-trt inverse drops unconfigured W"); + xyzac_pose = linuxcnc_xyzac_trt_forward(joints, trt_parameters); + ok &= expect_near(xyzac_pose.a, joints.a, "xyzac-trt forward keeps configured A after xyzbc setup"); + ok &= expect_near(xyzac_pose.b, 0.0, "xyzac-trt forward drops unconfigured B after xyzbc setup"); + + trt_parameters = linuxcnc_xyzbc_trt_default_parameters(250.0); + joints = {}; + joints.x = 10.0; + joints.y = 20.0; + joints.z = 30.0; + joints.b = 90.0; + joints.c = 0.0; + pose = linuxcnc_xyzbc_trt_forward(joints, trt_parameters); + ok &= expect_near(pose.x, 185.0, "xyzbc-trt B90 C0 forward x after xyzac setup"); + ok &= expect_near(pose.y, 20.0, "xyzbc-trt B90 C0 forward y after xyzac setup"); + ok &= expect_near(pose.z, 265.0, "xyzbc-trt B90 C0 forward z after xyzac setup"); + ok &= expect_near(pose.a, 0.0, "xyzbc-trt forward drops unconfigured A after xyzac setup"); + ok &= expect_near(pose.b, 90.0, "xyzbc-trt forward keeps configured B after xyzac setup"); + LinuxCncScaraParameters scara_parameters = linuxcnc_scara_default_parameters(); LinuxCncScaraJoints scara_joints{}; scara_joints.j0 = 0.0; @@ -355,6 +625,32 @@ int main() { ok &= expect_near(xyzab_tcp_roundtrip.z, axis_joints.z, "xyzab-tdr type1 roundtrip z"); ok &= expect_near(xyzab_tcp_roundtrip.a, axis_joints.a, "xyzab-tdr type1 roundtrip a"); ok &= expect_near(xyzab_tcp_roundtrip.b, axis_joints.b, "xyzab-tdr type1 roundtrip b"); + ok &= expect(!linuxcnc_xyzab_tdr_kins_switch(2), + "xyzab-tdr LinuxCNC kinematicsSwitch rejects invalid switchkins type"); + bool xyzab_kinstype_is_0 = true; + bool xyzab_kinstype_is_1 = true; + ok &= expect(linuxcnc_xyzab_tdr_kins_switch_state(&xyzab_kinstype_is_0, &xyzab_kinstype_is_1), + "xyzab-tdr LinuxCNC switch state is readable after invalid switchkins type"); + ok &= expect(!xyzab_kinstype_is_0, + "xyzab-tdr LinuxCNC invalid switch clears kinstype.is-0"); + ok &= expect(!xyzab_kinstype_is_1, + "xyzab-tdr LinuxCNC invalid switch clears kinstype.is-1"); + ok &= expect(linuxcnc_xyzab_tdr_kins_switch(1), + "xyzab-tdr LinuxCNC kinematicsSwitch restores valid switchkins type"); + ok &= expect(linuxcnc_xyzab_tdr_kins_switch_state(&xyzab_kinstype_is_0, &xyzab_kinstype_is_1), + "xyzab-tdr LinuxCNC switch state is readable after type1 switch"); + ok &= expect(!xyzab_kinstype_is_0, + "xyzab-tdr LinuxCNC type1 switch clears kinstype.is-0"); + ok &= expect(xyzab_kinstype_is_1, + "xyzab-tdr LinuxCNC type1 switch sets kinstype.is-1"); + ok &= expect(linuxcnc_xyzab_tdr_kins_switch(0), + "xyzab-tdr LinuxCNC kinematicsSwitch accepts type0 switch"); + ok &= expect(linuxcnc_xyzab_tdr_kins_switch_state(&xyzab_kinstype_is_0, &xyzab_kinstype_is_1), + "xyzab-tdr LinuxCNC switch state is readable after type0 switch"); + ok &= expect(xyzab_kinstype_is_0, + "xyzab-tdr LinuxCNC type0 switch sets kinstype.is-0"); + ok &= expect(!xyzab_kinstype_is_1, + "xyzab-tdr LinuxCNC type0 switch clears kinstype.is-1"); LinuxCncPumaParameters puma_parameters = linuxcnc_puma_default_parameters(); LinuxCncAxisJoints puma_joints{}; @@ -377,6 +673,31 @@ int main() { ok &= expect_near(puma_roundtrip.c, puma_joints.c, "pumakins roundtrip j5"); ok &= expect_near(puma_fflags, 0, "pumakins non-singular inverse flags"); + int genser_default_iterations = 0; + LinuxCncGenserParameters genser_default_parameters = + linuxcnc_genser_default_parameters(&genser_default_iterations); + ok &= expect_near(genser_default_iterations, 100, "genserkins LinuxCNC default max iterations"); + ok &= expect_near(genser_default_parameters.a[2], 300.0, "genserkins LinuxCNC default A3"); + ok &= expect_near(genser_default_parameters.a[3], 50.0, "genserkins LinuxCNC default A4"); + ok &= expect_near_eps(genser_default_parameters.alpha[1], + -1.5707963267948966, + 1e-12, + "genserkins LinuxCNC default ALPHA2"); + ok &= expect_near_eps(genser_default_parameters.alpha[3], + -1.5707963267948966, + 1e-12, + "genserkins LinuxCNC default ALPHA4"); + ok &= expect_near_eps(genser_default_parameters.alpha[4], + 1.5707963267948966, + 1e-12, + "genserkins LinuxCNC default ALPHA5"); + ok &= expect_near_eps(genser_default_parameters.alpha[5], + -1.5707963267948966, + 1e-12, + "genserkins LinuxCNC default ALPHA6"); + ok &= expect_near(genser_default_parameters.d[2], 70.0, "genserkins LinuxCNC default D3"); + ok &= expect_near(genser_default_parameters.d[3], 400.0, "genserkins LinuxCNC default D4"); + LinuxCncGenserParameters genser_parameters = linuxcnc_genser_puma560_parameters(); LinuxCncAxisJoints genser_joints{}; CncSimPose genser_pose = linuxcnc_genser_forward(genser_joints, genser_parameters); @@ -441,5 +762,16 @@ int main() { "genserkins puma560 iterative inverse c"); ok &= expect(genser_iterations > 0, "genserkins puma560 inverse should iterate from perturbed estimate"); + LinuxCncAxisJoints limited_genser_inverse{}; + genser_iterations = -1; + const bool limited_genser_ok = linuxcnc_genser_inverse_checked(genser_pose, + genser_estimate, + genser_parameters, + &limited_genser_inverse, + &genser_iterations, + 1); + ok &= expect(!limited_genser_ok, "genserkins puma560 inverse fails when max_iterations is too low"); + ok &= expect_near(genser_iterations, 1, "genserkins failed inverse reports LinuxCNC iteration count"); + return ok ? 0 : 1; } diff --git a/core/tools/cnc_sim_dump.cpp b/core/tools/cnc_sim_dump.cpp index 7920ac0..bc051ff 100644 --- a/core/tools/cnc_sim_dump.cpp +++ b/core/tools/cnc_sim_dump.cpp @@ -107,6 +107,8 @@ int main(int argc, char **argv) { CncSimHandle *sim = cnc_sim_create(); bool first = true; cnc_sim_set_event_callback(sim, print_event, &first); + const char smoke_config[] = "{\"backend\":\"smoke\"}"; + cnc_sim_load_config_json(sim, smoke_config, sizeof(smoke_config) - 1); std::cout << "[\n"; const int rc = cnc_sim_parse_program(sim, program.c_str(), program.size()); diff --git a/core/tools/linuxcnc_rs274_dump.cpp b/core/tools/linuxcnc_rs274_dump.cpp index 00b74ec..ea2bf1e 100644 --- a/core/tools/linuxcnc_rs274_dump.cpp +++ b/core/tools/linuxcnc_rs274_dump.cpp @@ -143,7 +143,10 @@ bool execute_line(InterpBase *interp, if (parse_simulator_gcode_control_line(line, &control_actions)) { bool simulator_only_line = true; for (const auto &action : control_actions) { - emit_simulator_gcode_control_action(sink, action, line_number); + const bool handled = emit_simulator_gcode_control_action(sink, action, line_number); + if (!handled) { + simulator_only_line = false; + } if (action.kind == SimulatorGcodeControlKind::RtcpState && !action.rtcp_enabled) { simulator_only_line = false; diff --git a/core/wasm_shims/Python.h b/core/wasm_shims/Python.h new file mode 100644 index 0000000..a330665 --- /dev/null +++ b/core/wasm_shims/Python.h @@ -0,0 +1,3 @@ +#pragma once + +#include "boost/python/object.hpp" diff --git a/core/wasm_shims/boost/noncopyable.hpp b/core/wasm_shims/boost/noncopyable.hpp new file mode 100644 index 0000000..aa56bda --- /dev/null +++ b/core/wasm_shims/boost/noncopyable.hpp @@ -0,0 +1,18 @@ +#ifndef CNC_SIM_WASM_SHIMS_BOOST_NONCOPYABLE_HPP +#define CNC_SIM_WASM_SHIMS_BOOST_NONCOPYABLE_HPP + +namespace boost { + +class noncopyable { +protected: + noncopyable() = default; + ~noncopyable() = default; + +private: + noncopyable(const noncopyable &) = delete; + noncopyable &operator=(const noncopyable &) = delete; +}; + +} // namespace boost + +#endif // CNC_SIM_WASM_SHIMS_BOOST_NONCOPYABLE_HPP diff --git a/core/wasm_shims/boost/python/class.hpp b/core/wasm_shims/boost/python/class.hpp new file mode 100644 index 0000000..cbbe6de --- /dev/null +++ b/core/wasm_shims/boost/python/class.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +#ifndef BOOST_DEDUCED_TYPENAME +#define BOOST_DEDUCED_TYPENAME typename +#endif + +#include "boost/python/object.hpp" diff --git a/core/wasm_shims/boost/python/converter/registry.hpp b/core/wasm_shims/boost/python/converter/registry.hpp new file mode 100644 index 0000000..a330665 --- /dev/null +++ b/core/wasm_shims/boost/python/converter/registry.hpp @@ -0,0 +1,3 @@ +#pragma once + +#include "boost/python/object.hpp" diff --git a/core/wasm_shims/boost/python/def.hpp b/core/wasm_shims/boost/python/def.hpp new file mode 100644 index 0000000..8079dbe --- /dev/null +++ b/core/wasm_shims/boost/python/def.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "boost/python/object.hpp" + +namespace boost { +namespace python { + +template +void def(Args &&...) { +} + +} // namespace python +} // namespace boost diff --git a/core/wasm_shims/boost/python/detail/caller.hpp b/core/wasm_shims/boost/python/detail/caller.hpp new file mode 100644 index 0000000..a330665 --- /dev/null +++ b/core/wasm_shims/boost/python/detail/caller.hpp @@ -0,0 +1,3 @@ +#pragma once + +#include "boost/python/object.hpp" diff --git a/core/wasm_shims/boost/python/enum.hpp b/core/wasm_shims/boost/python/enum.hpp new file mode 100644 index 0000000..64e73a5 --- /dev/null +++ b/core/wasm_shims/boost/python/enum.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "boost/python/object.hpp" + +namespace boost { +namespace python { + +template +class enum_ { +public: + explicit enum_(const char *) { + } + + template + enum_ &value(const char *, Value) { + return *this; + } + + enum_ &export_values() { + return *this; + } +}; + +} // namespace python +} // namespace boost diff --git a/core/wasm_shims/boost/python/exception_translator.hpp b/core/wasm_shims/boost/python/exception_translator.hpp new file mode 100644 index 0000000..1f5a1a2 --- /dev/null +++ b/core/wasm_shims/boost/python/exception_translator.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "boost/python/object.hpp" + +namespace boost { +namespace python { + +template +void register_exception_translator(Translator) { +} + +} // namespace python +} // namespace boost diff --git a/core/wasm_shims/boost/python/module.hpp b/core/wasm_shims/boost/python/module.hpp new file mode 100644 index 0000000..42e73bb --- /dev/null +++ b/core/wasm_shims/boost/python/module.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include "boost/python/object.hpp" + +#define BOOST_PYTHON_MODULE(name) extern "C" void init_##name() diff --git a/core/wasm_shims/boost/python/object.hpp b/core/wasm_shims/boost/python/object.hpp index eee5374..8932a21 100644 --- a/core/wasm_shims/boost/python/object.hpp +++ b/core/wasm_shims/boost/python/object.hpp @@ -1,24 +1,126 @@ #pragma once +#include +#include +#include +#include #include +#include "boost/noncopyable.hpp" extern "C" { +using Py_ssize_t = std::ptrdiff_t; +struct _object; +using PyObject = _object; + struct _typeobject { const char *tp_name; + std::size_t tp_basicsize; + std::size_t tp_itemsize; + void *tp_dealloc; + void *tp_print; + void *tp_getattr; + void *tp_setattr; + void *tp_compare; + void *tp_repr; + void *tp_as_number; + void *tp_as_sequence; + void *tp_as_mapping; + void *tp_hash; + void *tp_call; + void *tp_str; + void *tp_getattro; + void *tp_setattro; + void *tp_as_buffer; + unsigned long tp_flags; + const char *tp_doc; + void *tp_traverse; + void *tp_clear; + void *tp_richcompare; + Py_ssize_t tp_weaklistoffset; + void *tp_iter; + void *tp_iternext; + void *tp_methods; + void *tp_members; + void *tp_getset; + struct _typeobject *tp_base; + void *tp_dict; + void *tp_descr_get; + void *tp_descr_set; + Py_ssize_t tp_dictoffset; + void *tp_init; + void *tp_alloc; + PyObject *(*tp_new)(struct _typeobject *, PyObject *, PyObject *); + void *tp_free; + void *tp_is_gc; + void *tp_bases; + void *tp_mro; + void *tp_cache; + void *tp_subclasses; + void *tp_weaklink; + void *tp_del; + unsigned int tp_version_tag; + void *tp_finalize; +#if PY_VERSION_HEX >= 0x030800f0 + void *tp_vectorcall; +#if PY_VERSION_HEX >= 0x030c00f0 + void *tp_watched; +#if PY_VERSION_HEX >= 0x030d00f0 + void *tp_versions_used; +#endif +#endif +#endif }; using PyTypeObject = _typeobject; struct _object { PyTypeObject *ob_type; }; -using PyObject = _object; struct _inittab { const char *name; PyObject *(*initfunc)(void); }; +using PyCFunction = PyObject *(*)(PyObject *, PyObject *); +using getter = PyObject *(*)(PyObject *, void *); +using setter = int (*)(PyObject *, PyObject *, void *); + +struct PyGetSetDef { + char *name; + getter get; + setter set; + char *doc; + void *closure; +}; + +struct PyMemberDef { + char *name; + int type; + Py_ssize_t offset; + int flags; + char *doc; +}; + +struct PyMethodDef { + const char *ml_name; + PyCFunction ml_meth; + int ml_flags; + const char *ml_doc; +}; + +struct PyModuleDef { + void *m_base; + const char *m_name; + const char *m_doc; + Py_ssize_t m_size; + PyMethodDef *m_methods; + void *m_slots; + void *m_traverse; + void *m_clear; + void *m_free; +}; + int Py_IsInitialized(void); PyObject *PyErr_Occurred(void); void PyErr_Clear(void); @@ -31,21 +133,66 @@ int PyUnicode_Check(PyObject *); int PyLong_Check(PyObject *); int PyFloat_Check(PyObject *); int PyGen_Check(PyObject *); +int PyObject_IsInstance(PyObject *, PyObject *); PyObject *PyObject_Str(PyObject *); const char *PyUnicode_AsUTF8(PyObject *); +PyObject *PyUnicode_FromString(const char *); +void PyErr_SetObject(PyObject *, PyObject *); +PyObject *PyErr_Format(PyObject *, const char *, ...); void Py_DecRef(PyObject *); +int PyArg_ParseTuple(PyObject *, const char *, ...); +int PyArg_VaParse(PyObject *, const char *, va_list); +PyObject *PyObject_CallMethod(PyObject *, char *, char *, ...); +PyObject *PyObject_GetAttrString(PyObject *, const char *); +int PyObject_SetAttrString(PyObject *, const char *, PyObject *); +int PyObject_IsTrue(PyObject *); +PyObject *PyLong_FromLong(long); +long PyLong_AsLong(PyObject *); +double PyFloat_AsDouble(PyObject *); +PyObject *PyTuple_New(Py_ssize_t); +PyObject *PyTuple_GetItem(PyObject *, Py_ssize_t); +Py_ssize_t PyTuple_Size(PyObject *); +int PyTuple_SetItem(PyObject *, Py_ssize_t, PyObject *); +PyObject *PyList_New(Py_ssize_t); +PyObject *PyList_GetItem(PyObject *, Py_ssize_t); +Py_ssize_t PyList_Size(PyObject *); +int PyList_SetItem(PyObject *, Py_ssize_t, PyObject *); +PyObject *PySequence_GetItem(PyObject *, Py_ssize_t); +Py_ssize_t PySequence_Length(PyObject *); +PyObject *Py_BuildValue(const char *, ...); +PyObject *PyModule_Create(PyModuleDef *); +int PyModule_AddObject(PyObject *, const char *, PyObject *); +int PyType_Ready(PyTypeObject *); +PyObject *PyType_GenericNew(PyTypeObject *, PyObject *, PyObject *); +void *PyObject_NewShim(std::size_t, PyTypeObject *); extern PyObject *PyExc_KeyError; +extern PyObject *PyExc_KeyboardInterrupt; +extern PyObject *PyExc_RuntimeError; extern PyObject *PyExc_StopIteration; +extern PyObject *PyExc_TypeError; extern PyObject *_Py_NoneStructPtr; +extern PyTypeObject PyString_Type; +extern PyTypeObject PyInt_Type; +extern PyTypeObject PyList_Type; +#define PyObject_HEAD PyObject ob_base; +#define PyVarObject_HEAD_INIT(type, size) +#define PyModuleDef_HEAD_INIT nullptr +#define Py_TPFLAGS_DEFAULT 0 +#define PyMODINIT_FUNC extern "C" PyObject * +#define METH_VARARGS 0x0001 #define Py_None (_Py_NoneStructPtr) #define Py_TYPE(ob) ((ob)->ob_type) +#define Py_DECREF(ob) Py_DecRef(reinterpret_cast(ob)) #define Py_XDECREF(ob) \ do { \ if (ob) { \ - Py_DecRef(ob); \ + Py_DecRef(reinterpret_cast(ob)); \ } \ } while (0) +#define PyTuple_SET_ITEM(op, i, v) PyTuple_SetItem((op), (i), (v)) +#define PyList_SET_ITEM(op, i, v) PyList_SetItem((op), (i), (v)) +#define PyObject_New(type, typeobj) reinterpret_cast(PyObject_NewShim(sizeof(type), (typeobj))) } @@ -106,6 +253,93 @@ public: } }; +struct no_init_t {}; +inline constexpr no_init_t no_init{}; + +struct default_call_policies {}; +struct reference_existing_object {}; + +template +struct return_value_policy { +}; + +template +struct with_custodian_and_ward_postcall { +}; + +template +struct init { +}; + +class arg { +public: + explicit arg(const char *) { + } +}; + +template +struct type_info { +}; + +template +type_info type_id() { + return {}; +} + +template +object make_getter(Args &&...) { + return {}; +} + +template +object make_setter(Args &&...) { + return {}; +} + +template +object make_function(Args &&...) { + return {}; +} + +template +class class_ { +public: + explicit class_(const char *) { + } + + class_(const char *, no_init_t) { + } + + class_(const char *, const char *) { + } + + class_(const char *, const char *, no_init_t) { + } + + template + class_(const char *, Arg) { + } + + template + class_ &def(Args &&...) { + return *this; + } + + template + class_ &def_readwrite(Args &&...) { + return *this; + } + + template + class_ &add_property(Args &&...) { + return *this; + } + + PyObject *ptr() const { + return nullptr; + } +}; + inline object getattr(const object &, const char *) { return {}; } @@ -115,6 +349,10 @@ public: template void append(const T &) { } + + template + void extend(const T &) { + } }; class dict : public object { @@ -131,8 +369,35 @@ public: } }; +template +tuple make_tuple(Args &&...) { + return {}; +} + +inline object operator%(const char *, const tuple &) { + return {}; +} + +inline object operator%(const object &, const tuple &) { + return {}; +} + +inline object operator+(const object &, const object &) { + return {}; +} + +inline object operator+(const object &, const char *) { + return {}; +} + +inline object operator+(const char *, const object &) { + return {}; +} + class scope { public: + scope() = default; + explicit scope(const object &) { } @@ -187,6 +452,8 @@ allow_null_result allow_null(T *value) { template class handle { public: + handle() = default; + template explicit handle(const U &) { } @@ -194,6 +461,10 @@ public: explicit operator bool() const { return false; } + + void *get() const { + return nullptr; + } }; class str : public object { diff --git a/core/wasm_shims/boost/python/object/class_detail.hpp b/core/wasm_shims/boost/python/object/class_detail.hpp new file mode 100644 index 0000000..7049839 --- /dev/null +++ b/core/wasm_shims/boost/python/object/class_detail.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "boost/python/object.hpp" + +namespace boost { +namespace python { +namespace objects { + +template +void *registered_class_object(T) { + return nullptr; +} + +} // namespace objects +} // namespace python +} // namespace boost diff --git a/core/wasm_shims/boost/python/return_internal_reference.hpp b/core/wasm_shims/boost/python/return_internal_reference.hpp new file mode 100644 index 0000000..c278e92 --- /dev/null +++ b/core/wasm_shims/boost/python/return_internal_reference.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "boost/python/object.hpp" + +namespace boost { +namespace python { + +template +struct return_internal_reference : BasePolicy { +}; + +} // namespace python +} // namespace boost diff --git a/core/wasm_shims/boost/python/suite/indexing/map_indexing_suite.hpp b/core/wasm_shims/boost/python/suite/indexing/map_indexing_suite.hpp new file mode 100644 index 0000000..cdba34a --- /dev/null +++ b/core/wasm_shims/boost/python/suite/indexing/map_indexing_suite.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace boost { +namespace python { + +template +class map_indexing_suite { +}; + +} // namespace python +} // namespace boost diff --git a/core/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc b/core/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc new file mode 100644 index 0000000..9f9650d --- /dev/null +++ b/core/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc @@ -0,0 +1,99 @@ +#include "cnc_sim_api.h" + +#include +#include +#include +#include +#include +#include + +namespace { + +int collect_event(const CncSimEvent *event, void *user_data) { + auto *events = static_cast *>(user_data); + events->push_back(*event); + return 0; +} + +bool near(double lhs, double rhs) { + return std::fabs(lhs - rhs) < 0.000001; +} + +bool wordexp_expands_home() { + const char *home = std::getenv("HOME"); + if (!home || !home[0]) { + return true; + } + + wordexp_t expanded{}; + if (wordexp("~/cnc_sim_wordexp_probe", &expanded, 0) != 0) { + return false; + } + + const std::string expected = std::string(home) + "/cnc_sim_wordexp_probe"; + const bool ok = expanded.we_wordc == 1 && + expanded.we_wordv && + expanded.we_wordv[0] && + std::strcmp(expanded.we_wordv[0], expected.c_str()) == 0; + wordfree(&expanded); + return ok; +} + +bool wordexp_expands_environment_variables() { + setenv("CNC_SIM_WORDEXP_PROBE", "linuxcnc-wordexp", 1); + + wordexp_t expanded{}; + if (wordexp("$CNC_SIM_WORDEXP_PROBE/path", &expanded, 0) != 0) { + return false; + } + + const bool ok = expanded.we_wordc == 1 && + expanded.we_wordv && + expanded.we_wordv[0] && + std::strcmp(expanded.we_wordv[0], "linuxcnc-wordexp/path") == 0; + wordfree(&expanded); + return ok; +} + +} // namespace + +int main() { + if (!wordexp_expands_home()) { + return 5; + } + if (!wordexp_expands_environment_variables()) { + return 6; + } + + std::vector events; + CncSimHandle *sim = cnc_sim_create(); + if (!sim) { + return 1; + } + if (cnc_sim_set_event_callback(sim, collect_event, &events) != 0) { + cnc_sim_destroy(sim); + return 2; + } + + const char program[] = + "G21 G90 G17\n" + "G0 X1\n" + "M30\n"; + if (cnc_sim_parse_program(sim, program, sizeof(program) - 1) != 0) { + cnc_sim_destroy(sim); + return 3; + } + + bool saw_linuxcnc_rapid = false; + bool saw_program_end = false; + for (const auto &event : events) { + saw_linuxcnc_rapid = saw_linuxcnc_rapid || + (event.type == CNC_SIM_EVENT_RAPID && + event.line == 2 && + near(event.end.x, 1.0)); + saw_program_end = saw_program_end || event.type == CNC_SIM_EVENT_PROGRAM_END; + } + + cnc_sim_destroy(sim); + return saw_linuxcnc_rapid && saw_program_end ? 0 : 4; +} diff --git a/core/wasm_shims/libgen.h b/core/wasm_shims/libgen.h new file mode 100644 index 0000000..999304f --- /dev/null +++ b/core/wasm_shims/libgen.h @@ -0,0 +1,20 @@ +#ifndef CNC_SIM_WASM_SHIMS_LIBGEN_H +#define CNC_SIM_WASM_SHIMS_LIBGEN_H + +#ifdef __cplusplus +extern "C" { +#endif + +char *dirname(char *); +char *basename(char *); + +#ifdef __cplusplus +} + +inline char *basename(const char *path) +{ + return basename(const_cast(path)); +} +#endif + +#endif // CNC_SIM_WASM_SHIMS_LIBGEN_H diff --git a/core/wasm_shims/linuxcnc_runtime_shim.cc b/core/wasm_shims/linuxcnc_runtime_shim.cc index 4676d0e..053ba20 100644 --- a/core/wasm_shims/linuxcnc_runtime_shim.cc +++ b/core/wasm_shims/linuxcnc_runtime_shim.cc @@ -1,6 +1,12 @@ #include "boost/python/object.hpp" #include "hal.h" +#include +#include +#include +#include +#include + int _task = 0; extern "C" PyObject *PyInit_interpreter(void) { @@ -11,6 +17,10 @@ extern "C" PyObject *PyInit_emccanon(void) { return nullptr; } +extern "C" PyObject *PyInit_gcode(void) { + return nullptr; +} + extern "C" { struct _inittab builtin_modules[] = { {"interpreter", PyInit_interpreter}, @@ -70,3 +80,119 @@ extern "C" int hal_get_param_value_by_name(const char *, } return -1; } + +namespace { + +std::string expand_home_directory(const char *words) { + std::string expanded(words); + if (expanded.empty() || expanded[0] != '~') { + return expanded; + } + if (expanded.size() > 1 && expanded[1] != '/') { + return expanded; + } + + const char *home = std::getenv("HOME"); + if (!home || !home[0]) { + return expanded; + } + expanded.replace(0, 1, home); + return expanded; +} + +bool variable_start(char value) { + const unsigned char ch = static_cast(value); + return std::isalpha(ch) || value == '_'; +} + +bool variable_char(char value) { + const unsigned char ch = static_cast(value); + return std::isalnum(ch) || value == '_'; +} + +std::string expand_environment_variables(const std::string &words) { + std::string expanded; + expanded.reserve(words.size()); + + for (size_t index = 0; index < words.size();) { + if (words[index] != '$' || index + 1 >= words.size()) { + expanded.push_back(words[index++]); + continue; + } + + if (words[index + 1] == '{') { + const size_t end = words.find('}', index + 2); + if (end == std::string::npos || end == index + 2) { + expanded.push_back(words[index++]); + continue; + } + const std::string name = words.substr(index + 2, end - index - 2); + if (const char *value = std::getenv(name.c_str())) { + expanded += value; + } + index = end + 1; + continue; + } + + if (!variable_start(words[index + 1])) { + expanded.push_back(words[index++]); + continue; + } + + size_t end = index + 2; + while (end < words.size() && variable_char(words[end])) { + ++end; + } + const std::string name = words.substr(index + 1, end - index - 1); + if (const char *value = std::getenv(name.c_str())) { + expanded += value; + } + index = end; + } + + return expanded; +} + +std::string expand_path_word(const char *words) { + return expand_environment_variables(expand_home_directory(words)); +} + +} // namespace + +extern "C" int wordexp(const char *words, wordexp_t *pwordexp, int) { + if (!words || !pwordexp) { + return WRDE_BADVAL; + } + + const std::string expanded = expand_path_word(words); + char **wordv = static_cast(std::calloc(2, sizeof(char *))); + if (!wordv) { + return WRDE_NOSPACE; + } + + wordv[0] = static_cast(std::malloc(expanded.size() + 1)); + if (!wordv[0]) { + std::free(wordv); + return WRDE_NOSPACE; + } + + std::strcpy(wordv[0], expanded.c_str()); + pwordexp->we_wordc = 1; + pwordexp->we_wordv = wordv; + pwordexp->we_offs = 0; + return 0; +} + +extern "C" void wordfree(wordexp_t *pwordexp) { + if (!pwordexp || !pwordexp->we_wordv) { + return; + } + + for (size_t index = 0; index < pwordexp->we_wordc; ++index) { + std::free(pwordexp->we_wordv[index]); + } + std::free(pwordexp->we_wordv); + pwordexp->we_wordc = 0; + pwordexp->we_wordv = nullptr; + pwordexp->we_offs = 0; +} diff --git a/core/wasm_shims/python_c_api_shim.cc b/core/wasm_shims/python_c_api_shim.cc index 396a32e..96fc444 100644 --- a/core/wasm_shims/python_c_api_shim.cc +++ b/core/wasm_shims/python_c_api_shim.cc @@ -1,13 +1,21 @@ #include "boost/python/object.hpp" +#include + extern "C" { static PyTypeObject none_type = {"NoneType"}; static PyObject none_object = {&none_type}; PyObject *PyExc_KeyError = reinterpret_cast(1); -PyObject *PyExc_StopIteration = reinterpret_cast(2); +PyObject *PyExc_KeyboardInterrupt = reinterpret_cast(2); +PyObject *PyExc_RuntimeError = reinterpret_cast(3); +PyObject *PyExc_StopIteration = reinterpret_cast(4); +PyObject *PyExc_TypeError = reinterpret_cast(5); PyObject *_Py_NoneStructPtr = &none_object; +PyTypeObject PyString_Type = {"str"}; +PyTypeObject PyInt_Type = {"int"}; +PyTypeObject PyList_Type = {"list"}; int Py_IsInitialized(void) { return 1; @@ -62,6 +70,10 @@ int PyGen_Check(PyObject *) { return 0; } +int PyObject_IsInstance(PyObject *, PyObject *) { + return 0; +} + PyObject *PyObject_Str(PyObject *) { return &none_object; } @@ -70,7 +82,122 @@ const char *PyUnicode_AsUTF8(PyObject *) { return ""; } +PyObject *PyUnicode_FromString(const char *) { + return &none_object; +} + +void PyErr_SetObject(PyObject *, PyObject *) { +} + +PyObject *PyErr_Format(PyObject *, const char *, ...) { + return nullptr; +} + void Py_DecRef(PyObject *) { } +int PyArg_ParseTuple(PyObject *, const char *, ...) { + return 0; +} + +int PyArg_VaParse(PyObject *, const char *, va_list) { + return 0; +} + +PyObject *PyObject_CallMethod(PyObject *, char *, char *, ...) { + return &none_object; +} + +PyObject *PyObject_GetAttrString(PyObject *, const char *) { + return &none_object; +} + +int PyObject_SetAttrString(PyObject *, const char *, PyObject *) { + return 0; +} + +int PyObject_IsTrue(PyObject *) { + return 0; +} + +PyObject *PyLong_FromLong(long) { + return &none_object; +} + +long PyLong_AsLong(PyObject *) { + return 0; +} + +double PyFloat_AsDouble(PyObject *) { + return 0.0; +} + +PyObject *PyTuple_New(Py_ssize_t) { + return &none_object; +} + +PyObject *PyTuple_GetItem(PyObject *, Py_ssize_t) { + return &none_object; +} + +Py_ssize_t PyTuple_Size(PyObject *) { + return 0; +} + +int PyTuple_SetItem(PyObject *, Py_ssize_t, PyObject *) { + return 0; +} + +PyObject *PyList_New(Py_ssize_t) { + return &none_object; +} + +PyObject *PyList_GetItem(PyObject *, Py_ssize_t) { + return &none_object; +} + +Py_ssize_t PyList_Size(PyObject *) { + return 0; +} + +int PyList_SetItem(PyObject *, Py_ssize_t, PyObject *) { + return 0; +} + +PyObject *PySequence_GetItem(PyObject *, Py_ssize_t) { + return &none_object; +} + +Py_ssize_t PySequence_Length(PyObject *) { + return 0; +} + +PyObject *Py_BuildValue(const char *, ...) { + return &none_object; +} + +PyObject *PyModule_Create(PyModuleDef *) { + return &none_object; +} + +int PyModule_AddObject(PyObject *, const char *, PyObject *) { + return 0; +} + +int PyType_Ready(PyTypeObject *) { + return 0; +} + +PyObject *PyType_GenericNew(PyTypeObject *, PyObject *, PyObject *) { + return &none_object; +} + +void *PyObject_NewShim(std::size_t size, PyTypeObject *type) { + auto *object = static_cast(std::calloc(1, size)); + if (object) { + object->ob_type = type; + } + return object; +} + } diff --git a/core/wasm_shims/pythonplugin/python_plugin.cc b/core/wasm_shims/pythonplugin/python_plugin.cc index dfde789..d7850ed 100644 --- a/core/wasm_shims/pythonplugin/python_plugin.cc +++ b/core/wasm_shims/pythonplugin/python_plugin.cc @@ -1,5 +1,8 @@ #include "pythonplugin/python_plugin.hh" +#include +#include + __attribute__((weak)) std::string handle_pyerror() { return "python disabled in wasm-safe probe"; } @@ -13,7 +16,30 @@ PythonPlugin *PythonPlugin::instantiate(struct _inittab *) { return python_plugin; } -int PythonPlugin::configure(const char *, const char *) { +int PythonPlugin::configure(const char *iniFilename, const char *section) { + if (!section) { + error_msg = "no section"; + status = PLUGIN_NO_SECTION; + return status; + } + + if (!iniFilename) { + iniFilename = std::getenv("INI_FILE_NAME"); + if (!iniFilename) { + error_msg = "no inifile"; + status = PLUGIN_NO_INIFILE; + return status; + } + } + + FILE *file = std::fopen(iniFilename, "r"); + if (!file) { + error_msg = "bad inifile"; + status = PLUGIN_BAD_INIFILE; + return status; + } + std::fclose(file); + status = PLUGIN_OK; return status; } @@ -23,12 +49,17 @@ bool PythonPlugin::is_callable(const char *, const char *) { } int PythonPlugin::call(const char *, - const char *, + const char *callable, boost::python::object, boost::python::object, boost::python::object &retval) { + if (!callable) { + return PLUGIN_NO_CALLABLE; + } + retval = boost::python::object(); - status = PLUGIN_NO_CALLABLE; + exception_msg = handle_pyerror(); + status = PLUGIN_EXCEPTION; return status; } diff --git a/core/wasm_shims/pythonplugin/python_plugin_probe_main.cc b/core/wasm_shims/pythonplugin/python_plugin_probe_main.cc index a95197b..8860bbb 100644 --- a/core/wasm_shims/pythonplugin/python_plugin_probe_main.cc +++ b/core/wasm_shims/pythonplugin/python_plugin_probe_main.cc @@ -8,10 +8,34 @@ int main() { if (!plugin->usable()) { return 2; } + if (plugin->configure(nullptr, nullptr) != PLUGIN_NO_SECTION) { + return 6; + } + if (plugin->configure("/tmp/cnc_sim_missing_python_plugin_probe.ini", "PYTHON") != PLUGIN_BAD_INIFILE) { + return 7; + } boost::python::object retval; if (plugin->run_string("noop", retval, false) != PLUGIN_OK) { return 3; } + if (plugin->call(nullptr, nullptr, boost::python::object(), boost::python::object(), retval) != PLUGIN_NO_CALLABLE) { + return 8; + } + if (plugin->plugin_status() != PLUGIN_OK) { + return 9; + } + if (plugin->call(nullptr, "noop", boost::python::object(), boost::python::object(), retval) != PLUGIN_EXCEPTION) { + return 10; + } + if (plugin->plugin_status() != PLUGIN_EXCEPTION) { + return 11; + } + if (plugin->last_exception().empty()) { + return 12; + } + if (plugin->run_string("noop", retval, false) != PLUGIN_OK) { + return 13; + } if (plugin->is_callable(nullptr, "noop")) { return 4; } @@ -20,4 +44,3 @@ int main() { } return 0; } - diff --git a/core/wasm_shims/structmember.h b/core/wasm_shims/structmember.h new file mode 100644 index 0000000..8c8ea6c --- /dev/null +++ b/core/wasm_shims/structmember.h @@ -0,0 +1,7 @@ +#pragma once + +#include "boost/python/object.hpp" + +#define T_INT 1 +#define T_DOUBLE 2 +#define READONLY 1 diff --git a/core/wasm_shims/tooldata/tooldata_mmap_backend.cc b/core/wasm_shims/tooldata/tooldata_mmap_backend.cc index fffb05c..bc3220e 100644 --- a/core/wasm_shims/tooldata/tooldata_mmap_backend.cc +++ b/core/wasm_shims/tooldata/tooldata_mmap_backend.cc @@ -19,6 +19,7 @@ extern "C" { toolidx_t tooldata_put(CANON_TOOL_TABLE tdata, int idx) { if (!mmap_created) { + std::fprintf(stderr, "%5d tooldata_put() no tool_mmap_base\n", getpid()); return IDX_FAIL; } if (idx < 0 || idx >= CANON_POCKETS_MAX) { @@ -110,7 +111,11 @@ int tool_mmap_creator(EMC_TOOL_STAT const *ptr, int random_toolchanger) { } int tool_mmap_user(void) { - return mmap_created ? 0 : -1; + if (!mmap_created) { + std::fprintf(stderr, "tool_mmap_user(): tool mmap not available\n"); + return -1; + } + return 0; } void tool_mmap_close(void) { diff --git a/docs/linuxcnc-porting.md b/docs/linuxcnc-porting.md index bdf4767..c81fdb1 100644 --- a/docs/linuxcnc-porting.md +++ b/docs/linuxcnc-porting.md @@ -113,13 +113,7 @@ This is a native-only stepping stone. Once stable, the same event sink is used b Status: started. -The public `cnc_sim_api` can now select backends through config JSON: - -```json -{"backend":"smoke"} -``` - -or, in a native build compiled with `CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND`: +The public `cnc_sim_api` defaults to the LinuxCNC RS274 backend and can still select backends through config JSON: ```json {"backend":"linuxcnc-rs274"} @@ -214,4 +208,4 @@ This matrix tracks LinuxCNC feature coverage for the web/WASM simulator. A featu | Probe moves `G38.2/G38.3/G38.4/G38.5` | covered as distinct probe events with LinuxCNC `probe_type` preserved through smoke, native, and source-linked paths | `tests/gcode/linuxcnc_probe_no_error.ngc` | | O-word subroutines, calls, and control flow | covered for numeric and named `O... sub/call/return/endsub` plus `if`/`elseif`/`else`, `while`, `repeat`, `do`/`while`, `break`, and `continue` through LinuxCNC file mode; smoke parser also covers named `O` sub/call/return/endsub and loop-control forms | `tests/gcode/smoke_oword_subprogram.ngc`, `tests/gcode/linuxcnc_named_oword_subprogram.ngc`, `tests/gcode/linuxcnc_oword_control_flow.ngc` | | Broader canned cycles `G73`, `G74`, `G82`-`G89` | partially covered: `G73`, `G74`, `G82`, `G83`, `G84`, `G85`, `G86`, `G87`, `G88`, `G89`; smoke parser maps supported cycles across `G17/G18/G19`, including LinuxCNC's special non-XY `G74/G84` tapping argument order and `G87` plane-specific `I/J/K` mapping; native/source regressions now check non-XY `G74/G84/G86/G87/G88` sequences | `tests/gcode/linuxcnc_canned_cycles_extended.ngc`, `tests/gcode/linuxcnc_canned_cycle_planes.ngc`, smoke API regression | -| Full source-level wasm build | pending | replace Python/HAL/INI/tooldata support dependencies | +| Full source-level wasm build | covered for the current wasm-safe RS274 source set, with remaining work focused on broadening browser-hostile dependency replacement and feature coverage | `./build-wasm.sh`, `test-linuxcnc-wasm-runtime-link.sh`, `test-web-wasm-node-smoke.cjs`, `test-web-wasm-browser-smoke.sh` | diff --git a/docs/linuxcnc-source-policy.md b/docs/linuxcnc-source-policy.md index 283d5fe..98fe1d0 100644 --- a/docs/linuxcnc-source-policy.md +++ b/docs/linuxcnc-source-policy.md @@ -26,7 +26,7 @@ Required workflow for every functional change: Current temporary exceptions: -- The smoke parser exists only to exercise the public API and UI while the LinuxCNC RS274 backend is being wired in. +- The smoke parser exists only to exercise the public API and UI around remaining fallback paths; native, source-linked, and wasm smoke coverage now route LinuxCNC RS274 behavior through the LinuxCNC-backed interpreter. - RTCP pivot behavior is limited to LinuxCNC source-backed switchkins paths; unsupported switchkins types must not fall back to hand-derived geometry. Both exceptions should shrink over time. New functional behavior should not be added to them unless it is directly copied from, or routed to, LinuxCNC source code. diff --git a/generate-linuxcnc-puma560-dh-parameters.sh b/generate-linuxcnc-puma560-dh-parameters.sh new file mode 100644 index 0000000..d950962 --- /dev/null +++ b/generate-linuxcnc-puma560-dh-parameters.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +source_path=${1:-configs/sim/axis/vismach/puma/puma560_dh.hal} +source_file="$linuxcnc_root/$source_path" + +if [[ ! -f "$source_file" ]]; then + echo "missing LinuxCNC puma560 DH HAL source: $source_file" >&2 + exit 1 +fi + +awk -v source_path="$source_path" ' +function fail(message) { + print message > "/dev/stderr" + exit 1 +} + +$1 == "setp" && $2 ~ /^genserkins\.(A|ALPHA|D)-[0-5]$/ { + split($2, qualified, ".") + split(qualified[2], name_index, "-") + name = name_index[1] + index = name_index[2] + 0 + values[name, index] = $3 +} + +END { + names[0] = "A" + arrays["A"] = "a" + names[1] = "ALPHA" + arrays["ALPHA"] = "alpha" + names[2] = "D" + arrays["D"] = "d" + + print "// Generated by ./generate-linuxcnc-puma560-dh-parameters.sh." + print "// Source: linuxcnc/" source_path " setp genserkins.{A,ALPHA,D}-N values." + + for (n = 0; n < 3; n++) { + name = names[n] + array = arrays[name] + for (i = 0; i < 6; i++) { + key = name SUBSEP i + if (!(key in values)) { + fail("missing " name "-" i " in " source_path) + } + print " parameters." array "[" i "] = " values[key] ";" + } + if (n < 2) { + print "" + } + } +} +' "$source_file" diff --git a/generate-linuxcnc-switchkins-remap-table.sh b/generate-linuxcnc-switchkins-remap-table.sh new file mode 100755 index 0000000..05969dc --- /dev/null +++ b/generate-linuxcnc-switchkins-remap-table.sh @@ -0,0 +1,238 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +manifest=${1:-linuxcnc-kinematics-source-files.txt} + +if [[ ! -d "$linuxcnc_root" ]]; then + echo "missing LinuxCNC root: $linuxcnc_root" >&2 + exit 1 +fi +if [[ ! -f "$manifest" ]]; then + echo "missing kinematics manifest: $manifest" >&2 + exit 1 +fi + +linuxcnc_root=$(cd "$linuxcnc_root" && pwd) + +normalize_alias() { + tr '[:upper:]' '[:lower:]' | tr -d '[:space:]' +} + +trim_value() { + sed 's/[[:space:]]*[#;].*$//; s/^[[:space:]]*//; s/[[:space:]]*$//' +} + +ini_value() { + local file=$1 + local key=$2 + sed -n "s/^[[:space:]]*$key[[:space:]]*=[[:space:]]*//Ip" "$file" | head -n 1 | trim_value +} + +ini_values() { + local file=$1 + local key=$2 + sed -n "s/^[[:space:]]*$key[[:space:]]*=[[:space:]]*//Ip" "$file" | trim_value +} + +remap_ngc_name() { + local file=$1 + local mcode=$2 + sed -n "s/.*REMAP[[:space:]]*=[[:space:]]*M$mcode[[:space:]][^#;]*ngc[[:space:]]*=[[:space:]]*\\([^[:space:]#;]*\\).*/\\1/Ip" "$file" | + head -n 1 +} + +resolve_config_path() { + local config_dir=$1 + local path=$2 + if [[ "$path" = /* ]]; then + printf '%s\n' "$path" + else + printf '%s\n' "$config_dir/$path" + fi +} + +relative_linuxcnc_path() { + local path=$1 + path=$(cd "$(dirname "$path")" && pwd)/$(basename "$path") + printf '%s\n' "${path#"$linuxcnc_root/"}" +} + +remap_source_for_mcode() { + local config=$1 + local mcode=$2 + local ngc_name + ngc_name=$(remap_ngc_name "$config" "$mcode") + if [[ -z "$ngc_name" ]]; then + return 1 + fi + if [[ "$ngc_name" != *.ngc ]]; then + ngc_name="$ngc_name.ngc" + fi + + local config_dir + config_dir=$(cd "$(dirname "$config")" && pwd) + local subroutine_paths + subroutine_paths=$(ini_values "$config" "SUBROUTINE_PATH") + if [[ -z "$subroutine_paths" ]]; then + subroutine_paths="." + fi + + local subroutine_path + while IFS= read -r subroutine_path; do + [[ -n "$subroutine_path" ]] || continue + local path_part + IFS=: read -ra path_parts <<<"$subroutine_path" + for path_part in "${path_parts[@]}"; do + [[ -n "$path_part" ]] || continue + local candidate_dir + candidate_dir=$(resolve_config_path "$config_dir" "$path_part") + if [[ -f "$candidate_dir/$ngc_name" ]]; then + relative_linuxcnc_path "$candidate_dir/$ngc_name" + return 0 + fi + done + done <<<"$subroutine_paths" + + return 1 +} + +kinstype_for_mcode() { + local config=$1 + local mcode=$2 + local source + if ! source=$(remap_source_for_mcode "$config" "$mcode"); then + printf '%s\n' "-1" + return 0 + fi + sed -n 's/^[[:space:]]*#[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/p' \ + "$linuxcnc_root/$source" | + head -n 1 +} + +json_compact_string() { + local value=$1 + printf '%s' "$value" | normalize_alias | sed 's/\\/\\\\/g; s/"/\\"/g' +} + +declare -A seen_aliases=() +entries=() + +add_alias() { + local alias=$1 + local m428=$2 + local m429=$3 + local m430=$4 + alias=$(json_compact_string "$alias") + [[ -n "$alias" ]] || return 0 + + local types="$m428,$m429,$m430" + if [[ -n "${seen_aliases[$alias]:-}" ]]; then + if [[ "${seen_aliases[$alias]}" != "$types" ]]; then + echo "ambiguous switchkins alias '$alias': ${seen_aliases[$alias]} and $types" >&2 + exit 1 + fi + return 0 + fi + + seen_aliases[$alias]=$types + entries+=(" {\"$alias\", $m428, $m429, $m430},") +} + +add_machine_aliases() { + local machine=$1 + local m428=$2 + local m429=$3 + local m430=$4 + [[ -n "$machine" ]] || return 0 + add_alias "$machine" "$m428" "$m429" "$m430" + add_alias "${machine%%(*}" "$m428" "$m429" "$m430" +} + +add_kinematics_aliases() { + local kinematics=$1 + local m428=$2 + local m429=$3 + local m430=$4 + [[ -n "$kinematics" ]] || return 0 + add_alias "$kinematics" "$m428" "$m429" "$m430" + + local first_word=${kinematics%%[[:space:]]*} + add_alias "$first_word" "$m428" "$m429" "$m430" + add_alias "${first_word%_kins}" "$m428" "$m429" "$m430" + add_alias "${first_word%-kins}" "$m428" "$m429" "$m430" + add_alias "$(printf '%s' "$first_word" | tr '_' '-')" "$m428" "$m429" "$m430" + add_alias "$(printf '%s' "${first_word%_kins}" | tr '_' '-')" "$m428" "$m429" "$m430" + add_alias "$(printf '%s' "${first_word%-kins}" | tr '_' '-')" "$m428" "$m429" "$m430" +} + +add_halfile_aliases() { + local halfile=$1 + local m428=$2 + local m429=$3 + local m430=$4 + [[ -n "$halfile" ]] || return 0 + [[ "$halfile" == LIB:* ]] && return 0 + add_alias "$halfile" "$m428" "$m429" "$m430" + local hal_base=${halfile##*/} + add_alias "$hal_base" "$m428" "$m429" "$m430" + add_alias "${hal_base%.hal}" "$m428" "$m429" "$m430" +} + +while IFS=: read -r group path _note; do + [[ "$group" == "config" && "$path" == *.ini ]] || continue + + config="$linuxcnc_root/$path" + [[ -f "$config" ]] || { + echo "missing LinuxCNC config source: $path" >&2 + exit 1 + } + if ! grep -Eiq 'REMAP[[:space:]]*=[[:space:]]*M(428|429|430)([^0-9]|$)' "$config"; then + continue + fi + + m428=$(kinstype_for_mcode "$config" 428) + m429=$(kinstype_for_mcode "$config" 429) + m430=$(kinstype_for_mcode "$config" 430) + [[ -n "$m428" ]] || m428=-1 + [[ -n "$m429" ]] || m429=-1 + [[ -n "$m430" ]] || m430=-1 + + machine=$(ini_value "$config" "MACHINE") + kinematics=$(ini_value "$config" "KINEMATICS") + halfiles=$(ini_values "$config" "HALFILE") + config_dir=${path%/*} + config_base=${path##*/} + config_stem=${config_base%.ini} + + add_alias "$path" "$m428" "$m429" "$m430" + add_alias "$config_dir" "$m428" "$m429" "$m430" + add_alias "${config_dir##*/}" "$m428" "$m429" "$m430" + add_alias "$config_stem" "$m428" "$m429" "$m430" + add_machine_aliases "$machine" "$m428" "$m429" "$m430" + add_kinematics_aliases "$kinematics" "$m428" "$m429" "$m430" + while IFS= read -r halfile; do + add_halfile_aliases "$halfile" "$m428" "$m429" "$m430" + done <<<"$halfiles" + + for mcode in 428 429 430; do + if remap_source=$(remap_source_for_mcode "$config" "$mcode"); then + remap_dir=${remap_source%/*} + add_alias "$remap_dir" "$m428" "$m429" "$m430" + add_alias "${remap_dir%/*}" "$m428" "$m429" "$m430" + add_alias "${remap_dir%/remap_subs}" "$m428" "$m429" "$m430" + remap_parent=${remap_dir%/remap_subs} + add_alias "${remap_parent##*/}" "$m428" "$m429" "$m430" + fi + done +done <"$manifest" + +cat <<'EOF' +// Generated by ./generate-linuxcnc-switchkins-remap-table.sh. +// Source: LinuxCNC INI MACHINE/KINEMATICS/HALFILE/SUBROUTINE_PATH/REMAP entries and +// adjacent remap_subs/{428,429,430}remap.ngc # assignments. +EOF + +printf '%s\n' "${entries[@]}" diff --git a/linuxcnc-kinematics-source-files.txt b/linuxcnc-kinematics-source-files.txt new file mode 100644 index 0000000..bca01a0 --- /dev/null +++ b/linuxcnc-kinematics-source-files.txt @@ -0,0 +1,98 @@ +# LinuxCNC kinematics source manifest for the wasm simulator port. +# Format: group:path:note +core:src/emc/kinematics/5axiskins.c:5-axis bridge mill kinematics used by M428/M429/M430 RTCP tests +core:src/emc/kinematics/corexykins.c:CoreXY kinematics module +core:src/emc/kinematics/cubic.c:cubic solver support for kinematics +core:src/emc/kinematics/genhexkins.c:generic hexapod kinematics module +core:src/emc/kinematics/genserfuncs.c:generic serial kinematics support functions +core:src/emc/kinematics/genserkins.c:generic serial kinematics module +core:src/emc/kinematics/kins_util.c:identity and coordinate utility kinematics functions +core:src/emc/kinematics/lineardeltakins.c:linear delta kinematics C module +core:src/emc/kinematics/lineardeltakins.cc:linear delta kinematics C++ module +core:src/emc/kinematics/maxkins.c:max kinematics module +core:src/emc/kinematics/pentakins.c:penta kinematics module +core:src/emc/kinematics/pumakins.c:PUMA kinematics module used by M428/M429/M430 coverage +core:src/emc/kinematics/rosekins.c:rose kinematics module +core:src/emc/kinematics/rotarydeltakins.c:rotary delta kinematics C module +core:src/emc/kinematics/rotarydeltakins.cc:rotary delta kinematics C++ module +core:src/emc/kinematics/rotatekins.c:rotate kinematics module +core:src/emc/kinematics/scarakins.c:SCARA kinematics module used by M428/M429/M430 coverage +core:src/emc/kinematics/scorbot-kins.c:Scorbot kinematics module +core:src/emc/kinematics/switchkins.c:switchable kinematics support +core:src/emc/kinematics/tripodkins.c:tripod kinematics module +core:src/emc/kinematics/trivkins.c:trivial kinematics module +core:src/emc/kinematics/trtfuncs.c:XYZBC/XYZAC table rotary tilting kinematics used by M428/M429/M430 coverage +core:src/emc/kinematics/ugenserkins.c:user generic serial kinematics module +core:src/emc/kinematics/userkfuncs.c:user kinematics helper functions used by M428/M429/M430 coverage +core:src/emc/kinematics/xyzac-trt-kins.c:XYZAC table rotary tilting switchkins module +core:src/emc/kinematics/xyzbc-trt-kins.c:XYZBC table rotary tilting switchkins module +config:configs/sim/axis/vismach/5axis/bridgemill/5axis.ini:bridgemill MACHINE/KINEMATICS and M428/M429/M430 remap configuration source +config:configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini:XYZAB table dual rotary MACHINE/KINEMATICS and M428/M429 remap configuration source +config:configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini:XYZAC TRT switchkins HAL parameter source used by M428 coverage +config:configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini:XYZBC TRT switchkins HAL parameter source used by M428 coverage +config:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini:XYZACB TRSRN MACHINE/KINEMATICS and M428/M429/M430 remap configuration source +config:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini:XYZBCA TRSRN MACHINE/KINEMATICS and M428/M429/M430 remap configuration source +config:configs/sim/axis/vismach/hexapod-sim/hexapod.ini:hexapod MACHINE/KINEMATICS and M428/M429/M430 remap configuration source +config:configs/sim/axis/vismach/melfa-sim/melfa.ini:melfa MACHINE/KINEMATICS and M428/M429/M430 remap configuration source +config:configs/sim/axis/vismach/millturn/millturn.ini:millturn MACHINE/KINEMATICS and M428/M429 remap configuration source +config:configs/sim/axis/vismach/puma/puma.ini:PUMA MACHINE/KINEMATICS and M428/M429/M430 remap configuration source +config:configs/sim/axis/vismach/puma/puma_cube.ini:PUMA cube MACHINE/KINEMATICS and M428/M429/M430 remap configuration source +config:configs/sim/axis/vismach/puma/puma560.ini:PUMA 560 MACHINE/KINEMATICS and M428/M429/M430 remap configuration source +config:configs/sim/axis/vismach/puma/puma560_dh.hal:PUMA 560 genserkins DH HAL parameters used by M428 coverage +config:configs/sim/axis/vismach/puma/puma560_uvw.ini:PUMA 560 UVW MACHINE/KINEMATICS and M428/M429/M430 remap configuration source +config:configs/sim/axis/vismach/scara/scara.ini:AXIS SCARA MACHINE/KINEMATICS and M428/M429/M430 remap configuration source +config:configs/sim/qtaxis/non-trivial/scara/scara.ini:QtAxis SCARA MACHINE/KINEMATICS and M428/M429/M430 remap configuration source +config:configs/sim/qtvcp_screens/non-trivial/scara/scara.ini:QtVCP SCARA MACHINE/KINEMATICS and M428/M429/M430 remap configuration source +remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting bridgemill kinstype 0 +remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting bridgemill kinstype 1 +remap:configs/sim/axis/vismach/5axis/bridgemill/remap_subs/430remap.ngc:LinuxCNC M430 remap source selecting bridgemill kinstype 2 +remap:configs/sim/axis/vismach/5axis/table-dual-rotary/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting table dual rotary kinstype 1 +remap:configs/sim/axis/vismach/5axis/table-dual-rotary/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting table dual rotary kinstype 0 +remap:configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting table rotary tilting kinstype 1 +remap:configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting table rotary tilting kinstype 0 +remap:configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/430remap.ngc:LinuxCNC M430 remap source selecting table rotary tilting kinstype 2 +remap:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting table rotary spindle rotary nutating kinstype 0 +remap:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting table rotary spindle rotary nutating kinstype 1 +remap:configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/430remap.ngc:LinuxCNC M430 remap source selecting table rotary spindle rotary nutating kinstype 2 +remap:configs/sim/axis/vismach/hexapod-sim/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting hexapod sim kinstype 0 +remap:configs/sim/axis/vismach/hexapod-sim/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting hexapod sim kinstype 1 +remap:configs/sim/axis/vismach/hexapod-sim/remap_subs/430remap.ngc:LinuxCNC M430 remap source selecting hexapod sim kinstype 2 +remap:configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting melfa sim kinstype 0 +remap:configs/sim/axis/vismach/melfa-sim/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting melfa sim kinstype 1 +remap:configs/sim/axis/vismach/melfa-sim/remap_subs/430remap.ngc:LinuxCNC M430 remap source selecting melfa sim kinstype 2 +remap:configs/sim/axis/vismach/millturn/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting millturn mill kinstype 0 +remap:configs/sim/axis/vismach/millturn/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting millturn turn kinstype 1 +remap:configs/sim/axis/vismach/puma/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting puma kinstype 0 +remap:configs/sim/axis/vismach/puma/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting puma kinstype 1 +remap:configs/sim/axis/vismach/puma/remap_subs/430remap.ngc:LinuxCNC M430 remap source selecting puma kinstype 2 +remap:configs/sim/axis/vismach/scara/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting scara kinstype 0 +remap:configs/sim/axis/vismach/scara/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting scara kinstype 1 +remap:configs/sim/axis/vismach/scara/remap_subs/430remap.ngc:LinuxCNC M430 remap source selecting scara kinstype 2 +remap:configs/sim/qtaxis/non-trivial/scara/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting qtaxis scara kinstype 0 +remap:configs/sim/qtaxis/non-trivial/scara/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting qtaxis scara kinstype 1 +remap:configs/sim/qtaxis/non-trivial/scara/remap_subs/430remap.ngc:LinuxCNC M430 remap source selecting qtaxis scara kinstype 2 +remap:configs/sim/qtvcp_screens/non-trivial/scara/remap_subs/428remap.ngc:LinuxCNC M428 remap source selecting qtvcp scara kinstype 0 +remap:configs/sim/qtvcp_screens/non-trivial/scara/remap_subs/429remap.ngc:LinuxCNC M429 remap source selecting qtvcp scara kinstype 1 +remap:configs/sim/qtvcp_screens/non-trivial/scara/remap_subs/430remap.ngc:LinuxCNC M430 remap source selecting qtvcp scara kinstype 2 +component:src/hal/components/xyzab_tdr_kins.comp:XYZAB table dual rotary switchkins component source used by M428/M429/M430 coverage +component:src/hal/components/xyzacb_trsrn.comp:XYZACB transformation component source adjacent to LinuxCNC RTCP kinematics coverage +component:src/hal/components/xyzbca_trsrn.comp:XYZBCA transformation component source adjacent to LinuxCNC RTCP kinematics coverage +component:src/hal/components/matrixkins.comp:matrix kinematics component source for LinuxCNC kinematics completeness +component:src/hal/components/userkins.comp:user kinematics component template source for LinuxCNC switchkins/user kinematics coverage +generated:src/objects/hal/components/xyzab_tdr_kins.c:halcompile-generated XYZAB table dual rotary switchkins C source used by the adapter +generated:src/objects/hal/components/xyzacb_trsrn.c:halcompile-generated XYZACB transformation C source +generated:src/objects/hal/components/xyzbca_trsrn.c:halcompile-generated XYZBCA transformation C source +generated:src/objects/hal/components/matrixkins.c:halcompile-generated matrix kinematics C source +generated:src/objects/hal/components/userkins.c:halcompile-generated user kinematics C source +header:src/emc/kinematics/cubic.h:cubic solver header +header:src/emc/kinematics/genhexkins.h:generic hexapod kinematics header +header:src/emc/kinematics/genserkins.h:generic serial kinematics header +header:src/emc/kinematics/kinematics.h:LinuxCNC kinematics declarations +header:src/emc/kinematics/lineardeltakins-common.h:linear delta shared header +header:src/emc/kinematics/pentakins.h:penta kinematics header +header:src/emc/kinematics/pumakins.h:PUMA kinematics header +header:src/emc/kinematics/rotarydeltakins-common.h:rotary delta shared header +header:src/emc/kinematics/switchkins.h:switchable kinematics header +metadata:src/emc/kinematics/Submakefile:LinuxCNC kinematics make build metadata +metadata:src/emc/kinematics/meson.build:LinuxCNC kinematics meson build metadata +asset:src/emc/kinematics/blend.fig:LinuxCNC kinematics reference asset tracked for full directory coverage diff --git a/linuxcnc-rs274-source-files.txt b/linuxcnc-rs274-source-files.txt index 1a58e95..1f7af1e 100644 --- a/linuxcnc-rs274-source-files.txt +++ b/linuxcnc-rs274-source-files.txt @@ -50,3 +50,5 @@ header:src/emc/rs274ngc/rs274ngc.hh:LinuxCNC rs274 public interface header header:src/emc/rs274ngc/rs274ngc_interp.hh:LinuxCNC rs274 interpreter class header header:src/emc/rs274ngc/rs274ngc_return.hh:LinuxCNC rs274 return code header header:src/emc/rs274ngc/units.h:LinuxCNC rs274 units helper header +metadata:src/emc/rs274ngc/Submakefile:LinuxCNC rs274 make build metadata tracked for full directory coverage +metadata:src/emc/rs274ngc/meson.build:LinuxCNC rs274 meson build metadata tracked for full directory coverage diff --git a/linuxcnc-rs274-wasm-source-files.txt b/linuxcnc-rs274-wasm-source-files.txt index 0773882..f80331e 100644 --- a/linuxcnc-rs274-wasm-source-files.txt +++ b/linuxcnc-rs274-wasm-source-files.txt @@ -26,6 +26,14 @@ core:src/emc/rs274ngc/rs274ngc_pre.cc:Interp implementation covered by rs274ngc_ core:src/emc/tooldata/tooldata_common.cc:shared tool table parser/format logic linked with wasm-safe mmap backend core:src/emc/ini/inifile.cc:INI reader needed by rs274ngc_pre source-link probe blocked:src/emc/tooldata/tooldata_mmap.cc:native mmap implementation +blocked:src/emc/rs274ngc/canonmodule.cc:Python module binding, tracked for full rs274ngc source coverage +blocked:src/emc/rs274ngc/gcodemodule.cc:Python gcode module binding, tracked for full rs274ngc source coverage +blocked:src/emc/rs274ngc/interpmodule.cc:Boost.Python interpreter module binding, tracked for full rs274ngc source coverage +blocked:src/emc/rs274ngc/pyarrays.cc:Boost.Python array binding, tracked for full rs274ngc source coverage +blocked:src/emc/rs274ngc/pyblock.cc:Boost.Python block binding, tracked for full rs274ngc source coverage +blocked:src/emc/rs274ngc/pyemctypes.cc:Boost.Python EMC type binding, tracked for full rs274ngc source coverage +blocked:src/emc/rs274ngc/pyinterp1.cc:Boost.Python interpreter binding, tracked for full rs274ngc source coverage +blocked:src/emc/rs274ngc/pyparamclass.cc:Boost.Python parameter binding, tracked for full rs274ngc source coverage header:src/emc/rs274ngc/array1.hh:LinuxCNC rs274 header tracked for full interpreter source coverage blocked-header:src/emc/rs274ngc/boost_pyenum_macros.hh:Boost.Python helper header, tracked but not wasm-safe API surface header:src/emc/rs274ngc/interp_array_types.hh:LinuxCNC rs274 block array type definitions @@ -42,3 +50,5 @@ header:src/emc/rs274ngc/rs274ngc.hh:LinuxCNC rs274 public interface header header:src/emc/rs274ngc/rs274ngc_interp.hh:LinuxCNC rs274 interpreter class header header:src/emc/rs274ngc/rs274ngc_return.hh:LinuxCNC rs274 return code header header:src/emc/rs274ngc/units.h:LinuxCNC rs274 units helper header +metadata:src/emc/rs274ngc/Submakefile:LinuxCNC rs274 make build metadata tracked for full directory coverage +metadata:src/emc/rs274ngc/meson.build:LinuxCNC rs274 meson build metadata tracked for full directory coverage diff --git a/list-cnc-sim-core-sources.sh b/list-cnc-sim-core-sources.sh index d0a40f5..411d132 100755 --- a/list-cnc-sim-core-sources.sh +++ b/list-cnc-sim-core-sources.sh @@ -3,9 +3,37 @@ set -euo pipefail cd "$(dirname "$0")" -filter=${1:-} +include_smoke=0 +filter= +for arg in "$@"; do + case "$arg" in + --with-smoke) + include_smoke=1 + ;; + --*) + echo "unknown cnc sim core source option: $arg" >&2 + exit 1 + ;; + *) + if [[ -n "$filter" ]]; then + echo "usage: $0 [--with-smoke] [filter]" >&2 + exit 1 + fi + filter=$arg + ;; + esac +done +source_list=$(mktemp) +trap 'rm -f "$source_list"' EXIT -./list-cmake-set-sources.sh cnc_sim_core_sources core/ | ./list-filtered-existing-paths.sh \ +{ + ./list-cmake-set-sources.sh cnc_sim_core_sources core/ + if [[ "$include_smoke" -eq 1 ]]; then + ./list-cmake-set-sources.sh cnc_sim_smoke_backend_sources core/ + fi +} > "$source_list" + +./list-filtered-existing-paths.sh \ "$filter" \ "missing cnc sim core source" \ - "missing cnc sim core source in CMake list" + "missing cnc sim core source in CMake list" < "$source_list" diff --git a/list-linuxcnc-kinematics-manifest-sources.sh b/list-linuxcnc-kinematics-manifest-sources.sh new file mode 100755 index 0000000..5d79351 --- /dev/null +++ b/list-linuxcnc-kinematics-manifest-sources.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +manifest=${1:-linuxcnc-kinematics-source-files.txt} +filter_group=${2:-core} +output_mode=${3:-relative} + +./list-linuxcnc-manifest-sources.sh \ + "$manifest" \ + "$filter_group" \ + "$output_mode" \ + core,config,remap,component,generated,header,metadata,asset \ + all \ + "missing kinematics manifest source" diff --git a/list-linuxcnc-rs274-api-project-sources.sh b/list-linuxcnc-rs274-api-project-sources.sh index cc25782..05588ec 100755 --- a/list-linuxcnc-rs274-api-project-sources.sh +++ b/list-linuxcnc-rs274-api-project-sources.sh @@ -4,8 +4,15 @@ set -euo pipefail cd "$(dirname "$0")" filter=${1:-} +source_list=$(mktemp) +trap 'rm -f "$source_list"' EXIT -./list-cmake-set-plus-append-sources.sh cnc_sim_core_sources cnc_sim_core_sources core/ | ./list-filtered-existing-paths.sh \ +{ + ./list-cmake-set-sources.sh cnc_sim_core_sources core/ + ./list-cmake-set-sources.sh cnc_sim_linuxcnc_rs274_backend_sources core/ +} > "$source_list" + +./list-filtered-existing-paths.sh \ "$filter" \ "missing LinuxCNC rs274 API project source" \ - "missing LinuxCNC rs274 API project source in CMake list" + "missing LinuxCNC rs274 API project source in CMake list" < "$source_list" diff --git a/list-linuxcnc-source-common-cxxflags.sh b/list-linuxcnc-source-common-cxxflags.sh index bebe13a..da110da 100755 --- a/list-linuxcnc-source-common-cxxflags.sh +++ b/list-linuxcnc-source-common-cxxflags.sh @@ -53,6 +53,7 @@ fi printf '%s\n' \ -std=c++17 \ + -fpermissive \ $([[ "$emit_ulapi" -eq 1 ]] && printf '%s\n' -DULAPI) \ $([[ "$syntax_only" -eq 1 ]] && printf '%s\n' -fsyntax-only) \ "${python_includes[@]}" \ diff --git a/list-linuxcnc-source-manifest-sources.sh b/list-linuxcnc-source-manifest-sources.sh index d29b77e..f5118d5 100755 --- a/list-linuxcnc-source-manifest-sources.sh +++ b/list-linuxcnc-source-manifest-sources.sh @@ -11,6 +11,6 @@ output_mode=${3:-relative} "$manifest" \ "$filter_group" \ "$output_mode" \ - core,header,binding,tooldata \ + core,header,metadata,binding,tooldata \ core \ "missing manifest file" diff --git a/list-linuxcnc-wasm-common-cxxflags.sh b/list-linuxcnc-wasm-common-cxxflags.sh index 2a108c8..feada0e 100755 --- a/list-linuxcnc-wasm-common-cxxflags.sh +++ b/list-linuxcnc-wasm-common-cxxflags.sh @@ -101,6 +101,7 @@ LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only printf '%s\n' \ "$std_flag" \ -DULAPI \ + -DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE \ $([[ "$syntax_only" -eq 1 ]] && printf '%s\n' -fsyntax-only) \ $([[ "$include_wctype" -eq 1 ]] && printf '%s\n' -include wctype.h) \ $([[ "$function_sections" -eq 1 ]] && printf '%s\n' -ffunction-sections) \ diff --git a/list-linuxcnc-wasm-manifest-sources.sh b/list-linuxcnc-wasm-manifest-sources.sh index 7802239..a477020 100755 --- a/list-linuxcnc-wasm-manifest-sources.sh +++ b/list-linuxcnc-wasm-manifest-sources.sh @@ -11,6 +11,6 @@ output_mode=${3:-relative} "$manifest" \ "$filter_group" \ "$output_mode" \ - core,header,blocked,blocked-header \ + core,header,metadata,blocked,blocked-header \ all \ "missing manifest source" diff --git a/list-project-source-set.sh b/list-project-source-set.sh index 7d068ba..b0c2272 100755 --- a/list-project-source-set.sh +++ b/list-project-source-set.sh @@ -17,6 +17,44 @@ case "$source_set" in core/src/canon_event_sink.cpp core/src/linuxcnc_canon_bridge.cpp core/src/linuxcnc_tooldata_fixture.cpp + core/src/linuxcnc_5axiskins_adapter.c + core/src/linuxcnc_5axiskins_adapter.cpp + core/src/linuxcnc_corexykins_adapter.c + core/src/linuxcnc_corexykins_adapter.cpp + core/src/linuxcnc_cubic_adapter.c + core/src/linuxcnc_cubic_adapter.cpp + core/src/linuxcnc_genserfuncs_adapter.c + core/src/linuxcnc_genserfuncs_adapter.cpp + core/src/linuxcnc_genhexkins_adapter.c + core/src/linuxcnc_genhexkins_adapter.cpp + core/src/linuxcnc_kins_util_adapter.c + core/src/linuxcnc_kins_util_adapter.cpp + core/src/linuxcnc_lineardeltakins_adapter.c + core/src/linuxcnc_lineardeltakins_adapter.cpp + core/src/linuxcnc_maxkins_adapter.c + core/src/linuxcnc_maxkins_adapter.cpp + core/src/linuxcnc_pentakins_adapter.c + core/src/linuxcnc_pentakins_adapter.cpp + core/src/linuxcnc_pumakins_adapter.c + core/src/linuxcnc_pumakins_adapter.cpp + core/src/linuxcnc_rotatekins_adapter.c + core/src/linuxcnc_rotatekins_adapter.cpp + core/src/linuxcnc_rosekins_adapter.c + core/src/linuxcnc_rosekins_adapter.cpp + core/src/linuxcnc_rotarydeltakins_adapter.c + core/src/linuxcnc_rotarydeltakins_adapter.cpp + core/src/linuxcnc_scarakins_adapter.c + core/src/linuxcnc_scarakins_adapter.cpp + core/src/linuxcnc_scorbot_kins_adapter.c + core/src/linuxcnc_scorbot_kins_adapter.cpp + core/src/linuxcnc_tripodkins_adapter.c + core/src/linuxcnc_tripodkins_adapter.cpp + core/src/linuxcnc_trtfuncs_adapter.c + core/src/linuxcnc_trtfuncs_adapter.cpp + core/src/linuxcnc_userkfuncs_adapter.c + core/src/linuxcnc_userkfuncs_adapter.cpp + core/src/linuxcnc_xyzab_tdr_kins_adapter.c + core/src/linuxcnc_xyzab_tdr_kins_adapter.cpp core/src/rtcp_kinematics.cpp core/src/simulator_gcode_controls.cpp core/tools/linuxcnc_rs274_dump.cpp @@ -28,6 +66,44 @@ case "$source_set" in sources=( core/src/canon_event_sink.cpp core/src/linuxcnc_canon_bridge.cpp + core/src/linuxcnc_5axiskins_adapter.c + core/src/linuxcnc_5axiskins_adapter.cpp + core/src/linuxcnc_corexykins_adapter.c + core/src/linuxcnc_corexykins_adapter.cpp + core/src/linuxcnc_cubic_adapter.c + core/src/linuxcnc_cubic_adapter.cpp + core/src/linuxcnc_genserfuncs_adapter.c + core/src/linuxcnc_genserfuncs_adapter.cpp + core/src/linuxcnc_genhexkins_adapter.c + core/src/linuxcnc_genhexkins_adapter.cpp + core/src/linuxcnc_kins_util_adapter.c + core/src/linuxcnc_kins_util_adapter.cpp + core/src/linuxcnc_lineardeltakins_adapter.c + core/src/linuxcnc_lineardeltakins_adapter.cpp + core/src/linuxcnc_maxkins_adapter.c + core/src/linuxcnc_maxkins_adapter.cpp + core/src/linuxcnc_pentakins_adapter.c + core/src/linuxcnc_pentakins_adapter.cpp + core/src/linuxcnc_pumakins_adapter.c + core/src/linuxcnc_pumakins_adapter.cpp + core/src/linuxcnc_rotatekins_adapter.c + core/src/linuxcnc_rotatekins_adapter.cpp + core/src/linuxcnc_rosekins_adapter.c + core/src/linuxcnc_rosekins_adapter.cpp + core/src/linuxcnc_rotarydeltakins_adapter.c + core/src/linuxcnc_rotarydeltakins_adapter.cpp + core/src/linuxcnc_scarakins_adapter.c + core/src/linuxcnc_scarakins_adapter.cpp + core/src/linuxcnc_scorbot_kins_adapter.c + core/src/linuxcnc_scorbot_kins_adapter.cpp + core/src/linuxcnc_tripodkins_adapter.c + core/src/linuxcnc_tripodkins_adapter.cpp + core/src/linuxcnc_trtfuncs_adapter.c + core/src/linuxcnc_trtfuncs_adapter.cpp + core/src/linuxcnc_userkfuncs_adapter.c + core/src/linuxcnc_userkfuncs_adapter.cpp + core/src/linuxcnc_xyzab_tdr_kins_adapter.c + core/src/linuxcnc_xyzab_tdr_kins_adapter.cpp core/src/rtcp_kinematics.cpp ) missing_source_message="missing LinuxCNC bridge smoke project source" diff --git a/test-all-native.sh b/test-all-native.sh index 202c634..d6d1f62 100755 --- a/test-all-native.sh +++ b/test-all-native.sh @@ -120,6 +120,7 @@ if grep -RInF "${forbidden_link_flags_process_substitution}LINUXCNC_ROOT=\"\$lin fi syntax_only_source_common_flags=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh --syntax-only) grep -Fx -- "-fsyntax-only" <<<"$syntax_only_source_common_flags" >/dev/null +grep -Fx -- "-fpermissive" <<<"$syntax_only_source_common_flags" >/dev/null core_include_source_common_flags=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh --core-include) grep -Fx -- "-I" <<<"$core_include_source_common_flags" >/dev/null grep -Fx -- "core/include" <<<"$core_include_source_common_flags" >/dev/null @@ -147,7 +148,11 @@ grep -F "target_link_libraries(cnc_sim_linuxcnc_canon_bridge PRIVATE cnc_sim_cor grep -F './list-linuxcnc-rs274-dump-project-sources.sh > "$project_source_list"' test-linuxcnc-source-link.sh >/dev/null grep -F './list-linuxcnc-rs274-dump-project-sources.sh > "$project_source_list"' test-linuxcnc-rs274-native.sh >/dev/null grep -F './list-linuxcnc-rs274-api-project-sources.sh > "$project_source_list"' test-linuxcnc-api-native.sh >/dev/null -grep -F './list-cnc-sim-core-sources.sh > "$core_source_list"' test-native.sh >/dev/null +grep -F './list-cnc-sim-core-sources.sh --with-smoke > "$core_source_list"' test-native.sh >/dev/null +grep -F -- '-DCNC_SIM_ENABLE_SMOKE_BACKEND' test-native.sh >/dev/null +grep -F 'option(CNC_SIM_ENABLE_SMOKE_BACKEND "Build the legacy explicit smoke G-code backend test harness" OFF)' core/CMakeLists.txt >/dev/null +grep -F 'CNC_SIM_ENABLE_SMOKE_BACKEND cannot be combined with CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND' core/CMakeLists.txt >/dev/null +grep -F 'smoke backend is not compiled into this build' core/src/gcode_backend.cpp >/dev/null grep -F './list-linuxcnc-bridge-smoke-project-sources.sh > "$project_source_list"' test-linuxcnc-bridge-native.sh >/dev/null grep -F 'LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-support-objects.sh > "$support_object_list"' test-linuxcnc-source-link.sh >/dev/null grep -F 'trap '\''rm -f "$source_list" "$missing_source_manifest" "$missing_source_log"'\'' EXIT' test-linuxcnc-source-syntax.sh >/dev/null @@ -233,6 +238,16 @@ native_manifest_tooldata=$(./list-linuxcnc-source-manifest-sources.sh "$manifest grep -Fx "src/emc/tooldata/tooldata_common.cc" <<<"$native_manifest_tooldata" >/dev/null native_manifest_header=$(./list-linuxcnc-source-manifest-sources.sh "$manifest" header) grep -Fx "src/emc/rs274ngc/rs274ngc_interp.hh" <<<"$native_manifest_header" >/dev/null +native_manifest_metadata=$(./list-linuxcnc-source-manifest-sources.sh "$manifest" metadata) +grep -Fx "src/emc/rs274ngc/Submakefile" <<<"$native_manifest_metadata" >/dev/null +grep -Fx "src/emc/rs274ngc/meson.build" <<<"$native_manifest_metadata" >/dev/null +rs274ngc_manifest_entries=$(./list-linuxcnc-source-manifest-sources.sh "$manifest" all | grep '^src/emc/rs274ngc/' | sort) +rs274ngc_source_entries=$(find "$linuxcnc_root/src/emc/rs274ngc" -maxdepth 1 -type f -printf 'src/emc/rs274ngc/%f\n' | sort) +if ! diff -u <(printf '%s\n' "$rs274ngc_source_entries") <(printf '%s\n' "$rs274ngc_manifest_entries") >/dev/null; then + echo "native rs274ngc manifest no longer covers the full LinuxCNC source directory" >&2 + diff -u <(printf '%s\n' "$rs274ngc_source_entries") <(printf '%s\n' "$rs274ngc_manifest_entries") >&2 || true + exit 1 +fi if grep -E '\.(hh|h)$' <<<"$native_manifest_core" >/dev/null; then echo "native manifest core compile group includes headers" >&2 exit 1 @@ -258,9 +273,43 @@ fi native_manifest_core_full=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full) grep -Fx "$linuxcnc_root/src/emc/rs274ngc/interp_arc.cc" <<<"$native_manifest_core_full" >/dev/null +kinematics_manifest_config=$(./list-linuxcnc-kinematics-manifest-sources.sh linuxcnc-kinematics-source-files.txt config) +grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini" <<<"$kinematics_manifest_config" >/dev/null +grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini" <<<"$kinematics_manifest_config" >/dev/null +grep -Fx "configs/sim/axis/vismach/puma/puma560_dh.hal" <<<"$kinematics_manifest_config" >/dev/null +grep -Fx "HALCMD = sets :x-offset -20" "$linuxcnc_root/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini" >/dev/null +grep -Fx "HALCMD = sets :z-offset -15" "$linuxcnc_root/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini" >/dev/null +grep -Fx "HALCMD = setp xyzbc-trt-kins.x-rot-point 0" "$linuxcnc_root/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini" >/dev/null +grep -Fx "HALCMD = setp xyzbc-trt-kins.conventional-directions 0" "$linuxcnc_root/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini" >/dev/null +grep -Fx "HALCMD = sets :z-offset 10" "$linuxcnc_root/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini" >/dev/null +grep -Fx "HALCMD = setp xyzac-trt-kins.conventional-directions 0" "$linuxcnc_root/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini" >/dev/null +grep -Fx "setp genserkins.A-2 17" "$linuxcnc_root/configs/sim/axis/vismach/puma/puma560_dh.hal" >/dev/null +grep -Fx "setp genserkins.ALPHA-4 -1.570796326" "$linuxcnc_root/configs/sim/axis/vismach/puma/puma560_dh.hal" >/dev/null +grep -Fx "setp genserkins.D-0 26.45" "$linuxcnc_root/configs/sim/axis/vismach/puma/puma560_dh.hal" >/dev/null + ./list-linuxcnc-rs274-dump-project-sources.sh >/dev/null rs274_dump_source=$(./list-linuxcnc-rs274-dump-project-sources.sh linuxcnc_rs274_dump.cpp) grep -Fx "core/tools/linuxcnc_rs274_dump.cpp" <<<"$rs274_dump_source" >/dev/null +rs274_dump_corexy_source=$(./list-linuxcnc-rs274-dump-project-sources.sh linuxcnc_corexykins_adapter.cpp) +grep -Fx "core/src/linuxcnc_corexykins_adapter.cpp" <<<"$rs274_dump_corexy_source" >/dev/null +rs274_dump_cubic_source=$(./list-linuxcnc-rs274-dump-project-sources.sh linuxcnc_cubic_adapter.cpp) +grep -Fx "core/src/linuxcnc_cubic_adapter.cpp" <<<"$rs274_dump_cubic_source" >/dev/null +rs274_dump_genhex_source=$(./list-linuxcnc-rs274-dump-project-sources.sh linuxcnc_genhexkins_adapter.cpp) +grep -Fx "core/src/linuxcnc_genhexkins_adapter.cpp" <<<"$rs274_dump_genhex_source" >/dev/null +rs274_dump_lineardelta_source=$(./list-linuxcnc-rs274-dump-project-sources.sh linuxcnc_lineardeltakins_adapter.cpp) +grep -Fx "core/src/linuxcnc_lineardeltakins_adapter.cpp" <<<"$rs274_dump_lineardelta_source" >/dev/null +rs274_dump_rotarydelta_source=$(./list-linuxcnc-rs274-dump-project-sources.sh linuxcnc_rotarydeltakins_adapter.cpp) +grep -Fx "core/src/linuxcnc_rotarydeltakins_adapter.cpp" <<<"$rs274_dump_rotarydelta_source" >/dev/null +rs274_dump_maxkins_source=$(./list-linuxcnc-rs274-dump-project-sources.sh linuxcnc_maxkins_adapter.cpp) +grep -Fx "core/src/linuxcnc_maxkins_adapter.cpp" <<<"$rs274_dump_maxkins_source" >/dev/null +rs274_dump_pentakins_source=$(./list-linuxcnc-rs274-dump-project-sources.sh linuxcnc_pentakins_adapter.cpp) +grep -Fx "core/src/linuxcnc_pentakins_adapter.cpp" <<<"$rs274_dump_pentakins_source" >/dev/null +rs274_dump_rosekins_source=$(./list-linuxcnc-rs274-dump-project-sources.sh linuxcnc_rosekins_adapter.cpp) +grep -Fx "core/src/linuxcnc_rosekins_adapter.cpp" <<<"$rs274_dump_rosekins_source" >/dev/null +rs274_dump_scorbot_source=$(./list-linuxcnc-rs274-dump-project-sources.sh linuxcnc_scorbot_kins_adapter.cpp) +grep -Fx "core/src/linuxcnc_scorbot_kins_adapter.cpp" <<<"$rs274_dump_scorbot_source" >/dev/null +rs274_dump_tripodkins_source=$(./list-linuxcnc-rs274-dump-project-sources.sh linuxcnc_tripodkins_adapter.cpp) +grep -Fx "core/src/linuxcnc_tripodkins_adapter.cpp" <<<"$rs274_dump_tripodkins_source" >/dev/null if ./list-linuxcnc-rs274-dump-project-sources.sh does_not_exist.cc >"$missing_dump_source_log" 2>&1; then echo "rs274 dump project source lister accepted missing source filter" >&2 exit 1 @@ -274,6 +323,15 @@ fi ./list-linuxcnc-rs274-api-project-sources.sh >/dev/null rs274_api_source=$(./list-linuxcnc-rs274-api-project-sources.sh linuxcnc_rs274_backend.cpp) grep -Fx "core/src/linuxcnc_rs274_backend.cpp" <<<"$rs274_api_source" >/dev/null +if ./list-linuxcnc-rs274-api-project-sources.sh smoke_gcode_parser.cpp >"$missing_api_source_log" 2>&1; then + echo "rs274 API project source lister included smoke backend source" >&2 + exit 1 +fi +if ! grep -F "missing LinuxCNC rs274 API project source in CMake list: smoke_gcode_parser.cpp" "$missing_api_source_log" >/dev/null; then + echo "rs274 API project source lister did not exclude smoke backend clearly" >&2 + sed -n '1,20p' "$missing_api_source_log" >&2 + exit 1 +fi if ./list-linuxcnc-rs274-api-project-sources.sh does_not_exist.cc >"$missing_api_source_log" 2>&1; then echo "rs274 API project source lister accepted missing source filter" >&2 exit 1 @@ -285,7 +343,16 @@ if ! grep -F "missing LinuxCNC rs274 API project source in CMake list: does_not_ fi ./list-cnc-sim-core-sources.sh >/dev/null -cnc_sim_core_source=$(./list-cnc-sim-core-sources.sh smoke_gcode_parser.cpp) +if ./list-cnc-sim-core-sources.sh smoke_gcode_parser.cpp >"$missing_core_source_log" 2>&1; then + echo "default cnc sim core source lister included smoke backend source" >&2 + exit 1 +fi +if ! grep -F "missing cnc sim core source in CMake list: smoke_gcode_parser.cpp" "$missing_core_source_log" >/dev/null; then + echo "default cnc sim core source lister did not exclude smoke backend clearly" >&2 + sed -n '1,20p' "$missing_core_source_log" >&2 + exit 1 +fi +cnc_sim_core_source=$(./list-cnc-sim-core-sources.sh --with-smoke smoke_gcode_parser.cpp) grep -Fx "core/src/smoke_gcode_parser.cpp" <<<"$cnc_sim_core_source" >/dev/null if ./list-cnc-sim-core-sources.sh does_not_exist.cc >"$missing_core_source_log" 2>&1; then echo "cnc sim core source lister accepted missing source filter" >&2 @@ -300,6 +367,26 @@ fi ./list-linuxcnc-bridge-smoke-project-sources.sh >/dev/null bridge_smoke_source=$(./list-linuxcnc-bridge-smoke-project-sources.sh linuxcnc_canon_bridge.cpp) grep -Fx "core/src/linuxcnc_canon_bridge.cpp" <<<"$bridge_smoke_source" >/dev/null +bridge_smoke_corexy_source=$(./list-linuxcnc-bridge-smoke-project-sources.sh linuxcnc_corexykins_adapter.cpp) +grep -Fx "core/src/linuxcnc_corexykins_adapter.cpp" <<<"$bridge_smoke_corexy_source" >/dev/null +bridge_smoke_cubic_source=$(./list-linuxcnc-bridge-smoke-project-sources.sh linuxcnc_cubic_adapter.cpp) +grep -Fx "core/src/linuxcnc_cubic_adapter.cpp" <<<"$bridge_smoke_cubic_source" >/dev/null +bridge_smoke_genhex_source=$(./list-linuxcnc-bridge-smoke-project-sources.sh linuxcnc_genhexkins_adapter.cpp) +grep -Fx "core/src/linuxcnc_genhexkins_adapter.cpp" <<<"$bridge_smoke_genhex_source" >/dev/null +bridge_smoke_lineardelta_source=$(./list-linuxcnc-bridge-smoke-project-sources.sh linuxcnc_lineardeltakins_adapter.cpp) +grep -Fx "core/src/linuxcnc_lineardeltakins_adapter.cpp" <<<"$bridge_smoke_lineardelta_source" >/dev/null +bridge_smoke_rotarydelta_source=$(./list-linuxcnc-bridge-smoke-project-sources.sh linuxcnc_rotarydeltakins_adapter.cpp) +grep -Fx "core/src/linuxcnc_rotarydeltakins_adapter.cpp" <<<"$bridge_smoke_rotarydelta_source" >/dev/null +bridge_smoke_maxkins_source=$(./list-linuxcnc-bridge-smoke-project-sources.sh linuxcnc_maxkins_adapter.cpp) +grep -Fx "core/src/linuxcnc_maxkins_adapter.cpp" <<<"$bridge_smoke_maxkins_source" >/dev/null +bridge_smoke_pentakins_source=$(./list-linuxcnc-bridge-smoke-project-sources.sh linuxcnc_pentakins_adapter.cpp) +grep -Fx "core/src/linuxcnc_pentakins_adapter.cpp" <<<"$bridge_smoke_pentakins_source" >/dev/null +bridge_smoke_rosekins_source=$(./list-linuxcnc-bridge-smoke-project-sources.sh linuxcnc_rosekins_adapter.cpp) +grep -Fx "core/src/linuxcnc_rosekins_adapter.cpp" <<<"$bridge_smoke_rosekins_source" >/dev/null +bridge_smoke_scorbot_source=$(./list-linuxcnc-bridge-smoke-project-sources.sh linuxcnc_scorbot_kins_adapter.cpp) +grep -Fx "core/src/linuxcnc_scorbot_kins_adapter.cpp" <<<"$bridge_smoke_scorbot_source" >/dev/null +bridge_smoke_tripodkins_source=$(./list-linuxcnc-bridge-smoke-project-sources.sh linuxcnc_tripodkins_adapter.cpp) +grep -Fx "core/src/linuxcnc_tripodkins_adapter.cpp" <<<"$bridge_smoke_tripodkins_source" >/dev/null if ./list-linuxcnc-bridge-smoke-project-sources.sh does_not_exist.cc >"$missing_bridge_source_log" 2>&1; then echo "LinuxCNC bridge smoke project source lister accepted missing source filter" >&2 exit 1 diff --git a/test-linuxcnc-source-link.sh b/test-linuxcnc-source-link.sh index 07db175..f3a7ee2 100755 --- a/test-linuxcnc-source-link.sh +++ b/test-linuxcnc-source-link.sh @@ -10,6 +10,7 @@ manifest=${1:-linuxcnc-rs274-source-files.txt} trap 'rm -rf "$build_dir"' EXIT LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt # The LinuxCNC source-linked dump path is not concurrency-safe under parallel execution. exec 9>"${TMPDIR:-/tmp}/cnc_sim_rs274_source_link.lock" flock 9 @@ -26,6 +27,7 @@ mapfile -t link_flags <<<"$link_flag_output" source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full) mapfile -t linuxcnc_sources <<<"$source_output" +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" --require-rs274-complete objects=() linuxcnc_index=0 diff --git a/test-linuxcnc-source-syntax.sh b/test-linuxcnc-source-syntax.sh index 8db95e0..cb5624a 100755 --- a/test-linuxcnc-source-syntax.sh +++ b/test-linuxcnc-source-syntax.sh @@ -18,7 +18,9 @@ if awk '/^rm -f "\$missing_source_manifest" "\$missing_source_log"$/{ found = 1 echo "linuxcnc source syntax probe still relies on manual cleanup for missing-source temporaries" >&2 exit 1 fi -LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" all full > "$source_list" +LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full > "$source_list" +LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" binding full >> "$source_list" +LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" tooldata full >> "$source_list" printf 'core:src/emc/rs274ngc/does_not_exist.cc:test missing source\n' >"$missing_source_manifest" if ./test-linuxcnc-source-objects.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then diff --git a/test-linuxcnc-wasm-blockers.sh b/test-linuxcnc-wasm-blockers.sh index 9a301ae..2ffc998 100755 --- a/test-linuxcnc-wasm-blockers.sh +++ b/test-linuxcnc-wasm-blockers.sh @@ -93,9 +93,13 @@ fi grep -F "[manifest]" <<<"$output" >/dev/null grep -F "core=25" <<<"$(sed -n '/^\[manifest\]/,/^$/p' <<<"$output")" >/dev/null -grep -F "blocked=1" <<<"$(sed -n '/^\[manifest\]/,/^$/p' <<<"$output")" >/dev/null +grep -F "blocked=9" <<<"$(sed -n '/^\[manifest\]/,/^$/p' <<<"$output")" >/dev/null grep -F "[python]" <<<"$output" >/dev/null -grep -F "(none)" <<<"$(sed -n '/^\[python\]/,/^$/p' <<<"$output")" >/dev/null +grep -F "src/emc/rs274ngc/canonmodule.cc" <<<"$(sed -n '/^\[python\]/,/^$/p' <<<"$output")" >/dev/null +grep -F "src/emc/rs274ngc/gcodemodule.cc" <<<"$(sed -n '/^\[python\]/,/^$/p' <<<"$output")" >/dev/null +grep -F "src/emc/rs274ngc/interpmodule.cc" <<<"$(sed -n '/^\[python\]/,/^$/p' <<<"$output")" >/dev/null +grep -F "src/emc/rs274ngc/pyarrays.cc" <<<"$(sed -n '/^\[python\]/,/^$/p' <<<"$output")" >/dev/null +grep -F "src/emc/rs274ngc/pyparamclass.cc" <<<"$(sed -n '/^\[python\]/,/^$/p' <<<"$output")" >/dev/null grep -F "[dlopen]" <<<"$output" >/dev/null grep -F "(none)" <<<"$(sed -n '/^\[dlopen\]/,/^$/p' <<<"$output")" >/dev/null grep -F "[tooldata]" <<<"$output" >/dev/null @@ -109,6 +113,17 @@ grep -F "src/emc/tooldata/tooldata_mmap.cc" <<<"$(sed -n '/^\[native-fs\]/,/^$/p grep -F "[blocked-replacements]" <<<"$output" >/dev/null grep -F "src/emc/tooldata/tooldata_mmap.cc -> core/wasm_shims/tooldata/tooldata_mmap_backend.cc" \ <<<"$(sed -n '/^\[blocked-replacements\]/,/^$/p' <<<"$output")" >/dev/null +grep -F "src/emc/rs274ngc/canonmodule.cc -> core/wasm_shims/linuxcnc_runtime_shim.cc" \ + <<<"$(sed -n '/^\[blocked-replacements\]/,/^$/p' <<<"$output")" >/dev/null +grep -F "src/emc/rs274ngc/interpmodule.cc -> core/wasm_shims/linuxcnc_runtime_shim.cc" \ + <<<"$(sed -n '/^\[blocked-replacements\]/,/^$/p' <<<"$output")" >/dev/null +grep -F "src/emc/rs274ngc/pyarrays.cc -> core/wasm_shims/python_c_api_shim.cc" \ + <<<"$(sed -n '/^\[blocked-replacements\]/,/^$/p' <<<"$output")" >/dev/null +grep -F "src/emc/rs274ngc/pyparamclass.cc -> core/wasm_shims/python_c_api_shim.cc" \ + <<<"$(sed -n '/^\[blocked-replacements\]/,/^$/p' <<<"$output")" >/dev/null +grep -F 'blocked replacement is not in wasm shim build set' analyze-linuxcnc-wasm-blockers.sh >/dev/null +grep -F 'wasm_safe_shim_in_build "$replacement"' 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 "[core-python-shimmed]" <<<"$output" >/dev/null grep -F "src/emc/rs274ngc/interp_python.cc" <<<"$(sed -n '/^\[core-python-shimmed\]/,/^$/p' <<<"$output")" >/dev/null grep -F "src/emc/rs274ngc/rs274ngc_pre.cc" <<<"$(sed -n '/^\[core-python-shimmed\]/,/^$/p' <<<"$output")" >/dev/null diff --git a/test-linuxcnc-wasm-cmake-safe-probe.sh b/test-linuxcnc-wasm-cmake-safe-probe.sh index 4bd0084..6afbbe6 100755 --- a/test-linuxcnc-wasm-cmake-safe-probe.sh +++ b/test-linuxcnc-wasm-cmake-safe-probe.sh @@ -51,7 +51,9 @@ cleanup_wasm_cmake_probe_temps() { "${duplicate_pre_blocker_manifest:-}" \ "${duplicate_pre_blocker_log:-}" \ "${duplicate_pre_link_manifest:-}" \ - "${duplicate_pre_link_log:-}"; do + "${duplicate_pre_link_log:-}" \ + "${duplicate_runtime_link_manifest:-}" \ + "${duplicate_runtime_link_log:-}"; do if [[ -n "$temp_path" ]]; then rm -rf "$temp_path" fi @@ -62,7 +64,7 @@ trap cleanup_wasm_cmake_probe_temps EXIT LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" rm -f "$missing_manifest" -if ./build-wasm.sh "$missing_manifest" >"$missing_manifest_log" 2>&1; then +if CNC_SIM_BUILD_WASM_SKIP_LOCK=1 ./build-wasm.sh "$missing_manifest" >"$missing_manifest_log" 2>&1; then echo "build-wasm accepted missing manifest: $missing_manifest" >&2 exit 1 fi @@ -72,7 +74,7 @@ if ! grep -F "missing manifest: $missing_manifest" "$missing_manifest_log" >/dev exit 1 fi -if LINUXCNC_ROOT="$missing_root" ./build-wasm.sh >"$missing_root_log" 2>&1; then +if CNC_SIM_BUILD_WASM_SKIP_LOCK=1 LINUXCNC_ROOT="$missing_root" ./build-wasm.sh >"$missing_root_log" 2>&1; then echo "build-wasm accepted missing LinuxCNC root: $missing_root" >&2 exit 1 fi @@ -94,9 +96,11 @@ grep -F -- './test-linuxcnc-wasm-tooldata-runtime-symbols.sh' build-wasm.sh >/de grep -F -- './test-linuxcnc-wasm-interp-base-link.sh' build-wasm.sh >/dev/null grep -F -- './test-linuxcnc-wasm-interp-find-link.sh' build-wasm.sh >/dev/null grep -F -- './test-linuxcnc-wasm-python-plugin-link.sh' build-wasm.sh >/dev/null +grep -F -- './test-linuxcnc-wasm-python-c-api-symbols.sh' build-wasm.sh >/dev/null grep -F -- './test-linuxcnc-wasm-rs274ngc-pre-object.sh' build-wasm.sh >/dev/null grep -F -- './test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh "$manifest"' build-wasm.sh >/dev/null grep -F -- './test-linuxcnc-wasm-rs274ngc-pre-link.sh "$manifest"' build-wasm.sh >/dev/null +grep -F -- './test-linuxcnc-wasm-runtime-link.sh "$manifest"' build-wasm.sh >/dev/null if grep -F '} > "$report_file" || true' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null; then echo "rs274ngc_pre blocker scan still hides report pipeline failures with || true" >&2 exit 1 @@ -124,9 +128,11 @@ fi for build_dir_probe_script in \ test-linuxcnc-wasm-interp-base-link.sh \ test-linuxcnc-wasm-interp-find-link.sh \ + test-linuxcnc-wasm-python-c-api-symbols.sh \ test-linuxcnc-wasm-python-plugin-link.sh \ test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh \ test-linuxcnc-wasm-rs274ngc-pre-link.sh \ + test-linuxcnc-wasm-runtime-link.sh \ test-linuxcnc-wasm-rs274ngc-pre-object.sh \ test-linuxcnc-wasm-source-objects.sh \ test-linuxcnc-wasm-tooldata-common-link.sh \ @@ -182,7 +188,8 @@ for forbidden_manual_cleanup in \ 'rm -f "$duplicate_source_manifest" "$duplicate_source_log"' \ 'rm -f "$duplicate_syntax_manifest" "$duplicate_syntax_log"' \ 'rm -f "$duplicate_pre_blocker_manifest" "$duplicate_pre_blocker_log"' \ - 'rm -f "$duplicate_pre_link_manifest" "$duplicate_pre_link_log"'; do + 'rm -f "$duplicate_pre_link_manifest" "$duplicate_pre_link_log"' \ + 'rm -f "$duplicate_runtime_link_manifest" "$duplicate_runtime_link_log"'; do if grep -Fx "$forbidden_manual_cleanup" test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null; then echo "wasm CMake safe probe still relies on manual cleanup: $forbidden_manual_cleanup" >&2 exit 1 @@ -196,20 +203,74 @@ if grep -RInF "${forbidden_common_flags_process_substitution}LINUXCNC_ROOT=\"\$l exit 1 fi grep -F -- '-DCNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST="$manifest"' build-wasm.sh >/dev/null -grep -F -- 'if ! command -v emcmake >/dev/null 2>&1; then' build-wasm.sh >/dev/null -grep -F "Emscripten is required. Install/activate emsdk so emcmake and emcc are in PATH." build-wasm.sh >/dev/null +grep -F -- 'for required_tool in cmake emcmake emcc node; do' build-wasm.sh >/dev/null +grep -F -- 'missing_tools+=("$required_tool")' build-wasm.sh >/dev/null +grep -F "WASM build tools are required. Missing:" build-wasm.sh >/dev/null +grep -F "Install/activate cmake, emsdk, and Node.js so cmake, emcmake, emcc, and node are in PATH." build-wasm.sh >/dev/null grep -F -- 'emcmake cmake -S core -B build/wasm \' build-wasm.sh >/dev/null +grep -F -- 'cmake --build build/wasm --target cnc_sim_wasm_runtime_probe' build-wasm.sh >/dev/null +grep -F -- 'cmake --build build/wasm --target cnc_sim_wasm' build-wasm.sh >/dev/null +grep -F -- 'mkdir -p web/public' build-wasm.sh >/dev/null +grep -F -- 'cp build/wasm/cnc_sim.js web/public/cnc_sim.js' build-wasm.sh >/dev/null +grep -F -- 'cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm' 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 -- 'node test-web-wasm-node-smoke.cjs' build-wasm.sh >/dev/null +grep -F -- './test-web-wasm-browser-smoke.sh' build-wasm.sh >/dev/null +if [[ "$(grep -Fc 'cp build/wasm/cnc_sim.js' build-wasm.sh)" -ne 1 ]]; then + echo "build-wasm should copy cnc_sim.js exactly once" >&2 + exit 1 +fi +if [[ "$(grep -Fc 'cp build/wasm/cnc_sim.wasm' build-wasm.sh)" -ne 1 ]]; then + echo "build-wasm should copy cnc_sim.wasm exactly once" >&2 + exit 1 +fi +if ! awk ' + index($0, "mkdir -p web/public") { mkdir_line = NR } + index($0, "cp build/wasm/cnc_sim.js web/public/cnc_sim.js") { cp_line = NR } + END { exit !(mkdir_line && cp_line && mkdir_line < cp_line) } +' build-wasm.sh; then + echo "build-wasm must create web/public before copying wasm artifacts" >&2 + exit 1 +fi +if ! awk ' + index($0, "cp build/wasm/cnc_sim.js web/public/cnc_sim.js") { cp_js_line = NR } + index($0, "cp build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm") { cp_wasm_line = NR } + index($0, "cmp -s build/wasm/cnc_sim.js web/public/cnc_sim.js") { cmp_js_line = NR } + index($0, "cmp -s build/wasm/cnc_sim.wasm web/public/cnc_sim.wasm") { cmp_wasm_line = NR } + index($0, "node test-web-wasm-node-smoke.cjs") { node_line = NR } + index($0, "./test-web-wasm-browser-smoke.sh") { browser_line = NR } + END { + exit !(cp_js_line && cp_wasm_line && cmp_js_line && cmp_wasm_line && node_line && browser_line && + cp_js_line < cmp_js_line && cp_wasm_line < cmp_wasm_line && + cmp_js_line < node_line && cmp_wasm_line < node_line && + node_line < browser_line) + } +' build-wasm.sh; then + echo "build-wasm must verify copied wasm artifacts before running the Node and browser smokes" >&2 + exit 1 +fi +if ! awk ' + index($0, "cmake --build build/wasm --target cnc_sim_wasm_runtime_probe") { runtime_probe_line = NR } + index($0, "cmake --build build/wasm --target cnc_sim_wasm") && + !index($0, "cnc_sim_wasm_runtime_probe") { wasm_line = NR } + END { exit !(runtime_probe_line && wasm_line && runtime_probe_line < wasm_line) } +' build-wasm.sh; then + echo "build-wasm does not build the CMake runtime probe before the browser WASM target" >&2 + exit 1 +fi grep -F -- 'default_manifest="$PWD/linuxcnc-rs274-wasm-source-files.txt"' build-wasm.sh >/dev/null grep -F -- 'manifest_core_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core)' build-wasm.sh >/dev/null grep -F -- 'manifest_blocked_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked)' build-wasm.sh >/dev/null grep -F -- 'count_nonempty_lines() {' build-wasm.sh >/dev/null 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 -- 'LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" --require-rs274-complete' build-wasm.sh >/dev/null if grep -F 'grep -c . <<<"$manifest_core_output" || true' build-wasm.sh >/dev/null; then echo "build-wasm still counts manifest output through grep -c with || true" >&2 exit 1 fi -grep -F -- 'if [[ "$manifest" == "$default_manifest" ]] && [[ "$manifest_core_count" -ne 25 || "$manifest_blocked_count" -ne 1 ]]; then' build-wasm.sh >/dev/null +grep -F -- 'if [[ "$manifest" == "$default_manifest" ]] && [[ "$manifest_core_count" -ne 25 || "$manifest_blocked_count" -ne 9 ]]; then' build-wasm.sh >/dev/null grep -Fx 'LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null ./list-linuxcnc-wasm-safe-shims.sh >/dev/null ./list-linuxcnc-wasm-safe-project-sources.sh >/dev/null @@ -222,6 +283,16 @@ if grep -E '\.(hh|h)$' <<<"$wasm_manifest_core" >/dev/null; then fi wasm_manifest_header=$(./list-linuxcnc-wasm-manifest-sources.sh "$manifest" header) grep -Fx "src/emc/rs274ngc/rs274ngc_interp.hh" <<<"$wasm_manifest_header" >/dev/null +wasm_manifest_metadata=$(./list-linuxcnc-wasm-manifest-sources.sh "$manifest" metadata) +grep -Fx "src/emc/rs274ngc/Submakefile" <<<"$wasm_manifest_metadata" >/dev/null +grep -Fx "src/emc/rs274ngc/meson.build" <<<"$wasm_manifest_metadata" >/dev/null +wasm_rs274ngc_manifest_entries=$(./list-linuxcnc-wasm-manifest-sources.sh "$manifest" all | grep '^src/emc/rs274ngc/' | sort) +wasm_rs274ngc_source_entries=$(find "$linuxcnc_root/src/emc/rs274ngc" -maxdepth 1 -type f -printf 'src/emc/rs274ngc/%f\n' | sort) +if ! diff -u <(printf '%s\n' "$wasm_rs274ngc_source_entries") <(printf '%s\n' "$wasm_rs274ngc_manifest_entries") >/dev/null; then + echo "wasm rs274ngc manifest no longer covers the full LinuxCNC source directory" >&2 + diff -u <(printf '%s\n' "$wasm_rs274ngc_source_entries") <(printf '%s\n' "$wasm_rs274ngc_manifest_entries") >&2 || true + exit 1 +fi wasm_manifest_blocked=$(./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked) grep -Fx "src/emc/tooldata/tooldata_mmap.cc" <<<"$wasm_manifest_blocked" >/dev/null wasm_manifest_blocked_header=$(./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked-header) @@ -252,6 +323,8 @@ dlfcn_shim=$(./list-linuxcnc-wasm-safe-shims.sh dlfcn.cc) grep -Fx "core/wasm_shims/dlfcn.cc" <<<"$dlfcn_shim" >/dev/null python_plugin_shim=$(./list-linuxcnc-wasm-safe-shims.sh python_plugin.cc) grep -Fx "core/wasm_shims/pythonplugin/python_plugin.cc" <<<"$python_plugin_shim" >/dev/null +python_c_api_shim=$(./list-linuxcnc-wasm-safe-shims.sh python_c_api_shim.cc) +grep -Fx "core/wasm_shims/python_c_api_shim.cc" <<<"$python_c_api_shim" >/dev/null rtapi_compat_shim=$(./list-linuxcnc-wasm-safe-shims.sh rtapi_compat.cc) grep -Fx "core/wasm_shims/rtapi_compat.cc" <<<"$rtapi_compat_shim" >/dev/null tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc) @@ -289,7 +362,8 @@ for script in \ test-linuxcnc-wasm-source-syntax.sh \ test-linuxcnc-wasm-source-objects.sh \ test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh \ - test-linuxcnc-wasm-rs274ngc-pre-link.sh; do + test-linuxcnc-wasm-rs274ngc-pre-link.sh \ + test-linuxcnc-wasm-runtime-link.sh; do if "./$script" "$missing_wasm_script_manifest" >"$missing_wasm_script_manifest_log" 2>&1; then echo "$script accepted missing manifest: $missing_wasm_script_manifest" >&2 exit 1 @@ -304,10 +378,12 @@ done missing_root_scripts=( test-linuxcnc-wasm-interp-base-link.sh test-linuxcnc-wasm-interp-find-link.sh + test-linuxcnc-wasm-python-c-api-symbols.sh test-linuxcnc-wasm-python-plugin-link.sh test-linuxcnc-wasm-rs274ngc-pre-object.sh test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh test-linuxcnc-wasm-rs274ngc-pre-link.sh + test-linuxcnc-wasm-runtime-link.sh test-linuxcnc-wasm-source-objects.sh test-linuxcnc-wasm-source-syntax.sh test-linuxcnc-wasm-tooldata-common-link.sh @@ -334,6 +410,7 @@ mkdir -p "$missing_source_root/src" "$missing_source_root/include" fixed_source_scripts=( "test-linuxcnc-wasm-interp-base-link.sh:src/emc/rs274ngc/interp_base.cc" "test-linuxcnc-wasm-interp-find-link.sh:src/emc/tooldata/tooldata_common.cc" + "test-linuxcnc-wasm-python-c-api-symbols.sh:src/emc/rs274ngc/interp_namedparams.cc" "test-linuxcnc-wasm-rs274ngc-pre-object.sh:src/emc/rs274ngc/rs274ngc_pre.cc" "test-linuxcnc-wasm-tooldata-common-link.sh:src/emc/tooldata/tooldata_common.cc" "test-linuxcnc-wasm-tooldata-link.sh:src/emc/tooldata/tooldata_common.cc" @@ -385,7 +462,7 @@ fi unknown_group_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_unknown_wasm_manifest_group.XXXXXX.txt") unknown_group_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_unknown_wasm_manifest_group.XXXXXX.log") printf 'bogus:src/emc/rs274ngc/interp_arc.cc:test unknown group\n' >"$unknown_group_manifest" -if ./build-wasm.sh "$unknown_group_manifest" >"$unknown_group_log" 2>&1; then +if CNC_SIM_BUILD_WASM_SKIP_LOCK=1 ./build-wasm.sh "$unknown_group_manifest" >"$unknown_group_log" 2>&1; then echo "build-wasm accepted unknown manifest group" >&2 exit 1 fi @@ -399,7 +476,8 @@ for script in \ test-linuxcnc-wasm-source-syntax.sh \ test-linuxcnc-wasm-source-objects.sh \ test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh \ - test-linuxcnc-wasm-rs274ngc-pre-link.sh; do + test-linuxcnc-wasm-rs274ngc-pre-link.sh \ + test-linuxcnc-wasm-runtime-link.sh; do if "./$script" "$unknown_group_manifest" >"$unknown_group_log" 2>&1; then echo "$script accepted unknown manifest group" >&2 exit 1 @@ -423,7 +501,7 @@ fi missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_manifest_source.XXXXXX.txt") missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_manifest_source.XXXXXX.log") printf 'core:src/emc/rs274ngc/does_not_exist.cc:test missing source\n' >"$missing_source_manifest" -if ./build-wasm.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then +if CNC_SIM_BUILD_WASM_SKIP_LOCK=1 ./build-wasm.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then echo "build-wasm accepted missing manifest source" >&2 exit 1 fi @@ -468,12 +546,21 @@ if ! grep -F "missing manifest source: src/emc/rs274ngc/does_not_exist.cc" "$mis sed -n '1,20p' "$missing_source_log" >&2 exit 1 fi +if ./test-linuxcnc-wasm-runtime-link.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then + echo "wasm runtime link probe accepted missing manifest source" >&2 + exit 1 +fi +if ! grep -F "missing manifest source: src/emc/rs274ngc/does_not_exist.cc" "$missing_source_log" >/dev/null; then + echo "wasm runtime link probe did not report missing manifest source clearly" >&2 + sed -n '1,20p' "$missing_source_log" >&2 + exit 1 +fi empty_partition_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_empty_wasm_manifest_partition.XXXXXX.log") empty_core_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_empty_wasm_manifest_core.XXXXXX.txt") empty_blocked_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_empty_wasm_manifest_blocked.XXXXXX.txt") printf 'blocked:src/emc/tooldata/tooldata_mmap.cc:test empty core partition\n' >"$empty_core_manifest" -if ./build-wasm.sh "$empty_core_manifest" >"$empty_partition_log" 2>&1; then +if CNC_SIM_BUILD_WASM_SKIP_LOCK=1 ./build-wasm.sh "$empty_core_manifest" >"$empty_partition_log" 2>&1; then echo "build-wasm accepted empty core manifest partition" >&2 exit 1 fi @@ -483,7 +570,7 @@ if ! grep -F "unexpected wasm manifest partition: core=0 blocked=1" "$empty_part exit 1 fi printf 'core:src/emc/rs274ngc/interp_arc.cc:test empty blocked partition\n' >"$empty_blocked_manifest" -if ./build-wasm.sh "$empty_blocked_manifest" >"$empty_partition_log" 2>&1; then +if CNC_SIM_BUILD_WASM_SKIP_LOCK=1 ./build-wasm.sh "$empty_blocked_manifest" >"$empty_partition_log" 2>&1; then echo "build-wasm accepted empty blocked manifest partition" >&2 exit 1 fi @@ -503,7 +590,7 @@ cp list-linuxcnc-manifest-sources.sh "$bad_default_manifest_dir/list-linuxcnc-ma cp list-linuxcnc-wasm-manifest-sources.sh "$bad_default_manifest_dir/list-linuxcnc-wasm-manifest-sources.sh" printf 'core:src/emc/rs274ngc/interp_arc.cc:test bad default core count\nblocked:src/emc/tooldata/tooldata_mmap.cc:test bad default blocked count\n' \ >"$bad_default_manifest_dir/linuxcnc-rs274-wasm-source-files.txt" -if LINUXCNC_ROOT="$linuxcnc_root_abs" "$bad_default_manifest_dir/build-wasm.sh" >"$bad_default_manifest_log" 2>&1; then +if CNC_SIM_BUILD_WASM_SKIP_LOCK=1 LINUXCNC_ROOT="$linuxcnc_root_abs" "$bad_default_manifest_dir/build-wasm.sh" >"$bad_default_manifest_log" 2>&1; then echo "build-wasm accepted bad default wasm manifest partition" >&2 exit 1 fi @@ -520,7 +607,7 @@ core:src/emc/rs274ngc/interp_arc.cc:duplicate core entry core:src/emc/rs274ngc/interp_arc.cc:duplicate core entry blocked:src/emc/tooldata/tooldata_mmap.cc:blocked entry EOF -if ./build-wasm.sh "$duplicate_manifest" >"$duplicate_manifest_log" 2>&1; then +if CNC_SIM_BUILD_WASM_SKIP_LOCK=1 ./build-wasm.sh "$duplicate_manifest" >"$duplicate_manifest_log" 2>&1; then echo "build-wasm accepted duplicate manifest source" >&2 exit 1 fi @@ -538,7 +625,7 @@ cat >"$comment_manifest" <<'EOF' core:src/emc/rs274ngc/interp_arc.cc:commented core entry blocked:src/emc/tooldata/tooldata_mmap.cc:commented blocked entry EOF -if ./build-wasm.sh "$comment_manifest" >"$comment_manifest_log" 2>&1; then +if CNC_SIM_BUILD_WASM_SKIP_LOCK=1 ./build-wasm.sh "$comment_manifest" >"$comment_manifest_log" 2>&1; then : fi if grep -F "duplicate manifest source" "$comment_manifest_log" >/dev/null; then @@ -615,6 +702,23 @@ if ! grep -F "duplicate manifest source: src/emc/rs274ngc/interp_arc.cc" "$dupli exit 1 fi +duplicate_runtime_link_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_runtime_link_manifest.XXXXXX.txt") +duplicate_runtime_link_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_runtime_link_manifest.XXXXXX.log") +cat >"$duplicate_runtime_link_manifest" <<'EOF' +core:src/emc/rs274ngc/interp_arc.cc:duplicate core entry +core:src/emc/rs274ngc/interp_arc.cc:duplicate core entry +blocked:src/emc/tooldata/tooldata_mmap.cc:blocked entry +EOF +if ./test-linuxcnc-wasm-runtime-link.sh "$duplicate_runtime_link_manifest" >"$duplicate_runtime_link_log" 2>&1; then + echo "runtime link probe accepted duplicate manifest source" >&2 + exit 1 +fi +if ! grep -F "duplicate manifest source: src/emc/rs274ngc/interp_arc.cc" "$duplicate_runtime_link_log" >/dev/null; then + echo "runtime link probe did not report duplicate manifest source clearly" >&2 + sed -n '1,20p' "$duplicate_runtime_link_log" >&2 + exit 1 +fi + linuxcnc_root=$(cd "$linuxcnc_root" && pwd) default_manifest="$PWD/linuxcnc-rs274-wasm-source-files.txt" @@ -632,7 +736,7 @@ required_shim_sources="$build_dir/wasm_safe_shim_sources.txt" required_project_sources="$build_dir/wasm_safe_project_sources.txt" ./list-linuxcnc-wasm-safe-project-sources.sh > "$required_project_sources" -if [[ "$manifest" == "$default_manifest" ]] && [[ "$manifest_core_count" -ne 25 || "$manifest_blocked_count" -ne 1 ]]; then +if [[ "$manifest" == "$default_manifest" ]] && [[ "$manifest_core_count" -ne 25 || "$manifest_blocked_count" -ne 9 ]]; then echo "unexpected wasm manifest partition: core=$manifest_core_count blocked=$manifest_blocked_count" >&2 exit 1 fi @@ -661,7 +765,15 @@ done < "$required_project_sources" grep -F "add_executable(linuxcnc_rs274_wasm_safe_probe EXCLUDE_FROM_ALL" core/CMakeLists.txt >/dev/null grep -F "wasm_shims/rs274ngc_pre_probe_main.cc" core/CMakeLists.txt >/dev/null +grep -F "target_compile_definitions(linuxcnc_rs274_wasm_safe_probe" core/CMakeLists.txt >/dev/null +grep -F "wasm_shims/cnc_sim_wasm_runtime_probe_main.cc" core/CMakeLists.txt >/dev/null +grep -F "add_executable(cnc_sim_wasm_runtime_probe EXCLUDE_FROM_ALL" core/CMakeLists.txt >/dev/null grep -F "target_link_libraries(linuxcnc_rs274_wasm_safe_probe PRIVATE fmt)" core/CMakeLists.txt >/dev/null +grep -F "target_link_libraries(cnc_sim_wasm_runtime_probe PRIVATE fmt)" core/CMakeLists.txt >/dev/null +if grep -F "target_link_libraries(cnc_sim_wasm PRIVATE fmt)" core/CMakeLists.txt >/dev/null; then + echo "wasm target still links host fmt library instead of using header-only fmt" >&2 + exit 1 +fi grep -F "add_custom_target(linuxcnc_rs274_wasm_blocked_sources" core/CMakeLists.txt >/dev/null grep -F "LinuxCNC source root does not exist" core/CMakeLists.txt >/dev/null grep -F 'if(NOT IS_DIRECTORY "${CNC_SIM_LINUXCNC_ROOT_ABS}")' core/CMakeLists.txt >/dev/null @@ -670,6 +782,9 @@ grep -F 'if(NOT EXISTS "${CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST}")' core/CMakeLi grep -F "LinuxCNC wasm source manifest is not a file" core/CMakeLists.txt >/dev/null grep -F 'if(IS_DIRECTORY "${CNC_SIM_LINUXCNC_WASM_SOURCE_MANIFEST}")' core/CMakeLists.txt >/dev/null grep -F "Bad LinuxCNC wasm source manifest line" core/CMakeLists.txt >/dev/null +grep -F 'manifest_group STREQUAL "header"' core/CMakeLists.txt >/dev/null +grep -F 'manifest_group STREQUAL "metadata"' core/CMakeLists.txt >/dev/null +grep -F 'manifest_group STREQUAL "blocked-header"' core/CMakeLists.txt >/dev/null grep -F "LinuxCNC wasm manifest source does not exist" core/CMakeLists.txt >/dev/null grep -F 'if(NOT EXISTS "${manifest_source}")' core/CMakeLists.txt >/dev/null grep -F "LinuxCNC wasm manifest source is not a file" core/CMakeLists.txt >/dev/null @@ -681,6 +796,36 @@ awk ' ' core/CMakeLists.txt grep -F "LinuxCNC wasm source manifest has no core entries" core/CMakeLists.txt >/dev/null grep -F "LinuxCNC wasm source manifest has no blocked entries" core/CMakeLists.txt >/dev/null +grep -F "WASM builds must enable CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE so LinuxCNC rs274 sources are linked" core/CMakeLists.txt >/dev/null +grep -F -- '-DCNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE=ON' build-wasm.sh >/dev/null +grep -F 'if(CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE)' core/CMakeLists.txt >/dev/null +grep -F 'list(APPEND cnc_sim_core_sources ${cnc_sim_linuxcnc_rs274_backend_sources})' core/CMakeLists.txt >/dev/null +grep -F 'set(cnc_sim_linuxcnc_kinematics_c_sources' core/CMakeLists.txt >/dev/null +grep -F 'project(cnc_sim_wasm_core LANGUAGES C CXX)' core/CMakeLists.txt >/dev/null +grep -F 'set(cnc_sim_wasm_safe_preinclude_options "SHELL:-include wctype.h")' core/CMakeLists.txt >/dev/null +grep -F 'if(EMSCRIPTEN)' core/CMakeLists.txt >/dev/null +grep -F 'list(APPEND cnc_sim_wasm_safe_preinclude_options "SHELL:-include libgen.h")' core/CMakeLists.txt >/dev/null +grep -F 'find_path(CNC_SIM_FMT_INCLUDE_DIR fmt/format.h' core/CMakeLists.txt >/dev/null +grep -F 'NO_CMAKE_FIND_ROOT_PATH' core/CMakeLists.txt >/dev/null +grep -F 'list(APPEND cnc_sim_wasm_safe_preinclude_options "SHELL:-idirafter ${CNC_SIM_FMT_INCLUDE_DIR}")' core/CMakeLists.txt >/dev/null +grep -F 'FMT_HEADER_ONLY=1' core/CMakeLists.txt >/dev/null +grep -F '${CNC_SIM_FMT_INCLUDE_DIR}' core/CMakeLists.txt >/dev/null +grep -F 'target_compile_options(linuxcnc_rs274_wasm_safe_probe_objects PRIVATE ${cnc_sim_wasm_safe_preinclude_options})' core/CMakeLists.txt >/dev/null +grep -F 'target_compile_options(linuxcnc_rs274_wasm_safe_probe PRIVATE ${cnc_sim_wasm_safe_preinclude_options})' core/CMakeLists.txt >/dev/null +grep -F 'target_compile_options(cnc_sim_objects PRIVATE ${cnc_sim_wasm_safe_preinclude_options})' core/CMakeLists.txt >/dev/null +grep -F 'target_compile_options(cnc_sim_wasm_runtime_probe PRIVATE ${cnc_sim_wasm_safe_preinclude_options})' core/CMakeLists.txt >/dev/null +test -f core/wasm_shims/boost/noncopyable.hpp +grep -F 'class noncopyable' core/wasm_shims/boost/noncopyable.hpp >/dev/null +grep -F 'noncopyable(const noncopyable &) = delete;' core/wasm_shims/boost/noncopyable.hpp >/dev/null +test -f core/wasm_shims/libgen.h +grep -F 'inline char *basename(const char *path)' core/wasm_shims/libgen.h >/dev/null +grep -F 'return basename(const_cast(path));' core/wasm_shims/libgen.h >/dev/null +grep -F 'extern "C" int wordexp(const char *words, wordexp_t *pwordexp, int)' core/wasm_shims/linuxcnc_runtime_shim.cc >/dev/null +grep -F 'extern "C" void wordfree(wordexp_t *pwordexp)' core/wasm_shims/linuxcnc_runtime_shim.cc >/dev/null +grep -F '$' core/CMakeLists.txt >/dev/null +grep -F '$' core/CMakeLists.txt >/dev/null +grep -F 'CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND' core/CMakeLists.txt >/dev/null +grep -F '#ifndef CNC_SIM_ENABLE_LINUXCNC_WASM_SAFE_PROBE' core/src/linuxcnc_rs274_backend.cpp >/dev/null if ! command -v cmake >/dev/null 2>&1; then echo "cmake not found; skipping linuxcnc wasm-safe CMake probe target" @@ -696,5 +841,7 @@ cmake -S core -B "$build_dir" \ cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe_objects cmake --build "$build_dir" --target linuxcnc_rs274_wasm_safe_probe "$build_dir/linuxcnc_rs274_wasm_safe_probe" +cmake --build "$build_dir" --target cnc_sim_wasm_runtime_probe +"$build_dir/cnc_sim_wasm_runtime_probe" echo "linuxcnc wasm-safe CMake probe target passed" diff --git a/test-linuxcnc-wasm-python-c-api-symbols.sh b/test-linuxcnc-wasm-python-c-api-symbols.sh new file mode 100755 index 0000000..5295d30 --- /dev/null +++ b/test-linuxcnc-wasm-python-c-api-symbols.sh @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +cxx=${CXX:-g++} +build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_python_c_api_symbols.XXXXXX") + +trap 'rm -rf "$build_dir"' EXIT +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only + +python_c_api_shim=$(./list-linuxcnc-wasm-safe-shims.sh python_c_api_shim.cc) +runtime_shim=$(./list-linuxcnc-wasm-safe-shims.sh linuxcnc_runtime_shim.cc) +linuxcnc_source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh \ + src/emc/rs274ngc/interp_namedparams.cc \ + src/emc/rs274ngc/interp_o_word.cc \ + src/emc/rs274ngc/interp_python.cc \ + src/emc/rs274ngc/interp_remap.cc \ + src/emc/rs274ngc/interp_setup.cc \ + src/emc/rs274ngc/rs274ngc_pre.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 \ + src/emc/rs274ngc/canonmodule.cc \ + src/emc/rs274ngc/interpmodule.cc \ + src/emc/rs274ngc/gcodemodule.cc) +mapfile -t linuxcnc_sources <<<"$linuxcnc_source_output" +python_binding_sources=("${linuxcnc_sources[@]:6}") + +common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh) +mapfile -t common_flags <<<"$common_flag_output" + +"$cxx" "${common_flags[@]}" \ + -c "$python_c_api_shim" \ + -o "$build_dir/python_c_api_shim.o" +"$cxx" "${common_flags[@]}" \ + -c "$runtime_shim" \ + -o "$build_dir/runtime_shim.o" + +nm --defined-only "$build_dir/python_c_api_shim.o" \ + | awk '$2 ~ /^[A-Z]$/ {print $3}' \ + | LC_ALL=C sort -u \ + > "$build_dir/shim.exports" + +rg -o 'Py[A-Za-z0-9_]+' "${linuxcnc_sources[@]}" \ + | sed 's/^.*://' \ + | LC_ALL=C sort -u \ + > "$build_dir/linuxcnc.py-tokens" + +awk ' + $0 == "PyObject" { next } + $0 == "PyTypeObject" { next } + $0 == "Py_None" { next } + $0 == "Py_TYPE" { next } + $0 == "Py_XDECREF" { next } + $0 == "Py_DECREF" { next } + $0 == "PyList_SET_ITEM" { next } + $0 == "PyTuple_SET_ITEM" { next } + $0 == "PyObject_HEAD" { next } + $0 == "PyVarObject_HEAD_INIT" { next } + $0 == "PyModuleDef_HEAD_INIT" { next } + $0 == "Py_TPFLAGS_DEFAULT" { next } + $0 == "PyCFunction" { next } + $0 == "PyGetSetDef" { next } + $0 == "PyMemberDef" { next } + $0 == "PyMethodDef" { next } + $0 == "PyModuleDef" { next } + $0 == "PyMODINIT_FUNC" { next } + $0 == "PyObject_New" { next } + $0 == "PyInit_emccanon" { next } + $0 == "PyInit_gcode" { next } + $0 == "PyInit_interpreter" { next } + $0 == "PyErr_Ocurred" { next } + $0 == "PyFloat_CheckAndError" { next } + $0 == "PyLong_CheckAndError" { next } + $0 == "Python" { next } + $0 == "PythonPlugin" { next } + { print } +' "$build_dir/linuxcnc.py-tokens" > "$build_dir/required.exports" + +comm -23 "$build_dir/required.exports" "$build_dir/shim.exports" > "$build_dir/missing.exports" +if [[ -s "$build_dir/missing.exports" ]]; then + echo "missing wasm Python C API shim exports used by LinuxCNC rs274 sources:" >&2 + cat "$build_dir/missing.exports" >&2 + exit 1 +fi + +for macro in Py_None Py_TYPE Py_XDECREF Py_DECREF PyTuple_SET_ITEM PyList_SET_ITEM PyObject_New; do + if ! grep -F "#define $macro" core/wasm_shims/boost/python/object.hpp >/dev/null; then + echo "missing wasm Python C API shim macro used by LinuxCNC rs274 sources: $macro" >&2 + exit 1 + fi +done + +grep -F "Py_DecRef" core/wasm_shims/boost/python/object.hpp >/dev/null +grep -F "_Py_NoneStructPtr" core/wasm_shims/boost/python/object.hpp >/dev/null +grep -F 'extern "C" PyObject *PyInit_gcode(void)' core/wasm_shims/linuxcnc_runtime_shim.cc >/dev/null + +for source in "${python_binding_sources[@]}"; do + object_name=$(basename "$source" .cc) + "$cxx" "${common_flags[@]}" \ + -c "$source" \ + -o "$build_dir/$object_name.o" +done + +nm -u "$build_dir"/*.o \ + | awk '{print $2}' \ + | sed '/^$/d' \ + | LC_ALL=C sort -u \ + > "$build_dir/object.undefined" +nm --defined-only "$build_dir"/*.o \ + | awk '{print $3}' \ + | sed '/^$/d' \ + | LC_ALL=C sort -u \ + > "$build_dir/object.defined" +comm -23 "$build_dir/object.undefined" "$build_dir/object.defined" \ + | awk '/^_?Py/ { print }' \ + > "$build_dir/object.missing-python" +if [[ -s "$build_dir/object.missing-python" ]]; then + echo "missing wasm Python C API object definitions used by LinuxCNC rs274 bindings:" >&2 + cat "$build_dir/object.missing-python" >&2 + exit 1 +fi + +echo "linuxcnc wasm Python C API symbol probe passed" diff --git a/test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh b/test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh index 8dd0534..8305582 100755 --- a/test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh +++ b/test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh @@ -63,7 +63,13 @@ done project_index=0 for source in "${project_sources[@]}"; do obj="$build_dir/project_${project_index}.o" - "$cxx" "${common_flags[@]}" -c "$source" -o "$obj" + 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 + "$cxx" "${common_flags[@]}" "${source_flags[@]}" -c "$source" -o "$obj" project_index=$((project_index + 1)) done @@ -94,7 +100,7 @@ nm --defined-only "$build_dir"/*.o \ | awk ' $0 ~ /^(_GLOBAL_OFFSET_TABLE_|_Unwind_Resume)$/ { next } $0 ~ /^(__assert_fail|__cxa_|__divdc3|__dso_handle|__errno_location|__gxx_personality_v0|__isoc23_|__muldc3|__stack_chk_fail)/ { next } - $0 ~ /^(abort|access|acos|asin|atan2|atof|atoi|basename|cabs|calloc|ceil|close|closedir|copysign|cos|cosl|exit|exp|fclose|fdatasync|fdopen|feof|fflush|fgetc|fgets|fileno|floor|fmod|fopen|fprintf|fputs|free|freelocale|fseek|fstat|ftell|fwrite|getcwd|getenv|getpid|gettimeofday|hypot|islower|isprint|isspace|isupper|isxdigit|link|localtime|log|lstat|malloc|memchr|memcmp|memcpy|memmove|memset|mkstemp|nearbyint|newlocale|open|opendir|perror|pow|printf|pthread_mutex_lock|pthread_mutex_unlock|putc|puts|read|readdir|realpath|realloc|remove|rename|setlocale|sin|sinl|snprintf|sprintf|sqrt|sscanf|stat|stderr|stdout|strcasecmp|strcat|strchr|strcmp|strcpy|strdup|strerror|strlen|strncasecmp|strncat|strncmp|strncpy|strrchr|strspn|strstr|strtod|strtok_r|strtol|strtoul|tan|tanl|tolower|toupper|towlower|unlink|uselocale|vfprintf|vsnprintf|wordexp|wordfree)$/ { next } + $0 ~ /^(abort|access|acos|asin|atan2|atof|atoi|basename|cabs|calloc|ceil|close|closedir|copysign|cos|cosl|exit|exp|fclose|fdatasync|fdopen|feof|fflush|fgetc|fgets|fileno|floor|fmax|fmod|fopen|fprintf|fputs|free|freelocale|fseek|fstat|ftell|fwrite|getcwd|getenv|getpid|gettimeofday|hypot|islower|isprint|isspace|isupper|isxdigit|link|localtime|log|lstat|malloc|memchr|memcmp|memcpy|memmove|memset|mkstemp|nearbyint|newlocale|open|opendir|perror|pow|printf|pthread_mutex_lock|pthread_mutex_unlock|putc|puts|read|readdir|realpath|realloc|remove|rename|setlocale|sin|sinl|snprintf|sprintf|sqrt|sscanf|stat|stderr|stdout|strcasecmp|strcat|strchr|strcmp|strcpy|strdup|strerror|strlen|strncasecmp|strncat|strncmp|strncpy|strrchr|strspn|strstr|strtod|strtok_r|strtol|strtoul|tan|tanl|tolower|toupper|towlower|unlink|uselocale|vfprintf|vsnprintf|wordexp|wordfree)$/ { next } $0 ~ /^(_ZN3fmt|_ZSt|_ZNSt|_ZNKSt|_ZNKRSt|_ZNS|_ZTI|_ZTS|_ZTV|_Zdl|_Znwm|_Znam|_Zda|_ZdaPv|_ZdaPvm|_ZdlPvm|_ZTv|_ZTh)/ { next } { print } ' diff --git a/test-linuxcnc-wasm-rs274ngc-pre-link.sh b/test-linuxcnc-wasm-rs274ngc-pre-link.sh index 30390fe..5f0fc3f 100755 --- a/test-linuxcnc-wasm-rs274ngc-pre-link.sh +++ b/test-linuxcnc-wasm-rs274ngc-pre-link.sh @@ -32,7 +32,13 @@ objects=() object_index=0 for source in "${sources[@]}"; do obj="$build_dir/source_${object_index}.o" - "$cxx" "${common_flags[@]}" -c "$source" -o "$obj" + source_flags=() + case "$source" in + core/src/linuxcnc_genserfuncs_adapter.c|core/src/linuxcnc_xyzab_tdr_kins_adapter.c) + source_flags+=(-fpermissive) + ;; + esac + "$cxx" "${common_flags[@]}" "${source_flags[@]}" -c "$source" -o "$obj" objects+=("$obj") object_index=$((object_index + 1)) done diff --git a/test-linuxcnc-wasm-runtime-link.sh b/test-linuxcnc-wasm-runtime-link.sh new file mode 100755 index 0000000..97cf1a3 --- /dev/null +++ b/test-linuxcnc-wasm-runtime-link.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc} +cxx=${CXX:-g++} +build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_runtime_link.XXXXXX") +manifest=${1:-linuxcnc-rs274-wasm-source-files.txt} + +trap 'rm -rf "$build_dir"' EXIT +LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest" + +common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20-sections) +mapfile -t common_flags <<<"$common_flag_output" +common_flags+=(-DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND) + +shim_output=$(./list-linuxcnc-wasm-safe-shims.sh) +mapfile -t sources <<<"$shim_output" + +core_source_output=$(./list-cmake-set-sources.sh cnc_sim_core_sources core/) +mapfile -t core_sources <<<"$core_source_output" +sources+=("${core_sources[@]}") + +backend_source_output=$(./list-cmake-set-sources.sh cnc_sim_linuxcnc_rs274_backend_sources core/) +mapfile -t backend_sources <<<"$backend_source_output" +sources+=("${backend_sources[@]}") + +source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full) +mapfile -t linuxcnc_sources <<<"$source_output" +sources+=("${linuxcnc_sources[@]}") +sources+=(core/wasm_shims/cnc_sim_wasm_runtime_probe_main.cc) + +objects=() +object_index=0 +for source in "${sources[@]}"; do + obj="$build_dir/source_${object_index}.o" + source_flags=() + case "$source" in + core/src/linuxcnc_genserfuncs_adapter.c|core/src/linuxcnc_xyzab_tdr_kins_adapter.c) + source_flags+=(-fpermissive) + ;; + esac + "$cxx" "${common_flags[@]}" "${source_flags[@]}" -c "$source" -o "$obj" + objects+=("$obj") + object_index=$((object_index + 1)) +done + +"$cxx" "${objects[@]}" \ + -Wl,--gc-sections \ + -lfmt \ + -o "$build_dir/cnc_sim_wasm_runtime_probe" + +nm -C "$build_dir/cnc_sim_wasm_runtime_probe" > "$build_dir/runtime.symbols" +grep -F "cnc_sim_parse_program" "$build_dir/runtime.symbols" >/dev/null +grep -F "parse_linuxcnc_rs274_backend" "$build_dir/runtime.symbols" >/dev/null +grep -F "parse_gcode_with_backend" "$build_dir/runtime.symbols" >/dev/null +grep -F "Interp::read(char const*)" "$build_dir/runtime.symbols" >/dev/null +grep -F "Interp::init()" "$build_dir/runtime.symbols" >/dev/null +grep -E '(^| )wordexp($| )' "$build_dir/runtime.symbols" >/dev/null +grep -E '(^| )wordfree($| )' "$build_dir/runtime.symbols" >/dev/null + +"$build_dir/cnc_sim_wasm_runtime_probe" + +echo "linuxcnc wasm runtime link probe passed" diff --git a/test-linuxcnc-wasm-tooldata-link.sh b/test-linuxcnc-wasm-tooldata-link.sh index 66ca632..fc529b5 100755 --- a/test-linuxcnc-wasm-tooldata-link.sh +++ b/test-linuxcnc-wasm-tooldata-link.sh @@ -24,6 +24,8 @@ mapfile -t common_flags <<<"$common_flag_output" core/wasm_shims/tooldata/tooldata_probe_main.cc \ -o "$build_dir/tooldata_probe" -"$build_dir/tooldata_probe" +"$build_dir/tooldata_probe" 2>"$build_dir/tooldata_probe.err" +grep -F "tool_mmap_user(): tool mmap not available" "$build_dir/tooldata_probe.err" >/dev/null +grep -F "tooldata_put() no tool_mmap_base" "$build_dir/tooldata_probe.err" >/dev/null echo "linuxcnc wasm tooldata shim link probe passed" diff --git a/test-native.sh b/test-native.sh index 6f83de7..c1ec568 100755 --- a/test-native.sh +++ b/test-native.sh @@ -6,35 +6,107 @@ cd "$(dirname "$0")" cxx=${CXX:-g++} build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_native.XXXXXX") trap 'rm -rf "$build_dir"' EXIT +./check-linuxcnc-inputs.sh linuxcnc-rs274-source-files.txt --require-rs274-complete +./check-linuxcnc-inputs.sh linuxcnc-rs274-wasm-source-files.txt --require-rs274-complete +./check-linuxcnc-inputs.sh linuxcnc-kinematics-source-files.txt --require-kinematics-complete +./check-linuxcnc-switchkins-remap-table.sh linuxcnc-kinematics-source-files.txt +grep -F '#include "linuxcnc_switchkins_remap_table.inc"' core/src/cnc_sim_api.cpp >/dev/null +grep -F 'switchkins: options.switchkins' web/src/wasm-core.js >/dev/null +grep -F 'kinematics: options.kinematics' web/src/wasm-core.js >/dev/null +grep -F 'pivotLength: options.pivotLength' web/src/wasm-core.js >/dev/null +grep -F 'xyzbcTrt: options.xyzbcTrt' web/src/wasm-core.js >/dev/null +grep -F 'globalThis.createCncSimModule' web/src/wasm-core.js >/dev/null +grep -F 'createModule(moduleOptions)' web/src/wasm-core.js >/dev/null +grep -F 'export type CncParseOptions' web/src/index.ts >/dev/null +grep -F '): CncEvent[];' web/src/index.ts >/dev/null +grep -F 'options?: CncParseOptions' web/src/wasm-core.d.ts >/dev/null +grep -F 'export type WasmModuleOptions' web/src/wasm-core.d.ts >/dev/null +grep -F 'printErr?: (text: string) => void;' web/src/wasm-core.d.ts >/dev/null +grep -F '"moduleResolution": "Bundler"' web/tsconfig.json >/dev/null +grep -F '"include": ["src/index.ts", "src/**/*.d.ts"]' web/tsconfig.json >/dev/null +grep -F 'createWasmSimulator' test-web-wasm-node-smoke.cjs >/dev/null +grep -F 'locateFile: (file) => path.join(wasmDir, file)' test-web-wasm-node-smoke.cjs >/dev/null +grep -F 'printErr: () => {}' test-web-wasm-node-smoke.cjs >/dev/null +grep -F 'expected web wasm-core options to pass LinuxCNC xyzac-trt RTCP config into WASM' test-web-wasm-node-smoke.cjs >/dev/null +grep -F 'node test-web-wasm-node-smoke.cjs' test-web-wasm-node-smoke.sh >/dev/null +grep -F 'createWasmSimulator' web/test-browser-wasm-smoke.html >/dev/null +grep -F 'browser wasm smoke passed' web/test-browser-wasm-smoke.html >/dev/null +grep -F 'chromium chromium-browser google-chrome' test-web-wasm-browser-smoke.sh >/dev/null +grep -F 'browser wasm smoke passed' test-web-wasm-browser-smoke.sh >/dev/null +kinematics_manifest_sources="$build_dir/linuxcnc_kinematics_manifest_sources.txt" +./list-linuxcnc-kinematics-manifest-sources.sh linuxcnc-kinematics-source-files.txt all > "$kinematics_manifest_sources" +grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/5axis/bridgemill/5axis.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/5axis/table-dual-rotary/xyzab-tdr.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzacb-trsrn_twp/xyzacb-trsrn.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/xyzbca-trsrn_twp/xyzbca-trsrn.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/hexapod-sim/hexapod.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/melfa-sim/melfa.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/millturn/millturn.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/puma/puma.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/puma/puma_cube.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/puma/puma560.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/puma/puma560_uvw.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/scara/scara.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/qtaxis/non-trivial/scara/scara.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/qtvcp_screens/non-trivial/scara/scara.ini" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/puma/puma560_dh.hal" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/5axis/bridgemill/remap_subs/428remap.ngc" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/5axis/table-dual-rotary/remap_subs/428remap.ngc" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/430remap.ngc" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/5axis/table-rotary_spindle-rotary-nutating/remap_subs/429remap.ngc" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/hexapod-sim/remap_subs/430remap.ngc" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/melfa-sim/remap_subs/429remap.ngc" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/millturn/remap_subs/429remap.ngc" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/puma/remap_subs/430remap.ngc" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/axis/vismach/scara/remap_subs/428remap.ngc" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/qtaxis/non-trivial/scara/remap_subs/429remap.ngc" "$kinematics_manifest_sources" >/dev/null +grep -Fx "configs/sim/qtvcp_screens/non-trivial/scara/remap_subs/430remap.ngc" "$kinematics_manifest_sources" >/dev/null +if [[ "$(grep -Ec '/remap_subs/(428|429|430)remap\.ngc$' "$kinematics_manifest_sources")" -ne 31 ]]; then + echo "expected kinematics manifest to cover all LinuxCNC M428/M429/M430 remap sources" >&2 + exit 1 +fi +grep -Fx "src/hal/components/xyzab_tdr_kins.comp" "$kinematics_manifest_sources" >/dev/null +grep -Fx "src/objects/hal/components/xyzab_tdr_kins.c" "$kinematics_manifest_sources" >/dev/null +grep -Fx "src/hal/components/xyzacb_trsrn.comp" "$kinematics_manifest_sources" >/dev/null +grep -Fx "src/objects/hal/components/xyzacb_trsrn.c" "$kinematics_manifest_sources" >/dev/null +grep -Fx "src/hal/components/xyzbca_trsrn.comp" "$kinematics_manifest_sources" >/dev/null +grep -Fx "src/objects/hal/components/xyzbca_trsrn.c" "$kinematics_manifest_sources" >/dev/null +grep -Fx "src/hal/components/matrixkins.comp" "$kinematics_manifest_sources" >/dev/null +grep -Fx "src/objects/hal/components/matrixkins.c" "$kinematics_manifest_sources" >/dev/null +grep -Fx "src/hal/components/userkins.comp" "$kinematics_manifest_sources" >/dev/null +grep -Fx "src/objects/hal/components/userkins.c" "$kinematics_manifest_sources" >/dev/null core_source_list="$build_dir/cnc_sim_core_sources.txt" -./list-cnc-sim-core-sources.sh > "$core_source_list" +./list-cnc-sim-core-sources.sh --with-smoke > "$core_source_list" mapfile -t core_sources < "$core_source_list" +smoke_cxxflags=(-std=c++17 -fpermissive -DCNC_SIM_ENABLE_SMOKE_BACKEND -I core/include -I core/src -I ../linuxcnc/include) -"$cxx" -std=c++17 -I core/include -I core/src \ +"$cxx" "${smoke_cxxflags[@]}" \ "${core_sources[@]}" \ core/tests/canon_event_sink_smoke.cpp \ -o "$build_dir/canon_event_sink_smoke" -"$cxx" -std=c++17 -I core/include -I core/src \ +"$cxx" "${smoke_cxxflags[@]}" \ "${core_sources[@]}" \ core/tests/rtcp_kinematics_smoke.cpp \ -o "$build_dir/rtcp_kinematics_smoke" -"$cxx" -std=c++17 -I core/include -I core/src \ +"$cxx" "${smoke_cxxflags[@]}" \ core/tests/linuxcnc_gees_table_smoke.cpp \ -o "$build_dir/linuxcnc_gees_table_smoke" -"$cxx" -std=c++17 -I core/include -I core/src \ +"$cxx" "${smoke_cxxflags[@]}" \ "${core_sources[@]}" \ core/tests/simulator_gcode_controls_smoke.cpp \ -o "$build_dir/simulator_gcode_controls_smoke" -"$cxx" -std=c++17 -I core/include -I core/src \ +"$cxx" "${smoke_cxxflags[@]}" \ "${core_sources[@]}" \ core/tests/cnc_sim_api_smoke.cpp \ -o "$build_dir/cnc_sim_api_smoke" -"$cxx" -std=c++17 -I core/include -I core/src \ +"$cxx" "${smoke_cxxflags[@]}" \ "${core_sources[@]}" \ core/tools/cnc_sim_dump.cpp \ -o "$build_dir/cnc_sim_dump" @@ -44,6 +116,8 @@ mapfile -t core_sources < "$core_source_list" "$build_dir/linuxcnc_gees_table_smoke" "$build_dir/simulator_gcode_controls_smoke" "$build_dir/cnc_sim_api_smoke" +./test-web-wasm-node-smoke.sh +./test-web-wasm-browser-smoke.sh basic_mill_json="$build_dir/cnc_sim_basic_mill.json" incremental_arc_json="$build_dir/cnc_sim_incremental_and_r_arc.json" subprogram_m98_json="$build_dir/cnc_sim_smoke_subprogram_m98.json" diff --git a/test-web-wasm-browser-smoke.sh b/test-web-wasm-browser-smoke.sh new file mode 100755 index 0000000..87b7027 --- /dev/null +++ b/test-web-wasm-browser-smoke.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +chromium_bin=${CHROMIUM:-} +if [[ -z "$chromium_bin" ]]; then + for candidate in chromium chromium-browser google-chrome; do + if command -v "$candidate" >/dev/null 2>&1; then + chromium_bin=$(command -v "$candidate") + break + fi + done +fi + +missing=() +if [[ -z "$chromium_bin" ]]; then + missing+=("chromium") +fi +for required in python3 web/public/cnc_sim.js web/public/cnc_sim.wasm web/test-browser-wasm-smoke.html; do + if [[ "$required" == python3 ]]; then + if ! command -v python3 >/dev/null 2>&1; then + missing+=("python3") + fi + elif [[ ! -f "$required" ]]; then + missing+=("$required") + fi +done + +if ((${#missing[@]} > 0)); then + echo "WASM browser smoke prerequisites are missing: ${missing[*]}." >&2 + echo "Run ./build-wasm.sh after activating emsdk to generate web/public/cnc_sim.js and web/public/cnc_sim.wasm." >&2 + exit 1 +fi + +server_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_browser_smoke_server.XXXXXX.log") +dom_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_browser_smoke_dom.XXXXXX.html") +profile_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_browser_smoke_profile.XXXXXX") +cleanup() { + if [[ -n "${server_pid:-}" ]]; then + kill "$server_pid" >/dev/null 2>&1 || true + wait "$server_pid" >/dev/null 2>&1 || true + fi + rm -f "$server_log" "$dom_log" + rm -rf "$profile_dir" +} +trap cleanup EXIT + +port=$(python3 - <<'PY' +import socket + +with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.bind(("127.0.0.1", 0)) + print(sock.getsockname()[1]) +PY +) + +python3 -m http.server "$port" --bind 127.0.0.1 --directory web >"$server_log" 2>&1 & +server_pid=$! + +for _ in {1..50}; do + if ! kill -0 "$server_pid" >/dev/null 2>&1; then + echo "browser smoke HTTP server exited early" >&2 + sed -n '1,40p' "$server_log" >&2 + exit 1 + fi + if python3 - "$port" <<'PY' +import socket +import sys + +with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.settimeout(0.1) + sys.exit(0 if sock.connect_ex(("127.0.0.1", int(sys.argv[1]))) == 0 else 1) +PY + then + break + fi + sleep 0.1 +done + +if ! python3 - "$port" <<'PY' +import socket +import sys + +with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.settimeout(0.1) + sys.exit(0 if sock.connect_ex(("127.0.0.1", int(sys.argv[1]))) == 0 else 1) +PY +then + echo "browser smoke HTTP server did not start on port $port" >&2 + sed -n '1,40p' "$server_log" >&2 + exit 1 +fi + +"$chromium_bin" \ + --headless=new \ + --disable-gpu \ + --no-sandbox \ + --user-data-dir="$profile_dir" \ + --virtual-time-budget=5000 \ + --dump-dom \ + "http://127.0.0.1:$port/test-browser-wasm-smoke.html" >"$dom_log" 2>&1 + +if ! grep -F "browser wasm smoke passed" "$dom_log" >/dev/null; then + echo "browser WASM smoke did not pass" >&2 + sed -n '1,120p' "$dom_log" >&2 + exit 1 +fi + +echo "browser wasm smoke passed" diff --git a/test-web-wasm-node-smoke.cjs b/test-web-wasm-node-smoke.cjs new file mode 100644 index 0000000..4c4eeb2 --- /dev/null +++ b/test-web-wasm-node-smoke.cjs @@ -0,0 +1,507 @@ +#!/usr/bin/env node +const path = require("path"); +const fs = require("fs"); +const os = require("os"); +const { pathToFileURL } = require("url"); + +const wasmDir = path.join(__dirname, "web", "public"); +const cjsLoader = path.join(os.tmpdir(), `cnc_sim_node_loader_${process.pid}.cjs`); +fs.copyFileSync(path.join(wasmDir, "cnc_sim.js"), cjsLoader); +const createCncSimModule = require(cjsLoader); + +const EVENT_TYPE = { + 2: "comment", + 9: "rapid", + 10: "linear-feed", + 13: "program-end", + 14: "rtcp-pivot", + 15: "kinematics-switch", +}; + +const EVENT_OFFSETS = { + type: 4, + line: 8, + tool: 16, + feed: 24, + dwell: 40, + end: 120, + reserved: 268, +}; + +function readEvent(module, ptr) { + const view = new DataView(module.HEAPU8.buffer, ptr, 272); + return { + type: EVENT_TYPE[view.getInt32(EVENT_OFFSETS.type, true)] || "other", + line: view.getInt32(EVENT_OFFSETS.line, true), + tool: view.getInt32(EVENT_OFFSETS.tool, true), + feed: view.getFloat64(EVENT_OFFSETS.feed, true), + dwellSeconds: view.getFloat64(EVENT_OFFSETS.dwell, true), + endX: view.getFloat64(EVENT_OFFSETS.end + 0, true), + endY: view.getFloat64(EVENT_OFFSETS.end + 8, true), + endZ: view.getFloat64(EVENT_OFFSETS.end + 16, true), + endA: view.getFloat64(EVENT_OFFSETS.end + 24, true), + endB: view.getFloat64(EVENT_OFFSETS.end + 32, true), + endC: view.getFloat64(EVENT_OFFSETS.end + 40, true), + reserved: view.getInt32(EVENT_OFFSETS.reserved, true), + }; +} + +function writeString(module, value) { + const bytes = module.lengthBytesUTF8(value) + 1; + const ptr = module._malloc(bytes); + module.stringToUTF8(value, ptr, bytes); + return { ptr, len: bytes - 1 }; +} + +function expectKinematicsSwitch(events, line, kinstype, label) { + if (!events.some((event) => event.type === "kinematics-switch" && + event.line === line && + event.reserved === kinstype)) { + throw new Error(`expected LinuxCNC WASM ${label} kinematics switch ${kinstype} on line ${line}`); + } +} + +function near(actual, expected) { + return Math.abs(actual - expected) < 1e-6; +} + +(async () => { + const module = await createCncSimModule({ + locateFile: (file) => path.join(wasmDir, file), + print: () => {}, + printErr: () => {}, + }); + + const create = module.cwrap("cnc_sim_create", "number", []); + const destroy = module.cwrap("cnc_sim_destroy", null, ["number"]); + const reset = module.cwrap("cnc_sim_reset", null, ["number"]); + const setDialect = module.cwrap("cnc_sim_set_dialect", "number", ["number", "number"]); + const setCallback = module.cwrap("cnc_sim_set_event_callback", "number", ["number", "number", "number"]); + const loadConfig = module.cwrap("cnc_sim_load_config_json", "number", ["number", "number", "number"]); + const parseProgram = module.cwrap("cnc_sim_parse_program", "number", ["number", "number", "number"]); + const lastError = module.cwrap("cnc_sim_last_error", "number", ["number"]); + + const handle = create(); + if (!handle) { + throw new Error("cnc_sim_create returned null"); + } + + let callbackPtr = 0; + try { + reset(handle); + if (setDialect(handle, 0) !== 0) { + throw new Error(module.UTF8ToString(lastError(handle))); + } + + const config = writeString(module, JSON.stringify({ backend: "linuxcnc-rs274" })); + try { + if (loadConfig(handle, config.ptr, config.len) !== 0) { + throw new Error(module.UTF8ToString(lastError(handle))); + } + } finally { + module._free(config.ptr); + } + + const events = []; + callbackPtr = module.addFunction((eventPtr) => { + events.push(readEvent(module, eventPtr)); + return 0; + }, "iii"); + if (setCallback(handle, callbackPtr, 0) !== 0) { + throw new Error(module.UTF8ToString(lastError(handle))); + } + + const program = writeString(module, "G21 G90\nG0 X0\nG1 X5 F100\nM30\n"); + try { + if (parseProgram(handle, program.ptr, program.len) !== 0) { + throw new Error(module.UTF8ToString(lastError(handle))); + } + } finally { + module._free(program.ptr); + } + + if (!events.some((event) => event.type === "linear-feed" && event.endX === 5)) { + throw new Error("expected LinuxCNC WASM linear-feed event ending at X5"); + } + if (!events.some((event) => event.type === "program-end")) { + throw new Error("expected LinuxCNC WASM program-end event"); + } + + events.length = 0; + const switchProgram = writeString(module, "M428\nM429\nM430\n"); + try { + if (parseProgram(handle, switchProgram.ptr, switchProgram.len) !== 0) { + throw new Error(module.UTF8ToString(lastError(handle))); + } + } finally { + module._free(switchProgram.ptr); + } + + for (const kinstype of [1, 0, 2]) { + if (!events.some((event) => event.type === "kinematics-switch" && event.reserved === kinstype)) { + throw new Error(`expected LinuxCNC WASM kinematics switch ${kinstype}`); + } + if (!events.some((event) => event.type === "comment" && + event.reserved === 68 && + event.tool === 3 && + event.feed === kinstype)) { + throw new Error(`expected LinuxCNC WASM M68 E3 Q${kinstype} remap side effect`); + } + } + if (!events.some((event) => event.type === "comment" && + event.reserved === 66 && + event.tool === 0 && + event.feed === 0)) { + throw new Error("expected LinuxCNC WASM M66 E0 L0 remap side effect"); + } + + const switchkinsCases = [ + { + label: "scara", + config: { backend: "linuxcnc-rs274", switchkins: "scara" }, + program: "M428\nM429\nM430\n", + expected: [ + [1, 0], + [2, 1], + [3, 2], + ], + }, + { + label: "table-dual-rotary", + config: { backend: "linuxcnc-rs274", switchkins: "table-dual-rotary" }, + program: "M428\nM429\n", + expected: [ + [1, 1], + [2, 0], + ], + }, + { + label: "sim-xyzab-tdr-kins", + config: { backend: "linuxcnc-rs274", switchkins: "sim-xyzab-tdr-kins" }, + program: "M428\nM429\n", + expected: [ + [1, 1], + [2, 0], + ], + }, + { + label: "puma", + config: { backend: "linuxcnc-rs274", switchkins: "puma" }, + program: "M428\nM429\nM430\n", + expected: [ + [1, 0], + [2, 1], + [3, 2], + ], + }, + { + label: "melfa-sim", + config: { backend: "linuxcnc-rs274", switchkins: "melfa-sim" }, + program: "M428\nM429\nM430\n", + expected: [ + [1, 0], + [2, 1], + [3, 2], + ], + }, + { + label: "hexapod-sim", + config: { backend: "linuxcnc-rs274", remap: "hexapod-sim" }, + program: "M428\nM429\nM430\n", + expected: [ + [1, 0], + [2, 1], + [3, 2], + ], + }, + { + label: "xyzbca-trsrn", + config: { backend: "linuxcnc-rs274", switchkins: "xyzbca-trsrn" }, + program: "M428\nM429\nM430\n", + expected: [ + [1, 0], + [2, 1], + [3, 2], + ], + }, + ]; + + for (const testCase of switchkinsCases) { + reset(handle); + events.length = 0; + const switchkinsConfig = writeString(module, JSON.stringify(testCase.config)); + try { + if (loadConfig(handle, switchkinsConfig.ptr, switchkinsConfig.len) !== 0) { + throw new Error(module.UTF8ToString(lastError(handle))); + } + } finally { + module._free(switchkinsConfig.ptr); + } + + const switchkinsProgram = writeString(module, testCase.program); + try { + if (parseProgram(handle, switchkinsProgram.ptr, switchkinsProgram.len) !== 0) { + throw new Error(module.UTF8ToString(lastError(handle))); + } + } finally { + module._free(switchkinsProgram.ptr); + } + + for (const [line, kinstype] of testCase.expected) { + expectKinematicsSwitch(events, line, kinstype, testCase.label); + } + } + + reset(handle); + events.length = 0; + const fiveaxisConfig = writeString( + module, + JSON.stringify({ + backend: "linuxcnc-rs274", + rtcp: { enabled: true, toolLength: 250 }, + kinematics: "5axiskins", + pivotLength: 250, + }), + ); + try { + if (loadConfig(handle, fiveaxisConfig.ptr, fiveaxisConfig.len) !== 0) { + throw new Error(module.UTF8ToString(lastError(handle))); + } + } finally { + module._free(fiveaxisConfig.ptr); + } + + const fiveaxisProgram = writeString(module, "M428\nG0 X260 Y20 Z280 B90 C0\n"); + try { + if (parseProgram(handle, fiveaxisProgram.ptr, fiveaxisProgram.len) !== 0) { + throw new Error(module.UTF8ToString(lastError(handle))); + } + } finally { + module._free(fiveaxisProgram.ptr); + } + + expectKinematicsSwitch(events, 1, 0, "5axiskins"); + if (!events.some((event) => event.type === "rtcp-pivot" && + event.line === 2 && + event.reserved === 0 && + near(event.dwellSeconds, 250) && + near(event.endX, 10) && + near(event.endY, 20) && + near(event.endZ, 30) && + near(event.endB, 90) && + near(event.endC, 0))) { + throw new Error("expected LinuxCNC WASM 5axiskins RTCP pivot from LinuxCNC 5axiskins inverse"); + } + + reset(handle); + events.length = 0; + const userkConfig = writeString( + module, + JSON.stringify({ + backend: "linuxcnc-rs274", + rtcp: { enabled: true, toolLength: 250 }, + }), + ); + try { + if (loadConfig(handle, userkConfig.ptr, userkConfig.len) !== 0) { + throw new Error(module.UTF8ToString(lastError(handle))); + } + } finally { + module._free(userkConfig.ptr); + } + + const userkProgram = writeString(module, "M430\nG0 X260 Y20 Z280 B90 C45\n"); + try { + if (parseProgram(handle, userkProgram.ptr, userkProgram.len) !== 0) { + throw new Error(module.UTF8ToString(lastError(handle))); + } + } finally { + module._free(userkProgram.ptr); + } + + expectKinematicsSwitch(events, 1, 2, "userk"); + if (!events.some((event) => event.type === "rtcp-pivot" && + event.line === 2 && + event.reserved === 2 && + near(event.dwellSeconds, 250) && + near(event.endX, 260) && + near(event.endY, 20) && + near(event.endZ, 280) && + near(event.endB, 90) && + near(event.endC, 45))) { + throw new Error("expected LinuxCNC WASM userk RTCP pivot from LinuxCNC userk identity inverse"); + } + + events.length = 0; + const configuredTrtHandle = create(); + if (!configuredTrtHandle) { + throw new Error("cnc_sim_create returned null for configured xyzbc-trt case"); + } + const configuredTrtConfig = writeString( + module, + JSON.stringify({ + backend: "linuxcnc-rs274", + rtcp: { enabled: false, toolLength: 11 }, + xyzbcTrt: { + xRotPoint: 3, + yRotPoint: -4, + zRotPoint: 5, + xOffset: 2, + zOffset: 7, + conventionalDirections: true, + }, + }), + ); + try { + reset(configuredTrtHandle); + if (setDialect(configuredTrtHandle, 0) !== 0) { + throw new Error(module.UTF8ToString(lastError(configuredTrtHandle))); + } + if (setCallback(configuredTrtHandle, callbackPtr, 0) !== 0) { + throw new Error(module.UTF8ToString(lastError(configuredTrtHandle))); + } + if (loadConfig(configuredTrtHandle, configuredTrtConfig.ptr, configuredTrtConfig.len) !== 0) { + throw new Error(module.UTF8ToString(lastError(configuredTrtHandle))); + } + } finally { + module._free(configuredTrtConfig.ptr); + } + + const configuredTrtProgram = writeString(module, "M428\nG43.4\nG0 X30 Y40 Z50 B20 C-30\n"); + try { + if (parseProgram(configuredTrtHandle, configuredTrtProgram.ptr, configuredTrtProgram.len) !== 0) { + throw new Error(module.UTF8ToString(lastError(configuredTrtHandle))); + } + } finally { + module._free(configuredTrtProgram.ptr); + destroy(configuredTrtHandle); + } + + expectKinematicsSwitch(events, 1, 1, "configured xyzbc-trt"); + if (!events.some((event) => event.type === "rtcp-pivot" && + event.line === 3 && + event.reserved === 1 && + near(event.dwellSeconds, 11) && + near(event.endX, -4.814629) && + near(event.endY, 47.605118) && + near(event.endZ, 48.160567) && + near(event.endB, 20) && + near(event.endC, -30))) { + throw new Error("expected LinuxCNC WASM configured xyzbc-trt RTCP pivot from LinuxCNC trtfuncs inverse"); + } + + events.length = 0; + const configuredXyzacHandle = create(); + if (!configuredXyzacHandle) { + throw new Error("cnc_sim_create returned null for configured xyzac-trt case"); + } + const configuredXyzacConfig = writeString( + module, + JSON.stringify({ + backend: "linuxcnc-rs274", + rtcp: { enabled: false, toolLength: 7 }, + switchkins: "xyzac-trt", + xyzbcTrt: { + yOffset: 20, + zOffset: 10, + }, + }), + ); + try { + reset(configuredXyzacHandle); + if (setDialect(configuredXyzacHandle, 0) !== 0) { + throw new Error(module.UTF8ToString(lastError(configuredXyzacHandle))); + } + if (setCallback(configuredXyzacHandle, callbackPtr, 0) !== 0) { + throw new Error(module.UTF8ToString(lastError(configuredXyzacHandle))); + } + if (loadConfig(configuredXyzacHandle, configuredXyzacConfig.ptr, configuredXyzacConfig.len) !== 0) { + throw new Error(module.UTF8ToString(lastError(configuredXyzacHandle))); + } + } finally { + module._free(configuredXyzacConfig.ptr); + } + + const configuredXyzacProgram = writeString(module, "M428\nG43.4\nG0 X12 Y-8 Z42 A35 C-25\n"); + try { + if (parseProgram(configuredXyzacHandle, configuredXyzacProgram.ptr, configuredXyzacProgram.len) !== 0) { + throw new Error(module.UTF8ToString(lastError(configuredXyzacHandle))); + } + } finally { + module._free(configuredXyzacProgram.ptr); + destroy(configuredXyzacHandle); + } + + expectKinematicsSwitch(events, 1, 1, "configured xyzac-trt"); + if (!events.some((event) => event.type === "rtcp-pivot" && + event.line === 3 && + event.reserved === 1 && + near(event.dwellSeconds, 7) && + near(event.endX, 7.494747) && + near(event.endY, -20.815946) && + near(event.endZ, 18.939732) && + near(event.endA, 35) && + near(event.endB, 0) && + near(event.endC, -25))) { + throw new Error("expected LinuxCNC WASM configured xyzac-trt RTCP pivot from LinuxCNC xyzab_tdr inverse"); + } + + globalThis.createCncSimModule = createCncSimModule; + const wasmCoreUrl = pathToFileURL(path.join(__dirname, "web", "src", "wasm-core.js")).href; + const { createWasmSimulator } = await import(wasmCoreUrl); + const webSimulator = await createWasmSimulator({ + locateFile: (file) => path.join(wasmDir, file), + print: () => {}, + printErr: () => {}, + }); + try { + const defaultBackendEvents = webSimulator.parse("G21 G90\nG1 X3 F30\nM30\n", "linuxcnc"); + if (!defaultBackendEvents.some((event) => event.type === "linear-feed" && + event.line === 2 && + near(event.end.x, 3))) { + throw new Error("expected web wasm-core default backend to use LinuxCNC RS274"); + } + + const webEvents = webSimulator.parse( + "M428\nG43.4\nG0 X12 Y-8 Z42 A35 C-25\n", + "linuxcnc", + { + backend: "linuxcnc-rs274", + rtcp: { enabled: false, toolLength: 7 }, + switchkins: "xyzac-trt", + xyzbcTrt: { + yOffset: 20, + zOffset: 10, + }, + }, + ); + if (!webEvents.some((event) => event.type === "rtcp-pivot" && + event.line === 3 && + event.reserved === 1 && + near(event.dwellSeconds, 7) && + near(event.end.x, 7.494747) && + near(event.end.y, -20.815946) && + near(event.end.z, 18.939732) && + near(event.end.a, 35) && + near(event.end.b, 0) && + near(event.end.c, -25))) { + throw new Error("expected web wasm-core options to pass LinuxCNC xyzac-trt RTCP config into WASM"); + } + } finally { + webSimulator.dispose(); + delete globalThis.createCncSimModule; + } + } finally { + if (callbackPtr) { + module.removeFunction(callbackPtr); + } + destroy(handle); + } + + console.log("web wasm node smoke passed"); +})().catch((error) => { + console.error(error && error.stack ? error.stack : error); + process.exit(1); +}).finally(() => { + fs.rmSync(cjsLoader, { force: true }); +}); diff --git a/test-web-wasm-node-smoke.sh b/test-web-wasm-node-smoke.sh new file mode 100755 index 0000000..1125bf6 --- /dev/null +++ b/test-web-wasm-node-smoke.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -euo pipefail + +cd "$(dirname "$0")" + +missing=() +for required in node web/public/cnc_sim.js web/public/cnc_sim.wasm; do + if [[ "$required" == node ]]; then + if ! command -v node >/dev/null 2>&1; then + missing+=("node") + fi + elif [[ ! -f "$required" ]]; then + missing+=("$required") + fi +done + +if ((${#missing[@]} > 0)); then + echo "WASM Node smoke prerequisites are missing: ${missing[*]}." >&2 + echo "Run ./build-wasm.sh after activating emsdk to generate web/public/cnc_sim.js and web/public/cnc_sim.wasm." >&2 + exit 1 +fi + +node test-web-wasm-node-smoke.cjs diff --git a/web/index.html b/web/index.html index f3ecb5b..908e40b 100644 --- a/web/index.html +++ b/web/index.html @@ -64,8 +64,8 @@ 0 LINES diff --git a/web/public/cnc_sim.js b/web/public/cnc_sim.js new file mode 100644 index 0000000..44a8294 --- /dev/null +++ b/web/public/cnc_sim.js @@ -0,0 +1,19 @@ + +var createCncSimModule = (() => { + var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; + if (typeof __filename != 'undefined') _scriptName = _scriptName || __filename; + return ( +function(moduleArg = {}) { + var moduleRtn; + +var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});var ENVIRONMENT_IS_WEB=typeof window=="object";var ENVIRONMENT_IS_WORKER=typeof importScripts=="function";var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string"&&process.type!="renderer";if(ENVIRONMENT_IS_NODE){}var moduleOverrides=Object.assign({},Module);var arguments_=[];var thisProgram="./this.program";var quit_=(status,toThrow)=>{throw toThrow};var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var readAsync,readBinary;if(ENVIRONMENT_IS_NODE){var fs=require("fs");var nodePath=require("path");scriptDirectory=__dirname+"/";readBinary=filename=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);var ret=fs.readFileSync(filename);return ret};readAsync=(filename,binary=true)=>{filename=isFileURI(filename)?new URL(filename):nodePath.normalize(filename);return new Promise((resolve,reject)=>{fs.readFile(filename,binary?undefined:"utf8",(err,data)=>{if(err)reject(err);else resolve(binary?data.buffer:data)})})};if(!Module["thisProgram"]&&process.argv.length>1){thisProgram=process.argv[1].replace(/\\/g,"/")}arguments_=process.argv.slice(2);quit_=(status,toThrow)=>{process.exitCode=status;throw toThrow}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptName){scriptDirectory=_scriptName}if(scriptDirectory.startsWith("blob:")){scriptDirectory=""}else{scriptDirectory=scriptDirectory.substr(0,scriptDirectory.replace(/[?#].*/,"").lastIndexOf("/")+1)}{if(ENVIRONMENT_IS_WORKER){readBinary=url=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=url=>{if(isFileURI(url)){return new Promise((resolve,reject)=>{var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=()=>{if(xhr.status==200||xhr.status==0&&xhr.response){resolve(xhr.response);return}reject(xhr.status)};xhr.onerror=reject;xhr.send(null)})}return fetch(url,{credentials:"same-origin"}).then(response=>{if(response.ok){return response.arrayBuffer()}return Promise.reject(new Error(response.status+" : "+response.url))})}}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.error.bind(console);Object.assign(Module,moduleOverrides);moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];var wasmBinary=Module["wasmBinary"];var wasmMemory;var ABORT=false;var EXITSTATUS;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateMemoryViews(){var b=wasmMemory.buffer;Module["HEAP8"]=HEAP8=new Int8Array(b);Module["HEAP16"]=HEAP16=new Int16Array(b);Module["HEAPU8"]=HEAPU8=new Uint8Array(b);Module["HEAPU16"]=HEAPU16=new Uint16Array(b);Module["HEAP32"]=HEAP32=new Int32Array(b);Module["HEAPU32"]=HEAPU32=new Uint32Array(b);Module["HEAPF32"]=HEAPF32=new Float32Array(b);Module["HEAPF64"]=HEAPF64=new Float64Array(b)}var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){var preRuns=Module["preRun"];if(preRuns){if(typeof preRuns=="function")preRuns=[preRuns];preRuns.forEach(addOnPreRun)}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.initialized)FS.init();FS.ignorePermissions=false;TTY.init();callRuntimeCallbacks(__ATINIT__)}function postRun(){var postRuns=Module["postRun"];if(postRuns){if(typeof postRuns=="function")postRuns=[postRuns];postRuns.forEach(addOnPostRun)}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;Module["monitorRunDependencies"]?.(runDependencies)}function removeRunDependency(id){runDependencies--;Module["monitorRunDependencies"]?.(runDependencies);if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}function abort(what){Module["onAbort"]?.(what);what="Aborted("+what+")";err(what);ABORT=true;what+=". Build with -sASSERTIONS for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";var isDataURI=filename=>filename.startsWith(dataURIPrefix);var isFileURI=filename=>filename.startsWith("file://");function findWasmBinary(){var f="cnc_sim.wasm";if(!isDataURI(f)){return locateFile(f)}return f}var wasmBinaryFile;function getBinarySync(file){if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}throw"both async and sync fetching of the wasm failed"}function getBinaryPromise(binaryFile){if(!wasmBinary){return readAsync(binaryFile).then(response=>new Uint8Array(response),()=>getBinarySync(binaryFile))}return Promise.resolve().then(()=>getBinarySync(binaryFile))}function instantiateArrayBuffer(binaryFile,imports,receiver){return getBinaryPromise(binaryFile).then(binary=>WebAssembly.instantiate(binary,imports)).then(receiver,reason=>{err(`failed to asynchronously prepare wasm: ${reason}`);abort(reason)})}function instantiateAsync(binary,binaryFile,imports,callback){if(!binary&&typeof WebAssembly.instantiateStreaming=="function"&&!isDataURI(binaryFile)&&!isFileURI(binaryFile)&&!ENVIRONMENT_IS_NODE&&typeof fetch=="function"){return fetch(binaryFile,{credentials:"same-origin"}).then(response=>{var result=WebAssembly.instantiateStreaming(response,imports);return result.then(callback,function(reason){err(`wasm streaming compile failed: ${reason}`);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(binaryFile,imports,callback)})})}return instantiateArrayBuffer(binaryFile,imports,callback)}function getWasmImports(){return{a:wasmImports}}function createWasm(){var info=getWasmImports();function receiveInstance(instance,module){wasmExports=instance.exports;wasmMemory=wasmExports["B"];updateMemoryViews();wasmTable=wasmExports["F"];addOnInit(wasmExports["C"]);removeRunDependency("wasm-instantiate");return wasmExports}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}if(Module["instantiateWasm"]){try{return Module["instantiateWasm"](info,receiveInstance)}catch(e){err(`Module.instantiateWasm callback failed with error: ${e}`);readyPromiseReject(e)}}wasmBinaryFile??=findWasmBinary();instantiateAsync(wasmBinary,wasmBinaryFile,info,receiveInstantiationResult).catch(readyPromiseReject);return{}}var tempDouble;var tempI64;function ExitStatus(status){this.name="ExitStatus";this.message=`Program terminated with exit(${status})`;this.status=status}var callRuntimeCallbacks=callbacks=>{callbacks.forEach(f=>f(Module))};var noExitRuntime=Module["noExitRuntime"]||true;var stackRestore=val=>__emscripten_stack_restore(val);var stackSave=()=>_emscripten_stack_get_current();class ExceptionInfo{constructor(excPtr){this.excPtr=excPtr;this.ptr=excPtr-24}set_type(type){HEAPU32[this.ptr+4>>2]=type}get_type(){return HEAPU32[this.ptr+4>>2]}set_destructor(destructor){HEAPU32[this.ptr+8>>2]=destructor}get_destructor(){return HEAPU32[this.ptr+8>>2]}set_caught(caught){caught=caught?1:0;HEAP8[this.ptr+12]=caught}get_caught(){return HEAP8[this.ptr+12]!=0}set_rethrown(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+13]=rethrown}get_rethrown(){return HEAP8[this.ptr+13]!=0}init(type,destructor){this.set_adjusted_ptr(0);this.set_type(type);this.set_destructor(destructor)}set_adjusted_ptr(adjustedPtr){HEAPU32[this.ptr+16>>2]=adjustedPtr}get_adjusted_ptr(){return HEAPU32[this.ptr+16>>2]}}var exceptionLast=0;var uncaughtExceptionCount=0;var ___cxa_throw=(ptr,type,destructor)=>{var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw exceptionLast};var PATH={isAbs:path=>path.charAt(0)==="/",splitPath:filename=>{var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:(parts,allowAboveRoot)=>{var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:path=>{var isAbsolute=PATH.isAbs(path),trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(p=>!!p),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:path=>{var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:path=>{if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},join:(...paths)=>PATH.normalize(paths.join("/")),join2:(l,r)=>PATH.normalize(l+"/"+r)};var initRandomFill=()=>{if(typeof crypto=="object"&&typeof crypto["getRandomValues"]=="function"){return view=>crypto.getRandomValues(view)}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");var randomFillSync=crypto_module["randomFillSync"];if(randomFillSync){return view=>crypto_module["randomFillSync"](view)}var randomBytes=crypto_module["randomBytes"];return view=>(view.set(randomBytes(view.byteLength)),view)}catch(e){}}abort("initRandomDevice")};var randomFill=view=>(randomFill=initRandomFill())(view);var PATH_FS={resolve:(...args)=>{var resolvedPath="",resolvedAbsolute=false;for(var i=args.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?args[i]:FS.cwd();if(typeof path!="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=PATH.isAbs(path)}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(p=>!!p),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:(from,to)=>{from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i{var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heapOrArray[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heapOrArray.buffer&&UTF8Decoder){return UTF8Decoder.decode(heapOrArray.subarray(idx,endPtr))}var str="";while(idx>10,56320|ch&1023)}}return str};var FS_stdin_getChar_buffer=[];var lengthBytesUTF8=str=>{var len=0;for(var i=0;i=55296&&c<=57343){len+=4;++i}else{len+=3}}return len};var stringToUTF8Array=(str,heap,outIdx,maxBytesToWrite)=>{if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx};function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var FS_stdin_getChar=()=>{if(!FS_stdin_getChar_buffer.length){var result=null;if(ENVIRONMENT_IS_NODE){var BUFSIZE=256;var buf=Buffer.alloc(BUFSIZE);var bytesRead=0;var fd=process.stdin.fd;try{bytesRead=fs.readSync(fd,buf,0,BUFSIZE)}catch(e){if(e.toString().includes("EOF"))bytesRead=0;else throw e}if(bytesRead>0){result=buf.slice(0,bytesRead).toString("utf-8")}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else{}if(!result){return null}FS_stdin_getChar_buffer=intArrayFromString(result,true)}return FS_stdin_getChar_buffer.shift()};var TTY={ttys:[],init(){},shutdown(){},register(dev,ops){TTY.ttys[dev]={input:[],output:[],ops};FS.registerDevice(dev,TTY.stream_ops)},stream_ops:{open(stream){var tty=TTY.ttys[stream.node.rdev];if(!tty){throw new FS.ErrnoError(43)}stream.tty=tty;stream.seekable=false},close(stream){stream.tty.ops.fsync(stream.tty)},fsync(stream){stream.tty.ops.fsync(stream.tty)},read(stream,buffer,offset,length,pos){if(!stream.tty||!stream.tty.ops.get_char){throw new FS.ErrnoError(60)}var bytesRead=0;for(var i=0;i0){out(UTF8ArrayToString(tty.output));tty.output=[]}},ioctl_tcgets(tty){return{c_iflag:25856,c_oflag:5,c_cflag:191,c_lflag:35387,c_cc:[3,28,127,21,4,0,1,0,17,19,26,0,18,15,23,22,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}},ioctl_tcsets(tty,optional_actions,data){return 0},ioctl_tiocgwinsz(tty){return[24,80]}},default_tty1_ops:{put_char(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output));tty.output=[]}else{if(val!=0)tty.output.push(val)}},fsync(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output));tty.output=[]}}}};var alignMemory=(size,alignment)=>Math.ceil(size/alignment)*alignment;var mmapAlloc=size=>{abort()};var MEMFS={ops_table:null,mount(mount){return MEMFS.createNode(null,"/",16384|511,0)},createNode(parent,name,mode,dev){if(FS.isBlkdev(mode)||FS.isFIFO(mode)){throw new FS.ErrnoError(63)}MEMFS.ops_table||={dir:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,lookup:MEMFS.node_ops.lookup,mknod:MEMFS.node_ops.mknod,rename:MEMFS.node_ops.rename,unlink:MEMFS.node_ops.unlink,rmdir:MEMFS.node_ops.rmdir,readdir:MEMFS.node_ops.readdir,symlink:MEMFS.node_ops.symlink},stream:{llseek:MEMFS.stream_ops.llseek}},file:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:{llseek:MEMFS.stream_ops.llseek,read:MEMFS.stream_ops.read,write:MEMFS.stream_ops.write,allocate:MEMFS.stream_ops.allocate,mmap:MEMFS.stream_ops.mmap,msync:MEMFS.stream_ops.msync}},link:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr,readlink:MEMFS.node_ops.readlink},stream:{}},chrdev:{node:{getattr:MEMFS.node_ops.getattr,setattr:MEMFS.node_ops.setattr},stream:FS.chrdev_stream_ops}};var node=FS.createNode(parent,name,mode,dev);if(FS.isDir(node.mode)){node.node_ops=MEMFS.ops_table.dir.node;node.stream_ops=MEMFS.ops_table.dir.stream;node.contents={}}else if(FS.isFile(node.mode)){node.node_ops=MEMFS.ops_table.file.node;node.stream_ops=MEMFS.ops_table.file.stream;node.usedBytes=0;node.contents=null}else if(FS.isLink(node.mode)){node.node_ops=MEMFS.ops_table.link.node;node.stream_ops=MEMFS.ops_table.link.stream}else if(FS.isChrdev(node.mode)){node.node_ops=MEMFS.ops_table.chrdev.node;node.stream_ops=MEMFS.ops_table.chrdev.stream}node.timestamp=Date.now();if(parent){parent.contents[name]=node;parent.timestamp=node.timestamp}return node},getFileDataAsTypedArray(node){if(!node.contents)return new Uint8Array(0);if(node.contents.subarray)return node.contents.subarray(0,node.usedBytes);return new Uint8Array(node.contents)},expandFileStorage(node,newCapacity){var prevCapacity=node.contents?node.contents.length:0;if(prevCapacity>=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup(parent,name){throw FS.genericErrors[44]},mknod(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp},unlink(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir(node){var entries=[".",".."];for(var key of Object.keys(node.contents)){entries.push(key)}return entries},symlink(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length{var dep=!noRunDep?getUniqueRunDependency(`al ${url}`):"";readAsync(url).then(arrayBuffer=>{onload(new Uint8Array(arrayBuffer));if(dep)removeRunDependency(dep)},err=>{if(onerror){onerror()}else{throw`Loading data file "${url}" failed.`}});if(dep)addRunDependency(dep)};var FS_createDataFile=(parent,name,fileData,canRead,canWrite,canOwn)=>{FS.createDataFile(parent,name,fileData,canRead,canWrite,canOwn)};var preloadPlugins=Module["preloadPlugins"]||[];var FS_handledByPreloadPlugin=(byteArray,fullname,finish,onerror)=>{if(typeof Browser!="undefined")Browser.init();var handled=false;preloadPlugins.forEach(plugin=>{if(handled)return;if(plugin["canHandle"](fullname)){plugin["handle"](byteArray,fullname,finish,onerror);handled=true}});return handled};var FS_createPreloadedFile=(parent,name,url,canRead,canWrite,onload,onerror,dontCreateFile,canOwn,preFinish)=>{var fullname=name?PATH_FS.resolve(PATH.join2(parent,name)):parent;var dep=getUniqueRunDependency(`cp ${fullname}`);function processData(byteArray){function finish(byteArray){preFinish?.();if(!dontCreateFile){FS_createDataFile(parent,name,byteArray,canRead,canWrite,canOwn)}onload?.();removeRunDependency(dep)}if(FS_handledByPreloadPlugin(byteArray,fullname,finish,()=>{onerror?.();removeRunDependency(dep)})){return}finish(byteArray)}addRunDependency(dep);if(typeof url=="string"){asyncLoad(url,processData,onerror)}else{processData(url)}};var FS_modeStringToFlags=str=>{var flagModes={r:0,"r+":2,w:512|64|1,"w+":512|64|2,a:1024|64|1,"a+":1024|64|2};var flags=flagModes[str];if(typeof flags=="undefined"){throw new Error(`Unknown file open mode: ${str}`)}return flags};var FS_getMode=(canRead,canWrite)=>{var mode=0;if(canRead)mode|=292|73;if(canWrite)mode|=146;return mode};var FS={root:null,mounts:[],devices:{},streams:[],nextInode:1,nameTable:null,currentPath:"/",initialized:false,ignorePermissions:true,ErrnoError:class{constructor(errno){this.name="ErrnoError";this.errno=errno}},genericErrors:{},filesystems:null,syncFSRequests:0,readFiles:{},FSStream:class{constructor(){this.shared={}}get object(){return this.node}set object(val){this.node=val}get isRead(){return(this.flags&2097155)!==1}get isWrite(){return(this.flags&2097155)!==0}get isAppend(){return this.flags&1024}get flags(){return this.shared.flags}set flags(val){this.shared.flags=val}get position(){return this.shared.position}set position(val){this.shared.position=val}},FSNode:class{constructor(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev;this.readMode=292|73;this.writeMode=146}get read(){return(this.mode&this.readMode)===this.readMode}set read(val){val?this.mode|=this.readMode:this.mode&=~this.readMode}get write(){return(this.mode&this.writeMode)===this.writeMode}set write(val){val?this.mode|=this.writeMode:this.mode&=~this.writeMode}get isFolder(){return FS.isDir(this.mode)}get isDevice(){return FS.isChrdev(this.mode)}},lookupPath(path,opts={}){path=PATH_FS.resolve(path);if(!path)return{path:"",node:null};var defaults={follow_mount:true,recurse_count:0};opts=Object.assign(defaults,opts);if(opts.recurse_count>8){throw new FS.ErrnoError(32)}var parts=path.split("/").filter(p=>!!p);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?`${mount}/${path}`:mount+path}path=path?`${node.name}/${path}`:node.name;node=node.parent}},hashName(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode(node){FS.hashRemoveNode(node)},isRoot(node){return node===node.parent},isMountpoint(node){return!!node.mounted},isFile(mode){return(mode&61440)===32768},isDir(mode){return(mode&61440)===16384},isLink(mode){return(mode&61440)===40960},isChrdev(mode){return(mode&61440)===8192},isBlkdev(mode){return(mode&61440)===24576},isFIFO(mode){return(mode&61440)===4096},isSocket(mode){return(mode&49152)===49152},flagsToPermissionString(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup(dir){if(!FS.isDir(dir.mode))return 54;var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd(){for(var fd=0;fd<=FS.MAX_OPEN_FDS;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStreamChecked(fd){var stream=FS.getStream(fd);if(!stream){throw new FS.ErrnoError(8)}return stream},getStream:fd=>FS.streams[fd],createStream(stream,fd=-1){stream=Object.assign(new FS.FSStream,stream);if(fd==-1){fd=FS.nextfd()}stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream(fd){FS.streams[fd]=null},dupStream(origStream,fd=-1){var stream=FS.createStream(origStream,fd);stream.stream_ops?.dup?.(stream);return stream},chrdev_stream_ops:{open(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;stream.stream_ops.open?.(stream)},llseek(){throw new FS.ErrnoError(70)}},major:dev=>dev>>8,minor:dev=>dev&255,makedev:(ma,mi)=>ma<<8|mi,registerDevice(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:dev=>FS.devices[dev],getMounts(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push(...m.mounts)}return mounts},syncfs(populate,callback){if(typeof populate=="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err(`warning: ${FS.syncFSRequests} FS.syncfs operations in flight at once, probably just doing extra work`)}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(mount=>{if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type,opts,mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(hash=>{var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup(parent,name){return parent.node_ops.lookup(parent,name)},mknod(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree(path,mode){var dirs=path.split("/");var d="";for(var i=0;iFS.currentPath,chdir(path){var lookup=FS.lookupPath(path,{follow:true});if(lookup.node===null){throw new FS.ErrnoError(44)}if(!FS.isDir(lookup.node.mode)){throw new FS.ErrnoError(54)}var errCode=FS.nodePermissions(lookup.node,"x");if(errCode){throw new FS.ErrnoError(errCode)}FS.currentPath=lookup.path},createDefaultDirectories(){FS.mkdir("/tmp");FS.mkdir("/home");FS.mkdir("/home/web_user")},createDefaultDevices(){FS.mkdir("/dev");FS.registerDevice(FS.makedev(1,3),{read:()=>0,write:(stream,buffer,offset,length,pos)=>length});FS.mkdev("/dev/null",FS.makedev(1,3));TTY.register(FS.makedev(5,0),TTY.default_tty_ops);TTY.register(FS.makedev(6,0),TTY.default_tty1_ops);FS.mkdev("/dev/tty",FS.makedev(5,0));FS.mkdev("/dev/tty1",FS.makedev(6,0));var randomBuffer=new Uint8Array(1024),randomLeft=0;var randomByte=()=>{if(randomLeft===0){randomLeft=randomFill(randomBuffer).byteLength}return randomBuffer[--randomLeft]};FS.createDevice("/dev","random",randomByte);FS.createDevice("/dev","urandom",randomByte);FS.mkdir("/dev/shm");FS.mkdir("/dev/shm/tmp")},createSpecialDirectories(){FS.mkdir("/proc");var proc_self=FS.mkdir("/proc/self");FS.mkdir("/proc/self/fd");FS.mount({mount(){var node=FS.createNode(proc_self,"fd",16384|511,73);node.node_ops={lookup(parent,name){var fd=+name;var stream=FS.getStreamChecked(fd);var ret={parent:null,mount:{mountpoint:"fake"},node_ops:{readlink:()=>stream.path}};ret.parent=ret;return ret}};return node}},{},"/proc/self/fd")},createStandardStreams(input,output,error){if(input){FS.createDevice("/dev","stdin",input)}else{FS.symlink("/dev/tty","/dev/stdin")}if(output){FS.createDevice("/dev","stdout",null,output)}else{FS.symlink("/dev/tty","/dev/stdout")}if(error){FS.createDevice("/dev","stderr",null,error)}else{FS.symlink("/dev/tty1","/dev/stderr")}var stdin=FS.open("/dev/stdin",0);var stdout=FS.open("/dev/stdout",1);var stderr=FS.open("/dev/stderr",1)},staticInit(){[44].forEach(code=>{FS.genericErrors[code]=new FS.ErrnoError(code);FS.genericErrors[code].stack=""});FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={MEMFS}},init(input,output,error){FS.initialized=true;input??=Module["stdin"];output??=Module["stdout"];error??=Module["stderr"];FS.createStandardStreams(input,output,error)},quit(){FS.initialized=false;for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]}setDataGetter(getter){this.getter=getter}cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=(from,to)=>{if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}return intArrayFromString(xhr.responseText||"",true)};var lazyArray=this;lazyArray.setDataGetter(chunkNum=>{var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]=="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]=="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true}get length(){if(!this.lengthKnown){this.cacheLength()}return this._length}get chunkSize(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}if(typeof XMLHttpRequest!="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(key=>{var fn=node.stream_ops[key];stream_ops[key]=(...args)=>{FS.forceLoadFile(node);return fn(...args)}});function writeChunks(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i{FS.forceLoadFile(node);return writeChunks(stream,buffer,offset,length,position)};stream_ops.mmap=(stream,length,position,prot,flags)=>{FS.forceLoadFile(node);var ptr=mmapAlloc(length);if(!ptr){throw new FS.ErrnoError(48)}writeChunks(stream,HEAP8,ptr,length,position);return{ptr,allocated:true}};node.stream_ops=stream_ops;return node}};var UTF8ToString=(ptr,maxBytesToRead)=>ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):"";var SYSCALLS={DEFAULT_POLLMASK:5,calculateAt(dirfd,path,allowEmpty){if(PATH.isAbs(path)){return path}var dir;if(dirfd===-100){dir=FS.cwd()}else{var dirstream=SYSCALLS.getStreamFromFD(dirfd);dir=dirstream.path}if(path.length==0){if(!allowEmpty){throw new FS.ErrnoError(44)}return dir}return PATH.join2(dir,path)},doStat(func,path,buf){var stat=func(path);HEAP32[buf>>2]=stat.dev;HEAP32[buf+4>>2]=stat.mode;HEAPU32[buf+8>>2]=stat.nlink;HEAP32[buf+12>>2]=stat.uid;HEAP32[buf+16>>2]=stat.gid;HEAP32[buf+20>>2]=stat.rdev;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+24>>2]=tempI64[0],HEAP32[buf+28>>2]=tempI64[1];HEAP32[buf+32>>2]=4096;HEAP32[buf+36>>2]=stat.blocks;var atime=stat.atime.getTime();var mtime=stat.mtime.getTime();var ctime=stat.ctime.getTime();tempI64=[Math.floor(atime/1e3)>>>0,(tempDouble=Math.floor(atime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAPU32[buf+48>>2]=atime%1e3*1e3*1e3;tempI64=[Math.floor(mtime/1e3)>>>0,(tempDouble=Math.floor(mtime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+56>>2]=tempI64[0],HEAP32[buf+60>>2]=tempI64[1];HEAPU32[buf+64>>2]=mtime%1e3*1e3*1e3;tempI64=[Math.floor(ctime/1e3)>>>0,(tempDouble=Math.floor(ctime/1e3),+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+72>>2]=tempI64[0],HEAP32[buf+76>>2]=tempI64[1];HEAPU32[buf+80>>2]=ctime%1e3*1e3*1e3;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+88>>2]=tempI64[0],HEAP32[buf+92>>2]=tempI64[1];return 0},doMsync(addr,stream,len,flags,offset){if(!FS.isFile(stream.node.mode)){throw new FS.ErrnoError(43)}if(flags&2){return 0}var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},getStreamFromFD(fd){var stream=FS.getStreamChecked(fd);return stream},varargs:undefined,getStr(ptr){var ret=UTF8ToString(ptr);return ret}};function ___syscall_faccessat(dirfd,path,amode,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(amode&~7){return-28}var lookup=FS.lookupPath(path,{follow:true});var node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function syscallGetVarargI(){var ret=HEAP32[+SYSCALLS.varargs>>2];SYSCALLS.varargs+=4;return ret}var syscallGetVarargP=syscallGetVarargI;function ___syscall_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=syscallGetVarargI();if(arg<0){return-28}while(FS.streams[arg]){arg++}var newStream;newStream=FS.dupStream(stream,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=syscallGetVarargI();stream.flags|=arg;return 0}case 12:{var arg=syscallGetVarargP();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0}return-28}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fdatasync(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_fstat64(fd,buf){try{var stream=SYSCALLS.getStreamFromFD(fd);return SYSCALLS.doStat(FS.stat,stream.path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var stringToUTF8=(str,outPtr,maxBytesToWrite)=>stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite);function ___syscall_getcwd(buf,size){try{if(size===0)return-28;var cwd=FS.cwd();var cwdLengthInBytes=lengthBytesUTF8(cwd)+1;if(size>>0,(tempDouble=id,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[dirp+pos>>2]=tempI64[0],HEAP32[dirp+pos+4>>2]=tempI64[1];tempI64=[(idx+1)*struct_size>>>0,(tempDouble=(idx+1)*struct_size,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[dirp+pos+8>>2]=tempI64[0],HEAP32[dirp+pos+12>>2]=tempI64[1];HEAP16[dirp+pos+16>>1]=280;HEAP8[dirp+pos+18]=type;stringToUTF8(name,dirp+pos+19,256);pos+=struct_size;idx+=1}FS.llseek(stream,idx*struct_size,0);return pos}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:{if(!stream.tty)return-59;return 0}case 21505:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcgets){var termios=stream.tty.ops.ioctl_tcgets(stream);var argp=syscallGetVarargP();HEAP32[argp>>2]=termios.c_iflag||0;HEAP32[argp+4>>2]=termios.c_oflag||0;HEAP32[argp+8>>2]=termios.c_cflag||0;HEAP32[argp+12>>2]=termios.c_lflag||0;for(var i=0;i<32;i++){HEAP8[argp+i+17]=termios.c_cc[i]||0}return 0}return 0}case 21510:case 21511:case 21512:{if(!stream.tty)return-59;return 0}case 21506:case 21507:case 21508:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tcsets){var argp=syscallGetVarargP();var c_iflag=HEAP32[argp>>2];var c_oflag=HEAP32[argp+4>>2];var c_cflag=HEAP32[argp+8>>2];var c_lflag=HEAP32[argp+12>>2];var c_cc=[];for(var i=0;i<32;i++){c_cc.push(HEAP8[argp+i+17])}return stream.tty.ops.ioctl_tcsets(stream.tty,op,{c_iflag,c_oflag,c_cflag,c_lflag,c_cc})}return 0}case 21519:{if(!stream.tty)return-59;var argp=syscallGetVarargP();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=syscallGetVarargP();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;if(stream.tty.ops.ioctl_tiocgwinsz){var winsize=stream.tty.ops.ioctl_tiocgwinsz(stream.tty);var argp=syscallGetVarargP();HEAP16[argp>>1]=winsize[0];HEAP16[argp+2>>1]=winsize[1]}return 0}case 21524:{if(!stream.tty)return-59;return 0}case 21515:{if(!stream.tty)return-59;return 0}default:return-28}}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_newfstatat(dirfd,path,buf,flags){try{path=SYSCALLS.getStr(path);var nofollow=flags&256;var allowEmpty=flags&4096;flags=flags&~6400;path=SYSCALLS.calculateAt(dirfd,path,allowEmpty);return SYSCALLS.doStat(nofollow?FS.lstat:FS.stat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_openat(dirfd,path,flags,varargs){SYSCALLS.varargs=varargs;try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);var mode=varargs?syscallGetVarargI():0;return FS.open(path,flags,mode).fd}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_readlinkat(dirfd,path,buf,bufsize){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_renameat(olddirfd,oldpath,newdirfd,newpath){try{oldpath=SYSCALLS.getStr(oldpath);newpath=SYSCALLS.getStr(newpath);oldpath=SYSCALLS.calculateAt(olddirfd,oldpath);newpath=SYSCALLS.calculateAt(newdirfd,newpath);FS.rename(oldpath,newpath);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.stat,path,buf)}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}function ___syscall_unlinkat(dirfd,path,flags){try{path=SYSCALLS.getStr(path);path=SYSCALLS.calculateAt(dirfd,path);if(flags===0){FS.unlink(path)}else if(flags===512){FS.rmdir(path)}else{abort("Invalid flags passed to unlinkat")}return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return-e.errno}}var __abort_js=()=>{abort("")};var __emscripten_memcpy_js=(dest,src,num)=>HEAPU8.copyWithin(dest,src,src+num);var isLeapYear=year=>year%4===0&&(year%100!==0||year%400===0);var MONTH_DAYS_LEAP_CUMULATIVE=[0,31,60,91,121,152,182,213,244,274,305,335];var MONTH_DAYS_REGULAR_CUMULATIVE=[0,31,59,90,120,151,181,212,243,273,304,334];var ydayFromDate=date=>{var leap=isLeapYear(date.getFullYear());var monthDaysCumulative=leap?MONTH_DAYS_LEAP_CUMULATIVE:MONTH_DAYS_REGULAR_CUMULATIVE;var yday=monthDaysCumulative[date.getMonth()]+date.getDate()-1;return yday};var convertI32PairToI53Checked=(lo,hi)=>hi+2097152>>>0<4194305-!!lo?(lo>>>0)+hi*4294967296:NaN;function __localtime_js(time_low,time_high,tmPtr){var time=convertI32PairToI53Checked(time_low,time_high);var date=new Date(time*1e3);HEAP32[tmPtr>>2]=date.getSeconds();HEAP32[tmPtr+4>>2]=date.getMinutes();HEAP32[tmPtr+8>>2]=date.getHours();HEAP32[tmPtr+12>>2]=date.getDate();HEAP32[tmPtr+16>>2]=date.getMonth();HEAP32[tmPtr+20>>2]=date.getFullYear()-1900;HEAP32[tmPtr+24>>2]=date.getDay();var yday=ydayFromDate(date)|0;HEAP32[tmPtr+28>>2]=yday;HEAP32[tmPtr+36>>2]=-(date.getTimezoneOffset()*60);var start=new Date(date.getFullYear(),0,1);var summerOffset=new Date(date.getFullYear(),6,1).getTimezoneOffset();var winterOffset=start.getTimezoneOffset();var dst=(summerOffset!=winterOffset&&date.getTimezoneOffset()==Math.min(winterOffset,summerOffset))|0;HEAP32[tmPtr+32>>2]=dst}var __tzset_js=(timezone,daylight,std_name,dst_name)=>{var currentYear=(new Date).getFullYear();var winter=new Date(currentYear,0,1);var summer=new Date(currentYear,6,1);var winterOffset=winter.getTimezoneOffset();var summerOffset=summer.getTimezoneOffset();var stdTimezoneOffset=Math.max(winterOffset,summerOffset);HEAPU32[timezone>>2]=stdTimezoneOffset*60;HEAP32[daylight>>2]=Number(winterOffset!=summerOffset);var extractZone=timezoneOffset=>{var sign=timezoneOffset>=0?"-":"+";var absOffset=Math.abs(timezoneOffset);var hours=String(Math.floor(absOffset/60)).padStart(2,"0");var minutes=String(absOffset%60).padStart(2,"0");return`UTC${sign}${hours}${minutes}`};var winterName=extractZone(winterOffset);var summerName=extractZone(summerOffset);if(summerOffsetDate.now();var getHeapMax=()=>2147483648;var growMemory=size=>{var b=wasmMemory.buffer;var pages=(size-b.byteLength+65535)/65536|0;try{wasmMemory.grow(pages);updateMemoryViews();return 1}catch(e){}};var _emscripten_resize_heap=requestedSize=>{var oldSize=HEAPU8.length;requestedSize>>>=0;var maxHeapSize=getHeapMax();if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignMemory(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=growMemory(newSize);if(replacement){return true}}return false};var ENV={};var getExecutableName=()=>thisProgram||"./this.program";var getEnvStrings=()=>{if(!getEnvStrings.strings){var lang=(typeof navigator=="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:lang,_:getExecutableName()};for(var x in ENV){if(ENV[x]===undefined)delete env[x];else env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(`${x}=${env[x]}`)}getEnvStrings.strings=strings}return getEnvStrings.strings};var stringToAscii=(str,buffer)=>{for(var i=0;i{var bufSize=0;getEnvStrings().forEach((string,i)=>{var ptr=environ_buf+bufSize;HEAPU32[__environ+i*4>>2]=ptr;stringToAscii(string,ptr);bufSize+=string.length+1});return 0};var _environ_sizes_get=(penviron_count,penviron_buf_size)=>{var strings=getEnvStrings();HEAPU32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(string=>bufSize+=string.length+1);HEAPU32[penviron_buf_size>>2]=bufSize;return 0};var runtimeKeepaliveCounter=0;var keepRuntimeAlive=()=>noExitRuntime||runtimeKeepaliveCounter>0;var _proc_exit=code=>{EXITSTATUS=code;if(!keepRuntimeAlive()){Module["onExit"]?.(code);ABORT=true}quit_(code,new ExitStatus(code))};var exitJS=(status,implicit)=>{EXITSTATUS=status;_proc_exit(status)};var _exit=exitJS;function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doReadv=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){var offset=convertI32PairToI53Checked(offset_low,offset_high);try{if(isNaN(offset))return 61;var stream=SYSCALLS.getStreamFromFD(fd);FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?+Math.floor(tempDouble/4294967296)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var doWritev=(stream,iov,iovcnt,offset)=>{var ret=0;for(var i=0;i>2];var len=HEAPU32[iov+4>>2];iov+=8;var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2]=num;return 0}catch(e){if(typeof FS=="undefined"||!(e.name==="ErrnoError"))throw e;return e.errno}}var getCFunc=ident=>{var func=Module["_"+ident];return func};var writeArrayToMemory=(array,buffer)=>{HEAP8.set(array,buffer)};var stackAlloc=sz=>__emscripten_stack_alloc(sz);var stringToUTF8OnStack=str=>{var size=lengthBytesUTF8(str)+1;var ret=stackAlloc(size);stringToUTF8(str,ret,size);return ret};var ccall=(ident,returnType,argTypes,args,opts)=>{var toC={string:str=>{var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=stringToUTF8OnStack(str)}return ret},array:arr=>{var ret=stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}};function convertReturnValue(ret){if(returnType==="string"){return UTF8ToString(ret)}if(returnType==="boolean")return Boolean(ret);return ret}var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i{var numericArgs=!argTypes||argTypes.every(type=>type==="number"||type==="boolean");var numericRet=returnType!=="string";if(numericRet&&numericArgs&&!opts){return getCFunc(ident)}return(...args)=>ccall(ident,returnType,argTypes,args,opts)};var uleb128Encode=(n,target)=>{if(n<128){target.push(n)}else{target.push(n%128|128,n>>7)}};var sigToWasmTypes=sig=>{var typeNames={i:"i32",j:"i64",f:"f32",d:"f64",e:"externref",p:"i32"};var type={parameters:[],results:sig[0]=="v"?[]:[typeNames[sig[0]]]};for(var i=1;i{var sigRet=sig.slice(0,1);var sigParam=sig.slice(1);var typeCodes={i:127,p:127,j:126,f:125,d:124,e:111};target.push(96);uleb128Encode(sigParam.length,target);for(var i=0;i{if(typeof WebAssembly.Function=="function"){return new WebAssembly.Function(sigToWasmTypes(sig),func)}var typeSectionBody=[1];generateFuncType(sig,typeSectionBody);var bytes=[0,97,115,109,1,0,0,0,1];uleb128Encode(typeSectionBody.length,bytes);bytes.push(...typeSectionBody);bytes.push(2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0);var module=new WebAssembly.Module(new Uint8Array(bytes));var instance=new WebAssembly.Instance(module,{e:{f:func}});var wrappedFunc=instance.exports["f"];return wrappedFunc};var wasmTableMirror=[];var wasmTable;var getWasmTableEntry=funcPtr=>{var func=wasmTableMirror[funcPtr];if(!func){if(funcPtr>=wasmTableMirror.length)wasmTableMirror.length=funcPtr+1;wasmTableMirror[funcPtr]=func=wasmTable.get(funcPtr)}return func};var updateTableMap=(offset,count)=>{if(functionsInTableMap){for(var i=offset;i{if(!functionsInTableMap){functionsInTableMap=new WeakMap;updateTableMap(0,wasmTable.length)}return functionsInTableMap.get(func)||0};var freeTableIndexes=[];var getEmptyTableSlot=()=>{if(freeTableIndexes.length){return freeTableIndexes.pop()}try{wasmTable.grow(1)}catch(err){if(!(err instanceof RangeError)){throw err}throw"Unable to grow wasm table. Set ALLOW_TABLE_GROWTH."}return wasmTable.length-1};var setWasmTableEntry=(idx,func)=>{wasmTable.set(idx,func);wasmTableMirror[idx]=wasmTable.get(idx)};var addFunction=(func,sig)=>{var rtn=getFunctionAddress(func);if(rtn){return rtn}var ret=getEmptyTableSlot();try{setWasmTableEntry(ret,func)}catch(err){if(!(err instanceof TypeError)){throw err}var wrapped=convertJsFunctionToWasm(func,sig);setWasmTableEntry(ret,wrapped)}functionsInTableMap.set(func,ret);return ret};var removeFunction=index=>{functionsInTableMap.delete(getWasmTableEntry(index));setWasmTableEntry(index,null);freeTableIndexes.push(index)};FS.createPreloadedFile=FS_createPreloadedFile;FS.staticInit();var wasmImports={a:___cxa_throw,s:___syscall_faccessat,c:___syscall_fcntl64,i:___syscall_fdatasync,A:___syscall_fstat64,x:___syscall_getcwd,t:___syscall_getdents64,h:___syscall_ioctl,y:___syscall_newfstatat,d:___syscall_openat,r:___syscall_readlinkat,q:___syscall_renameat,z:___syscall_stat64,p:___syscall_unlinkat,n:__abort_js,k:__emscripten_memcpy_js,l:__localtime_js,u:__tzset_js,j:_emscripten_date_now,o:_emscripten_resize_heap,v:_environ_get,w:_environ_sizes_get,g:_exit,b:_fd_close,e:_fd_read,m:_fd_seek,f:_fd_write};var wasmExports=createWasm();var ___wasm_call_ctors=()=>(___wasm_call_ctors=wasmExports["C"])();var _malloc=Module["_malloc"]=a0=>(_malloc=Module["_malloc"]=wasmExports["D"])(a0);var _free=Module["_free"]=a0=>(_free=Module["_free"]=wasmExports["E"])(a0);var _cnc_sim_create=Module["_cnc_sim_create"]=()=>(_cnc_sim_create=Module["_cnc_sim_create"]=wasmExports["G"])();var _cnc_sim_destroy=Module["_cnc_sim_destroy"]=a0=>(_cnc_sim_destroy=Module["_cnc_sim_destroy"]=wasmExports["H"])(a0);var _cnc_sim_reset=Module["_cnc_sim_reset"]=a0=>(_cnc_sim_reset=Module["_cnc_sim_reset"]=wasmExports["I"])(a0);var _cnc_sim_set_dialect=Module["_cnc_sim_set_dialect"]=(a0,a1)=>(_cnc_sim_set_dialect=Module["_cnc_sim_set_dialect"]=wasmExports["J"])(a0,a1);var _cnc_sim_set_event_callback=Module["_cnc_sim_set_event_callback"]=(a0,a1,a2)=>(_cnc_sim_set_event_callback=Module["_cnc_sim_set_event_callback"]=wasmExports["K"])(a0,a1,a2);var _cnc_sim_load_config_json=Module["_cnc_sim_load_config_json"]=(a0,a1,a2)=>(_cnc_sim_load_config_json=Module["_cnc_sim_load_config_json"]=wasmExports["L"])(a0,a1,a2);var _cnc_sim_parse_program=Module["_cnc_sim_parse_program"]=(a0,a1,a2)=>(_cnc_sim_parse_program=Module["_cnc_sim_parse_program"]=wasmExports["M"])(a0,a1,a2);var _cnc_sim_last_error=Module["_cnc_sim_last_error"]=a0=>(_cnc_sim_last_error=Module["_cnc_sim_last_error"]=wasmExports["N"])(a0);var __emscripten_stack_restore=a0=>(__emscripten_stack_restore=wasmExports["O"])(a0);var __emscripten_stack_alloc=a0=>(__emscripten_stack_alloc=wasmExports["P"])(a0);var _emscripten_stack_get_current=()=>(_emscripten_stack_get_current=wasmExports["Q"])();Module["ccall"]=ccall;Module["cwrap"]=cwrap;Module["addFunction"]=addFunction;Module["removeFunction"]=removeFunction;Module["UTF8ToString"]=UTF8ToString;Module["stringToUTF8"]=stringToUTF8;Module["lengthBytesUTF8"]=lengthBytesUTF8;var calledRun;var calledPrerun;dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(){if(runDependencies>0){return}if(!calledPrerun){calledPrerun=1;preRun();if(runDependencies>0){return}}function doRun(){if(calledRun)return;calledRun=1;Module["calledRun"]=1;if(ABORT)return;initRuntime();readyPromiseResolve(Module);Module["onRuntimeInitialized"]?.();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(()=>{setTimeout(()=>Module["setStatus"](""),1);doRun()},1)}else{doRun()}}if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();moduleRtn=readyPromise; + + + return moduleRtn; +} +); +})(); +if (typeof exports === 'object' && typeof module === 'object') + module.exports = createCncSimModule; +else if (typeof define === 'function' && define['amd']) + define([], () => createCncSimModule); diff --git a/web/src/index.ts b/web/src/index.ts index bc3db47..e52ad17 100644 --- a/web/src/index.ts +++ b/web/src/index.ts @@ -48,12 +48,55 @@ export type CncEvent = { reserved: number; }; +export type CncRtcpOptions = { + enabled: boolean; + toolLength?: number; + tool_length?: number; + toolLengths?: Record; + tool_lengths?: Record; +}; + +export type CncXyzbcTrtOptions = { + xRotPoint?: number; + yRotPoint?: number; + zRotPoint?: number; + xOffset?: number; + yOffset?: number; + zOffset?: number; + conventionalDirections?: boolean; + x_rot_point?: number; + y_rot_point?: number; + z_rot_point?: number; + x_offset?: number; + y_offset?: number; + z_offset?: number; + conventional_directions?: boolean; +}; + +export type CncParseOptions = { + backend?: string; + rtcp?: CncRtcpOptions; + blockDelete?: boolean; + block_delete?: boolean; + switchkins?: string; + remap?: string; + kinematics?: string; + trt?: "xyzbc" | "xyzac" | string; + pivotLength?: number; + pivot_length?: number; + kinematicsType?: number; + kinematics_type?: number; + fiveaxisKinematicsType?: number; + fiveaxis_kinematics_type?: number; + xyzbcTrt?: CncXyzbcTrtOptions; +}; + export type SimulatorCore = { parse( program: string, dialect: CncDialect, - options?: { backend?: string; rtcp?: { enabled: boolean; toolLength: number } }, - ): Promise; + options?: CncParseOptions, + ): CncEvent[]; }; export async function createSimulatorCore(): Promise { diff --git a/web/src/wasm-core.d.ts b/web/src/wasm-core.d.ts index 0c85d1d..007f69e 100644 --- a/web/src/wasm-core.d.ts +++ b/web/src/wasm-core.d.ts @@ -1,8 +1,14 @@ -import type { CncDialect, CncEvent } from "./index"; +import type { CncDialect, CncEvent, CncParseOptions } from "./index"; export type WasmSimulator = { - parse(program: string, dialect?: CncDialect, options?: { backend?: string }): CncEvent[]; + parse(program: string, dialect?: CncDialect, options?: CncParseOptions): CncEvent[]; dispose(): void; }; -export function createWasmSimulator(): Promise; +export type WasmModuleOptions = { + locateFile?: (file: string) => string; + print?: (text: string) => void; + printErr?: (text: string) => void; +}; + +export function createWasmSimulator(moduleOptions?: WasmModuleOptions): Promise; diff --git a/web/src/wasm-core.js b/web/src/wasm-core.js index 36af81a..92705c1 100644 --- a/web/src/wasm-core.js +++ b/web/src/wasm-core.js @@ -74,17 +74,20 @@ function readEvent(module, ptr) { } async function loadModuleFactory() { + if (globalThis.createCncSimModule) { + return globalThis.createCncSimModule; + } const module = await import("/cnc_sim.js"); return module.default ?? module.createCncSimModule ?? globalThis.createCncSimModule; } -export async function createWasmSimulator() { +export async function createWasmSimulator(moduleOptions = {}) { const createModule = await loadModuleFactory(); if (!createModule) { throw new Error("cnc_sim.js did not export createCncSimModule"); } - const module = await createModule(); + const module = await createModule(moduleOptions); const create = module.cwrap("cnc_sim_create", "number", []); const destroy = module.cwrap("cnc_sim_destroy", null, ["number"]); const reset = module.cwrap("cnc_sim_reset", null, ["number"]); @@ -104,8 +107,25 @@ export async function createWasmSimulator() { setDialect(handle, DIALECT[dialect] ?? DIALECT.linuxcnc); const config = JSON.stringify({ - backend: options.backend ?? "smoke", + backend: options.backend ?? "linuxcnc-rs274", ...(options.rtcp ? { rtcp: options.rtcp } : {}), + ...(options.blockDelete !== undefined ? { blockDelete: options.blockDelete } : {}), + ...(options.block_delete !== undefined ? { block_delete: options.block_delete } : {}), + ...(options.switchkins ? { switchkins: options.switchkins } : {}), + ...(options.remap ? { remap: options.remap } : {}), + ...(options.kinematics ? { kinematics: options.kinematics } : {}), + ...(options.trt ? { trt: options.trt } : {}), + ...(options.pivotLength !== undefined ? { pivotLength: options.pivotLength } : {}), + ...(options.pivot_length !== undefined ? { pivot_length: options.pivot_length } : {}), + ...(options.kinematicsType !== undefined ? { kinematicsType: options.kinematicsType } : {}), + ...(options.kinematics_type !== undefined ? { kinematics_type: options.kinematics_type } : {}), + ...(options.fiveaxisKinematicsType !== undefined + ? { fiveaxisKinematicsType: options.fiveaxisKinematicsType } + : {}), + ...(options.fiveaxis_kinematics_type !== undefined + ? { fiveaxis_kinematics_type: options.fiveaxis_kinematics_type } + : {}), + ...(options.xyzbcTrt ? { xyzbcTrt: options.xyzbcTrt } : {}), }); const configBytes = module.lengthBytesUTF8(config) + 1; const configPtr = module._malloc(configBytes); diff --git a/web/test-browser-wasm-smoke.html b/web/test-browser-wasm-smoke.html new file mode 100644 index 0000000..f873cfb --- /dev/null +++ b/web/test-browser-wasm-smoke.html @@ -0,0 +1,56 @@ + + + + + CNC WASM Browser Smoke + + +
pending
+ + + + diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 0000000..3485d70 --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "noEmit": true, + "allowJs": false, + "checkJs": false, + "lib": ["ES2022", "DOM"], + "skipLibCheck": true + }, + "include": ["src/index.ts", "src/**/*.d.ts"] +}