Files
linuxcncWebWASM/scripts/generate_axis3_tutorial_sequence_report.py

68 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""Compare selected-case native and Worker operation ledgers."""
from __future__ import annotations
import hashlib
import json
import subprocess
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
REPORT = ROOT / "coverage/axis3-tutorial-sequence.report.json"
def canonical(value: Any) -> str:
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
def command(args: list[str]) -> tuple[int, str, str]:
result = subprocess.run(args, cwd=ROOT, capture_output=True, text=True, check=False)
return result.returncode, result.stdout, result.stderr
def last_json(text: str, schema: str) -> dict[str, Any] | None:
for line in reversed(text.splitlines()):
try: value = json.loads(line)
except json.JSONDecodeError: continue
if value.get("schema") == schema: return value
return None
def generate() -> dict[str, Any]:
native_rc, native_out, native_err = command(["python3", "scripts/run_axis3_tutorial_native_sequence.py", "--output", "coverage/axis3-tutorial-native-sequence.report.json"])
worker_rc, worker_out, worker_err = command(["node", "web/worker/test_axis3_tutorial_sequence.mjs"])
native = json.loads((ROOT / "coverage/axis3-tutorial-native-sequence.report.json").read_text(encoding="utf-8"))
worker = last_json(worker_out, "axis3-tutorial-sequence.v1")
native_rows = [row for row in native.get("operations", []) if row.get("operation") != "MANUAL_TOOLCHANGE"]
worker_rows = worker.get("operations", []) if worker else []
first_divergence = None
for index, native_row in enumerate(native_rows):
native_error = native_row.get("error")
if native_error:
operation = native_row.get("operation")
first_divergence = {"index": index, "operation": operation, "native_error": native_error, "worker_status": "verified", "reason": "native 3axis-tutorial HAL has no simulated_home component; HOME_ALL remains unhomed" if operation == "HOME_ALL" else "native_operation_error"}
break
report: dict[str, Any] = {
"schema": "axis3-tutorial-sequence-report.v1",
"task_id": "TUTORIAL-12",
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
"case": "linuxcnc/configs/sim/axis/vismach/3axis-tutorial",
"status": "blocked" if native.get("status") != "verified" or first_divergence else "verified",
"native": {"status": native.get("status"), "returncode": native_rc, "report_sha256": native.get("report_sha256"), "stdout_sha256": hashlib.sha256(native_out.encode()).hexdigest(), "stderr_sha256": hashlib.sha256(native_err.encode()).hexdigest()},
"worker": {"status": "verified" if worker_rc == 0 and worker else "blocked", "returncode": worker_rc, "sample": worker, "stdout_sha256": hashlib.sha256(worker_out.encode()).hexdigest(), "stderr_sha256": hashlib.sha256(worker_err.encode()).hexdigest()},
"first_divergence": first_divergence,
"host_boundaries": native.get("host_boundaries", []),
"operation_contract": ["STATE_ESTOP_RESET", "STATE_ON", "MODE_MANUAL", "HOME_Z", "HOME_X", "HOME_Y", "JOG_X_INCREMENT", "JOG_Y_INCREMENT", "JOG_Z_INCREMENT", "MODE_MDI", "MDI_G21_G90", "MDI_G0", "MDI_G1", "PROGRAM_OPEN", "MODE_AUTO", "PROGRAM_RUN", "PROGRAM_PAUSE", "PROGRAM_RESUME", "PROGRAM_ABORT", "TOOL_CHANGE_T1"],
"known_boundaries": ["case INI has no HOME_SEQUENCE, so AXIS source semantics require individual Z/X/Y home rather than Home All", "manual toolchange is completed through the source-defined hal_manualtoolchange.change_button pin", "Worker fixed-tick sequence remains independently verified"],
}
report["report_sha256"] = hashlib.sha256(canonical(report).encode()).hexdigest()
REPORT.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
print(f"axis3_tutorial_sequence={report['status']} report_sha256={report['report_sha256']}")
return report
if __name__ == "__main__":
raise SystemExit(0 if generate()["status"] in {"verified", "blocked"} else 1)