Complete sim config boundary coverage

This commit is contained in:
2026-06-11 06:12:06 +08:00
parent 5bd8f12872
commit 1ab5571ae1
76 changed files with 18975 additions and 146 deletions

View File

@@ -0,0 +1,202 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
LINUXCNC_ROOT="${LINUXCNC_ROOT:-$ROOT_DIR/../linuxcnc}"
MACHINE_DIR="$LINUXCNC_ROOT/configs/sim/axis/vismach/millturn"
INI_FILE="$MACHINE_DIR/millturn.ini"
RUN_DIR="$ROOT_DIR/build/native/millturn-user-m-runtime"
LINUXCNC_STDOUT="$RUN_DIR/linuxcnc.stdout.log"
LINUXCNC_STDERR="$RUN_DIR/linuxcnc.stderr.log"
required_commands=(tclsh halrun halcmd linuxcnc)
missing_commands=()
LINUXCNC_PID=""
cleanup() {
if [[ -n "$LINUXCNC_PID" ]]; then
kill "$LINUXCNC_PID" 2>/dev/null || true
wait "$LINUXCNC_PID" 2>/dev/null || true
fi
}
trap cleanup EXIT
command_path() {
command -v "$1" 2>/dev/null || true
}
csv_join() {
local IFS=,
printf '%s' "$*"
}
print_kv() {
printf '%s=%s\n' "$1" "$2"
}
runtime_requirements=()
for command_name in "${required_commands[@]}"; do
path="$(command_path "$command_name")"
if [[ -n "$path" ]]; then
runtime_requirements+=("$command_name:1")
print_kv "millturn_user_m_runtime_${command_name}_path" "$path"
else
runtime_requirements+=("$command_name:0")
missing_commands+=("$command_name")
fi
done
source_ready=1
for rel in \
millturn.ini \
mcodes/M128 \
mcodes/M129 \
remap_subs/428remap.ngc \
remap_subs/429remap.ngc; do
if [[ ! -f "$MACHINE_DIR/$rel" ]]; then
source_ready=0
print_kv "millturn_user_m_missing_source_${rel//[^A-Za-z0-9]/_}" "$MACHINE_DIR/$rel"
fi
done
runtime_ready=0
if [[ "${#missing_commands[@]}" -eq 0 ]]; then
runtime_ready=1
fi
print_kv millturn_user_m_runtime_requirements "$(csv_join "${runtime_requirements[@]}")"
if [[ "${#missing_commands[@]}" -eq 0 ]]; then
print_kv millturn_user_m_missing_requirements "-"
else
print_kv millturn_user_m_missing_requirements "$(csv_join "${missing_commands[@]}")"
fi
print_kv millturn_user_m_source_proof_ready "$source_ready"
print_kv millturn_user_m_runtime_ready "$runtime_ready"
print_kv millturn_user_m_execution_enabled 0
print_kv millturn_user_m_promotion_allowed 0
if [[ "$source_ready" != "1" ]]; then
print_kv millturn_user_m_runtime_probe_status "blocked_missing_source"
exit 0
fi
if [[ "$runtime_ready" != "1" ]]; then
print_kv millturn_user_m_runtime_probe_status "skipped_missing_host_runtime"
exit 0
fi
if [[ "${ENABLE_MILLTURN_USER_M_RUNTIME_PROBE:-0}" != "1" ]]; then
print_kv millturn_user_m_runtime_probe_status "ready_disabled_by_default"
print_kv millturn_user_m_runtime_probe_note "set_ENABLE_MILLTURN_USER_M_RUNTIME_PROBE_1_to_run_full_process_probe"
exit 0
fi
mkdir -p "$RUN_DIR"
wait_for_hal_pin() {
local pin="$1"
local limit="${MILLTURN_USER_M_RUNTIME_WAIT_SECONDS:-30}"
local end=$((SECONDS + limit))
while (( SECONDS < end )); do
if halcmd getp "$pin" >/dev/null 2>&1; then
return 0
fi
sleep 0.25
done
return 1
}
read_hal_pin() {
halcmd getp "$1" 2>/dev/null | tr -d '[:space:]'
}
expect_hal_pin() {
local label="$1"
local pin="$2"
local expected="$3"
local actual
actual="$(read_hal_pin "$pin")"
print_kv "millturn_user_m_${label}_${pin//[^A-Za-z0-9]/_}" "$actual"
[[ "$actual" == "$expected" ]]
}
set_switchkins_target() {
local target="$1"
local guard_pin="kinstype.is-$target"
if ! halcmd setp motion.switchkins-type "$target" >/dev/null 2>&1; then
halcmd unlinkp motion.switchkins-type >/dev/null 2>&1 || true
halcmd setp motion.switchkins-type "$target"
fi
for _ in $(seq 1 40); do
if [[ "$(read_hal_pin "$guard_pin")" =~ ^(TRUE|1)$ ]]; then
return 0
fi
sleep 0.05
done
return 1
}
run_user_m_case() {
local code="$1"
local mode="$2"
local switchkins_target="$3"
local expected_x_min="$4"
local expected_x_max="$5"
local expected_y_min="$6"
local expected_y_max="$7"
local expected_z_min="$8"
local expected_z_max="$9"
print_kv "millturn_user_m_${code}_runtime_mode" "$mode"
if ! set_switchkins_target "$switchkins_target"; then
print_kv "millturn_user_m_${code}_switchkins_target_ready" 0
return 1
fi
print_kv "millturn_user_m_${code}_switchkins_target_ready" 1
INI_FILE_NAME="$INI_FILE" "$MACHINE_DIR/mcodes/$code"
local ok=1
expect_hal_pin "${code}" ini.x.min_limit "$expected_x_min" || ok=0
expect_hal_pin "${code}" ini.x.max_limit "$expected_x_max" || ok=0
expect_hal_pin "${code}" ini.y.min_limit "$expected_y_min" || ok=0
expect_hal_pin "${code}" ini.y.max_limit "$expected_y_max" || ok=0
expect_hal_pin "${code}" ini.z.min_limit "$expected_z_min" || ok=0
expect_hal_pin "${code}" ini.z.max_limit "$expected_z_max" || ok=0
expect_hal_pin "${code}" ini.x.max_velocity "60" || ok=0
expect_hal_pin "${code}" ini.y.max_velocity "60" || ok=0
expect_hal_pin "${code}" ini.z.max_velocity "60" || ok=0
expect_hal_pin "${code}" ini.x.max_acceleration "400" || ok=0
expect_hal_pin "${code}" ini.y.max_acceleration "400" || ok=0
expect_hal_pin "${code}" ini.z.max_acceleration "400" || ok=0
print_kv "millturn_user_m_${code}_runtime_state_ok" "$ok"
[[ "$ok" == "1" ]]
}
print_kv millturn_user_m_runtime_probe_ini "$INI_FILE"
print_kv millturn_user_m_runtime_probe_linuxcnc_stdout "$LINUXCNC_STDOUT"
print_kv millturn_user_m_runtime_probe_linuxcnc_stderr "$LINUXCNC_STDERR"
linuxcnc -r "$INI_FILE" >"$LINUXCNC_STDOUT" 2>"$LINUXCNC_STDERR" &
LINUXCNC_PID="$!"
if ! wait_for_hal_pin ini.x.min_limit || ! wait_for_hal_pin motion.switchkins-type; then
print_kv millturn_user_m_runtime_probe_status "runtime_state_probe_failed"
print_kv millturn_user_m_runtime_probe_note "linuxcnc_started_but_required_hal_pins_did_not_appear"
exit 1
fi
probe_ok=1
run_user_m_case M128 mill 0 -300 300 -100 100 -240 0 || probe_ok=0
run_user_m_case M129 turn 1 -240 0 -100 100 -300 300 || probe_ok=0
if [[ "$probe_ok" == "1" ]]; then
print_kv millturn_user_m_runtime_probe_status "runtime_state_probe_passed"
print_kv millturn_user_m_runtime_probe_note "linuxcnc_owned_M128_M129_tcl_hal_state_probe_passed_without_promotion"
exit 0
fi
print_kv millturn_user_m_runtime_probe_status "runtime_state_probe_failed"
print_kv millturn_user_m_runtime_probe_note "linuxcnc_owned_M128_M129_tcl_hal_state_probe_failed"
exit 1

View File

@@ -0,0 +1,189 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
LINUXCNC_ROOT="${LINUXCNC_ROOT:-$ROOT_DIR/../linuxcnc}"
FIXTURE_FAMILY="axis/remap/stop-lookahead/nc_files"
FIXTURE_DIR="$LINUXCNC_ROOT/configs/sim/axis/remap/stop-lookahead"
FIXTURE_INI="$FIXTURE_DIR/demo.ini"
RUN_DIR="$ROOT_DIR/build/native/python-remap-runtime"
PYTHON_RUNTIME_STDOUT="$RUN_DIR/python_lifecycle.stdout.log"
PYTHON_RUNTIME_STDERR="$RUN_DIR/python_lifecycle.stderr.log"
required_commands=(python3 linuxcnc)
missing_requirements=()
command_path() {
command -v "$1" 2>/dev/null || true
}
csv_join() {
local IFS=,
printf '%s' "$*"
}
print_kv() {
printf '%s=%s\n' "$1" "$2"
}
runtime_requirements=()
for command_name in "${required_commands[@]}"; do
path="$(command_path "$command_name")"
if [[ -n "$path" ]]; then
runtime_requirements+=("$command_name:1")
print_kv "python_remap_runtime_${command_name}_path" "$path"
else
runtime_requirements+=("$command_name:0")
missing_requirements+=("$command_name")
fi
done
source_ready=1
for rel in \
src/emc/rs274ngc/interp_python.cc \
src/emc/pythonplugin/python_plugin.cc \
configs/sim/axis/remap/stop-lookahead/demo.ini \
configs/sim/axis/remap/stop-lookahead/python/remap.py \
configs/sim/axis/remap/stop-lookahead/python/toplevel.py; do
if [[ ! -f "$LINUXCNC_ROOT/$rel" ]]; then
source_ready=0
missing_requirements+=("$rel")
print_kv "python_remap_missing_source_${rel//[^A-Za-z0-9]/_}" "$LINUXCNC_ROOT/$rel"
fi
done
runtime_ready=0
if [[ "${#missing_requirements[@]}" -eq 0 ]]; then
runtime_ready=1
fi
print_kv python_remap_runtime_fixture_family "$FIXTURE_FAMILY"
print_kv python_remap_runtime_fixture_id "stop_lookahead_python_runtime_lifecycle"
print_kv python_remap_runtime_fixture_scope "no_python_callables_no_ngc_only_subpaths"
print_kv python_remap_runtime_phases "initialize_python,apply_ini_python_path,execute_toplevel,callable_lookup,pycall_dispatch,callable_invoke,remap_phase_dispatch,generator_finish,execute_python_runtime,reload_on_change"
print_kv python_remap_runtime_modules "axis/remap/stop-lookahead/python/remap.py,axis/remap/stop-lookahead/python/toplevel.py"
print_kv python_remap_runtime_requirements "$(csv_join "${runtime_requirements[@]}")"
if [[ "${#missing_requirements[@]}" -eq 0 ]]; then
print_kv python_remap_missing_requirements "-"
else
print_kv python_remap_missing_requirements "$(csv_join "${missing_requirements[@]}")"
fi
print_kv python_remap_source_proof_ready "$source_ready"
print_kv python_remap_runtime_ready "$runtime_ready"
print_kv python_remap_execution_enabled 0
print_kv python_remap_promotion_allowed 0
if [[ "$source_ready" != "1" ]]; then
print_kv python_remap_runtime_probe_status "blocked_missing_source"
exit 0
fi
if [[ "$runtime_ready" != "1" ]]; then
print_kv python_remap_runtime_probe_status "skipped_missing_host_runtime"
exit 0
fi
if [[ "${ENABLE_PYTHON_REMAP_RUNTIME_PROBE:-0}" != "1" ]]; then
print_kv python_remap_runtime_probe_status "ready_disabled_by_default"
print_kv python_remap_runtime_probe_note "set_ENABLE_PYTHON_REMAP_RUNTIME_PROBE_1_to_run_python_lifecycle_probe"
exit 0
fi
mkdir -p "$RUN_DIR"
rm -f "$PYTHON_RUNTIME_STDOUT" "$PYTHON_RUNTIME_STDERR"
print_kv python_remap_runtime_probe_fixture "$FIXTURE_DIR"
print_kv python_remap_runtime_probe_ini "$FIXTURE_INI"
print_kv python_remap_runtime_probe_stdout "$PYTHON_RUNTIME_STDOUT"
print_kv python_remap_runtime_probe_stderr "$PYTHON_RUNTIME_STDERR"
if python3 - "$LINUXCNC_ROOT" "$FIXTURE_DIR" "$FIXTURE_INI" "$RUN_DIR" \
>"$PYTHON_RUNTIME_STDOUT" 2>"$PYTHON_RUNTIME_STDERR" <<'PY'
import configparser
import importlib
import inspect
import os
from pathlib import Path
import re
import sys
import tempfile
linuxcnc_root = Path(sys.argv[1])
fixture_dir = Path(sys.argv[2])
fixture_ini = Path(sys.argv[3])
run_dir = Path(sys.argv[4])
def emit(name, value):
print(f"{name}={value}")
def require(condition, message):
if not condition:
raise RuntimeError(message)
interp_return = linuxcnc_root / "src/emc/nml_intf/interp_return.hh"
match = re.search(
r"INTERP_EXECUTE_FINISH\s*=\s*([0-9]+)",
interp_return.read_text(encoding="utf-8"),
)
require(match, "missing_INTERP_EXECUTE_FINISH_source_value")
execute_finish = int(match.group(1))
emit("python_remap_lifecycle_execute_finish_source_value", execute_finish)
stub_dir = Path(tempfile.mkdtemp(prefix="python-remap-interpreter-", dir=run_dir))
(stub_dir / "interpreter.py").write_text(
f"INTERP_EXECUTE_FINISH = {execute_finish}\n",
encoding="utf-8",
)
parser = configparser.ConfigParser(strict=False)
parser.optionxform = str
parser.read(fixture_ini)
require(parser.has_section("PYTHON"), "missing_PYTHON_section")
path_prepend = parser.get("PYTHON", "PATH_PREPEND", fallback="")
toplevel = parser.get("PYTHON", "TOPLEVEL", fallback="")
require(path_prepend == "python", f"PATH_PREPEND_drift:{path_prepend}")
require(toplevel == "python/toplevel.py", f"TOPLEVEL_drift:{toplevel}")
emit("python_remap_lifecycle_ini_path_prepend", path_prepend)
emit("python_remap_lifecycle_ini_toplevel", toplevel)
sys.path.insert(0, str(stub_dir))
sys.path.insert(0, str(fixture_dir / path_prepend))
sys.path.insert(0, str(linuxcnc_root / "lib/python"))
import interpreter # noqa: E402
require(interpreter.INTERP_EXECUTE_FINISH == execute_finish, "interpreter_sentinel_drift")
emit("python_remap_lifecycle_interpreter_sentinel_ok", 1)
toplevel_module = importlib.import_module(Path(toplevel).stem)
emit("python_remap_lifecycle_toplevel_imported", int(toplevel_module.__name__ == "toplevel"))
remap_module = importlib.import_module("remap")
emit("python_remap_lifecycle_remap_imported", int(remap_module.__name__ == "remap"))
require(hasattr(remap_module, "queuebuster"), "missing_queuebuster_callable")
require(callable(remap_module.queuebuster), "queuebuster_not_callable")
emit("python_remap_lifecycle_callable_lookup_ok", 1)
result = remap_module.queuebuster(object())
require(inspect.isgenerator(result), "queuebuster_not_generator")
emit("python_remap_lifecycle_generator_returned", 1)
first_yield = next(result)
require(first_yield == execute_finish, f"queuebuster_yield_drift:{first_yield}")
emit("python_remap_lifecycle_generator_first_yield", first_yield)
try:
next(result)
raise RuntimeError("queuebuster_generator_did_not_finish")
except StopIteration:
emit("python_remap_lifecycle_generator_finish_ok", 1)
emit("python_remap_runtime_lifecycle_probe_ok", 1)
PY
then
cat "$PYTHON_RUNTIME_STDOUT"
print_kv python_remap_runtime_probe_status "runtime_lifecycle_probe_passed"
print_kv python_remap_runtime_probe_note "linuxcnc_stop_lookahead_python_lifecycle_probe_passed_without_promotion"
exit 0
fi
cat "$PYTHON_RUNTIME_STDOUT" || true
print_kv python_remap_runtime_probe_status "runtime_lifecycle_probe_failed"
print_kv python_remap_runtime_probe_note "linuxcnc_stop_lookahead_python_lifecycle_probe_failed"
exit 1

View File

@@ -0,0 +1,232 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
LINUXCNC_ROOT="${LINUXCNC_ROOT:-$ROOT_DIR/../linuxcnc}"
DB_DEMO_DIR="$LINUXCNC_ROOT/configs/sim/axis/db_demo"
DB_PROGRAM="$DB_DEMO_DIR/db_nonran.py"
RUN_DIR="$ROOT_DIR/build/native/tool-db-runtime"
DB_SAVEFILE="$RUN_DIR/db_nonran_probe"
DB_STDOUT="$RUN_DIR/db_program.stdout.log"
DB_STDERR="$RUN_DIR/db_program.stderr.log"
required_commands=(python3 linuxcnc milltask halcmd)
missing_requirements=()
command_path() {
command -v "$1" 2>/dev/null || true
}
csv_join() {
local IFS=,
printf '%s' "$*"
}
print_kv() {
printf '%s=%s\n' "$1" "$2"
}
runtime_requirements=()
for command_name in "${required_commands[@]}"; do
path="$(command_path "$command_name")"
if [[ -n "$path" ]]; then
runtime_requirements+=("$command_name:1")
print_kv "tool_db_runtime_${command_name}_path" "$path"
else
runtime_requirements+=("$command_name:0")
missing_requirements+=("$command_name")
fi
done
source_ready=1
for rel in \
configs/sim/axis/db_demo/db_nonran.ini \
configs/sim/axis/db_demo/db.py \
lib/python/tooldb.py \
lib/python/linuxcnc.so; do
if [[ ! -e "$LINUXCNC_ROOT/$rel" ]]; then
source_ready=0
missing_requirements+=("$rel")
print_kv "tool_db_missing_source_${rel//[^A-Za-z0-9]/_}" "$LINUXCNC_ROOT/$rel"
fi
done
if [[ ! -x "$DB_PROGRAM" ]]; then
source_ready=0
missing_requirements+=("configs/sim/axis/db_demo/db_nonran.py:executable")
fi
runtime_ready=0
if [[ "${#missing_requirements[@]}" -eq 0 ]]; then
runtime_ready=1
fi
print_kv tool_db_runtime_requirements "$(csv_join "${runtime_requirements[@]}")"
if [[ "${#missing_requirements[@]}" -eq 0 ]]; then
print_kv tool_db_missing_requirements "-"
else
print_kv tool_db_missing_requirements "$(csv_join "${missing_requirements[@]}")"
fi
print_kv tool_db_source_proof_ready "$source_ready"
print_kv tool_db_runtime_ready "$runtime_ready"
print_kv tool_db_execution_enabled 0
print_kv tool_db_promotion_allowed 0
if [[ "$source_ready" != "1" ]]; then
print_kv tool_db_runtime_probe_status "blocked_missing_source"
exit 0
fi
if [[ "$runtime_ready" != "1" ]]; then
print_kv tool_db_runtime_probe_status "skipped_missing_host_runtime"
exit 0
fi
if [[ "${ENABLE_TOOL_DB_RUNTIME_PROBE:-0}" != "1" ]]; then
print_kv tool_db_runtime_probe_status "ready_disabled_by_default"
print_kv tool_db_runtime_probe_note "set_ENABLE_TOOL_DB_RUNTIME_PROBE_1_to_run_db_protocol_probe"
exit 0
fi
mkdir -p "$RUN_DIR"
rm -f "$DB_SAVEFILE" "$DB_STDOUT" "$DB_STDERR"
print_kv tool_db_runtime_probe_program "$DB_PROGRAM"
print_kv tool_db_runtime_probe_db_savefile "$DB_SAVEFILE"
print_kv tool_db_runtime_probe_stdout "$DB_STDOUT"
print_kv tool_db_runtime_probe_stderr "$DB_STDERR"
if python3 - "$DB_PROGRAM" "$DB_DEMO_DIR" "$LINUXCNC_ROOT/lib/python" "$DB_SAVEFILE" "$DB_STDOUT" "$DB_STDERR" <<'PY'
import os
import select
import subprocess
import sys
import time
db_program, db_demo_dir, python_path, db_savefile, stdout_log, stderr_log = sys.argv[1:]
env = os.environ.copy()
env["PYTHONPATH"] = python_path + (os.pathsep + env["PYTHONPATH"] if env.get("PYTHONPATH") else "")
stdout_file = open(stdout_log, "w", encoding="utf-8")
stderr_file = open(stderr_log, "w", encoding="utf-8")
proc = subprocess.Popen(
[db_program, "--period_minutes=999", db_savefile],
cwd=db_demo_dir,
env=env,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=stderr_file,
text=True,
bufsize=1,
)
def emit(name, value):
print(f"{name}={value}")
def read_line(timeout=5.0):
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
ready, _, _ = select.select([proc.stdout], [], [], 0.1)
if ready:
line = proc.stdout.readline()
if line == "":
raise RuntimeError("db_program_stdout_closed")
line = line.rstrip("\n")
stdout_file.write(line + "\n")
stdout_file.flush()
return line
if proc.poll() is not None:
raise RuntimeError(f"db_program_exited_{proc.returncode}")
raise RuntimeError("timeout_waiting_for_db_program_reply")
def send(command):
proc.stdin.write(command + "\n")
proc.stdin.flush()
def read_until_fini(command, timeout=5.0):
send(command)
lines = []
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
line = read_line(max(0.1, deadline - time.monotonic()))
lines.append(line)
if line.startswith("FINI"):
return lines
raise RuntimeError(f"timeout_waiting_for_fini_{command.split()[0]}")
def require(condition, message):
if not condition:
raise RuntimeError(message)
ok = False
try:
version = read_line()
emit("tool_db_protocol_version", version)
require(version == "v2.1", f"version_drift:{version}")
get_all = read_until_fini("g")
tool_lines = [line for line in get_all if line.startswith("T")]
emit("tool_db_get_all_count", len(tool_lines))
require(len(tool_lines) == 10, "get_all_tool_count_drift")
for toolno in range(10, 20):
expected = f"T{toolno} "
require(any(line.startswith(expected) for line in tool_lines), f"missing_tool_{toolno}")
require(any(line.startswith(expected) and f"P{toolno + 100}" in line for line in tool_lines), f"pocket_drift_{toolno}")
emit("tool_db_get_all_fini", int(get_all[-1].startswith("FINI")))
put_reply = read_until_fini("p t11 p111 d0.33 z0.11")
emit("tool_db_put_tool_update_fini", int(put_reply[-1].startswith("FINI")))
get_after_put = read_until_fini("g")
t11_rows = [line for line in get_after_put if line.startswith("T11 ")]
require(t11_rows and "D0.33" in t11_rows[0] and "Z0.11" in t11_rows[0], "put_update_state_drift")
emit("tool_db_put_tool_update_state_ok", 1)
load_reply = read_until_fini("l t14 p0")
emit("tool_db_load_spindle_fini", int(load_reply[-1].startswith("FINI")))
t14_loaded = read_until_fini("t 14")
require(any(line.startswith("T14 ") and "P0" in line for line in t14_loaded), "load_spindle_state_drift")
emit("tool_db_load_spindle_state_ok", 1)
unload_reply = read_until_fini("u t0 p0")
emit("tool_db_unload_spindle_fini", int(unload_reply[-1].startswith("FINI")))
t14_unloaded = read_until_fini("t 14")
require(any(line.startswith("T14 ") and "P114" in line for line in t14_unloaded), "unload_spindle_state_drift")
emit("tool_db_unload_spindle_state_ok", 1)
require(os.path.exists(db_savefile), "db_savefile_missing")
with open(db_savefile, "r", encoding="utf-8") as handle:
saved = handle.read()
require("T11 P111 D0.33 Z0.11" in saved, "db_savefile_put_update_missing")
require("T14 P114" in saved, "db_savefile_unload_state_missing")
emit("tool_db_persistence_state_ok", 1)
ok = True
finally:
try:
if proc.stdin:
proc.stdin.close()
except Exception:
pass
if proc.poll() is None:
proc.terminate()
try:
proc.wait(timeout=2)
except subprocess.TimeoutExpired:
proc.kill()
proc.wait(timeout=2)
stderr_file.close()
stdout_file.close()
emit("tool_db_runtime_protocol_probe_ok", int(ok))
sys.exit(0 if ok else 1)
PY
then
print_kv tool_db_runtime_probe_status "runtime_protocol_probe_passed"
print_kv tool_db_runtime_probe_note "linuxcnc_owned_DB_PROGRAM_protocol_probe_passed_without_promotion"
exit 0
fi
print_kv tool_db_runtime_probe_status "runtime_protocol_probe_failed"
print_kv tool_db_runtime_probe_note "linuxcnc_owned_DB_PROGRAM_protocol_probe_failed"
exit 1

View File

@@ -32,6 +32,8 @@ hash_build_validation_inputs() {
-o -name '*.run.stdout.log' \
-o -name '*.run.stderr.log' \
-o -name 'source-probes.tsv' \
-o -name 'native-source-proof-summary.tsv' \
-o -name 'native-runtime-probe-summary.tsv' \
-o -name 'native-probes.ok' \
-o -name 'linuxcnc_interp_minimal_harness' \
\) \
@@ -122,13 +124,195 @@ check_exitcode() {
fi
}
check_native_source_proof_summary_row() {
local boundary_class="$1"
local proof_key="$2"
local summary_file="$BUILD_DIR/native-source-proof-summary.tsv"
local row
row="$(awk -F'\t' -v boundary_class="$boundary_class" -v proof_key="$proof_key" '
NR > 1 && $1 == boundary_class && $2 == proof_key { print; count++ }
END {
if (count != 1) {
exit 1
}
}
' "$summary_file")" || {
echo "missing native source proof summary row: $boundary_class $proof_key" >&2
exit 1
}
local proof_value
local execution_enabled
local promotion_allowed
local stdout_log
IFS=$'\t' read -r _ _ proof_value execution_enabled promotion_allowed stdout_log <<< "$row"
if [[ "$proof_value" != "1" ]]; then
echo "$boundary_class native source proof is not complete: $proof_value" >&2
exit 1
fi
if [[ "$execution_enabled" != "0" || "$promotion_allowed" != "0" ]]; then
echo "$boundary_class unexpectedly enables execution or promotion" >&2
exit 1
fi
if [[ ! -f "$BUILD_DIR/$stdout_log" ]]; then
echo "$boundary_class summary references missing stdout log: $stdout_log" >&2
exit 1
fi
}
check_native_source_proof_summary() {
local summary_file="$BUILD_DIR/native-source-proof-summary.tsv"
if [[ ! -f "$summary_file" ]]; then
echo "missing native source proof summary: $summary_file" >&2
exit 1
fi
local expected_header
expected_header=$'boundary_class\tproof_key\tproof_value\texecution_enabled\tpromotion_allowed\tstdout_log'
if [[ "$(sed -n '1p' "$summary_file")" != "$expected_header" ]]; then
echo "native source proof summary header drift: $summary_file" >&2
exit 1
fi
local row_count
row_count="$(awk 'END { print NR - 1 }' "$summary_file")"
if [[ "$row_count" != "3" ]]; then
echo "native source proof summary must contain exactly 3 proof rows, got $row_count" >&2
exit 1
fi
check_native_source_proof_summary_row \
L4-USER-M-PROCESS \
millturn_user_m_native_source_state_proof
check_native_source_proof_summary_row \
L4-TOOL-DB \
tool_db_native_source_protocol_proof
check_native_source_proof_summary_row \
L4-PYTHON-REMAP \
python_remap_native_source_inventory_proof
}
check_native_runtime_probe_summary_row() {
local boundary_class="$1"
local boundary_kind="$2"
local target="$3"
local runtime_probe="$4"
local summary_file="$BUILD_DIR/native-runtime-probe-summary.tsv"
local row
row="$(awk -F'\t' \
-v boundary_class="$boundary_class" \
-v boundary_kind="$boundary_kind" \
-v target="$target" \
-v runtime_probe="$runtime_probe" '
NR > 1 && $1 == boundary_class && $2 == boundary_kind && $3 == target && $4 == runtime_probe {
print
count++
}
END {
if (count != 1) {
exit 1
}
}
' "$summary_file")" || {
echo "missing native runtime probe summary row: $boundary_class $boundary_kind $target" >&2
exit 1
}
local runtime_ready
local source_proof_ready
local probe_status
local execution_enabled
local promotion_allowed
local missing_requirements
local stdout_log
IFS=$'\t' read -r _ _ _ _ runtime_ready source_proof_ready probe_status execution_enabled promotion_allowed missing_requirements stdout_log <<< "$row"
if [[ "$source_proof_ready" != "1" ]]; then
echo "$target runtime probe lacks source proof readiness: $source_proof_ready" >&2
exit 1
fi
if [[ "$execution_enabled" != "0" || "$promotion_allowed" != "0" ]]; then
echo "$target runtime probe unexpectedly enables execution or promotion" >&2
exit 1
fi
if [[ ! "$probe_status" =~ ^(skipped_missing_host_runtime|ready_disabled_by_default|not_implemented_full_process_guard|runtime_state_probe_passed|runtime_protocol_probe_passed|runtime_lifecycle_probe_passed)$ ]]; then
echo "$target runtime probe has invalid status: $probe_status" >&2
exit 1
fi
if [[ "$runtime_ready" == "0" && "$probe_status" != "skipped_missing_host_runtime" ]]; then
echo "$target runtime probe is not ready but did not skip: $probe_status" >&2
exit 1
fi
if [[ "$runtime_ready" == "1" && "$missing_requirements" != "-" ]]; then
echo "$target runtime probe is ready but lists missing requirements: $missing_requirements" >&2
exit 1
fi
if [[ ! -f "$BUILD_DIR/$stdout_log" ]]; then
echo "$target runtime summary references missing stdout log: $stdout_log" >&2
exit 1
fi
}
check_native_runtime_probe_summary() {
local summary_file="$BUILD_DIR/native-runtime-probe-summary.tsv"
if [[ ! -f "$summary_file" ]]; then
echo "missing native runtime probe summary: $summary_file" >&2
exit 1
fi
local expected_header
expected_header=$'boundary_class\tboundary_kind\ttarget\truntime_probe\truntime_ready\tsource_proof_ready\tprobe_status\texecution_enabled\tpromotion_allowed\tmissing_requirements\tstdout_log'
if [[ "$(sed -n '1p' "$summary_file")" != "$expected_header" ]]; then
echo "native runtime probe summary header drift: $summary_file" >&2
exit 1
fi
local row_count
row_count="$(awk 'END { print NR - 1 }' "$summary_file")"
if [[ "$row_count" != "3" ]]; then
echo "native runtime probe summary must contain exactly 3 probe rows, got $row_count" >&2
exit 1
fi
check_native_runtime_probe_summary_row \
L4-USER-M-PROCESS \
external_user_m_process \
axis/vismach/millturn/example.ngc \
linuxcnc_millturn_user_m_runtime_probe
check_native_runtime_probe_summary_row \
L4-TOOL-DB \
tool_database_process \
axis/db_demo/base.ngc \
linuxcnc_tool_db_runtime_probe
check_native_runtime_probe_summary_row \
L4-PYTHON-REMAP \
python_runtime \
axis/remap/stop-lookahead/nc_files \
linuxcnc_python_remap_runtime_probe
}
check_source_probe_coverage
check_native_source_proof_summary
check_native_runtime_probe_summary
check_exitcode linuxcnc_interp_state_probe
check_exitcode linuxcnc_emc_status_probe
check_exitcode linuxcnc_emc_status_probe.run
check_exitcode linuxcnc_inifile_source_probe
check_exitcode linuxcnc_5axis_remap_asset_probe
check_exitcode linuxcnc_5axis_remap_asset_probe.run
check_exitcode linuxcnc_millturn_user_m_boundary_probe
check_exitcode linuxcnc_millturn_user_m_boundary_probe.run
check_exitcode linuxcnc_millturn_user_m_runtime_probe.run
check_exitcode linuxcnc_tool_db_boundary_probe
check_exitcode linuxcnc_tool_db_boundary_probe.run
check_exitcode linuxcnc_python_remap_boundary_probe
check_exitcode linuxcnc_python_remap_boundary_probe.run
check_exitcode linuxcnc_python_remap_runtime_probe.run
check_exitcode linuxcnc_tp_api_probe
check_exitcode linuxcnc_tp_api_probe.run
check_exitcode linuxcnc_kinematics_probe
@@ -381,6 +565,157 @@ grep -Fq "xyzac_trt_switch_mcodes_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT
grep -Fq "xyzbc_trt_switch_mcodes_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
grep -Fq "fiveaxis_remap_assets_from_linuxcnc=1" "$FIVEAXIS_REMAP_ASSET_STDOUT"
MILLTURN_USER_M_BOUNDARY_STDOUT="$BUILD_DIR/linuxcnc_millturn_user_m_boundary_probe.run.stdout.log"
grep -Fq "millturn_ini_open=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_RS274NGC.USER_M_PATH_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_RS274NGC.SUBROUTINE_PATH_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_DISPLAY.PYVCP_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_HAL.HALUI_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_state_mode=mill" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_guard_pin=kinstype.is-0" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_script_tcl_hal_runtime=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_remap_calls_user_m_process=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_switchkins_output_pin=motion.analog-out-03" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_switchkins_target=0" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_work_offset_pocket=P7" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_work_offset_words=X-290 Y0 Z-160 A0" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_active_g5x=G59.1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_calls_M128=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M428_source_transition_from_linuxcnc=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_X_AXIS_X.MIN_LIMIT_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_X_AXIS_X.MAX_LIMIT_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_Z_AXIS_Z.MAX_LIMIT_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M128_source_state_targets_from_linuxcnc=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_state_mode=turn" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_guard_pin=kinstype.is-1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_script_tcl_hal_runtime=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_remap_calls_user_m_process=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_switchkins_output_pin=motion.analog-out-03" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_switchkins_target=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_work_offset_pocket=P8" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_work_offset_words=X-160 Y0 Z-290 A0" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_active_g5x=G59.2" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_calls_M129=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M429_source_transition_from_linuxcnc=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_X_AXIS_X.MIN_LIMIT_TURN_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_Z_AXIS_Z.MAX_LIMIT_TURN_ok=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "M129_source_state_targets_from_linuxcnc=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_user_m_native_source_state_proof=1" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_user_m_execution_enabled=0" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
grep -Fq "millturn_user_m_promotion_allowed=0" "$MILLTURN_USER_M_BOUNDARY_STDOUT"
MILLTURN_USER_M_RUNTIME_STDOUT="$BUILD_DIR/linuxcnc_millturn_user_m_runtime_probe.run.stdout.log"
grep -Fq "millturn_user_m_runtime_requirements=" "$MILLTURN_USER_M_RUNTIME_STDOUT"
grep -Fq "millturn_user_m_source_proof_ready=1" "$MILLTURN_USER_M_RUNTIME_STDOUT"
grep -Fq "millturn_user_m_execution_enabled=0" "$MILLTURN_USER_M_RUNTIME_STDOUT"
grep -Fq "millturn_user_m_promotion_allowed=0" "$MILLTURN_USER_M_RUNTIME_STDOUT"
if grep -Fq "millturn_user_m_runtime_ready=1" "$MILLTURN_USER_M_RUNTIME_STDOUT"; then
grep -Eq "millturn_user_m_runtime_probe_status=(ready_disabled_by_default|runtime_state_probe_passed)" \
"$MILLTURN_USER_M_RUNTIME_STDOUT"
if grep -Fq "millturn_user_m_runtime_probe_status=runtime_state_probe_passed" "$MILLTURN_USER_M_RUNTIME_STDOUT"; then
grep -Fq "millturn_user_m_M128_runtime_state_ok=1" "$MILLTURN_USER_M_RUNTIME_STDOUT"
grep -Fq "millturn_user_m_M129_runtime_state_ok=1" "$MILLTURN_USER_M_RUNTIME_STDOUT"
fi
else
grep -Fq "millturn_user_m_runtime_ready=0" "$MILLTURN_USER_M_RUNTIME_STDOUT"
grep -Fq "millturn_user_m_runtime_probe_status=skipped_missing_host_runtime" \
"$MILLTURN_USER_M_RUNTIME_STDOUT"
fi
TOOL_DB_BOUNDARY_STDOUT="$BUILD_DIR/linuxcnc_tool_db_boundary_probe.run.stdout.log"
grep -Fq "db_nonran_ini_open=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "db_nonran_EMCIO.RANDOM_TOOLCHANGER_ok=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "db_nonran_EMCIO.DB_PROGRAM_ok=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "db_nonran_tool_table_ignored_with_db_program=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_task_db_mode_init=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_task_ignores_tool_table_with_db_program=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_task_load_unload_notify=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_child_process_boundary=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_program_executable_check=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_v2_1_handshake=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_getall_g_until_fini=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_notify_l_u_p_protocol=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_program_tooldb_module=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_program_nonran_callbacks=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_program_nonran_state_targets=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_program_reload_rules=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_native_source_protocol_proof=1" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_execution_enabled=0" "$TOOL_DB_BOUNDARY_STDOUT"
grep -Fq "tool_db_promotion_allowed=0" "$TOOL_DB_BOUNDARY_STDOUT"
TOOL_DB_RUNTIME_STDOUT="$BUILD_DIR/linuxcnc_tool_db_runtime_probe.run.stdout.log"
grep -Fq "tool_db_runtime_requirements=" "$TOOL_DB_RUNTIME_STDOUT"
grep -Fq "tool_db_source_proof_ready=1" "$TOOL_DB_RUNTIME_STDOUT"
grep -Fq "tool_db_execution_enabled=0" "$TOOL_DB_RUNTIME_STDOUT"
grep -Fq "tool_db_promotion_allowed=0" "$TOOL_DB_RUNTIME_STDOUT"
if grep -Fq "tool_db_runtime_ready=1" "$TOOL_DB_RUNTIME_STDOUT"; then
grep -Eq "tool_db_runtime_probe_status=(ready_disabled_by_default|runtime_protocol_probe_passed)" \
"$TOOL_DB_RUNTIME_STDOUT"
if grep -Fq "tool_db_runtime_probe_status=runtime_protocol_probe_passed" "$TOOL_DB_RUNTIME_STDOUT"; then
grep -Fq "tool_db_runtime_protocol_probe_ok=1" "$TOOL_DB_RUNTIME_STDOUT"
grep -Fq "tool_db_get_all_fini=1" "$TOOL_DB_RUNTIME_STDOUT"
grep -Fq "tool_db_put_tool_update_state_ok=1" "$TOOL_DB_RUNTIME_STDOUT"
grep -Fq "tool_db_load_spindle_state_ok=1" "$TOOL_DB_RUNTIME_STDOUT"
grep -Fq "tool_db_unload_spindle_state_ok=1" "$TOOL_DB_RUNTIME_STDOUT"
grep -Fq "tool_db_persistence_state_ok=1" "$TOOL_DB_RUNTIME_STDOUT"
fi
else
grep -Fq "tool_db_runtime_ready=0" "$TOOL_DB_RUNTIME_STDOUT"
grep -Fq "tool_db_runtime_probe_status=skipped_missing_host_runtime" \
"$TOOL_DB_RUNTIME_STDOUT"
fi
PYTHON_REMAP_BOUNDARY_STDOUT="$BUILD_DIR/linuxcnc_python_remap_boundary_probe.run.stdout.log"
grep -Fq "python_runtime_pycall_dispatch=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_runtime_remap_phases=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_runtime_generator_finish=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_runtime_callable_lookup=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_runtime_execute_runtime=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_plugin_initializes_python=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_plugin_toplevel_exec_file=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_plugin_ini_python_path=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_plugin_callable_invoke=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_plugin_reload_on_change=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_family_axis_laser_inventory=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_family_twp_nutating_inventory=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_family_axis_remap_cycle_inventory=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_family_gmoccapy_stdglue_inventory=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_remap_native_source_inventory_proof=1" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_remap_execution_enabled=0" "$PYTHON_REMAP_BOUNDARY_STDOUT"
grep -Fq "python_remap_promotion_allowed=0" "$PYTHON_REMAP_BOUNDARY_STDOUT"
PYTHON_REMAP_RUNTIME_STDOUT="$BUILD_DIR/linuxcnc_python_remap_runtime_probe.run.stdout.log"
grep -Fq "python_remap_runtime_fixture_family=axis/remap/stop-lookahead/nc_files" \
"$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_runtime_fixture_id=stop_lookahead_python_runtime_lifecycle" \
"$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_runtime_fixture_scope=no_python_callables_no_ngc_only_subpaths" \
"$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_runtime_requirements=" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_runtime_modules=axis/remap/stop-lookahead/python/remap.py,axis/remap/stop-lookahead/python/toplevel.py" \
"$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_source_proof_ready=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_execution_enabled=0" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_promotion_allowed=0" "$PYTHON_REMAP_RUNTIME_STDOUT"
if grep -Fq "python_remap_runtime_ready=1" "$PYTHON_REMAP_RUNTIME_STDOUT"; then
grep -Eq "python_remap_runtime_probe_status=(ready_disabled_by_default|runtime_lifecycle_probe_passed)" \
"$PYTHON_REMAP_RUNTIME_STDOUT"
if grep -Fq "python_remap_runtime_probe_status=runtime_lifecycle_probe_passed" "$PYTHON_REMAP_RUNTIME_STDOUT"; then
grep -Fq "python_remap_runtime_lifecycle_probe_ok=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_lifecycle_interpreter_sentinel_ok=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_lifecycle_toplevel_imported=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_lifecycle_remap_imported=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_lifecycle_callable_lookup_ok=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_lifecycle_generator_returned=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_lifecycle_generator_first_yield=2" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_lifecycle_generator_finish_ok=1" "$PYTHON_REMAP_RUNTIME_STDOUT"
fi
else
grep -Fq "python_remap_runtime_ready=0" "$PYTHON_REMAP_RUNTIME_STDOUT"
grep -Fq "python_remap_runtime_probe_status=skipped_missing_host_runtime" \
"$PYTHON_REMAP_RUNTIME_STDOUT"
fi
REMAP_PARSE_STDOUT="$BUILD_DIR/linuxcnc_remap_parse_harness.run.stdout.log"
check_remap_descriptor() {

View File

@@ -22,7 +22,8 @@ while [[ $# -gt 0 ]]; do
Usage: $0 [--only PATH_SUBSTRING]
Runs LinuxCNC configs/sim .ngc programs through LinuxCNC's rs274 standalone
entry point and writes logs plus summary.tsv under:
entry point and writes logs plus summary.tsv, class-summary.tsv, and
path-matrix.tsv under:
$BUILD_DIR
EOF
exit 0
@@ -44,6 +45,10 @@ if [[ ! -d "$SIM_DIR" ]]; then
exit 1
fi
mkdir -p "$BUILD_DIR"
exec 9>"$BUILD_DIR/inventory.lock"
flock 9
"$ROOT_DIR/tools/build_native_probes.sh"
export LD_LIBRARY_PATH="$LINUXCNC_DIR/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
@@ -52,6 +57,8 @@ export PYTHONPATH="$LINUXCNC_DIR/lib/python${PYTHONPATH:+:$PYTHONPATH}"
mkdir -p "$BUILD_DIR"/{logs,out,wrapped,params}
: > "$BUILD_DIR/skipped.tsv"
SUMMARY_FILE="$BUILD_DIR/summary.tsv"
CLASS_SUMMARY_FILE="$BUILD_DIR/class-summary.tsv"
PATH_MATRIX_FILE="$BUILD_DIR/path-matrix.tsv"
printf 'path\tclass\tstatus\texpected_failure\texit_code\tseconds\tini\ttbl\toutput_lines\tstderr_summary\n' > "$SUMMARY_FILE"
relpath() {
@@ -392,9 +399,69 @@ while IFS= read -r ngc; do
"$stderr_summary" >> "$SUMMARY_FILE"
done < <(find "$SIM_DIR" -type f -name '*.ngc' | sort)
{
printf 'class\tstatus\texpected_failure\tcount\n'
awk -F '\t' '
NR > 1 {
class[$2] += 1
status[$2 "\t" $3] += 1
if ($4 != "") {
expected[$2 "\t" $4] += 1
}
}
END {
for (key in class) {
print key "\tALL\t-\t" class[key]
}
for (key in status) {
split(key, parts, "\t")
print parts[1] "\t" parts[2] "\t-\t" status[key]
}
for (key in expected) {
split(key, parts, "\t")
print parts[1] "\tEXPECTED_FAIL\t" parts[2] "\t" expected[key]
}
}
' "$SUMMARY_FILE" | sort
} > "$CLASS_SUMMARY_FILE"
awk -F '\t' '
BEGIN {
OFS = "\t"
print "path", "class", "status", "expected_failure", "ini", "tbl", "runtime_family", "blocked"
}
NR > 1 {
runtime_family = $1
sub("/[^/]+$", "", runtime_family)
blocked = ""
if ($1 ~ /^axis\/db_demo\//) {
blocked = "L4-TOOL-DB"
} else if ($1 ~ /^axis\/vismach\/millturn\// && $2 == "main") {
blocked = "L4-USER-M-PROCESS"
} else if ($4 == "upstream-demo-missing-motion-gcode") {
blocked = "UPSTREAM-DEMO"
} else if ($1 ~ /^axis\/remap\// && $2 == "main") {
blocked = "L4-PYTHON-REMAP"
} else if ($1 ~ /^axis\/vismach\/VMC_toolchange\// && $2 == "main") {
blocked = "L4-PYTHON-REMAP"
} else if ($1 ~ /^gmoccapy\//) {
blocked = "L4-PYTHON-REMAP"
} else if ($1 ~ /^axis\/laser\//) {
blocked = "L4-PYTHON-REMAP"
} else if ($1 ~ /^axis\/vismach\/5axis\/table-rotary_spindle-rotary-nutating\/demos\//) {
blocked = "L4-PYTHON-REMAP"
} else if ($2 == "remap_subroutine") {
blocked = "ASSET-ONLY"
}
print $1, $2, $3, ($4 == "" ? "-" : $4), $7, $8, runtime_family, (blocked == "" ? "-" : blocked)
}
' "$SUMMARY_FILE" > "$PATH_MATRIX_FILE"
{
printf 'sim config harness summary\n'
printf 'summary: %s\n' "$SUMMARY_FILE"
printf 'class_summary: %s\n' "$CLASS_SUMMARY_FILE"
printf 'path_matrix: %s\n' "$PATH_MATRIX_FILE"
printf 'total: %s\n' "$total"
printf 'pass: %s\n' "$pass"
printf 'fail: %s\n' "$fail"