235 lines
7.7 KiB
Bash
235 lines
7.7 KiB
Bash
#!/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"
|
|
print_kv tool_db_runtime_probe_note "missing_source_requirements_keep_tool_db_process_blocked"
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$runtime_ready" != "1" ]]; then
|
|
print_kv tool_db_runtime_probe_status "skipped_missing_host_runtime"
|
|
print_kv tool_db_runtime_probe_note "missing_host_runtime_requirements_keep_tool_db_process_blocked"
|
|
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
|