Files
wasm-simulator/test-linuxcnc-source-link.sh
2026-05-22 16:19:55 +08:00

278 lines
9.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
linuxcnc_root=${LINUXCNC_ROOT:-../linuxcnc}
cxx=${CXX:-g++}
build_dir=${TMPDIR:-/tmp}/cnc_sim_rs274_source_link
python_includes=$(python3.13-config --includes 2>/dev/null || python3-config --includes)
rm -rf "$build_dir"
mkdir -p "$build_dir"
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"
)
objects=()
while IFS=: read -r group path note; do
case "$group" in
core)
obj="$build_dir/$(basename "$path" .cc).o"
"$cxx" "${common_flags[@]}" -c "$linuxcnc_root/$path" -o "$obj"
objects+=("$obj")
;;
""|\#*|binding|tooldata)
;;
*)
echo "unknown manifest group: $group" >&2
exit 1
;;
esac
done < linuxcnc-rs274-source-files.txt
for source in \
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; do
obj="$build_dir/$(basename "$source" .cpp).o"
"$cxx" "${common_flags[@]}" -c "$source" -o "$obj"
objects+=("$obj")
done
support_objects=(
"$linuxcnc_root/src/objects/emc/rs274ngc/interpmodule.o"
"$linuxcnc_root/src/objects/emc/rs274ngc/canonmodule.o"
"$linuxcnc_root/src/objects/emc/rs274ngc/pyarrays.o"
"$linuxcnc_root/src/objects/emc/rs274ngc/pyblock.o"
"$linuxcnc_root/src/objects/emc/rs274ngc/pyemctypes.o"
"$linuxcnc_root/src/objects/emc/rs274ngc/pyinterp1.o"
"$linuxcnc_root/src/objects/emc/rs274ngc/pyparamclass.o"
"$linuxcnc_root/src/objects/emc/nml_intf/emcops.o"
"$linuxcnc_root/src/objects/emc/sai/dummyemcstat.o"
"$linuxcnc_root/src/objects/libnml/nml/stat_msg.o"
)
for obj in "${support_objects[@]}"; do
if [[ ! -f "$obj" ]]; then
echo "missing LinuxCNC support object: $obj" >&2
echo "build LinuxCNC first or set LINUXCNC_ROOT to a built tree" >&2
exit 1
fi
done
"$cxx" "${objects[@]}" "${support_objects[@]}" \
-L "$linuxcnc_root/lib" \
-Wl,-rpath,"$linuxcnc_root/lib" \
-llinuxcnc-uspace-posix \
-llinuxcncini \
-llinuxcnchal \
-lpyplugin \
-ltooldata \
-lnml \
-lposemath \
-lboost_python313 \
-lpython3.13 \
-lfmt \
-ldl \
-o "$build_dir/linuxcnc_rs274_source_dump"
var_file="$build_dir/rs274ngc-source.var"
cp "$linuxcnc_root/tests/halui/jogging/sim.var" "$var_file"
CNC_SIM_RS274_VAR="$var_file" \
"$build_dir/linuxcnc_rs274_source_dump" tests/gcode/basic_mill.ngc \
>/tmp/cnc_sim_linuxcnc_source_basic_mill.json
CNC_SIM_RS274_VAR="$var_file" \
"$build_dir/linuxcnc_rs274_source_dump" tests/gcode/linuxcnc_rtcp_controls.ngc \
>/tmp/cnc_sim_linuxcnc_source_rtcp_controls.json
CNC_SIM_RS274_VAR="$var_file" \
"$build_dir/linuxcnc_rs274_source_dump" tests/gcode/linuxcnc_tool_length.ngc \
>/tmp/cnc_sim_linuxcnc_source_tool_length.json
CNC_SIM_RS274_VAR="$var_file" \
"$build_dir/linuxcnc_rs274_source_dump" tests/gcode/linuxcnc_canned_cycle.ngc \
>/tmp/cnc_sim_linuxcnc_source_canned_cycle.json
CNC_SIM_RS274_VAR="$var_file" \
"$build_dir/linuxcnc_rs274_source_dump" tests/gcode/linuxcnc_canned_cycles_extended.ngc \
>/tmp/cnc_sim_linuxcnc_source_canned_cycles_extended.json
CNC_SIM_RS274_VAR="$var_file" \
"$build_dir/linuxcnc_rs274_source_dump" tests/gcode/linuxcnc_cutter_comp.ngc \
>/tmp/cnc_sim_linuxcnc_source_cutter_comp.json
CNC_SIM_RS274_FILE_MODE=1 CNC_SIM_RS274_VAR="$var_file" \
"$build_dir/linuxcnc_rs274_source_dump" tests/gcode/smoke_oword_subprogram.ngc \
>/tmp/cnc_sim_linuxcnc_source_oword_subprogram.json
python3 - <<'PY'
import json
from pathlib import Path
events = json.loads(Path("/tmp/cnc_sim_linuxcnc_source_basic_mill.json").read_text())
types = {event["type"] for event in events}
required = {
"set-units",
"set-plane",
"set-spindle",
"tool-change",
"rapid",
"set-feed",
"linear-feed",
"arc-feed",
"program-end",
}
missing = sorted(required - types)
if missing:
raise SystemExit("missing source-linked events: " + ", ".join(missing))
final_motion = [event for event in events if event["type"] in {"rapid", "linear-feed", "arc-feed"}][-1]
if final_motion["end"]["x"] != 0 or final_motion["end"]["y"] != 0:
raise SystemExit("unexpected source-linked final XY position")
controls = json.loads(Path("/tmp/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)]:
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)]:
raise SystemExit("unexpected source-linked RTCP state sequence")
tool_length = json.loads(Path("/tmp/cnc_sim_linuxcnc_source_tool_length.json").read_text())
if not any(
event["type"] == "comment" and
event["line"] == 2 and
event["reserved"] == 430 and
event["start"]["z"] == 12.5
for event in tool_length
):
raise SystemExit("missing source-linked G43 H1 tool length offset event")
if not any(
event["type"] == "comment" and
event["line"] == 3 and
event["reserved"] == 430 and
event["start"]["z"] == 0
for event in tool_length
):
raise SystemExit("missing source-linked G49 tool length clear event")
cycle = json.loads(Path("/tmp/cnc_sim_linuxcnc_source_canned_cycle.json").read_text())
motions = [event for event in cycle if event["type"] in {"rapid", "linear-feed"}]
expected_cycle = [
("rapid", 0, 0, 5),
("rapid", 20, 0, 5),
("rapid", 20, 0, 3),
("linear-feed", 20, 0, -2),
("rapid", 20, 0, 3),
]
actual_cycle = [(event["type"], event["end"]["x"], event["end"]["y"], event["end"]["z"]) for event in motions]
if actual_cycle != expected_cycle:
raise SystemExit(f"unexpected source-linked G81/G80 expansion: {actual_cycle!r}")
extended_cycle = json.loads(Path("/tmp/cnc_sim_linuxcnc_source_canned_cycles_extended.json").read_text())
if not any(
event["type"] == "linear-feed" and
event["line"] == 3 and
event["end"]["x"] == 10 and
event["end"]["z"] == -2
for event in extended_cycle
):
raise SystemExit("missing source-linked G82 feed to bottom")
if not any(
event["type"] == "dwell" and
event["line"] == 3 and
event["dwellSeconds"] == 0.25
for event in extended_cycle
):
raise SystemExit("missing source-linked G82 dwell at bottom")
g83_feed_depths = [
event["end"]["z"]
for event in extended_cycle
if event["type"] == "linear-feed" and event["line"] == 4
]
if g83_feed_depths != [1, 0, -1, -2, -3]:
raise SystemExit(f"unexpected source-linked G83 peck depths: {g83_feed_depths!r}")
if not any(
event["type"] == "rapid" and
event["line"] == 4 and
event["end"]["x"] == 20 and
event["end"]["z"] == 2
for event in extended_cycle
):
raise SystemExit("missing source-linked G83 final retract to R plane")
cutter_comp = json.loads(Path("/tmp/cnc_sim_linuxcnc_source_cutter_comp.json").read_text())
cutter_comp_motions = [
event
for event in cutter_comp
if event["type"] in {"rapid", "linear-feed", "arc-feed"}
]
expected_cutter_comp = [
("rapid", 2, 0, 0),
("linear-feed", 6, 17, 3),
("linear-feed", 7, 17, 20),
("linear-feed", 8, 0, 0),
("linear-feed", 11, 17, -3),
("linear-feed", 12, 17, -20),
("linear-feed", 13, 0, 0),
("linear-feed", 16, 17, 3),
("linear-feed", 17, 17, 20),
("linear-feed", 18, 0, 0),
("linear-feed", 21, 17, -3),
("linear-feed", 22, 17, -20),
("linear-feed", 23, 0, 0),
]
actual_cutter_comp = [
(event["type"], event["line"], event["end"]["x"], event["end"]["y"])
for event in cutter_comp_motions
]
if actual_cutter_comp != expected_cutter_comp:
raise SystemExit(f"unexpected source-linked cutter compensation path: {actual_cutter_comp!r}")
oword = json.loads(Path("/tmp/cnc_sim_linuxcnc_source_oword_subprogram.json").read_text())
if not any(
event["type"] == "linear-feed" and
event["line"] == 8 and
event["start"]["x"] == 0 and
event["end"]["x"] == 7
for event in oword
):
raise SystemExit("missing source-linked O-word expression motion")
if not any(
event["type"] == "linear-feed" and
event["line"] == 9 and
event["start"]["y"] == 0 and
event["end"]["y"] == 2
for event in oword
):
raise SystemExit("missing source-linked O-word #2 argument motion")
if any(
event["type"] == "linear-feed" and
event["line"] == 11 and
event["end"]["x"] == 99
for event in oword
):
raise SystemExit("source-linked O-word return should skip remaining subprogram body")
PY
echo "linuxcnc rs274 source link smoke passed (${#objects[@]} local objects)"
echo "dumped /tmp/cnc_sim_linuxcnc_source_basic_mill.json"
echo "dumped /tmp/cnc_sim_linuxcnc_source_rtcp_controls.json"
echo "dumped /tmp/cnc_sim_linuxcnc_source_tool_length.json"
echo "dumped /tmp/cnc_sim_linuxcnc_source_canned_cycle.json"
echo "dumped /tmp/cnc_sim_linuxcnc_source_canned_cycles_extended.json"
echo "dumped /tmp/cnc_sim_linuxcnc_source_cutter_comp.json"
echo "dumped /tmp/cnc_sim_linuxcnc_source_oword_subprogram.json"