满庭芳·源流归一

结论:LinuxCNC rs274ngc 源码移植边界继续收拢。已将 rs274ngc 头文件纳入 manifest 跟踪,core 编译组仍只包含可编译源文件;启用 CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND 时 API 默认使用 LinuxCNC rs274 后端;M428/M429/M430 与 5axis/TRT/TDR/RTCP 相关路径继续保持来自 LinuxCNC 源码或 remap 配置的薄桥接。

验证:./test-linuxcnc-source-syntax.sh;./test-linuxcnc-api-native.sh;./test-native.sh;./test-linuxcnc-source-link.sh。
This commit is contained in:
cnc
2026-05-28 18:00:53 +08:00
parent 40a63c76f7
commit b40914747d
65 changed files with 5189 additions and 1117 deletions

View File

@@ -143,11 +143,19 @@ RTCP 默认关闭。开启后,原有 `rapid`、`linear-feed`、`arc-feed` 事
"toolLengths": {
"7": 125
}
},
"xyzbcTrt": {
"xRotPoint": 0,
"yRotPoint": 0,
"zRotPoint": 0,
"xOffset": -20,
"zOffset": -15,
"conventionalDirections": false
}
}
```
当前 RTCP 内核是第一版头头/摆头类几何模型:编程 XYZ 视为刀尖点,刀长沿本地 `-Z`A/B/C 按 `Rz(C) * Ry(B) * Rx(A)` 旋转。后续会继续加入机型拓扑、旋转中心偏置、刀表刀长和不同五轴构型
RTCP pivot 事件只在已知 LinuxCNC switchkins 类型下输出。`M428` 切换到 `XYZBC_TRT` 后,`xyzbcTrt` 对应 LinuxCNC `xyzbc-trt-kins` 的 HAL pin`x-rot-point``y-rot-point``z-rot-point``x-offset``z-offset``conventional-directions`;刀长仍来自 `toolLength` / `toolLengths``M430` 使用 LinuxCNC `userkfuncs.c``kins_util.c` identity 运动学路径。未支持或未知的 switchkins 类型不会回退到自研 RTCP 几何
RTCP G 码控制已开始标准化:
@@ -161,11 +169,11 @@ RTCP G 码控制已开始标准化:
core 已对原型中的运动学切换 M 码做标准化解析:
- `M429`:切换到三轴恒等运动学 `IDENTITY`,关闭 RTCP
- `M428`恢复到原始五轴运动学,开启 RTCP
- `M430`:切换到 `FIVEAXIS_BC`,开启 RTCP
- `M429`:切换到三轴恒等运动学 `IDENTITY`
- `M428`切换到 LinuxCNC `xyzbc-trt-kins` 的 XYZBC TRT 运动学
- `M430`:切换到 LinuxCNC 默认 `userk` 运动学;当前对齐 `userkfuncs.c` 的 identity 正/逆解
解析后会发出 `kinematics-switch` 事件。事件中的 `reserved` 字段表示运动学类型:`0=IDENTITY``1=ORIGINAL``2=FIVEAXIS_BC``feed` 字段临时表示 RTCP 状态:`0=关闭``1=开启`。同一行中的 `M428 M429 M430` 会按出现顺序产生三个切换事件。
解析后会发出 `kinematics-switch` 事件。事件中的 `reserved` 字段表示运动学类型:`0=IDENTITY``1=XYZBC_TRT``2=USERK_IDENTITY`。这些 M 码对齐 LinuxCNC `remap_subs/428remap.ngc``429remap.ngc``430remap.ngc`,只设置 `motion.switchkins-type`,不隐式开启或关闭 RTCPRTCP 状态仍由 `G43.4``G43.5``G49` 控制。同一行中的 `M428 M429 M430` 会按出现顺序产生三个切换事件。
`linuxcnc-rs274` native API backend 会在送入 LinuxCNC 解释器前拦截只包含这些切换 M 码的行,避免 LinuxCNC 去查找外部用户 M-code 文件。

View File

@@ -6,15 +6,7 @@ cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
python_blockers=()
core_python_sources=()
@@ -30,9 +22,6 @@ core_native_fs_sources=()
core_count=0
blocked_count=0
blocked_replacements=()
core_source_list=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_blocker_core_sources.XXXXXX.txt")
blocked_source_list=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_blocker_blocked_sources.XXXXXX.txt")
trap 'rm -f "$core_source_list" "$blocked_source_list"' EXIT
blocked_replacement_for() {
case "$1" in
@@ -45,15 +34,19 @@ blocked_replacement_for() {
esac
}
./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core > "$core_source_list"
./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked > "$blocked_source_list"
core_count=$(wc -l < "$core_source_list")
blocked_count=$(wc -l < "$blocked_source_list")
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)
mapfile -t blocked_sources <<<"$blocked_source_output"
core_count=${#core_sources[@]}
blocked_count=${#blocked_sources[@]}
scan_source() {
local group=$1
local path=$2
full_path="$linuxcnc_root/$path"
full_path=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh \
--missing-message "missing manifest source" \
"$path")
if [[ "$group" == "blocked" ]]; then
if ! replacement=$(blocked_replacement_for "$path"); then
echo "missing blocked replacement mapping: $path" >&2
@@ -111,15 +104,15 @@ scan_source() {
fi
}
while IFS= read -r path; do
for path in "${core_sources[@]}"; do
[[ -z "$path" ]] && continue
scan_source core "$path"
done < "$core_source_list"
done
while IFS= read -r path; do
for path in "${blocked_sources[@]}"; do
[[ -z "$path" ]] && continue
scan_source blocked "$path"
done < "$blocked_source_list"
done
print_group() {
local title=$1

View File

@@ -6,32 +6,35 @@ cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
manifest=$(cd "$(dirname "$manifest")" && pwd)/$(basename "$manifest")
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
default_manifest="$PWD/linuxcnc-rs274-wasm-source-files.txt"
count_nonempty_lines() {
local text=${1-}
local count=0
local line
while IFS= read -r line; do
if [[ -n "$line" ]]; then
count=$((count + 1))
fi
done <<<"$text"
printf '%s\n' "$count"
}
# This script writes fixed build and public artifact paths, so serialize it.
exec 9>"${TMPDIR:-/tmp}/cnc_sim_build_wasm.lock"
flock 9
manifest_core_count=0
manifest_blocked_count=0
manifest_core_sources=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_manifest_core.XXXXXX.txt")
manifest_blocked_sources=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_manifest_blocked.XXXXXX.txt")
trap 'rm -f "$manifest_core_sources" "$manifest_blocked_sources"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core > "$manifest_core_sources"
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked > "$manifest_blocked_sources"
manifest_core_count=$(wc -l < "$manifest_core_sources")
manifest_blocked_count=$(wc -l < "$manifest_blocked_sources")
manifest_core_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core)
manifest_blocked_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked)
manifest_core_count=$(count_nonempty_lines "$manifest_core_output")
manifest_blocked_count=$(count_nonempty_lines "$manifest_blocked_output")
if [[ "$manifest_core_count" -eq 0 || "$manifest_blocked_count" -eq 0 ]]; then
echo "unexpected wasm manifest partition: core=$manifest_core_count blocked=$manifest_blocked_count" >&2

21
check-linuxcnc-inputs.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
manifest=${1:-}
if [[ "$manifest" == "--root-only" ]]; then
manifest=
fi
if [[ -n "$manifest" && ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi

View File

@@ -2,10 +2,11 @@
#include <cmath>
#include "rtcp_kinematics.h"
namespace {
constexpr int kCssSpindleModeEvent = 960;
constexpr int kConstantRpmSpindleModeEvent = 970;
void rotate_xy(double *x, double *y, double degrees) {
const double radians = degrees * std::acos(-1.0) / 180.0;
const double c = std::cos(radians);
@@ -32,7 +33,9 @@ void CanonEventSink::reset() {
tool_length_offset_ = {};
xy_rotation_degrees_ = 0.0;
callback_status_ = 0;
kinematics_type_ = 1;
// LinuxCNC xyzbc-trt-kins with sparm=identityfirst starts at switchkins-type 0.
// Source: linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzbc-trt.ini.
kinematics_type_ = 0;
feed_override_ = false;
speed_override_ = false;
adaptive_feed_ = false;
@@ -67,6 +70,24 @@ void CanonEventSink::configure_rtcp(bool enabled, double tool_length) {
rtcp_tool_length_ = tool_length;
}
void CanonEventSink::configure_xyzbc_trt(const LinuxCncXyzbcTrtParameters &parameters) {
xyzbc_trt_parameters_ = parameters;
xyzbc_trt_parameters_configured_ = true;
use_xyzac_trt_ = false;
}
void CanonEventSink::configure_xyzac_trt(const LinuxCncXyzbcTrtParameters &parameters) {
xyzbc_trt_parameters_ = parameters;
xyzbc_trt_parameters_configured_ = true;
use_xyzac_trt_ = true;
}
void CanonEventSink::configure_fiveaxis(double pivot_length, int kinematics_type) {
fiveaxis_configured_ = true;
fiveaxis_pivot_length_ = pivot_length;
fiveaxis_kinematics_type_ = kinematics_type;
}
void CanonEventSink::set_tool_length(int h_code, double tool_length) {
if (h_code <= 0) {
default_rtcp_tool_length_ = tool_length;
@@ -119,15 +140,37 @@ void CanonEventSink::set_rtcp_state(bool enabled, int h_code, int line) {
emit(event);
}
void CanonEventSink::switch_kinematics(int kinematics_type, bool rtcp_enabled, int line) {
void CanonEventSink::switch_kinematics(int kinematics_type, int line) {
kinematics_type_ = kinematics_type;
rtcp_enabled_ = rtcp_enabled;
// Source: linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/{428,429,430}remap.ngc.
// M68 E3 Q#<kinstype>, then M66 E0 L0 to force synchronization.
set_analog_output(3, static_cast<double>(kinematics_type_), false, line);
wait_input(0, 0, 0, 0.0, line);
CncSimEvent event = base_event(CNC_SIM_EVENT_KINEMATICS_SWITCH, line);
event.reserved = kinematics_type_;
event.feed = rtcp_enabled_ ? 1.0 : 0.0;
emit(event);
}
void CanonEventSink::switch_m_code_kinematics(int m_code, int default_kinematics_type, int line) {
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.
if (m_code == 428) {
switch_kinematics(0, line);
return;
}
if (m_code == 429) {
switch_kinematics(1, line);
return;
}
if (m_code == 430) {
switch_kinematics(2, line);
return;
}
}
switch_kinematics(default_kinematics_type, line);
}
void CanonEventSink::set_g5x_offset(int index, const CncSimPose &offset, int line) {
CncSimEvent event = base_event(CNC_SIM_EVENT_SET_G5X_OFFSET, line);
event.tool = index;
@@ -198,7 +241,7 @@ void CanonEventSink::set_spindle_mode(int spindle, double mode, int line) {
CncSimEvent event = base_event(CNC_SIM_EVENT_COMMENT, line);
event.tool = spindle;
event.feed = mode;
event.reserved = mode == 0.0 ? 970 : 960;
event.reserved = mode == 0.0 ? kConstantRpmSpindleModeEvent : kCssSpindleModeEvent;
emit(event);
}
@@ -479,16 +522,67 @@ void CanonEventSink::emit(CncSimEvent event) {
}
void CanonEventSink::emit_rtcp_pivot(int line, const CncSimPose &start, const CncSimPose &end) {
if (!rtcp_enabled_ || rtcp_tool_length_ == 0.0 || callback_status_ != 0) {
if (!rtcp_enabled_ || callback_status_ != 0) {
return;
}
CncSimEvent event = base_event(CNC_SIM_EVENT_RTCP_PIVOT, line);
event.start = rtcp_pivot_from_tool_tip(start, rtcp_tool_length_);
event.end = rtcp_pivot_from_tool_tip(end, rtcp_tool_length_);
const RtcpVector tool = rtcp_tool_vector_from_pose(end, rtcp_tool_length_);
event.center.x = tool.x;
event.center.y = tool.y;
event.center.z = tool.z;
if (fiveaxis_configured_ && kinematics_type_ == fiveaxis_kinematics_type_) {
const LinuxCncFiveAxisJoints start_joints =
linuxcnc_5axis_inverse(start, fiveaxis_pivot_length_);
const LinuxCncFiveAxisJoints end_joints =
linuxcnc_5axis_inverse(end, fiveaxis_pivot_length_);
event.start.x = start_joints.x;
event.start.y = start_joints.y;
event.start.z = start_joints.z;
event.start.b = start_joints.b;
event.start.c = start_joints.c;
event.start.w = start_joints.w;
event.end.x = end_joints.x;
event.end.y = end_joints.y;
event.end.z = end_joints.z;
event.end.b = end_joints.b;
event.end.c = end_joints.c;
event.end.w = end_joints.w;
event.reserved = kinematics_type_;
event.dwell_seconds = fiveaxis_pivot_length_;
} else if (kinematics_type_ == 1 || kinematics_type_ == 2) {
LinuxCncFiveAxisJoints start_joints{};
LinuxCncFiveAxisJoints end_joints{};
if (kinematics_type_ == 1) {
LinuxCncXyzbcTrtParameters parameters = xyzbc_trt_parameters_configured_
? xyzbc_trt_parameters_
: linuxcnc_xyzbc_trt_default_parameters(rtcp_tool_length_);
parameters.tool_offset = rtcp_tool_length_;
if (use_xyzac_trt_) {
start_joints = linuxcnc_xyzac_trt_inverse(start, parameters);
end_joints = linuxcnc_xyzac_trt_inverse(end, parameters);
} else {
start_joints = linuxcnc_xyzbc_trt_inverse(start, parameters);
end_joints = linuxcnc_xyzbc_trt_inverse(end, parameters);
}
} else {
start_joints = linuxcnc_xyzbc_userk_inverse(start);
end_joints = linuxcnc_xyzbc_userk_inverse(end);
}
event.start.x = start_joints.x;
event.start.y = start_joints.y;
event.start.z = start_joints.z;
event.start.a = start_joints.a;
event.start.b = start_joints.b;
event.start.c = start_joints.c;
event.start.w = start_joints.w;
event.end.x = end_joints.x;
event.end.y = end_joints.y;
event.end.z = end_joints.z;
event.end.a = end_joints.a;
event.end.b = end_joints.b;
event.end.c = end_joints.c;
event.end.w = end_joints.w;
event.reserved = kinematics_type_;
event.dwell_seconds = rtcp_tool_length_;
} else {
return;
}
emit(event);
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "cnc_sim_api.h"
#include "rtcp_kinematics.h"
#include <unordered_map>
@@ -12,11 +13,15 @@ public:
bool callback_aborted() const;
void set_block_delete(bool enabled);
void configure_rtcp(bool enabled, double tool_length);
void configure_xyzbc_trt(const LinuxCncXyzbcTrtParameters &parameters);
void configure_xyzac_trt(const LinuxCncXyzbcTrtParameters &parameters);
void configure_fiveaxis(double pivot_length, int kinematics_type);
void 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, bool rtcp_enabled, int line);
void switch_kinematics(int kinematics_type, int line);
void 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);
@@ -97,9 +102,15 @@ private:
double default_rtcp_tool_length_ = 0.0;
double rtcp_tool_length_ = 0.0;
int rtcp_h_code_ = 0;
LinuxCncXyzbcTrtParameters xyzbc_trt_parameters_{};
bool xyzbc_trt_parameters_configured_ = false;
bool use_xyzac_trt_ = false;
bool fiveaxis_configured_ = false;
double fiveaxis_pivot_length_ = 250.0;
int fiveaxis_kinematics_type_ = 0;
CncSimPose tool_length_offset_{};
double xy_rotation_degrees_ = 0.0;
int kinematics_type_ = 1;
int kinematics_type_ = 0;
bool feed_override_ = false;
bool speed_override_ = false;
bool adaptive_feed_ = false;

View File

@@ -9,7 +9,11 @@
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;
};
@@ -128,6 +132,86 @@ void apply_tool_length_table(CanonEventSink &sink, const std::string &compact) {
}
}
bool find_number_any(const std::string &compact, double *value, const char *first) {
return find_number_value(compact, first, value);
}
template <typename... Keys>
bool find_number_any(const std::string &compact, double *value, const char *first, Keys... rest) {
return find_number_value(compact, first, value) || find_number_any(compact, value, rest...);
}
bool contains_bool_any(const std::string &compact, bool *value, const char *first) {
return contains_bool_value(compact, first, value);
}
template <typename... Keys>
bool contains_bool_any(const std::string &compact, bool *value, const char *first, Keys... rest) {
return contains_bool_value(compact, first, value) || contains_bool_any(compact, value, rest...);
}
void apply_xyzbc_trt_config(CanonEventSink &sink, const std::string &compact) {
LinuxCncXyzbcTrtParameters parameters = linuxcnc_xyzbc_trt_default_parameters(0.0);
bool configured = false;
const bool xyzac = compact.find("\"trt\":\"xyzac\"") != std::string::npos ||
compact.find("\"kinematics\":\"xyzac-trt\"") != std::string::npos ||
compact.find("\"switchkins\":\"xyzac-trt\"") != std::string::npos;
const bool xyzbc = compact.find("\"trt\":\"xyzbc\"") != std::string::npos ||
compact.find("\"kinematics\":\"xyzbc-trt\"") != std::string::npos ||
compact.find("\"switchkins\":\"xyzbc-trt\"") != std::string::npos;
configured = find_number_any(compact, &parameters.x_rot_point, "xrotpoint", "x_rot_point", "x-rot-point") ||
configured;
configured = find_number_any(compact, &parameters.y_rot_point, "yrotpoint", "y_rot_point", "y-rot-point") ||
configured;
configured = find_number_any(compact, &parameters.z_rot_point, "zrotpoint", "z_rot_point", "z-rot-point") ||
configured;
configured = find_number_any(compact, &parameters.x_offset, "xoffset", "x_offset", "x-offset") ||
configured;
configured = find_number_any(compact, &parameters.y_offset, "yoffset", "y_offset", "y-offset") ||
configured;
configured = find_number_any(compact, &parameters.z_offset, "zoffset", "z_offset", "z-offset") ||
configured;
configured = contains_bool_any(compact,
&parameters.conventional_directions,
"conventionaldirections",
"conventional_directions",
"conventional-directions") ||
configured;
if (configured || xyzac || xyzbc) {
configured = true;
}
if (configured && xyzac) {
sink.configure_xyzac_trt(parameters);
} else if (configured) {
sink.configure_xyzbc_trt(parameters);
}
}
void apply_fiveaxis_config(CanonEventSink &sink, const std::string &compact) {
const bool fiveaxis = compact.find("\"kinematics\":\"5axiskins\"") != std::string::npos ||
compact.find("\"switchkins\":\"5axiskins\"") != std::string::npos ||
compact.find("\"kinematics\":\"5axis\"") != std::string::npos;
double pivot_length = 250.0;
const bool has_pivot_length = find_number_any(compact,
&pivot_length,
"pivotlength",
"pivot_length",
"pivot-length");
double kinematics_type = 0.0;
const bool has_kinematics_type = find_number_any(compact,
&kinematics_type,
"fiveaxiskinematicstype",
"fiveaxis_kinematics_type",
"fiveaxis-kinematics-type",
"kinematicstype",
"kinematics_type");
if (fiveaxis || has_pivot_length || has_kinematics_type) {
sink.configure_fiveaxis(pivot_length, static_cast<int>(kinematics_type));
}
}
int apply_config(CncSimHandle *handle, const std::string &json) {
if (json.empty()) {
return 0;
@@ -158,6 +242,8 @@ int apply_config(CncSimHandle *handle, const std::string &json) {
handle->sink.set_block_delete(block_delete);
}
apply_tool_length_table(handle->sink, compact);
apply_xyzbc_trt_config(handle->sink, compact);
apply_fiveaxis_config(handle->sink, compact);
return 0;
}

View File

@@ -15,6 +15,8 @@
namespace {
constexpr int kToolLengthOffsetEvent = 430;
CanonEventSink *active_sink = nullptr;
int active_line = 0;
bool flood_on = false;
@@ -515,7 +517,7 @@ void USE_TOOL_LENGTH_OFFSET(const EmcPose &offset) {
event.version = 1;
event.type = CNC_SIM_EVENT_COMMENT;
event.line = event_line();
event.reserved = 430;
event.reserved = kToolLengthOffsetEvent;
event.start = tool_offset;
active_sink->emit_raw(event);
}

View File

@@ -217,7 +217,7 @@ int parse_linuxcnc_rs274_backend(CanonEventSink &sink,
++line_number;
cnc_sim_linuxcnc_set_current_line(line_number);
std::vector<SimulatorGcodeControlAction> control_actions;
if (parse_simulator_gcode_control_line(line, &control_actions)) {
if (parse_simulator_gcode_control_line(line, sink.block_delete(), &control_actions)) {
bool simulator_only_line = true;
for (const auto &action : control_actions) {
emit_simulator_gcode_control_action(sink, action, line_number);

File diff suppressed because it is too large Load Diff

View File

@@ -8,10 +8,134 @@ struct RtcpVector {
double z;
};
// Rotation order is intrinsic tool orientation A then B then C, represented as
// Rz(C) * Ry(B) * Rx(A) applied to a local tool vector.
RtcpVector rtcp_rotate_abc_degrees(RtcpVector vector, double a_deg, double b_deg, double c_deg);
struct LinuxCncFiveAxisJoints {
double x;
double y;
double z;
double a;
double b;
double c;
double w;
};
RtcpVector rtcp_tool_vector_from_pose(const CncSimPose &pose, double tool_length);
CncSimPose rtcp_pivot_from_tool_tip(const CncSimPose &tool_tip, double tool_length);
CncSimPose rtcp_tool_tip_from_pivot(const CncSimPose &pivot, double tool_length);
struct LinuxCncAxisJoints {
double x;
double y;
double z;
double a;
double b;
double c;
double u;
double v;
double w;
};
struct LinuxCncXyzbcTrtParameters {
double x_rot_point;
double y_rot_point;
double z_rot_point;
double x_offset;
double y_offset;
double z_offset;
double tool_offset;
bool conventional_directions;
};
struct LinuxCncScaraParameters {
double d1;
double d2;
double d3;
double d4;
double d5;
double d6;
};
struct LinuxCncXyzabTdrParameters {
double tool_offset_z;
double x_offset;
double z_offset;
double x_rot_point;
double y_rot_point;
double z_rot_point;
};
struct LinuxCncPumaParameters {
double a2;
double a3;
double d3;
double d4;
double d6;
};
struct LinuxCncGenserParameters {
double a[6];
double alpha[6];
double d[6];
int unrotate[6];
};
struct LinuxCncScaraJoints {
double j0;
double j1;
double j2;
double j3;
double j4;
double j5;
};
LinuxCncAxisJoints linuxcnc_identity_default_joints();
CncSimPose linuxcnc_identity_forward(const LinuxCncAxisJoints &joints);
LinuxCncAxisJoints linuxcnc_identity_inverse(const CncSimPose &pose);
CncSimPose linuxcnc_rotatekins_forward(const LinuxCncAxisJoints &joints);
LinuxCncAxisJoints linuxcnc_rotatekins_inverse(const CncSimPose &pose);
CncSimPose linuxcnc_5axis_forward(const LinuxCncFiveAxisJoints &joints, double pivot_length);
LinuxCncFiveAxisJoints linuxcnc_5axis_inverse(const CncSimPose &pose, double pivot_length);
LinuxCncXyzbcTrtParameters linuxcnc_xyzbc_trt_default_parameters(double tool_offset);
CncSimPose linuxcnc_xyzbc_trt_forward(const LinuxCncFiveAxisJoints &joints,
const LinuxCncXyzbcTrtParameters &parameters);
LinuxCncFiveAxisJoints linuxcnc_xyzbc_trt_inverse(const CncSimPose &pose,
const LinuxCncXyzbcTrtParameters &parameters);
CncSimPose linuxcnc_xyzac_trt_forward(const LinuxCncFiveAxisJoints &joints,
const LinuxCncXyzbcTrtParameters &parameters);
LinuxCncFiveAxisJoints linuxcnc_xyzac_trt_inverse(const CncSimPose &pose,
const LinuxCncXyzbcTrtParameters &parameters);
CncSimPose linuxcnc_xyzbc_userk_forward(const LinuxCncFiveAxisJoints &joints);
LinuxCncFiveAxisJoints linuxcnc_xyzbc_userk_inverse(const CncSimPose &pose);
LinuxCncScaraParameters linuxcnc_scara_default_parameters();
CncSimPose linuxcnc_scara_forward(const LinuxCncScaraJoints &joints,
const LinuxCncScaraParameters &parameters,
int *iflags);
LinuxCncScaraJoints linuxcnc_scara_inverse(const CncSimPose &pose,
const LinuxCncScaraParameters &parameters,
int iflags);
LinuxCncXyzabTdrParameters linuxcnc_xyzab_tdr_default_parameters(double tool_offset_z);
CncSimPose linuxcnc_xyzab_tdr_identity_forward(const LinuxCncAxisJoints &joints);
LinuxCncAxisJoints linuxcnc_xyzab_tdr_identity_inverse(const CncSimPose &pose);
CncSimPose linuxcnc_xyzab_tdr_tcp_forward(const LinuxCncAxisJoints &joints,
const LinuxCncXyzabTdrParameters &parameters);
LinuxCncAxisJoints linuxcnc_xyzab_tdr_tcp_inverse(const CncSimPose &pose,
const LinuxCncXyzabTdrParameters &parameters);
LinuxCncPumaParameters linuxcnc_puma_default_parameters();
CncSimPose linuxcnc_puma_forward(const LinuxCncAxisJoints &joints,
const LinuxCncPumaParameters &parameters,
int *iflags);
LinuxCncAxisJoints linuxcnc_puma_inverse(const CncSimPose &pose,
const LinuxCncAxisJoints &current_joints,
const LinuxCncPumaParameters &parameters,
int iflags,
int *fflags);
LinuxCncGenserParameters linuxcnc_genser_puma560_parameters();
CncSimPose linuxcnc_genser_forward(const LinuxCncAxisJoints &joints,
const LinuxCncGenserParameters &parameters);
LinuxCncAxisJoints linuxcnc_genser_inverse(const CncSimPose &pose,
const LinuxCncAxisJoints &joint_estimate,
const LinuxCncGenserParameters &parameters,
int *iterations,
int max_iterations = 100);

View File

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

View File

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

View File

@@ -149,11 +149,13 @@ constexpr double kRadiusToleranceMm = kRadiusToleranceInch * kMmPerInch;
constexpr double kCenterArcRadiusToleranceInch = 2.0 * 0.001 * M_SQRT2;
constexpr double kCenterArcRadiusToleranceMm = 2.0 * 0.01 * M_SQRT2;
constexpr double kSpiralRelativeTolerance = 0.001;
constexpr int kConstantRpmSpindleModeEvent = 970;
constexpr int kCutterCompOffEvent = 400;
constexpr int kCutterCompLeftEvent = 410;
constexpr int kCutterCompRightEvent = 420;
constexpr int kCutterCompLeftExplicitEvent = 411;
constexpr int kCutterCompRightExplicitEvent = 421;
constexpr int kToolLengthOffsetEvent = 430;
enum class LinuxCncReaderSymbol {
None,
@@ -178,6 +180,12 @@ 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<char, double> &words, char letter, int *value);
bool evaluate_expression(const std::string &text,
const std::vector<CallFrame> &call_stack,
const std::unordered_map<int, double> &parameters,
const std::unordered_map<std::string, double> &named_parameters,
double *value,
std::string *error);
template <size_t Size>
bool contains_int(const std::array<int, Size> &values, int value) {
@@ -685,6 +693,91 @@ LinuxCncIntegerReadResult read_linuxcnc_integer_value(const std::string &text,
return LinuxCncIntegerReadResult::Ok;
}
bool is_current_position_parameter_index(int index) {
return index >= 5420 && index <= 5428;
}
bool line_reads_current_position_parameter(const std::string &line,
const std::vector<CallFrame> &call_stack,
const std::unordered_map<int, double> &parameters,
const std::unordered_map<std::string, double> &named_parameters) {
for (size_t i = 0; i < line.size(); ++i) {
if (line[i] != '#') {
continue;
}
int index = 0;
size_t index_end = i + 1;
size_t parameter_start = i + 1;
while (parameter_start < line.size() &&
std::isspace(static_cast<unsigned char>(line[parameter_start]))) {
++parameter_start;
}
if (parameter_start < line.size() && line[parameter_start] == '[') {
const size_t close = find_matching_square_bracket(line, parameter_start);
if (close != std::string::npos) {
size_t bracket_index_end = parameter_start + 1;
const LinuxCncIntegerReadResult bracket_result =
read_linuxcnc_integer_value(line, parameter_start + 1, &index, &bracket_index_end);
size_t after_bracket_index = bracket_index_end;
while (after_bracket_index < close &&
std::isspace(static_cast<unsigned char>(line[after_bracket_index]))) {
++after_bracket_index;
}
bool has_current_position_index =
bracket_result == LinuxCncIntegerReadResult::Ok &&
after_bracket_index == close &&
is_current_position_parameter_index(index);
if (!has_current_position_index) {
double index_value = 0.0;
if (evaluate_expression(line.substr(parameter_start + 1,
close - parameter_start - 1),
call_stack,
parameters,
named_parameters,
&index_value,
nullptr)) {
int expression_index = 0;
has_current_position_index =
linuxcnc_integer_from_real(index_value, &expression_index) ==
LinuxCncIntegerReadResult::Ok &&
is_current_position_parameter_index(expression_index);
}
}
if (has_current_position_index) {
size_t after_close = close + 1;
while (after_close < line.size() &&
std::isspace(static_cast<unsigned char>(line[after_close]))) {
++after_close;
}
if (after_close < line.size() && line[after_close] == '=') {
continue;
}
return true;
}
}
continue;
}
const LinuxCncIntegerReadResult result =
read_linuxcnc_integer_value(line, i + 1, &index, &index_end);
if (result != LinuxCncIntegerReadResult::Ok ||
!is_current_position_parameter_index(index)) {
continue;
}
size_t after_index = index_end;
while (after_index < line.size() &&
std::isspace(static_cast<unsigned char>(line[after_index]))) {
++after_index;
}
if (after_index < line.size() && line[after_index] == '=') {
continue;
}
return true;
}
return false;
}
bool parse_linuxcnc_real_number(const char *text, double *value, size_t *consumed) {
size_t end = 0;
while (text[end] == '+' || text[end] == '-') {
@@ -1190,6 +1283,7 @@ bool parse_word_list_impl(const std::string &line, ParsedWordList *parsed, std::
parsed->theta_flag = false;
parsed->theta_value = 0.0;
std::array<bool, 26> seen_letters{};
std::array<double, 26> seen_letter_values{};
const char *text = line.c_str();
for (size_t i = 0; text[i] != '\0';) {
if (std::isspace(static_cast<unsigned char>(text[i]))) {
@@ -1338,13 +1432,27 @@ bool parse_word_list_impl(const std::string &line, ParsedWordList *parsed, std::
if (letter != 'G' && letter != 'M') {
const size_t letter_index = static_cast<size_t>(letter - 'A');
if (strict && seen_letters[letter_index]) {
if (error) {
*error = "Multiple " + std::string(1, static_cast<char>(std::tolower(static_cast<unsigned char>(letter)))) +
" words on one line";
if ((letter == 'P' || letter == 'Q') && seen_letter_values[letter_index] <= -1.0) {
parsed->words.erase(std::remove_if(parsed->words.begin(),
parsed->words.end(),
[letter](const Word &word) {
return word.letter == letter;
}),
parsed->words.end());
} else {
if (error) {
const char duplicate_letter =
(letter == 'U' || letter == 'V' || letter == 'W')
? letter
: static_cast<char>(std::tolower(static_cast<unsigned char>(letter)));
*error = "Multiple " + std::string(1, duplicate_letter) +
" words on one line";
}
return false;
}
return false;
}
seen_letters[letter_index] = true;
seen_letter_values[letter_index] = value;
}
parsed->words.push_back({letter, value});
i = next;
@@ -3309,7 +3417,7 @@ std::string expand_expressions_and_parameters(const std::string &line,
const size_t close = find_matching_square_bracket(line, function_start);
if (close == std::string::npos) {
if (error) {
*error = "unterminated bracket expression";
*error = "Unclosed expression";
}
return {};
}
@@ -3356,7 +3464,7 @@ std::string expand_expressions_and_parameters(const std::string &line,
const size_t close = find_matching_square_bracket(line, i);
if (close == std::string::npos) {
if (error) {
*error = "unterminated bracket expression";
*error = "Unclosed expression";
}
return {};
}
@@ -3391,7 +3499,7 @@ std::string expand_expressions_and_parameters(const std::string &line,
const size_t close = find_matching_square_bracket(line, end);
if (close == std::string::npos) {
if (error) {
*error = "unterminated indirect parameter expression";
*error = "Unclosed expression";
}
return {};
}
@@ -3400,7 +3508,7 @@ std::string expand_expressions_and_parameters(const std::string &line,
const size_t close = line.find('>', end + 1);
if (close == std::string::npos) {
if (error) {
*error = "unterminated indirect named parameter expression";
*error = "Named parameter not terminated";
}
return {};
}
@@ -3445,7 +3553,7 @@ std::string expand_expressions_and_parameters(const std::string &line,
const size_t close = find_matching_square_bracket(line, j);
if (close == std::string::npos) {
if (error) {
*error = "unterminated indexed parameter expression";
*error = "Unclosed expression";
}
return {};
}
@@ -3491,7 +3599,7 @@ std::string expand_expressions_and_parameters(const std::string &line,
const size_t close = line.find('>', j + 1);
if (close == std::string::npos) {
if (error) {
*error = "unterminated named parameter expression";
*error = "Named parameter not terminated";
}
return {};
}
@@ -3653,7 +3761,7 @@ bool parse_parameter_assignment(const std::string &line,
const size_t close = text.find('>', pos + 1);
if (close == std::string::npos) {
if (error) {
*error = "unterminated named parameter assignment";
*error = "Named parameter not terminated";
}
return true;
}
@@ -3669,7 +3777,7 @@ bool parse_parameter_assignment(const std::string &line,
const size_t close = find_matching_square_bracket(text, pos);
if (close == std::string::npos) {
if (error) {
*error = "unterminated indexed parameter assignment";
*error = "Unclosed expression";
}
return true;
}
@@ -3793,7 +3901,7 @@ bool parse_parameter_assignment(const std::string &line,
const size_t close = find_matching_square_bracket(text, pos);
if (close == std::string::npos) {
if (error) {
*error = "unterminated parameter assignment expression";
*error = "Unclosed expression";
}
return true;
}
@@ -4994,6 +5102,119 @@ bool validate_r_arc_reaches_end_point(const CncSimEvent &event, int plane, doubl
return true;
}
bool validate_absolute_center_arc_axis_words(const std::unordered_map<char, double> &words,
int plane,
bool absolute,
std::string *error) {
if (!absolute || words.count('R')) {
return true;
}
auto missing = [error](char axis) {
if (error) {
*error = std::string(1, axis) + " word missing in absolute center arc";
}
return false;
};
if (plane == 17 && (words.count('I') || words.count('J'))) {
if (!words.count('I')) {
return missing('I');
}
if (!words.count('J')) {
return missing('J');
}
} else if (plane == 18 && (words.count('I') || words.count('K'))) {
if (!words.count('I')) {
return missing('I');
}
if (!words.count('K')) {
return missing('K');
}
} else if (plane == 19 && (words.count('J') || words.count('K'))) {
if (!words.count('J')) {
return missing('J');
}
if (!words.count('K')) {
return missing('K');
}
}
return true;
}
bool validate_arc_format_words(const std::unordered_map<char, double> &words, std::string *error) {
const bool has_r = words.count('R') != 0;
const bool has_ijk = words.count('I') || words.count('J') || words.count('K');
if (has_r && has_ijk) {
if (error) {
*error = "Mixed radius ijk format for arc";
}
return false;
}
if (!has_r && !has_ijk) {
if (error) {
*error = "R i j k words all missing for arc";
}
return false;
}
return true;
}
bool validate_arc_plane_center_words(const std::unordered_map<char, double> &words,
int plane,
std::string *error) {
if (plane == 17 && words.count('K')) {
if (error) {
*error = "K word given for arc in xy plane";
}
return false;
}
if (plane == 18 && words.count('J')) {
if (error) {
*error = "J word given for arc in xz plane";
}
return false;
}
if (plane == 19 && words.count('I')) {
if (error) {
*error = "I word given for arc in yz plane";
}
return false;
}
return true;
}
bool validate_r_arc_endpoint_words(const std::unordered_map<char, double> &words,
const ParsedWordList &parsed,
int plane,
std::string *error) {
if (!words.count('R')) {
return true;
}
if (plane == 17) {
if (!words.count('X') && !words.count('Y') &&
!parsed.radius_flag && !parsed.theta_flag) {
if (error) {
*error = "X and y words missing for arc in xy plane";
}
return false;
}
} else if (plane == 18) {
if (!words.count('X') && !words.count('Z')) {
if (error) {
*error = "X and z words missing for arc in xz plane";
}
return false;
}
} else if (plane == 19) {
if (!words.count('Y') && !words.count('Z')) {
if (error) {
*error = "Y and z words missing for arc in yz plane";
}
return false;
}
}
return true;
}
bool modal_motion_is_threading(int modal_motion) {
return modal_motion == kModalMotionG33 ||
modal_motion == kModalMotionG331;
@@ -5101,7 +5322,7 @@ void emit_tool_length_offset(CanonEventSink &sink, const CncSimPose &offset, int
event.version = 1;
event.type = CNC_SIM_EVENT_COMMENT;
event.line = line;
event.reserved = 430;
event.reserved = kToolLengthOffsetEvent;
event.start = offset;
sink.emit_raw(event);
}
@@ -5645,6 +5866,13 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string
}
refresh_readonly_named_parameters(line_number);
if (cutter_comp_on_ &&
line_reads_current_position_parameter(line, call_stack, parameters, named_parameters)) {
if (error) {
*error = "Cannot read current position with cutter radius compensation on";
}
return -1;
}
std::string parameter_error;
std::vector<PendingParameterAssignment> pending_assignments;
if (!extract_parameter_assignments(line,
@@ -5726,7 +5954,7 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string
}
std::vector<SimulatorGcodeControlAction> simulator_control_actions;
if (!has_keyword_oword &&
parse_simulator_gcode_control_line(line, &simulator_control_actions)) {
parse_simulator_gcode_control_line(line, sink_.block_delete(), &simulator_control_actions)) {
bool simulator_only_line = true;
for (const auto &action : simulator_control_actions) {
emit_simulator_gcode_control_action(sink_, action, line_number);
@@ -6578,7 +6806,8 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string
if (l == 0) {
if (spindle_tool_id_ > 0) {
if (error) {
*error = "G10 L0 not allowed with loaded tool";
*error = "G10 L0 not allowed with loaded tool <" +
std::to_string(spindle_tool_id_) + ">";
}
return -1;
}
@@ -6761,21 +6990,30 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string
if (explicit_comp) {
if (!words.count('D')) {
if (error) {
*error = "G41.1/G42.1 requires a D word";
*error = std::string(cutter_comp_left ? "G41.1" : "G42.1") +
" with no D word";
}
return -1;
}
if (words.count('L') && sink_.plane() != 18) {
if (error) {
*error = "G41.1/G42.1 with L word requires G18";
*error = std::string(cutter_comp_left ? "G41.1" : "G42.1") +
" with L word, but plane is not G18";
}
return -1;
}
} else if (words.count('D')) {
const double d_value = words.at('D');
if (std::fabs(d_value - std::round(d_value)) > 0.0001 || d_value < 0.0) {
if (std::fabs(d_value - std::round(d_value)) > 0.0001) {
if (error) {
*error = "G41/G42 requires a non-negative whole-number D word";
*error = std::string(cutter_comp_left ? "G41" : "G42") +
" requires D word to be a whole number";
}
return -1;
}
if (d_value < 0.0) {
if (error) {
*error = "Negative d word tool radius index used";
}
return -1;
}
@@ -7626,6 +7864,16 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string
break;
}
const int spindle = selected_spindle;
if ((m == 3 || m == 4 || m == 5) &&
parsed_word_list.dollar_flag &&
!smoke_spindle_selector_valid_for_speed(parsed_word_list.dollar_value)) {
if (error) {
*error = "Spindle ($) number out of range in M" + std::to_string(m) +
" Command\nnum_spindles =" + std::to_string(kSmokeNumSpindles) +
". $=" + std::to_string(rounded_value(parsed_word_list.dollar_value)) + "\n";
}
return -1;
}
if (m == 3) {
sink_.start_spindle(spindle, 1, line_number);
if (stop_if_callback_aborted(sink_, error)) {
@@ -7944,17 +8192,17 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string
return -1;
}
} else if (m == 428) {
sink_.switch_kinematics(1, true, line_number);
sink_.switch_kinematics(1, line_number);
if (stop_if_callback_aborted(sink_, error)) {
return -1;
}
} else if (m == 429) {
sink_.switch_kinematics(0, false, line_number);
sink_.switch_kinematics(0, line_number);
if (stop_if_callback_aborted(sink_, error)) {
return -1;
}
} else if (m == 430) {
sink_.switch_kinematics(2, true, line_number);
sink_.switch_kinematics(2, line_number);
if (stop_if_callback_aborted(sink_, error)) {
return -1;
}
@@ -8077,8 +8325,15 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string
}
return -1;
}
const bool arc_center_or_radius_words =
(modal_motion == 2 || modal_motion == 3) &&
(words.count('I') || words.count('J') || words.count('K') ||
(words.count('R') && !has_m_code(word_list, 19)));
const bool current_line_requests_motion =
has_axis_words || parsed_word_list.radius_flag || parsed_word_list.theta_flag;
has_axis_words ||
parsed_word_list.radius_flag ||
parsed_word_list.theta_flag ||
arc_center_or_radius_words;
const bool explicit_threading_motion =
has_g_code(word_list, 33.0) || has_g_code(word_list, 33.1) || has_g_code(word_list, 76.0);
if ((current_line_requests_motion || explicit_threading_motion) && !has_axis_words &&
@@ -8723,11 +8978,23 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string
}
return -1;
}
if (!validate_arc_format_words(words, error)) {
return -1;
}
if (!validate_arc_plane_center_words(words, sink_.plane(), error)) {
return -1;
}
if (!validate_r_arc_endpoint_words(words, parsed_word_list, sink_.plane(), error)) {
return -1;
}
event.arc_turns = modal_motion == 2 ? -1 : 1;
if (words.count('R') &&
!validate_r_arc_reaches_end_point(event, sink_.plane(), words.at('R') * sink_.unit_scale(), error)) {
return -1;
}
if (!validate_absolute_center_arc_axis_words(words, sink_.plane(), arc_absolute_, error)) {
return -1;
}
set_arc_center(&event, words, sink_.plane(), sink_.unit_scale(), arc_absolute_);
if (!words.count('R') &&
!validate_arc_center_radius_consistency(event, sink_.plane(), sink_.unit_scale(), error)) {
@@ -8964,7 +9231,7 @@ int SmokeGcodeParser::parse(const char *program, size_t program_len, std::string
if (stop_if_callback_aborted(sink_, error)) {
return -1;
}
emit_comment_state(sink_, line_number, 970);
emit_comment_state(sink_, line_number, kConstantRpmSpindleModeEvent);
if (stop_if_callback_aborted(sink_, error)) {
return -1;
}

View File

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

View File

@@ -6,6 +6,10 @@
namespace {
constexpr int kToolLengthOffsetEvent = 430;
constexpr int kCssSpindleModeEvent = 960;
constexpr int kConstantRpmSpindleModeEvent = 970;
int collect_event(const CncSimEvent *event, void *user_data) {
auto *events = static_cast<std::vector<CncSimEvent> *>(user_data);
events->push_back(*event);
@@ -54,6 +58,34 @@ bool saw_comment_state(const std::vector<CncSimEvent> &events,
} // namespace
int main() {
{
std::vector<CncSimEvent> default_events;
CncSimHandle *default_sim = cnc_sim_create();
cnc_sim_set_event_callback(default_sim, collect_event, &default_events);
const char default_program[] =
"G21 G90 G17\n"
"G0 X1\n"
"M30\n";
bool default_ok = true;
default_ok &= expect(cnc_sim_parse_program(default_sim,
default_program,
sizeof(default_program) - 1) == 0,
cnc_sim_last_error(default_sim));
bool saw_default_linuxcnc_rapid = false;
for (const auto &event : default_events) {
saw_default_linuxcnc_rapid = saw_default_linuxcnc_rapid ||
(event.type == CNC_SIM_EVENT_RAPID &&
event.line == 2 &&
near(event.end.x, 1.0));
}
default_ok &= expect(saw_default_linuxcnc_rapid,
"expected compiled API default backend to use LinuxCNC rs274");
cnc_sim_destroy(default_sim);
if (!default_ok) {
return 1;
}
}
const char config[] =
"{\"backend\":\"linuxcnc-rs274\",\"rtcp\":{\"enabled\":true,\"toolLength\":100,\"toolLengths\":{\"7\":125}}}";
const char program[] =
@@ -62,6 +94,7 @@ int main() {
"M428 M429 M430\n"
"G49\n"
"G43.4 H7\n"
"G43.5 H7\n"
"S8000 M3\n"
"G0 X0 Y0 Z5\n"
"F600\n"
@@ -91,6 +124,7 @@ int main() {
bool saw_m430 = false;
bool saw_g49 = false;
bool saw_g434 = false;
bool saw_g435 = false;
bool saw_g5x_offset = false;
bool saw_xy_rotation = false;
bool saw_g92_clear = false;
@@ -180,22 +214,20 @@ int main() {
saw_end = saw_end || event.type == CNC_SIM_EVENT_PROGRAM_END;
saw_rtcp = saw_rtcp ||
(event.type == CNC_SIM_EVENT_RTCP_PIVOT &&
event.end.z == 130.0);
event.reserved == 2 &&
event.end.z == 5.0);
saw_m428 = saw_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 3 &&
event.reserved == 1 &&
event.feed == 1.0);
event.reserved == 1);
saw_m429 = saw_m429 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 3 &&
event.reserved == 0 &&
event.feed == 0.0);
event.reserved == 0);
saw_m430 = saw_m430 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 3 &&
event.reserved == 2 &&
event.feed == 1.0);
event.reserved == 2);
saw_g49 = saw_g49 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 4 &&
@@ -206,6 +238,12 @@ int main() {
event.feed == 1.0 &&
event.tool == 7 &&
event.dwell_seconds == 125.0);
saw_g435 = saw_g435 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 6 &&
event.feed == 1.0 &&
event.tool == 7 &&
event.dwell_seconds == 125.0);
}
ok &= expect(saw_tool, "expected LinuxCNC tool-change event");
@@ -217,8 +255,17 @@ int main() {
ok &= expect(saw_m428, "expected LinuxCNC M428 kinematics switch");
ok &= expect(saw_m429, "expected LinuxCNC M429 kinematics switch");
ok &= expect(saw_m430, "expected LinuxCNC M430 kinematics switch");
ok &= expect(saw_comment_state(events, 3, 68, 3, 1.0),
"expected LinuxCNC M428 remap M68 E3 Q1 side effect");
ok &= expect(saw_comment_state(events, 3, 68, 3, 0.0),
"expected LinuxCNC M429 remap M68 E3 Q0 side effect");
ok &= expect(saw_comment_state(events, 3, 68, 3, 2.0),
"expected LinuxCNC M430 remap M68 E3 Q2 side effect");
ok &= expect(saw_comment_state(events, 3, 66, 0, 0.0),
"expected LinuxCNC M428/M429/M430 remap M66 E0 L0 side effect");
ok &= expect(saw_g49, "expected LinuxCNC G49 RTCP off state");
ok &= expect(saw_g434, "expected LinuxCNC G43.4 RTCP on state with H code");
ok &= expect(saw_g435, "expected LinuxCNC G43.5 RTCP on state with H code");
const char arc_planes_program[] =
"G21 G90 G17\n"
@@ -416,25 +463,25 @@ int main() {
saw_g431_tool_length = saw_g431_tool_length ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 2 &&
event.reserved == 430 &&
event.reserved == kToolLengthOffsetEvent &&
event.start.x == 1.0 &&
event.start.z == 2.0);
saw_g432_tool_length = saw_g432_tool_length ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 3 &&
event.reserved == 430 &&
event.reserved == kToolLengthOffsetEvent &&
event.start.x == 1.25 &&
event.start.z == 2.5);
saw_g49_dynamic_tool_length_clear = saw_g49_dynamic_tool_length_clear ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 5 &&
event.reserved == 430 &&
event.reserved == kToolLengthOffsetEvent &&
event.start.x == 0.0 &&
event.start.z == 0.0);
saw_g432_h_tool_length = saw_g432_h_tool_length ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 4 &&
event.reserved == 430 &&
event.reserved == kToolLengthOffsetEvent &&
event.start.x == 1.25 &&
event.start.z == 15.0);
}
@@ -517,12 +564,12 @@ int main() {
saw_g97_mode = saw_g97_mode ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 2 &&
event.reserved == 970 &&
event.reserved == kConstantRpmSpindleModeEvent &&
event.feed == 0.0);
saw_g96_mode = saw_g96_mode ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 3 &&
event.reserved == 960 &&
event.reserved == kCssSpindleModeEvent &&
event.feed == 2500.0);
}
ok &= expect(saw_g97_mode, "expected LinuxCNC G97 constant-RPM spindle mode state");
@@ -903,12 +950,12 @@ int main() {
saw_tool_length = saw_tool_length ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 2 &&
event.reserved == 430 &&
event.reserved == kToolLengthOffsetEvent &&
near(event.start.z, 12.5));
saw_tool_length_clear = saw_tool_length_clear ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 3 &&
event.reserved == 430 &&
event.reserved == kToolLengthOffsetEvent &&
event.start.z == 0.0);
}
ok &= expect(saw_tool_length, "expected LinuxCNC G43 H1 tool length event");
@@ -1272,6 +1319,157 @@ int main() {
}
ok &= expect(saw_block_delete_disabled_line, "expected LinuxCNC disabled block-delete line to execute");
const char block_delete_rtcp_controls_program[] =
"G21 G90 G17\n"
"/N10 M428\n"
"/N20 G43.4 H7\n"
"M30\n";
events.clear();
ok &= expect(cnc_sim_load_config_json(sim, block_delete_on_config, sizeof(block_delete_on_config) - 1) == 0,
cnc_sim_last_error(sim));
ok &= expect(cnc_sim_parse_program(sim,
block_delete_rtcp_controls_program,
sizeof(block_delete_rtcp_controls_program) - 1) == 0,
cnc_sim_last_error(sim));
bool saw_block_delete_skipped_m428 = false;
bool saw_block_delete_skipped_g434 = false;
for (const auto &event : events) {
saw_block_delete_skipped_m428 = saw_block_delete_skipped_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 2);
saw_block_delete_skipped_g434 = saw_block_delete_skipped_g434 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 3);
}
ok &= expect(!saw_block_delete_skipped_m428, "expected LinuxCNC block-delete M428 line to be skipped");
ok &= expect(!saw_block_delete_skipped_g434, "expected LinuxCNC block-delete G43.4 line to be skipped");
events.clear();
ok &= expect(cnc_sim_load_config_json(sim, block_delete_off_config, sizeof(block_delete_off_config) - 1) == 0,
cnc_sim_last_error(sim));
ok &= expect(cnc_sim_parse_program(sim,
block_delete_rtcp_controls_program,
sizeof(block_delete_rtcp_controls_program) - 1) == 0,
cnc_sim_last_error(sim));
bool saw_block_delete_disabled_m428 = false;
bool saw_block_delete_disabled_g434 = false;
for (const auto &event : events) {
saw_block_delete_disabled_m428 = saw_block_delete_disabled_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 2 &&
event.reserved == 1);
saw_block_delete_disabled_g434 = saw_block_delete_disabled_g434 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 3 &&
event.tool == 7);
}
ok &= expect(saw_block_delete_disabled_m428, "expected LinuxCNC disabled block-delete M428 to execute");
ok &= expect(saw_block_delete_disabled_g434, "expected LinuxCNC disabled block-delete G43.4 to execute");
const char unary_sign_rtcp_controls_program[] =
"G21 G90 G17\n"
"M++428\n"
"G+43.4 H++7\n"
"M30\n";
events.clear();
ok &= expect(cnc_sim_load_config_json(sim, config, sizeof(config) - 1) == 0,
cnc_sim_last_error(sim));
ok &= expect(cnc_sim_parse_program(sim,
unary_sign_rtcp_controls_program,
sizeof(unary_sign_rtcp_controls_program) - 1) == 0,
cnc_sim_last_error(sim));
bool saw_unary_sign_m428 = false;
bool saw_unary_sign_g434 = false;
for (const auto &event : events) {
saw_unary_sign_m428 = saw_unary_sign_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 2 &&
event.reserved == 1);
saw_unary_sign_g434 = saw_unary_sign_g434 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 3 &&
event.tool == 7);
}
ok &= expect(saw_unary_sign_m428, "expected LinuxCNC source M++428 to execute as M428");
ok &= expect(saw_unary_sign_g434, "expected LinuxCNC source G+43.4 H++7 to execute as G43.4 H7");
const char numbered_rtcp_controls_program[] =
"G21 G90 G17\n"
"N10 M428\n"
"N20 G43.4 H7\n"
"M30\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
numbered_rtcp_controls_program,
sizeof(numbered_rtcp_controls_program) - 1) == 0,
cnc_sim_last_error(sim));
bool saw_numbered_m428 = false;
bool saw_numbered_g434 = false;
for (const auto &event : events) {
saw_numbered_m428 = saw_numbered_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 2 &&
event.reserved == 1);
saw_numbered_g434 = saw_numbered_g434 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 3 &&
event.tool == 7);
}
ok &= expect(saw_numbered_m428, "expected LinuxCNC source N10 M428 to execute as M428");
ok &= expect(saw_numbered_g434, "expected LinuxCNC source N20 G43.4 H7 to execute as G43.4 H7");
const char bracket_rtcp_controls_program[] =
"G21 G90 G17\n"
"M[428]\n"
"G[43.4] H[7]\n"
"M30\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
bracket_rtcp_controls_program,
sizeof(bracket_rtcp_controls_program) - 1) == 0,
cnc_sim_last_error(sim));
bool saw_bracket_m428 = false;
bool saw_bracket_g434 = false;
for (const auto &event : events) {
saw_bracket_m428 = saw_bracket_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 2 &&
event.reserved == 1);
saw_bracket_g434 = saw_bracket_g434 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 3 &&
event.tool == 7);
}
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 exponent_m428_control_program[] =
"G21 G90\n"
"M4.28e2\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
exponent_m428_control_program,
sizeof(exponent_m428_control_program) - 1) != 0,
"expected LinuxCNC source backend to reject exponent M428 control syntax");
const char exponent_g434_control_program[] =
"G21 G90\n"
"G4.34e1 H7\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
exponent_g434_control_program,
sizeof(exponent_g434_control_program) - 1) != 0,
"expected LinuxCNC source backend to reject exponent G43.4 control syntax");
const char exponent_h_control_program[] =
"G21 G90\n"
"G43.4 H7e0\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
exponent_h_control_program,
sizeof(exponent_h_control_program) - 1) != 0,
"expected LinuxCNC source backend to reject exponent H control syntax");
cnc_sim_destroy(sim);
int abort_count = 0;

View File

@@ -11,6 +11,12 @@
namespace {
constexpr int kToolLengthOffsetEvent = 430;
constexpr int kCssSpindleModeEvent = 960;
constexpr int kConstantRpmSpindleModeEvent = 970;
constexpr int kCutterCompOffEvent = 400;
constexpr int kCutterCompLeftExplicitEvent = 411;
int collect_event(const CncSimEvent *event, void *user_data) {
auto *events = static_cast<std::vector<CncSimEvent> *>(user_data);
events->push_back(*event);
@@ -114,6 +120,7 @@ int main() {
"M428 M429 M430\n"
"G49\n"
"G43.4 H7\n"
"G43.5 H7\n"
"S8000 M3\n"
"G0 X0 Y0 Z5\n"
"F600\n"
@@ -154,6 +161,7 @@ int main() {
bool saw_m430 = false;
bool saw_g49 = false;
bool saw_g434 = false;
bool saw_g435 = false;
bool saw_spindle_cw = false;
bool saw_spindle_orient = false;
bool saw_spindle_orient_wait = false;
@@ -379,23 +387,21 @@ int main() {
saw_plane = saw_plane || event.type == CNC_SIM_EVENT_SET_PLANE;
saw_rtcp = saw_rtcp ||
(event.type == CNC_SIM_EVENT_RTCP_PIVOT &&
event.line == 7 &&
event.end.z == 130.0);
event.line == 8 &&
event.reserved == 2 &&
event.end.z == 5.0);
saw_m428 = saw_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 3 &&
event.reserved == 1 &&
event.feed == 1.0);
event.reserved == 1);
saw_m429 = saw_m429 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 3 &&
event.reserved == 0 &&
event.feed == 0.0);
event.reserved == 0);
saw_m430 = saw_m430 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 3 &&
event.reserved == 2 &&
event.feed == 1.0);
event.reserved == 2);
saw_g49 = saw_g49 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 4 &&
@@ -406,21 +412,27 @@ int main() {
event.feed == 1.0 &&
event.tool == 7 &&
event.dwell_seconds == 125.0);
saw_g435 = saw_g435 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 6 &&
event.feed == 1.0 &&
event.tool == 7 &&
event.dwell_seconds == 125.0);
saw_spindle_cw = saw_spindle_cw ||
(event.type == CNC_SIM_EVENT_SET_SPINDLE &&
event.line == 6 &&
event.line == 7 &&
event.spindle == 8000.0 &&
event.reserved == 1);
saw_spindle_orient = saw_spindle_orient ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 15 &&
event.line == 16 &&
event.reserved == 1901 &&
event.tool == 0 &&
event.feed == 45.0 &&
event.arc_turns == 1);
saw_spindle_orient_wait = saw_spindle_orient_wait ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 15 &&
event.line == 16 &&
event.reserved == 1902 &&
event.tool == 0 &&
event.dwell_seconds == 2.0);
@@ -429,7 +441,7 @@ int main() {
event.start.x == 20.0 && event.end.x == 25.0);
saw_r_arc_center = saw_r_arc_center ||
(event.type == CNC_SIM_EVENT_ARC_FEED &&
event.line == 14 &&
event.line == 15 &&
(event.center.x != event.start.x || event.center.y != event.start.y));
}
ok &= expect(saw_rapid, "expected rapid event");
@@ -442,15 +454,97 @@ int main() {
ok &= expect(saw_rtcp, "expected RTCP pivot event from config");
ok &= expect(saw_m428, "expected M428 original kinematics switch");
ok &= expect(saw_m429, "expected M429 identity kinematics switch");
ok &= expect(saw_m430, "expected M430 five-axis BC kinematics switch");
ok &= expect(saw_m430, "expected M430 userk kinematics switch");
ok &= expect(saw_comment_state(events, 3, 68, 3, 1.0),
"expected M428 remap M68 E3 Q1 side effect");
ok &= expect(saw_comment_state(events, 3, 68, 3, 0.0),
"expected M429 remap M68 E3 Q0 side effect");
ok &= expect(saw_comment_state(events, 3, 68, 3, 2.0),
"expected M430 remap M68 E3 Q2 side effect");
ok &= expect(saw_comment_state(events, 3, 66, 0, 0.0),
"expected M428/M429/M430 remap M66 E0 L0 side effect");
ok &= expect(saw_g49, "expected G49 RTCP off state");
ok &= expect(saw_g434, "expected G43.4 RTCP on state with H code");
ok &= expect(saw_g435, "expected G43.5 RTCP on state with H code");
ok &= expect(saw_spindle_cw, "expected M3 clockwise spindle state");
ok &= expect(saw_spindle_orient, "expected M19 spindle orient state");
ok &= expect(saw_spindle_orient_wait, "expected M19 spindle orient wait state");
ok &= expect(saw_incremental_move, "expected G91 incremental move");
ok &= expect(saw_r_arc_center, "expected R arc center calculation");
{
std::vector<CncSimEvent> configured_events;
CncSimHandle *configured_sim = cnc_sim_create();
cnc_sim_set_event_callback(configured_sim, collect_event, &configured_events);
const char configured_rtcp[] =
"{\"backend\":\"smoke\",\"rtcp\":{\"enabled\":false,\"toolLength\":11},"
"\"xyzbcTrt\":{\"xRotPoint\":3,\"yRotPoint\":-4,\"zRotPoint\":5,"
"\"xOffset\":2,\"zOffset\":7,\"conventionalDirections\":true}}";
const char configured_program[] =
"M428\n"
"G43.4\n"
"G0 X30 Y40 Z50 B20 C-30\n";
ok &= expect(cnc_sim_load_config_json(configured_sim,
configured_rtcp,
sizeof(configured_rtcp) - 1) == 0,
cnc_sim_last_error(configured_sim));
ok &= expect(cnc_sim_parse_program(configured_sim,
configured_program,
sizeof(configured_program) - 1) == 0,
cnc_sim_last_error(configured_sim));
bool saw_configured_rtcp = false;
for (const auto &event : configured_events) {
saw_configured_rtcp = saw_configured_rtcp ||
(event.type == CNC_SIM_EVENT_RTCP_PIVOT &&
event.reserved == 1 &&
near(event.dwell_seconds, 11.0) &&
near(event.end.x, -4.814629) &&
near(event.end.y, 47.605118) &&
near(event.end.z, 48.160567) &&
near(event.end.b, 20.0) &&
near(event.end.c, -30.0));
}
ok &= expect(saw_configured_rtcp, "expected JSON-configured xyzbc-trt pins to affect M428 inverse");
cnc_sim_destroy(configured_sim);
}
{
std::vector<CncSimEvent> fiveaxis_events;
CncSimHandle *fiveaxis_sim = cnc_sim_create();
cnc_sim_set_event_callback(fiveaxis_sim, collect_event, &fiveaxis_events);
const char fiveaxis_config[] =
"{\"backend\":\"smoke\",\"rtcp\":{\"enabled\":true,\"toolLength\":250},"
"\"kinematics\":\"5axiskins\",\"pivotLength\":250}";
const char fiveaxis_program[] =
"M428\n"
"G0 X260 Y20 Z280 B90 C0\n";
ok &= expect(cnc_sim_load_config_json(fiveaxis_sim,
fiveaxis_config,
sizeof(fiveaxis_config) - 1) == 0,
cnc_sim_last_error(fiveaxis_sim));
ok &= expect(cnc_sim_parse_program(fiveaxis_sim,
fiveaxis_program,
sizeof(fiveaxis_program) - 1) == 0,
cnc_sim_last_error(fiveaxis_sim));
bool saw_fiveaxis_rtcp = false;
bool saw_fiveaxis_m428 = false;
for (const auto &event : fiveaxis_events) {
saw_fiveaxis_m428 = saw_fiveaxis_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.reserved == 0);
saw_fiveaxis_rtcp = saw_fiveaxis_rtcp ||
(event.type == CNC_SIM_EVENT_RTCP_PIVOT &&
event.reserved == 0 &&
near(event.end.x, 10.0) &&
near(event.end.y, 20.0) &&
near(event.end.z, 30.0) &&
near(event.end.b, 90.0));
}
ok &= expect(saw_fiveaxis_m428, "expected LinuxCNC bridgemill M428 to select 5axiskins type 0");
ok &= expect(saw_fiveaxis_rtcp, "expected 5axiskins inverse from LinuxCNC 5axiskins.c for M428");
cnc_sim_destroy(fiveaxis_sim);
}
const char io_controls_program[] =
"G21 G90 G17\n"
"M62 P1\n"
@@ -1282,19 +1376,19 @@ int main() {
saw_g431_tool_length = saw_g431_tool_length ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 2 &&
event.reserved == 430 &&
event.reserved == kToolLengthOffsetEvent &&
event.start.x == 1.0 &&
event.start.z == 2.0);
saw_g432_tool_length = saw_g432_tool_length ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 3 &&
event.reserved == 430 &&
event.reserved == kToolLengthOffsetEvent &&
event.start.x == 1.25 &&
event.start.z == 2.5);
saw_g49_tool_length_clear = saw_g49_tool_length_clear ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 5 &&
event.reserved == 430 &&
event.reserved == kToolLengthOffsetEvent &&
event.start.x == 0.0 &&
event.start.z == 0.0);
saw_g432_followup_comment = saw_g432_followup_comment ||
@@ -1304,7 +1398,7 @@ int main() {
saw_g432_h_tool_length = saw_g432_h_tool_length ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 4 &&
event.reserved == 430 &&
event.reserved == kToolLengthOffsetEvent &&
event.start.x == 1.25 &&
event.start.z == 15.0);
}
@@ -1428,12 +1522,12 @@ int main() {
saw_g43_h_tool_length = saw_g43_h_tool_length ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 2 &&
event.reserved == 430 &&
event.reserved == kToolLengthOffsetEvent &&
event.start.z == 12.5);
saw_g43_current_tool_length = saw_g43_current_tool_length ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 5 &&
event.reserved == 430 &&
event.reserved == kToolLengthOffsetEvent &&
event.start.z == 0.0);
}
ok &= expect(saw_g43_h_tool_length, "expected smoke G43 H1 tool-table offset");
@@ -1814,6 +1908,50 @@ int main() {
"D word with no G41, G41.1, G42, G42.1, G71, G71.1, G71.2 G73, G83 or G96 to use it",
"expected LinuxCNC modal-motion-D error text");
const char negative_cutter_comp_d_program[] =
"G21 G90 G17\n"
"G41 D-1\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
negative_cutter_comp_d_program,
sizeof(negative_cutter_comp_d_program) - 1) != 0,
"expected LinuxCNC G41 to reject a negative D tool index");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Negative d word tool radius index used",
"expected LinuxCNC negative cutter-comp D error text");
const char noninteger_cutter_comp_d_program[] =
"G21 G90 G17\n"
"G41 D1.5\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
noninteger_cutter_comp_d_program,
sizeof(noninteger_cutter_comp_d_program) - 1) != 0,
"expected LinuxCNC G41 to reject a non-integer D tool index");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "G41 requires D word to be a whole number",
"expected LinuxCNC non-integer cutter-comp D error text");
const char missing_explicit_cutter_comp_d_program[] =
"G21 G90 G17\n"
"G42.1\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
missing_explicit_cutter_comp_d_program,
sizeof(missing_explicit_cutter_comp_d_program) - 1) != 0,
"expected LinuxCNC G42.1 to require a D word");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "G42.1 with no D word",
"expected LinuxCNC missing explicit cutter-comp D error text");
const char explicit_cutter_comp_l_wrong_plane_program[] =
"G21 G90 G17\n"
"G41.1 D0.25 L1\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
explicit_cutter_comp_l_wrong_plane_program,
sizeof(explicit_cutter_comp_l_wrong_plane_program) - 1) != 0,
"expected LinuxCNC G41.1 L word to require G18");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "G41.1 with L word, but plane is not G18",
"expected LinuxCNC explicit cutter-comp L-plane error text");
const char cutter_comp_program[] =
"G21 G90 G17\n"
"G0 X0 Y0 Z0\n"
@@ -1828,8 +1966,12 @@ int main() {
bool saw_g41 = false;
bool saw_g40 = false;
for (const auto &event : events) {
saw_g41 = saw_g41 || (event.type == CNC_SIM_EVENT_COMMENT && event.reserved == 411);
saw_g40 = saw_g40 || (event.type == CNC_SIM_EVENT_COMMENT && event.reserved == 400);
saw_g41 = saw_g41 ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.reserved == kCutterCompLeftExplicitEvent);
saw_g40 = saw_g40 ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.reserved == kCutterCompOffEvent);
}
ok &= expect(saw_g41, "expected G41.1 cutter compensation state event");
ok &= expect(saw_g40, "expected G40 cutter compensation clear event");
@@ -2095,12 +2237,12 @@ int main() {
saw_g97_mode = saw_g97_mode ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 2 &&
event.reserved == 970 &&
event.reserved == kConstantRpmSpindleModeEvent &&
event.feed == 0.0);
saw_g96_mode = saw_g96_mode ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 3 &&
event.reserved == 960 &&
event.reserved == kCssSpindleModeEvent &&
event.feed == 2500.0);
}
ok &= expect(saw_g97_mode, "expected G97 constant-RPM spindle mode state");
@@ -3787,6 +3929,18 @@ int main() {
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Cannot do zero repeats of cycle",
cnc_sim_last_error(sim));
const char noninteger_cycle_l_program[] =
"G21 G90 G17\n"
"F100\n"
"G81 X1 Z-1 R1 L1.5\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
noninteger_cycle_l_program,
sizeof(noninteger_cycle_l_program) - 1) != 0,
"expected noninteger canned cycle L word to fail");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Non integer value for integer",
cnc_sim_last_error(sim));
const char missing_cycle_feed_program[] =
"G21 G90 G17\n"
"G81 X1 Z-1 R1\n";
@@ -3875,6 +4029,16 @@ int main() {
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Negative or zero q value used",
cnc_sim_last_error(sim));
const char negative_then_positive_g83_q_program[] =
"G21 G90 G17\n"
"F100\n"
"G83 X1 Z-1 R1 Q-2 Q0.5\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
negative_then_positive_g83_q_program,
sizeof(negative_then_positive_g83_q_program) - 1) == 0,
cnc_sim_last_error(sim));
const char cross_cycle_q_program[] =
"G21 G90 G17\n"
"G73 X1 Z-1 R1 Q0.5\n"
@@ -5904,6 +6068,86 @@ int main() {
sizeof(readonly_numbered_parameter_assignment_program) - 1) != 0,
"expected LinuxCNC to reject assignment to read-only numbered parameter");
const char cutter_comp_read_current_position_program[] =
"G21 G90 G17\n"
"G41.1 D1\n"
"#1 = #5420\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
cutter_comp_read_current_position_program,
sizeof(cutter_comp_read_current_position_program) - 1) != 0,
"expected LinuxCNC to reject reading #5420 with cutter compensation on");
ok &= expect(std::string(cnc_sim_last_error(sim)) ==
"Cannot read current position with cutter radius compensation on",
"expected LinuxCNC cutter-comp current-position read error text");
const char cutter_comp_read_indexed_current_position_program[] =
"G21 G90 G17\n"
"G41.1 D1\n"
"#1 = #[5420]\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
cutter_comp_read_indexed_current_position_program,
sizeof(cutter_comp_read_indexed_current_position_program) - 1) != 0,
"expected LinuxCNC to reject reading #[5420] with cutter compensation on");
ok &= expect(std::string(cnc_sim_last_error(sim)) ==
"Cannot read current position with cutter radius compensation on",
"expected LinuxCNC cutter-comp indexed current-position read error text");
const char cutter_comp_read_spaced_indexed_current_position_program[] =
"G21 G90 G17\n"
"G41.1 D1\n"
"G1 X# [5420]\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
cutter_comp_read_spaced_indexed_current_position_program,
sizeof(cutter_comp_read_spaced_indexed_current_position_program) - 1) != 0,
"expected LinuxCNC to reject reading # [5420] with cutter compensation on");
ok &= expect(std::string(cnc_sim_last_error(sim)) ==
"Cannot read current position with cutter radius compensation on",
"expected LinuxCNC cutter-comp spaced indexed current-position read error text");
const char cutter_comp_read_expression_indexed_current_position_program[] =
"G21 G90 G17\n"
"G41.1 D1\n"
"#1 = #[5400 + 20]\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
cutter_comp_read_expression_indexed_current_position_program,
sizeof(cutter_comp_read_expression_indexed_current_position_program) - 1) != 0,
"expected LinuxCNC to reject reading #[5400 + 20] with cutter compensation on");
ok &= expect(std::string(cnc_sim_last_error(sim)) ==
"Cannot read current position with cutter radius compensation on",
"expected LinuxCNC cutter-comp expression-indexed current-position read error text");
const char cutter_comp_read_parameter_indexed_current_position_program[] =
"G21 G90 G17\n"
"#1 = 5420\n"
"G41.1 D1\n"
"#2 = #[#1]\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
cutter_comp_read_parameter_indexed_current_position_program,
sizeof(cutter_comp_read_parameter_indexed_current_position_program) - 1) != 0,
"expected LinuxCNC to reject reading #[#1] with cutter compensation on");
ok &= expect(std::string(cnc_sim_last_error(sim)) ==
"Cannot read current position with cutter radius compensation on",
"expected LinuxCNC cutter-comp parameter-indexed current-position read error text");
const char cutter_comp_read_named_indexed_current_position_program[] =
"G21 G90 G17\n"
"#<idx> = 5420\n"
"G41.1 D1\n"
"#2 = #[#<idx>]\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
cutter_comp_read_named_indexed_current_position_program,
sizeof(cutter_comp_read_named_indexed_current_position_program) - 1) != 0,
"expected LinuxCNC to reject reading #[#<idx>] with cutter compensation on");
ok &= expect(std::string(cnc_sim_last_error(sim)) ==
"Cannot read current position with cutter radius compensation on",
"expected LinuxCNC cutter-comp named-indexed current-position read error text");
const char arc_center_tolerance_good_imperial_program[] =
"G20\n"
"F1\n"
@@ -6378,6 +6622,98 @@ int main() {
ok &= expect(std::string(cnc_sim_last_error(sim)) == "bad number format (conversion failed) parsing '1.2.3'",
"expected LinuxCNC malformed-real-value error text");
const char unclosed_word_expression_program[] =
"G21 G90\n"
"F100\n"
"G1 X[1 + 2\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
unclosed_word_expression_program,
sizeof(unclosed_word_expression_program) - 1) != 0,
"expected LinuxCNC to reject an unclosed word expression");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Unclosed expression",
"expected LinuxCNC unclosed-word-expression error text");
const char unclosed_parameter_assignment_expression_program[] =
"#1 = [1 + 2\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
unclosed_parameter_assignment_expression_program,
sizeof(unclosed_parameter_assignment_expression_program) - 1) != 0,
"expected LinuxCNC to reject an unclosed parameter assignment expression");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Unclosed expression",
"expected LinuxCNC unclosed-parameter-assignment-expression error text");
const char unclosed_indexed_parameter_reference_program[] =
"G21 G90\n"
"F100\n"
"G1 X#[1 + 2\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
unclosed_indexed_parameter_reference_program,
sizeof(unclosed_indexed_parameter_reference_program) - 1) != 0,
"expected LinuxCNC to reject an unclosed indexed parameter reference");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Unclosed expression",
"expected LinuxCNC unclosed-indexed-parameter-reference error text");
const char unclosed_indexed_parameter_assignment_program[] =
"#[1 + 2 = 3\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
unclosed_indexed_parameter_assignment_program,
sizeof(unclosed_indexed_parameter_assignment_program) - 1) != 0,
"expected LinuxCNC to reject an unclosed indexed parameter assignment");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Unclosed expression",
"expected LinuxCNC unclosed-indexed-parameter-assignment error text");
const char unclosed_indirect_parameter_reference_program[] =
"#1 = 3\n"
"G21 G90\n"
"F100\n"
"G1 X##[1 + 2\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
unclosed_indirect_parameter_reference_program,
sizeof(unclosed_indirect_parameter_reference_program) - 1) != 0,
"expected LinuxCNC to reject an unclosed indirect parameter reference");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Unclosed expression",
"expected LinuxCNC unclosed-indirect-parameter-reference error text");
const char unterminated_named_parameter_reference_program[] =
"G21 G90\n"
"F100\n"
"G1 X#<missing\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
unterminated_named_parameter_reference_program,
sizeof(unterminated_named_parameter_reference_program) - 1) != 0,
"expected LinuxCNC to reject an unterminated named parameter reference");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Named parameter not terminated",
"expected LinuxCNC unterminated-named-parameter-reference error text");
const char unterminated_indirect_named_parameter_reference_program[] =
"#1 = 3\n"
"G21 G90\n"
"F100\n"
"G1 X##<missing\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
unterminated_indirect_named_parameter_reference_program,
sizeof(unterminated_indirect_named_parameter_reference_program) - 1) != 0,
"expected LinuxCNC to reject an unterminated indirect named parameter reference");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Named parameter not terminated",
"expected LinuxCNC unterminated-indirect-named-parameter-reference error text");
const char unterminated_named_parameter_assignment_program[] =
"#<missing = 3\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
unterminated_named_parameter_assignment_program,
sizeof(unterminated_named_parameter_assignment_program) - 1) != 0,
"expected LinuxCNC to reject an unterminated named parameter assignment");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Named parameter not terminated",
"expected LinuxCNC unterminated-named-parameter-assignment error text");
const char unused_p_word_program[] =
"G21 G90\n"
"F100\n"
@@ -6398,6 +6734,115 @@ int main() {
sizeof(invalid_arc_p_word_program) - 1) != 0,
"expected LinuxCNC to reject P value less than 1 with G2/G3");
const char absolute_center_arc_missing_axis_program[] =
"G21 G90 G17\n"
"F100\n"
"G90.1\n"
"G2 X1 Y0 I0.5\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
absolute_center_arc_missing_axis_program,
sizeof(absolute_center_arc_missing_axis_program) - 1) != 0,
"expected LinuxCNC absolute-center arc to reject a missing center axis");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "J word missing in absolute center arc",
"expected LinuxCNC missing absolute-center arc axis error text");
const char mixed_radius_ijk_arc_program[] =
"G21 G90 G17\n"
"F100\n"
"G2 X1 Y0 R1 I0.5 J0\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
mixed_radius_ijk_arc_program,
sizeof(mixed_radius_ijk_arc_program) - 1) != 0,
"expected LinuxCNC to reject mixed R and IJK arc format");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Mixed radius ijk format for arc",
"expected LinuxCNC mixed R/IJK arc error text");
const char missing_arc_format_words_program[] =
"G21 G90 G17\n"
"F100\n"
"G2 X1 Y0\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
missing_arc_format_words_program,
sizeof(missing_arc_format_words_program) - 1) != 0,
"expected LinuxCNC to reject arc with no R/I/J/K words");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "R i j k words all missing for arc",
"expected LinuxCNC missing R/I/J/K arc error text");
const char xy_arc_with_k_program[] =
"G21 G90 G17\n"
"F100\n"
"G2 X1 Y0 I0.5 J0 K0\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
xy_arc_with_k_program,
sizeof(xy_arc_with_k_program) - 1) != 0,
"expected LinuxCNC to reject K word in XY-plane arc");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "K word given for arc in xy plane",
"expected LinuxCNC XY-plane arc K-word error text");
const char xz_arc_with_j_program[] =
"G21 G90 G18\n"
"F100\n"
"G2 X1 Z0 I0.5 K0 J0\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
xz_arc_with_j_program,
sizeof(xz_arc_with_j_program) - 1) != 0,
"expected LinuxCNC to reject J word in XZ-plane arc");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "J word given for arc in xz plane",
"expected LinuxCNC XZ-plane arc J-word error text");
const char yz_arc_with_i_program[] =
"G21 G90 G19\n"
"F100\n"
"G2 Y1 Z0 J0.5 K0 I0\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
yz_arc_with_i_program,
sizeof(yz_arc_with_i_program) - 1) != 0,
"expected LinuxCNC to reject I word in YZ-plane arc");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "I word given for arc in yz plane",
"expected LinuxCNC YZ-plane arc I-word error text");
const char xy_r_arc_missing_endpoint_words_program[] =
"G21 G90 G17\n"
"F100\n"
"G2 R1\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
xy_r_arc_missing_endpoint_words_program,
sizeof(xy_r_arc_missing_endpoint_words_program) - 1) != 0,
"expected LinuxCNC to reject XY R-format arc with no endpoint words");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "X and y words missing for arc in xy plane",
"expected LinuxCNC XY R-format missing endpoint error text");
const char xz_r_arc_missing_endpoint_words_program[] =
"G21 G90 G18\n"
"F100\n"
"G2 R1\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
xz_r_arc_missing_endpoint_words_program,
sizeof(xz_r_arc_missing_endpoint_words_program) - 1) != 0,
"expected LinuxCNC to reject XZ R-format arc with no endpoint words");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "X and z words missing for arc in xz plane",
"expected LinuxCNC XZ R-format missing endpoint error text");
const char yz_r_arc_missing_endpoint_words_program[] =
"G21 G90 G19\n"
"F100\n"
"G2 R1\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
yz_r_arc_missing_endpoint_words_program,
sizeof(yz_r_arc_missing_endpoint_words_program) - 1) != 0,
"expected LinuxCNC to reject YZ R-format arc with no endpoint words");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Y and z words missing for arc in yz plane",
"expected LinuxCNC YZ R-format missing endpoint error text");
const char r_arc_too_small_program[] =
"G21 G90 G17\n"
"F100\n"
@@ -6628,6 +7073,8 @@ int main() {
g10_l0_loaded_tool_program,
sizeof(g10_l0_loaded_tool_program) - 1) != 0,
"expected LinuxCNC to reject G10 L0 with a loaded tool");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "G10 L0 not allowed with loaded tool <1>",
"expected LinuxCNC G10 L0 loaded-tool error text");
}
const char out_of_range_g10_l1_p_word_program[] =
@@ -7104,6 +7551,24 @@ int main() {
ok &= expect(saw_near_negative_one_g4_dwell,
"expected LinuxCNC to execute G4 with P just above -1");
const char negative_then_positive_g4_p_word_program[] =
"G21 G90\n"
"G4 P-2 P3\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
negative_then_positive_g4_p_word_program,
sizeof(negative_then_positive_g4_p_word_program) - 1) == 0,
cnc_sim_last_error(sim));
bool saw_overridden_g4_dwell = false;
for (const auto &event : events) {
saw_overridden_g4_dwell = saw_overridden_g4_dwell ||
(event.type == CNC_SIM_EVENT_DWELL &&
event.line == 2 &&
near(event.dwell_seconds, 3.0));
}
ok &= expect(saw_overridden_g4_dwell,
"expected LinuxCNC P sentinel duplicate rule to keep the later G4 P value");
const char g4_with_arc_program[] =
"G21 G90 G17\n"
"F100\n"
@@ -7513,6 +7978,18 @@ int main() {
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Multiple x words on one line",
"expected LinuxCNC duplicate-X error text");
const char duplicate_u_word_program[] =
"G21 G90\n"
"F100\n"
"G1 U1 U2\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
duplicate_u_word_program,
sizeof(duplicate_u_word_program) - 1) != 0,
"expected LinuxCNC to reject multiple U words on one line");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Multiple U words on one line",
"expected LinuxCNC duplicate-U error text");
const char duplicate_spindle_selector_program[] =
"G21 G90\n"
"M3 $0 $1\n";
@@ -7767,6 +8244,33 @@ int main() {
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Unknown g code used",
"expected mixed G43.4 motion to use LinuxCNC unknown-G error text");
const char exponent_m428_control_program[] =
"G21 G90\n"
"M4.28e2\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
exponent_m428_control_program,
sizeof(exponent_m428_control_program) - 1) != 0,
"expected LinuxCNC-style M parsing to reject exponent M428 control syntax");
const char exponent_g434_control_program[] =
"G21 G90\n"
"G4.34e1 H7\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
exponent_g434_control_program,
sizeof(exponent_g434_control_program) - 1) != 0,
"expected LinuxCNC-style G parsing to reject exponent G43.4 control syntax");
const char exponent_h_control_program[] =
"G21 G90\n"
"G43.4 H7e0\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
exponent_h_control_program,
sizeof(exponent_h_control_program) - 1) != 0,
"expected LinuxCNC-style H parsing to reject exponent H control syntax");
const char invalid_character_program[] =
"G21 G90\n"
"?\n";
@@ -7902,6 +8406,42 @@ int main() {
ok &= expect(saw_spindle_all_cw, "expected LinuxCNC M3 $-1 spindle state");
ok &= expect(saw_spindle_all_stop, "expected LinuxCNC M5 $-1 spindle state");
const char m3_dollar_word_out_of_range_program[] =
"G21\n"
"M3 $1\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
m3_dollar_word_out_of_range_program,
sizeof(m3_dollar_word_out_of_range_program) - 1) != 0,
"expected LinuxCNC to reject out-of-range M3 $ spindle selector");
ok &= expect(std::string(cnc_sim_last_error(sim)) ==
"Spindle ($) number out of range in M3 Command\nnum_spindles =1. $=1\n",
"expected LinuxCNC out-of-range M3 $ spindle error text");
const char m4_dollar_word_out_of_range_program[] =
"G21\n"
"M4 $1\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
m4_dollar_word_out_of_range_program,
sizeof(m4_dollar_word_out_of_range_program) - 1) != 0,
"expected LinuxCNC to reject out-of-range M4 $ spindle selector");
ok &= expect(std::string(cnc_sim_last_error(sim)) ==
"Spindle ($) number out of range in M4 Command\nnum_spindles =1. $=1\n",
"expected LinuxCNC out-of-range M4 $ spindle error text");
const char m5_dollar_word_out_of_range_program[] =
"G21\n"
"M5 $1\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
m5_dollar_word_out_of_range_program,
sizeof(m5_dollar_word_out_of_range_program) - 1) != 0,
"expected LinuxCNC to reject out-of-range M5 $ spindle selector");
ok &= expect(std::string(cnc_sim_last_error(sim)) ==
"Spindle ($) number out of range in M5 Command\nnum_spindles =1. $=1\n",
"expected LinuxCNC out-of-range M5 $ spindle error text");
const char spindle_mode_dollar_program[] =
"G21\n"
"G97 S1000 $0\n"
@@ -7919,12 +8459,12 @@ int main() {
saw_g97_dollar_mode = saw_g97_dollar_mode ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 2 &&
event.reserved == 970 &&
event.reserved == kConstantRpmSpindleModeEvent &&
event.tool == 0);
saw_g96_dollar_mode = saw_g96_dollar_mode ||
(event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 3 &&
event.reserved == 960 &&
event.reserved == kCssSpindleModeEvent &&
event.tool == 0 &&
near(event.feed, 2500.0));
saw_g95_dollar_feed_mode = saw_g95_dollar_feed_mode ||
@@ -8487,6 +9027,28 @@ int main() {
sizeof(polar_g92_program) - 1) != 0,
"expected LinuxCNC to reject polar coordinates with G92");
const char polar_g52_program[] =
"G21 G90 G17\n"
"G52 @1 ^90\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
polar_g52_program,
sizeof(polar_g52_program) - 1) != 0,
"expected LinuxCNC to reject polar coordinates with G52");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "All axes missing with g52 or g92",
"expected LinuxCNC G52 polar missing-axis error text");
const char polar_g52_with_x_program[] =
"G21 G90 G17\n"
"G52 X1 @1 ^90\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
polar_g52_with_x_program,
sizeof(polar_g52_with_x_program) - 1) != 0,
"expected LinuxCNC to reject X word mixed with polar coordinate in G52");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "Cannot specify both polar coordinate and X word",
"expected LinuxCNC G52 polar/X conflict error text");
const char polar_g10_program[] =
"G21 G90 G17\n"
"G10 L2 P1 @1 ^90\n";
@@ -8667,6 +9229,18 @@ int main() {
sizeof(bad_precision_g_code_program) - 1) != 0,
"expected LinuxCNC to reject G code not on a tenth boundary");
const char boundary_noninteger_g_code_program[] =
"G21 G90\n"
"F100\n"
"G1.9999 X1\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
boundary_noninteger_g_code_program,
sizeof(boundary_noninteger_g_code_program) - 1) != 0,
"expected LinuxCNC to reject G code exactly at the read_g upper tolerance boundary");
ok &= expect(std::string(cnc_sim_last_error(sim)) == "G-code out of range",
"expected LinuxCNC boundary-G error text");
const char scientific_notation_word_program[] =
"G21 G90\n"
"F100\n"
@@ -9819,6 +10393,130 @@ int main() {
}
ok &= expect(saw_block_delete_disabled_line, "expected smoke disabled block-delete line to execute");
const char block_delete_rtcp_controls_program[] =
"G21 G90 G17\n"
"/N10 M428\n"
"/N20 G43.4 H7\n"
"M30\n";
config_rc = cnc_sim_load_config_json(sim, block_delete_on_config, sizeof(block_delete_on_config) - 1);
events.clear();
ok &= expect(config_rc == 0, cnc_sim_last_error(sim));
ok &= expect(cnc_sim_parse_program(sim,
block_delete_rtcp_controls_program,
sizeof(block_delete_rtcp_controls_program) - 1) == 0,
cnc_sim_last_error(sim));
bool saw_smoke_block_delete_skipped_m428 = false;
bool saw_smoke_block_delete_skipped_g434 = false;
for (const auto &event : events) {
saw_smoke_block_delete_skipped_m428 = saw_smoke_block_delete_skipped_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 2);
saw_smoke_block_delete_skipped_g434 = saw_smoke_block_delete_skipped_g434 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 3);
}
ok &= expect(!saw_smoke_block_delete_skipped_m428, "expected smoke block-delete M428 line to be skipped");
ok &= expect(!saw_smoke_block_delete_skipped_g434, "expected smoke block-delete G43.4 line to be skipped");
config_rc = cnc_sim_load_config_json(sim, block_delete_off_config, sizeof(block_delete_off_config) - 1);
events.clear();
ok &= expect(config_rc == 0, cnc_sim_last_error(sim));
ok &= expect(cnc_sim_parse_program(sim,
block_delete_rtcp_controls_program,
sizeof(block_delete_rtcp_controls_program) - 1) == 0,
cnc_sim_last_error(sim));
bool saw_smoke_block_delete_disabled_m428 = false;
bool saw_smoke_block_delete_disabled_g434 = false;
for (const auto &event : events) {
saw_smoke_block_delete_disabled_m428 = saw_smoke_block_delete_disabled_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 2 &&
event.reserved == 1);
saw_smoke_block_delete_disabled_g434 = saw_smoke_block_delete_disabled_g434 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 3 &&
event.tool == 7);
}
ok &= expect(saw_smoke_block_delete_disabled_m428, "expected smoke disabled block-delete M428 to execute");
ok &= expect(saw_smoke_block_delete_disabled_g434, "expected smoke disabled block-delete G43.4 to execute");
const char unary_sign_rtcp_controls_program[] =
"G21 G90 G17\n"
"M++428\n"
"G+43.4 H++7\n"
"M30\n";
config_rc = cnc_sim_load_config_json(sim, smoke_config, sizeof(smoke_config) - 1);
events.clear();
ok &= expect(config_rc == 0, cnc_sim_last_error(sim));
ok &= expect(cnc_sim_parse_program(sim,
unary_sign_rtcp_controls_program,
sizeof(unary_sign_rtcp_controls_program) - 1) == 0,
cnc_sim_last_error(sim));
bool saw_smoke_unary_sign_m428 = false;
bool saw_smoke_unary_sign_g434 = false;
for (const auto &event : events) {
saw_smoke_unary_sign_m428 = saw_smoke_unary_sign_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 2 &&
event.reserved == 1);
saw_smoke_unary_sign_g434 = saw_smoke_unary_sign_g434 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 3 &&
event.tool == 7);
}
ok &= expect(saw_smoke_unary_sign_m428, "expected smoke M++428 to execute as M428");
ok &= expect(saw_smoke_unary_sign_g434, "expected smoke G+43.4 H++7 to execute as G43.4 H7");
const char numbered_rtcp_controls_program[] =
"G21 G90 G17\n"
"N10 M428\n"
"N20 G43.4 H7\n"
"M30\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
numbered_rtcp_controls_program,
sizeof(numbered_rtcp_controls_program) - 1) == 0,
cnc_sim_last_error(sim));
bool saw_smoke_numbered_m428 = false;
bool saw_smoke_numbered_g434 = false;
for (const auto &event : events) {
saw_smoke_numbered_m428 = saw_smoke_numbered_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 2 &&
event.reserved == 1);
saw_smoke_numbered_g434 = saw_smoke_numbered_g434 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 3 &&
event.tool == 7);
}
ok &= expect(saw_smoke_numbered_m428, "expected smoke N10 M428 to execute as M428");
ok &= expect(saw_smoke_numbered_g434, "expected smoke N20 G43.4 H7 to execute as G43.4 H7");
const char bracket_rtcp_controls_program[] =
"G21 G90 G17\n"
"M[428]\n"
"G[43.4] H[7]\n"
"M30\n";
events.clear();
ok &= expect(cnc_sim_parse_program(sim,
bracket_rtcp_controls_program,
sizeof(bracket_rtcp_controls_program) - 1) == 0,
cnc_sim_last_error(sim));
bool saw_smoke_bracket_m428 = false;
bool saw_smoke_bracket_g434 = false;
for (const auto &event : events) {
saw_smoke_bracket_m428 = saw_smoke_bracket_m428 ||
(event.type == CNC_SIM_EVENT_KINEMATICS_SWITCH &&
event.line == 2 &&
event.reserved == 1);
saw_smoke_bracket_g434 = saw_smoke_bracket_g434 ||
(event.type == CNC_SIM_EVENT_RTCP_STATE &&
event.line == 3 &&
event.tool == 7);
}
ok &= expect(saw_smoke_bracket_m428, "expected smoke M[428] to execute as M428");
ok &= expect(saw_smoke_bracket_g434, "expected smoke G[43.4] H[7] to execute as G43.4 H7");
const char comment_then_slash_program[] =
"G21 G90 F100\n"
"(comment) /G1 X5\n"

View File

@@ -8,6 +8,9 @@
namespace {
constexpr int kToolLengthOffsetEvent = 430;
constexpr int kCssSpindleModeEvent = 960;
int collect_event(const CncSimEvent *event, void *user_data) {
auto *events = static_cast<std::vector<CncSimEvent> *>(user_data);
events->push_back(*event);
@@ -189,7 +192,7 @@ int main() {
event.spindle == 0.0 &&
event.reserved == 0);
saw_spindle_mode = saw_spindle_mode || (event.type == CNC_SIM_EVENT_COMMENT &&
event.reserved == 960 &&
event.reserved == kCssSpindleModeEvent &&
event.feed == 2500.0 &&
event.tool == 0);
saw_feed_mode = saw_feed_mode || (event.type == CNC_SIM_EVENT_SET_FEED &&
@@ -320,7 +323,7 @@ int main() {
event.feed == 15.0);
saw_tool_length = saw_tool_length || (event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 19 &&
event.reserved == 430 &&
event.reserved == kToolLengthOffsetEvent &&
event.start.z == 12.5);
saw_comment = saw_comment || (event.type == CNC_SIM_EVENT_COMMENT &&
event.line == 20);

View File

@@ -105,6 +105,15 @@ int parse_named_integer_constant(const std::string &source, const std::string &n
return std::stoi(match[1].str());
}
int parse_named_enum_value(const std::string &source, const std::string &name) {
const std::regex pattern(R"(\b)" + name + R"(\s*=\s*(-?\d+))");
std::smatch match;
if (!std::regex_search(source, match, pattern)) {
return -1;
}
return std::stoi(match[1].str());
}
int parse_constexpr_integer(const std::string &source, const std::string &name) {
const std::regex pattern(R"(constexpr\s+int\s+)" + name + R"(\s*=\s*(\d+))");
std::smatch match;
@@ -822,6 +831,8 @@ int main() {
const std::string interp_internal_source = read_file(interp_internal_path);
const std::string parameter_def_source = read_file(parameter_def_path);
const std::string linuxcnc_h_source = read_file(linuxcnc_h_path);
const std::string canon_event_sink_source = read_file("core/src/canon_event_sink.cpp");
const std::string linuxcnc_canon_bridge_source = read_file("core/src/linuxcnc_canon_bridge.cpp");
const std::string smoke_source = read_file("core/src/smoke_gcode_parser.cpp");
bool ok = true;
@@ -832,6 +843,8 @@ int main() {
ok &= expect(!interp_internal_source.empty(), "expected to read LinuxCNC interp_internal.hh");
ok &= expect(!parameter_def_source.empty(), "expected to read LinuxCNC interp_parameter_def.hh");
ok &= expect(!linuxcnc_h_source.empty(), "expected to read LinuxCNC linuxcnc.h");
ok &= expect(!canon_event_sink_source.empty(), "expected to read canon_event_sink.cpp");
ok &= expect(!linuxcnc_canon_bridge_source.empty(), "expected to read linuxcnc_canon_bridge.cpp");
ok &= expect(!smoke_source.empty(), "expected to read smoke_gcode_parser.cpp");
const int linuxcnc_max_parameters =
@@ -1034,6 +1047,56 @@ int main() {
ok &= expect(smoke_min_analog_output_index == linuxcnc_min_analog_output_index,
"expected smoke M67/M68 minimum analog output index to match LinuxCNC");
const int linuxcnc_cutter_comp_off = parse_named_enum_value(interp_internal_source, "G_40");
const int linuxcnc_cutter_comp_left = parse_named_enum_value(interp_internal_source, "G_41");
const int linuxcnc_cutter_comp_left_explicit = parse_named_enum_value(interp_internal_source, "G_41_1");
const int linuxcnc_cutter_comp_right = parse_named_enum_value(interp_internal_source, "G_42");
const int linuxcnc_cutter_comp_right_explicit = parse_named_enum_value(interp_internal_source, "G_42_1");
ok &= expect(parse_constexpr_integer(smoke_source, "kCutterCompOffEvent") == linuxcnc_cutter_comp_off,
"expected smoke cutter-comp off event to match LinuxCNC G40 enum");
ok &= expect(parse_constexpr_integer(smoke_source, "kCutterCompLeftEvent") == linuxcnc_cutter_comp_left,
"expected smoke cutter-comp left event to match LinuxCNC G41 enum");
ok &= expect(parse_constexpr_integer(smoke_source, "kCutterCompLeftExplicitEvent") ==
linuxcnc_cutter_comp_left_explicit,
"expected smoke explicit cutter-comp left event to match LinuxCNC G41.1 enum");
ok &= expect(parse_constexpr_integer(smoke_source, "kCutterCompRightEvent") == linuxcnc_cutter_comp_right,
"expected smoke cutter-comp right event to match LinuxCNC G42 enum");
ok &= expect(parse_constexpr_integer(smoke_source, "kCutterCompRightExplicitEvent") ==
linuxcnc_cutter_comp_right_explicit,
"expected smoke explicit cutter-comp right event to match LinuxCNC G42.1 enum");
const int linuxcnc_tool_length_offset = parse_named_enum_value(interp_internal_source, "G_43");
const int linuxcnc_dynamic_tool_length_offset =
parse_named_enum_value(interp_internal_source, "G_43_1");
const int linuxcnc_additive_tool_length_offset =
parse_named_enum_value(interp_internal_source, "G_43_2");
const int linuxcnc_cancel_tool_length_offset =
parse_named_enum_value(interp_internal_source, "G_49");
ok &= expect(parse_constexpr_integer(smoke_source, "kToolLengthOffsetEvent") ==
linuxcnc_tool_length_offset,
"expected smoke tool-length offset event to match LinuxCNC G43 enum");
ok &= expect(parse_constexpr_integer(linuxcnc_canon_bridge_source, "kToolLengthOffsetEvent") ==
linuxcnc_tool_length_offset,
"expected LinuxCNC bridge tool-length offset event to match LinuxCNC G43 enum");
ok &= expect(linuxcnc_dynamic_tool_length_offset == 431,
"expected LinuxCNC G43.1 enum to remain dynamic tool-length offset");
ok &= expect(linuxcnc_additive_tool_length_offset == 432,
"expected LinuxCNC G43.2 enum to remain additive tool-length offset");
ok &= expect(linuxcnc_cancel_tool_length_offset == 490,
"expected LinuxCNC G49 enum to remain cancel tool-length offset");
const int linuxcnc_css_spindle_mode = parse_named_enum_value(interp_internal_source, "G_96");
const int linuxcnc_constant_rpm_spindle_mode = parse_named_enum_value(interp_internal_source, "G_97");
ok &= expect(parse_constexpr_integer(canon_event_sink_source, "kCssSpindleModeEvent") ==
linuxcnc_css_spindle_mode,
"expected canon sink CSS spindle mode event to match LinuxCNC G96 enum");
ok &= expect(parse_constexpr_integer(canon_event_sink_source, "kConstantRpmSpindleModeEvent") ==
linuxcnc_constant_rpm_spindle_mode,
"expected canon sink constant-RPM spindle mode event to match LinuxCNC G97 enum");
ok &= expect(parse_constexpr_integer(smoke_source, "kConstantRpmSpindleModeEvent") ==
linuxcnc_constant_rpm_spindle_mode,
"expected smoke constant-RPM spindle mode event to match LinuxCNC G97 enum");
const std::vector<double> linuxcnc_g_code_read_real_constants =
parse_g_code_read_real_constants(interp_read_source);
const double smoke_g_code_scale = parse_constexpr_double(smoke_source, "kLinuxCncGCodeScale");
@@ -1184,11 +1247,21 @@ int main() {
"expected complete smoke G modal table");
ok &= expect(smoke_max_g_code == static_cast<int>(gees.size()) - 1,
"expected smoke max G code to match LinuxCNC gees[] table");
if (gees.size() > 490) {
ok &= expect(gees[430] == 8, "expected LinuxCNC G43 to remain tool-length modal group 8");
if (gees.size() > static_cast<std::size_t>(linuxcnc_cancel_tool_length_offset) &&
linuxcnc_tool_length_offset >= 0 &&
linuxcnc_dynamic_tool_length_offset >= 0 &&
linuxcnc_additive_tool_length_offset >= 0 &&
linuxcnc_cancel_tool_length_offset >= 0) {
ok &= expect(gees[linuxcnc_tool_length_offset] == 8,
"expected LinuxCNC G43 to remain tool-length modal group 8");
ok &= expect(gees[linuxcnc_dynamic_tool_length_offset] == 8,
"expected LinuxCNC G43.1 to remain tool-length modal group 8");
ok &= expect(gees[linuxcnc_additive_tool_length_offset] == 8,
"expected LinuxCNC G43.2 to remain tool-length modal group 8");
ok &= expect(gees[434] == -1, "expected LinuxCNC G43.4 to stay out of gees[]");
ok &= expect(gees[435] == -1, "expected LinuxCNC G43.5 to stay out of gees[]");
ok &= expect(gees[490] == 8, "expected LinuxCNC G49 to remain tool-length modal group 8");
ok &= expect(gees[linuxcnc_cancel_tool_length_offset] == 8,
"expected LinuxCNC G49 to remain tool-length modal group 8");
}
if (gees.size() == smoke_g.size()) {
ok &= expect_tables_equal(smoke_g, gees, "G modal group");

View File

@@ -17,7 +17,39 @@ bool expect_near(double actual, double expected, const char *message) {
return true;
}
bool expect_pose_near(const CncSimPose &actual, const CncSimPose &expected, const char *message) {
bool expect(bool condition, const char *message) {
if (!condition) {
std::cerr << "FAIL: " << message << '\n';
return false;
}
return true;
}
bool expect_near_eps(double actual, double expected, double epsilon, const char *message) {
if (std::fabs(actual - expected) >= epsilon) {
std::cerr << "FAIL: " << message << " actual=" << actual << " expected=" << expected
<< " epsilon=" << epsilon << '\n';
return false;
}
return true;
}
bool expect_joints_near(const LinuxCncFiveAxisJoints &actual,
const LinuxCncFiveAxisJoints &expected,
const char *message) {
bool ok = true;
ok &= expect_near(actual.x, expected.x, message);
ok &= expect_near(actual.y, expected.y, message);
ok &= expect_near(actual.z, expected.z, message);
ok &= expect_near(actual.b, expected.b, message);
ok &= expect_near(actual.c, expected.c, message);
ok &= expect_near(actual.w, expected.w, message);
return ok;
}
bool expect_axis_joints_near(const LinuxCncAxisJoints &actual,
const LinuxCncAxisJoints &expected,
const char *message) {
bool ok = true;
ok &= expect_near(actual.x, expected.x, message);
ok &= expect_near(actual.y, expected.y, message);
@@ -25,6 +57,9 @@ bool expect_pose_near(const CncSimPose &actual, const CncSimPose &expected, cons
ok &= expect_near(actual.a, expected.a, message);
ok &= expect_near(actual.b, expected.b, message);
ok &= expect_near(actual.c, expected.c, message);
ok &= expect_near(actual.u, expected.u, message);
ok &= expect_near(actual.v, expected.v, message);
ok &= expect_near(actual.w, expected.w, message);
return ok;
}
@@ -33,43 +68,378 @@ bool expect_pose_near(const CncSimPose &actual, const CncSimPose &expected, cons
int main() {
bool ok = true;
CncSimPose tip{};
tip.x = 10.0;
tip.y = 20.0;
tip.z = 30.0;
LinuxCncAxisJoints axis_joints{};
axis_joints.x = 1.0;
axis_joints.y = 2.0;
axis_joints.z = 3.0;
axis_joints.a = 4.0;
axis_joints.b = 5.0;
axis_joints.c = 6.0;
axis_joints.u = 7.0;
axis_joints.v = 8.0;
axis_joints.w = 9.0;
CncSimPose identity_pose = linuxcnc_identity_forward(axis_joints);
ok &= expect_near(identity_pose.x, 1.0, "identity/trivkins forward x");
ok &= expect_near(identity_pose.c, 6.0, "identity/trivkins forward c");
ok &= expect_near(identity_pose.w, 9.0, "identity/trivkins forward w");
ok &= expect_axis_joints_near(linuxcnc_identity_inverse(identity_pose),
axis_joints,
"identity/trivkins forward/inverse roundtrip");
CncSimPose pivot = rtcp_pivot_from_tool_tip(tip, 100.0);
ok &= expect_near(pivot.x, 10.0, "zero-angle pivot x");
ok &= expect_near(pivot.y, 20.0, "zero-angle pivot y");
ok &= expect_near(pivot.z, 130.0, "zero-angle pivot z");
axis_joints = {};
axis_joints.x = 10.0;
axis_joints.y = 0.0;
axis_joints.z = 3.0;
axis_joints.a = 4.0;
axis_joints.b = 5.0;
axis_joints.c = 90.0;
axis_joints.u = 7.0;
axis_joints.v = 8.0;
axis_joints.w = 9.0;
CncSimPose rotate_pose = linuxcnc_rotatekins_forward(axis_joints);
ok &= expect_near(rotate_pose.x, 0.0, "rotatekins C90 forward x");
ok &= expect_near(rotate_pose.y, -10.0, "rotatekins C90 forward y");
ok &= expect_near(rotate_pose.z, 3.0, "rotatekins C90 forward z");
ok &= expect_axis_joints_near(linuxcnc_rotatekins_inverse(rotate_pose),
axis_joints,
"rotatekins forward/inverse roundtrip");
tip.a = 90.0;
pivot = rtcp_pivot_from_tool_tip(tip, 100.0);
ok &= expect_near(pivot.x, 10.0, "A90 pivot x");
ok &= expect_near(pivot.y, -80.0, "A90 pivot y");
ok &= expect_near(pivot.z, 30.0, "A90 pivot z");
LinuxCncFiveAxisJoints joints{};
joints.x = 10.0;
joints.y = 20.0;
joints.z = 30.0;
CncSimPose pose = linuxcnc_5axis_forward(joints, 250.0);
ok &= expect_near(pose.x, 10.0, "5axiskins B0 C0 forward x");
ok &= expect_near(pose.y, 20.0, "5axiskins B0 C0 forward y");
ok &= expect_near(pose.z, 30.0, "5axiskins B0 C0 forward z");
tip.a = 0.0;
tip.b = 90.0;
pivot = rtcp_pivot_from_tool_tip(tip, 100.0);
ok &= expect_near(pivot.x, 110.0, "B90 pivot x");
ok &= expect_near(pivot.y, 20.0, "B90 pivot y");
ok &= expect_near(pivot.z, 30.0, "B90 pivot z");
joints.b = 90.0;
joints.c = 0.0;
pose = linuxcnc_5axis_forward(joints, 250.0);
ok &= expect_near(pose.x, 260.0, "5axiskins B90 C0 forward x");
ok &= expect_near(pose.y, 20.0, "5axiskins B90 C0 forward y");
ok &= expect_near(pose.z, 280.0, "5axiskins B90 C0 forward z");
tip.a = 35.0;
tip.b = -20.0;
tip.c = 15.0;
pivot = rtcp_pivot_from_tool_tip(tip, 123.4);
const CncSimPose roundtrip = rtcp_tool_tip_from_pivot(pivot, 123.4);
ok &= expect_pose_near(roundtrip, tip, "RTCP pivot/tool-tip roundtrip");
joints.c = 90.0;
pose = linuxcnc_5axis_forward(joints, 250.0);
ok &= expect_near(pose.x, 10.0, "5axiskins B90 C90 forward x");
ok &= expect_near(pose.y, 270.0, "5axiskins B90 C90 forward y");
ok &= expect_near(pose.z, 280.0, "5axiskins B90 C90 forward z");
tip = {};
tip.a = 90.0;
tip.b = 90.0;
const RtcpVector vector = rtcp_tool_vector_from_pose(tip, 10.0);
ok &= expect_near(vector.x, 0.0, "A90 B90 tool vector x");
ok &= expect_near(vector.y, 10.0, "A90 B90 tool vector y");
ok &= expect_near(vector.z, 0.0, "A90 B90 tool vector z");
joints.x = -12.5;
joints.y = 4.0;
joints.z = 81.25;
joints.b = 37.0;
joints.c = -23.0;
joints.w = -15.0;
pose = linuxcnc_5axis_forward(joints, 250.0);
const LinuxCncFiveAxisJoints roundtrip_joints = linuxcnc_5axis_inverse(pose, 250.0);
ok &= expect_joints_near(roundtrip_joints, joints, "5axiskins forward/inverse roundtrip");
LinuxCncXyzbcTrtParameters trt_parameters = linuxcnc_xyzbc_trt_default_parameters(250.0);
joints = {};
joints.x = 10.0;
joints.y = 20.0;
joints.z = 30.0;
pose = linuxcnc_xyzbc_trt_forward(joints, trt_parameters);
ok &= expect_near(pose.x, 10.0, "xyzbc-trt B0 C0 forward x");
ok &= expect_near(pose.y, 20.0, "xyzbc-trt B0 C0 forward y");
ok &= expect_near(pose.z, 30.0, "xyzbc-trt B0 C0 forward z");
ok &= expect_near(pose.w, 0.0, "xyzbc-trt XYZBC forward drops unconfigured W");
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");
ok &= expect_near(pose.y, 20.0, "xyzbc-trt B90 C0 forward y");
ok &= expect_near(pose.z, 265.0, "xyzbc-trt B90 C0 forward z");
joints.c = 90.0;
pose = linuxcnc_xyzbc_trt_forward(joints, trt_parameters);
ok &= expect_near(pose.x, 20.0, "xyzbc-trt B90 C90 forward x");
ok &= expect_near(pose.y, -185.0, "xyzbc-trt B90 C90 forward y");
ok &= expect_near(pose.z, 265.0, "xyzbc-trt B90 C90 forward z");
pose = {};
pose.x = 225.0;
pose.y = 20.0;
pose.z = -205.0;
pose.b = 90.0;
LinuxCncFiveAxisJoints trt_inverse_joints = linuxcnc_xyzbc_trt_inverse(pose, trt_parameters);
ok &= expect_near(trt_inverse_joints.x, 10.0, "xyzbc-trt B90 C0 inverse x");
ok &= expect_near(trt_inverse_joints.y, 20.0, "xyzbc-trt B90 C0 inverse y");
ok &= expect_near(trt_inverse_joints.z, 30.0, "xyzbc-trt B90 C0 inverse z");
ok &= expect_near(trt_inverse_joints.b, 90.0, "xyzbc-trt B90 C0 inverse b");
ok &= expect_near(trt_inverse_joints.w, 0.0, "xyzbc-trt XYZBC inverse drops unconfigured W");
trt_parameters.conventional_directions = true;
joints.x = -12.5;
joints.y = 4.0;
joints.z = 81.25;
joints.b = 37.0;
joints.c = -23.0;
joints.w = 0.0;
pose = linuxcnc_xyzbc_trt_forward(joints, trt_parameters);
const LinuxCncFiveAxisJoints trt_roundtrip_joints = linuxcnc_xyzbc_trt_inverse(pose, trt_parameters);
ok &= expect_joints_near(trt_roundtrip_joints, joints, "xyzbc-trt conventional forward/inverse roundtrip");
pose = {};
pose.x = 1.0;
pose.y = 2.0;
pose.z = 3.0;
pose.b = 4.0;
pose.c = 5.0;
pose.w = 6.0;
const LinuxCncFiveAxisJoints userk_joints = linuxcnc_xyzbc_userk_inverse(pose);
ok &= expect_near(userk_joints.x, 1.0, "xyzbc userk identity inverse x");
ok &= expect_near(userk_joints.y, 2.0, "xyzbc userk identity inverse y");
ok &= expect_near(userk_joints.z, 3.0, "xyzbc userk identity inverse z");
ok &= expect_near(userk_joints.b, 4.0, "xyzbc userk identity inverse b");
ok &= expect_near(userk_joints.c, 5.0, "xyzbc userk identity inverse c");
ok &= expect_near(userk_joints.w, 0.0, "xyzbc userk identity inverse drops unconfigured W");
joints = {};
joints.x = 6.0;
joints.y = 5.0;
joints.z = 4.0;
joints.a = 12.0;
joints.b = 3.0;
joints.c = 2.0;
joints.w = 1.0;
const CncSimPose userk_pose = linuxcnc_xyzbc_userk_forward(joints);
ok &= expect_near(userk_pose.x, 6.0, "xyzbc userk identity forward x");
ok &= expect_near(userk_pose.y, 5.0, "xyzbc userk identity forward y");
ok &= expect_near(userk_pose.z, 4.0, "xyzbc userk identity forward z");
ok &= expect_near(userk_pose.a, 0.0, "xyzbc userk identity forward drops unconfigured A");
ok &= expect_near(userk_pose.b, 3.0, "xyzbc userk identity forward b");
ok &= expect_near(userk_pose.c, 2.0, "xyzbc userk identity forward c");
ok &= expect_near(userk_pose.w, 0.0, "xyzbc userk identity forward drops unconfigured W");
trt_parameters = linuxcnc_xyzbc_trt_default_parameters(250.0);
joints = {};
joints.x = 10.0;
joints.y = 20.0;
joints.z = 30.0;
CncSimPose xyzac_pose = linuxcnc_xyzac_trt_forward(joints, trt_parameters);
ok &= expect_near(xyzac_pose.x, 10.0, "xyzac-trt A0 C0 forward x");
ok &= expect_near(xyzac_pose.y, 20.0, "xyzac-trt A0 C0 forward y");
ok &= expect_near(xyzac_pose.z, 30.0, "xyzac-trt A0 C0 forward z");
joints.a = 90.0;
joints.c = 0.0;
xyzac_pose = linuxcnc_xyzac_trt_forward(joints, trt_parameters);
ok &= expect_near(xyzac_pose.x, 10.0, "xyzac-trt A90 C0 forward x");
ok &= expect_near(xyzac_pose.y, -205.0, "xyzac-trt A90 C0 forward y");
ok &= expect_near(xyzac_pose.z, 215.0, "xyzac-trt A90 C0 forward z");
joints.c = 90.0;
xyzac_pose = linuxcnc_xyzac_trt_forward(joints, trt_parameters);
ok &= expect_near(xyzac_pose.x, -205.0, "xyzac-trt A90 C90 forward x");
ok &= expect_near(xyzac_pose.y, -10.0, "xyzac-trt A90 C90 forward y");
ok &= expect_near(xyzac_pose.z, 215.0, "xyzac-trt A90 C90 forward z");
trt_parameters.conventional_directions = true;
joints.x = -12.5;
joints.y = 4.0;
joints.z = 81.25;
joints.a = 37.0;
joints.c = -23.0;
joints.b = 0.0;
joints.w = 0.0;
xyzac_pose = linuxcnc_xyzac_trt_forward(joints, trt_parameters);
const LinuxCncFiveAxisJoints xyzac_roundtrip_joints =
linuxcnc_xyzac_trt_inverse(xyzac_pose, trt_parameters);
ok &= expect_near(xyzac_roundtrip_joints.x, joints.x, "xyzac-trt conventional roundtrip x");
ok &= expect_near(xyzac_roundtrip_joints.y, joints.y, "xyzac-trt conventional roundtrip y");
ok &= expect_near(xyzac_roundtrip_joints.z, joints.z, "xyzac-trt conventional roundtrip z");
ok &= expect_near(xyzac_roundtrip_joints.a, joints.a, "xyzac-trt conventional roundtrip a");
ok &= expect_near(xyzac_roundtrip_joints.c, joints.c, "xyzac-trt conventional roundtrip c");
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");
LinuxCncScaraParameters scara_parameters = linuxcnc_scara_default_parameters();
LinuxCncScaraJoints scara_joints{};
scara_joints.j0 = 0.0;
scara_joints.j1 = 0.0;
scara_joints.j2 = 10.0;
scara_joints.j3 = 0.0;
scara_joints.j4 = 11.0;
scara_joints.j5 = 12.0;
int scara_iflags = 0;
CncSimPose scara_pose = linuxcnc_scara_forward(scara_joints, scara_parameters, &scara_iflags);
ok &= expect_near(scara_pose.x, 640.0, "scarakins default forward x");
ok &= expect_near(scara_pose.y, 0.0, "scarakins default forward y");
ok &= expect_near(scara_pose.z, 480.0, "scarakins default forward z");
ok &= expect_near(scara_pose.c, 0.0, "scarakins default forward c");
ok &= expect_near(scara_pose.a, 11.0, "scarakins default forward a");
ok &= expect_near(scara_pose.b, 12.0, "scarakins default forward b");
ok &= expect_near(scara_iflags, 1, "scarakins forward sets iflags for joint1 below 90");
LinuxCncScaraJoints scara_inverse_joints =
linuxcnc_scara_inverse(scara_pose, scara_parameters, scara_iflags);
ok &= expect_near(scara_inverse_joints.j0, scara_joints.j0, "scarakins roundtrip j0");
ok &= expect_near(scara_inverse_joints.j1, scara_joints.j1, "scarakins roundtrip j1");
ok &= expect_near(scara_inverse_joints.j2, scara_joints.j2, "scarakins roundtrip j2");
ok &= expect_near(scara_inverse_joints.j3, scara_joints.j3, "scarakins roundtrip j3");
ok &= expect_near(scara_inverse_joints.j4, scara_joints.j4, "scarakins roundtrip j4");
ok &= expect_near(scara_inverse_joints.j5, scara_joints.j5, "scarakins roundtrip j5");
scara_joints.j0 = 15.0;
scara_joints.j1 = 120.0;
scara_joints.j2 = -5.0;
scara_joints.j3 = 20.0;
scara_pose = linuxcnc_scara_forward(scara_joints, scara_parameters, &scara_iflags);
ok &= expect_near(scara_iflags, 0, "scarakins forward clears iflags for joint1 at or above 90");
scara_inverse_joints = linuxcnc_scara_inverse(scara_pose, scara_parameters, scara_iflags);
ok &= expect_near(scara_inverse_joints.j0, scara_joints.j0, "scarakins iflags0 roundtrip j0");
ok &= expect_near(scara_inverse_joints.j1, scara_joints.j1, "scarakins iflags0 roundtrip j1");
ok &= expect_near(scara_inverse_joints.j2, scara_joints.j2, "scarakins iflags0 roundtrip j2");
ok &= expect_near(scara_inverse_joints.j3, scara_joints.j3, "scarakins iflags0 roundtrip j3");
axis_joints = {};
axis_joints.x = 1.0;
axis_joints.y = 2.0;
axis_joints.z = 3.0;
axis_joints.a = 4.0;
axis_joints.b = 5.0;
axis_joints.c = 6.0;
CncSimPose xyzab_identity_pose = linuxcnc_xyzab_tdr_identity_forward(axis_joints);
ok &= expect_near(xyzab_identity_pose.x, 1.0, "xyzab-tdr type0 identity forward x");
ok &= expect_near(xyzab_identity_pose.y, 2.0, "xyzab-tdr type0 identity forward y");
ok &= expect_near(xyzab_identity_pose.z, 3.0, "xyzab-tdr type0 identity forward z");
ok &= expect_near(xyzab_identity_pose.a, 4.0, "xyzab-tdr type0 identity forward a");
ok &= expect_near(xyzab_identity_pose.b, 5.0, "xyzab-tdr type0 identity forward b");
ok &= expect_near(xyzab_identity_pose.c, 0.0, "xyzab-tdr type0 identity drops unconfigured c");
LinuxCncAxisJoints xyzab_identity_joints = linuxcnc_xyzab_tdr_identity_inverse(xyzab_identity_pose);
ok &= expect_near(xyzab_identity_joints.x, 1.0, "xyzab-tdr type0 identity inverse x");
ok &= expect_near(xyzab_identity_joints.y, 2.0, "xyzab-tdr type0 identity inverse y");
ok &= expect_near(xyzab_identity_joints.z, 3.0, "xyzab-tdr type0 identity inverse z");
ok &= expect_near(xyzab_identity_joints.a, 4.0, "xyzab-tdr type0 identity inverse a");
ok &= expect_near(xyzab_identity_joints.b, 5.0, "xyzab-tdr type0 identity inverse b");
ok &= expect_near(xyzab_identity_joints.c, 0.0, "xyzab-tdr type0 identity inverse drops c");
LinuxCncXyzabTdrParameters xyzab_parameters = linuxcnc_xyzab_tdr_default_parameters(250.0);
axis_joints = {};
axis_joints.x = 10.0;
axis_joints.y = 20.0;
axis_joints.z = 30.0;
axis_joints.a = 90.0;
axis_joints.b = 0.0;
CncSimPose xyzab_tcp_pose = linuxcnc_xyzab_tdr_tcp_forward(axis_joints, xyzab_parameters);
ok &= expect_near(xyzab_tcp_pose.x, 10.0, "xyzab-tdr type1 A90 B0 forward x");
ok &= expect_near(xyzab_tcp_pose.y, 220.0, "xyzab-tdr type1 A90 B0 forward y");
ok &= expect_near(xyzab_tcp_pose.z, 270.0, "xyzab-tdr type1 A90 B0 forward z");
axis_joints.a = 0.0;
axis_joints.b = 90.0;
xyzab_tcp_pose = linuxcnc_xyzab_tdr_tcp_forward(axis_joints, xyzab_parameters);
ok &= expect_near(xyzab_tcp_pose.x, -220.0, "xyzab-tdr type1 A0 B90 forward x");
ok &= expect_near(xyzab_tcp_pose.y, 20.0, "xyzab-tdr type1 A0 B90 forward y");
ok &= expect_near(xyzab_tcp_pose.z, 240.0, "xyzab-tdr type1 A0 B90 forward z");
xyzab_parameters.x_rot_point = 3.0;
xyzab_parameters.y_rot_point = -4.0;
xyzab_parameters.z_rot_point = 5.0;
xyzab_parameters.x_offset = 2.0;
xyzab_parameters.z_offset = 7.0;
xyzab_parameters.tool_offset_z = 11.0;
axis_joints.x = -12.5;
axis_joints.y = 4.0;
axis_joints.z = 81.25;
axis_joints.a = 37.0;
axis_joints.b = -23.0;
xyzab_tcp_pose = linuxcnc_xyzab_tdr_tcp_forward(axis_joints, xyzab_parameters);
LinuxCncAxisJoints xyzab_tcp_roundtrip =
linuxcnc_xyzab_tdr_tcp_inverse(xyzab_tcp_pose, xyzab_parameters);
ok &= expect_near(xyzab_tcp_roundtrip.x, axis_joints.x, "xyzab-tdr type1 roundtrip x");
ok &= expect_near(xyzab_tcp_roundtrip.y, axis_joints.y, "xyzab-tdr type1 roundtrip y");
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");
LinuxCncPumaParameters puma_parameters = linuxcnc_puma_default_parameters();
LinuxCncAxisJoints puma_joints{};
puma_joints.x = 20.0;
puma_joints.y = -35.0;
puma_joints.z = 45.0;
puma_joints.a = 30.0;
puma_joints.b = 40.0;
puma_joints.c = -25.0;
int puma_iflags = 0;
CncSimPose puma_pose = linuxcnc_puma_forward(puma_joints, puma_parameters, &puma_iflags);
int puma_fflags = 0;
LinuxCncAxisJoints puma_roundtrip =
linuxcnc_puma_inverse(puma_pose, puma_joints, puma_parameters, puma_iflags, &puma_fflags);
ok &= expect_near(puma_roundtrip.x, puma_joints.x, "pumakins roundtrip j0");
ok &= expect_near(puma_roundtrip.y, puma_joints.y, "pumakins roundtrip j1");
ok &= expect_near(puma_roundtrip.z, puma_joints.z, "pumakins roundtrip j2");
ok &= expect_near(puma_roundtrip.a, puma_joints.a, "pumakins roundtrip j3");
ok &= expect_near(puma_roundtrip.b, puma_joints.b, "pumakins roundtrip j4");
ok &= expect_near(puma_roundtrip.c, puma_joints.c, "pumakins roundtrip j5");
ok &= expect_near(puma_fflags, 0, "pumakins non-singular inverse flags");
LinuxCncGenserParameters genser_parameters = linuxcnc_genser_puma560_parameters();
LinuxCncAxisJoints genser_joints{};
CncSimPose genser_pose = linuxcnc_genser_forward(genser_joints, genser_parameters);
ok &= expect_near(genser_pose.x, 17.0, "genserkins puma560 zero forward x");
ok &= expect_near(genser_pose.y, 5.4999999693964785, "genserkins puma560 zero forward y");
ok &= expect_near(genser_pose.z, 7.199999995628068, "genserkins puma560 zero forward z");
ok &= expect_near(genser_pose.a, 179.99999990891155, "genserkins puma560 zero forward a");
ok &= expect_near(genser_pose.b, 0.0, "genserkins puma560 zero forward b");
ok &= expect_near(genser_pose.c, 0.0, "genserkins puma560 zero forward c");
genser_joints.x = 20.0;
genser_joints.y = -35.0;
genser_joints.z = 45.0;
genser_joints.a = 30.0;
genser_joints.b = 40.0;
genser_joints.c = -25.0;
genser_pose = linuxcnc_genser_forward(genser_joints, genser_parameters);
ok &= expect_near(genser_pose.x, 15.636973119039572, "genserkins puma560 forward x");
ok &= expect_near(genser_pose.y, 10.791926162750677, "genserkins puma560 forward y");
ok &= expect_near(genser_pose.z, -1.5388033640849728, "genserkins puma560 forward z");
ok &= expect_near(genser_pose.a, 160.1443654010874, "genserkins puma560 forward a");
ok &= expect_near(genser_pose.b, -45.62792804902442, "genserkins puma560 forward b");
ok &= expect_near(genser_pose.c, 21.545586017440204, "genserkins puma560 forward c");
int genser_iterations = -1;
LinuxCncAxisJoints genser_inverse_joints =
linuxcnc_genser_inverse(genser_pose,
genser_joints,
genser_parameters,
&genser_iterations);
ok &= expect_near(genser_inverse_joints.x, genser_joints.x, "genserkins puma560 inverse x");
ok &= expect_near(genser_inverse_joints.y, genser_joints.y, "genserkins puma560 inverse y");
ok &= expect_near(genser_inverse_joints.z, genser_joints.z, "genserkins puma560 inverse z");
ok &= expect_near(genser_inverse_joints.a, genser_joints.a, "genserkins puma560 inverse a");
ok &= expect_near(genser_inverse_joints.b, genser_joints.b, "genserkins puma560 inverse b");
ok &= expect_near(genser_inverse_joints.c, genser_joints.c, "genserkins puma560 inverse c");
ok &= expect_near(genser_iterations, 0, "genserkins puma560 inverse exact-estimate iterations");
LinuxCncAxisJoints genser_estimate = genser_joints;
genser_estimate.x += 0.25;
genser_estimate.y -= 0.2;
genser_estimate.z += 0.15;
genser_estimate.a -= 0.1;
genser_estimate.b += 0.12;
genser_estimate.c -= 0.08;
genser_inverse_joints =
linuxcnc_genser_inverse(genser_pose,
genser_estimate,
genser_parameters,
&genser_iterations);
ok &= expect_near_eps(genser_inverse_joints.x, genser_joints.x, 1e-5,
"genserkins puma560 iterative inverse x");
ok &= expect_near_eps(genser_inverse_joints.y, genser_joints.y, 1e-5,
"genserkins puma560 iterative inverse y");
ok &= expect_near_eps(genser_inverse_joints.z, genser_joints.z, 1e-5,
"genserkins puma560 iterative inverse z");
ok &= expect_near_eps(genser_inverse_joints.a, genser_joints.a, 1e-5,
"genserkins puma560 iterative inverse a");
ok &= expect_near_eps(genser_inverse_joints.b, genser_joints.b, 1e-5,
"genserkins puma560 iterative inverse b");
ok &= expect_near_eps(genser_inverse_joints.c, genser_joints.c, 1e-5,
"genserkins puma560 iterative inverse c");
ok &= expect(genser_iterations > 0, "genserkins puma560 inverse should iterate from perturbed estimate");
return ok ? 0 : 1;
}

View File

@@ -24,16 +24,88 @@ int main() {
ok &= expect(actions.size() == 3, "expected three kinematics actions");
ok &= expect(actions[0].kind == SimulatorGcodeControlKind::KinematicsSwitch &&
actions[0].value == 1 &&
actions[0].rtcp_enabled,
"expected M428 original kinematics action");
!actions[0].rtcp_enabled,
"expected M428 kinematics switch without RTCP state change");
ok &= expect(actions[1].kind == SimulatorGcodeControlKind::KinematicsSwitch &&
actions[1].value == 0 &&
!actions[1].rtcp_enabled,
"expected M429 identity kinematics action");
ok &= expect(actions[2].kind == SimulatorGcodeControlKind::KinematicsSwitch &&
actions[2].value == 2 &&
actions[2].rtcp_enabled,
"expected M430 five-axis BC kinematics action");
!actions[2].rtcp_enabled,
"expected M430 userk switch without RTCP state change");
ok &= expect(parse_simulator_gcode_control_line("/M428", false, &actions),
"expected disabled block-delete M428 control line to execute");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::KinematicsSwitch &&
actions[0].value == 1,
"expected disabled block-delete M428 action");
ok &= expect(!parse_simulator_gcode_control_line("/M428", true, &actions),
"expected enabled block-delete M428 control line to be skipped");
ok &= expect(actions.empty(), "expected no actions for enabled block-delete M428");
ok &= expect(parse_simulator_gcode_control_line("/N10 M428", false, &actions),
"expected disabled block-delete leading-N M428 control line to execute");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::KinematicsSwitch &&
actions[0].value == 1,
"expected disabled block-delete /N10 M428 action");
ok &= expect(!parse_simulator_gcode_control_line("/N10 M428", true, &actions),
"expected enabled block-delete leading-N M428 control line to be skipped");
ok &= expect(actions.empty(), "expected no actions for enabled block-delete /N10 M428");
ok &= expect(!parse_simulator_gcode_control_line("M428 (unterminated", &actions),
"expected unclosed comment on M428 line to fall through to LinuxCNC error handling");
ok &= expect(actions.empty(), "expected no actions for M428 line with unclosed comment");
ok &= expect(!parse_simulator_gcode_control_line("M428 (outer (nested))", &actions),
"expected nested comment on M428 line to fall through to LinuxCNC error handling");
ok &= expect(actions.empty(), "expected no actions for M428 line with nested comment");
ok &= expect(!parse_simulator_gcode_control_line("(comment) /M428", false, &actions),
"expected comment-prefixed slash M428 to fall through to LinuxCNC error handling");
ok &= expect(actions.empty(), "expected no actions for comment-prefixed slash M428");
ok &= expect(parse_simulator_gcode_control_line("M428.00009", &actions),
"expected LinuxCNC integer tolerance to accept near-integer M428");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::KinematicsSwitch &&
actions[0].value == 1,
"expected near-integer M428 to parse as M428");
ok &= expect(parse_simulator_gcode_control_line("M427.99995", &actions),
"expected LinuxCNC integer tolerance to round high-fraction M word");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::KinematicsSwitch &&
actions[0].value == 1,
"expected high-fraction M427.99995 to parse as M428");
ok &= expect(parse_simulator_gcode_control_line("M++428", &actions),
"expected LinuxCNC unary plus M value to parse as M428");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::KinematicsSwitch &&
actions[0].value == 1,
"expected M++428 to parse as M428");
ok &= expect(parse_simulator_gcode_control_line("N10 M428", &actions),
"expected LinuxCNC leading N number before M428");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::KinematicsSwitch &&
actions[0].value == 1,
"expected N10 M428 to parse as M428");
ok &= expect(parse_simulator_gcode_control_line("N10.25 M428", &actions),
"expected LinuxCNC fractional N number before M428");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::KinematicsSwitch &&
actions[0].value == 1,
"expected N10.25 M428 to parse as M428");
ok &= expect(parse_simulator_gcode_control_line("M[428]", &actions),
"expected LinuxCNC bracketed M expression value to parse as M428");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::KinematicsSwitch &&
actions[0].value == 1,
"expected M[428] to parse as M428");
ok &= expect(!parse_simulator_gcode_control_line("M428.00011", &actions),
"expected non-integer M word above lower tolerance to fall through");
ok &= expect(actions.empty(), "expected no actions for lower-bound non-integer M word");
ok &= expect(!parse_simulator_gcode_control_line("M427.99989", &actions),
"expected M word below upper round-up tolerance to fall through");
ok &= expect(actions.empty(), "expected no actions for upper-bound non-integer M word");
ok &= expect(!parse_simulator_gcode_control_line("M4.28e2", &actions),
"expected LinuxCNC non-exponent M-number parsing to leave trailing e2");
ok &= expect(actions.empty(), "expected no actions for exponent M word");
ok &= expect(parse_simulator_gcode_control_line("G49 (cancel RTCP)", &actions),
"expected G49 control line");
@@ -42,6 +114,15 @@ int main() {
!actions[0].rtcp_enabled &&
actions[0].h_code == 0,
"expected G49 RTCP off action");
ok &= expect(!parse_simulator_gcode_control_line("G49 (unterminated", &actions),
"expected unclosed comment on G49 line to fall through to LinuxCNC error handling");
ok &= expect(actions.empty(), "expected no actions for G49 line with unclosed comment");
ok &= expect(!parse_simulator_gcode_control_line("G49 (outer (nested))", &actions),
"expected nested comment on G49 line to fall through to LinuxCNC error handling");
ok &= expect(actions.empty(), "expected no actions for G49 line with nested comment");
ok &= expect(!parse_simulator_gcode_control_line("(comment) /G49", false, &actions),
"expected comment-prefixed slash G49 to fall through to LinuxCNC error handling");
ok &= expect(actions.empty(), "expected no actions for comment-prefixed slash G49");
ok &= expect(parse_simulator_gcode_control_line("G43.4 H7", &actions),
"expected G43.4 H7 control line");
@@ -50,6 +131,116 @@ int main() {
actions[0].rtcp_enabled &&
actions[0].h_code == 7,
"expected G43.4 RTCP on action with H7");
ok &= expect(parse_simulator_gcode_control_line("G43.5 H7", &actions),
"expected G43.5 H7 control line");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::RtcpState &&
actions[0].value == 435 &&
actions[0].rtcp_enabled &&
actions[0].h_code == 7,
"expected G43.5 RTCP on action with H7");
ok &= expect(parse_simulator_gcode_control_line("/G43.4 H7", false, &actions),
"expected disabled block-delete G43.4 control line to execute");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::RtcpState &&
actions[0].rtcp_enabled &&
actions[0].h_code == 7,
"expected disabled block-delete G43.4 action");
ok &= expect(!parse_simulator_gcode_control_line("/G43.4 H7", true, &actions),
"expected enabled block-delete G43.4 control line to be skipped");
ok &= expect(actions.empty(), "expected no actions for enabled block-delete G43.4");
ok &= expect(parse_simulator_gcode_control_line("/N20 G43.4 H7", false, &actions),
"expected disabled block-delete leading-N G43.4 control line to execute");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::RtcpState &&
actions[0].rtcp_enabled &&
actions[0].h_code == 7,
"expected disabled block-delete /N20 G43.4 H7 action");
ok &= expect(!parse_simulator_gcode_control_line("/N20 G43.4 H7", true, &actions),
"expected enabled block-delete leading-N G43.4 control line to be skipped");
ok &= expect(actions.empty(), "expected no actions for enabled block-delete /N20 G43.4 H7");
ok &= expect(parse_simulator_gcode_control_line("G43.40009 H7", &actions),
"expected LinuxCNC read_g tolerance to accept near G43.4");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::RtcpState &&
actions[0].value == 434 &&
actions[0].rtcp_enabled &&
actions[0].h_code == 7,
"expected near G43.4 to parse as G43.4");
ok &= expect(parse_simulator_gcode_control_line("G43.39995 H7", &actions),
"expected LinuxCNC read_g tolerance to round high-fraction G word");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::RtcpState &&
actions[0].value == 434 &&
actions[0].rtcp_enabled &&
actions[0].h_code == 7,
"expected high-fraction G43.39995 to parse as G43.4");
ok &= expect(parse_simulator_gcode_control_line("G+43.4 H+7", &actions),
"expected LinuxCNC unary plus G/H values to parse");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::RtcpState &&
actions[0].value == 434 &&
actions[0].rtcp_enabled &&
actions[0].h_code == 7,
"expected G+43.4 H+7 to parse as G43.4 H7");
ok &= expect(parse_simulator_gcode_control_line("N20 G43.4 H7", &actions),
"expected LinuxCNC leading N number before G43.4");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::RtcpState &&
actions[0].value == 434 &&
actions[0].rtcp_enabled &&
actions[0].h_code == 7,
"expected N20 G43.4 H7 to parse as G43.4 H7");
ok &= expect(parse_simulator_gcode_control_line("G[43.4] H[7]", &actions),
"expected LinuxCNC bracketed G/H expression values to parse");
ok &= expect(actions.size() == 1 &&
actions[0].kind == SimulatorGcodeControlKind::RtcpState &&
actions[0].value == 434 &&
actions[0].rtcp_enabled &&
actions[0].h_code == 7,
"expected G[43.4] H[7] to parse as G43.4 H7");
ok &= expect(!parse_simulator_gcode_control_line("G43.4002 H7", &actions),
"expected G word above read_g out-of-range tolerance to fall through");
ok &= expect(actions.empty(), "expected no actions for out-of-range G word");
ok &= expect(!parse_simulator_gcode_control_line("G43.3998 H7", &actions),
"expected G word below read_g round-up tolerance to fall through");
ok &= expect(actions.empty(), "expected no actions below G word round-up tolerance");
ok &= expect(!parse_simulator_gcode_control_line("G4.34e1 H7", &actions),
"expected LinuxCNC non-exponent G-number parsing to leave trailing e1");
ok &= expect(actions.empty(), "expected no actions for exponent G word");
ok &= expect(parse_simulator_gcode_control_line("G43.4 H7.00009", &actions),
"expected LinuxCNC integer tolerance to accept near-integer H");
ok &= expect(actions.size() == 1 && actions[0].h_code == 7,
"expected near-integer H to keep the lower integer");
ok &= expect(parse_simulator_gcode_control_line("G43.4 H7.99995", &actions),
"expected LinuxCNC integer tolerance to round high-fraction H");
ok &= expect(actions.size() == 1 && actions[0].h_code == 8,
"expected high-fraction H to round up");
ok &= expect(parse_simulator_gcode_control_line("G43.4 H++7", &actions),
"expected LinuxCNC recursive unary plus H value to parse");
ok &= expect(actions.size() == 1 && actions[0].h_code == 7,
"expected H++7 to parse as H7");
ok &= expect(!parse_simulator_gcode_control_line("G43.4 H7.00011", &actions),
"expected non-integer H above lower tolerance to fall through");
ok &= expect(actions.empty(), "expected no actions for lower-bound non-integer H");
ok &= expect(!parse_simulator_gcode_control_line("G43.4 H7.99989", &actions),
"expected H below the upper round-up tolerance to fall through");
ok &= expect(actions.empty(), "expected no actions for upper-bound non-integer H");
ok &= expect(!parse_simulator_gcode_control_line("G43.4 H7e0", &actions),
"expected LinuxCNC non-exponent H-number parsing to leave trailing e0");
ok &= expect(actions.empty(), "expected no actions for exponent H word");
ok &= expect(!parse_simulator_gcode_control_line("G43.4 H-2", &actions),
"expected LinuxCNC negative-H rule to fall through");
ok &= expect(actions.empty(), "expected no actions for invalid negative H");
ok &= expect(!parse_simulator_gcode_control_line("G43.4 H7 H8", &actions),
"expected duplicate H words to fall through to LinuxCNC error handling");
ok &= expect(actions.empty(), "expected no actions for duplicate H words");
ok &= expect(!parse_simulator_gcode_control_line("G43.4 G49", &actions),
"expected duplicate RTCP-state G words to fall through to LinuxCNC modal-group handling");
ok &= expect(actions.empty(), "expected no actions for duplicate RTCP-state G words");
ok &= expect(!parse_simulator_gcode_control_line("G49 H7", &actions),
"expected G49 with unused H word to fall through to LinuxCNC error handling");
ok &= expect(actions.empty(), "expected no actions for G49 with unused H word");
ok &= expect(!parse_simulator_gcode_control_line("G43.4 H7 X10", &actions),
"expected mixed RTCP/motion line to fall through to interpreter");

View File

@@ -18,6 +18,9 @@ The event callback emits normalized `CncSimEvent` records. JavaScript can transf
## LinuxCNC integration plan
Functional behavior must come from LinuxCNC source code, not independently
designed project code. See `docs/linuxcnc-source-policy.md`.
1. Build a native `CanonEventSink` that implements all functions declared in `canon.hh`.
2. Link the sink with `src/emc/rs274ngc` and `src/emc/nml_intf` instead of the task controller.
3. Stub or remove Python remap support for the first browser target.
@@ -58,10 +61,18 @@ Feature parity needs more than G-code parsing:
## Five-axis and RTCP
The first RTCP implementation is a geometry kernel, not the full LinuxCNC motion
controller. It treats programmed XYZ as the tool-center point, applies A/B/C
orientation to a local tool vector, and computes the compensated pivot/spindle
point needed to keep the tool tip fixed.
RTCP pivot events are emitted only for known LinuxCNC switchkins modes. These
paths must use formulas copied from LinuxCNC source code, not independently
derived geometry. The current source mapping is:
- `M428` / `XYZBC_TRT`: `linuxcnc/src/emc/kinematics/trtfuncs.c`,
`xyzbcKinematicsForward()` and `xyzbcKinematicsInverse()`
- `M430` / `USERK_IDENTITY`: `linuxcnc/src/emc/kinematics/userkfuncs.c`,
delegating to `identityKinematicsForward()` and
`identityKinematicsInverse()` in `kins_util.c`
- The retained `5axiskins` helpers map to
`linuxcnc/src/emc/kinematics/5axiskins.c`,
`fiveaxis_KinematicsForward()` and `fiveaxis_KinematicsInverse()`
RTCP is opt-in through config JSON. When enabled, the original motion events
remain programmed tool-tip motion and an additional `rtcp-pivot` event is emitted
@@ -74,7 +85,5 @@ Current files:
- `core/src/rtcp_kinematics.cpp`
- `core/tests/rtcp_kinematics_smoke.cpp`
The next integration step is to add machine configuration for rotary topology,
pivot offsets, tool length sources, and rotation order, then apply RTCP
compensation while converting LinuxCNC Canon motion events into renderable
machine/tool-tip trajectories.
The next integration step is to add more LinuxCNC source-backed kinematics for
additional switchkins or machine topologies.

View File

@@ -19,7 +19,7 @@ g++ -std=c++17 -I core/include core/src/cnc_sim_api.cpp core/tests/cnc_sim_api_s
Status: in progress.
This parser only exists to test the ABI and UI before Emscripten and LinuxCNC are wired in. It currently handles:
This parser only exists to test the ABI and UI before Emscripten and LinuxCNC are wired in. It is not a production feature implementation. Do not extend it with independently designed G-code behavior; when a behavior needs to change, first locate the LinuxCNC source implementation and either route to that source-backed backend or port the corresponding source logic. It currently handles:
- multiple G/M words on one line
- `G0`, `G1`, `G2`, `G3`
@@ -39,7 +39,7 @@ This parser only exists to test the ABI and UI before Emscripten and LinuxCNC ar
- canned cycles `G73`, `G74`, `G81`, `G82`, `G83`, `G84`, `G85`, `G86`, `G87`, `G88`, `G89` with `G80`, `G98`, `G99`, `L`
- coordinate offset events for `G10 L2`, `G10 L20`, `G54`-`G59.3`, `G52`, `G92`, `G92.1`, `G92.2`, and `G92.3`
It is not the production interpreter.
It is not the production interpreter. Any remaining smoke-only behavior is technical debt to replace with LinuxCNC source-backed code.
## Step 2: Canon event sink

View File

@@ -0,0 +1,32 @@
# LinuxCNC Source Policy
The simulator must not grow independently designed functional behavior.
Functional behavior must come from LinuxCNC source code.
Allowed project code:
- Thin adapters between LinuxCNC source code and the simulator C API.
- Platform shims needed to compile LinuxCNC code for native tests or wasm.
- Event serialization, test fixtures, build scripts, and documentation.
- Temporary smoke scaffolding only when it is explicitly marked as temporary.
Not allowed as final behavior:
- Hand-written G-code interpretation semantics.
- Hand-derived kinematics, RTCP, coordinate transform, cutter compensation, canned cycle, modal, parameter, or expression behavior.
- Behavior copied from documentation, controller intuition, or examples without checking the LinuxCNC implementation.
Required workflow for every functional change:
1. Identify the LinuxCNC source file and function that defines the behavior.
2. Add or update a test that demonstrates the LinuxCNC behavior.
3. Port, wrap, or route to that LinuxCNC implementation.
4. Record the source file/function in code comments or nearby documentation when the mapping is not obvious.
5. Run `./test-native.sh` and `./test-linuxcnc-source-link.sh`.
Current temporary exceptions:
- The smoke parser exists only to exercise the public API and UI while the LinuxCNC RS274 backend is being wired in.
- 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.

View File

@@ -34,4 +34,19 @@ binding:src/emc/rs274ngc/pyparamclass.cc:Boost.Python bindings; exclude from was
tooldata:src/emc/tooldata/tooldata_common.cc:shared tool table parser/format logic
tooldata:src/emc/tooldata/tooldata_mmap.cc:native mmap implementation; replace for wasm
tooldata:src/emc/tooldata/tooldata_db.cc:external database integration; exclude from wasm core
header:src/emc/rs274ngc/array1.hh:LinuxCNC rs274 header tracked for full interpreter source coverage
header:src/emc/rs274ngc/boost_pyenum_macros.hh:LinuxCNC rs274 Boost.Python enum helper header
header:src/emc/rs274ngc/interp_array_types.hh:LinuxCNC rs274 block array type definitions
header:src/emc/rs274ngc/interp_base.hh:LinuxCNC rs274 base interpreter declarations
header:src/emc/rs274ngc/interp_fwd.hh:LinuxCNC rs274 forward declarations
header:src/emc/rs274ngc/interp_inspection.hh:LinuxCNC rs274 inspection declarations
header:src/emc/rs274ngc/interp_internal.hh:LinuxCNC rs274 internal interpreter declarations
header:src/emc/rs274ngc/interp_parameter_def.hh:LinuxCNC rs274 parameter definitions
header:src/emc/rs274ngc/interp_python.hh:LinuxCNC rs274 Python/remap declarations
header:src/emc/rs274ngc/interp_queue.hh:LinuxCNC rs274 queue declarations
header:src/emc/rs274ngc/modal_state.hh:LinuxCNC rs274 modal state declarations
header:src/emc/rs274ngc/paramclass.hh:LinuxCNC rs274 parameter class declarations
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

View File

@@ -26,3 +26,19 @@ 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
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
header:src/emc/rs274ngc/interp_base.hh:LinuxCNC rs274 base interpreter declarations
header:src/emc/rs274ngc/interp_fwd.hh:LinuxCNC rs274 forward declarations
header:src/emc/rs274ngc/interp_inspection.hh:LinuxCNC rs274 inspection declarations
header:src/emc/rs274ngc/interp_internal.hh:LinuxCNC rs274 internal interpreter declarations
header:src/emc/rs274ngc/interp_parameter_def.hh:LinuxCNC rs274 parameter definitions
header:src/emc/rs274ngc/interp_python.hh:LinuxCNC rs274 Python/remap declarations, compile-satisfied by wasm shims
header:src/emc/rs274ngc/interp_queue.hh:LinuxCNC rs274 queue declarations
header:src/emc/rs274ngc/modal_state.hh:LinuxCNC rs274 modal state declarations
header:src/emc/rs274ngc/paramclass.hh:LinuxCNC rs274 parameter class declarations
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

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
if [[ "$#" -lt 2 || "$#" -gt 3 ]]; then
echo "usage: $0 <set-name> <append-name> [relative-prefix]" >&2
exit 1
fi
set_name=$1
append_name=$2
relative_prefix=${3:-}
awk -v set_name="$set_name" -v append_name="$append_name" -v relative_prefix="$relative_prefix" '
$0 ~ "^[[:space:]]*set\\(" set_name "([[:space:]]|$)" {
in_set = 1
next
}
in_set && /^[[:space:]]*\)/ {
in_set = 0
next
}
$0 ~ "^[[:space:]]*list\\(APPEND " append_name "([[:space:]]|$)" {
in_append = 1
next
}
in_append && /^[[:space:]]*\)/ {
in_append = 0
next
}
in_set || in_append {
line = $0
sub(/^[[:space:]]*/, "", line)
sub(/[[:space:]]*$/, "", line)
if (line == "") {
next
}
sub(/^\$\{CMAKE_CURRENT_SOURCE_DIR\}\//, "core/", line)
if (line !~ /^core\// && relative_prefix != "") {
line = relative_prefix line
}
print line
}
' core/CMakeLists.txt

35
list-cmake-set-sources.sh Executable file
View File

@@ -0,0 +1,35 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
if [[ "$#" -lt 1 || "$#" -gt 2 ]]; then
echo "usage: $0 <cmake-set-name> [relative-prefix]" >&2
exit 1
fi
set_name=$1
relative_prefix=${2:-}
awk -v set_name="$set_name" -v relative_prefix="$relative_prefix" '
$0 ~ "^[[:space:]]*set\\(" set_name "([[:space:]]|$)" {
in_list = 1
next
}
in_list && /^[[:space:]]*\)/ {
exit
}
in_list {
line = $0
sub(/^[[:space:]]*/, "", line)
sub(/[[:space:]]*$/, "", line)
if (line == "") {
next
}
sub(/^\$\{CMAKE_CURRENT_SOURCE_DIR\}\//, "core/", line)
if (line !~ /^core\// && relative_prefix != "") {
line = relative_prefix line
}
print line
}
' core/CMakeLists.txt

View File

@@ -5,40 +5,7 @@ cd "$(dirname "$0")"
filter=${1:-}
awk '
/set\(cnc_sim_core_sources/ {
in_list = 1
next
}
in_list && /^[[:space:]]*\)/ {
exit
}
in_list {
line = $0
sub(/^[[:space:]]*/, "", line)
sub(/[[:space:]]*$/, "", line)
if (line == "") {
next
}
print "core/" line
}
' core/CMakeLists.txt | while IFS= read -r source; do
if [[ ! -f "$source" ]]; then
echo "missing cnc sim core source: $source" >&2
exit 1
fi
if [[ -n "$filter" && "$(basename "$source")" != "$filter" ]]; then
continue
fi
printf '%s\n' "$source"
done | {
found=0
while IFS= read -r source; do
found=1
printf '%s\n' "$source"
done
if [[ -n "$filter" && "$found" -eq 0 ]]; then
echo "missing cnc sim core source in CMake list: $filter" >&2
exit 1
fi
}
./list-cmake-set-sources.sh cnc_sim_core_sources core/ | ./list-filtered-existing-paths.sh \
"$filter" \
"missing cnc sim core source" \
"missing cnc sim core source in CMake list"

36
list-filtered-existing-paths.sh Executable file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
if [[ "$#" -lt 3 ]]; then
echo "usage: $0 <filter> <missing-path-message> <missing-filter-message> [missing-path-hint]" >&2
exit 1
fi
filter=$1
missing_path_message=$2
missing_filter_message=$3
missing_path_hint=${4:-}
found=0
while IFS= read -r path; do
[[ -z "$path" ]] && continue
if [[ ! -f "$path" ]]; then
echo "$missing_path_message: $path" >&2
if [[ -n "$missing_path_hint" ]]; then
echo "$missing_path_hint" >&2
fi
exit 1
fi
if [[ -n "$filter" && "$(basename "$path")" != "$filter" ]]; then
continue
fi
found=1
printf '%s\n' "$path"
done
if [[ -n "$filter" && "$found" -eq 0 ]]; then
echo "$missing_filter_message: $filter" >&2
exit 1
fi

View File

@@ -5,26 +5,4 @@ cd "$(dirname "$0")"
filter=${1:-}
sources=(
core/src/canon_event_sink.cpp
core/src/linuxcnc_canon_bridge.cpp
core/src/rtcp_kinematics.cpp
)
found=0
for source in "${sources[@]}"; do
if [[ ! -f "$source" ]]; then
echo "missing LinuxCNC bridge smoke project source: $source" >&2
exit 1
fi
if [[ -n "$filter" && "$(basename "$source")" != "$filter" ]]; then
continue
fi
found=1
printf '%s\n' "$source"
done
if [[ -n "$filter" && "$found" -eq 0 ]]; then
echo "missing LinuxCNC bridge smoke project source in list: $filter" >&2
exit 1
fi
./list-project-source-set.sh linuxcnc-bridge-smoke "$filter"

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
manifest=${1:?manifest required}
filter_group=${2:?filter group required}
output_mode=${3:?output mode required}
allowed_groups=${4:?allowed groups required}
duplicate_groups=${5:?duplicate groups required}
missing_source_message=${6:?missing source message required}
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
contains_group() {
local groups=$1
local group=$2
[[ ",$groups," == *",$group,"* ]]
}
checks_duplicate_group() {
local groups=$1
local group=$2
[[ "$groups" == "all" ]] || contains_group "$groups" "$group"
}
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
if [[ "$filter_group" != "all" ]] && ! contains_group "$allowed_groups" "$filter_group"; then
echo "unknown manifest source filter: $filter_group" >&2
exit 1
fi
case "$output_mode" in
relative|full)
;;
*)
echo "unknown manifest source output mode: $output_mode" >&2
exit 1
;;
esac
seen_sources=()
while IFS=: read -r group path note; do
case "$group" in
""|\#*)
continue
;;
esac
if ! contains_group "$allowed_groups" "$group"; then
echo "unknown manifest group: $group" >&2
exit 1
fi
full_path=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh \
--missing-message "$missing_source_message" \
"$path")
if checks_duplicate_group "$duplicate_groups" "$group"; then
if printf '%s\n' "${seen_sources[@]}" | grep -Fx -- "$path" >/dev/null; then
echo "duplicate manifest source: $path" >&2
exit 1
fi
seen_sources+=("$path")
fi
if [[ "$filter_group" == "all" || "$filter_group" == "$group" ]]; then
if [[ "$output_mode" == "full" ]]; then
printf '%s\n' "$full_path"
else
printf '%s\n' "$path"
fi
fi
done < "$manifest"

View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
printf '%s\n' \
-L "$linuxcnc_root/lib" \
-Wl,-rpath,"$linuxcnc_root/lib"

View File

@@ -5,49 +5,7 @@ cd "$(dirname "$0")"
filter=${1:-}
awk '
/set\(cnc_sim_core_sources/ {
in_core = 1
next
}
in_core && /^[[:space:]]*\)/ {
in_core = 0
next
}
/list\(APPEND cnc_sim_core_sources/ {
in_backend_sources = 1
next
}
in_backend_sources && /^[[:space:]]*\)/ {
in_backend_sources = 0
next
}
in_core || in_backend_sources {
line = $0
sub(/^[[:space:]]*/, "", line)
sub(/[[:space:]]*$/, "", line)
if (line == "") {
next
}
print "core/" line
}
' core/CMakeLists.txt | while IFS= read -r source; do
if [[ ! -f "$source" ]]; then
echo "missing LinuxCNC rs274 API project source: $source" >&2
exit 1
fi
if [[ -n "$filter" && "$(basename "$source")" != "$filter" ]]; then
continue
fi
printf '%s\n' "$source"
done | {
found=0
while IFS= read -r source; do
found=1
printf '%s\n' "$source"
done
if [[ -n "$filter" && "$found" -eq 0 ]]; then
echo "missing LinuxCNC rs274 API project source in CMake list: $filter" >&2
exit 1
fi
}
./list-cmake-set-plus-append-sources.sh cnc_sim_core_sources cnc_sim_core_sources core/ | ./list-filtered-existing-paths.sh \
"$filter" \
"missing LinuxCNC rs274 API project source" \
"missing LinuxCNC rs274 API project source in CMake list"

View File

@@ -5,29 +5,4 @@ cd "$(dirname "$0")"
filter=${1:-}
sources=(
core/src/canon_event_sink.cpp
core/src/linuxcnc_canon_bridge.cpp
core/src/linuxcnc_tooldata_fixture.cpp
core/src/rtcp_kinematics.cpp
core/src/simulator_gcode_controls.cpp
core/tools/linuxcnc_rs274_dump.cpp
)
found=0
for source in "${sources[@]}"; do
if [[ ! -f "$source" ]]; then
echo "missing LinuxCNC rs274 dump project source: $source" >&2
exit 1
fi
if [[ -n "$filter" && "$(basename "$source")" != "$filter" ]]; then
continue
fi
found=1
printf '%s\n' "$source"
done
if [[ -n "$filter" && "$found" -eq 0 ]]; then
echo "missing LinuxCNC rs274 dump project source in list: $filter" >&2
exit 1
fi
./list-project-source-set.sh linuxcnc-rs274-dump "$filter"

View File

@@ -0,0 +1,67 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
include_python=0
include_core_include=0
include_core_src=0
syntax_only=0
emit_ulapi=1
while [[ "$#" -gt 0 ]]; do
case "$1" in
--python)
include_python=1
shift
;;
--core-include)
include_core_include=1
shift
;;
--core-src)
include_core_src=1
shift
;;
--syntax-only)
syntax_only=1
shift
;;
--no-ulapi)
emit_ulapi=0
shift
;;
--)
shift
break
;;
*)
echo "unknown source cxxflag option: $1" >&2
exit 1
;;
esac
done
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
python_includes=()
if [[ "$include_python" -eq 1 ]]; then
python_include_output=$(python3.13-config --includes 2>/dev/null || python3-config --includes)
read -r -a python_includes <<<"$python_include_output"
fi
printf '%s\n' \
-std=c++17 \
$([[ "$emit_ulapi" -eq 1 ]] && printf '%s\n' -DULAPI) \
$([[ "$syntax_only" -eq 1 ]] && printf '%s\n' -fsyntax-only) \
"${python_includes[@]}" \
$([[ "$include_core_include" -eq 1 ]] && printf '%s\n' -I core/include) \
$([[ "$include_core_src" -eq 1 ]] && printf '%s\n' -I core/src) \
-I "$linuxcnc_root/src" \
-I "$linuxcnc_root/src/emc" \
-I "$linuxcnc_root/src/emc/nml_intf" \
-I "$linuxcnc_root/src/emc/rs274ngc" \
-I "$linuxcnc_root/src/emc/motion" \
-I "$linuxcnc_root/src/emc/pythonplugin" \
-I "$linuxcnc_root/include"

View File

@@ -4,21 +4,43 @@ set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
missing_source_message="missing LinuxCNC source"
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
while [[ "$#" -gt 0 ]]; do
case "$1" in
--missing-message)
if [[ "$#" -lt 2 ]]; then
echo "missing --missing-message value" >&2
exit 1
fi
missing_source_message=$2
shift 2
;;
--)
shift
break
;;
-*)
echo "unknown source file option: $1" >&2
exit 1
;;
*)
break
;;
esac
done
if [[ "$#" -eq 0 ]]; then
echo "usage: $0 <linuxcnc-relative-source>..." >&2
echo "usage: $0 [--missing-message <message>] <linuxcnc-relative-source>..." >&2
exit 1
fi
for source in "$@"; do
full_source="$linuxcnc_root/$source"
if [[ ! -f "$full_source" ]]; then
echo "missing LinuxCNC source: $source" >&2
echo "$missing_source_message: $source" >&2
exit 1
fi
printf '%s\n' "$full_source"

View File

@@ -6,67 +6,11 @@ cd "$(dirname "$0")"
manifest=${1:-linuxcnc-rs274-source-files.txt}
filter_group=${2:-core}
output_mode=${3:-relative}
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
case "$filter_group" in
core|binding|tooldata|all)
;;
*)
echo "unknown manifest source filter: $filter_group" >&2
exit 1
;;
esac
case "$output_mode" in
relative|full)
;;
*)
echo "unknown manifest source output mode: $output_mode" >&2
exit 1
;;
esac
seen_sources=()
while IFS=: read -r group path note; do
case "$group" in
""|\#*)
continue
;;
core|binding|tooldata)
;;
*)
echo "unknown manifest group: $group" >&2
exit 1
;;
esac
if [[ ! -f "$linuxcnc_root/$path" ]]; then
echo "missing manifest file: $path" >&2
exit 1
fi
if [[ "$group" == "core" ]]; then
if printf '%s\n' "${seen_sources[@]}" | grep -Fx -- "$path" >/dev/null; then
echo "duplicate manifest source: $path" >&2
exit 1
fi
seen_sources+=("$path")
fi
if [[ "$filter_group" == "all" || "$filter_group" == "$group" ]]; then
if [[ "$output_mode" == "full" ]]; then
printf '%s\n' "$linuxcnc_root/$path"
else
printf '%s\n' "$path"
fi
fi
done < "$manifest"
./list-linuxcnc-manifest-sources.sh \
"$manifest" \
"$filter_group" \
"$output_mode" \
core,header,binding,tooldata \
core \
"missing manifest file"

View File

@@ -6,10 +6,7 @@ cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
filter=${1:-}
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
objects=(
src/objects/emc/rs274ngc/interpmodule.o
@@ -24,22 +21,10 @@ objects=(
src/objects/libnml/nml/stat_msg.o
)
found=0
for object in "${objects[@]}"; do
full_object="$linuxcnc_root/$object"
if [[ ! -f "$full_object" ]]; then
echo "missing LinuxCNC support object: $full_object" >&2
echo "build LinuxCNC first or set LINUXCNC_ROOT to a built tree" >&2
exit 1
fi
if [[ -n "$filter" && "$(basename "$object")" != "$filter" ]]; then
continue
fi
found=1
printf '%s\n' "$full_object"
done
if [[ -n "$filter" && "$found" -eq 0 ]]; then
echo "missing LinuxCNC support object in list: $filter" >&2
exit 1
fi
printf '%s\n' "$linuxcnc_root/$object"
done | ./list-filtered-existing-paths.sh \
"$filter" \
"missing LinuxCNC support object" \
"missing LinuxCNC support object in list" \
"build LinuxCNC first or set LINUXCNC_ROOT to a built tree"

View File

@@ -0,0 +1,117 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
std_flag=-std=c++17
include_core_include=0
include_core_src=0
include_tooldata=0
include_wctype=0
syntax_only=0
function_sections=0
data_sections=0
profile=base
while [[ "$#" -gt 0 ]]; do
case "$1" in
--profile)
if [[ "$#" -lt 2 ]]; then
echo "missing --profile value" >&2
exit 1
fi
profile=$2
shift 2
;;
--std)
if [[ "$#" -lt 2 ]]; then
echo "missing --std value" >&2
exit 1
fi
std_flag=-std="$2"
shift 2
;;
--core-include)
include_core_include=1
shift
;;
--core-src)
include_core_src=1
shift
;;
--tooldata-include)
include_tooldata=1
shift
;;
--wctype)
include_wctype=1
shift
;;
--syntax-only)
syntax_only=1
shift
;;
--function-sections)
function_sections=1
shift
;;
--data-sections)
data_sections=1
shift
;;
--)
shift
break
;;
*)
echo "unknown wasm cxxflag option: $1" >&2
exit 1
;;
esac
done
case "$profile" in
base|core20|core20-sections)
;;
*)
echo "unknown wasm cxxflag profile: $profile" >&2
exit 1
;;
esac
case "$profile" in
core20|core20-sections)
std_flag=-std=c++20
include_core_include=1
include_core_src=1
include_wctype=1
;;
esac
case "$profile" in
core20-sections)
function_sections=1
data_sections=1
;;
esac
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
printf '%s\n' \
"$std_flag" \
-DULAPI \
$([[ "$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) \
$([[ "$data_sections" -eq 1 ]] && printf '%s\n' -fdata-sections) \
$([[ "$include_core_include" -eq 1 ]] && printf '%s\n' -I "$(pwd)/core/include") \
$([[ "$include_core_src" -eq 1 ]] && printf '%s\n' -I "$(pwd)/core/src") \
$([[ "$include_tooldata" -eq 1 ]] && printf '%s\n' -I "$linuxcnc_root/src/emc/tooldata") \
-I "$(pwd)/core/wasm_shims" \
-I "$linuxcnc_root/src" \
-I "$linuxcnc_root/src/emc" \
-I "$linuxcnc_root/src/emc/nml_intf" \
-I "$linuxcnc_root/src/emc/rs274ngc" \
-I "$linuxcnc_root/src/emc/motion" \
-I "$linuxcnc_root/include"

View File

@@ -6,65 +6,11 @@ cd "$(dirname "$0")"
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
filter_group=${2:-core}
output_mode=${3:-relative}
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
case "$filter_group" in
core|blocked|all)
;;
*)
echo "unknown manifest source filter: $filter_group" >&2
exit 1
;;
esac
case "$output_mode" in
relative|full)
;;
*)
echo "unknown manifest source output mode: $output_mode" >&2
exit 1
;;
esac
seen_sources=()
while IFS=: read -r group path note; do
case "$group" in
""|\#*)
continue
;;
core|blocked)
;;
*)
echo "unknown manifest group: $group" >&2
exit 1
;;
esac
if [[ ! -f "$linuxcnc_root/$path" ]]; then
echo "missing manifest source: $path" >&2
exit 1
fi
if printf '%s\n' "${seen_sources[@]}" | grep -Fx -- "$path" >/dev/null; then
echo "duplicate manifest source: $path" >&2
exit 1
fi
seen_sources+=("$path")
if [[ "$filter_group" == "all" || "$filter_group" == "$group" ]]; then
if [[ "$output_mode" == "full" ]]; then
printf '%s\n' "$linuxcnc_root/$path"
else
printf '%s\n' "$path"
fi
fi
done < "$manifest"
./list-linuxcnc-manifest-sources.sh \
"$manifest" \
"$filter_group" \
"$output_mode" \
core,header,blocked,blocked-header \
all \
"missing manifest source"

View File

@@ -5,41 +5,7 @@ cd "$(dirname "$0")"
filter=${1:-}
awk '
/set\(linuxcnc_rs274_wasm_safe_probe_project_sources/ {
in_list = 1
next
}
in_list && /^[[:space:]]*\)/ {
exit
}
in_list {
line = $0
sub(/^[[:space:]]*/, "", line)
sub(/[[:space:]]*$/, "", line)
if (line == "") {
next
}
sub(/^\$\{CMAKE_CURRENT_SOURCE_DIR\}\//, "core/", line)
print line
}
' core/CMakeLists.txt | while IFS= read -r source; do
if [[ ! -f "$source" ]]; then
echo "missing wasm-safe project source: $source" >&2
exit 1
fi
if [[ -n "$filter" && "$(basename "$source")" != "$filter" ]]; then
continue
fi
printf '%s\n' "$source"
done | {
found=0
while IFS= read -r source; do
found=1
printf '%s\n' "$source"
done
if [[ -n "$filter" && "$found" -eq 0 ]]; then
echo "missing wasm-safe project source in CMake list: $filter" >&2
exit 1
fi
}
./list-cmake-set-sources.sh linuxcnc_rs274_wasm_safe_probe_project_sources | ./list-filtered-existing-paths.sh \
"$filter" \
"missing wasm-safe project source" \
"missing wasm-safe project source in CMake list"

View File

@@ -5,41 +5,7 @@ cd "$(dirname "$0")"
filter=${1:-}
awk '
/set\(linuxcnc_wasm_safe_probe_shims/ {
in_list = 1
next
}
in_list && /^[[:space:]]*\)/ {
exit
}
in_list {
line = $0
sub(/^[[:space:]]*/, "", line)
sub(/[[:space:]]*$/, "", line)
if (line == "") {
next
}
sub(/^\$\{CMAKE_CURRENT_SOURCE_DIR\}\//, "core/", line)
print line
}
' core/CMakeLists.txt | while IFS= read -r source; do
if [[ ! -f "$source" ]]; then
echo "missing wasm-safe shim source: $source" >&2
exit 1
fi
if [[ -n "$filter" && "$(basename "$source")" != "$filter" ]]; then
continue
fi
printf '%s\n' "$source"
done | {
found=0
while IFS= read -r source; do
found=1
printf '%s\n' "$source"
done
if [[ -n "$filter" && "$found" -eq 0 ]]; then
echo "missing wasm-safe shim in CMake list: $filter" >&2
exit 1
fi
}
./list-cmake-set-sources.sh linuxcnc_wasm_safe_probe_shims | ./list-filtered-existing-paths.sh \
"$filter" \
"missing wasm-safe shim source" \
"missing wasm-safe shim in CMake list"

45
list-project-source-set.sh Executable file
View File

@@ -0,0 +1,45 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
if [[ "$#" -lt 1 || "$#" -gt 2 ]]; then
echo "usage: $0 <source-set> [filter]" >&2
exit 1
fi
source_set=$1
filter=${2:-}
case "$source_set" in
linuxcnc-rs274-dump)
sources=(
core/src/canon_event_sink.cpp
core/src/linuxcnc_canon_bridge.cpp
core/src/linuxcnc_tooldata_fixture.cpp
core/src/rtcp_kinematics.cpp
core/src/simulator_gcode_controls.cpp
core/tools/linuxcnc_rs274_dump.cpp
)
missing_source_message="missing LinuxCNC rs274 dump project source"
missing_filter_message="missing LinuxCNC rs274 dump project source in list"
;;
linuxcnc-bridge-smoke)
sources=(
core/src/canon_event_sink.cpp
core/src/linuxcnc_canon_bridge.cpp
core/src/rtcp_kinematics.cpp
)
missing_source_message="missing LinuxCNC bridge smoke project source"
missing_filter_message="missing LinuxCNC bridge smoke project source in list"
;;
*)
echo "unknown project source set: $source_set" >&2
exit 1
;;
esac
printf '%s\n' "${sources[@]}" | ./list-filtered-existing-paths.sh \
"$filter" \
"$missing_source_message" \
"$missing_filter_message"

View File

@@ -8,12 +8,128 @@ linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
missing_root=/tmp/does-not-exist-linuxcnc
missing_root_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_missing_root.XXXXXX.log")
missing_manifest=${TMPDIR:-/tmp}/cnc_sim_missing_native_manifest.txt
missing_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_native_manifest.XXXXXX.log")
unknown_group_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_unknown_manifest_group.XXXXXX.txt")
unknown_group_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_unknown_manifest_group.XXXXXX.log")
duplicate_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_duplicate_manifest.XXXXXX.txt")
duplicate_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_duplicate_manifest.XXXXXX.log")
unknown_filter_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_unknown_manifest_filter.XXXXXX.log")
unknown_output_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_unknown_manifest_output.XXXXXX.log")
missing_dump_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_rs274_dump_project_source.XXXXXX.log")
missing_api_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_rs274_api_project_source.XXXXXX.log")
missing_core_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_core_source.XXXXXX.log")
missing_bridge_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_bridge_smoke_project_source.XXXXXX.log")
missing_support_object_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_linuxcnc_support_object.XXXXXX.log")
missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_syntax_missing_source_manifest.XXXXXX.txt")
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_syntax_missing_source.XXXXXX.log")
cleanup_native_test_temps() {
rm -f \
"$missing_root_log" \
"$missing_manifest" \
"$missing_manifest_log" \
"$unknown_group_manifest" \
"$unknown_group_log" \
"$duplicate_manifest" \
"$duplicate_log" \
"$unknown_filter_log" \
"$unknown_output_log" \
"$missing_dump_source_log" \
"$missing_api_source_log" \
"$missing_core_source_log" \
"$missing_bridge_source_log" \
"$missing_support_object_log" \
"$missing_source_manifest" \
"$missing_source_log"
}
trap cleanup_native_test_temps EXIT
grep -Fx 'trap cleanup_native_test_temps EXIT' test-all-native.sh >/dev/null
if ! awk '
/cleanup_native_test_temps\(\)/ { in_cleanup = 1 }
in_cleanup && index($0, "\"$missing_manifest\"") { found_missing_manifest = 1 }
in_cleanup && /^}/ { in_cleanup = 0 }
END { exit !found_missing_manifest }
' test-all-native.sh; then
echo "native aggregate test cleanup no longer removes fixed missing-manifest path" >&2
exit 1
fi
for forbidden_manual_cleanup in \
'rm -f "$missing_root_log"' \
'rm -f "$missing_manifest_log"' \
'rm -f "$unknown_group_manifest" "$unknown_group_log"' \
'rm -f "$duplicate_manifest" "$duplicate_log"' \
'rm -f "$unknown_filter_log"' \
'rm -f "$unknown_output_log"' \
'rm -f "$missing_dump_source_log"' \
'rm -f "$missing_api_source_log"' \
'rm -f "$missing_core_source_log"' \
'rm -f "$missing_bridge_source_log"' \
'rm -f "$missing_support_object_log"' \
'rm -f "$missing_source_manifest" "$missing_source_log"'; do
if grep -Fx "$forbidden_manual_cleanup" test-all-native.sh >/dev/null; then
echo "native aggregate test still relies on manual cleanup: $forbidden_manual_cleanup" >&2
exit 1
fi
done
grep -F 'exec 9>"${TMPDIR:-/tmp}/cnc_sim_rs274_source_link.lock"' test-linuxcnc-source-link.sh >/dev/null
grep -F "flock 9" test-linuxcnc-source-link.sh >/dev/null
grep -F "missing LinuxCNC support object:" list-linuxcnc-source-support-objects.sh >/dev/null
grep -F "missing LinuxCNC support object" list-linuxcnc-source-support-objects.sh >/dev/null
grep -F 'echo "$missing_path_message: $path"' list-filtered-existing-paths.sh >/dev/null
grep -F "build LinuxCNC first or set LINUXCNC_ROOT to a built tree" list-linuxcnc-source-support-objects.sh >/dev/null
native_link_flags=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-native-linkflags.sh)
grep -Fx -- "-L" <<<"$native_link_flags" >/dev/null
grep -Fx -- "$linuxcnc_root/lib" <<<"$native_link_flags" >/dev/null
grep -Fx -- "-Wl,-rpath,$linuxcnc_root/lib" <<<"$native_link_flags" >/dev/null
grep -F 'exec 9>"${TMPDIR:-/tmp}/cnc_sim_linuxcnc_rs274_native.lock"' test-linuxcnc-rs274-native.sh >/dev/null
grep -F "flock 9" test-linuxcnc-rs274-native.sh >/dev/null
for build_dir_probe_script in \
test-linuxcnc-source-objects.sh \
test-linuxcnc-source-link.sh \
test-linuxcnc-bridge-native.sh \
test-linuxcnc-rs274-native.sh \
test-linuxcnc-api-native.sh; do
if ! awk '
index($0, "trap '\''rm -rf \"$build_dir\"'\'' EXIT") { trap_line = NR }
index($0, "./check-linuxcnc-inputs.sh") && !check_line { check_line = NR }
END { exit !(trap_line && check_line && trap_line < check_line) }
' "$build_dir_probe_script"; then
echo "$build_dir_probe_script installs build_dir cleanup after input validation" >&2
exit 1
fi
done
grep -F 'python_include_output=$(python3.13-config --includes 2>/dev/null || python3-config --includes)' list-linuxcnc-source-common-cxxflags.sh >/dev/null
if grep -F 'read -r -a python_includes <<<"$(' list-linuxcnc-source-common-cxxflags.sh >/dev/null; then
echo "source common cxxflags still hides python-config failures behind read" >&2
exit 1
fi
forbidden_common_flags_process_substitution='readarray -t common_flags < <('
forbidden_link_flags_process_substitution='readarray -t link_flags < <('
forbidden_source_common_flags_helper='./list-linuxcnc-source-common-cxxflags.sh'
forbidden_native_link_flags_helper='./list-linuxcnc-native-linkflags.sh'
if grep -RInF "${forbidden_common_flags_process_substitution}LINUXCNC_ROOT=\"\$linuxcnc_root\" ${forbidden_source_common_flags_helper}" test-linuxcnc-*.sh >/dev/null; then
echo "native probes still read source common flags through process substitution" >&2
grep -RInF "${forbidden_common_flags_process_substitution}LINUXCNC_ROOT=\"\$linuxcnc_root\" ${forbidden_source_common_flags_helper}" test-linuxcnc-*.sh >&2
exit 1
fi
if grep -RInF "${forbidden_link_flags_process_substitution}LINUXCNC_ROOT=\"\$linuxcnc_root\" ${forbidden_native_link_flags_helper}" test-linuxcnc-*.sh >/dev/null; then
echo "native probes still read link flags through process substitution" >&2
grep -RInF "${forbidden_link_flags_process_substitution}LINUXCNC_ROOT=\"\$linuxcnc_root\" ${forbidden_native_link_flags_helper}" test-linuxcnc-*.sh >&2
exit 1
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
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
python_source_common_flags=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh --python)
grep -F "include/python" <<<"$python_source_common_flags" >/dev/null
no_ulapi_source_common_flags=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh --no-ulapi)
if grep -Fx -- "-DULAPI" <<<"$no_ulapi_source_common_flags" >/dev/null; then
echo "source common cxxflags emitted ULAPI with --no-ulapi" >&2
exit 1
fi
grep -F "CNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND is native-only; it links LinuxCNC librs274" core/CMakeLists.txt >/dev/null
grep -F "Set CNC_SIM_LINUXCNC_ROOT to the LinuxCNC source root" core/CMakeLists.txt >/dev/null
grep -F "find_package(Python3 REQUIRED COMPONENTS Development)" core/CMakeLists.txt >/dev/null
@@ -34,6 +150,11 @@ grep -F './list-linuxcnc-rs274-api-project-sources.sh > "$project_source_list"'
grep -F './list-cnc-sim-core-sources.sh > "$core_source_list"' test-native.sh >/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
if grep -F 'rm -f "$missing_source_manifest" "$missing_source_log"' test-linuxcnc-source-syntax.sh >/dev/null; then
echo "linuxcnc source syntax probe still relies on manual cleanup for missing-source temporaries" >&2
exit 1
fi
for script in \
test-linuxcnc-source-syntax.sh \
test-linuxcnc-source-objects.sh \
@@ -51,10 +172,7 @@ for script in \
exit 1
fi
done
rm -f "$missing_root_log"
missing_manifest=${TMPDIR:-/tmp}/cnc_sim_missing_native_manifest.txt
missing_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_native_manifest.XXXXXX.log")
rm -f "$missing_manifest"
for script in \
list-linuxcnc-source-manifest-sources.sh \
@@ -71,10 +189,7 @@ for script in \
exit 1
fi
done
rm -f "$missing_manifest_log"
unknown_group_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_unknown_manifest_group.XXXXXX.txt")
unknown_group_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_unknown_manifest_group.XXXXXX.log")
printf 'bogus:src/emc/rs274ngc/interp_arc.cc:test unknown group\n' >"$unknown_group_manifest"
for script in \
list-linuxcnc-source-manifest-sources.sh \
@@ -91,10 +206,7 @@ for script in \
exit 1
fi
done
rm -f "$unknown_group_manifest" "$unknown_group_log"
duplicate_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_duplicate_manifest.XXXXXX.txt")
duplicate_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_duplicate_manifest.XXXXXX.log")
printf 'core:src/emc/rs274ngc/interp_arc.cc:test duplicate source\ncore:src/emc/rs274ngc/interp_arc.cc:test duplicate source\n' >"$duplicate_manifest"
for script in \
list-linuxcnc-source-manifest-sources.sh \
@@ -111,16 +223,20 @@ for script in \
exit 1
fi
done
rm -f "$duplicate_manifest" "$duplicate_log"
./list-linuxcnc-source-manifest-sources.sh "$manifest" core >/dev/null
grep -Fx "src/emc/rs274ngc/interp_arc.cc" \
< <(./list-linuxcnc-source-manifest-sources.sh "$manifest" core) >/dev/null
grep -Fx "src/emc/rs274ngc/interpmodule.cc" \
< <(./list-linuxcnc-source-manifest-sources.sh "$manifest" binding) >/dev/null
grep -Fx "src/emc/tooldata/tooldata_common.cc" \
< <(./list-linuxcnc-source-manifest-sources.sh "$manifest" tooldata) >/dev/null
unknown_filter_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_unknown_manifest_filter.XXXXXX.log")
native_manifest_core=$(./list-linuxcnc-source-manifest-sources.sh "$manifest" core)
grep -Fx "src/emc/rs274ngc/interp_arc.cc" <<<"$native_manifest_core" >/dev/null
native_manifest_binding=$(./list-linuxcnc-source-manifest-sources.sh "$manifest" binding)
grep -Fx "src/emc/rs274ngc/interpmodule.cc" <<<"$native_manifest_binding" >/dev/null
native_manifest_tooldata=$(./list-linuxcnc-source-manifest-sources.sh "$manifest" tooldata)
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
if grep -E '\.(hh|h)$' <<<"$native_manifest_core" >/dev/null; then
echo "native manifest core compile group includes headers" >&2
exit 1
fi
if ./list-linuxcnc-source-manifest-sources.sh "$manifest" bogus >"$unknown_filter_log" 2>&1; then
echo "native manifest source lister accepted unknown filter" >&2
exit 1
@@ -130,8 +246,6 @@ if ! grep -F "unknown manifest source filter: bogus" "$unknown_filter_log" >/dev
sed -n '1,20p' "$unknown_filter_log" >&2
exit 1
fi
rm -f "$unknown_filter_log"
unknown_output_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_unknown_manifest_output.XXXXXX.log")
if ./list-linuxcnc-source-manifest-sources.sh "$manifest" core bogus >"$unknown_output_log" 2>&1; then
echo "native manifest source lister accepted unknown output mode" >&2
exit 1
@@ -141,14 +255,12 @@ if ! grep -F "unknown manifest source output mode: bogus" "$unknown_output_log"
sed -n '1,20p' "$unknown_output_log" >&2
exit 1
fi
rm -f "$unknown_output_log"
grep -Fx "$linuxcnc_root/src/emc/rs274ngc/interp_arc.cc" \
< <(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full) >/dev/null
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
./list-linuxcnc-rs274-dump-project-sources.sh >/dev/null
grep -Fx "core/tools/linuxcnc_rs274_dump.cpp" \
< <(./list-linuxcnc-rs274-dump-project-sources.sh linuxcnc_rs274_dump.cpp) >/dev/null
missing_dump_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_rs274_dump_project_source.XXXXXX.log")
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
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
@@ -158,12 +270,10 @@ if ! grep -F "missing LinuxCNC rs274 dump project source in list: does_not_exist
sed -n '1,20p' "$missing_dump_source_log" >&2
exit 1
fi
rm -f "$missing_dump_source_log"
./list-linuxcnc-rs274-api-project-sources.sh >/dev/null
grep -Fx "core/src/linuxcnc_rs274_backend.cpp" \
< <(./list-linuxcnc-rs274-api-project-sources.sh linuxcnc_rs274_backend.cpp) >/dev/null
missing_api_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_rs274_api_project_source.XXXXXX.log")
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 does_not_exist.cc >"$missing_api_source_log" 2>&1; then
echo "rs274 API project source lister accepted missing source filter" >&2
exit 1
@@ -173,12 +283,10 @@ if ! grep -F "missing LinuxCNC rs274 API project source in CMake list: does_not_
sed -n '1,20p' "$missing_api_source_log" >&2
exit 1
fi
rm -f "$missing_api_source_log"
./list-cnc-sim-core-sources.sh >/dev/null
grep -Fx "core/src/smoke_gcode_parser.cpp" \
< <(./list-cnc-sim-core-sources.sh smoke_gcode_parser.cpp) >/dev/null
missing_core_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_core_source.XXXXXX.log")
cnc_sim_core_source=$(./list-cnc-sim-core-sources.sh 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
exit 1
@@ -188,12 +296,10 @@ if ! grep -F "missing cnc sim core source in CMake list: does_not_exist.cc" "$mi
sed -n '1,20p' "$missing_core_source_log" >&2
exit 1
fi
rm -f "$missing_core_source_log"
./list-linuxcnc-bridge-smoke-project-sources.sh >/dev/null
grep -Fx "core/src/linuxcnc_canon_bridge.cpp" \
< <(./list-linuxcnc-bridge-smoke-project-sources.sh linuxcnc_canon_bridge.cpp) >/dev/null
missing_bridge_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_bridge_smoke_project_source.XXXXXX.log")
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
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
@@ -203,12 +309,10 @@ if ! grep -F "missing LinuxCNC bridge smoke project source in list: does_not_exi
sed -n '1,20p' "$missing_bridge_source_log" >&2
exit 1
fi
rm -f "$missing_bridge_source_log"
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-support-objects.sh >/dev/null
grep -F "/src/objects/emc/rs274ngc/interpmodule.o" \
< <(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-support-objects.sh interpmodule.o) >/dev/null
missing_support_object_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_linuxcnc_support_object.XXXXXX.log")
interpmodule_support_object=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-support-objects.sh interpmodule.o)
grep -F "/src/objects/emc/rs274ngc/interpmodule.o" <<<"$interpmodule_support_object" >/dev/null
if LINUXCNC_ROOT="$missing_root" ./list-linuxcnc-source-support-objects.sh >"$missing_support_object_log" 2>&1; then
echo "LinuxCNC support object lister accepted missing LinuxCNC root: $missing_root" >&2
exit 1
@@ -227,10 +331,7 @@ if ! grep -F "missing LinuxCNC support object in list: does_not_exist.o" "$missi
sed -n '1,20p' "$missing_support_object_log" >&2
exit 1
fi
rm -f "$missing_support_object_log"
missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_syntax_missing_source_manifest.XXXXXX.txt")
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_native_syntax_missing_source.XXXXXX.log")
printf 'core:src/emc/rs274ngc/does_not_exist.cc:test missing source\n' >"$missing_source_manifest"
if ./test-linuxcnc-source-syntax.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
echo "test-linuxcnc-source-syntax.sh accepted missing manifest file" >&2
@@ -241,7 +342,6 @@ if ! grep -F "missing manifest file: src/emc/rs274ngc/does_not_exist.cc" "$missi
sed -n '1,20p' "$missing_source_log" >&2
exit 1
fi
rm -f "$missing_source_manifest" "$missing_source_log"
./test-native.sh
./test-linuxcnc-source-syntax.sh "$manifest"

View File

@@ -7,12 +7,8 @@ linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_native.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
var_file="$build_dir/rs274ngc-api.var"
cp "$linuxcnc_root/tests/halui/jogging/sim.var" "$var_file"
@@ -20,21 +16,22 @@ project_source_list="$build_dir/linuxcnc_rs274_api_project_sources.txt"
./list-linuxcnc-rs274-api-project-sources.sh > "$project_source_list"
mapfile -t project_sources < "$project_source_list"
"$cxx" -std=c++17 \
common_flag_output=$(
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh \
--no-ulapi \
--python \
--core-include \
--core-src
)
mapfile -t common_flags <<<"$common_flag_output"
link_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-native-linkflags.sh)
mapfile -t link_flags <<<"$link_flag_output"
"$cxx" "${common_flags[@]}" \
-DCNC_SIM_ENABLE_LINUXCNC_RS274_BACKEND \
$(python3.13-config --includes 2>/dev/null || python3-config --includes) \
-I core/include \
-I core/src \
-I "$linuxcnc_root/src" \
-I "$linuxcnc_root/src/emc" \
-I "$linuxcnc_root/src/emc/nml_intf" \
-I "$linuxcnc_root/src/emc/rs274ngc" \
-I "$linuxcnc_root/src/emc/motion" \
-I "$linuxcnc_root/include" \
"${project_sources[@]}" \
core/tests/cnc_sim_api_linuxcnc_rs274_smoke.cpp \
-L "$linuxcnc_root/lib" \
-Wl,-rpath,"$linuxcnc_root/lib" \
"${link_flags[@]}" \
-lrs274 \
-ltooldata \
-lpython3.13 \

View File

@@ -7,29 +7,26 @@ linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_native.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
project_source_list="$build_dir/linuxcnc_bridge_smoke_project_sources.txt"
./list-linuxcnc-bridge-smoke-project-sources.sh > "$project_source_list"
mapfile -t project_sources < "$project_source_list"
"$cxx" -std=c++17 \
-I core/include \
-I core/src \
-I "$linuxcnc_root/src/emc" \
-I "$linuxcnc_root/src/emc/nml_intf" \
-I "$linuxcnc_root/src/emc/rs274ngc" \
-I "$linuxcnc_root/src/emc/motion" \
-I "$linuxcnc_root/src" \
-I "$linuxcnc_root/include" \
common_flag_output=$(
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh \
--no-ulapi \
--core-include \
--core-src
)
mapfile -t common_flags <<<"$common_flag_output"
link_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-native-linkflags.sh)
mapfile -t link_flags <<<"$link_flag_output"
"$cxx" "${common_flags[@]}" \
"${project_sources[@]}" \
core/tests/linuxcnc_canon_bridge_smoke.cpp \
-L "$linuxcnc_root/lib" \
-Wl,-rpath,"$linuxcnc_root/lib" \
"${link_flags[@]}" \
-ltooldata \
-o "$build_dir/linuxcnc_canon_bridge_smoke"

View File

@@ -7,12 +7,8 @@ linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_native.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
# The LinuxCNC rs274 backend path can bus-error when two dumps run concurrently.
exec 9>"${TMPDIR:-/tmp}/cnc_sim_linuxcnc_rs274_native.lock"
flock 9
@@ -29,19 +25,20 @@ project_source_list="$build_dir/linuxcnc_rs274_dump_project_sources.txt"
./list-linuxcnc-rs274-dump-project-sources.sh > "$project_source_list"
mapfile -t project_sources < "$project_source_list"
"$cxx" -std=c++17 \
$(python3.13-config --includes 2>/dev/null || python3-config --includes) \
-I core/include \
-I core/src \
-I "$linuxcnc_root/src" \
-I "$linuxcnc_root/src/emc" \
-I "$linuxcnc_root/src/emc/nml_intf" \
-I "$linuxcnc_root/src/emc/rs274ngc" \
-I "$linuxcnc_root/src/emc/motion" \
-I "$linuxcnc_root/include" \
common_flag_output=$(
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh \
--no-ulapi \
--python \
--core-include \
--core-src
)
mapfile -t common_flags <<<"$common_flag_output"
link_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-native-linkflags.sh)
mapfile -t link_flags <<<"$link_flag_output"
"$cxx" "${common_flags[@]}" \
"${project_sources[@]}" \
-L "$linuxcnc_root/lib" \
-Wl,-rpath,"$linuxcnc_root/lib" \
"${link_flags[@]}" \
-lrs274 \
-ltooldata \
-lpython3.13 \
@@ -126,7 +123,7 @@ check(OUTPUT_DIR / "cnc_sim_linuxcnc_basic_mill.json", required_motion | {"tool-
controls = load("cnc_sim_linuxcnc_rtcp_controls.json")
kin = [event for event in controls if event["type"] == "kinematics-switch"]
if [(event["reserved"], event["feed"]) for event in kin] != [(1, 1), (0, 0), (2, 1)]:
if [event["reserved"] for event in kin] != [1, 0, 2]:
raise SystemExit("unexpected kinematics switch sequence")
rtcp = [event for event in controls if event["type"] == "rtcp-state"]
if [(event["line"], event["tool"], event["feed"]) for event in rtcp] != [(3, 0, 0), (4, 7, 1)]:

View File

@@ -6,43 +6,26 @@ cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_rs274_source_link.XXXXXX")
python_includes=$(python3.13-config --includes 2>/dev/null || python3-config --includes)
manifest=${1:-linuxcnc-rs274-source-files.txt}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
# 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
common_flags=(
-std=c++17
-DULAPI
$python_includes
-I core/include
-I core/src
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/src/emc/pythonplugin"
-I "$linuxcnc_root/include"
common_flag_output=$(
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh \
--python \
--core-include \
--core-src
)
mapfile -t common_flags <<<"$common_flag_output"
link_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-native-linkflags.sh)
mapfile -t link_flags <<<"$link_flag_output"
linuxcnc_sources=()
source_list="$build_dir/linuxcnc_core_sources.txt"
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full > "$source_list"
mapfile -t linuxcnc_sources < "$source_list"
source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full)
mapfile -t linuxcnc_sources <<<"$source_output"
objects=()
linuxcnc_index=0
@@ -73,8 +56,7 @@ LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-support-objects.sh > "$sup
mapfile -t support_objects < "$support_object_list"
"$cxx" "${objects[@]}" "${support_objects[@]}" \
-L "$linuxcnc_root/lib" \
-Wl,-rpath,"$linuxcnc_root/lib" \
"${link_flags[@]}" \
-llinuxcnc-uspace-posix \
-llinuxcncini \
-llinuxcnchal \
@@ -812,7 +794,7 @@ if not any(
controls = json.loads((OUTPUT_DIR / "cnc_sim_linuxcnc_source_rtcp_controls.json").read_text())
kin = [event for event in controls if event["type"] == "kinematics-switch"]
if [(event["reserved"], event["feed"]) for event in kin] != [(1, 1), (0, 0), (2, 1)]:
if [event["reserved"] for event in kin] != [1, 0, 2]:
raise SystemExit("unexpected source-linked kinematics switch sequence")
rtcp = [event for event in controls if event["type"] == "rtcp-state"]
if [(event["line"], event["tool"], event["feed"]) for event in rtcp] != [(3, 0, 0), (4, 7, 1)]:

View File

@@ -6,43 +6,21 @@ cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_rs274_objects.XXXXXX")
python_includes=$(python3.13-config --includes 2>/dev/null || python3-config --includes)
manifest=${1:-linuxcnc-rs274-source-files.txt}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
common_flags=(
-std=c++17
-DULAPI
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/src/emc/pythonplugin"
-I "$linuxcnc_root/include"
)
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh --python)
mapfile -t common_flags <<<"$common_flag_output"
sources=()
source_list="$build_dir/linuxcnc_core_sources.txt"
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full > "$source_list"
mapfile -t sources < "$source_list"
source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" core full)
mapfile -t sources <<<"$source_output"
source_index=0
for source in "${sources[@]}"; do
obj="$build_dir/linuxcnc_${source_index}.o"
# shellcheck disable=SC2086
"$cxx" "${common_flags[@]}" $python_includes -c "$source" -o "$obj"
"$cxx" "${common_flags[@]}" -c "$source" -o "$obj"
source_index=$((source_index + 1))
done

View File

@@ -5,25 +5,21 @@ cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
python_includes=$(python3.13-config --includes 2>/dev/null || python3-config --includes)
manifest=${1:-linuxcnc-rs274-source-files.txt}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
source_list=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_source_syntax_sources.XXXXXX.txt")
trap 'rm -f "$source_list"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-manifest-sources.sh "$manifest" all full > "$source_list"
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_rs274_manifest_source.XXXXXX.txt")
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_rs274_manifest_source.XXXXXX.log")
source_list=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_source_syntax_sources.XXXXXX.txt")
trap 'rm -f "$source_list" "$missing_source_manifest" "$missing_source_log"' EXIT
grep -Fx 'trap '\''rm -f "$source_list" "$missing_source_manifest" "$missing_source_log"'\'' EXIT' test-linuxcnc-source-syntax.sh >/dev/null
if awk '/^rm -f "\$missing_source_manifest" "\$missing_source_log"$/{ found = 1 } END { exit !found }' test-linuxcnc-source-syntax.sh >/dev/null; then
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"
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
echo "linuxcnc source object probe accepted missing manifest file" >&2
@@ -43,25 +39,13 @@ if ! grep -F "missing manifest file: src/emc/rs274ngc/does_not_exist.cc" "$missi
sed -n '1,20p' "$missing_source_log" >&2
exit 1
fi
rm -f "$missing_source_manifest" "$missing_source_log"
common_flags=(
-std=c++17
-DULAPI
-fsyntax-only
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/src/emc/pythonplugin"
-I "$linuxcnc_root/include"
)
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-common-cxxflags.sh --python --syntax-only)
mapfile -t common_flags <<<"$common_flag_output"
while IFS= read -r source; do
[[ -z "$source" ]] && continue
# shellcheck disable=SC2086
"$cxx" "${common_flags[@]}" $python_includes "$source"
"$cxx" "${common_flags[@]}" "$source"
done < "$source_list"
echo "linuxcnc rs274 source syntax probe passed"

View File

@@ -7,6 +7,26 @@ manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
missing_manifest=${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocker_manifest.txt
missing_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocker_manifest.XXXXXX.log")
missing_root_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_blocker_missing_root.XXXXXX.log")
missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocker_manifest_source.XXXXXX.txt")
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocker_manifest_source.XXXXXX.log")
duplicate_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_blocker_manifest.XXXXXX.txt")
duplicate_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_blocker_manifest.XXXXXX.log")
missing_replacement_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocked_replacement.XXXXXX.txt")
missing_replacement_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocked_replacement.XXXXXX.log")
trap 'rm -f "$missing_manifest" "$missing_manifest_log" "$missing_root_log" "$missing_source_manifest" "$missing_source_log" "$duplicate_manifest" "$duplicate_manifest_log" "$missing_replacement_manifest" "$missing_replacement_log"' EXIT
if ! awk '
/^trap '\''rm -f "\$missing_manifest" "\$missing_manifest_log" "\$missing_root_log" "\$missing_source_manifest" "\$missing_source_log" "\$duplicate_manifest" "\$duplicate_manifest_log" "\$missing_replacement_manifest" "\$missing_replacement_log"'\'' EXIT$/ { found_trap = 1 }
END { exit !found_trap }
' test-linuxcnc-wasm-blockers.sh; then
echo "wasm blocker scan cleanup trap no longer covers fixed blocker temporaries" >&2
exit 1
fi
if awk '/^rm -f "\$missing_source_manifest" "\$missing_source_log"$/{ found = 1 } END { exit !found }' test-linuxcnc-wasm-blockers.sh >/dev/null; then
echo "wasm blocker scan still relies on manual cleanup for missing-source temporaries" >&2
exit 1
fi
rm -f "$missing_manifest"
if ./analyze-linuxcnc-wasm-blockers.sh "$missing_manifest" >"$missing_manifest_log" 2>&1; then
echo "wasm blocker analyzer accepted missing manifest: $missing_manifest" >&2
@@ -17,10 +37,8 @@ if ! grep -F "missing manifest: $missing_manifest" "$missing_manifest_log" >/dev
sed -n '1,20p' "$missing_manifest_log" >&2
exit 1
fi
rm -f "$missing_manifest_log"
missing_root=/tmp/does-not-exist-linuxcnc
missing_root_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_blocker_missing_root.XXXXXX.log")
if LINUXCNC_ROOT="$missing_root" ./analyze-linuxcnc-wasm-blockers.sh "$manifest" >"$missing_root_log" 2>&1; then
echo "wasm blocker analyzer accepted missing LinuxCNC root" >&2
exit 1
@@ -30,12 +48,9 @@ if ! grep -F "missing LinuxCNC root: $missing_root" "$missing_root_log" >/dev/nu
sed -n '1,20p' "$missing_root_log" >&2
exit 1
fi
rm -f "$missing_root_log"
output=$(./analyze-linuxcnc-wasm-blockers.sh "$manifest")
missing_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocker_manifest_source.XXXXXX.txt")
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocker_manifest_source.XXXXXX.log")
printf 'blocked:src/emc/tooldata/does_not_exist.cc:test missing blocked source\n' >"$missing_source_manifest"
if ./analyze-linuxcnc-wasm-blockers.sh "$missing_source_manifest" >"$missing_source_log" 2>&1; then
echo "wasm blocker analyzer accepted missing manifest source" >&2
@@ -46,10 +61,7 @@ if ! grep -F "missing manifest source: src/emc/tooldata/does_not_exist.cc" "$mis
sed -n '1,20p' "$missing_source_log" >&2
exit 1
fi
rm -f "$missing_source_manifest" "$missing_source_log"
duplicate_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_blocker_manifest.XXXXXX.txt")
duplicate_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_blocker_manifest.XXXXXX.log")
cat >"$duplicate_manifest" <<'EOF'
core:src/emc/rs274ngc/interp_arc.cc:duplicate core entry
core:src/emc/rs274ngc/interp_arc.cc:duplicate core entry
@@ -64,10 +76,7 @@ if ! grep -F "duplicate manifest source: src/emc/rs274ngc/interp_arc.cc" "$dupli
sed -n '1,20p' "$duplicate_manifest_log" >&2
exit 1
fi
rm -f "$duplicate_manifest" "$duplicate_manifest_log"
missing_replacement_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocked_replacement.XXXXXX.txt")
missing_replacement_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_blocked_replacement.XXXXXX.log")
cat >"$missing_replacement_manifest" <<'EOF'
core:src/emc/rs274ngc/interp_arc.cc:core entry
blocked:src/emc/tooldata/tooldata_db.cc:blocked entry without wasm replacement
@@ -81,7 +90,6 @@ if ! grep -F "missing blocked replacement mapping: src/emc/tooldata/tooldata_db.
sed -n '1,20p' "$missing_replacement_log" >&2
exit 1
fi
rm -f "$missing_replacement_manifest" "$missing_replacement_log"
grep -F "[manifest]" <<<"$output" >/dev/null
grep -F "core=25" <<<"$(sed -n '/^\[manifest\]/,/^$/p' <<<"$output")" >/dev/null

View File

@@ -6,20 +6,61 @@ cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_cmake_safe_probe.XXXXXX")
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
missing_manifest=${TMPDIR:-/tmp}/cnc_sim_missing_build_wasm_manifest.txt
missing_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_build_wasm_manifest.XXXXXX.log")
missing_root=/tmp/does-not-exist-linuxcnc
missing_root_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_build_wasm_missing_root.XXXXXX.log")
cleanup_wasm_cmake_probe_temps() {
local temp_path
for temp_path in \
"$build_dir" \
"$missing_manifest" \
"$missing_manifest_log" \
"$missing_root_log" \
"${unknown_manifest_filter_log:-}" \
"${unknown_manifest_output_log:-}" \
"${missing_shim_log:-}" \
"${missing_project_source_log:-}" \
"${missing_wasm_script_manifest:-}" \
"${missing_wasm_script_manifest_log:-}" \
"${missing_source_root:-}" \
"${missing_source_log:-}" \
"${missing_interp_find_root:-}" \
"${missing_interp_find_log:-}" \
"${missing_tooldata_runtime_root:-}" \
"${missing_tooldata_runtime_log:-}" \
"${unknown_group_manifest:-}" \
"${unknown_group_log:-}" \
"${blocker_scan_log:-}" \
"${blocker_report_file:-}" \
"${missing_source_manifest:-}" \
"${empty_partition_log:-}" \
"${empty_core_manifest:-}" \
"${empty_blocked_manifest:-}" \
"${bad_default_manifest_dir:-}" \
"${bad_default_manifest_log:-}" \
"${duplicate_manifest:-}" \
"${duplicate_manifest_log:-}" \
"${comment_manifest:-}" \
"${comment_manifest_log:-}" \
"${duplicate_source_manifest:-}" \
"${duplicate_source_log:-}" \
"${duplicate_syntax_manifest:-}" \
"${duplicate_syntax_log:-}" \
"${duplicate_pre_blocker_manifest:-}" \
"${duplicate_pre_blocker_log:-}" \
"${duplicate_pre_link_manifest:-}" \
"${duplicate_pre_link_log:-}"; do
if [[ -n "$temp_path" ]]; then
rm -rf "$temp_path"
fi
done
}
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
echo "build-wasm accepted missing manifest: $missing_manifest" >&2
@@ -30,10 +71,7 @@ if ! grep -F "missing manifest: $missing_manifest" "$missing_manifest_log" >/dev
sed -n '1,20p' "$missing_manifest_log" >&2
exit 1
fi
rm -f "$missing_manifest_log"
missing_root=/tmp/does-not-exist-linuxcnc
missing_root_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_build_wasm_missing_root.XXXXXX.log")
if 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
@@ -59,21 +97,135 @@ grep -F -- './test-linuxcnc-wasm-python-plugin-link.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
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
fi
grep -Fx 'keep_report_file=0' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null
grep -Fx 'trap cleanup_rs274ngc_pre_blocker_temps EXIT' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null
grep -Fx 'keep_report_file=1' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null
if ! awk '
index($0, "if [[ \"$keep_report_file\" -eq 0 && -n \"${report_file:-}\" ]]; then") { cleanup_line = NR }
index($0, "keep_report_file=1") { keep_line = NR }
index($0, "echo \"wrote $report_file\"") { wrote_line = NR }
END { exit !(cleanup_line && keep_line && wrote_line && cleanup_line < keep_line && keep_line < wrote_line) }
' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh; then
echo "rs274ngc_pre blocker scan does not clean failed reports before preserving successful ones" >&2
exit 1
fi
if ! awk '
index($0, "LINUXCNC_ROOT=\"$linuxcnc_root\" ./check-linuxcnc-inputs.sh \"$manifest\"") { check_line = NR }
index($0, "report_file=$(mktemp ") { report_line = NR }
END { exit !(check_line && report_line && check_line < report_line) }
' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh; then
echo "rs274ngc_pre blocker scan creates its report before input validation" >&2
exit 1
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-plugin-link.sh \
test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh \
test-linuxcnc-wasm-rs274ngc-pre-link.sh \
test-linuxcnc-wasm-rs274ngc-pre-object.sh \
test-linuxcnc-wasm-source-objects.sh \
test-linuxcnc-wasm-tooldata-common-link.sh \
test-linuxcnc-wasm-tooldata-link.sh \
test-linuxcnc-wasm-tooldata-mmap-symbols.sh \
test-linuxcnc-wasm-tooldata-runtime-symbols.sh; do
if ! awk '
$0 ~ /^trap .* EXIT$/ { trap_line = NR }
index($0, "./check-linuxcnc-inputs.sh") && !check_line { check_line = NR }
END { exit !(trap_line && check_line && trap_line < check_line) }
' "$build_dir_probe_script"; then
echo "$build_dir_probe_script installs build_dir cleanup after input validation" >&2
exit 1
fi
done
grep -F 'trap '\''rm -f "$missing_manifest" "$missing_manifest_log" "$missing_root_log" "$missing_source_manifest" "$missing_source_log" "$duplicate_manifest" "$duplicate_manifest_log" "$missing_replacement_manifest" "$missing_replacement_log"'\'' EXIT' test-linuxcnc-wasm-blockers.sh >/dev/null
if grep -F 'rm -f "$missing_source_manifest" "$missing_source_log"' test-linuxcnc-wasm-blockers.sh >/dev/null; then
echo "wasm blocker scan still relies on manual cleanup for missing-source temporaries" >&2
exit 1
fi
grep -Fx 'trap cleanup_wasm_cmake_probe_temps EXIT' test-linuxcnc-wasm-cmake-safe-probe.sh >/dev/null
if ! awk '
/cleanup_wasm_cmake_probe_temps\(\)/ { in_cleanup = 1 }
in_cleanup && index($0, "\"$missing_manifest\"") { found_missing_manifest = 1 }
in_cleanup && index($0, "\"${missing_wasm_script_manifest:-}\"") { found_missing_script_manifest = 1 }
in_cleanup && /^}/ { in_cleanup = 0 }
END { exit !(found_missing_manifest && found_missing_script_manifest) }
' test-linuxcnc-wasm-cmake-safe-probe.sh; then
echo "wasm CMake safe probe cleanup no longer removes fixed missing-manifest paths" >&2
exit 1
fi
for forbidden_manual_cleanup in \
'rm -f "$missing_manifest_log"' \
'rm -f "$unknown_manifest_filter_log"' \
'rm -f "$unknown_manifest_output_log"' \
'rm -f "$missing_shim_log"' \
'rm -f "$missing_project_source_log"' \
'rm -f "$missing_wasm_script_manifest_log"' \
'rm -rf "$missing_source_root"' \
'rm -f "$missing_source_log"' \
'rm -rf "$missing_interp_find_root"' \
'rm -f "$missing_interp_find_log"' \
'rm -rf "$missing_tooldata_runtime_root"' \
'rm -f "$missing_tooldata_runtime_log"' \
'rm -f "$unknown_group_manifest" "$unknown_group_log"' \
'rm -f "$blocker_report_file" "$blocker_scan_log"' \
'rm -f "$missing_source_manifest" "$missing_source_log"' \
'rm -f "$empty_core_manifest" "$empty_blocked_manifest" "$empty_partition_log"' \
'rm -rf "$bad_default_manifest_dir"' \
'rm -f "$bad_default_manifest_log"' \
'rm -f "$duplicate_manifest" "$duplicate_manifest_log"' \
'rm -f "$comment_manifest" "$comment_manifest_log"' \
'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
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
fi
done
forbidden_common_flags_process_substitution='readarray -t common_flags < <('
forbidden_common_flags_helper='./list-linuxcnc-wasm-common-cxxflags.sh'
if grep -RInF "${forbidden_common_flags_process_substitution}LINUXCNC_ROOT=\"\$linuxcnc_root\" ${forbidden_common_flags_helper}" test-linuxcnc-wasm-*.sh >/dev/null; then
echo "wasm probes still read common flags through process substitution" >&2
grep -RInF "${forbidden_common_flags_process_substitution}LINUXCNC_ROOT=\"\$linuxcnc_root\" ${forbidden_common_flags_helper}" test-linuxcnc-wasm-*.sh >&2
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 -- 'emcmake cmake -S core -B build/wasm \' build-wasm.sh >/dev/null
grep -F -- 'default_manifest="$PWD/linuxcnc-rs274-wasm-source-files.txt"' build-wasm.sh >/dev/null
grep -F -- 'LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core > "$manifest_core_sources"' build-wasm.sh >/dev/null
grep -F -- 'LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked > "$manifest_blocked_sources"' 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
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 -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
./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core >/dev/null
grep -Fx "src/emc/rs274ngc/interp_arc.cc" \
< <(./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core) >/dev/null
grep -Fx "src/emc/tooldata/tooldata_mmap.cc" \
< <(./list-linuxcnc-wasm-manifest-sources.sh "$manifest" blocked) >/dev/null
wasm_manifest_core=$(./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core)
grep -Fx "src/emc/rs274ngc/interp_arc.cc" <<<"$wasm_manifest_core" >/dev/null
if grep -E '\.(hh|h)$' <<<"$wasm_manifest_core" >/dev/null; then
echo "wasm manifest core compile group includes headers" >&2
exit 1
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_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)
grep -Fx "src/emc/rs274ngc/boost_pyenum_macros.hh" <<<"$wasm_manifest_blocked_header" >/dev/null
unknown_manifest_filter_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_unknown_wasm_manifest_filter.XXXXXX.log")
if ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" bogus >"$unknown_manifest_filter_log" 2>&1; then
echo "wasm manifest source lister accepted unknown filter" >&2
@@ -84,7 +236,6 @@ if ! grep -F "unknown manifest source filter: bogus" "$unknown_manifest_filter_l
sed -n '1,20p' "$unknown_manifest_filter_log" >&2
exit 1
fi
rm -f "$unknown_manifest_filter_log"
unknown_manifest_output_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_unknown_wasm_manifest_output.XXXXXX.log")
if ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core bogus >"$unknown_manifest_output_log" 2>&1; then
echo "wasm manifest source lister accepted unknown output mode" >&2
@@ -95,20 +246,20 @@ if ! grep -F "unknown manifest source output mode: bogus" "$unknown_manifest_out
sed -n '1,20p' "$unknown_manifest_output_log" >&2
exit 1
fi
rm -f "$unknown_manifest_output_log"
grep -Fx "$linuxcnc_root/src/emc/rs274ngc/interp_arc.cc" \
< <(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full) >/dev/null
grep -Fx "core/wasm_shims/dlfcn.cc" < <(./list-linuxcnc-wasm-safe-shims.sh dlfcn.cc) >/dev/null
grep -Fx "core/wasm_shims/pythonplugin/python_plugin.cc" \
< <(./list-linuxcnc-wasm-safe-shims.sh python_plugin.cc) >/dev/null
grep -Fx "core/wasm_shims/rtapi_compat.cc" \
< <(./list-linuxcnc-wasm-safe-shims.sh rtapi_compat.cc) >/dev/null
grep -Fx "core/wasm_shims/tooldata/tooldata_mmap_backend.cc" \
< <(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc) >/dev/null
grep -Fx "core/wasm_shims/tooldata/tooldata_runtime_stubs.cc" \
< <(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc) >/dev/null
grep -Fx "core/src/linuxcnc_canon_bridge.cpp" \
< <(./list-linuxcnc-wasm-safe-project-sources.sh linuxcnc_canon_bridge.cpp) >/dev/null
wasm_manifest_core_full=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full)
grep -Fx "$linuxcnc_root/src/emc/rs274ngc/interp_arc.cc" <<<"$wasm_manifest_core_full" >/dev/null
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
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)
grep -Fx "core/wasm_shims/tooldata/tooldata_mmap_backend.cc" <<<"$tooldata_mmap_backend_shim" >/dev/null
tooldata_runtime_stubs_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc)
grep -Fx "core/wasm_shims/tooldata/tooldata_runtime_stubs.cc" <<<"$tooldata_runtime_stubs_shim" >/dev/null
wasm_bridge_source=$(./list-linuxcnc-wasm-safe-project-sources.sh linuxcnc_canon_bridge.cpp)
grep -Fx "core/src/linuxcnc_canon_bridge.cpp" <<<"$wasm_bridge_source" >/dev/null
missing_shim_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_safe_shim.XXXXXX.log")
if ./list-linuxcnc-wasm-safe-shims.sh does_not_exist.cc >"$missing_shim_log" 2>&1; then
echo "wasm-safe shim lister accepted missing shim filter" >&2
@@ -119,7 +270,6 @@ if ! grep -F "missing wasm-safe shim in CMake list: does_not_exist.cc" "$missing
sed -n '1,20p' "$missing_shim_log" >&2
exit 1
fi
rm -f "$missing_shim_log"
missing_project_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_safe_project_source.XXXXXX.log")
if ./list-linuxcnc-wasm-safe-project-sources.sh does_not_exist.cc >"$missing_project_source_log" 2>&1; then
echo "wasm-safe project source lister accepted missing source filter" >&2
@@ -130,8 +280,6 @@ if ! grep -F "missing wasm-safe project source in CMake list: does_not_exist.cc"
sed -n '1,20p' "$missing_project_source_log" >&2
exit 1
fi
rm -f "$missing_project_source_log"
rm -f "$missing_root_log"
missing_wasm_script_manifest=${TMPDIR:-/tmp}/cnc_sim_missing_wasm_script_manifest.txt
missing_wasm_script_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_wasm_script_manifest.XXXXXX.log")
@@ -152,7 +300,6 @@ for script in \
exit 1
fi
done
rm -f "$missing_wasm_script_manifest_log"
missing_root_scripts=(
test-linuxcnc-wasm-interp-base-link.sh
@@ -179,7 +326,6 @@ for script in "${missing_root_scripts[@]}"; do
exit 1
fi
done
rm -f "$missing_root_log"
missing_source_root=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_missing_linuxcnc_source_root.XXXXXX")
missing_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_linuxcnc_source.XXXXXX.log")
@@ -207,8 +353,6 @@ for script_and_path in "${fixed_source_scripts[@]}"; do
exit 1
fi
done
rm -rf "$missing_source_root"
rm -f "$missing_source_log"
missing_interp_find_root=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_missing_interp_find_source_root.XXXXXX")
missing_interp_find_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_interp_find_source.XXXXXX.log")
@@ -223,8 +367,6 @@ if ! grep -F "missing LinuxCNC source: src/emc/rs274ngc/interp_find.cc" "$missin
sed -n '1,20p' "$missing_interp_find_log" >&2
exit 1
fi
rm -rf "$missing_interp_find_root"
rm -f "$missing_interp_find_log"
missing_tooldata_runtime_root=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_missing_tooldata_runtime_source_root.XXXXXX")
missing_tooldata_runtime_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_missing_tooldata_runtime_source.XXXXXX.log")
@@ -239,8 +381,6 @@ if ! grep -F "missing LinuxCNC source: src/emc/tooldata/tooldata_nml.cc" "$missi
sed -n '1,20p' "$missing_tooldata_runtime_log" >&2
exit 1
fi
rm -rf "$missing_tooldata_runtime_root"
rm -f "$missing_tooldata_runtime_log"
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")
@@ -270,7 +410,6 @@ for script in \
exit 1
fi
done
rm -f "$unknown_group_manifest" "$unknown_group_log"
blocker_scan_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_wasm_blocker_scan.XXXXXX.log")
./test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >"$blocker_scan_log" 2>&1
@@ -280,7 +419,6 @@ if [[ -z "$blocker_report_file" || ! -f "$blocker_report_file" ]]; then
sed -n '1,20p' "$blocker_scan_log" >&2
exit 1
fi
rm -f "$blocker_report_file" "$blocker_scan_log"
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")
@@ -330,7 +468,6 @@ 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
rm -f "$missing_source_manifest" "$missing_source_log"
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")
@@ -355,12 +492,14 @@ if ! grep -F "unexpected wasm manifest partition: core=1 blocked=0" "$empty_part
sed -n '1,20p' "$empty_partition_log" >&2
exit 1
fi
rm -f "$empty_core_manifest" "$empty_blocked_manifest" "$empty_partition_log"
bad_default_manifest_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_bad_default_wasm_manifest.XXXXXX")
bad_default_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_bad_default_wasm_manifest.XXXXXX.log")
linuxcnc_root_abs=$(cd "$linuxcnc_root" && pwd)
cp build-wasm.sh "$bad_default_manifest_dir/build-wasm.sh"
cp check-linuxcnc-inputs.sh "$bad_default_manifest_dir/check-linuxcnc-inputs.sh"
cp list-linuxcnc-source-files.sh "$bad_default_manifest_dir/list-linuxcnc-source-files.sh"
cp list-linuxcnc-manifest-sources.sh "$bad_default_manifest_dir/list-linuxcnc-manifest-sources.sh"
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"
@@ -373,8 +512,6 @@ if ! grep -F "unexpected wasm manifest partition: core=1 blocked=1" "$bad_defaul
sed -n '1,20p' "$bad_default_manifest_log" >&2
exit 1
fi
rm -rf "$bad_default_manifest_dir"
rm -f "$bad_default_manifest_log"
duplicate_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_manifest.XXXXXX.txt")
duplicate_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_manifest.XXXXXX.log")
@@ -392,7 +529,6 @@ if ! grep -F "duplicate manifest source: src/emc/rs274ngc/interp_arc.cc" "$dupli
sed -n '1,20p' "$duplicate_manifest_log" >&2
exit 1
fi
rm -f "$duplicate_manifest" "$duplicate_manifest_log"
comment_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_comment_wasm_manifest.XXXXXX.txt")
comment_manifest_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_comment_wasm_manifest.XXXXXX.log")
@@ -410,7 +546,6 @@ if grep -F "duplicate manifest source" "$comment_manifest_log" >/dev/null; then
sed -n '1,20p' "$comment_manifest_log" >&2
exit 1
fi
rm -f "$comment_manifest" "$comment_manifest_log"
duplicate_source_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_source_manifest.XXXXXX.txt")
duplicate_source_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_source_manifest.XXXXXX.log")
@@ -428,7 +563,6 @@ if ! grep -F "duplicate manifest source: src/emc/rs274ngc/interp_arc.cc" "$dupli
sed -n '1,20p' "$duplicate_source_log" >&2
exit 1
fi
rm -f "$duplicate_source_manifest" "$duplicate_source_log"
duplicate_syntax_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_syntax_manifest.XXXXXX.txt")
duplicate_syntax_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_syntax_manifest.XXXXXX.log")
@@ -446,7 +580,6 @@ if ! grep -F "duplicate manifest source: src/emc/rs274ngc/interp_arc.cc" "$dupli
sed -n '1,20p' "$duplicate_syntax_log" >&2
exit 1
fi
rm -f "$duplicate_syntax_manifest" "$duplicate_syntax_log"
duplicate_pre_blocker_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_pre_blocker_manifest.XXXXXX.txt")
duplicate_pre_blocker_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_pre_blocker_manifest.XXXXXX.log")
@@ -464,7 +597,6 @@ if ! grep -F "duplicate manifest source: src/emc/rs274ngc/interp_arc.cc" "$dupli
sed -n '1,20p' "$duplicate_pre_blocker_log" >&2
exit 1
fi
rm -f "$duplicate_pre_blocker_manifest" "$duplicate_pre_blocker_log"
duplicate_pre_link_manifest=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_pre_link_manifest.XXXXXX.txt")
duplicate_pre_link_log=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_duplicate_wasm_pre_link_manifest.XXXXXX.log")
@@ -482,7 +614,6 @@ if ! grep -F "duplicate manifest source: src/emc/rs274ngc/interp_arc.cc" "$dupli
sed -n '1,20p' "$duplicate_pre_link_log" >&2
exit 1
fi
rm -f "$duplicate_pre_link_manifest" "$duplicate_pre_link_log"
linuxcnc_root=$(cd "$linuxcnc_root" && pwd)
default_manifest="$PWD/linuxcnc-rs274-wasm-source-files.txt"

View File

@@ -7,26 +7,14 @@ linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_interp_base_link.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
dlfcn_shim=$(./list-linuxcnc-wasm-safe-shims.sh dlfcn.cc)
interp_base_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/rs274ngc/interp_base.cc)
common_flags=(
-std=c++17
-DULAPI
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh)
mapfile -t common_flags <<<"$common_flag_output"
"$cxx" "${common_flags[@]}" \
"$dlfcn_shim" \

View File

@@ -7,34 +7,21 @@ linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_interp_find_link.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc)
tooldata_runtime_stubs_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc)
rtapi_compat_shim=$(./list-linuxcnc-wasm-safe-shims.sh rtapi_compat.cc)
readarray -t linuxcnc_sources < <(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh \
linuxcnc_source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh \
src/emc/tooldata/tooldata_common.cc \
src/emc/rs274ngc/interp_find.cc)
mapfile -t linuxcnc_sources <<<"$linuxcnc_source_output"
tooldata_common_source=${linuxcnc_sources[0]}
interp_find_source=${linuxcnc_sources[1]}
common_flags=(
-std=c++17
-DULAPI
-ffunction-sections
-fdata-sections
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --function-sections --data-sections)
mapfile -t common_flags <<<"$common_flag_output"
"$cxx" "${common_flags[@]}" \
-c "$tooldata_common_source" \

View File

@@ -7,25 +7,13 @@ linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_python_plugin_link.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
python_plugin_shim=$(./list-linuxcnc-wasm-safe-shims.sh python_plugin.cc)
common_flags=(
-std=c++17
-DULAPI
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh)
mapfile -t common_flags <<<"$common_flag_output"
"$cxx" "${common_flags[@]}" \
"$python_plugin_shim" \

View File

@@ -6,48 +6,52 @@ cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_rs274ngc_pre_link_blockers.XXXXXX")
report_file=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_rs274ngc_pre_undefined.XXXXXX.txt")
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
keep_report_file=0
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
cleanup_rs274ngc_pre_blocker_temps() {
rm -rf "$build_dir"
if [[ "$keep_report_file" -eq 0 && -n "${report_file:-}" ]]; then
rm -f "$report_file"
fi
}
trap cleanup_rs274ngc_pre_blocker_temps EXIT
grep -Fx 'trap cleanup_rs274ngc_pre_blocker_temps EXIT' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh >/dev/null
if awk '/^} > "\$report_file" \|\| true$/{ found = 1 } END { exit !found }' 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
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
if ! awk '
index($0, "LINUXCNC_ROOT=\"$linuxcnc_root\" ./check-linuxcnc-inputs.sh \"$manifest\"") { check_line = NR }
index($0, "report_file=$(mktemp ") { report_line = NR }
END { exit !(check_line && report_line && check_line < report_line) }
' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh; then
echo "rs274ngc_pre blocker scan creates its report before input validation" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
if ! awk '
index($0, "if [[ \"$keep_report_file\" -eq 0 && -n \"${report_file:-}\" ]]; then") { cleanup_line = NR }
index($0, "keep_report_file=1") { keep_line = NR }
index($0, "echo \"wrote $report_file\"") { wrote_line = NR }
END { exit !(cleanup_line && keep_line && wrote_line && cleanup_line < keep_line && keep_line < wrote_line) }
' test-linuxcnc-wasm-rs274ngc-pre-link-blockers.sh; then
echo "rs274ngc_pre blocker scan does not clean failed reports before preserving successful ones" >&2
exit 1
fi
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
report_file=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_rs274ngc_pre_undefined.XXXXXX.txt")
common_flags=(
-std=c++20
-DULAPI
-ffunction-sections
-fdata-sections
-include wctype.h
-I "$(pwd)/core/include"
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20-sections)
mapfile -t common_flags <<<"$common_flag_output"
shim_list="$build_dir/wasm_safe_shims.txt"
./list-linuxcnc-wasm-safe-shims.sh > "$shim_list"
mapfile -t shim_sources < "$shim_list"
shim_output=$(./list-linuxcnc-wasm-safe-shims.sh)
mapfile -t shim_sources <<<"$shim_output"
project_source_list="$build_dir/wasm_safe_project_sources.txt"
./list-linuxcnc-wasm-safe-project-sources.sh > "$project_source_list"
mapfile -t project_sources < "$project_source_list"
project_source_output=$(./list-linuxcnc-wasm-safe-project-sources.sh)
mapfile -t project_sources <<<"$project_source_output"
linuxcnc_sources=()
source_list="$build_dir/wasm_core_sources.txt"
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full > "$source_list"
mapfile -t linuxcnc_sources < "$source_list"
source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full)
mapfile -t linuxcnc_sources <<<"$source_output"
shim_index=0
for source in "${shim_sources[@]}"; do
@@ -87,11 +91,14 @@ nm --defined-only "$build_dir"/*.o \
{
comm -23 "$undefined_file" "$defined_file" \
| grep -Ev '^(_GLOBAL_OFFSET_TABLE_|_Unwind_Resume)$' \
| grep -Ev '^(__assert_fail|__cxa_|__divdc3|__dso_handle|__errno_location|__gxx_personality_v0|__isoc23_|__muldc3|__stack_chk_fail)' \
| grep -Ev '^(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)$' \
| grep -Ev '^(_ZN3fmt|_ZSt|_ZNSt|_ZNKSt|_ZNKRSt|_ZNS|_ZTI|_ZTS|_ZTV|_Zdl|_Znwm|_Znam|_Zda|_ZdaPv|_ZdaPvm|_ZdlPvm|_ZTv|_ZTh)'
} > "$report_file" || true
| 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 ~ /^(_ZN3fmt|_ZSt|_ZNSt|_ZNKSt|_ZNKRSt|_ZNS|_ZTI|_ZTS|_ZTV|_Zdl|_Znwm|_Znam|_Zda|_ZdaPv|_ZdaPvm|_ZdlPvm|_ZTv|_ZTh)/ { next }
{ print }
'
} > "$report_file"
if grep -F "_ZN6Interp13read_commentEPcPiP12block_structPd" "$report_file" >/dev/null; then
echo "unexpected unresolved read_comment after interp_read.cc" >&2
@@ -139,4 +146,5 @@ if [[ -s "$report_file" ]]; then
fi
echo "linuxcnc wasm rs274ngc_pre link blocker scan passed"
keep_report_file=1
echo "wrote $report_file"

View File

@@ -8,45 +8,20 @@ cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_rs274ngc_pre_link.XXXXXX")
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
common_flags=(
-std=c++20
-DULAPI
-ffunction-sections
-fdata-sections
-I "$(pwd)/core/src"
-include wctype.h
-I "$(pwd)/core/include"
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20-sections)
mapfile -t common_flags <<<"$common_flag_output"
shim_list="$build_dir/wasm_safe_shims.txt"
./list-linuxcnc-wasm-safe-shims.sh > "$shim_list"
mapfile -t sources < "$shim_list"
project_source_list="$build_dir/wasm_safe_project_sources.txt"
./list-linuxcnc-wasm-safe-project-sources.sh > "$project_source_list"
mapfile -t project_sources < "$project_source_list"
shim_output=$(./list-linuxcnc-wasm-safe-shims.sh)
mapfile -t sources <<<"$shim_output"
project_source_output=$(./list-linuxcnc-wasm-safe-project-sources.sh)
mapfile -t project_sources <<<"$project_source_output"
sources+=("${project_sources[@]}")
source_list="$build_dir/wasm_core_sources.txt"
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full > "$source_list"
mapfile -t linuxcnc_sources < "$source_list"
source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full)
mapfile -t linuxcnc_sources <<<"$source_output"
for source in "${linuxcnc_sources[@]}"; do
sources+=("$source")
done

View File

@@ -7,24 +7,12 @@ linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_rs274ngc_pre_object.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
rs274ngc_pre_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/rs274ngc/rs274ngc_pre.cc)
common_flags=(
-std=c++17
-DULAPI
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh)
mapfile -t common_flags <<<"$common_flag_output"
"$cxx" "${common_flags[@]}" \
-c "$rs274ngc_pre_source" \

View File

@@ -8,39 +8,17 @@ manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_safe_probe.XXXXXX")
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
shim_list="$build_dir/wasm_safe_shims.txt"
./list-linuxcnc-wasm-safe-shims.sh > "$shim_list"
mapfile -t shim_sources < "$shim_list"
shim_output=$(./list-linuxcnc-wasm-safe-shims.sh)
mapfile -t shim_sources <<<"$shim_output"
common_flags=(
-std=c++20
-DULAPI
-include wctype.h
-I "$(pwd)/core/include"
-I "$(pwd)/core/src"
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20)
mapfile -t common_flags <<<"$common_flag_output"
source_list="$build_dir/wasm_core_sources.txt"
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full > "$source_list"
mapfile -t sources < "$source_list"
source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full)
mapfile -t sources <<<"$source_output"
shim_index=0
for source in "${shim_sources[@]}"; do

View File

@@ -7,36 +7,13 @@ linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
manifest=${1:-linuxcnc-rs274-wasm-source-files.txt}
cxx=${CXX:-g++}
if [[ ! -f "$manifest" ]]; then
echo "missing manifest: $manifest" >&2
exit 1
fi
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh "$manifest"
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --profile core20 --syntax-only)
mapfile -t common_flags <<<"$common_flag_output"
common_flags=(
-std=c++20
-DULAPI
-fsyntax-only
-include wctype.h
-I "$(pwd)/core/include"
-I "$(pwd)/core/src"
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
source_list=$(mktemp "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_source_syntax_sources.XXXXXX.txt")
trap 'rm -f "$source_list"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full > "$source_list"
mapfile -t sources < "$source_list"
source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-manifest-sources.sh "$manifest" core full)
mapfile -t sources <<<"$source_output"
for source in "${sources[@]}"; do
"$cxx" "${common_flags[@]}" "$source"

View File

@@ -7,27 +7,15 @@ linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_tooldata_common_link.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc)
tooldata_runtime_stubs_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc)
tooldata_common_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/tooldata/tooldata_common.cc)
common_flags=(
-std=c++17
-DULAPI
-I "$linuxcnc_root/src/emc/tooldata"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh --tooldata-include)
mapfile -t common_flags <<<"$common_flag_output"
"$cxx" "${common_flags[@]}" \
"$tooldata_common_source" \

View File

@@ -7,27 +7,15 @@ linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_tooldata_link.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc)
tooldata_runtime_stubs_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc)
tooldata_common_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/tooldata/tooldata_common.cc)
common_flags=(
-std=c++17
-DULAPI
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh)
mapfile -t common_flags <<<"$common_flag_output"
"$cxx" "${common_flags[@]}" \
"$tooldata_common_source" \

View File

@@ -7,26 +7,14 @@ linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_tooldata_mmap_symbols.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc)
tooldata_mmap_source=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh src/emc/tooldata/tooldata_mmap.cc)
common_flags=(
-std=c++17
-DULAPI
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh)
mapfile -t common_flags <<<"$common_flag_output"
"$cxx" "${common_flags[@]}" \
-c "$tooldata_mmap_backend_shim" \

View File

@@ -7,31 +7,20 @@ linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=$(mktemp -d "${TMPDIR:-/tmp}/cnc_sim_linuxcnc_wasm_tooldata_runtime_symbols.XXXXXX")
if [[ ! -d "$linuxcnc_root" ]]; then
echo "missing LinuxCNC root: $linuxcnc_root" >&2
exit 1
fi
trap 'rm -rf "$build_dir"' EXIT
LINUXCNC_ROOT="$linuxcnc_root" ./check-linuxcnc-inputs.sh --root-only
tooldata_mmap_backend_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_mmap_backend.cc)
tooldata_runtime_stubs_shim=$(./list-linuxcnc-wasm-safe-shims.sh tooldata_runtime_stubs.cc)
readarray -t linuxcnc_sources < <(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh \
linuxcnc_source_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-source-files.sh \
src/emc/tooldata/tooldata_db.cc \
src/emc/tooldata/tooldata_nml.cc)
mapfile -t linuxcnc_sources <<<"$linuxcnc_source_output"
tooldata_db_source=${linuxcnc_sources[0]}
tooldata_nml_source=${linuxcnc_sources[1]}
common_flags=(
-std=c++17
-DULAPI
-I "$(pwd)/core/wasm_shims"
-I "$linuxcnc_root/src"
-I "$linuxcnc_root/src/emc"
-I "$linuxcnc_root/src/emc/nml_intf"
-I "$linuxcnc_root/src/emc/rs274ngc"
-I "$linuxcnc_root/src/emc/motion"
-I "$linuxcnc_root/include"
)
common_flag_output=$(LINUXCNC_ROOT="$linuxcnc_root" ./list-linuxcnc-wasm-common-cxxflags.sh)
mapfile -t common_flags <<<"$common_flag_output"
"$cxx" "${common_flags[@]}" \
-c "$tooldata_runtime_stubs_shim" \