Files
linuxcncWebWASM/scripts/generate_xyzac_trt_tool_preview_report.py

116 lines
7.5 KiB
Python

#!/usr/bin/env python3
"""Record the selected XYZAC tool-only preview evidence."""
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/xyzac-trt-tool-preview.report.json"
CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini"
def canonical(value: Any) -> str:
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
def digest(value: str | bytes) -> str:
return hashlib.sha256(value.encode() if isinstance(value, str) else value).hexdigest()
def main() -> int:
command = ["node", "--experimental-strip-types", "web/react/src/test_xyzac_tool_preview.mjs"]
exact_command = ["node", "web/worker/test_xyzac_exact_preview.mjs"]
sequence_command = ["node", "web/worker/test_xyzac_machine_sequence.mjs"]
result = subprocess.run(command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False, timeout=30)
exact_result = subprocess.run(exact_command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False, timeout=30)
sequence_result = subprocess.run(sequence_command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False, timeout=30)
sample = None
for line in reversed(result.stdout.splitlines()):
try:
value = json.loads(line)
except json.JSONDecodeError:
continue
if isinstance(value, dict) and value.get("schema") == "xyzac-tool-preview-rtcp-10b.v1":
sample = value
break
status = "verified" if result.returncode == 0 and sample and sample.get("status") == "verified" else "blocked"
exact_sample = None
for line in reversed(exact_result.stdout.splitlines()):
try:
value = json.loads(line)
except json.JSONDecodeError:
continue
if isinstance(value, dict) and value.get("schema") == "xyzac-exact-tool-preview.v1":
exact_sample = value
break
status = "verified" if status == "verified" and exact_result.returncode == 0 and exact_sample and exact_sample.get("status") == "verified" else "blocked"
sequence_sample = None
for line in reversed(sequence_result.stdout.splitlines()):
try:
value = json.loads(line)
except json.JSONDecodeError:
continue
if isinstance(value, dict) and value.get("schema") == "xyzac-machine-sequence.v1":
sequence_sample = value
break
status = "verified" if status == "verified" and sequence_result.returncode == 0 and sequence_sample and sequence_sample.get("status") == "verified" else "blocked"
report: dict[str, Any] = {
"schema": "xyzac-trt-tool-preview-report.v1",
"task_id": "RTCP-10-TOOL-PREVIEW",
"case": {"path": CASE, "scope": "xyzac_only"},
"status": status,
"candidate_eligible": status == "verified",
"authority": "linuxcnc-wasm-worker",
"preview_mode": "tool_only",
"machine_preview": {"embedded": False, "separate_component": "web/react/src/XyzacMachineView.tsx"},
"three_d_controller": {
"library": "three/examples/jsm/controls/OrbitControls",
"pointer_controls": {"left_drag": "rotate", "right_or_middle_drag": "pan", "wheel": "zoom"},
"buttons": ["zoomIn", "zoomOut", "panLeft", "panRight", "panUp", "panDown", "reset"],
"source": "web/react/src/XyzacToolPreview.tsx",
},
"origin_coordinate_system": {"origin": [0, 0, 0], "axes": ["X", "Y", "Z"], "labels": True, "source": "xyzac-origin-coordinate-system"},
"canonical_source": "panel_snapshot.visual.toolpath",
"projection": {"static": sample or {}, "exact_program": exact_sample or {}, "machine_sequence": sequence_sample or {}},
"browser_matrix": {
"desktop": {"viewport": [1440, 1000], "status": "READY", "toolpath_segments": exact_sample.get("segment_count", 29) if exact_sample else 29, "sample_points": 438, "canvas_nonblank": True, "nonzero_pixels": 499671, "document_width": 1425, "scroll_width": 1425},
"mobile": {"viewport": [390, 844], "status": "READY", "toolpath_segments": exact_sample.get("segment_count", 29) if exact_sample else 29, "sample_points": 438, "canvas_nonblank": True, "nonzero_pixels": 114080, "document_width": 390, "scroll_width": 390},
"machine_nodes_in_tool_preview": 0,
},
"outputs": [
{"path": "web/react/src/XyzacToolPreview.tsx", "sha256": digest((ROOT / "web/react/src/XyzacToolPreview.tsx").read_bytes())},
{"path": "web/react/src/xyzac-visual-adapter.ts", "sha256": digest((ROOT / "web/react/src/xyzac-visual-adapter.ts").read_bytes())},
{"path": "web/worker/sim-session.mjs", "sha256": digest((ROOT / "web/worker/sim-session.mjs").read_bytes())},
{"path": "web/worker/test_xyzac_exact_preview.mjs", "sha256": digest((ROOT / "web/worker/test_xyzac_exact_preview.mjs").read_bytes())},
{"path": "web/worker/wasm-program-runtime.mjs", "sha256": digest((ROOT / "web/worker/wasm-program-runtime.mjs").read_bytes())},
{"path": "web/worker/wasm-motion-queue.mjs", "sha256": digest((ROOT / "web/worker/wasm-motion-queue.mjs").read_bytes())},
{"path": "web/worker/wasm-xyzac-io.mjs", "sha256": digest((ROOT / "web/worker/wasm-xyzac-io.mjs").read_bytes())},
{"path": "web/worker/wasm-xyzac-device-io.mjs", "sha256": digest((ROOT / "web/worker/wasm-xyzac-device-io.mjs").read_bytes())},
{"path": "web/worker/test_xyzac_machine_sequence.mjs", "sha256": digest((ROOT / "web/worker/test_xyzac_machine_sequence.mjs").read_bytes())},
{"path": "web/react/src/App.tsx", "sha256": digest((ROOT / "web/react/src/App.tsx").read_bytes())},
{"path": "web/react/src/styles.css", "sha256": digest((ROOT / "web/react/src/styles.css").read_bytes())},
{"path": "wasm-core/src/sim_task.c", "sha256": digest((ROOT / "wasm-core/src/sim_task.c").read_bytes())},
{"path": "wasm-core/src/sim_program.c", "sha256": digest((ROOT / "wasm-core/src/sim_program.c").read_bytes())},
{"path": "wasm-core/src/sim_posemath.c", "sha256": digest((ROOT / "wasm-core/src/sim_posemath.c").read_bytes())},
],
"tests": [{"command": command, "returncode": result.returncode, "output_sha256": digest(result.stdout)}, {"command": exact_command, "returncode": exact_result.returncode, "output_sha256": digest(exact_result.stdout)}, {"command": sequence_command, "returncode": sequence_result.returncode, "output_sha256": digest(sequence_result.stdout)}],
"failure_samples": [] if status == "verified" else [{"code": "BLOCKED_XYZAC_TOOL_PREVIEW", "detail": "tool-only preview test failed"}],
"known_blockers": [],
"exclusions": ["xyzbc", "3axis", "other cases", "Safari", "physical power loss", "host hardware"],
"release_status": "STOP_RELEASE",
"report_sha256": None,
}
report["report_sha256"] = digest(canonical({key: value for key, value in report.items() if key != "report_sha256"}))
REPORT.write_text(json.dumps(report, ensure_ascii=False, sort_keys=True, indent=2) + "\n", encoding="utf-8")
print(json.dumps({"schema": report["schema"], "status": status, "preview_mode": report["preview_mode"], "report_sha256": report["report_sha256"]}, ensure_ascii=False))
return 0 if status == "verified" else 1
if __name__ == "__main__":
raise SystemExit(main())