#!/usr/bin/env bash set -euo pipefail ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)" UPSTREAM_RS274="$ROOT_DIR/../linuxcnc/bin/rs274" GCODE_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/gcode" CANON_FIXTURE_DIR="$ROOT_DIR/tests/fixtures/canon" FIXTURES=( minimal_linear length_units plane_selection modal_incremental ) if [[ ! -x "$UPSTREAM_RS274" ]]; then echo "missing upstream rs274 executable: $UPSTREAM_RS274" >&2 exit 1 fi TMP_DIR="$(mktemp -d)" trap 'rm -rf "$TMP_DIR"' EXIT normalize_standalone_events() { awk ' function norm(value) { return sprintf("%.12g", value + 0) } /^canon_event=/ { line = $0 sub(/^canon_event=/, "", line) if (line ~ /^SELECT_PLANE plane=/) { value = line sub(/^SELECT_PLANE plane=/, "", value) if (value == "1") { print "SELECT_PLANE|XY" } else if (value == "2") { print "SELECT_PLANE|YZ" } else if (value == "3") { print "SELECT_PLANE|XZ" } next } if (line ~ /^USE_LENGTH_UNITS units=/) { value = line sub(/^USE_LENGTH_UNITS units=/, "", value) if (value == "1") { print "USE_LENGTH_UNITS|INCHES" } else if (value == "2") { print "USE_LENGTH_UNITS|MM" } next } if (line ~ /^SET_FEED_RATE rate=/) { value = line sub(/^SET_FEED_RATE rate=/, "", value) print "SET_FEED_RATE|" norm(value) next } if (line ~ /^STRAIGHT_(TRAVERSE|FEED) /) { split(line, fields, /[[:space:]]+/) for (i in fields) { split(fields[i], pair, "=") values[pair[1]] = pair[2] } print fields[1] "|" norm(values["x"]) "|" norm(values["y"]) "|" \ norm(values["z"]) "|" norm(values["a"]) "|" norm(values["b"]) "|" \ norm(values["c"]) delete values next } } ' } normalize_upstream_rs274_output() { awk ' function norm(value) { return sprintf("%.12g", value + 0) } function args_of(call, args) { args = call sub(/^[^(]+\(/, "", args) sub(/\).*$/, "", args) gsub(/[[:space:]]/, "", args) return args } /^[[:space:]]*[0-9]+[[:space:]]+N\.\.\.\.\./ { line = $0 sub(/^[[:space:]]*[0-9]+[[:space:]]+N\.\.\.\.\.[[:space:]]+/, "", line) if (line == "ON_RESET()") { seen_reset = 1 next } if (!seen_reset) { next } if (line ~ /^SELECT_PLANE\(CANON_PLANE_/) { value = line sub(/^SELECT_PLANE\(CANON_PLANE_/, "", value) sub(/\)$/, "", value) print "SELECT_PLANE|" value next } if (line ~ /^USE_LENGTH_UNITS\(CANON_UNITS_/) { value = line sub(/^USE_LENGTH_UNITS\(CANON_UNITS_/, "", value) sub(/\)$/, "", value) print "USE_LENGTH_UNITS|" value next } if (line ~ /^SET_FEED_RATE\(/) { args = args_of(line) print "SET_FEED_RATE|" norm(args) next } if (line ~ /^STRAIGHT_(TRAVERSE|FEED)\(/) { op = line sub(/\(.*/, "", op) args = args_of(line) split(args, values, ",") print op "|" norm(values[1]) "|" norm(values[2]) "|" norm(values[3]) "|" \ norm(values[4]) "|" norm(values[5]) "|" norm(values[6]) next } } ' } for name in "${FIXTURES[@]}"; do gcode_file="$GCODE_FIXTURE_DIR/$name.ngc" expected_file="$CANON_FIXTURE_DIR/$name.events" native_raw="$TMP_DIR/$name.rs274.raw" native_norm="$TMP_DIR/$name.rs274.normalized" expected_norm="$TMP_DIR/$name.expected.normalized" if [[ ! -f "$gcode_file" ]]; then echo "missing G-code fixture: $gcode_file" >&2 exit 1 fi if [[ ! -f "$expected_file" ]]; then echo "missing canonical fixture: $expected_file" >&2 exit 1 fi set +e "$UPSTREAM_RS274" -g "$gcode_file" > "$native_raw" 2>&1 rs274_rc=$? set -e if [[ "$rs274_rc" != "0" ]] && ! grep -Fq "File ended with no percent sign (%) or program end (M2)" "$native_raw"; then echo "upstream rs274 failed for fixture: $name" >&2 sed -n '1,160p' "$native_raw" >&2 exit 1 fi normalize_upstream_rs274_output < "$native_raw" > "$native_norm" normalize_standalone_events < "$expected_file" > "$expected_norm" if [[ ! -s "$native_norm" ]]; then echo "no normalized upstream rs274 events for fixture: $name" >&2 sed -n '1,160p' "$native_raw" >&2 exit 1 fi if ! diff -u "$expected_norm" "$native_norm"; then echo "native LinuxCNC rs274 baseline mismatch for fixture: $name" >&2 exit 1 fi done echo "native LinuxCNC fixture baseline validation complete"