68 lines
4.0 KiB
Python
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)
|