Complete XYZAC LinuxCNC WASM parity and smooth 3D preview
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
完整仿真功能范围、任务分解和 M0-M8 里程碑见 [`完整仿真移植实施计划`](docs/linuxcnc-web-wasm-complete-simulation-plan.md);技术实施接续入口为 [`nextTask1.md`](nextTask1.md)。
|
完整仿真功能范围、任务分解和 M0-M8 里程碑见 [`完整仿真移植实施计划`](docs/linuxcnc-web-wasm-complete-simulation-plan.md);技术实施接续入口为 [`nextTask1.md`](nextTask1.md)。
|
||||||
|
|
||||||
|
五轴 XYZAC TRT Web/WASM 源码对标轨道的独立接续入口为 [`nextTask-rtcp.md`](nextTask-rtcp.md),详细设计和实施资料位于 [`doc-rtcp`](doc-rtcp/README.md)。
|
||||||
|
|
||||||
恢复或开始工作时,首先读取 [`nextTask1.md`](nextTask1.md)。它是当前仿真轨道的唯一接续入口,刻意限制在 8 KiB 以内,不包含聊天记录、完整日志或历史证据。只有定位旧证据时才按需读取 [`docs/task-history.md`](docs/task-history.md) 和其中引用的产物。
|
恢复或开始工作时,首先读取 [`nextTask1.md`](nextTask1.md)。它是当前仿真轨道的唯一接续入口,刻意限制在 8 KiB 以内,不包含聊天记录、完整日志或历史证据。只有定位旧证据时才按需读取 [`docs/task-history.md`](docs/task-history.md) 和其中引用的产物。
|
||||||
|
|
||||||
项目整体流程、LinuxCNC 案例到 WASM/Worker/前端的映射,以及后续任务起点见
|
项目整体流程、LinuxCNC 案例到 WASM/Worker/前端的映射,以及后续任务起点见
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"schema": "runtime-mapping-report.v1",
|
"schema": "runtime-mapping-report.v1",
|
||||||
"status": "passed",
|
"status": "passed",
|
||||||
"mapped_error_codes": 18,
|
"mapped_error_codes": 18,
|
||||||
"commands": 27,
|
"commands": 50,
|
||||||
"limits": 6,
|
"limits": 6,
|
||||||
"real_exclusions": 3
|
"real_exclusions": 3
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,20 +5,20 @@
|
|||||||
"host devices, unsupported dynamic loaders, and missing LinuxCNC source resources remain blocked"
|
"host devices, unsupported dynamic loaders, and missing LinuxCNC source resources remain blocked"
|
||||||
],
|
],
|
||||||
"catalog": {
|
"catalog": {
|
||||||
"catalog_sha256": "ac762b8b27b4b7988c0c8c532c6299a0957a69538ccb42bc65f78593b8ab4815",
|
"catalog_sha256": "3c35f4b6df095ea0c4d3fcaacab1b5eee08274e451e0229b9668ca6107ef0167",
|
||||||
"module": "web/workspace/sim-case-catalog.mjs"
|
"module": "web/workspace/sim-case-catalog.mjs"
|
||||||
},
|
},
|
||||||
"counts": {
|
"counts": {
|
||||||
"cases": 235,
|
"cases": 236,
|
||||||
"files": 2000,
|
"files": 2006,
|
||||||
"indexed_blocked": 226,
|
"indexed_blocked": 227,
|
||||||
"indexed_static_ready": 9
|
"indexed_static_ready": 9
|
||||||
},
|
},
|
||||||
"implementation_status": "verified",
|
"implementation_status": "verified",
|
||||||
"inputs": [
|
"inputs": [
|
||||||
{
|
{
|
||||||
"path": "coverage/full-configuration-startup-matrix.report.json",
|
"path": "coverage/full-configuration-startup-matrix.report.json",
|
||||||
"sha256": "f2140e4c1685ca17738645faedb962dcaa7210200c9151084bd5df40285eadf9"
|
"sha256": "9345288c2e5353d382a17e2ff18f881d8516f2e792920325c2b5cc28ac17a2f9"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "coverage/hal-component-manifest.report.json",
|
"path": "coverage/hal-component-manifest.report.json",
|
||||||
@@ -27,33 +27,33 @@
|
|||||||
],
|
],
|
||||||
"outputs": [
|
"outputs": [
|
||||||
{
|
{
|
||||||
"bytes": 226050,
|
"bytes": 227026,
|
||||||
"path": "web/workspace/sim-case-catalog.mjs",
|
"path": "web/workspace/sim-case-catalog.mjs",
|
||||||
"sha256": "a43d9868fb8d749cd9aa7958cfc6630cbc1bbf78827feb612d7c2f952d267b99",
|
"sha256": "e90af5f821e30e5f655dae245448b4ae691961d7b03ee47138bdcbbbb6d16b6a",
|
||||||
"status": "present"
|
"status": "present"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bytes": 11291,
|
"bytes": 14544,
|
||||||
"path": "web/workspace/sim-case-loader.mjs",
|
"path": "web/workspace/sim-case-loader.mjs",
|
||||||
"sha256": "9f76f8fd886a9e39990b0036c4a75c1cc5a02325da5487bbe5793702a746f8ee",
|
"sha256": "a79b4c89e2d06f7d7dba5378848983ae1f8c2baa56729f1238bf31210c1b5675",
|
||||||
"status": "present"
|
"status": "present"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bytes": 3444,
|
"bytes": 3686,
|
||||||
"path": "web/workspace/hal-configuration.mjs",
|
"path": "web/workspace/hal-configuration.mjs",
|
||||||
"sha256": "3979f687b9a808256b918b9e1919a0ed6d59c591cb990d4b7bdb57903c50a385",
|
"sha256": "44823665f199acc48bbfdf0903acf19e95d5ac4aeededcd570293cb7456169d6",
|
||||||
"status": "present"
|
"status": "present"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bytes": 2911,
|
"bytes": 3546,
|
||||||
"path": "web/workspace/test_sim_case_loader.mjs",
|
"path": "web/workspace/test_sim_case_loader.mjs",
|
||||||
"sha256": "0a07affb488505f91889cb3b843eb84782725b9f337313f1023510778e3381c3",
|
"sha256": "99f1656f402491c4f1d56b495d7274f440775bc4b557720dd82640b904bc14ba",
|
||||||
"status": "present"
|
"status": "present"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bytes": 16777,
|
"bytes": 16849,
|
||||||
"path": "web/app/app.mjs",
|
"path": "web/app/app.mjs",
|
||||||
"sha256": "5ae3c3df053383f5ae3242aba20c2c8818b04639868e96514955bd1a760ec2ef",
|
"sha256": "f2af4be261941e4eaa48e32c1d929fcb8adfa4fe91475c398eec03f1fa99a8a4",
|
||||||
"status": "present"
|
"status": "present"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
"status": "present"
|
"status": "present"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"report_sha256": "b17fe0aad525c7b9e914d6a1ff384ab640bc8d3f475e8553aa3745bed02c49c0",
|
"report_sha256": "09ddb06b10860f9e37d7cb8d40499cb8b6fe3e121b88cd3b371b9df6b3fa91c5",
|
||||||
"schema": "linuxcnc-sim-case-catalog-report.v1",
|
"schema": "linuxcnc-sim-case-catalog-report.v1",
|
||||||
"scope": "configs/sim case catalog and exact-path browser loader input",
|
"scope": "configs/sim case catalog and exact-path browser loader input",
|
||||||
"status": "verified",
|
"status": "verified",
|
||||||
|
|||||||
42
coverage/task-packets/PARITY-00.handoff.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "PARITY-00",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"result": "Native C, WASM C and Worker protocol/control/state/canonical/recovery traces compare tick-by-tick with positive divergence detection",
|
||||||
|
"report": {"path": "coverage/xyzac-trt-trace-parity.report.json", "sha256": "39c63aa9af38fd98e49fd68ec2401ee8715fcafef98d47c070a47ab6f6dc0a48"},
|
||||||
|
"outputs": [
|
||||||
|
{"path": "scripts/compare_xyzac_native_wasm_trace.py", "sha256": "89834553f2a7afc47e351bbddd33f83259665b943cd7f22fe48acbba32aaf7c4"},
|
||||||
|
{"path": "web/worker/test_xyzac_trace_parity.mjs", "sha256": "f5c6a7cd3e8b7f72bb1b25b5c368c0e52d494b9c811022d34435e668612ec4ce"},
|
||||||
|
{"path": "coverage/xyzac-trt-trace-parity.report.json", "sha256": "ef914bb7823b9cdb0f6e15ca409515659d5575d09cdc6da2c458cd81fafa595a"},
|
||||||
|
{"path": "wasm-core/tests/sim_xyzac_trace_test.c", "sha256": "0a3d5f748d9f69f4212c3a1cdd65fe2986c253442c4de2de78de2e5a5635ff08"}
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"baseline": "passed",
|
||||||
|
"download_index": "passed",
|
||||||
|
"pointer": "passed",
|
||||||
|
"protocol_level": "passed",
|
||||||
|
"control_level": "passed",
|
||||||
|
"state_level": "passed",
|
||||||
|
"canonical_level": "passed",
|
||||||
|
"recovery_level": "passed",
|
||||||
|
"native_wasm_exact": "passed",
|
||||||
|
"native_worker_semantic": "passed",
|
||||||
|
"positive_divergence_tick": "passed",
|
||||||
|
"report_self_hash": "passed",
|
||||||
|
"native_ctest_21": "passed",
|
||||||
|
"react_typecheck_build": "passed"
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"levels": ["protocol", "control", "state", "canonical", "recovery"],
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"positive_sample_tick": 2,
|
||||||
|
"positive_sample_path": "$[1].active_type",
|
||||||
|
"native_wasm_trace_sha256": "18d2ef9d77f9a7a5bf2fa8d9380c8b04383a58ee2b0258364cba31ec34a168f0"
|
||||||
|
},
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": "RTCP-09",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
40
coverage/task-packets/RELEASE-00.handoff.json
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "RELEASE-00",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"result": "Selected xyzac-trt.ini WASM-only release gate verified; release remains STOP_RELEASE by explicit scope governance",
|
||||||
|
"report": {"path": "coverage/xyzac-trt-wasm-only-release-gate.report.json", "sha256": "368bc17b4ade17b8567abe299b7d7395abc0f09d187028a0cb750aa170b5891c"},
|
||||||
|
"outputs": [
|
||||||
|
{"path": "scripts/generate_xyzac_trt_wasm_only_release_gate.py", "sha256": "06e4c564d0b4fc482150ffd19ae7ce93d553a6bf525144159875d824054eca70"},
|
||||||
|
{"path": "coverage/xyzac-trt-wasm-only-release-gate.report.json", "sha256": "f85319a0d1a5bc28a2ab06aed6c043ba1fff8a5d69d8901754b6d62ce6eb0c1c"}
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"baseline": "passed",
|
||||||
|
"download_index": "passed",
|
||||||
|
"pointer": "passed",
|
||||||
|
"wasm_authority": "passed",
|
||||||
|
"all_13_selected_case_reports": "passed",
|
||||||
|
"native_worker_trace": "passed",
|
||||||
|
"browser_matrix": "passed",
|
||||||
|
"recovery_endpoint": "passed",
|
||||||
|
"scope_exclusions_explicit": "passed",
|
||||||
|
"release_report_self_hash": "passed"
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"selected_case": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"candidate_eligible": false,
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"blockers": [
|
||||||
|
"HOST_EXCLUDED: Safari not in project scope",
|
||||||
|
"REAL-EXCLUDED: physical power loss not simulated",
|
||||||
|
"HOST_EXCLUDED: xyzbc/3axis/other LinuxCNC cases out of scope"
|
||||||
|
],
|
||||||
|
"first_divergent_tick": null
|
||||||
|
},
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": null,
|
||||||
|
"next_task_status": "complete",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
42
coverage/task-packets/RTCP-01.handoff.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "RTCP-01",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"result": "XYZAC Worker topology, logical scheduler, command ledger and cancellation/reconcile contract verified",
|
||||||
|
"outputs": [
|
||||||
|
{"path": "web/worker/xyzac-session.mjs", "sha256": "9cbbbd08ae372e515bafe906f87fe3a9552571f52997b4e5495b42a36097c6b3"},
|
||||||
|
{"path": "web/worker/xyzac-scheduler.mjs", "sha256": "0c7544298869c9c2858cce1275db8f3f2034655a5aa92aac5bd6dbf2b16e3221"},
|
||||||
|
{"path": "web/worker/xyzac-command-ledger.mjs", "sha256": "90d5fd10547f544338014a026a407cfb73f08c639bf0f1bef9c19a85fbcd16f5"},
|
||||||
|
{"path": "web/worker/test_xyzac_session.mjs", "sha256": "76cd74a09a0318a33d70529f7c5b9cad25fd0daebd6dd8bb05f87b42626858ef"}
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"baseline": "passed",
|
||||||
|
"download_index": "passed",
|
||||||
|
"pointer": "passed",
|
||||||
|
"xyzac_session": "passed",
|
||||||
|
"xyzac_case_worker": "passed",
|
||||||
|
"xyzac_program_authority": "passed",
|
||||||
|
"xyzac_io_authority": "passed",
|
||||||
|
"ledger_replay_commit": "passed",
|
||||||
|
"ledger_timeout_reconcile": "passed",
|
||||||
|
"cancel_no_blind_retry": "passed",
|
||||||
|
"scheduler_logical_tick": "passed",
|
||||||
|
"native_ctest": "20/20 passed",
|
||||||
|
"react_typecheck_build": "passed"
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"session_authority": "linuxcnc-wasm-worker",
|
||||||
|
"machine": {"coordinates": "XYZAC", "joint_count": 5, "kinematics": "xyzac-trt", "rotary_units": "degrees"},
|
||||||
|
"scheduler_tick": 3,
|
||||||
|
"scheduler_state_hash": "fnv1a64:d6b307bada354385",
|
||||||
|
"replayed": true,
|
||||||
|
"committed_operation": "step-1",
|
||||||
|
"pending_after_abort": 0,
|
||||||
|
"first_divergent_tick": null
|
||||||
|
},
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": "RTCP-02",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
35
coverage/task-packets/RTCP-02.handoff.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "RTCP-02",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"result": "Exact xyzac-trt.ini resource closure and startup state machine verified; xyzbc/3axis/other cases excluded",
|
||||||
|
"report": {"path": "coverage/xyzac-trt-startup.report.json", "sha256": "12cdae5c4547d8f2647da1f4721e147807ba738474adeb60901b3d837e4cb39e"},
|
||||||
|
"outputs": [
|
||||||
|
{"path": "web/workspace/xyzac-trt-case-loader.mjs", "sha256": "8480a20a13301cfc8dd37fbe66cf9b4cbfbfd7bd18623e81499a583c72f9f44a"},
|
||||||
|
{"path": "coverage/xyzac-trt-startup.report.json", "sha256": "56e040b645bcefc3d6c57244cb2d3aba16f421ff5137089d9c3336970dfd968b"},
|
||||||
|
{"path": "web/worker/test_xyzac_startup.mjs", "sha256": "013023e5f0a01d93ed8ab568879937f462a97e2fe08742995f15775c867fb8aa"},
|
||||||
|
{"path": "scripts/generate_xyzac_trt_startup_report.py", "sha256": "0b7fca4feb6039423b92164cd57802b7aaaefa7e141dffd38cb024f9b031fb5e"}
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"baseline": "passed",
|
||||||
|
"download_index": "passed",
|
||||||
|
"pointer": "passed",
|
||||||
|
"xyzac_startup_test": "passed",
|
||||||
|
"startup_report_self_hash": "passed",
|
||||||
|
"exact_resource_count": 15,
|
||||||
|
"required_ini_sections": 7,
|
||||||
|
"hal_record_count": 165,
|
||||||
|
"missing_resource_fail_closed": "passed",
|
||||||
|
"xyzac_worker_case": "passed",
|
||||||
|
"native_ctest": "20/20 passed",
|
||||||
|
"react_typecheck": "passed"
|
||||||
|
},
|
||||||
|
"startup_state_sequence": ["TASK_NEW", "CONFIG_RESOLVED", "HAL_READY", "RUNTIME_READY", "TASK_READY"],
|
||||||
|
"machine": {"coordinates": "XYZAC", "joint_count": 5, "rotary_units": "degrees", "kinematics": "xyzac-trt"},
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": "RTCP-03",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
41
coverage/task-packets/RTCP-03.handoff.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "RTCP-03",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"result": "XYZAC authoritative snapshot, axis_state projection and gate matrix verified",
|
||||||
|
"report": {"path": "coverage/xyzac-trt-gate-matrix.report.json", "sha256": "25deaea6a1fb442d86e660319fdd1d3061ca7da0af7d48858258aad4c71ec56c"},
|
||||||
|
"outputs": [
|
||||||
|
{"path": "web/worker/test_xyzac_gate_matrix.mjs", "sha256": "07e4f05f3b7bf470fb87ecea077587e7584928d28fe7e28932f8a649c960525d"},
|
||||||
|
{"path": "coverage/xyzac-trt-gate-matrix.report.json", "sha256": "7c8cb2dad644e54462b30dd0ec3e3e792ecee8da67c9c2de236bc38f1c0e6188"},
|
||||||
|
{"path": "web/app/axis-contract.mjs", "sha256": "51854238857e03c42abe91f1dec4da3f49734386a6cf3d99dae97d7f07ebafd5"},
|
||||||
|
{"path": "web/worker/sim-session.mjs", "sha256": "bdde47643f15780fe32cc10fe1b8542bd70c3170fd057b26d34b5c79ccd9dc29"},
|
||||||
|
{"path": "scripts/generate_xyzac_trt_gate_matrix_report.py", "sha256": "a5ec4edbe3f416f134812fe8e4b7222a426de0aa7b524fb9deafbcc3d999b6a9"}
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"baseline": "passed",
|
||||||
|
"download_index": "passed",
|
||||||
|
"pointer": "passed",
|
||||||
|
"xyzac_gate_matrix": "passed",
|
||||||
|
"gate_report_self_hash": "passed",
|
||||||
|
"xyzac_worker_case": "passed",
|
||||||
|
"panel_gate_regression": "passed",
|
||||||
|
"axis_contract_regression": "passed",
|
||||||
|
"react_typecheck": "passed"
|
||||||
|
},
|
||||||
|
"gate_scenarios": {
|
||||||
|
"no_file": "axis_taskfile_missing",
|
||||||
|
"highlight_missing": "axis_highlight_missing",
|
||||||
|
"switchkins_hal_missing": "xyzac_hal_pin_vars_missing",
|
||||||
|
"switchkins_estop": "xyzac_estop_active",
|
||||||
|
"running_pause_abort": "verified",
|
||||||
|
"paused_resume": "verified",
|
||||||
|
"vismach_clear_visual_only": "verified"
|
||||||
|
},
|
||||||
|
"state_hash": "fnv1a64:a95c3da31e470f59",
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": "RTCP-04",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
20
coverage/task-packets/RTCP-04.handoff.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "RTCP-04",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"result": "WASM canonical XYZAC program load/preview/ledger flow verified",
|
||||||
|
"report": {"path": "coverage/xyzac-trt-program-preview.report.json", "sha256": "ad151b42a738667f0d62f1811409190adc06f5b59f3bde9019eec8b895abf736"},
|
||||||
|
"outputs": [
|
||||||
|
{"path": "web/worker/test_xyzac_program_preview.mjs", "sha256": "2ecec15067d0350741b1a02d14dd4aed3587ef1bc236065a51c28bc3391c2eef"},
|
||||||
|
{"path": "coverage/xyzac-trt-program-preview.report.json", "sha256": "3398060c346e51691717b43c164322c333cab951a5c83ac3dd5e87fe0e452429"},
|
||||||
|
{"path": "web/worker/wasm-program-runtime.mjs", "sha256": "3a91b2111f9803d58c8dc6dfc8fb0db4f7343cc7b87108bc53b642a13032c844"}
|
||||||
|
],
|
||||||
|
"checks": {"baseline": "passed", "download_index": "passed", "pointer": "passed", "xyzac_program_preview": "passed", "preview_report_self_hash": "passed", "source_path_hash": "passed", "canonical_segments": "passed", "invalid_gcode_fail_closed": "passed", "empty_program_fail_closed": "passed", "program_load_replay": "passed", "xyzac_worker_case": "passed", "react_typecheck": "passed"},
|
||||||
|
"evidence": {"source_hash": "fnv1a64:b7f4678312207b31", "line_count": 5, "segment_count": 2, "preview_hash": "fnv1a64:e226b45322b06337", "state_hash": "fnv1a64:d1505250bdc90614", "preview_authority": "linuxcnc-wasm-sim_program", "first_divergent_tick": null},
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": "RTCP-05",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
20
coverage/task-packets/RTCP-05.handoff.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "RTCP-05",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"result": "WASM XYZAC Run/Pause/Resume/Step/Abort/Restart/Run-from-line workflow verified",
|
||||||
|
"report": {"path": "coverage/xyzac-trt-workflow.report.json", "sha256": "5abbef3106546d65d0951cce07777532903653abeed52221a9c8ecd142d5bc0d"},
|
||||||
|
"outputs": [
|
||||||
|
{"path": "web/worker/test_xyzac_workflow.mjs", "sha256": "6c738c6993d43b4ce16feecfe87d2b507824a88b06ead2f0659db6ca25c346b5"},
|
||||||
|
{"path": "scripts/generate_xyzac_trt_workflow_report.py", "sha256": "f1f4b34105b64d7caea34921ec8504d0c58006ee0fd9fa15561cecb1cba93abb"},
|
||||||
|
{"path": "coverage/xyzac-trt-workflow.report.json", "sha256": "0a9749ed647fb5ead26681719468470b7cf9e765ea4df5881d74404c019c0a5d"}
|
||||||
|
],
|
||||||
|
"checks": {"baseline": "passed", "download_index": "passed", "pointer": "passed", "xyzac_workflow": "passed", "workflow_report_self_hash": "passed", "run_pause_resume_step": "passed", "abort_queue_empty": "passed", "restart_source_hash": "passed", "run_from_line": "passed", "checkpoint_recovery": "passed", "xyzac_preview_regression": "passed", "xyzac_gate_regression": "passed", "react_typecheck": "passed"},
|
||||||
|
"evidence": {"tick_after_first": 1, "active_before_abort": 1, "pause_cursor": 1, "restart_source_hash": "fnv1a64:7e075e2e695feafd", "run_from_line": 4, "recovery": "recovered", "final_state_hash": "fnv1a64:de58dbdf1fa2606f", "first_divergent_tick": null},
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": "RTCP-06",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
21
coverage/task-packets/RTCP-06.handoff.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "RTCP-06",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"result": "WASM XYZAC MDI, five-axis jog, home, limits and ESTOP recovery verified",
|
||||||
|
"report": {"path": "coverage/xyzac-trt-manual-control.report.json", "sha256": "ec2180de513726dc70c0940ed15e03a7d9e54714960ba7246aba19eaf95d849b"},
|
||||||
|
"outputs": [
|
||||||
|
{"path": "web/worker/test_xyzac_manual_control.mjs", "sha256": "75ed33fcbe9f40f0c244029b4dd57017c4f9944107c88c5500ade5c6e67fe893"},
|
||||||
|
{"path": "coverage/xyzac-trt-manual-control.report.json", "sha256": "012871cb9758621d0b38a0acf240e5d077b332a72bad42d9592fe39c86567a99"},
|
||||||
|
{"path": "wasm-core/src/sim_posemath.c", "sha256": "2e63d8cc2bf4275a89238b2622b41dcd8dfabfe6d24ef5918628083a4f4f5216"},
|
||||||
|
{"path": "wasm-core/src/sim_motion_queue.c", "sha256": "252de61937b685689c479be60e6ce398364790ef5034e54cd89db138b368937f"}
|
||||||
|
],
|
||||||
|
"checks": {"baseline": "passed", "download_index": "passed", "pointer": "passed", "xyzac_manual_control": "passed", "manual_report_self_hash": "passed", "home_five_joints": "passed", "jog_xyz": "passed", "jog_ac_degrees": "passed", "mdi": "passed", "nonfinite_speed": "passed", "range_boundary": "passed", "following_error": "passed", "mdi_running_gate": "passed", "estop_recovery": "passed", "xyzac_workflow_regression": "passed", "react_typecheck": "passed"},
|
||||||
|
"evidence": {"linear_axes": ["X", "Y", "Z"], "angular_axes": ["A", "C"], "angular_units": "degrees", "home_joint_count": 5, "jog_state_hash": "fnv1a64:458a605f469d43cc", "first_divergent_tick": null},
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": "RTCP-07",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
60
coverage/task-packets/RTCP-07.handoff.json
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "RTCP-07",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"result": "WASM XYZAC HAL, PyVCP, tool, spindle, coolant, device acknowledgement and alarm authority verified",
|
||||||
|
"report": {
|
||||||
|
"path": "coverage/xyzac-trt-hal-tool-alarm.report.json",
|
||||||
|
"sha256": "3b59d77a19277228a3d37d00486c5344571392651adcf4b7d019217d0880fbe8"
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"path": "web/worker/test_xyzac_hal_tool_alarm.mjs",
|
||||||
|
"sha256": "731ff64fc5b9abd70c6ab6649a2644e51e363bda710a0d4ec9faeffa3bb77db3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "coverage/xyzac-trt-hal-tool-alarm.report.json",
|
||||||
|
"sha256": "d0e8fef743ea45ed230a29bb5deefa563f07d82df85e6f9c70be56f67e79031a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/worker/wasm-xyzac-io.mjs",
|
||||||
|
"sha256": "c5b77654b952e0656edc9d649f07af6e3244d649313761b9ff42aed329c98f0b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/worker/sim-session.mjs",
|
||||||
|
"sha256": "ec5846105eeecb5dcf5d9ec579ff00b185eaaba2039caeabe1e9a199a6749873"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"baseline": "passed",
|
||||||
|
"download_index": "passed",
|
||||||
|
"pointer": "passed",
|
||||||
|
"xyzac_hal_tool_alarm": "passed",
|
||||||
|
"hal_tool_alarm_report_self_hash": "passed",
|
||||||
|
"pyvcp_switches": "passed",
|
||||||
|
"vismach_clear_visual_only": "passed",
|
||||||
|
"hal_write_allowlist": "passed",
|
||||||
|
"tool_spindle_coolant": "passed",
|
||||||
|
"device_ack_timeout_abort": "passed",
|
||||||
|
"xyzac_io_regression": "passed",
|
||||||
|
"xyzac_manual_control_regression": "passed",
|
||||||
|
"native_ctest": "passed",
|
||||||
|
"react_typecheck_build": "passed"
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"pyvcp_m_codes": ["M429", "M428", "M430"],
|
||||||
|
"hal_allowed": "motion.probe-input",
|
||||||
|
"hal_denied_code": "wasm_hal_write_denied",
|
||||||
|
"tool": 7,
|
||||||
|
"spindle_rpm": 1200,
|
||||||
|
"ack_timeout_code": "wasm_sim_device_ack_ack_-1008",
|
||||||
|
"state_hash": "fnv1a64:78582773ec5a3bbe",
|
||||||
|
"first_divergent_tick": null
|
||||||
|
},
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": "RTCP-08",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
60
coverage/task-packets/RTCP-08.handoff.json
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "RTCP-08",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"result": "WASM XYZAC identity/TRT/userk kinematics, offsets, limits, multi-turn G93 arc, switchkins and lifecycle recovery verified",
|
||||||
|
"report": {
|
||||||
|
"path": "coverage/xyzac-trt-rtcp.report.json",
|
||||||
|
"sha256": "c370004d8f2d962937b3b91abe2b0dd1632283f570e29d2d194731bc0fe0a083"
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
{"path": "web/worker/test_xyzac_rtcp.mjs", "sha256": "9bc14997e56a8d0f6f0043b3a92fa5efe0fc8b3fa9c973bffa9045446973208f"},
|
||||||
|
{"path": "coverage/xyzac-trt-rtcp.report.json", "sha256": "b1562d6d2c9cd5144c7c8c9b334bd8bf67919ea7297f103ec67cb32773608d08"},
|
||||||
|
{"path": "web/worker/wasm-xyzac-kinematics.mjs", "sha256": "f14581a52830df5f55d612a1ec88d82737bb429ff63b5267ea3903984be6050e"},
|
||||||
|
{"path": "web/worker/sim-session.mjs", "sha256": "f3b3f78146a08ad0f770bceae9cb46e66dd997c1270e3be7e27c544c2f5d3b6b"},
|
||||||
|
{"path": "wasm-core/src/sim_interpreter.c", "sha256": "714396428b6826d9ac8c05d654dcc33acf24eef4b6393f610c8997997e540e5a"}
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"baseline": "passed",
|
||||||
|
"download_index": "passed",
|
||||||
|
"pointer": "passed",
|
||||||
|
"xyzac_rtcp_k0_k6": "passed",
|
||||||
|
"rtcp_report_self_hash": "passed",
|
||||||
|
"identity_forward_inverse": "passed",
|
||||||
|
"xyzac_forward_inverse_offsets": "passed",
|
||||||
|
"userk_static_capability": "passed",
|
||||||
|
"dynamic_userk_rejection": "passed",
|
||||||
|
"switchkins_pose_preservation": "passed",
|
||||||
|
"g93_multiturn_arc": "passed",
|
||||||
|
"pause_abort_restart_recovery": "passed",
|
||||||
|
"native_ctest_20": "passed",
|
||||||
|
"wasm_switchkins_kinematics_userkins": "passed",
|
||||||
|
"xyzac_worker_regressions": "passed",
|
||||||
|
"motion_checkpoint_regression": "passed",
|
||||||
|
"react_typecheck_build": "passed"
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"coordinates": "XYZAC",
|
||||||
|
"linear_units": "mm",
|
||||||
|
"angular_units": "deg",
|
||||||
|
"range_error": "wasm_xyzac_forward_-702",
|
||||||
|
"nonfinite_error": "wasm_xyzac_forward_-701",
|
||||||
|
"dynamic_userk_error": -712,
|
||||||
|
"helix_turn": -3,
|
||||||
|
"recovered_active_type": 1,
|
||||||
|
"recovered_state_hash": "fnv1a64:8c57efd84475776a",
|
||||||
|
"first_divergent_tick": null
|
||||||
|
},
|
||||||
|
"failure_samples": [
|
||||||
|
{
|
||||||
|
"classification": "HOST_EXCLUDED",
|
||||||
|
"code": "wasm_ctest_node18_local_url",
|
||||||
|
"detail": "CTest sim_api_test uses Node 18 fetch on a bare local .wasm path; real Node 24 Worker module and single-file WASM tests passed"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"next_task": "PARITY-00",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
42
coverage/task-packets/RTCP-09.handoff.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "RTCP-09",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"result": "XYZAC additive Worker commands, typed snapshot projection, React controls, gate/operation/alarm/recovery visibility and browser layout verified",
|
||||||
|
"report": {"path": "coverage/xyzac-trt-protocol-react.report.json", "sha256": "ad83b98df362f1f7e25effbdbf43da2b2ea4e2b0f1f185568f33495c88aba854"},
|
||||||
|
"outputs": [
|
||||||
|
{"path": "web/react/src/XyzacTrtPanel.tsx", "sha256": "f17e25f74d87a39a1bd7520f65cf1b285cb4c38d8e4fcaec13320e41c4ca6511"},
|
||||||
|
{"path": "web/react/src/xyzac-panel-contract.ts", "sha256": "650ac562d6153c992762a138752e5821e0d4fc16706622511cb94e4a733e6733"},
|
||||||
|
{"path": "coverage/xyzac-trt-protocol-react.report.json", "sha256": "94bb31615c0cbe5a227fbfd6af42ed1b6ace82880d2453bd3cde89340647fc6d"},
|
||||||
|
{"path": "web/worker/test_xyzac_protocol_react.mjs", "sha256": "69d942dfdcb7bc0d7fb26d6708220a47af13aa84acdcf490780980f6de0cc03d"}
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"baseline": "passed",
|
||||||
|
"download_index": "passed",
|
||||||
|
"pointer": "passed",
|
||||||
|
"additive_power": "passed",
|
||||||
|
"kinematics_select": "passed",
|
||||||
|
"visual_clear_visual_only": "passed",
|
||||||
|
"gate_rejection_reason": "passed",
|
||||||
|
"operation_pending_ack_result": "passed",
|
||||||
|
"react_worker_only_static_audit": "passed",
|
||||||
|
"react_typecheck_build": "passed",
|
||||||
|
"browser_desktop": "passed",
|
||||||
|
"browser_mobile": "passed",
|
||||||
|
"canvas_nonblank": "passed",
|
||||||
|
"horizontal_overflow": "passed"
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"commands": ["POWER", "KINEMATICS_SELECT", "VISUAL_CLEAR"],
|
||||||
|
"components": ["XyzacTrtPanel", "XyzacProgramPane", "XyzacKinematicsControl", "XyzacManualPanel", "XyzacDiagnostics"],
|
||||||
|
"browser": {"desktop": [1440, 1000], "mobile": [390, 844], "desktop_status": "READY", "mobile_status": "READY", "desktop_scroll_width": 1425, "mobile_scroll_width": 390},
|
||||||
|
"state_hash": "fnv1a64:4034107d88cc3171",
|
||||||
|
"first_divergent_tick": null
|
||||||
|
},
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": "RTCP-10",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
43
coverage/task-packets/RTCP-10.handoff.json
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "RTCP-10",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"result": "Three.js XYZAC TRT Vismach hierarchy, signed table/saddle/spindle translations, A/C rotations, tool offset and authoritative visual trace verified",
|
||||||
|
"report": {"path": "coverage/xyzac-trt-threejs.report.json", "sha256": "592f729730cb122238edefd90417399cd2f21a2084603001d1b46aadad7b261f"},
|
||||||
|
"outputs": [
|
||||||
|
{"path": "web/react/src/XyzacMachineView.tsx", "sha256": "2645e2dadeda98357e73974ae7a48b5fb825a2362ddca33689604bb11ee78a71"},
|
||||||
|
{"path": "web/react/src/XyzacToolPreview.tsx", "sha256": "32f6879594c3ed9bb6a9f2bb17650ea2cfb89a972c93ce31091bcccc73cdb6ef"},
|
||||||
|
{"path": "web/react/src/xyzac-visual-adapter.ts", "sha256": "1e9aca3e5f206876310268b86c5cabd0c37cd23a28cfbf599e29612effdf20dd"},
|
||||||
|
{"path": "web/react/src/test_xyzac_threejs.mjs", "sha256": "a858df19658826e92e5d6f6df566a16741741648631f4e02f36b2825b97f6e7d"},
|
||||||
|
{"path": "web/react/src/test_xyzac_tool_preview.mjs", "sha256": "be345e8df99a1bb4912c2a1fb334ed1823e03dd2ba67ca6cf60bee927d269b0a"},
|
||||||
|
{"path": "coverage/xyzac-trt-threejs.report.json", "sha256": "0bdd0e072c7d1c87f1a3183ca522cc61baf0bdc3d26a94199d1f3e005a960dbb"}
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"baseline": "passed",
|
||||||
|
"download_index": "passed",
|
||||||
|
"pointer": "passed",
|
||||||
|
"machine_hierarchy": "passed",
|
||||||
|
"tool_preview_separated": "passed",
|
||||||
|
"ac_direction_offset": "passed",
|
||||||
|
"authoritative_tick_hash": "passed",
|
||||||
|
"canvas_nonblank_desktop": "passed",
|
||||||
|
"canvas_nonblank_mobile": "passed",
|
||||||
|
"visual_clear_nonmutating": "passed",
|
||||||
|
"horizontal_overflow_desktop_mobile": "passed",
|
||||||
|
"react_typecheck_build": "passed",
|
||||||
|
"xyzac_worker_regressions": "passed",
|
||||||
|
"native_ctest_21": "passed"
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"machine_groups": ["xyzac-machine", "table-x", "saddle-y", "spindle-z", "tilt-a", "rotate-c", "tool-offset", "xyzac-toolpath", "xyzac-live-toolpath"],
|
||||||
|
"direction": {"table_x": -1, "saddle_y": -1, "spindle_z": -1, "tilt_a": "x+", "rotate_c": "z+", "tool_offset": "z-"},
|
||||||
|
"browser_pixel_oracle": {"desktop_nonzero_pixels": 777266, "mobile_nonzero_pixels": 118864},
|
||||||
|
"first_divergent_tick": null
|
||||||
|
},
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": "RTCP-11",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
45
coverage/task-packets/RTCP-11.handoff.json
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "RTCP-11",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"result": "XYZAC WASM checkpoint/journal/restart/replay/reconcile recovery verified with no duplicate M428 and fail-closed corruption paths",
|
||||||
|
"report": {"path": "coverage/xyzac-trt-recovery.report.json", "sha256": "9dc18abe81d4d7f84ef2543d664895f43ffd52ab5b4925672402724902b2369f"},
|
||||||
|
"outputs": [
|
||||||
|
{"path": "web/worker/test_xyzac_recovery.mjs", "sha256": "d8f02b0ab5b7c9fc5ef30a3a67f7896071ecf9bbaa7d2f3614c031d118e655e7"},
|
||||||
|
{"path": "coverage/xyzac-trt-recovery.report.json", "sha256": "a863e06fce29904913b38526fe0f780f3f8912f0e741763c1e7221283a335808"},
|
||||||
|
{"path": "scripts/generate_xyzac_trt_recovery_report.py", "sha256": "5e75fe82fd069d891e7a34a07da7aa9b4e7b3152b6b61ce791a77a97e2dc957c"},
|
||||||
|
{"path": "web/worker/sim-session.mjs", "sha256": "0ab5e206629e005eabb834db8bb90106e28f2cfcc23ddec4acbb96f38c3d5423"}
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"baseline": "passed",
|
||||||
|
"download_index": "passed",
|
||||||
|
"pointer": "passed",
|
||||||
|
"checkpoint_minimum_fields": "passed",
|
||||||
|
"endpoint_pose_joints_program_hash": "passed",
|
||||||
|
"no_duplicate_m428": "passed",
|
||||||
|
"ledger_reconcile": "passed",
|
||||||
|
"corrupt_checkpoint_fail_closed": "passed",
|
||||||
|
"schema_mismatch_fail_closed": "passed",
|
||||||
|
"journal_commit_recovery": "passed",
|
||||||
|
"journal_corruption_fail_closed": "passed",
|
||||||
|
"opfs_unavailable_fail_closed": "passed",
|
||||||
|
"native_ctest_21": "passed",
|
||||||
|
"react_typecheck_build": "passed"
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"required_checkpoint_fields": ["context_id", "simulation_tick", "event_cursor", "state_hash", "task", "program", "kinematics", "motion", "wasm_motion", "ledger"],
|
||||||
|
"endpoint_at_checkpoint_equal": true,
|
||||||
|
"no_duplicate_m428": true,
|
||||||
|
"replayed_operation": true,
|
||||||
|
"corrupt_checkpoint": "blocked",
|
||||||
|
"schema_mismatch": "blocked",
|
||||||
|
"opfs_physical_crash": "REAL-EXCLUDED",
|
||||||
|
"first_divergent_tick": null
|
||||||
|
},
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": "RTCP-12",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
38
coverage/task-packets/RTCP-12.handoff.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "RTCP-12",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"result": "Selected xyzac-trt.ini native/Worker/WASM/React/Three.js parity aggregate verified with explicit release exclusions",
|
||||||
|
"report": {"path": "coverage/xyzac-trt-web-wasm-parity.report.json", "sha256": "676f94870045670d2808c2193bded706fdd4909f06d70be00f46dd2653d92927"},
|
||||||
|
"outputs": [
|
||||||
|
{"path": "scripts/generate_xyzac_trt_web_wasm_parity_report.py", "sha256": "fd658a157981d8ca8bd95eecea3cf803443c0f10c7d7ca1dde75c184a69abd4d"},
|
||||||
|
{"path": "coverage/xyzac-trt-web-wasm-parity.report.json", "sha256": "032912a30e5bdd970f6ff10cadb9a30642b7bdb45252ea40cd7f1b953a1050c7"}
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"baseline": "passed",
|
||||||
|
"download_index": "passed",
|
||||||
|
"pointer": "passed",
|
||||||
|
"all_13_selected_case_reports": "passed",
|
||||||
|
"native_wasm_trace": "passed",
|
||||||
|
"native_worker_trace": "passed",
|
||||||
|
"first_divergent_tick_normal": "null",
|
||||||
|
"first_divergent_tick_positive_sample": "2",
|
||||||
|
"browser_desktop_mobile": "passed",
|
||||||
|
"recovery_endpoint_equal": "passed",
|
||||||
|
"blocker_identity_explicit": "passed",
|
||||||
|
"report_self_hash": "passed"
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"report_count": 13,
|
||||||
|
"candidate_eligible": false,
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"excluded": ["Safari", "physical power loss", "host hardware", "xyzbc", "3axis", "other cases"],
|
||||||
|
"first_divergent_tick": null
|
||||||
|
},
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": "RELEASE-00",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
33
coverage/task-packets/SRC-00-REMEDIATION.handoff.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "SRC-00-REMEDIATION",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"result": "resolved native source startup, exact intro resource, and XYZAC native/WASM/Worker evidence",
|
||||||
|
"reports": [
|
||||||
|
{"path": "coverage/xyzac-trt-native-wasm-parity.report.json", "sha256": "ec9c3d9062ab68ab542e5193852e7f8296a40c9c1813d9a303b0971cf4b89598"},
|
||||||
|
{"path": "coverage/xyzac-trt-source-slice-inventory.report.json", "sha256": "69385c93662a6627dc6d83eaca851411ddf76156ae9aa85f56d4e029455e68ef"}
|
||||||
|
],
|
||||||
|
"resolved": [
|
||||||
|
"run-5axis-rtcp.sh now uses the repository run-in-place launcher",
|
||||||
|
"sim_lib.tcl recognizes xyzac-trt-kins and xyzbc-trt-kins",
|
||||||
|
"INTRO_GRAPHIC uses the existing linuxcnc.gif exact manifest asset",
|
||||||
|
"Worker loads xyzac-trt-headless_cmds.hal fixture and TASK_READY",
|
||||||
|
"WASM C API supplies XYZAC five-joint kinematics and M428/M429/M430 transitions"
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"native_xyzac_xyzbc_startup": "passed",
|
||||||
|
"native_wasm_kinematics_trace": "passed",
|
||||||
|
"native_wasm_switchkins_trace": "passed",
|
||||||
|
"xyzac_worker_case": "passed",
|
||||||
|
"source_slice_self_hash": "passed",
|
||||||
|
"native_ctest": "20/20",
|
||||||
|
"wasm_kinematics_tests": "passed",
|
||||||
|
"react_typecheck": "passed"
|
||||||
|
},
|
||||||
|
"evidence": {"control_count": 110, "menus": 80, "toolbar": 20, "keyboard": 10, "slices": 11, "direct_sources": 35, "unregistered_slices": 0, "js_authority": 0, "first_divergent_tick": null},
|
||||||
|
"packet": {"path": "coverage/task-packets/SRC-00-REMEDIATION.json", "sha256": "855839e8a1e5ca5168330ab25a3dc6eebc87ce3f9b6e69475c53e2b74a2b4e33"},
|
||||||
|
"next_task": "WASM-00",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
42
coverage/task-packets/SRC-00.handoff.json
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "SRC-00",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"result": "source inventory, native/WASM parity, and exact XYZAC Worker authority evidence verified",
|
||||||
|
"reports": [
|
||||||
|
{"path": "coverage/xyzac-trt-source-slice-inventory.report.json", "sha256": "601fb94f39681e382262b0ed6d6890f9db67e27a8ff4383b977fb073b46562e4"},
|
||||||
|
{"path": "coverage/xyzac-trt-native-wasm-parity.report.json", "sha256": "f67633527c623e826c14d89687139d109d046fdeaf445fc754506a66c8262d8e"}
|
||||||
|
],
|
||||||
|
"checks": {
|
||||||
|
"baseline": "passed",
|
||||||
|
"download_index": "passed",
|
||||||
|
"pointer": "passed",
|
||||||
|
"source_slice_unittest": "passed",
|
||||||
|
"source_slice_self_hash": "passed",
|
||||||
|
"native_xyzac_xyzbc_startup": "passed",
|
||||||
|
"native_wasm_kinematics_trace": "passed",
|
||||||
|
"native_wasm_switchkins_trace": "passed",
|
||||||
|
"xyzac_worker_case": "passed",
|
||||||
|
"mjs_syntax": "passed",
|
||||||
|
"react_typecheck": "passed"
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"control_count": 110,
|
||||||
|
"menus": 80,
|
||||||
|
"toolbar": 20,
|
||||||
|
"keyboard": 10,
|
||||||
|
"slices": 11,
|
||||||
|
"direct_sources": 35,
|
||||||
|
"unregistered_slices": 0,
|
||||||
|
"blocked_resources": 0,
|
||||||
|
"js_authority": 0,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"worker_state_hash": "fnv1a64:72275811a4320d7c"
|
||||||
|
},
|
||||||
|
"failure_samples": [],
|
||||||
|
"packet": {"path": "coverage/task-packets/SRC-00.json", "sha256": "a13fa893653494bf12cee5e15c38cc6c74edc6ffcd02a10cb788c4a87e3cf37b"},
|
||||||
|
"next_task": "WASM-00",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
45
coverage/task-packets/WASM-00-REMEDIATION.handoff.json
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "WASM-00-REMEDIATION",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "verified",
|
||||||
|
"result": "selected XYZAC authority verified for the simulation scope; physical power-loss and Safari are accepted scope exclusions",
|
||||||
|
"report": {"path": "coverage/xyzac-trt-wasm-authority-audit.report.json", "sha256": "279a9c2b0496e415cb3da25fc387ee245e826e942b67471a25796a3c23122205"},
|
||||||
|
"implemented": [
|
||||||
|
"sim_program C/WASM runtime owns selected XYZAC program load/control/interpreter/motion events",
|
||||||
|
"selected XYZAC jog/home/limit validation routes through WASM motion queue and home-limit",
|
||||||
|
"selected XYZAC snapshots omit JS compatibility planner state",
|
||||||
|
"sim_io C/WASM owns spindle/tool/probe/ESTOP/recovery and HAL scalar writes",
|
||||||
|
"sim_device_io C/WASM owns encoder/coolant/door/interlock/ack state",
|
||||||
|
"Chromium SIGKILL/profile reopen and corruption detection verified",
|
||||||
|
"Firefox Worker OPFS round-trip verified"
|
||||||
|
],
|
||||||
|
"tests": {
|
||||||
|
"native_ctest": "20/20 passed",
|
||||||
|
"react_typecheck_build": "passed",
|
||||||
|
"xyzac_worker": "passed",
|
||||||
|
"xyzac_program_authority": "passed",
|
||||||
|
"xyzac_io_authority": "passed",
|
||||||
|
"native_xyzac_xyzbc_startup": "passed",
|
||||||
|
"native_wasm_parity": "passed",
|
||||||
|
"chromium_opfs_crash": "passed",
|
||||||
|
"chromium_opfs_corruption": "passed",
|
||||||
|
"firefox_opfs_round_trip": "passed",
|
||||||
|
"authority_audit": "passed"
|
||||||
|
},
|
||||||
|
"authority_matrix": {
|
||||||
|
"program_interpreter": "verified",
|
||||||
|
"motion_jog_home_limit": "verified",
|
||||||
|
"task_state": "verified",
|
||||||
|
"hal_io_tool_probe": "verified",
|
||||||
|
"kinematics_rtcp_switchkins": "verified"
|
||||||
|
},
|
||||||
|
"accepted_scope_exclusions": [
|
||||||
|
{"code": "PHYSICAL_POWER_LOSS_OUTSIDE_BROWSER", "status": "accepted_scope_exclusion"},
|
||||||
|
{"code": "SAFARI_NOT_REQUIRED", "status": "accepted_scope_exclusion"}
|
||||||
|
],
|
||||||
|
"failure_samples": [],
|
||||||
|
"next_task": "RTCP-01",
|
||||||
|
"next_task_status": "queued",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
38
coverage/task-packets/WASM-00.handoff.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"schema": "task-handoff.v1",
|
||||||
|
"task_id": "WASM-00",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"status": "blocked",
|
||||||
|
"result": "XYZAC WASM authority audit completed; selected Worker trace is verified but JS authority and external recovery/browser boundaries remain open",
|
||||||
|
"report": {"path": "coverage/xyzac-trt-wasm-authority-audit.report.json", "sha256": "36fdd77229015634dcde9f796e32eb0435ae2729dc4267aca28d081179a44d7f"},
|
||||||
|
"packet": {"path": "coverage/task-packets/WASM-00.json", "sha256": "4bdb5ca36b7a57d977248693ebea2b76a6bc9cc7d3a8998d59983630a342e73e"},
|
||||||
|
"checks": {
|
||||||
|
"authority_audit_unittest": "passed",
|
||||||
|
"report_self_hash": "passed",
|
||||||
|
"xyzac_worker_trace": "passed",
|
||||||
|
"worker_motion_authority": "wasm",
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"react_direct_wasm_pointer": "passed",
|
||||||
|
"react_direct_opfs_writer": "passed"
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"control_count": 110,
|
||||||
|
"switchkins": ["M429", "M428", "M430"],
|
||||||
|
"tick_sequence": [0, 0, 0],
|
||||||
|
"state_hash": "fnv1a64:72275811a4320d7c",
|
||||||
|
"js_authority_call_count": 0,
|
||||||
|
"js_authority_findings": 3,
|
||||||
|
"blocker_count": 5,
|
||||||
|
"candidate_eligible": false
|
||||||
|
},
|
||||||
|
"failure_samples": [
|
||||||
|
{"code": "JS_PROGRAM_CONTROL_FLOW", "detail": "ProgramRunner executes control directives and schedules interpreter blocks from JS"},
|
||||||
|
{"code": "JS_MOTION_STATE_MIRROR", "detail": "MotionState owns XYZAC-relevant HOME/JOG/limit/fault transitions"},
|
||||||
|
{"code": "JS_COMPATIBILITY_PLANNER_PRESENT", "detail": "AuthoritativeRuntime remains constructed and serializable for the selected session"},
|
||||||
|
{"code": "BROWSER_OPFS_ORACLE_BLOCKED", "detail": "physical OPFS crash/restart and Firefox/Safari matrix are unavailable"},
|
||||||
|
{"code": "FULL_XYZAC_AUTHORITY_MATRIX_PENDING", "detail": "HAL/IO/tool/probe and non-selected authority closure remain outside WASM-00"}
|
||||||
|
],
|
||||||
|
"next_task": "RTCP-01",
|
||||||
|
"next_task_status": "blocked",
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
}
|
||||||
76
coverage/xyzac-trt-gate-matrix.report.json
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"candidate_eligible": true,
|
||||||
|
"case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"sha256": "f985970db293b70c055e4c41fc316d0c16037b5dc7ce07490e18f554e851f811"
|
||||||
|
},
|
||||||
|
"command_trace": [
|
||||||
|
"HELLO",
|
||||||
|
"SESSION_ATTACH",
|
||||||
|
"LOAD_WORKSPACE",
|
||||||
|
"PROGRAM_LOAD",
|
||||||
|
"PROGRAM_RUN",
|
||||||
|
"PROGRAM_PAUSE",
|
||||||
|
"PROGRAM_ABORT",
|
||||||
|
"ESTOP",
|
||||||
|
"IO_RECOVER",
|
||||||
|
"SNAPSHOT_REQUEST"
|
||||||
|
],
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"failure_samples": [],
|
||||||
|
"gate_scenarios": {
|
||||||
|
"highlight_missing": {
|
||||||
|
"reason": "axis_highlight_missing",
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"no_file": {
|
||||||
|
"reason": "axis_taskfile_missing",
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"paused_resume": {
|
||||||
|
"resume": true,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"running_pause_abort": {
|
||||||
|
"abort": true,
|
||||||
|
"pause": true,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"switchkins_estop": {
|
||||||
|
"reason": "xyzac_estop_active",
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"switchkins_hal_missing": {
|
||||||
|
"reason": "xyzac_hal_pin_vars_missing",
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"vismach_clear": {
|
||||||
|
"status": "verified",
|
||||||
|
"visual_only": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": "ae3ebd07146e75e1fa50c90d37e56ca195e2c6b82069718aedf9b63338ad2219",
|
||||||
|
"schema": "xyzac-gate-matrix-rtcp-03.v1",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"state_hash": "fnv1a64:9abaa158693c6300",
|
||||||
|
"status": "verified",
|
||||||
|
"task_id": "RTCP-03",
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"web/worker/test_xyzac_gate_matrix.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "a8e61897c9e8f666970804f4ba8d0a959557960df40bf26261a7b4d73517de9a",
|
||||||
|
"returncode": 0,
|
||||||
|
"status": "verified"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tick_trace": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
87
coverage/xyzac-trt-hal-tool-alarm.report.json
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
{
|
||||||
|
"ack": {
|
||||||
|
"abort": "verified",
|
||||||
|
"normal": "verified",
|
||||||
|
"timeout": "wasm_sim_device_ack_ack_-1008"
|
||||||
|
},
|
||||||
|
"alarm": {
|
||||||
|
"code": "wasm_hal_write_denied",
|
||||||
|
"operation_id": "hal-denied"
|
||||||
|
},
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"candidate_eligible": true,
|
||||||
|
"case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"sha256": "f985970db293b70c055e4c41fc316d0c16037b5dc7ce07490e18f554e851f811"
|
||||||
|
},
|
||||||
|
"command_trace": [
|
||||||
|
"MDI_EXECUTE:M429",
|
||||||
|
"MDI_EXECUTE:M428",
|
||||||
|
"MDI_EXECUTE:M430",
|
||||||
|
"VCP_UPDATE:VISMACH_CLEAR",
|
||||||
|
"HAL_SET_ALLOWED",
|
||||||
|
"HAL_SET_DENIED",
|
||||||
|
"SPINDLE_COMMAND",
|
||||||
|
"COOLANT_COMMAND",
|
||||||
|
"TOOL_PREPARE",
|
||||||
|
"TOOL_CHANGE",
|
||||||
|
"MDI:G43",
|
||||||
|
"MDI:G49",
|
||||||
|
"DEVICE_ACK",
|
||||||
|
"DEVICE_ACK_TIMEOUT",
|
||||||
|
"DEVICE_ACK_ABORT"
|
||||||
|
],
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"coolant": {
|
||||||
|
"flood": 1,
|
||||||
|
"lube": 0,
|
||||||
|
"mist": 1
|
||||||
|
},
|
||||||
|
"failure_samples": [],
|
||||||
|
"hal": {
|
||||||
|
"allowed": "motion.probe-input",
|
||||||
|
"denied": "wasm_hal_write_denied"
|
||||||
|
},
|
||||||
|
"hal_write_policy": {
|
||||||
|
"direct_switchkins_write": "denied",
|
||||||
|
"mode": "allowlist",
|
||||||
|
"switchkins_path": "M429/M428/M430"
|
||||||
|
},
|
||||||
|
"pyvcp": [
|
||||||
|
"M429",
|
||||||
|
"M428",
|
||||||
|
"M430",
|
||||||
|
"vismach-clear"
|
||||||
|
],
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": "75c368880a881da1230d3e66ec5265ad693053dc9eecce462d92c84f5a025872",
|
||||||
|
"schema": "xyzac-hal-tool-alarm-rtcp-07.v1",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"spindle_rpm": 1200,
|
||||||
|
"state_hash": "fnv1a64:184c713588cb828d",
|
||||||
|
"status": "verified",
|
||||||
|
"task_id": "RTCP-07",
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"web/worker/test_xyzac_hal_tool_alarm.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "02c7e7eace9b8be9d3a3144f259fdfbf870569df26ba01c22b4146e1bc3bc9e2",
|
||||||
|
"returncode": 0,
|
||||||
|
"status": "verified"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tick_trace": [
|
||||||
|
0,
|
||||||
|
10,
|
||||||
|
30,
|
||||||
|
31
|
||||||
|
],
|
||||||
|
"tool": 7,
|
||||||
|
"visual_clear": {
|
||||||
|
"runtime_hash_unchanged": true,
|
||||||
|
"status": "verified",
|
||||||
|
"visual_only": true
|
||||||
|
}
|
||||||
|
}
|
||||||
96
coverage/xyzac-trt-manual-control.report.json
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
{
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"axes": [
|
||||||
|
{
|
||||||
|
"axis": "x",
|
||||||
|
"end": 0.01,
|
||||||
|
"start": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"axis": "y",
|
||||||
|
"end": 0.01,
|
||||||
|
"start": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"axis": "z",
|
||||||
|
"end": 0.01,
|
||||||
|
"start": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"axis": "a",
|
||||||
|
"end": 0.01,
|
||||||
|
"start": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"axis": "c",
|
||||||
|
"end": 0.01,
|
||||||
|
"start": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"boundary": {
|
||||||
|
"following_error": "motion_following_error",
|
||||||
|
"nonfinite_speed": "motion_queue",
|
||||||
|
"out_of_range": "motion_queue"
|
||||||
|
},
|
||||||
|
"candidate_eligible": true,
|
||||||
|
"case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"sha256": "f985970db293b70c055e4c41fc316d0c16037b5dc7ce07490e18f554e851f811"
|
||||||
|
},
|
||||||
|
"command_trace": [
|
||||||
|
"HOME",
|
||||||
|
"JOG_START:X",
|
||||||
|
"SIM_STEP",
|
||||||
|
"JOG_STOP",
|
||||||
|
"JOG_START:Y",
|
||||||
|
"JOG_START:Z",
|
||||||
|
"JOG_START:A",
|
||||||
|
"JOG_START:C",
|
||||||
|
"MDI_EXECUTE",
|
||||||
|
"PROGRAM_RUN",
|
||||||
|
"MDI_EXECUTE_BLOCKED",
|
||||||
|
"ESTOP",
|
||||||
|
"IO_RECOVER"
|
||||||
|
],
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"estop_recovery": "verified",
|
||||||
|
"failure_samples": [],
|
||||||
|
"home": {
|
||||||
|
"authority": "wasm_home_limit",
|
||||||
|
"joints": 5
|
||||||
|
},
|
||||||
|
"mdi": "verified",
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": "0660bca7d4ae6d68e584346606911c98aaacb14922f1b5fc9a7a44d782936be6",
|
||||||
|
"schema": "xyzac-manual-control-rtcp-06.v1",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"state_hash": "fnv1a64:fac98a76f1a41fbc",
|
||||||
|
"status": "verified",
|
||||||
|
"task_id": "RTCP-06",
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"web/worker/test_xyzac_manual_control.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "930fe1e249389de9b0c0460f59c36e46820e3e006dc4675ffdf2528891731066",
|
||||||
|
"returncode": 0,
|
||||||
|
"status": "verified"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tick_trace": [
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"units": {
|
||||||
|
"angular_axes": [
|
||||||
|
"A",
|
||||||
|
"C"
|
||||||
|
],
|
||||||
|
"angular_units": "degrees",
|
||||||
|
"linear_axes": [
|
||||||
|
"X",
|
||||||
|
"Y",
|
||||||
|
"Z"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
116
coverage/xyzac-trt-native-wasm-parity.report.json
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
{
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"case": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"js_authority": 0,
|
||||||
|
"native": {
|
||||||
|
"command": [
|
||||||
|
"./build-native/sim_xyzac_trace_test"
|
||||||
|
],
|
||||||
|
"output_sha256": "18d2ef9d77f9a7a5bf2fa8d9380c8b04383a58ee2b0258364cba31ec34a168f0",
|
||||||
|
"returncode": 0,
|
||||||
|
"trace_sha256": "ca13e8dcb6d2816787a98c7bddc04db552b88f8d8a994b49301973a1bd11245a"
|
||||||
|
},
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": "3a6278c6c80adf6565944b0fdce78349e640dc197c3e187eea16cdbbf6572ed3",
|
||||||
|
"schema": "xyzac-trt-native-wasm-parity.v2",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"status": "verified",
|
||||||
|
"task_id": "PARITY-00",
|
||||||
|
"trace": {
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"levels": [
|
||||||
|
"protocol",
|
||||||
|
"control",
|
||||||
|
"state",
|
||||||
|
"canonical",
|
||||||
|
"recovery"
|
||||||
|
],
|
||||||
|
"native_wasm": {
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"levels": {
|
||||||
|
"canonical": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"control": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"protocol": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"recovery": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"native_worker": {
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"levels": {
|
||||||
|
"canonical": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"control": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"protocol": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"recovery": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"positive_divergence_sample": {
|
||||||
|
"detected": true,
|
||||||
|
"difference_path": "$[1].active_type",
|
||||||
|
"first_divergent_tick": 2,
|
||||||
|
"mutation": "state[1].active_type=99"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wasm": {
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"build-wasm/sim_xyzac_trace_wasm_test.js"
|
||||||
|
],
|
||||||
|
"output_sha256": "18d2ef9d77f9a7a5bf2fa8d9380c8b04383a58ee2b0258364cba31ec34a168f0",
|
||||||
|
"returncode": 0,
|
||||||
|
"trace_sha256": "ca13e8dcb6d2816787a98c7bddc04db552b88f8d8a994b49301973a1bd11245a"
|
||||||
|
},
|
||||||
|
"worker": {
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"web/worker/test_xyzac_trace_parity.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "773f77181d7fd891a25a9ec6e7c3109f5f6e9b7ddcc3f876e1e973657959558e",
|
||||||
|
"returncode": 0,
|
||||||
|
"trace_sha256": "ca13e8dcb6d2816787a98c7bddc04db552b88f8d8a994b49301973a1bd11245a"
|
||||||
|
}
|
||||||
|
}
|
||||||
62
coverage/xyzac-trt-program-preview.report.json
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"candidate_eligible": true,
|
||||||
|
"case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"sha256": "f985970db293b70c055e4c41fc316d0c16037b5dc7ce07490e18f554e851f811"
|
||||||
|
},
|
||||||
|
"command_trace": [
|
||||||
|
"PROGRAM_LOAD",
|
||||||
|
"WASM_PROGRAM_PREVIEW",
|
||||||
|
"SNAPSHOT_REQUEST",
|
||||||
|
"PROGRAM_LOAD_REPLAY",
|
||||||
|
"PROGRAM_LOAD_INVALID",
|
||||||
|
"PROGRAM_LOAD_EMPTY"
|
||||||
|
],
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"error_cases": {
|
||||||
|
"empty_source": "program_source_invalid",
|
||||||
|
"invalid_gcode": "error"
|
||||||
|
},
|
||||||
|
"failure_samples": [],
|
||||||
|
"line_count": 4,
|
||||||
|
"preview_authority": "linuxcnc-wasm-sim_program",
|
||||||
|
"preview_fields": [
|
||||||
|
"id",
|
||||||
|
"source_line",
|
||||||
|
"start",
|
||||||
|
"end",
|
||||||
|
"motion_type",
|
||||||
|
"center",
|
||||||
|
"normal",
|
||||||
|
"turn"
|
||||||
|
],
|
||||||
|
"preview_hash": "fnv1a64:6bdaeeddf0262c90",
|
||||||
|
"recovery": {
|
||||||
|
"no_duplicate_side_effect": true,
|
||||||
|
"replay_same_operation": "verified"
|
||||||
|
},
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": "bde1a054c9a633a62595ec19e5097d73499ca8fbcf07c817c01265dbf643f04a",
|
||||||
|
"schema": "xyzac-program-preview-rtcp-04.v1",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"segment_count": 2,
|
||||||
|
"source_hash": "fnv1a64:b7f4678312207b31",
|
||||||
|
"state_hash": "fnv1a64:fda625a2e7b00ac7",
|
||||||
|
"status": "verified",
|
||||||
|
"task_id": "RTCP-04",
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"web/worker/test_xyzac_program_preview.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "79dd24d44e07958fd86484197c4502d3bdadd6d6971b7e74727b594d1d6b630f",
|
||||||
|
"returncode": 0,
|
||||||
|
"status": "verified"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tick_trace": [
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
146
coverage/xyzac-trt-protocol-react.report.json
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
{
|
||||||
|
"additive_commands": [
|
||||||
|
"POWER",
|
||||||
|
"KINEMATICS_SELECT",
|
||||||
|
"VISUAL_CLEAR"
|
||||||
|
],
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"browser_matrix": {
|
||||||
|
"console_exceptions": 0,
|
||||||
|
"desktop": {
|
||||||
|
"canvas_nonblank": true,
|
||||||
|
"document_width": 1425,
|
||||||
|
"scroll_width": 1425,
|
||||||
|
"status": "READY",
|
||||||
|
"switchkins_controls": 3,
|
||||||
|
"viewport": [
|
||||||
|
1440,
|
||||||
|
1000
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"functional_note": "Chromium simulation only; Safari and physical power loss are excluded by scope",
|
||||||
|
"mobile": {
|
||||||
|
"canvas_nonblank": true,
|
||||||
|
"document_width": 390,
|
||||||
|
"scroll_width": 390,
|
||||||
|
"status": "READY",
|
||||||
|
"switchkins_controls": 3,
|
||||||
|
"viewport": [
|
||||||
|
390,
|
||||||
|
844
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"candidate_eligible": true,
|
||||||
|
"case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"scope": "xyzac_only"
|
||||||
|
},
|
||||||
|
"components": [
|
||||||
|
"XyzacTrtPanel",
|
||||||
|
"XyzacProgramPane",
|
||||||
|
"XyzacKinematicsControl",
|
||||||
|
"XyzacManualPanel",
|
||||||
|
"XyzacDiagnostics"
|
||||||
|
],
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"default_case": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"exclusions": [
|
||||||
|
"xyzbc",
|
||||||
|
"3axis",
|
||||||
|
"other cases",
|
||||||
|
"Safari",
|
||||||
|
"physical power loss",
|
||||||
|
"host hardware"
|
||||||
|
],
|
||||||
|
"failure_samples": [],
|
||||||
|
"gates": {
|
||||||
|
"controller": [
|
||||||
|
"blocked",
|
||||||
|
"pending",
|
||||||
|
"result"
|
||||||
|
],
|
||||||
|
"invalid": "xyzac_kinematics_type_invalid",
|
||||||
|
"running": "xyzac_interp_not_idle"
|
||||||
|
},
|
||||||
|
"known_blockers": [],
|
||||||
|
"operation_phases": [
|
||||||
|
"blocked",
|
||||||
|
"pending",
|
||||||
|
"result"
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"path": "web/react/src/XyzacTrtPanel.tsx",
|
||||||
|
"sha256": "f17e25f74d87a39a1bd7520f65cf1b285cb4c38d8e4fcaec13320e41c4ca6511"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/react/src/xyzac-panel-contract.ts",
|
||||||
|
"sha256": "a1afaa368927bbc5b7d9e21a64213b963a97b687cecb0005bb9b8e47e969e823"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/worker/test_xyzac_protocol_react.mjs",
|
||||||
|
"sha256": "69d942dfdcb7bc0d7fb26d6708220a47af13aa84acdcf490780980f6de0cc03d"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": "a8ee87b6cb3ccbd683e9888c85a063dabbe17fafdd2708636bd48be592b7b520",
|
||||||
|
"schema": "xyzac-trt-protocol-react-report.v1",
|
||||||
|
"state_hash": "fnv1a64:2de42fa3ac15497a",
|
||||||
|
"status": "verified",
|
||||||
|
"switchkins": [
|
||||||
|
{
|
||||||
|
"operation_id": "select-0-4",
|
||||||
|
"phase": "committed",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation_id": "select-1-5",
|
||||||
|
"phase": "committed",
|
||||||
|
"type": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation_id": "select-2-6",
|
||||||
|
"phase": "committed",
|
||||||
|
"type": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation_id": "select-1-7",
|
||||||
|
"phase": "committed",
|
||||||
|
"type": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"task_id": "RTCP-09",
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"web/worker/test_xyzac_protocol_react.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "cf72a973132e3e79c155fcb0feb58f07259c082970287127d786e94a92cc4327",
|
||||||
|
"returncode": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"npm",
|
||||||
|
"run",
|
||||||
|
"typecheck"
|
||||||
|
],
|
||||||
|
"output_sha256": "08fb3e3d0d9396a56f8720eb15d9f6920f8a30c2dad7b0b6672ac446801f27e3",
|
||||||
|
"returncode": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"visibility": {
|
||||||
|
"alarms": true,
|
||||||
|
"gate_reason": true,
|
||||||
|
"pending_ack_result_error": true,
|
||||||
|
"recovery": true
|
||||||
|
},
|
||||||
|
"worker_only_boundary": {
|
||||||
|
"react_direct_opfs": 0,
|
||||||
|
"react_direct_wasm": 0,
|
||||||
|
"react_post_message": 0,
|
||||||
|
"status": "verified",
|
||||||
|
"transport": "ReactWorkerClient"
|
||||||
|
}
|
||||||
|
}
|
||||||
102
coverage/xyzac-trt-recovery.report.json
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
{
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"candidate_eligible": true,
|
||||||
|
"case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"scope": "xyzac_only"
|
||||||
|
},
|
||||||
|
"checkpoint": {
|
||||||
|
"active_type": 1,
|
||||||
|
"required_fields": [
|
||||||
|
"context_id",
|
||||||
|
"simulation_tick",
|
||||||
|
"event_cursor",
|
||||||
|
"state_hash",
|
||||||
|
"task",
|
||||||
|
"program",
|
||||||
|
"kinematics",
|
||||||
|
"motion",
|
||||||
|
"wasm_motion",
|
||||||
|
"ledger"
|
||||||
|
],
|
||||||
|
"state_hash": "fnv1a64:1463b940dede5cf0"
|
||||||
|
},
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"endpoint": {
|
||||||
|
"at_checkpoint_equal": true,
|
||||||
|
"continuous_hash": "fnv1a64:a4dee4490d19b4ec",
|
||||||
|
"restored_hash": "fnv1a64:ca9e3481aaa28f6d"
|
||||||
|
},
|
||||||
|
"exclusions": [
|
||||||
|
"xyzbc",
|
||||||
|
"3axis",
|
||||||
|
"other cases",
|
||||||
|
"Safari",
|
||||||
|
"physical power loss",
|
||||||
|
"host hardware"
|
||||||
|
],
|
||||||
|
"fail_closed": {
|
||||||
|
"corrupt_checkpoint": "blocked",
|
||||||
|
"journal_corrupt": "blocked",
|
||||||
|
"journal_schema": "blocked",
|
||||||
|
"schema_mismatch": "blocked"
|
||||||
|
},
|
||||||
|
"failure_samples": [],
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"journal": {
|
||||||
|
"corrupt_status": "blocked",
|
||||||
|
"schema_status": "blocked",
|
||||||
|
"status": "recovered"
|
||||||
|
},
|
||||||
|
"known_blockers": [],
|
||||||
|
"no_duplicate_side_effect": {
|
||||||
|
"m428": true,
|
||||||
|
"replayed_operation": true
|
||||||
|
},
|
||||||
|
"opfs": {
|
||||||
|
"physical_crash": "REAL-EXCLUDED",
|
||||||
|
"probe_status": "blocked",
|
||||||
|
"unavailable_status": "blocked"
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"path": "web/worker/test_xyzac_recovery.mjs",
|
||||||
|
"sha256": "d8f02b0ab5b7c9fc5ef30a3a67f7896071ecf9bbaa7d2f3614c031d118e655e7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/worker/sim-session.mjs",
|
||||||
|
"sha256": "7ce91549f0f3a06ff77db9ee281fd9544848f6e41e08f9da302ee2e7827194e3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/worker/wasm-program-runtime.mjs",
|
||||||
|
"sha256": "60da7b953ac4ee2659f2b05c6bb93b58c8d0c2c4b56363f6a93e44aab7aebb02"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": "6c38c4e541868934f7a8231d36314b7e736daeb6d685f1dad48809fa49ef65ae",
|
||||||
|
"required_checkpoint_fields": [
|
||||||
|
"context_id",
|
||||||
|
"simulation_tick",
|
||||||
|
"event_cursor",
|
||||||
|
"state_hash",
|
||||||
|
"task",
|
||||||
|
"program",
|
||||||
|
"kinematics",
|
||||||
|
"motion",
|
||||||
|
"wasm_motion",
|
||||||
|
"ledger"
|
||||||
|
],
|
||||||
|
"schema": "xyzac-trt-recovery-report.v1",
|
||||||
|
"status": "verified",
|
||||||
|
"task_id": "RTCP-11",
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"web/worker/test_xyzac_recovery.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "d13944363157b9b97a440adb6135ec8977332f31778fb7ae40383e52ba2f1f35",
|
||||||
|
"returncode": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
256
coverage/xyzac-trt-rtcp.report.json
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
{
|
||||||
|
"authority": "linuxcnc-wasm-sim_kinematics_switch",
|
||||||
|
"boundaries": {
|
||||||
|
"nonfinite_joints": "wasm_xyzac_forward_-701",
|
||||||
|
"nonfinite_pose": "wasm_xyzac_inverse_-701",
|
||||||
|
"range": "wasm_xyzac_forward_-702",
|
||||||
|
"xyzac_singularity": "closed_form_no_singular_branch"
|
||||||
|
},
|
||||||
|
"candidate_eligible": true,
|
||||||
|
"case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"sha256": "f985970db293b70c055e4c41fc316d0c16037b5dc7ce07490e18f554e851f811"
|
||||||
|
},
|
||||||
|
"command_trace": [
|
||||||
|
"WASM:IDENTITY_FORWARD_INVERSE",
|
||||||
|
"WASM:XYZAC_FORWARD_INVERSE",
|
||||||
|
"WASM:TOOL_ROTATION_AXIS_OFFSETS",
|
||||||
|
"WASM:USERK_IDENTITY_TEMPLATE",
|
||||||
|
"WASM:DYNAMIC_USERK_REJECT",
|
||||||
|
"WORKER:MDI_M429_M428_M430",
|
||||||
|
"WASM:G93_G2_P3_XYZAC_HELIX",
|
||||||
|
"WORKER:PROGRAM_PAUSE_RESUME_ABORT_RESTART",
|
||||||
|
"WORKER:CHECKPOINT_RECOVER"
|
||||||
|
],
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"environment": {
|
||||||
|
"functional_node": "v24",
|
||||||
|
"wasm_ctest_harness": "HOST_EXCLUDED",
|
||||||
|
"wasm_ctest_node": "v18"
|
||||||
|
},
|
||||||
|
"exclusions": [
|
||||||
|
"xyzbc",
|
||||||
|
"3axis",
|
||||||
|
"other cases",
|
||||||
|
"Safari",
|
||||||
|
"physical power loss",
|
||||||
|
"host hardware"
|
||||||
|
],
|
||||||
|
"failure_samples": [
|
||||||
|
{
|
||||||
|
"classification": "HOST_EXCLUDED",
|
||||||
|
"code": "wasm_ctest_node18_local_url",
|
||||||
|
"detail": "CTest sim_api_test uses Node 18 fetch on a bare local .wasm path; real Node 24 Worker module and single-file WASM tests passed"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"helix": {
|
||||||
|
"end": {
|
||||||
|
"a": 30,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": 10,
|
||||||
|
"y": 0,
|
||||||
|
"z": 5
|
||||||
|
},
|
||||||
|
"motion_type": 2,
|
||||||
|
"turn": -3,
|
||||||
|
"velocity": 6.538822170860375
|
||||||
|
},
|
||||||
|
"identity": {
|
||||||
|
"pose": {
|
||||||
|
"a": 30,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": 12,
|
||||||
|
"y": -5,
|
||||||
|
"z": 20
|
||||||
|
},
|
||||||
|
"state_hash": "fnv1a64:1b181f155736dc31"
|
||||||
|
},
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"path": "linuxcnc/src/emc/kinematics/xyzac-trt-kins.c",
|
||||||
|
"sha256": "24c633613db9876c2ec246e1519a63d288fd27c68296d89f89f0368105634e69"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "linuxcnc/src/emc/kinematics/trtfuncs.c",
|
||||||
|
"sha256": "ced2f9d4732681e2e82153b2f6343eccdf6972222d558d1e277bf1ec0bdc87fd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "linuxcnc/src/emc/kinematics/switchkins.c",
|
||||||
|
"sha256": "4de32a4a147e5ceee5346b9c6bf471dddf5c64ff58afe5044406c50bcd96b7f5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "linuxcnc/src/emc/kinematics/userkfuncs.c",
|
||||||
|
"sha256": "146238645955f8d4e2cb0cd34ff44db3a9dad01e149f81c08f8a53c19f1609bc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"sha256": "f985970db293b70c055e4c41fc316d0c16037b5dc7ce07490e18f554e851f811"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/helix_ac.ngc",
|
||||||
|
"sha256": "06cb755e33dc35d9a179b3c48e4203c8469632ab248d94379b10f2538dd0b54a"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"known_blockers": [],
|
||||||
|
"lifecycle": {
|
||||||
|
"abort": "STOPPED",
|
||||||
|
"pause": "PAUSED",
|
||||||
|
"recovery": "recovered",
|
||||||
|
"restart": "loaded",
|
||||||
|
"resume": "RUNNING"
|
||||||
|
},
|
||||||
|
"mapping": {
|
||||||
|
"angular_units": "deg",
|
||||||
|
"coordinates": "XYZAC",
|
||||||
|
"limits": {
|
||||||
|
"a": [
|
||||||
|
-100,
|
||||||
|
50
|
||||||
|
],
|
||||||
|
"c": [
|
||||||
|
-36000,
|
||||||
|
36000
|
||||||
|
],
|
||||||
|
"x": [
|
||||||
|
-200,
|
||||||
|
200
|
||||||
|
],
|
||||||
|
"y": [
|
||||||
|
-100,
|
||||||
|
100
|
||||||
|
],
|
||||||
|
"z": [
|
||||||
|
-120,
|
||||||
|
120
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"linear_units": "mm"
|
||||||
|
},
|
||||||
|
"offsets": {
|
||||||
|
"base": {
|
||||||
|
"a": 30,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": 50.44481813796586,
|
||||||
|
"y": -20.265859980688887,
|
||||||
|
"z": 32.32050807568878
|
||||||
|
},
|
||||||
|
"changed": {
|
||||||
|
"a": 30,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": 43.07044946720128,
|
||||||
|
"y": -25.569160839588,
|
||||||
|
"z": 35.6698729810778
|
||||||
|
},
|
||||||
|
"configured": {
|
||||||
|
"tool_offset": 25,
|
||||||
|
"x_rot_point": 5,
|
||||||
|
"y_offset": 20,
|
||||||
|
"z_offset": 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"recovery": {
|
||||||
|
"active_type": 1,
|
||||||
|
"state_hash": "fnv1a64:8c57efd84475776a"
|
||||||
|
},
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": "c370004d8f2d962937b3b91abe2b0dd1632283f570e29d2d194731bc0fe0a083",
|
||||||
|
"schema": "xyzac-rtcp-08-report.v1",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"singularity_classification": {
|
||||||
|
"reason": "xyzac-trt forward/inverse is closed-form and has no denominator singular branch; A=-90 degree boundary roundtrip is retained",
|
||||||
|
"status": "not_applicable"
|
||||||
|
},
|
||||||
|
"status": "verified",
|
||||||
|
"switchkins": [
|
||||||
|
{
|
||||||
|
"active_type": 0,
|
||||||
|
"code": "M429",
|
||||||
|
"state_hash": "fnv1a64:7614a240beddd8ba"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"active_type": 1,
|
||||||
|
"code": "M428",
|
||||||
|
"state_hash": "fnv1a64:1463b940dede5cf0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"active_type": 2,
|
||||||
|
"code": "M430",
|
||||||
|
"state_hash": "fnv1a64:310f1bed69a45614"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"active_type": 1,
|
||||||
|
"code": "M428",
|
||||||
|
"state_hash": "fnv1a64:6c90a42b0ca912d4"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"task_id": "RTCP-08",
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"web/worker/test_xyzac_rtcp.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "3b4411bd1f640a689bde7a99e087965edd0694ca49696d1a936397113980e6c1",
|
||||||
|
"returncode": 0,
|
||||||
|
"status": "verified"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tick_trace": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"trt": {
|
||||||
|
"joints": [
|
||||||
|
49.99999999999999,
|
||||||
|
10.000000000000004,
|
||||||
|
30.000000000000007,
|
||||||
|
30,
|
||||||
|
45
|
||||||
|
],
|
||||||
|
"pivot_boundary": {
|
||||||
|
"a": -90,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": 14.142135623730951,
|
||||||
|
"y": -14.142135623730951,
|
||||||
|
"z": -4.999999999999998
|
||||||
|
},
|
||||||
|
"pose": {
|
||||||
|
"a": 30,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": 50.44481813796586,
|
||||||
|
"y": -20.265859980688887,
|
||||||
|
"z": 32.32050807568878
|
||||||
|
},
|
||||||
|
"state_hash": "fnv1a64:928adf4a1feb90e9"
|
||||||
|
},
|
||||||
|
"userk": {
|
||||||
|
"dynamic_module_error": -712,
|
||||||
|
"native_type": "userk-identity-template"
|
||||||
|
}
|
||||||
|
}
|
||||||
2666
coverage/xyzac-trt-source-slice-inventory.report.json
Normal file
2417
coverage/xyzac-trt-startup.report.json
Normal file
161
coverage/xyzac-trt-threejs.report.json
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
{
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"browser_matrix": {
|
||||||
|
"canvas_context": "webgl2",
|
||||||
|
"console_exceptions": 0,
|
||||||
|
"desktop": {
|
||||||
|
"canvas_nonblank": true,
|
||||||
|
"document_width": 1425,
|
||||||
|
"machine_nodes_in_tool_preview": 0,
|
||||||
|
"nonzero_pixels": 499671,
|
||||||
|
"scroll_width": 1425,
|
||||||
|
"status": "READY",
|
||||||
|
"toolpath_segments": 3,
|
||||||
|
"viewport": [
|
||||||
|
1440,
|
||||||
|
1000
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"functional_note": "Chromium simulation only; Safari and physical power loss are excluded by scope",
|
||||||
|
"mobile": {
|
||||||
|
"canvas_nonblank": true,
|
||||||
|
"document_width": 390,
|
||||||
|
"machine_nodes_in_tool_preview": 0,
|
||||||
|
"nonzero_pixels": 114080,
|
||||||
|
"scroll_width": 390,
|
||||||
|
"status": "READY",
|
||||||
|
"toolpath_segments": 3,
|
||||||
|
"viewport": [
|
||||||
|
390,
|
||||||
|
844
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"preserve_drawing_buffer": true
|
||||||
|
},
|
||||||
|
"candidate_eligible": true,
|
||||||
|
"case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"scope": "xyzac_only"
|
||||||
|
},
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"direction": {
|
||||||
|
"rotate_c": "z+",
|
||||||
|
"saddle_y": -1,
|
||||||
|
"spindle_z": -1,
|
||||||
|
"table_x": -1,
|
||||||
|
"tilt_a": "x+",
|
||||||
|
"tool_offset": "z-"
|
||||||
|
},
|
||||||
|
"exclusions": [
|
||||||
|
"xyzbc",
|
||||||
|
"3axis",
|
||||||
|
"other cases",
|
||||||
|
"Safari",
|
||||||
|
"physical power loss",
|
||||||
|
"host hardware"
|
||||||
|
],
|
||||||
|
"failure_samples": [],
|
||||||
|
"known_blockers": [],
|
||||||
|
"machine_groups": [
|
||||||
|
"xyzac-machine",
|
||||||
|
"table-x",
|
||||||
|
"saddle-y",
|
||||||
|
"spindle-z",
|
||||||
|
"tilt-a",
|
||||||
|
"rotate-c",
|
||||||
|
"tool-offset",
|
||||||
|
"xyzac-toolpath",
|
||||||
|
"xyzac-live-toolpath"
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"path": "web/react/src/XyzacMachineView.tsx",
|
||||||
|
"sha256": "fd3804b8b86d6630054755c00f35828591dec5093f86ea080c9d6fb5db7aa05d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/react/src/XyzacToolPreview.tsx",
|
||||||
|
"sha256": "caca009f96336847c8d9b8d5823c3d7de86ed77c37cae2fe9f5f3e4d30132d57"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/react/src/xyzac-visual-adapter.ts",
|
||||||
|
"sha256": "2e67bf4522ff87648d0b7de1f6fc447d51e780eed66c299d7f4bc3c35d7b33f8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/react/src/test_xyzac_threejs.mjs",
|
||||||
|
"sha256": "840f191d5243016e6ecba4404f2950a067d63f9c1f280a2c1655f30bf021324b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/react/src/test_xyzac_tool_preview.mjs",
|
||||||
|
"sha256": "99f60e050a3ba90b3feb2922b007a49cb4a9e2f88610a0f55f1b6a9c8f07cdbd"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"projection": {
|
||||||
|
"active_segment": 7,
|
||||||
|
"clear_visual_nonmutating": true,
|
||||||
|
"state_hash": "fnv1a64:xyzac-authoritative",
|
||||||
|
"tick": 42
|
||||||
|
},
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": "fd1296fc1819bee5371708e869b0547dd64bd91f98eab1d12f1ddce043450f6a",
|
||||||
|
"schema": "xyzac-trt-threejs-report.v1",
|
||||||
|
"status": "verified",
|
||||||
|
"task_id": "RTCP-10",
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"--experimental-strip-types",
|
||||||
|
"web/react/src/test_xyzac_threejs.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "2760c09251a854eec84d854cfb8847244bf21653e7d096fa625d4fe2970924ee",
|
||||||
|
"returncode": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"--experimental-strip-types",
|
||||||
|
"web/react/src/test_xyzac_tool_preview.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "26a7f3645c9ca6212fc5664e870eee5dff2a146bb71a1d14de6845e982cddf92",
|
||||||
|
"returncode": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"npm",
|
||||||
|
"run",
|
||||||
|
"typecheck"
|
||||||
|
],
|
||||||
|
"output_sha256": "08fb3e3d0d9396a56f8720eb15d9f6920f8a30c2dad7b0b6672ac446801f27e3",
|
||||||
|
"returncode": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tool_preview": {
|
||||||
|
"active_segment": 3,
|
||||||
|
"canonical_toolpath_segments": 1,
|
||||||
|
"joints": [
|
||||||
|
11,
|
||||||
|
-4,
|
||||||
|
19,
|
||||||
|
31,
|
||||||
|
44
|
||||||
|
],
|
||||||
|
"machine_scene_nodes": 0,
|
||||||
|
"schema": "xyzac-tool-preview-rtcp-10b.v1",
|
||||||
|
"source": "linuxcnc-wasm-worker",
|
||||||
|
"state_hash": "fnv1a64:tool-preview",
|
||||||
|
"status": "verified",
|
||||||
|
"tick": 12,
|
||||||
|
"tool_pose": {
|
||||||
|
"a": 31,
|
||||||
|
"b": 0,
|
||||||
|
"c": 44,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": 11,
|
||||||
|
"y": -4,
|
||||||
|
"z": 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"visual_source": "authoritative panel_snapshot.visual + panel_snapshot.kinematics; main preview is tool-only"
|
||||||
|
}
|
||||||
436
coverage/xyzac-trt-tool-preview.report.json
Normal file
@@ -0,0 +1,436 @@
|
|||||||
|
{
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"browser_matrix": {
|
||||||
|
"desktop": {
|
||||||
|
"canvas_nonblank": true,
|
||||||
|
"document_width": 1425,
|
||||||
|
"nonzero_pixels": 499671,
|
||||||
|
"sample_points": 438,
|
||||||
|
"scroll_width": 1425,
|
||||||
|
"status": "READY",
|
||||||
|
"toolpath_segments": 29,
|
||||||
|
"viewport": [
|
||||||
|
1440,
|
||||||
|
1000
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"machine_nodes_in_tool_preview": 0,
|
||||||
|
"mobile": {
|
||||||
|
"canvas_nonblank": true,
|
||||||
|
"document_width": 390,
|
||||||
|
"nonzero_pixels": 114080,
|
||||||
|
"sample_points": 438,
|
||||||
|
"scroll_width": 390,
|
||||||
|
"status": "READY",
|
||||||
|
"toolpath_segments": 29,
|
||||||
|
"viewport": [
|
||||||
|
390,
|
||||||
|
844
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"candidate_eligible": true,
|
||||||
|
"canonical_source": "panel_snapshot.visual.toolpath",
|
||||||
|
"case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"scope": "xyzac_only"
|
||||||
|
},
|
||||||
|
"exclusions": [
|
||||||
|
"xyzbc",
|
||||||
|
"3axis",
|
||||||
|
"other cases",
|
||||||
|
"Safari",
|
||||||
|
"physical power loss",
|
||||||
|
"host hardware"
|
||||||
|
],
|
||||||
|
"failure_samples": [],
|
||||||
|
"known_blockers": [],
|
||||||
|
"machine_preview": {
|
||||||
|
"embedded": false,
|
||||||
|
"separate_component": "web/react/src/XyzacMachineView.tsx"
|
||||||
|
},
|
||||||
|
"origin_coordinate_system": {
|
||||||
|
"axes": [
|
||||||
|
"X",
|
||||||
|
"Y",
|
||||||
|
"Z"
|
||||||
|
],
|
||||||
|
"labels": true,
|
||||||
|
"origin": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"source": "xyzac-origin-coordinate-system"
|
||||||
|
},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"path": "web/react/src/XyzacToolPreview.tsx",
|
||||||
|
"sha256": "caca009f96336847c8d9b8d5823c3d7de86ed77c37cae2fe9f5f3e4d30132d57"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/react/src/xyzac-visual-adapter.ts",
|
||||||
|
"sha256": "2e67bf4522ff87648d0b7de1f6fc447d51e780eed66c299d7f4bc3c35d7b33f8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/worker/sim-session.mjs",
|
||||||
|
"sha256": "7ce91549f0f3a06ff77db9ee281fd9544848f6e41e08f9da302ee2e7827194e3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/worker/test_xyzac_exact_preview.mjs",
|
||||||
|
"sha256": "dc5cc312aab46003e12a4bfc26d695b05d78843e61808c550cb4a5925338687b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/worker/wasm-program-runtime.mjs",
|
||||||
|
"sha256": "60da7b953ac4ee2659f2b05c6bb93b58c8d0c2c4b56363f6a93e44aab7aebb02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/worker/wasm-motion-queue.mjs",
|
||||||
|
"sha256": "85b53bf7a31b9b1f1e7e76dad4bf29aabb36defcf6344d34bbb192c157a80764"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/worker/wasm-xyzac-io.mjs",
|
||||||
|
"sha256": "fdb92e8819e38010aa7a1b8f5f58473a930644fbfeba3d7d5a5c23e075ee92ca"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/worker/wasm-xyzac-device-io.mjs",
|
||||||
|
"sha256": "1d813d59048afb6854b6fd7783dc86f65bf37ccc46f0d4f592b56cbe044211a5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/worker/test_xyzac_machine_sequence.mjs",
|
||||||
|
"sha256": "9dcd89d54596fe4b339d5aca4e028a0c44e173a8e646f1254defc706bd3fcc96"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/react/src/App.tsx",
|
||||||
|
"sha256": "8cbec29f00c44e18a01ac92734e4f8b64764cc04db4cec48ce9c28693bd2bbef"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/react/src/styles.css",
|
||||||
|
"sha256": "c39b6848f0c8d176991acb633206697bbc01bd8dbf28cba69afe605b1d7e6cb9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "wasm-core/src/sim_task.c",
|
||||||
|
"sha256": "06aa4ba015d863e1749cfe11877a82507fd6aff2c6922a114f30f6c8871b0b5b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "wasm-core/src/sim_program.c",
|
||||||
|
"sha256": "1ab97bbe8750d678b8cffebbad9cad44926285b627858cf2b1aad22da3e0457f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "wasm-core/src/sim_posemath.c",
|
||||||
|
"sha256": "2e63d8cc2bf4275a89238b2622b41dcd8dfabfe6d24ef5918628083a4f4f5216"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preview_mode": "tool_only",
|
||||||
|
"projection": {
|
||||||
|
"exact_program": {
|
||||||
|
"arc_count": 4,
|
||||||
|
"arc_turn": -3,
|
||||||
|
"case": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"clear_visual_only": true,
|
||||||
|
"helix_details": [
|
||||||
|
{
|
||||||
|
"axial_delta": -5,
|
||||||
|
"center": {
|
||||||
|
"x": 20,
|
||||||
|
"y": 20,
|
||||||
|
"z": 10
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"a": 20,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": 10,
|
||||||
|
"y": 20,
|
||||||
|
"z": 5
|
||||||
|
},
|
||||||
|
"kind": "helix",
|
||||||
|
"radius": 10,
|
||||||
|
"samples": 97,
|
||||||
|
"samples_source": "linuxcnc-wasm-sim_posemath_segment_point",
|
||||||
|
"source_line": 67,
|
||||||
|
"start": {
|
||||||
|
"a": 20,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": 10,
|
||||||
|
"y": 20,
|
||||||
|
"z": 10
|
||||||
|
},
|
||||||
|
"turns": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"axial_delta": -5,
|
||||||
|
"center": {
|
||||||
|
"x": -20,
|
||||||
|
"y": 20,
|
||||||
|
"z": 10
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"a": 20,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": -30,
|
||||||
|
"y": 20,
|
||||||
|
"z": 5
|
||||||
|
},
|
||||||
|
"kind": "helix",
|
||||||
|
"radius": 10,
|
||||||
|
"samples": 97,
|
||||||
|
"samples_source": "linuxcnc-wasm-sim_posemath_segment_point",
|
||||||
|
"source_line": 67,
|
||||||
|
"start": {
|
||||||
|
"a": 20,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": -30,
|
||||||
|
"y": 20,
|
||||||
|
"z": 10
|
||||||
|
},
|
||||||
|
"turns": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"axial_delta": -5,
|
||||||
|
"center": {
|
||||||
|
"x": -20,
|
||||||
|
"y": -20,
|
||||||
|
"z": 10
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"a": 20,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": -30,
|
||||||
|
"y": -20,
|
||||||
|
"z": 5
|
||||||
|
},
|
||||||
|
"kind": "helix",
|
||||||
|
"radius": 10,
|
||||||
|
"samples": 97,
|
||||||
|
"samples_source": "linuxcnc-wasm-sim_posemath_segment_point",
|
||||||
|
"source_line": 67,
|
||||||
|
"start": {
|
||||||
|
"a": 20,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": -30,
|
||||||
|
"y": -20,
|
||||||
|
"z": 10
|
||||||
|
},
|
||||||
|
"turns": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"axial_delta": -5,
|
||||||
|
"center": {
|
||||||
|
"x": 20,
|
||||||
|
"y": -20,
|
||||||
|
"z": 10
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"a": 20,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": 10,
|
||||||
|
"y": -20,
|
||||||
|
"z": 5
|
||||||
|
},
|
||||||
|
"kind": "helix",
|
||||||
|
"radius": 10,
|
||||||
|
"samples": 97,
|
||||||
|
"samples_source": "linuxcnc-wasm-sim_posemath_segment_point",
|
||||||
|
"source_line": 67,
|
||||||
|
"start": {
|
||||||
|
"a": 20,
|
||||||
|
"b": 0,
|
||||||
|
"c": 45,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": 10,
|
||||||
|
"y": -20,
|
||||||
|
"z": 10
|
||||||
|
},
|
||||||
|
"turns": 3
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"helix_validation": {
|
||||||
|
"radius_mm": 10,
|
||||||
|
"sample_points_per_helix": 97,
|
||||||
|
"samples_source": "linuxcnc-wasm-sim_posemath_segment_point",
|
||||||
|
"turns_per_helix": 3,
|
||||||
|
"z_end": 5,
|
||||||
|
"z_start": 10
|
||||||
|
},
|
||||||
|
"motion_mode_source": "linuxcnc-wasm-sim_program_canonical",
|
||||||
|
"program_path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins.ngc",
|
||||||
|
"rapid_count": 25,
|
||||||
|
"restart_visual_segments": 29,
|
||||||
|
"schema": "xyzac-exact-tool-preview.v1",
|
||||||
|
"segment_count": 29,
|
||||||
|
"source_hash": "fnv1a64:522e219683194ad2",
|
||||||
|
"state_hash": "fnv1a64:c7e53444cce4a142",
|
||||||
|
"status": "verified",
|
||||||
|
"visual_segments": 29
|
||||||
|
},
|
||||||
|
"machine_sequence": {
|
||||||
|
"active_progress": 0.09848857801796111,
|
||||||
|
"active_segment_id": 4,
|
||||||
|
"authority": {
|
||||||
|
"home_limit": "wasm_home_limit",
|
||||||
|
"io": "wasm_io",
|
||||||
|
"motion": "wasm",
|
||||||
|
"program": "linuxcnc-wasm-sim_program",
|
||||||
|
"visual": "wasm_canonical_motion"
|
||||||
|
},
|
||||||
|
"case": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"current_line_after_step": 68,
|
||||||
|
"estop_queue_depth": 0,
|
||||||
|
"full_run": {
|
||||||
|
"cycles": 8000,
|
||||||
|
"final_switchkins_type": 0,
|
||||||
|
"program_status": "completed",
|
||||||
|
"queue_depth": 0,
|
||||||
|
"task_state": "STOPPED"
|
||||||
|
},
|
||||||
|
"home_joint_count": 5,
|
||||||
|
"js_motion_unchanged": true,
|
||||||
|
"live_axis_sample": {
|
||||||
|
"a": 1.9697715603592223,
|
||||||
|
"b": 0,
|
||||||
|
"c": 4.43198601080825,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": 10,
|
||||||
|
"y": 20,
|
||||||
|
"z": 10
|
||||||
|
},
|
||||||
|
"live_joints": [
|
||||||
|
8.42458503579587,
|
||||||
|
20.712530389342145,
|
||||||
|
10.024505737045653,
|
||||||
|
1.9697715603592223,
|
||||||
|
4.43198601080825
|
||||||
|
],
|
||||||
|
"rtcp_active_type": 1,
|
||||||
|
"rtcp_joint_pose_delta": [
|
||||||
|
-1.5754149642041302,
|
||||||
|
0.7125303893421453,
|
||||||
|
0.02450573704565251,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"schema": "xyzac-machine-sequence.v1",
|
||||||
|
"sequence": [
|
||||||
|
"ESTOP",
|
||||||
|
"ESTOP_RELEASE",
|
||||||
|
"IO_RECOVER",
|
||||||
|
"POWER",
|
||||||
|
"HOME_ALL",
|
||||||
|
"PROGRAM_RUN",
|
||||||
|
"SIM_STEP"
|
||||||
|
],
|
||||||
|
"state_hash": "fnv1a64:c8f6326c97e651f9",
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"static": {
|
||||||
|
"active_segment": 3,
|
||||||
|
"canonical_toolpath_segments": 1,
|
||||||
|
"joints": [
|
||||||
|
11,
|
||||||
|
-4,
|
||||||
|
19,
|
||||||
|
31,
|
||||||
|
44
|
||||||
|
],
|
||||||
|
"machine_scene_nodes": 0,
|
||||||
|
"schema": "xyzac-tool-preview-rtcp-10b.v1",
|
||||||
|
"source": "linuxcnc-wasm-worker",
|
||||||
|
"state_hash": "fnv1a64:tool-preview",
|
||||||
|
"status": "verified",
|
||||||
|
"tick": 12,
|
||||||
|
"tool_pose": {
|
||||||
|
"a": 31,
|
||||||
|
"b": 0,
|
||||||
|
"c": 44,
|
||||||
|
"u": 0,
|
||||||
|
"v": 0,
|
||||||
|
"w": 0,
|
||||||
|
"x": 11,
|
||||||
|
"y": -4,
|
||||||
|
"z": 19
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": "94b18227ec72689934629a5b428cf6a9f16500d30b200d4b36a10d03c84c56b9",
|
||||||
|
"schema": "xyzac-trt-tool-preview-report.v1",
|
||||||
|
"status": "verified",
|
||||||
|
"task_id": "RTCP-10-TOOL-PREVIEW",
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"--experimental-strip-types",
|
||||||
|
"web/react/src/test_xyzac_tool_preview.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "26a7f3645c9ca6212fc5664e870eee5dff2a146bb71a1d14de6845e982cddf92",
|
||||||
|
"returncode": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"web/worker/test_xyzac_exact_preview.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "60cec025efd2f29de45fb6139f0929f329a505bd331212e6b01ab5fbb5f8fca8",
|
||||||
|
"returncode": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"web/worker/test_xyzac_machine_sequence.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "2a72e723603080bb3eff8169b8be5352fa46d6b0c8ae04dbeeb2e5e1371e1c47",
|
||||||
|
"returncode": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"three_d_controller": {
|
||||||
|
"buttons": [
|
||||||
|
"zoomIn",
|
||||||
|
"zoomOut",
|
||||||
|
"panLeft",
|
||||||
|
"panRight",
|
||||||
|
"panUp",
|
||||||
|
"panDown",
|
||||||
|
"reset"
|
||||||
|
],
|
||||||
|
"library": "three/examples/jsm/controls/OrbitControls",
|
||||||
|
"pointer_controls": {
|
||||||
|
"left_drag": "rotate",
|
||||||
|
"right_or_middle_drag": "pan",
|
||||||
|
"wheel": "zoom"
|
||||||
|
},
|
||||||
|
"source": "web/react/src/XyzacToolPreview.tsx"
|
||||||
|
}
|
||||||
|
}
|
||||||
165
coverage/xyzac-trt-trace-parity.report.json
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
{
|
||||||
|
"builds": [
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"cmake",
|
||||||
|
"--build",
|
||||||
|
"build-native",
|
||||||
|
"--target",
|
||||||
|
"sim_xyzac_trace_test"
|
||||||
|
],
|
||||||
|
"output_sha256": "8c35b1682f73592d30612d7b0fabdb384fcfff35cb5a076cb48b85b1368aa794",
|
||||||
|
"returncode": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"cmake",
|
||||||
|
"--build",
|
||||||
|
"build-wasm",
|
||||||
|
"--target",
|
||||||
|
"sim_xyzac_trace_wasm_test"
|
||||||
|
],
|
||||||
|
"output_sha256": "8c35b1682f73592d30612d7b0fabdb384fcfff35cb5a076cb48b85b1368aa794",
|
||||||
|
"returncode": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"candidate_eligible": true,
|
||||||
|
"case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"scope": "xyzac_only"
|
||||||
|
},
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"exclusions": [
|
||||||
|
"xyzbc",
|
||||||
|
"3axis",
|
||||||
|
"other cases",
|
||||||
|
"Safari",
|
||||||
|
"physical power loss",
|
||||||
|
"host hardware"
|
||||||
|
],
|
||||||
|
"failure_samples": [],
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"path": "wasm-core/tests/sim_xyzac_trace_test.c",
|
||||||
|
"sha256": "0a3d5f748d9f69f4212c3a1cdd65fe2986c253442c4de2de78de2e5a5635ff08"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "web/worker/test_xyzac_trace_parity.mjs",
|
||||||
|
"sha256": "f5c6a7cd3e8b7f72bb1b25b5c368c0e52d494b9c811022d34435e668612ec4ce"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "coverage/xyzac-trt-rtcp.report.json",
|
||||||
|
"sha256": "b1562d6d2c9cd5144c7c8c9b334bd8bf67919ea7297f103ec67cb32773608d08"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"known_blockers": [],
|
||||||
|
"levels": [
|
||||||
|
"protocol",
|
||||||
|
"control",
|
||||||
|
"state",
|
||||||
|
"canonical",
|
||||||
|
"recovery"
|
||||||
|
],
|
||||||
|
"native_wasm": {
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"levels": {
|
||||||
|
"canonical": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"control": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"protocol": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"recovery": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"native_worker": {
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"levels": {
|
||||||
|
"canonical": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"control": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"protocol": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"recovery": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"difference_path": null,
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"status": "verified"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"positive_divergence_sample": {
|
||||||
|
"detected": true,
|
||||||
|
"difference_path": "$[1].active_type",
|
||||||
|
"first_divergent_tick": 2,
|
||||||
|
"mutation": "state[1].active_type=99"
|
||||||
|
},
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": "39c63aa9af38fd98e49fd68ec2401ee8715fcafef98d47c070a47ab6f6dc0a48",
|
||||||
|
"schema": "xyzac-trt-trace-parity-report.v1",
|
||||||
|
"status": "verified",
|
||||||
|
"task_id": "PARITY-00",
|
||||||
|
"traces": {
|
||||||
|
"native": {
|
||||||
|
"command": [
|
||||||
|
"./build-native/sim_xyzac_trace_test"
|
||||||
|
],
|
||||||
|
"output_sha256": "18d2ef9d77f9a7a5bf2fa8d9380c8b04383a58ee2b0258364cba31ec34a168f0",
|
||||||
|
"returncode": 0,
|
||||||
|
"trace_sha256": "ca13e8dcb6d2816787a98c7bddc04db552b88f8d8a994b49301973a1bd11245a"
|
||||||
|
},
|
||||||
|
"wasm": {
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"build-wasm/sim_xyzac_trace_wasm_test.js"
|
||||||
|
],
|
||||||
|
"output_sha256": "18d2ef9d77f9a7a5bf2fa8d9380c8b04383a58ee2b0258364cba31ec34a168f0",
|
||||||
|
"returncode": 0,
|
||||||
|
"trace_sha256": "ca13e8dcb6d2816787a98c7bddc04db552b88f8d8a994b49301973a1bd11245a"
|
||||||
|
},
|
||||||
|
"worker": {
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"web/worker/test_xyzac_trace_parity.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "773f77181d7fd891a25a9ec6e7c3109f5f6e9b7ddcc3f876e1e973657959558e",
|
||||||
|
"returncode": 0,
|
||||||
|
"trace_sha256": "ca13e8dcb6d2816787a98c7bddc04db552b88f8d8a994b49301973a1bd11245a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2257
coverage/xyzac-trt-wasm-authority-audit.report.json
Normal file
111
coverage/xyzac-trt-wasm-only-release-gate.report.json
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
{
|
||||||
|
"blocker_identity": [
|
||||||
|
{
|
||||||
|
"classification": "HOST_EXCLUDED",
|
||||||
|
"detail": "Safari is explicitly excluded from this project",
|
||||||
|
"id": "safari_not_in_project_scope"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"classification": "REAL-EXCLUDED",
|
||||||
|
"detail": "simulation environment intentionally excludes real physical power loss",
|
||||||
|
"id": "physical_power_loss_not_simulated"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"classification": "HOST_EXCLUDED",
|
||||||
|
"detail": "only xyzac-trt.ini is selected",
|
||||||
|
"id": "other_linuxcnc_cases_out_of_scope"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"candidate_eligible": false,
|
||||||
|
"case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"scope": "xyzac_only"
|
||||||
|
},
|
||||||
|
"checks": {
|
||||||
|
"all_task_reports": {
|
||||||
|
"count": 14,
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"browser_matrix": {
|
||||||
|
"desktop": {
|
||||||
|
"canvas_nonblank": true,
|
||||||
|
"document_width": 1425,
|
||||||
|
"machine_nodes_in_tool_preview": 0,
|
||||||
|
"nonzero_pixels": 499671,
|
||||||
|
"scroll_width": 1425,
|
||||||
|
"status": "READY",
|
||||||
|
"toolpath_segments": 3,
|
||||||
|
"viewport": [
|
||||||
|
1440,
|
||||||
|
1000
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"mobile": {
|
||||||
|
"canvas_nonblank": true,
|
||||||
|
"document_width": 390,
|
||||||
|
"machine_nodes_in_tool_preview": 0,
|
||||||
|
"nonzero_pixels": 114080,
|
||||||
|
"scroll_width": 390,
|
||||||
|
"status": "READY",
|
||||||
|
"toolpath_segments": 3,
|
||||||
|
"viewport": [
|
||||||
|
390,
|
||||||
|
844
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"recovery": {
|
||||||
|
"at_checkpoint_equal": true,
|
||||||
|
"continuous_hash": "fnv1a64:a4dee4490d19b4ec",
|
||||||
|
"restored_hash": "fnv1a64:ca9e3481aaa28f6d",
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"scope_exclusions": {
|
||||||
|
"Safari": "out_of_scope",
|
||||||
|
"host_hardware": "out_of_scope",
|
||||||
|
"other_cases": [
|
||||||
|
"xyzbc",
|
||||||
|
"3axis"
|
||||||
|
],
|
||||||
|
"physical_power_loss": "REAL-EXCLUDED",
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"selected_case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"trace_parity": {
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"levels": [
|
||||||
|
"protocol",
|
||||||
|
"control",
|
||||||
|
"state",
|
||||||
|
"canonical",
|
||||||
|
"recovery"
|
||||||
|
],
|
||||||
|
"status": "verified"
|
||||||
|
},
|
||||||
|
"wasm_authority": {
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"js_authority": 0,
|
||||||
|
"status": "verified"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"failure_samples": [],
|
||||||
|
"input": {
|
||||||
|
"path": "coverage/xyzac-trt-web-wasm-parity.report.json",
|
||||||
|
"sha256": "d8bd04725525c1c5881d749466c8793f4b771e34ba260c5d35cb1d29f889d4fc"
|
||||||
|
},
|
||||||
|
"known_blockers": [
|
||||||
|
"STOP_RELEASE is intentional until an owner authorizes scope expansion"
|
||||||
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"release_decision": "selected_case_verified_but_release_stopped_by_scope_exclusions",
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": "70dc5704e2530f9a42d6f5985f242be4fc4b691a2272fd15b9fa0bd8031a0728",
|
||||||
|
"schema": "xyzac-trt-wasm-only-release-gate.v1",
|
||||||
|
"status": "verified",
|
||||||
|
"task_id": "RELEASE-00"
|
||||||
|
}
|
||||||
138
coverage/xyzac-trt-web-wasm-parity.report.json
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
{
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"authority_audit": {},
|
||||||
|
"blocker_identity": {
|
||||||
|
"case_mismatches": [],
|
||||||
|
"failed_reports": [],
|
||||||
|
"host_excluded": [
|
||||||
|
"Safari",
|
||||||
|
"physical power loss",
|
||||||
|
"host hardware"
|
||||||
|
],
|
||||||
|
"missing_reports": [],
|
||||||
|
"release_status": "STOP_RELEASE"
|
||||||
|
},
|
||||||
|
"browser_matrix": {
|
||||||
|
"canvas_context": "webgl2",
|
||||||
|
"console_exceptions": 0,
|
||||||
|
"desktop": {
|
||||||
|
"canvas_nonblank": true,
|
||||||
|
"document_width": 1425,
|
||||||
|
"machine_nodes_in_tool_preview": 0,
|
||||||
|
"nonzero_pixels": 499671,
|
||||||
|
"scroll_width": 1425,
|
||||||
|
"status": "READY",
|
||||||
|
"toolpath_segments": 3,
|
||||||
|
"viewport": [
|
||||||
|
1440,
|
||||||
|
1000
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"functional_note": "Chromium simulation only; Safari and physical power loss are excluded by scope",
|
||||||
|
"mobile": {
|
||||||
|
"canvas_nonblank": true,
|
||||||
|
"document_width": 390,
|
||||||
|
"machine_nodes_in_tool_preview": 0,
|
||||||
|
"nonzero_pixels": 114080,
|
||||||
|
"scroll_width": 390,
|
||||||
|
"status": "READY",
|
||||||
|
"toolpath_segments": 3,
|
||||||
|
"viewport": [
|
||||||
|
390,
|
||||||
|
844
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"preserve_drawing_buffer": true
|
||||||
|
},
|
||||||
|
"candidate_eligible": false,
|
||||||
|
"case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"scope": "xyzac_only"
|
||||||
|
},
|
||||||
|
"checks": {
|
||||||
|
"all_reports_present": true,
|
||||||
|
"all_reports_verified": true,
|
||||||
|
"all_selected_case": true,
|
||||||
|
"browser_desktop_mobile": true,
|
||||||
|
"recovery_endpoint_equal": true,
|
||||||
|
"trace_first_divergent_tick_null": true
|
||||||
|
},
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"exclusions": [
|
||||||
|
"xyzbc",
|
||||||
|
"3axis",
|
||||||
|
"other cases",
|
||||||
|
"Safari",
|
||||||
|
"physical power loss",
|
||||||
|
"host hardware"
|
||||||
|
],
|
||||||
|
"failure_samples": [],
|
||||||
|
"known_blockers": [
|
||||||
|
"HOST_EXCLUDED: Safari not in project scope",
|
||||||
|
"REAL-EXCLUDED: physical power loss not simulated"
|
||||||
|
],
|
||||||
|
"parity": {
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"native_wasm": "verified",
|
||||||
|
"native_worker": "verified",
|
||||||
|
"positive_divergence_tick": 2,
|
||||||
|
"trace_levels": [
|
||||||
|
"protocol",
|
||||||
|
"control",
|
||||||
|
"state",
|
||||||
|
"canonical",
|
||||||
|
"recovery"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"recovery": {
|
||||||
|
"at_checkpoint_equal": true,
|
||||||
|
"continuous_hash": "fnv1a64:a4dee4490d19b4ec",
|
||||||
|
"restored_hash": "fnv1a64:ca9e3481aaa28f6d"
|
||||||
|
},
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_hashes": {
|
||||||
|
"coverage/xyzac-trt-gate-matrix.report.json": "495f452f59848114bb885561e26e7080f356650cc1b620cfddf515685fbf1628",
|
||||||
|
"coverage/xyzac-trt-hal-tool-alarm.report.json": "2abcfd59bf16e596c9183296061ff66ce00f6d9b6efee121519a499007001216",
|
||||||
|
"coverage/xyzac-trt-manual-control.report.json": "b7333980194ff9b6344d34ec97a181fbb79b5730184dd13a6d1a9067c9212af9",
|
||||||
|
"coverage/xyzac-trt-program-preview.report.json": "ee41e2b4ac97f719817e21eee2bd3d503fc78c783ec1bc185466b3c56fc62045",
|
||||||
|
"coverage/xyzac-trt-protocol-react.report.json": "bc25315930095ca096bae0f97047c41d45bf9ff83373ac7a73f52273b33d9237",
|
||||||
|
"coverage/xyzac-trt-recovery.report.json": "deff32f2998930dfcd9373ca0a6a960d1fa7e5732ef785cc7cacf336b9dc3459",
|
||||||
|
"coverage/xyzac-trt-rtcp.report.json": "b1562d6d2c9cd5144c7c8c9b334bd8bf67919ea7297f103ec67cb32773608d08",
|
||||||
|
"coverage/xyzac-trt-source-slice-inventory.report.json": "20382f2bcd13fdaf3f25521b7a8c42e83e0409f75092c643b4c79a7613fdf287",
|
||||||
|
"coverage/xyzac-trt-startup.report.json": "56e040b645bcefc3d6c57244cb2d3aba16f421ff5137089d9c3336970dfd968b",
|
||||||
|
"coverage/xyzac-trt-threejs.report.json": "2791930c38a48ab9fa61f284e57483708787e89b558c75ddc9d99e4b3e76ad28",
|
||||||
|
"coverage/xyzac-trt-tool-preview.report.json": "4a5b286b0a13324a1e773669ef34ab9a64fd0376dfaf5adf39a95d9b420bd965",
|
||||||
|
"coverage/xyzac-trt-trace-parity.report.json": "ef914bb7823b9cdb0f6e15ca409515659d5575d09cdc6da2c458cd81fafa595a",
|
||||||
|
"coverage/xyzac-trt-wasm-authority-audit.report.json": "e7c84d3f36db280c13c1ab8a29f4f827b4257eb34b8cb2bbc47357a920001692",
|
||||||
|
"coverage/xyzac-trt-workflow.report.json": "ede73fab04f3cc3e64a13fbe0ab10603253e9a01b4357feff1f3f54313777e75"
|
||||||
|
},
|
||||||
|
"report_sha256": "bf2f04c1798cd43016833836bab8c789ca71699ae414746601ae0efed010666d",
|
||||||
|
"schema": "xyzac-trt-web-wasm-parity-report.v1",
|
||||||
|
"source_coverage": {
|
||||||
|
"axis_keyboard": 10,
|
||||||
|
"axis_menus": 80,
|
||||||
|
"axis_toolbar": 20,
|
||||||
|
"blocked_resources": 0,
|
||||||
|
"direct_sources": 35,
|
||||||
|
"slices": 11,
|
||||||
|
"unregistered_slices": 0
|
||||||
|
},
|
||||||
|
"status": "verified",
|
||||||
|
"task_id": "RTCP-12",
|
||||||
|
"task_reports": {
|
||||||
|
"coverage/xyzac-trt-gate-matrix.report.json": "verified",
|
||||||
|
"coverage/xyzac-trt-hal-tool-alarm.report.json": "verified",
|
||||||
|
"coverage/xyzac-trt-manual-control.report.json": "verified",
|
||||||
|
"coverage/xyzac-trt-program-preview.report.json": "verified",
|
||||||
|
"coverage/xyzac-trt-protocol-react.report.json": "verified",
|
||||||
|
"coverage/xyzac-trt-recovery.report.json": "verified",
|
||||||
|
"coverage/xyzac-trt-rtcp.report.json": "verified",
|
||||||
|
"coverage/xyzac-trt-source-slice-inventory.report.json": "verified",
|
||||||
|
"coverage/xyzac-trt-startup.report.json": "verified",
|
||||||
|
"coverage/xyzac-trt-threejs.report.json": "verified",
|
||||||
|
"coverage/xyzac-trt-tool-preview.report.json": "verified",
|
||||||
|
"coverage/xyzac-trt-trace-parity.report.json": "verified",
|
||||||
|
"coverage/xyzac-trt-wasm-authority-audit.report.json": "verified",
|
||||||
|
"coverage/xyzac-trt-workflow.report.json": "verified"
|
||||||
|
}
|
||||||
|
}
|
||||||
63
coverage/xyzac-trt-workflow.report.json
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"abort_queue_empty": true,
|
||||||
|
"active_segment_before_abort": 1,
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"candidate_eligible": true,
|
||||||
|
"case": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"sha256": "f985970db293b70c055e4c41fc316d0c16037b5dc7ce07490e18f554e851f811"
|
||||||
|
},
|
||||||
|
"command_trace": [
|
||||||
|
"PROGRAM_LOAD",
|
||||||
|
"PROGRAM_RUN",
|
||||||
|
"SIM_STEP",
|
||||||
|
"PROGRAM_PAUSE",
|
||||||
|
"SNAPSHOT_REQUEST",
|
||||||
|
"PROGRAM_RESUME",
|
||||||
|
"SIM_STEP",
|
||||||
|
"PROGRAM_ABORT",
|
||||||
|
"PROGRAM_RESTART",
|
||||||
|
"PROGRAM_SEEK",
|
||||||
|
"PROGRAM_RESUME",
|
||||||
|
"SIM_STEP",
|
||||||
|
"CHECKPOINT",
|
||||||
|
"RECOVER"
|
||||||
|
],
|
||||||
|
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||||
|
"failure_samples": [],
|
||||||
|
"pause_cursor_stable": true,
|
||||||
|
"recovery_status": "recovered",
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": "d98a16e6582c0bc9fdaa410da64be369cc21d56a61a04b43436d4900c12461b0",
|
||||||
|
"restart_source_hash_preserved": true,
|
||||||
|
"run_from_line": 4,
|
||||||
|
"schema": "xyzac-workflow-rtcp-05.v1",
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"state_hash": "fnv1a64:285d97dde7d56ea7",
|
||||||
|
"status": "verified",
|
||||||
|
"task_id": "RTCP-05",
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"command": [
|
||||||
|
"node",
|
||||||
|
"web/worker/test_xyzac_workflow.mjs"
|
||||||
|
],
|
||||||
|
"output_sha256": "197bc634f7f9ddf290b1a64d3cbd1f52d257dcc4982e5de3bd16f6358c70702b",
|
||||||
|
"returncode": 0,
|
||||||
|
"status": "verified"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tick_trace": [
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"workflow": {
|
||||||
|
"abort": "verified",
|
||||||
|
"pause": "verified",
|
||||||
|
"recovery": "verified",
|
||||||
|
"restart": "verified",
|
||||||
|
"resume": "verified",
|
||||||
|
"run": "verified",
|
||||||
|
"run_from_line": "verified",
|
||||||
|
"step": "verified"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
doc-g/5axis-rtcp-running.png
Normal file
|
After Width: | Height: | Size: 186 KiB |
62
doc-g/5axis-rtcp-shortcut.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# 五轴 RTCP 案例快捷方式
|
||||||
|
|
||||||
|
快捷方式:[`../run-5axis-rtcp.sh`](../run-5axis-rtcp.sh)
|
||||||
|
|
||||||
|
默认执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run-5axis-rtcp.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
默认案例是:
|
||||||
|
|
||||||
|
```text
|
||||||
|
linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
它使用 `XYZAC` 五轴表旋转/倾斜(TRT)拓扑、`xyzac-trt-kins` 和 Vismach `xyzac-trt-gui`。默认演示程序是:
|
||||||
|
|
||||||
|
```text
|
||||||
|
linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins.ngc
|
||||||
|
```
|
||||||
|
|
||||||
|
该顶层演示调用 `remap_subs/xyzac_switchkins_sub.ngc`;其螺旋插补子程序 `remap_subs/helix_ac.ngc` 使用:
|
||||||
|
|
||||||
|
```gcode
|
||||||
|
M428 ;TCP:xyzac
|
||||||
|
```
|
||||||
|
|
||||||
|
`M428` 通过 remap 将 `motion.switchkins-type` 切换到 XYZAC TCP/RTCP 运动学;`helix_ac.ngc` 在需要机床坐标定位时使用 `M429` 切回 identity,再用 `M428` 进入 TCP。配置的 `M430` 进入 userk。AXIS 右侧的 `TCP:XYZAC` 按钮也提供同一切换入口。
|
||||||
|
|
||||||
|
## XYZBC 变体
|
||||||
|
|
||||||
|
快捷方式接受可选配置名:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run-5axis-rtcp.sh xyzbc-trt.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
该变体使用 `XYZBC` 和 `xyzbc-trt-kins`,默认程序为 `demos/xyzbc_switchkins.ngc`。
|
||||||
|
|
||||||
|
## 启动证据
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
实测启动结果:
|
||||||
|
|
||||||
|
- AXIS 标题:`xyzac_switchkins.ngc - AXIS 2.10.0~pre1 on sim-xyzac-trt-kins (switchkins)`。
|
||||||
|
- 右侧 `SWITCHKINS` 面板显示 `TCP:XYZAC`。
|
||||||
|
- Vismach 预览显示五轴机床和多轴刀具轨迹。
|
||||||
|
- LinuxCNC 状态接口返回 `file=./demos/xyzac_switchkins.ngc`,`COORDINATES=XYZAC`。
|
||||||
|
- 当前环境使用 POSIX non-realtime 回退;这验证的是仿真配置和 RTCP 路径,不是实时硬件性能。
|
||||||
|
|
||||||
|
## 来源依据
|
||||||
|
|
||||||
|
- `linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini`
|
||||||
|
- `linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins.ngc`
|
||||||
|
- `linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/xyzac_switchkins_sub.ngc`
|
||||||
|
- `linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/helix_ac.ngc`
|
||||||
|
- `linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/428remap.ngc`
|
||||||
|
- `linuxcnc/src/emc/kinematics/xyzac-trt-kins.c`
|
||||||
|
- `linuxcnc/src/emc/kinematics/trtfuncs.c`
|
||||||
|
- `linuxcnc/docs/src/motion/5-axis-kinematics.adoc`
|
||||||
29
doc-g/README.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# GMOCCAPY 调查资料
|
||||||
|
|
||||||
|
本目录保存本次 GMOCCAPY 实机仿真检查的截图、文件清单、源码分析和加载测试证据。后续与本案例有关的分析文档继续写入 `./doc-g`,不把临时调查结论散落到项目根目录。
|
||||||
|
|
||||||
|
## 入口
|
||||||
|
|
||||||
|
- [五轴 RTCP 文档集](../doc-rtcp/README.md)
|
||||||
|
- [HTML 图文使用说明](generate.html)
|
||||||
|
- [五轴 RTCP 快捷方式](5axis-rtcp-shortcut.md)
|
||||||
|
- [控制面板数据流与控制流](gmoccapy-control-panel-data-control-flow.md)
|
||||||
|
- [GMOCCAPY 分析报告](gmoccapy-analysis.md)
|
||||||
|
- [逐文件清单](gmoccapy-file-inventory.md)
|
||||||
|
- [机器可读清单](gmoccapy-file-inventory.json)
|
||||||
|
|
||||||
|
## 截图
|
||||||
|
|
||||||
|
- `gmoccapy-important-changes.png`:用户提供的首次启动提示。
|
||||||
|
- `gmoccapy-important-changes-v2.png`:本轮新增的用户截图证据,已与系统 GMOCCAPY 3.5.2 源码逐项对照。
|
||||||
|
- `gmoccapy-before-load.png`:主界面尚未加载程序。
|
||||||
|
- `gmoccapy-after-popup-close.png`:关闭提示后的主界面。
|
||||||
|
- `gmoccapy-load-file-page.png`:实际打开的程序文件选择页,能看到 `.ngc` 文件。
|
||||||
|
- `gmoccapy-program-loaded.png`:通过 LinuxCNC API 加载 `3dtest.ngc` 后的路径和刀具轨迹。
|
||||||
|
|
||||||
|
## 可复现命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run-gmoccapy.sh
|
||||||
|
python3 doc-g/generate_inventory.py
|
||||||
|
```
|
||||||
264
doc-g/generate.html
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>GMOCCAPY 仿真使用说明</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--ink: #1e293b;
|
||||||
|
--muted: #64748b;
|
||||||
|
--line: #dbe3ec;
|
||||||
|
--paper: #ffffff;
|
||||||
|
--wash: #f4f7fb;
|
||||||
|
--blue: #155e9a;
|
||||||
|
--blue-soft: #e8f2fb;
|
||||||
|
--red: #d62828;
|
||||||
|
--green: #18864b;
|
||||||
|
--shadow: 0 12px 28px rgba(15, 23, 42, .08);
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
html { scroll-behavior: smooth; }
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--ink);
|
||||||
|
background: var(--wash);
|
||||||
|
font: 16px/1.65 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||||
|
}
|
||||||
|
a { color: var(--blue); }
|
||||||
|
.hero {
|
||||||
|
padding: 52px max(24px, calc((100vw - 1180px) / 2));
|
||||||
|
color: #fff;
|
||||||
|
background: #102a43;
|
||||||
|
border-bottom: 5px solid #d62828;
|
||||||
|
}
|
||||||
|
.hero h1 { max-width: 760px; margin: 0; font-size: clamp(2rem, 4vw, 3.5rem); line-height: 1.12; }
|
||||||
|
.hero p { max-width: 720px; margin: 18px 0 0; color: #d8e6f2; font-size: 1.08rem; }
|
||||||
|
.layout { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: 28px; max-width: 1180px; margin: 30px auto; padding: 0 24px 60px; }
|
||||||
|
.toc { position: sticky; top: 20px; align-self: start; padding: 18px; background: var(--paper); border: 1px solid var(--line); border-radius: 8px; box-shadow: var(--shadow); }
|
||||||
|
.toc strong { display: block; margin-bottom: 10px; }
|
||||||
|
.toc a { display: block; padding: 5px 0; text-decoration: none; font-size: .94rem; }
|
||||||
|
main { min-width: 0; }
|
||||||
|
section { margin-bottom: 32px; padding: 28px; background: var(--paper); border: 1px solid var(--line); border-radius: 8px; box-shadow: var(--shadow); }
|
||||||
|
h2 { margin: 0 0 14px; color: #123b5d; font-size: 1.7rem; line-height: 1.2; }
|
||||||
|
h3 { margin: 24px 0 8px; color: #234e70; font-size: 1.16rem; }
|
||||||
|
p { margin: 10px 0; }
|
||||||
|
.notice { padding: 16px 18px; border-left: 5px solid var(--red); background: #fff5f5; }
|
||||||
|
.success { padding: 16px 18px; border-left: 5px solid var(--green); background: #effaf3; }
|
||||||
|
.steps { display: grid; gap: 12px; margin: 18px 0 0; padding: 0; list-style: none; counter-reset: step; }
|
||||||
|
.steps li { position: relative; padding: 14px 18px 14px 58px; border: 1px solid var(--line); border-radius: 6px; background: #fbfdff; counter-increment: step; }
|
||||||
|
.steps li::before { content: counter(step); position: absolute; left: 17px; top: 13px; display: grid; place-items: center; width: 28px; height: 28px; color: #fff; background: var(--blue); border-radius: 50%; font-weight: 700; }
|
||||||
|
code { padding: 2px 5px; background: #eef2f7; border-radius: 4px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .92em; }
|
||||||
|
pre { overflow: auto; padding: 14px 16px; color: #e5edf5; background: #172b3d; border-radius: 6px; }
|
||||||
|
.figure { margin: 22px 0 4px; }
|
||||||
|
.annotated { position: relative; overflow: hidden; border: 1px solid #bbc8d5; background: #111; border-radius: 6px; }
|
||||||
|
.annotated img { display: block; width: 100%; height: auto; }
|
||||||
|
.annotated svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
|
||||||
|
.annotated .arrow { stroke: var(--red); stroke-width: 8; fill: none; stroke-linecap: round; stroke-linejoin: round; }
|
||||||
|
.annotated .arrow-thin { stroke-width: 5; }
|
||||||
|
.annotated .label { fill: #fff; stroke: var(--red); stroke-width: 5; paint-order: stroke; font: 700 30px system-ui, sans-serif; }
|
||||||
|
figcaption { margin-top: 8px; color: var(--muted); font-size: .92rem; }
|
||||||
|
.two-col { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
|
||||||
|
.fact { padding: 16px; border: 1px solid var(--line); border-radius: 6px; background: #fbfdff; }
|
||||||
|
.fact strong { display: block; margin-bottom: 5px; color: #123b5d; }
|
||||||
|
.button-chain { display: grid; gap: 8px; max-width: 760px; margin: 20px 0; }
|
||||||
|
.chain-item { display: grid; grid-template-columns: 34px 1fr; gap: 12px; align-items: center; padding: 12px 14px; border: 2px solid #c8d8e8; border-radius: 6px; background: #f8fbfe; }
|
||||||
|
.chain-item .badge { display: grid; place-items: center; width: 30px; height: 30px; color: #fff; background: var(--red); border-radius: 50%; font-weight: 800; }
|
||||||
|
.chain-item small { display: block; color: var(--muted); }
|
||||||
|
.chain-arrow { color: var(--red); font-size: 1.8rem; font-weight: 800; line-height: .8; text-align: center; }
|
||||||
|
.auto-panel { position: relative; max-width: 760px; margin: 20px 0; padding: 18px; overflow: hidden; border: 2px solid #9fb4c8; border-radius: 6px; background: #e9eef3; }
|
||||||
|
.auto-panel-title { margin-bottom: 12px; color: #123b5d; font-weight: 800; }
|
||||||
|
.auto-buttons { display: grid; grid-template-columns: repeat(6, minmax(82px, 1fr)); gap: 9px; align-items: stretch; }
|
||||||
|
.auto-btn { min-height: 104px; display: grid; place-items: center; align-content: center; gap: 5px; padding: 9px 5px; border: 1px solid #b7c4cf; border-radius: 6px; background: #fff; text-align: center; }
|
||||||
|
.auto-btn img { width: 38px; height: 38px; object-fit: contain; }
|
||||||
|
.auto-btn.target { border: 4px solid var(--red); background: #fff1f1; }
|
||||||
|
.auto-btn code { padding: 1px 3px; font-size: .72rem; }
|
||||||
|
.auto-panel svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
|
||||||
|
.auto-panel .arrow { stroke: var(--red); stroke-width: 6; fill: none; stroke-linecap: round; stroke-linejoin: round; }
|
||||||
|
.auto-panel .label { fill: #fff; stroke: var(--red); stroke-width: 4; paint-order: stroke; font: 800 22px system-ui, sans-serif; }
|
||||||
|
@media (max-width: 650px) { .auto-buttons { grid-template-columns: repeat(3, minmax(82px, 1fr)); } }
|
||||||
|
table { width: 100%; border-collapse: collapse; font-size: .94rem; }
|
||||||
|
th, td { padding: 9px 10px; border-bottom: 1px solid var(--line); text-align: left; vertical-align: top; }
|
||||||
|
th { color: #123b5d; background: #f0f5fa; }
|
||||||
|
.footer { max-width: 1180px; margin: 0 auto; padding: 0 24px 44px; color: var(--muted); font-size: .9rem; }
|
||||||
|
@media (max-width: 820px) {
|
||||||
|
.layout { display: block; margin-top: 18px; }
|
||||||
|
.toc { position: static; margin-bottom: 18px; }
|
||||||
|
section { padding: 20px; }
|
||||||
|
.two-col { grid-template-columns: 1fr; }
|
||||||
|
.hero { padding: 38px 24px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="hero">
|
||||||
|
<h1>GMOCCAPY 仿真使用说明</h1>
|
||||||
|
<p>从首次启动提示、进入 AUTO、选择 G-code,到确认程序已加载。红色箭头表示需要点击的位置;本手册基于当前工作区的实际 GMOCCAPY 截图和源码分析。</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="layout">
|
||||||
|
<nav class="toc" aria-label="目录">
|
||||||
|
<strong>本页导航</strong>
|
||||||
|
<a href="#quick">快速流程</a>
|
||||||
|
<a href="#popup">关闭首次提示</a>
|
||||||
|
<a href="#auto">进入 AUTO</a>
|
||||||
|
<a href="#select">选择程序</a>
|
||||||
|
<a href="#verify">确认加载</a>
|
||||||
|
<a href="#trouble">常见问题</a>
|
||||||
|
<a href="#evidence">文件与证据</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section id="quick">
|
||||||
|
<h2>快速流程</h2>
|
||||||
|
<ol class="steps">
|
||||||
|
<li>启动项目快捷方式:<code>./run-gmoccapy.sh</code>。</li>
|
||||||
|
<li>首次弹窗出现时,点击右侧 <strong>X</strong>;可选中“Don't show this again”。</li>
|
||||||
|
<li>确认机器上电并完成 homing,然后点击右侧第三个模式按钮进入 AUTO。</li>
|
||||||
|
<li>进入 AUTO 后,在 AUTO 按钮组中点击“打开文件”图标,选择 <code>.ngc</code> 文件。</li>
|
||||||
|
<li>点击文件选择页底部的绿色对勾确认,检查顶部文件路径和刀具轨迹。</li>
|
||||||
|
<li>加载不会自动运行;确认安全后再点击 Run,或使用 <code>R</code> 快捷键。</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="popup">
|
||||||
|
<h2>1. 关闭首次启动提示</h2>
|
||||||
|
<div class="notice"><strong>先处理这个窗口。</strong> 用户提供的“Important change(s)”是模态提示,会挡住 GMOCCAPY 主界面;在它关闭前,底层 AUTO 和加载按钮都无法正常点击。</div>
|
||||||
|
<figure class="figure">
|
||||||
|
<div class="annotated">
|
||||||
|
<img src="gmoccapy-important-changes.png" alt="GMOCCAPY Important changes 首次启动提示">
|
||||||
|
<svg viewBox="0 0 697 837" aria-hidden="true">
|
||||||
|
<defs><marker id="arrow-popup" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto"><path d="M0,0 L12,6 L0,12 z" fill="#d62828"/></marker></defs>
|
||||||
|
<path class="arrow" marker-end="url(#arrow-popup)" d="M420 260 C505 285 574 340 637 399"/>
|
||||||
|
<text class="label" x="385" y="240">1 关闭提示</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<figcaption>箭头指向右侧 X。也可以勾选底部“Don't show this again”,避免每次启动重复出现。</figcaption>
|
||||||
|
</figure>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="auto">
|
||||||
|
<h2>2. 进入 AUTO 模式</h2>
|
||||||
|
<p><strong>关键点:</strong>你要找的“加载程序”按钮不在当前 MANUAL/JOG 页面。它只有在切换到 AUTO 后,才会被放入 AUTO 专用按钮组。</p>
|
||||||
|
<figure class="figure">
|
||||||
|
<div class="annotated">
|
||||||
|
<img src="gmoccapy-after-popup-close.png" alt="关闭提示后的 GMOCCAPY 主界面">
|
||||||
|
<svg viewBox="0 0 1960 1500" aria-hidden="true">
|
||||||
|
<defs><marker id="arrow-auto" markerWidth="18" markerHeight="18" refX="15" refY="9" orient="auto"><path d="M0,0 L18,9 L0,18 z" fill="#d62828"/></marker></defs>
|
||||||
|
<path class="arrow" marker-end="url(#arrow-auto)" d="M1415 235 C1530 270 1640 330 1710 390"/>
|
||||||
|
<text class="label" x="1210" y="220">2 点击 AUTO</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<figcaption>箭头指向右侧竖向模式按钮中的 AUTO 图标。若该按钮灰色,先处理“急停、上电、homing”状态。</figcaption>
|
||||||
|
</figure>
|
||||||
|
<h3>具体点击顺序</h3>
|
||||||
|
<ol class="steps">
|
||||||
|
<li><strong>先看右侧竖条。</strong>从上到下通常是急停、上电、AUTO、MDI 等图标。点击第三个“彩色小方块/机床箭头”图标,不要点击 MDI 计算器图标。</li>
|
||||||
|
<li><strong>等待页面切换。</strong>右侧或底部按钮区域会从手动点动按钮切换为 AUTO 按钮组。窗口大小、主题和侧面板会改变图标的排列,但按钮的 tooltip 仍是 <code>Load a new program</code>。</li>
|
||||||
|
<li><strong>再点打开文件按钮。</strong>在 AUTO 按钮组中寻找文件夹/打开文件图标。它的源码 ID 是 <code>btn_load</code>,不是文件选择页里的绿色对勾。</li>
|
||||||
|
<li><strong>看到文件网格才算进入选择界面。</strong>点击 <code>btn_load</code> 后,GMOCCAPY 才会打开“选择 G-code 程序”界面;源码函数是 <code>on_btn_load_clicked()</code>,它把按钮页切换到文件选择页。</li>
|
||||||
|
</ol>
|
||||||
|
<div class="button-chain" aria-label="打开 G-code 文件界面的按钮链">
|
||||||
|
<div class="chain-item"><span class="badge">A</span><div><strong>右侧第三个 AUTO 图标</strong><small><code>rbt_auto</code>:先切换按钮组。</small></div></div>
|
||||||
|
<div class="chain-arrow" aria-hidden="true">↓</div>
|
||||||
|
<div class="chain-item"><span class="badge">B</span><div><strong>AUTO 页面中的打开文件图标</strong><small><code>btn_load</code>,tooltip 为 <code>Load a new program</code>;点击它才打开文件选择界面。</small></div></div>
|
||||||
|
<div class="chain-arrow" aria-hidden="true">↓</div>
|
||||||
|
<div class="chain-item"><span class="badge">C</span><div><strong>文件网格和底部绿色对勾</strong><small><code>btn_select</code>:选择文件后确认返回路径。</small></div></div>
|
||||||
|
</div>
|
||||||
|
<h3>在 AUTO 页面里,具体就是这个打开文件图标</h3>
|
||||||
|
<p>下图不是另一个入口,而是从 GMOCCAPY Glade 的 <code>hbtb_auto</code> 按钮组和当前 icon theme 放大的控件图。红框和箭头明确标出 <code>btn_load</code>;鼠标悬停时 tooltip 是 <code>Load a new program</code>。</p>
|
||||||
|
<div class="auto-panel" role="img" aria-label="AUTO 页面按钮组中 btn_load 打开文件图标的源码对应图">
|
||||||
|
<div class="auto-panel-title">AUTO 页面按钮组:hbtb_auto</div>
|
||||||
|
<div class="auto-buttons">
|
||||||
|
<div class="auto-btn target"><img src="gmoccapy-open-file-icon.png" alt="打开文件图标"><strong>打开文件</strong><code>btn_load</code></div>
|
||||||
|
<div class="auto-btn"><span style="font-size: 2rem">↻</span><strong>重载</strong><code>btn_reload</code></div>
|
||||||
|
<div class="auto-btn"><span style="font-size: 2rem">▶</span><strong>运行</strong><code>btn_run</code></div>
|
||||||
|
<div class="auto-btn"><span style="font-size: 2rem">■</span><strong>停止</strong><code>btn_stop</code></div>
|
||||||
|
<div class="auto-btn"><span style="font-size: 2rem">Ⅱ</span><strong>暂停</strong><code>tbtn_pause</code></div>
|
||||||
|
<div class="auto-btn"><span style="font-size: 2rem">→</span><strong>单步</strong><code>btn_step</code></div>
|
||||||
|
</div>
|
||||||
|
<svg viewBox="0 0 760 178" aria-hidden="true">
|
||||||
|
<defs><marker id="arrow-btn-load" markerWidth="16" markerHeight="16" refX="13" refY="8" orient="auto"><path d="M0,0 L16,8 L0,16 z" fill="#d62828"/></marker></defs>
|
||||||
|
<path class="arrow" marker-end="url(#arrow-btn-load)" d="M305 28 C245 38 156 50 67 91"/>
|
||||||
|
<text class="label" x="320" y="30">点击这里:btn_load</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="notice"><strong>如果你只看到右侧 AUTO 图标而没有这排按钮:</strong>你还没有完成 AUTO 页面切换,或者 AUTO 被急停/未上电/未 homing 状态门控。先点击上面的 AUTO 入口,等按钮组切换完成,再寻找红框位置。</div>
|
||||||
|
<div class="notice"><strong>不要把两个按钮混淆:</strong><code>btn_load</code> 负责“打开文件选择界面”;文件界面底部的绿色对勾 <code>btn_select</code> 负责“确认当前选中的文件”。</div>
|
||||||
|
<div class="success">当前仿真配置的共享 home switch 曾报告 <code>Cannot home while shared home switch is closed j=2</code>。这会让 AUTO 保持门控;它是仿真 HAL 状态问题,不是加载按钮消失。</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="select">
|
||||||
|
<h2>3. 选择 G-code 程序</h2>
|
||||||
|
<p>只有完成上一节的 <strong>AUTO → 打开文件图标</strong> 两次点击后,才会出现下面这个文件界面。进入文件页后,GMOCCAPY 会按配置中的 <code>PROGRAM_PREFIX = ../../nc_files/</code> 显示程序。当前配置解析到:</p>
|
||||||
|
<pre>/home/mes006/linuxcncWebWASM/linuxcnc/nc_files/</pre>
|
||||||
|
<figure class="figure">
|
||||||
|
<div class="annotated">
|
||||||
|
<img src="gmoccapy-load-file-page.png" alt="GMOCCAPY 文件选择页">
|
||||||
|
<svg viewBox="0 0 1960 1500" aria-hidden="true">
|
||||||
|
<defs><marker id="arrow-file" markerWidth="18" markerHeight="18" refX="15" refY="9" orient="auto"><path d="M0,0 L18,9 L0,18 z" fill="#d62828"/></marker></defs>
|
||||||
|
<path class="arrow arrow-thin" marker-end="url(#arrow-file)" d="M1530 570 C1490 520 1440 475 1355 420"/>
|
||||||
|
<text class="label" x="1500" y="640">3 选择 .ngc</text>
|
||||||
|
<path class="arrow arrow-thin" marker-end="url(#arrow-file)" d="M1290 1260 C1390 1280 1480 1320 1530 1370"/>
|
||||||
|
<text class="label" x="1060" y="1240">4 确认选择</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<figcaption>示例中可以看到 <code>3dtest.ngc</code>。底部右侧绿色对勾用于返回选中的路径;黄色弯箭头用于返回上一页。文件网格不会在 MANUAL/JOG 页面直接出现。</figcaption>
|
||||||
|
</figure>
|
||||||
|
<h3>界面出现后的点击顺序</h3>
|
||||||
|
<ol class="steps">
|
||||||
|
<li>在文件网格中单击目标文件,例如 <code>3dtest.ngc</code>;选中项会出现高亮。</li>
|
||||||
|
<li>点击底部右侧绿色对勾(<code>btn_select</code>)。它会返回文件路径并关闭文件选择页。</li>
|
||||||
|
<li>回到主界面后,顶部标题应显示完整路径,预览区应显示刀具轨迹。</li>
|
||||||
|
</ol>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="verify">
|
||||||
|
<h2>4. 确认程序已经加载</h2>
|
||||||
|
<p>加载成功后,顶部标题不再是 <code>No Program loaded</code>,而会显示完整文件路径;左侧预览区会出现刀具轨迹。加载本身不会启动运动。</p>
|
||||||
|
<figure class="figure">
|
||||||
|
<div class="annotated">
|
||||||
|
<img src="gmoccapy-program-loaded.png" alt="3dtest.ngc 已加载并显示刀具轨迹">
|
||||||
|
<svg viewBox="0 0 1960 1500" aria-hidden="true">
|
||||||
|
<defs><marker id="arrow-verify" markerWidth="18" markerHeight="18" refX="15" refY="9" orient="auto"><path d="M0,0 L18,9 L0,18 z" fill="#d62828"/></marker></defs>
|
||||||
|
<path class="arrow arrow-thin" marker-end="url(#arrow-verify)" d="M1310 95 C1120 80 930 55 730 38"/>
|
||||||
|
<text class="label" x="1320" y="140">5 已加载路径</text>
|
||||||
|
<path class="arrow arrow-thin" marker-end="url(#arrow-verify)" d="M665 850 C545 760 420 650 280 560"/>
|
||||||
|
<text class="label" x="680" y="900">6 刀具轨迹预览</text>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<figcaption>本次测试加载的文件是 <code>3dtest.ngc</code>;LinuxCNC 状态接口返回该路径,解释器保持空闲。</figcaption>
|
||||||
|
</figure>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="trouble">
|
||||||
|
<h2>常见问题</h2>
|
||||||
|
<div class="two-col">
|
||||||
|
<div class="fact"><strong>看不到加载按钮</strong>按“关闭首次提示 → 右侧第三个 AUTO 图标 → AUTO 按钮组中的打开文件图标”顺序操作。文件网格本身不是入口,它是点击 <code>btn_load</code> 之后才出现的。</div>
|
||||||
|
<div class="fact"><strong>AUTO 图标是灰色</strong>检查急停复位、机器上电和 homing。GMOCCAPY 会在状态不满足时禁用模式切换。</div>
|
||||||
|
<div class="fact"><strong>加载后没有运动</strong>这是正常的。加载只生成程序和预览;要执行必须点击 Run 或按 <code>R</code>。</div>
|
||||||
|
<div class="fact"><strong>启动日志提示 POSIX non-realtime</strong>当前环境没有实时内核。可用于仿真界面和加载检查,但不能作为实时硬件性能证明。</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="evidence">
|
||||||
|
<h2>源码、配置与证据</h2>
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>项目</th><th>位置</th><th>用途</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td>AUTO 入口</td><td><code>rbt_auto</code> / <code>on_rbt_auto_pressed</code></td><td>右侧第三个模式按钮;先进入 AUTO,加载按钮才可见。</td></tr>
|
||||||
|
<tr><td>AUTO 按钮组</td><td><code>hbtb_auto</code></td><td>GMOCCAPY Glade 中承载打开、重载、运行、停止、暂停和单步按钮。</td></tr>
|
||||||
|
<tr><td>加载按钮</td><td><code>gmoccapy.py:5046</code>、<code>btn_load</code></td><td>切换到文件选择页。</td></tr>
|
||||||
|
<tr><td>文件确认</td><td><code>gmoccapy.py:5116</code>、<code>btn_select</code></td><td>将选中的文件交给 <code>hal_action_open.load_file(path)</code>。</td></tr>
|
||||||
|
<tr><td>程序目录</td><td><code>configs/sim/gmoccapy/gmoccapy.ini</code></td><td><code>PROGRAM_PREFIX = ../../nc_files/</code>。</td></tr>
|
||||||
|
<tr><td>逐文件分析</td><td><a href="gmoccapy-file-inventory.md">gmoccapy-file-inventory.md</a></td><td>1,233 个配置、实现、资源、图标、翻译和文档文件的哈希清单。</td></tr>
|
||||||
|
<tr><td>完整分析</td><td><a href="gmoccapy-analysis.md">gmoccapy-analysis.md</a></td><td>本次提示、界面、加载测试和限制的详细记录。</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p>本页是静态 HTML,不依赖网络。复制整个 <code>doc-g</code> 目录后,直接用浏览器打开本文件即可。</p>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
<div class="footer">GMOCCAPY 仿真使用说明 · 证据和后续文档统一保存在 <code>./doc-g</code></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
118
doc-g/generate_inventory.py
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Build a deterministic inventory for the GMOCCAPY source and case assets."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
OUT = Path(__file__).resolve().parent
|
||||||
|
|
||||||
|
|
||||||
|
def sha256(path: Path) -> str:
|
||||||
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def classify(rel: str) -> str:
|
||||||
|
path = Path(rel)
|
||||||
|
if "/configs/sim/gmoccapy/" in rel:
|
||||||
|
if path.suffix == ".ini":
|
||||||
|
return "simulation_ini"
|
||||||
|
if path.suffix in {".hal", ".tbl", ".para", ".var", ".pref"}:
|
||||||
|
return "simulation_runtime_data"
|
||||||
|
if path.suffix in {".ngc", ".py", ".ui", ".glade", ".svg", ".txt", ".README"}:
|
||||||
|
return "simulation_resource"
|
||||||
|
return "simulation_asset"
|
||||||
|
if "/src/emc/usr_intf/gmoccapy/" in rel or "/lib/python/gmoccapy/" in rel:
|
||||||
|
return "implementation"
|
||||||
|
if "/share/gmoccapy/" in rel:
|
||||||
|
if "icons/" in rel:
|
||||||
|
return "icon_asset"
|
||||||
|
return "installed_ui_asset"
|
||||||
|
if "/src/po/gmoccapy/" in rel:
|
||||||
|
return "translation"
|
||||||
|
if "/docs/src/gui/images/gmoccapy_" in rel or "/docs/src/user/images/gmoccapy_" in rel:
|
||||||
|
return "documentation_asset"
|
||||||
|
if "/docs/src/gui/gmoccapy" in rel or "/docs/man/man1/gmoccapy" in rel:
|
||||||
|
return "documentation"
|
||||||
|
return "cross_reference"
|
||||||
|
|
||||||
|
|
||||||
|
def collect(paths: list[Path]) -> list[dict[str, object]]:
|
||||||
|
files: list[Path] = []
|
||||||
|
for base in paths:
|
||||||
|
if base.is_file():
|
||||||
|
files.append(base)
|
||||||
|
elif base.is_dir():
|
||||||
|
files.extend(p for p in base.rglob("*") if p.is_file())
|
||||||
|
rows = []
|
||||||
|
for path in sorted(set(files)):
|
||||||
|
rel = path.relative_to(ROOT).as_posix()
|
||||||
|
rows.append(
|
||||||
|
{
|
||||||
|
"path": rel,
|
||||||
|
"role": classify(rel),
|
||||||
|
"bytes": path.stat().st_size,
|
||||||
|
"sha256": sha256(path),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
rows = collect(
|
||||||
|
[
|
||||||
|
ROOT / "linuxcnc/configs/sim/gmoccapy",
|
||||||
|
ROOT / "linuxcnc/src/emc/usr_intf/gmoccapy",
|
||||||
|
ROOT / "linuxcnc/lib/python/gmoccapy",
|
||||||
|
ROOT / "linuxcnc/share/gmoccapy",
|
||||||
|
ROOT / "linuxcnc/src/po/gmoccapy",
|
||||||
|
ROOT / "linuxcnc/docs/src/gui/gmoccapy.adoc",
|
||||||
|
ROOT / "linuxcnc/docs/man/man1/gmoccapy.1",
|
||||||
|
*sorted((ROOT / "linuxcnc/docs/src/gui/images").glob("gmoccapy_*")),
|
||||||
|
*sorted((ROOT / "linuxcnc/docs/src/user/images").glob("gmoccapy_*")),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
summary: dict[str, int] = {}
|
||||||
|
for row in rows:
|
||||||
|
role = str(row["role"])
|
||||||
|
summary[role] = summary.get(role, 0) + 1
|
||||||
|
payload = {
|
||||||
|
"schema": "gmoccapy-file-inventory.v1",
|
||||||
|
"scope": [
|
||||||
|
"linuxcnc/configs/sim/gmoccapy/**",
|
||||||
|
"linuxcnc/src/emc/usr_intf/gmoccapy/**",
|
||||||
|
"linuxcnc/lib/python/gmoccapy/**",
|
||||||
|
"linuxcnc/share/gmoccapy/**",
|
||||||
|
"linuxcnc/src/po/gmoccapy/**",
|
||||||
|
"linuxcnc/docs/src/gui/gmoccapy.adoc",
|
||||||
|
"linuxcnc/docs/man/man1/gmoccapy.1",
|
||||||
|
"linuxcnc/docs/src/gui/images/gmoccapy_*",
|
||||||
|
"linuxcnc/docs/src/user/images/gmoccapy_*",
|
||||||
|
],
|
||||||
|
"file_count": len(rows),
|
||||||
|
"role_counts": dict(sorted(summary.items())),
|
||||||
|
"files": rows,
|
||||||
|
}
|
||||||
|
(OUT / "gmoccapy-file-inventory.json").write_text(
|
||||||
|
json.dumps(payload, ensure_ascii=False, sort_keys=True, indent=2) + "\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
lines = [
|
||||||
|
"# GMOCCAPY 文件清单",
|
||||||
|
"",
|
||||||
|
f"清单范围内共 `{len(rows)}` 个文件;每项包含相对路径、角色、字节数和 SHA-256。",
|
||||||
|
"",
|
||||||
|
"| 角色 | 数量 |",
|
||||||
|
"|---|---:|",
|
||||||
|
]
|
||||||
|
lines.extend(f"| `{role}` | {count} |" for role, count in sorted(summary.items()))
|
||||||
|
lines.extend(["", "完整逐文件数据见 [`gmoccapy-file-inventory.json`](gmoccapy-file-inventory.json)。", ""])
|
||||||
|
(OUT / "gmoccapy-file-inventory.md").write_text("\n".join(lines), encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
BIN
doc-g/gmoccapy-3dtest-loaded-through-ui.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
doc-g/gmoccapy-after-popup-close.png
Normal file
|
After Width: | Height: | Size: 294 KiB |
170
doc-g/gmoccapy-analysis.md
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
# GMOCCAPY 分析与加载测试
|
||||||
|
|
||||||
|
更新时间:2026-08-25
|
||||||
|
|
||||||
|
## 结论
|
||||||
|
|
||||||
|
“没有看到加载程序按钮”不是按钮缺失,而是首次启动提示和 GMOCCAPY 的模式门控共同造成的视觉结果:
|
||||||
|
|
||||||
|
1. 首次启动会弹出模态的 `Important change(s)` 窗口。该窗口覆盖主界面并阻止底层点击;用户截图中的右侧 `X` 是关闭按钮,底部复选框是 `Don't show this again`。
|
||||||
|
2. 加载按钮只属于 AUTO 页面按钮组,不属于 MANUAL/JOG 页面。源码将它声明为 `btn_load`,提示为 `Load a new program`,图标为 `img_open`。
|
||||||
|
3. AUTO 模式还受机器状态和 homing 门控。`gmoccapy.py` 在未满足状态时会禁用 `rbt_auto`,并提示 `It is not possible to change to Auto Mode at the moment`。
|
||||||
|
4. 关闭首次提示、进入 AUTO 后,点击文件夹/打开文件图标会进入文件选择页。当前 `gmoccapy.ini` 的 `PROGRAM_PREFIX = ../../nc_files/` 解析到 `linuxcnc/nc_files`。
|
||||||
|
|
||||||
|
## 用户截图分析
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
用户提供的图片尺寸为 `697 x 837`,内容是 GMOCCAPY 3.5.x 的版本变更提示:
|
||||||
|
|
||||||
|
- LinuxCNC 2.10/GMOCCAPY 3.5.0 不再在换刀后自动保留 G43;需要通过 REMAP 实现自动重新激活。
|
||||||
|
- 3.5.1 的工具表和偏置页默认使用计算器输入,可在设置或按钮中改变。
|
||||||
|
- MDI 和 G-code 编辑模式新增调用计算器的按钮。
|
||||||
|
- `release notes` 是橙色链接;右下方复选框用于永久隐藏该提示。
|
||||||
|
|
||||||
|
这张提示不是文件选择器,也不是加载失败信息。应先关闭它,再操作主界面。
|
||||||
|
|
||||||
|
### 新截图的源码逐项对照
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
这张截图与前一张内容一致,尺寸为 `697 x 837`,SHA-256 为 `df7322c30bcb6af131591a04b11cad24e7b6d186beaff83059fcfd8ffbae2d1b`。截图中的每一块文字都能在当前系统安装的 `/usr/bin/gmoccapy` 中找到:
|
||||||
|
|
||||||
|
| 截图区域 | 源码证据 | 含义 |
|
||||||
|
|---|---|---|
|
||||||
|
| `Important change(s)` 标题 | `/usr/bin/gmoccapy:557-560` | `_startup_message()` 建立启动提示标题;注释要求新增消息只能追加,不能删除旧消息,否则隐藏索引会错位。 |
|
||||||
|
| 3.5.0 G43 提示 | `/usr/bin/gmoccapy:561-563` | GMOCCAPY 3.5.0 不再承诺换刀后自动保留 G43;想自动恢复必须使用 REMAP,示例在 GMOCCAPY 仿真配置中。 |
|
||||||
|
| 3.5.1 计算器提示 | `/usr/bin/gmoccapy:564-565` | 工具表和偏置页默认启用计算器输入;设置或页面按钮可以关闭;MDI 和 G-code 编辑页也增加了计算器按钮。 |
|
||||||
|
| `release notes` 链接 | `/usr/bin/gmoccapy:570-573` | 以 Pango markup 拼接 LinuxCNC 官方 release notes URL,并通过通知组件显示。点击它会打开文档,不会关闭提示。 |
|
||||||
|
| `Don't show this again` | `notification.py:151-155` | `show_checkbox=True` 时创建 GTK `CheckButton`;它不是普通说明文字,而是真正影响后续启动行为的复选框。 |
|
||||||
|
| 右侧 `X` | `notification.py:156-164` | 创建名为 `notification_close` 的 48x48 关闭按钮;点击后删除该消息,并把复选框状态发回 GMOCCAPY。 |
|
||||||
|
|
||||||
|
### 弹窗什么时候出现
|
||||||
|
|
||||||
|
系统运行时在 GUI 初始化和周期任务准备完成后,于 `/usr/bin/gmoccapy:519` 调用 `_startup_message()`。它读取偏好项 `hide_startup_messsage`(注意源码中确实有三个 `s` 的拼写):
|
||||||
|
|
||||||
|
```python
|
||||||
|
hide_message = self.prefs.getpref("hide_startup_messsage", 0, int)
|
||||||
|
if hide_message < len(messages):
|
||||||
|
...
|
||||||
|
self.notification.add_message(message, INFO_ICON, show_checkbox=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
当前消息数组有两项,因此默认值 `0` 会显示两项。关闭时如果复选框被勾选,`/usr/bin/gmoccapy:6241-6242` 将 `self.num`(当前为 `2`)写回 `hide_startup_messsage`。下次启动判断 `2 < 2` 为假,提示不再出现。普通点击 `X` 不勾选复选框时,只删除本次通知,下次仍会显示。
|
||||||
|
|
||||||
|
### 版本边界:截图运行时与仓库锁定源不同
|
||||||
|
|
||||||
|
这是本次分析中最重要的来源边界:
|
||||||
|
|
||||||
|
- 截图对应的实际运行程序是 `/usr/bin/gmoccapy`,其中 `_RELEASE = "3.5.2"`,并包含 `_startup_message()`、3.5.0/3.5.1 文本和隐藏偏好逻辑。
|
||||||
|
- 仓库锁定源是 `linuxcnc/VERSION = 2.10.0~pre0`;`linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py:79` 的版本为 `3.4.3`,该文件没有 `_startup_message()` 实现,也没有截图中的 3.5.0/3.5.1 文本。
|
||||||
|
- 本地启动日志曾显示 `LinuxCNC 2.10.0~pre1` 和 `gmoccapy 3.5.2`,与截图完全一致。因此这张截图是系统安装包的运行时证据,不应反向宣称锁定仓库 2.10.0~pre0/3.4.3 源码已经包含该功能。
|
||||||
|
|
||||||
|
已记录的运行时文件哈希:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/usr/bin/gmoccapy 11e7aee9216d24e4cc41018306f2541845bca9a9d6e16c38580f3ede9cca8d4d
|
||||||
|
/usr/share/gmoccapy/gmoccapy.glade 5fdc1557b02a716d30414fe739614fcd7c1d14cbe2e0a4c202c567a60b460adb
|
||||||
|
/usr/lib/python3/dist-packages/gmoccapy/notification.py
|
||||||
|
e0317fea738745f480c79d1f690bc52e88cc7f81bb4d14fa09c8c7196afb4e2f
|
||||||
|
```
|
||||||
|
|
||||||
|
### 用户应如何处理
|
||||||
|
|
||||||
|
1. 只想本次继续操作:点击右侧 `X`,不要勾选复选框。
|
||||||
|
2. 不想以后重复看到:先勾选 `Don't show this again`,再点击 `X`。
|
||||||
|
3. 想查看变更依据:点击 `release notes`,但这需要浏览器/网络或本地文档可用。
|
||||||
|
4. 使用 G43 的程序:不要仅依赖界面自动行为;在 AUTO 程序中按机床配置和 REMAP 约定明确处理刀具长度补偿,并在正式加工前验证工具表和偏置。
|
||||||
|
|
||||||
|
## 实际界面证据
|
||||||
|
|
||||||
|
### 未加载状态
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
主界面顶部显示 `No Program loaded`。右侧竖向按钮中第三个为 AUTO 模式图标;在 MANUAL 页面时,AUTO 页面里的 `Load a new program` 不会显示在当前按钮组。
|
||||||
|
|
||||||
|
### 文件选择页
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
这张截图是实际运行 GMOCCAPY 后进入的文件选择页。页面显示 `gcmc_lib`、`macros`、`ngcgui_lib` 等目录以及 `3D_Chips.ngc`、`3dtest.ngc` 等程序。底部右侧绿色对勾是确认选择,右下黄色回退箭头是返回。
|
||||||
|
|
||||||
|
进入这张页面的完整点击链是:
|
||||||
|
|
||||||
|
1. 关闭首次 `Important change(s)` 模态提示。
|
||||||
|
2. 在主界面右侧点击第三个模式图标,进入 AUTO;源码控件为 `rbt_auto`。
|
||||||
|
3. 等待 AUTO 按钮组出现,在其中点击打开文件图标;源码控件为 `btn_load`,tooltip 为 `Load a new program`。
|
||||||
|
4. `on_btn_load_clicked()` 将按钮页切换到文件选择页;此时才会看到本截图中的文件网格。
|
||||||
|
5. 选中 `.ngc` 文件后点击底部绿色对勾,源码控件为 `btn_select`。
|
||||||
|
|
||||||
|
因此,文件网格不是主界面的固定按钮,也不是 MDI 入口;在 MANUAL/JOG 页面直接寻找它不会找到。
|
||||||
|
|
||||||
|
### 加载后的程序
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
通过 LinuxCNC 的 `command.program_open()` 加载:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/home/mes006/linuxcncWebWASM/linuxcnc/nc_files/3dtest.ngc
|
||||||
|
```
|
||||||
|
|
||||||
|
加载成功的证据是:
|
||||||
|
|
||||||
|
- 顶部由 `No Program loaded` 变成完整程序路径;
|
||||||
|
- 预览区出现 G-code 刀具轨迹;
|
||||||
|
- `linuxcnc.stat().file` 返回上述路径;
|
||||||
|
- `interp_state` 保持空闲,加载本身不会自动运行程序。
|
||||||
|
|
||||||
|
## 源码与界面映射
|
||||||
|
|
||||||
|
| 层 | 文件/符号 | 结论 |
|
||||||
|
|---|---|---|
|
||||||
|
| 主逻辑 | `linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py` | `on_btn_load_clicked()` 切到文件选择页;`on_IconFileSelection1_selected()` 调用 `hal_action_open.load_file(path)`。 |
|
||||||
|
| 加载按钮 | `btn_load` / `img_open` | tooltip 是 `Load a new program`,只在 AUTO 按钮页 `hbtb_auto`。 |
|
||||||
|
| 运行按钮 | `btn_run` / `img_run` | tooltip 是 `Run the loaded program`;加载后仍需手动运行。 |
|
||||||
|
| 文件确认 | `btn_select` / `img_select` | tooltip 是 `select the highlighted file and return the path`。 |
|
||||||
|
| 返回 | `btn_back_file_load` / `img_back_file_load` | 关闭文件选择页但不返回路径。 |
|
||||||
|
| 文件资源 | `linuxcnc/lib/python/gladevcp/iconview.py` 及 GMOCCAPY 初始化 | 读取 `PROGRAM_PREFIX`,按配置扩展名展示文件和目录。 |
|
||||||
|
| 首次提示 | GMOCCAPY preferences/release-notes 逻辑 | 模态提示优先于主界面,必须先关闭。 |
|
||||||
|
|
||||||
|
## 配置与资源分析
|
||||||
|
|
||||||
|
默认配置为 `linuxcnc/configs/sim/gmoccapy/gmoccapy.ini`:
|
||||||
|
|
||||||
|
- `[DISPLAY] DISPLAY = gmoccapy`:选择 GMOCCAPY GUI。
|
||||||
|
- `[DISPLAY] PROGRAM_PREFIX = ../../nc_files/`:从配置目录解析到 `linuxcnc/nc_files`。
|
||||||
|
- `[RS274NGC] PARAMETER_FILE = sim.var`:仿真参数文件。
|
||||||
|
- `[RS274NGC] SUBROUTINE_PATH = macros`:宏和子程序搜索路径。
|
||||||
|
- `[EMCIO] TOOL_TABLE = tool.tbl`:工具表。
|
||||||
|
- `[HAL]` 加载 `core_sim.hal`、`spindle_sim.hal`、`simulated_home.hal`,并在 GUI 启动后加载 `gmoccapy_postgui.hal`。
|
||||||
|
|
||||||
|
`configs/sim/gmoccapy` 不是单一案例,而是一组配置族,包含标准三轴、四轴、XYZAB/XYZAC/XYZCW、多轴、侧面板、消息、工具传感器、用户页、等离子和车床配置。完整逐文件路径与哈希见 [文件清单](gmoccapy-file-inventory.md)。
|
||||||
|
|
||||||
|
## 运行环境与限制
|
||||||
|
|
||||||
|
- 启动入口:[`../run-gmoccapy.sh`](../run-gmoccapy.sh),默认执行 `gmoccapy.ini`。
|
||||||
|
- 实际 GUI 进程:`/usr/bin/python3 /usr/bin/gmoccapy -ini .../gmoccapy.ini`。
|
||||||
|
- 当前环境没有实时内核,LinuxCNC 日志明确回退到 POSIX non-realtime;这不影响本次“加载和预览”验证,但不等同于实时硬件运行验证。
|
||||||
|
- `simulated_home.hal` 的共享 home switch 在本次手动尝试中报告 `Cannot home while shared home switch is closed j=2`,因此 AUTO 模式按钮可能保持禁用。加载程序本身仍可通过 GUI 文件页/API 验证。
|
||||||
|
- 加载和运行是两个动作:`program_open` 只读入程序并生成预览;需要 `AUTO` 页面中的 Run 按钮或 `R` 快捷键才会执行。
|
||||||
|
|
||||||
|
## 推荐操作顺序
|
||||||
|
|
||||||
|
1. 启动 `./run-gmoccapy.sh`。
|
||||||
|
2. 关闭首次 `Important change(s)` 提示;可勾选 `Don't show this again`。
|
||||||
|
3. 确认急停复位、机器上电,并按配置完成 homing;否则 AUTO 可能被门控。
|
||||||
|
4. 点击右侧 AUTO 图标,进入 AUTO 按钮页。
|
||||||
|
5. 点击带打开文件/文件夹图标的 `Load a new program`。
|
||||||
|
6. 选择 `linuxcnc/nc_files` 下的 `.ngc` 文件,点击底部绿色对勾。
|
||||||
|
7. 检查顶部路径和预览轨迹,再使用 Run;不要把加载误认为已经执行。
|
||||||
|
|
||||||
|
## 证据文件
|
||||||
|
|
||||||
|
- `gmoccapy-important-changes.png`:用户原始提示截图。
|
||||||
|
- `gmoccapy-before-load.png`:未加载程序。
|
||||||
|
- `gmoccapy-after-popup-close.png`:关闭提示后的主界面。
|
||||||
|
- `gmoccapy-load-file-page.png`:实际文件选择页。
|
||||||
|
- `gmoccapy-program-loaded.png`:加载后的程序和轨迹。
|
||||||
|
- `gmoccapy-file-inventory.json`:逐文件 SHA-256 清单。
|
||||||
BIN
doc-g/gmoccapy-auto-active-icon.png
Normal file
|
After Width: | Height: | Size: 738 B |
BIN
doc-g/gmoccapy-auto-api.png
Normal file
|
After Width: | Height: | Size: 278 KiB |
BIN
doc-g/gmoccapy-auto-api2.png
Normal file
|
After Width: | Height: | Size: 277 KiB |
BIN
doc-g/gmoccapy-auto-click2.png
Normal file
|
After Width: | Height: | Size: 282 KiB |
BIN
doc-g/gmoccapy-auto-click3.png
Normal file
|
After Width: | Height: | Size: 281 KiB |
BIN
doc-g/gmoccapy-auto-focused.png
Normal file
|
After Width: | Height: | Size: 296 KiB |
BIN
doc-g/gmoccapy-auto-homed.png
Normal file
|
After Width: | Height: | Size: 296 KiB |
BIN
doc-g/gmoccapy-auto-mode.png
Normal file
|
After Width: | Height: | Size: 294 KiB |
BIN
doc-g/gmoccapy-auto-success-candidate.png
Normal file
|
After Width: | Height: | Size: 294 KiB |
BIN
doc-g/gmoccapy-auto.png
Normal file
|
After Width: | Height: | Size: 281 KiB |
BIN
doc-g/gmoccapy-before-load.png
Normal file
|
After Width: | Height: | Size: 282 KiB |
323
doc-g/gmoccapy-control-panel-data-control-flow.md
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
# GMOCCAPY 控制面板功能、数据流与控制流
|
||||||
|
|
||||||
|
更新时间:2026-08-25
|
||||||
|
|
||||||
|
本文是后续分析 GMOCCAPY 控制面板的总入口。它把普通三轴 `gmoccapy.ini` 和五轴 XYZAC RTCP 案例放在同一套模型中,区分 UI 控件、GMOCCAPY Python handler、LinuxCNC command/status、TASK/motion、HAL 和 Vismach 的责任边界。
|
||||||
|
|
||||||
|
## 1. 范围与版本边界
|
||||||
|
|
||||||
|
| 项目 | 当前工作区/运行时 |
|
||||||
|
|---|---|
|
||||||
|
| 仓库源码 | `linuxcnc/VERSION = 2.10.0~pre0` |
|
||||||
|
| 仓库 GMOCCAPY 源码 | `linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py`,`_RELEASE = 3.4.3` |
|
||||||
|
| 实际系统 GUI | `/usr/bin/gmoccapy`,运行时版本 `3.5.2`,系统 LinuxCNC `2.10.0~pre1` |
|
||||||
|
| 普通面板入口 | [`run-gmoccapy.sh`](../run-gmoccapy.sh),默认 `configs/sim/gmoccapy/gmoccapy.ini` |
|
||||||
|
| 五轴 RTCP 入口 | [`run-5axis-rtcp.sh`](../run-5axis-rtcp.sh),默认 XYZAC TRT switchkins |
|
||||||
|
| 发布/仿真边界 | POSIX non-realtime 仿真;不等同于实时硬件验证 |
|
||||||
|
|
||||||
|
截图中的 `Important change(s)` 弹窗来自系统 GMOCCAPY 3.5.2,不存在于仓库 3.4.3 源文件中;两套来源不能混为一个源码版本。详细证据见 [GMOCCAPY 分析报告](gmoccapy-analysis.md)。
|
||||||
|
|
||||||
|
## 2. 总体架构
|
||||||
|
|
||||||
|
```text
|
||||||
|
快捷方式 / linuxcnc -r CASE.ini
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
LinuxCNC 启动器与 INI 解析
|
||||||
|
│
|
||||||
|
├── linuxcncsvr / NML 状态与命令通道
|
||||||
|
├── milltask / RS274NGC interpreter / task mode
|
||||||
|
├── motmod + kinematics / fixed servo cycle
|
||||||
|
├── HAL files / HALUI / simulated devices
|
||||||
|
└── DISPLAY=gmoccapy 或 DISPLAY=axis
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
GMOCCAPY Gtk/Glade 控制面板
|
||||||
|
│
|
||||||
|
┌─────────┴─────────┐
|
||||||
|
│ │
|
||||||
|
用户点击/键盘/HAL linuxcnc.stat/HAL status
|
||||||
|
│ ▲
|
||||||
|
▼ │
|
||||||
|
Python handler ── command ─┴── TASK/motion/HAL feedback
|
||||||
|
│
|
||||||
|
├── 模式/按钮页切换
|
||||||
|
├── 程序加载/预览
|
||||||
|
├── AUTO 运行/暂停/停止/单步
|
||||||
|
├── MDI 命令
|
||||||
|
└── homing、tool、offset、spindle、coolant
|
||||||
|
```
|
||||||
|
|
||||||
|
GMOCCAPY 不是运动学计算器。它负责把操作员动作转换为 LinuxCNC command,并把 LinuxCNC status/HAL status 投影回界面;真正的解释器、轨迹规划、逆解/正解和关节命令由 LinuxCNC runtime 完成。
|
||||||
|
|
||||||
|
## 3. 启动控制流
|
||||||
|
|
||||||
|
### 3.1 快捷方式层
|
||||||
|
|
||||||
|
`run-gmoccapy.sh` 和 `run-5axis-rtcp.sh` 都遵循相同模式:
|
||||||
|
|
||||||
|
1. 计算仓库绝对路径,避免从任意工作目录启动时相对路径漂移。
|
||||||
|
2. 校验 INI、演示程序、LinuxCNC launcher 是否存在。
|
||||||
|
3. 进入 LinuxCNC 根目录。
|
||||||
|
4. 执行 `/usr/bin/linuxcnc -r CASE.ini`,保留终端诊断。
|
||||||
|
|
||||||
|
五轴快捷方式额外检查 RTCP 子程序中确实存在 `M428`,并支持 `xyzac-trt.ini` 与 `xyzbc-trt.ini` 两个变体。
|
||||||
|
|
||||||
|
### 3.2 INI 到进程
|
||||||
|
|
||||||
|
以普通 GMOCCAPY 为例:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[DISPLAY] DISPLAY=gmoccapy
|
||||||
|
[TASK] TASK=milltask
|
||||||
|
[HAL] core_sim.hal + spindle_sim.hal + simulated_home.hal
|
||||||
|
[EMCIO] TOOL_TABLE=tool.tbl
|
||||||
|
[RS274NGC] PARAMETER_FILE=sim.var, SUBROUTINE_PATH=macros
|
||||||
|
[KINS] trivkins coordinates=xyz
|
||||||
|
```
|
||||||
|
|
||||||
|
启动器随后创建 `linuxcncsvr`、`milltask`、`halui` 和 GMOCCAPY。GMOCCAPY 进程通过 `-ini` 得到同一个 INI 路径,不能自行替代 TASK/motion/HAL。
|
||||||
|
|
||||||
|
### 3.3 GMOCCAPY 初始化顺序
|
||||||
|
|
||||||
|
仓库源码 `gmoccapy.py` 的主初始化顺序为:
|
||||||
|
|
||||||
|
1. 解析 `-user_mode`、`-logo` 参数(约 `243-270` 行)。
|
||||||
|
2. 读取 INI(`_get_ini_data()`,约 `272-275` 行)。
|
||||||
|
3. 读取偏好文件(`_get_pref_data()`)。
|
||||||
|
4. 动态创建 DRO、home、touch-off、jog、macro 等控件(约 `280-305` 行)。
|
||||||
|
5. 建立 GMOCCAPY HAL pins(约 `307` 行)。
|
||||||
|
6. 连接 Glade signals(约 `315-320` 行)。
|
||||||
|
7. 显示主窗口(约 `324-325` 行)。
|
||||||
|
8. 初始化主题、G-code preview、kinematics view、offset/tool editor、keyboard 和文件选择器(约 `327-342` 行)。
|
||||||
|
|
||||||
|
系统 3.5.2 运行时在 GUI 初始化后额外调用 `_startup_message()`,生成版本提示弹窗;它属于安装包新增功能,不属于仓库 3.4.3 初始化链。
|
||||||
|
|
||||||
|
## 4. 控制面板模式和按钮门控
|
||||||
|
|
||||||
|
### 4.1 模式按钮
|
||||||
|
|
||||||
|
Glade 中的主要模式控件:
|
||||||
|
|
||||||
|
| 控件 | 作用 | handler/状态 |
|
||||||
|
|---|---|---|
|
||||||
|
| `rbt_manual` | MANUAL/JOG | `on_rbt_manual_pressed()` 发出 `MODE_MANUAL` |
|
||||||
|
| `rbt_mdi` | MDI | `on_rbt_mdi_pressed()` 发出 `MODE_MDI` |
|
||||||
|
| `rbt_auto` | AUTO | `on_rbt_auto_pressed()` 发出 `MODE_AUTO` |
|
||||||
|
| `tbtn_setup` | 设置页 | 进入设置前强制 MANUAL,并禁用模式按钮 |
|
||||||
|
| `tbtn_user_tabs` | 用户页 | 切换主 notebook 页面 |
|
||||||
|
|
||||||
|
`on_hal_status_mode_auto()` 在收到 HAL status 后将主页面切回 page 0,把按钮 notebook 切到 `_BB_AUTO`,并将 jog notebook 切到 AUTO 页面(约 `2804-2824` 行)。因此“点击 AUTO 图标”与“看到 AUTO 按钮组”是同一个状态转换的两个可观察结果。
|
||||||
|
|
||||||
|
### 4.2 急停、上电、homing 门控
|
||||||
|
|
||||||
|
`on_tbtn_setup_toggled()` 和 HAL status handler 根据以下条件设置按钮敏感度:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ESTOP → manual/mdi/auto 均禁用
|
||||||
|
ON + 未 homed → 只允许 manual
|
||||||
|
ON + all_homed 或 NO_FORCE_HOMING → manual/mdi/auto 全部可用
|
||||||
|
```
|
||||||
|
|
||||||
|
对应仓库源码约为 `3793-3808` 行。未满足条件时,点击 AUTO 会被 `on_hal_status_mode_auto()` 拒绝,显示 `It is not possible to change to Auto Mode at the moment`,并回到 MANUAL。
|
||||||
|
|
||||||
|
这解释了为什么控制面板可能“没有加载按钮”:加载按钮不在 MANUAL 页面,而且 AUTO 页面本身可能因状态门控没有激活。
|
||||||
|
|
||||||
|
## 5. G-code 加载数据流
|
||||||
|
|
||||||
|
### 5.1 控件链
|
||||||
|
|
||||||
|
```text
|
||||||
|
rbt_auto
|
||||||
|
│ on_hal_status_mode_auto()
|
||||||
|
▼
|
||||||
|
hbtb_auto / _BB_AUTO
|
||||||
|
│
|
||||||
|
│ btn_load(图标 img_open,tooltip=Load a new program)
|
||||||
|
│ on_btn_load_clicked()
|
||||||
|
▼
|
||||||
|
_BB_LOAD_FILE + IconFileSelection1
|
||||||
|
│
|
||||||
|
│ 用户选中路径
|
||||||
|
│ btn_select / IconFileSelection1_selected
|
||||||
|
▼
|
||||||
|
hal_action_open.load_file(path)
|
||||||
|
▼
|
||||||
|
LinuxCNC task/interpreter + preview/status
|
||||||
|
▼
|
||||||
|
ntb_preview page 0 + ntb_button _BB_AUTO + stat.file
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 打开文件界面
|
||||||
|
|
||||||
|
`on_btn_load_clicked()`(`gmoccapy.py:5046-5078`)执行:
|
||||||
|
|
||||||
|
1. `ntb_button.set_current_page(_BB_LOAD_FILE)`。
|
||||||
|
2. `ntb_preview.set_current_page(3)`。
|
||||||
|
3. 打开 full-size preview。
|
||||||
|
4. `_show_iconview_tab(True)`。
|
||||||
|
5. 刷新 `IconFileSelection1` 文件列表。
|
||||||
|
6. 暂时禁用 MANUAL、MDI 和设置按钮,避免文件选择过程中切换模式。
|
||||||
|
|
||||||
|
文件选择器在 `_init_IconFileSelection()` 和 `_init_file_to_load()` 中读取 `PROGRAM_PREFIX` 与 `[FILTER] PROGRAM_EXTENSION`。普通案例 `PROGRAM_PREFIX = ../../nc_files/` 最终指向仓库的 `linuxcnc/nc_files`。
|
||||||
|
|
||||||
|
### 5.3 确认文件
|
||||||
|
|
||||||
|
`on_IconFileSelection1_selected()`(`gmoccapy.py:5114-5125`)执行:
|
||||||
|
|
||||||
|
1. 将选中的 `path` 交给 `hal_action_open.load_file(path)`。
|
||||||
|
2. 恢复 preview page 0。
|
||||||
|
3. 关闭 full-size preview 和 icon view。
|
||||||
|
4. 把按钮 notebook 切回 `_BB_AUTO`。
|
||||||
|
5. 重新启用 MANUAL、MDI 和设置按钮。
|
||||||
|
|
||||||
|
这一步是“加载程序”,不是“运行程序”。运行还需要 `btn_run`/`AUTO_RUN`;源码键盘 handler 将 `R` 映射为运行、`P` 映射为暂停、`S` 映射为恢复。
|
||||||
|
|
||||||
|
### 5.4 启动时自动加载
|
||||||
|
|
||||||
|
`on_window1_show()` 会读取偏好项 `open_file`(约 `2920-2924` 行)。如果用户在设置页指定“启动时加载文件”,GMOCCAPY 会在窗口显示后直接调用 `hal_action_open.load_file(file)`。这条路径绕过手动点击文件选择器,但仍通过同一个 LinuxCNC load action。
|
||||||
|
|
||||||
|
## 6. AUTO 运行控制流
|
||||||
|
|
||||||
|
```text
|
||||||
|
btn_run / R
|
||||||
|
▼
|
||||||
|
linuxcnc.command().auto(AUTO_RUN, line)
|
||||||
|
▼
|
||||||
|
milltask / RS274NGC interpreter
|
||||||
|
▼
|
||||||
|
TP queue → motion controller → kinematics → joint commands
|
||||||
|
▼
|
||||||
|
HAL simulated device / spindle / coolant / tool change
|
||||||
|
▼
|
||||||
|
linuxcnc.stat + hal_status
|
||||||
|
▼
|
||||||
|
GMOCCAPY DRO、progress、preview、button sensitivity
|
||||||
|
```
|
||||||
|
|
||||||
|
相关按钮属于 `hbtb_auto`:`btn_load`、`btn_reload`、`btn_run`、`btn_stop`、`tbtn_pause`、`btn_step`、`btn_from_line`、`tbtn_optional_blocks`、`btn_edit`。Glade 负责控件和 signal 绑定,Python handler 负责调用 LinuxCNC command;运动本身不在 GTK handler 中实现。
|
||||||
|
|
||||||
|
## 7. MDI、Jog、Home、Tool 和 Offset 数据流
|
||||||
|
|
||||||
|
### MDI
|
||||||
|
|
||||||
|
`rbt_mdi` 将 task mode 设为 `MODE_MDI`。MDI 输入提交后进入 LinuxCNC interpreter;G-code 解析错误通过 LinuxCNC error channel 返回,再由 GMOCCAPY notification/error handler 显示。
|
||||||
|
|
||||||
|
### Jog
|
||||||
|
|
||||||
|
JOG 按钮由 `_make_jog_button()` 动态生成;轴/关节列表来自 INI 的 `COORDINATES`、kinematics 类型和 joint-axis mapping。Jog 命令仍经 LinuxCNC command 通道,不直接改变 DRO;DRO 的真值来自 status feedback。
|
||||||
|
|
||||||
|
### Home
|
||||||
|
|
||||||
|
Home 按钮最终调用 `command.home(joint_or_axis)`。仿真 HAL 的 `sim_home_switch` 或自定义 home HAL 决定 home switch 状态;`all_homed` HAL status 决定 MDI/AUTO 是否重新开放。
|
||||||
|
|
||||||
|
### Tool/Offset
|
||||||
|
|
||||||
|
工具表来自 `[EMCIO] TOOL_TABLE`,offset page 和 tool editor 通过 LinuxCNC tool data/action 更新。G43/G49/G43.1 的解释器语义由 RS274NGC 负责;GMOCCAPY 只显示和提交工具/offset 操作。系统 3.5.2 的启动提示明确提醒:AUTO 中换刀后的 G43 不能假定由界面自动恢复,应由程序/REMAP 明确处理。
|
||||||
|
|
||||||
|
## 8. 五轴 XYZAC RTCP 控制流
|
||||||
|
|
||||||
|
### 8.1 配置层
|
||||||
|
|
||||||
|
五轴案例 `xyzac-trt.ini` 的关键字段:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[DISPLAY] OPEN_FILE = ./demos/xyzac_switchkins.ngc
|
||||||
|
[RS274NGC] SUBROUTINE_PATH = ./remap_subs
|
||||||
|
[RS274NGC] REMAP = M428 ... ngc=428remap
|
||||||
|
[RS274NGC] REMAP = M429 ... ngc=429remap
|
||||||
|
[RS274NGC] REMAP = M430 ... ngc=430remap
|
||||||
|
[KINS] KINEMATICS = xyzac-trt-kins sparm=identityfirst
|
||||||
|
[TRAJ] COORDINATES = XYZAC
|
||||||
|
[HAL] HALCMD = net ... motion.switchkins-type
|
||||||
|
[HAL] HALCMD = loadusr -W xyzac-trt-gui
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 switchkins 状态
|
||||||
|
|
||||||
|
`xyzac-trt-kins.c` 将 `sparm=identityfirst` 定义为:
|
||||||
|
|
||||||
|
```text
|
||||||
|
switchkins-type 0 → identity kinematics
|
||||||
|
switchkins-type 1 → xyzac TRT kinematics
|
||||||
|
switchkins-type 2 → userk kinematics
|
||||||
|
```
|
||||||
|
|
||||||
|
`trtfuncs.c` 为 XYZAC/XYZBC 实现 forward/inverse mapping,并声明 `tool-offset`、旋转中心和轴 offset HAL pins。XYZAC 的五个关节是 `X Y Z A C`;A 是倾斜台,C 是水平旋转台。
|
||||||
|
|
||||||
|
### 8.3 M428/M429 控制链
|
||||||
|
|
||||||
|
```text
|
||||||
|
G-code M428
|
||||||
|
▼
|
||||||
|
428remap.ngc
|
||||||
|
├── M68 E3 Q1 写 motion.analog-out-03 = 1
|
||||||
|
├── M66 E0 L0 等待同步
|
||||||
|
└── 检查 motion.switchkins-type == 1
|
||||||
|
▼
|
||||||
|
xyzac-trt-kins inverse/forward
|
||||||
|
▼
|
||||||
|
XYZAC joint commands with TCP/RTCP compensation
|
||||||
|
```
|
||||||
|
|
||||||
|
`M429` 对称地写入 `Q0`,切回 identity;`M430` 写入 `Q2`,进入 userk。`helix_ac.ngc` 演示了典型顺序:先用 M429 在 machine coordinates 安全定位,再用 M428 执行带 A/C 姿态变化的螺旋插补,结束后再切回 M429 做返回定位。
|
||||||
|
|
||||||
|
### 8.4 Vismach feedback
|
||||||
|
|
||||||
|
INI 中的 HAL nets 将以下反馈送到 `xyzac-trt-gui`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
joint.0.pos-fb → table-x
|
||||||
|
joint.1.pos-fb → saddle-y
|
||||||
|
joint.2.pos-fb → spindle-z
|
||||||
|
joint.3.pos-fb → tilt-a
|
||||||
|
joint.4.pos-fb → rotate-c
|
||||||
|
motion.tooloffset.z → tool-offset
|
||||||
|
xyzac-trt-kins.y-offset/z-offset → Vismach geometry
|
||||||
|
```
|
||||||
|
|
||||||
|
因此 Vismach 不是运动 authority;它是 joint feedback、tool offset 和 geometry offset 的可视化消费者。
|
||||||
|
|
||||||
|
## 9. 状态真值与可观察结果
|
||||||
|
|
||||||
|
| 状态 | authority | 面板表现 |
|
||||||
|
|---|---|---|
|
||||||
|
| 急停 | TASK/motion state | E-stop active,模式按钮禁用 |
|
||||||
|
| 上电 | TASK state + HAL | Power icon active,允许进一步操作 |
|
||||||
|
| all homed | HAL status / joint state | MDI/AUTO 解除门控 |
|
||||||
|
| 当前模式 | `stat.task_mode` | MANUAL/MDI/AUTO 页面切换 |
|
||||||
|
| 当前文件 | `stat.file` / file-loaded status | 顶部文件名、G-code view、Run gate |
|
||||||
|
| 解释器状态 | `stat.interp_state` | Run/Pause/Step/Stop 敏感度 |
|
||||||
|
| 当前 G/M codes | `stat.gcodes` / `stat.mcodes` | G-Code 信息区域 |
|
||||||
|
| 机床位置 | `stat.position` / joint feedback | DRO 和 Vismach |
|
||||||
|
| kinematics mode | `motion.switchkins-type` | AXIS switchkins 面板/RTCP 模式 |
|
||||||
|
|
||||||
|
任何未来 Web/WASM 控制面板都应消费这些 authority 的 snapshot/event,而不是在 JS/GUI 层镜像一套可写的机床状态。
|
||||||
|
|
||||||
|
## 10. 现有证据与复现
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run-gmoccapy.sh
|
||||||
|
./run-5axis-rtcp.sh
|
||||||
|
./run-5axis-rtcp.sh xyzbc-trt.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
已验证的五轴运行证据:
|
||||||
|
|
||||||
|
- [5axis-rtcp-running.png](5axis-rtcp-running.png)
|
||||||
|
- [5axis-rtcp-shortcut.md](5axis-rtcp-shortcut.md)
|
||||||
|
- `linuxcnc` 状态返回 `file=./demos/xyzac_switchkins.ngc`
|
||||||
|
- AXIS 标题包含 `sim-xyzac-trt-kins (switchkins)`
|
||||||
|
- `xyzac-trt-gui` 进程和五轴 Vismach 预览存在
|
||||||
|
|
||||||
|
## 11. 后续分析顺序
|
||||||
|
|
||||||
|
分析任何新的 GMOCCAPY/五轴控制问题时,按以下顺序取证:
|
||||||
|
|
||||||
|
1. 先记录实际运行时路径、LinuxCNC 版本、GMOCCAPY 版本;不要把系统安装脚本和仓库源码混为一份。
|
||||||
|
2. 读取 INI 的 DISPLAY/TASK/HAL/RS274NGC/KINS/TRAJ/EMCIO 段,建立配置闭包。
|
||||||
|
3. 在 Glade 中找到 widget ID,再在 `gmoccapy.py` 找 signal handler;不要只根据截图猜按钮语义。
|
||||||
|
4. 追踪 handler 发出的 `linuxcnc.command()`,确认 command 进入 TASK/motion/HAL 的路径。
|
||||||
|
5. 用 `linuxcnc.stat()`、HAL pins、日志和 Vismach feedback 交叉验证,不把 DRO 或 preview 当作 authority。
|
||||||
|
6. 五轴问题先确认 kinematics type、M428/M429 状态、tool offset、rotation center/axis offset,再分析轨迹差异。
|
||||||
|
7. 任何缺失的官方 oracle、实时硬件、浏览器或资源输入都保留为明确 blocker,不以 GUI 截图替代完整等价证明。
|
||||||
7427
doc-g/gmoccapy-file-inventory.json
Normal file
18
doc-g/gmoccapy-file-inventory.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# GMOCCAPY 文件清单
|
||||||
|
|
||||||
|
清单范围内共 `1233` 个文件;每项包含相对路径、角色、字节数和 SHA-256。
|
||||||
|
|
||||||
|
| 角色 | 数量 |
|
||||||
|
|---|---:|
|
||||||
|
| `documentation` | 2 |
|
||||||
|
| `documentation_asset` | 27 |
|
||||||
|
| `icon_asset` | 973 |
|
||||||
|
| `implementation` | 28 |
|
||||||
|
| `installed_ui_asset` | 1 |
|
||||||
|
| `simulation_asset` | 16 |
|
||||||
|
| `simulation_ini` | 29 |
|
||||||
|
| `simulation_resource` | 69 |
|
||||||
|
| `simulation_runtime_data` | 64 |
|
||||||
|
| `translation` | 24 |
|
||||||
|
|
||||||
|
完整逐文件数据见 [`gmoccapy-file-inventory.json`](gmoccapy-file-inventory.json)。
|
||||||
BIN
doc-g/gmoccapy-homed.png
Normal file
|
After Width: | Height: | Size: 285 KiB |
BIN
doc-g/gmoccapy-homing.png
Normal file
|
After Width: | Height: | Size: 284 KiB |
BIN
doc-g/gmoccapy-hover-auto.png
Normal file
|
After Width: | Height: | Size: 296 KiB |
BIN
doc-g/gmoccapy-important-changes-v2.png
Normal file
|
After Width: | Height: | Size: 201 KiB |
BIN
doc-g/gmoccapy-important-changes.png
Normal file
|
After Width: | Height: | Size: 197 KiB |
BIN
doc-g/gmoccapy-load-file-page.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
doc-g/gmoccapy-open-file-icon.png
Normal file
|
After Width: | Height: | Size: 486 B |
BIN
doc-g/gmoccapy-popup.png
Normal file
|
After Width: | Height: | Size: 277 KiB |
BIN
doc-g/gmoccapy-program-loaded.png
Normal file
|
After Width: | Height: | Size: 295 KiB |
BIN
doc-g/gmoccapy-ui-load-confirmed.png
Normal file
|
After Width: | Height: | Size: 254 KiB |
BIN
doc-g/root-current.png
Normal file
|
After Width: | Height: | Size: 5.7 MiB |
BIN
doc-rtcp/5axis-rtcp-running.png
Normal file
|
After Width: | Height: | Size: 186 KiB |
62
doc-rtcp/5axis-rtcp-shortcut.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# 五轴 RTCP 案例快捷方式
|
||||||
|
|
||||||
|
快捷方式:[`../run-5axis-rtcp.sh`](../run-5axis-rtcp.sh)
|
||||||
|
|
||||||
|
默认执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run-5axis-rtcp.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
默认案例是:
|
||||||
|
|
||||||
|
```text
|
||||||
|
linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
它使用 `XYZAC` 五轴表旋转/倾斜(TRT)拓扑、`xyzac-trt-kins` 和 Vismach `xyzac-trt-gui`。默认演示程序是:
|
||||||
|
|
||||||
|
```text
|
||||||
|
linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins.ngc
|
||||||
|
```
|
||||||
|
|
||||||
|
该顶层演示调用 `remap_subs/xyzac_switchkins_sub.ngc`;其螺旋插补子程序 `remap_subs/helix_ac.ngc` 使用:
|
||||||
|
|
||||||
|
```gcode
|
||||||
|
M428 ;TCP:xyzac
|
||||||
|
```
|
||||||
|
|
||||||
|
`M428` 通过 remap 将 `motion.switchkins-type` 切换到 XYZAC TCP/RTCP 运动学;`helix_ac.ngc` 在需要机床坐标定位时使用 `M429` 切回 identity,再用 `M428` 进入 TCP。配置的 `M430` 进入 userk。AXIS 右侧的 `TCP:XYZAC` 按钮也提供同一切换入口。
|
||||||
|
|
||||||
|
## XYZBC 变体
|
||||||
|
|
||||||
|
快捷方式接受可选配置名:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run-5axis-rtcp.sh xyzbc-trt.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
该变体使用 `XYZBC` 和 `xyzbc-trt-kins`,默认程序为 `demos/xyzbc_switchkins.ngc`。
|
||||||
|
|
||||||
|
## 启动证据
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
实测启动结果:
|
||||||
|
|
||||||
|
- AXIS 标题:`xyzac_switchkins.ngc - AXIS 2.10.0~pre1 on sim-xyzac-trt-kins (switchkins)`。
|
||||||
|
- 右侧 `SWITCHKINS` 面板显示 `TCP:XYZAC`。
|
||||||
|
- Vismach 预览显示五轴机床和多轴刀具轨迹。
|
||||||
|
- LinuxCNC 状态接口返回 `file=./demos/xyzac_switchkins.ngc`,`COORDINATES=XYZAC`。
|
||||||
|
- 当前环境使用 POSIX non-realtime 回退;这验证的是仿真配置和 RTCP 路径,不是实时硬件性能。
|
||||||
|
|
||||||
|
## 来源依据
|
||||||
|
|
||||||
|
- `linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini`
|
||||||
|
- `linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins.ngc`
|
||||||
|
- `linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/xyzac_switchkins_sub.ngc`
|
||||||
|
- `linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/helix_ac.ngc`
|
||||||
|
- `linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/428remap.ngc`
|
||||||
|
- `linuxcnc/src/emc/kinematics/xyzac-trt-kins.c`
|
||||||
|
- `linuxcnc/src/emc/kinematics/trtfuncs.c`
|
||||||
|
- `linuxcnc/docs/src/motion/5-axis-kinematics.adoc`
|
||||||
51
doc-rtcp/README.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# 五轴 RTCP 文档集
|
||||||
|
|
||||||
|
接续入口:[`../nextTask-rtcp.md`](../nextTask-rtcp.md)
|
||||||
|
|
||||||
|
持续执行提示词:[`xyzac-trt-autonomous-execution-prompt.md`](xyzac-trt-autonomous-execution-prompt.md)
|
||||||
|
|
||||||
|
本目录从 `doc-g` 集中迁移五轴机床、XYZAC/XYZBC TRT、switchkins、RTCP 和控制面板数据流资料。原 `doc-g` 文件保留不变;本目录是后续五轴案例分析的独立入口。
|
||||||
|
|
||||||
|
## 快速入口
|
||||||
|
|
||||||
|
- [五轴 RTCP 快捷方式](5axis-rtcp-shortcut.md)
|
||||||
|
- [控制面板功能、数据流与控制流](gmoccapy-control-panel-data-control-flow.md)
|
||||||
|
- [`xyzac-trt.ini` 控制面板与 Web/WASM 设计基础](xyzac-trt-control-panel-web-wasm-design.md)
|
||||||
|
- [`xyzac-trt.ini` Web/WASM 实施计划与里程碑](xyzac-trt-web-wasm-implementation-plan.md)
|
||||||
|
- [`xyzac-trt.ini` 源码对标与 WASM 唯一权威合同](xyzac-trt-web-wasm-source-parity-contract.md)
|
||||||
|
- [GMOCCAPY 运行时和面板分析](gmoccapy-analysis.md)
|
||||||
|
- [五轴 RTCP 运行截图](5axis-rtcp-running.png)
|
||||||
|
|
||||||
|
## 运行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run-5axis-rtcp.sh
|
||||||
|
./run-5axis-rtcp.sh xyzbc-trt.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
快捷方式仍位于仓库根目录;文档中的 `../run-5axis-rtcp.sh` 链接指向同一入口。
|
||||||
|
|
||||||
|
默认案例:
|
||||||
|
|
||||||
|
```text
|
||||||
|
linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
## 文档边界
|
||||||
|
|
||||||
|
- `5axis-rtcp-shortcut.md`:案例入口、XYZAC/XYZBC 变体、M428/M429 和启动证据。
|
||||||
|
- `gmoccapy-control-panel-data-control-flow.md`:从控制面板控件到 LinuxCNC TASK/motion/HAL/Vismach 的完整数据流和控制流,其中第 8 节是五轴 RTCP 专章。
|
||||||
|
- `gmoccapy-analysis.md`:GMOCCAPY 系统运行时版本、启动提示和面板行为;作为五轴界面分析的背景材料。
|
||||||
|
- `gmoccapy-file-inventory.*`:GMOCCAPY 相关配置、实现、资源和图标的逐文件清单。
|
||||||
|
|
||||||
|
## 来源
|
||||||
|
|
||||||
|
五轴核心来源保持在 LinuxCNC 源目录:
|
||||||
|
|
||||||
|
```text
|
||||||
|
linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/
|
||||||
|
linuxcnc/src/emc/kinematics/xyzac-trt-kins.c
|
||||||
|
linuxcnc/src/emc/kinematics/xyzbc-trt-kins.c
|
||||||
|
linuxcnc/src/emc/kinematics/trtfuncs.c
|
||||||
|
linuxcnc/docs/src/motion/5-axis-kinematics.adoc
|
||||||
|
```
|
||||||
170
doc-rtcp/gmoccapy-analysis.md
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
# GMOCCAPY 分析与加载测试
|
||||||
|
|
||||||
|
更新时间:2026-08-25
|
||||||
|
|
||||||
|
## 结论
|
||||||
|
|
||||||
|
“没有看到加载程序按钮”不是按钮缺失,而是首次启动提示和 GMOCCAPY 的模式门控共同造成的视觉结果:
|
||||||
|
|
||||||
|
1. 首次启动会弹出模态的 `Important change(s)` 窗口。该窗口覆盖主界面并阻止底层点击;用户截图中的右侧 `X` 是关闭按钮,底部复选框是 `Don't show this again`。
|
||||||
|
2. 加载按钮只属于 AUTO 页面按钮组,不属于 MANUAL/JOG 页面。源码将它声明为 `btn_load`,提示为 `Load a new program`,图标为 `img_open`。
|
||||||
|
3. AUTO 模式还受机器状态和 homing 门控。`gmoccapy.py` 在未满足状态时会禁用 `rbt_auto`,并提示 `It is not possible to change to Auto Mode at the moment`。
|
||||||
|
4. 关闭首次提示、进入 AUTO 后,点击文件夹/打开文件图标会进入文件选择页。当前 `gmoccapy.ini` 的 `PROGRAM_PREFIX = ../../nc_files/` 解析到 `linuxcnc/nc_files`。
|
||||||
|
|
||||||
|
## 用户截图分析
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
用户提供的图片尺寸为 `697 x 837`,内容是 GMOCCAPY 3.5.x 的版本变更提示:
|
||||||
|
|
||||||
|
- LinuxCNC 2.10/GMOCCAPY 3.5.0 不再在换刀后自动保留 G43;需要通过 REMAP 实现自动重新激活。
|
||||||
|
- 3.5.1 的工具表和偏置页默认使用计算器输入,可在设置或按钮中改变。
|
||||||
|
- MDI 和 G-code 编辑模式新增调用计算器的按钮。
|
||||||
|
- `release notes` 是橙色链接;右下方复选框用于永久隐藏该提示。
|
||||||
|
|
||||||
|
这张提示不是文件选择器,也不是加载失败信息。应先关闭它,再操作主界面。
|
||||||
|
|
||||||
|
### 新截图的源码逐项对照
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
这张截图与前一张内容一致,尺寸为 `697 x 837`,SHA-256 为 `df7322c30bcb6af131591a04b11cad24e7b6d186beaff83059fcfd8ffbae2d1b`。截图中的每一块文字都能在当前系统安装的 `/usr/bin/gmoccapy` 中找到:
|
||||||
|
|
||||||
|
| 截图区域 | 源码证据 | 含义 |
|
||||||
|
|---|---|---|
|
||||||
|
| `Important change(s)` 标题 | `/usr/bin/gmoccapy:557-560` | `_startup_message()` 建立启动提示标题;注释要求新增消息只能追加,不能删除旧消息,否则隐藏索引会错位。 |
|
||||||
|
| 3.5.0 G43 提示 | `/usr/bin/gmoccapy:561-563` | GMOCCAPY 3.5.0 不再承诺换刀后自动保留 G43;想自动恢复必须使用 REMAP,示例在 GMOCCAPY 仿真配置中。 |
|
||||||
|
| 3.5.1 计算器提示 | `/usr/bin/gmoccapy:564-565` | 工具表和偏置页默认启用计算器输入;设置或页面按钮可以关闭;MDI 和 G-code 编辑页也增加了计算器按钮。 |
|
||||||
|
| `release notes` 链接 | `/usr/bin/gmoccapy:570-573` | 以 Pango markup 拼接 LinuxCNC 官方 release notes URL,并通过通知组件显示。点击它会打开文档,不会关闭提示。 |
|
||||||
|
| `Don't show this again` | `notification.py:151-155` | `show_checkbox=True` 时创建 GTK `CheckButton`;它不是普通说明文字,而是真正影响后续启动行为的复选框。 |
|
||||||
|
| 右侧 `X` | `notification.py:156-164` | 创建名为 `notification_close` 的 48x48 关闭按钮;点击后删除该消息,并把复选框状态发回 GMOCCAPY。 |
|
||||||
|
|
||||||
|
### 弹窗什么时候出现
|
||||||
|
|
||||||
|
系统运行时在 GUI 初始化和周期任务准备完成后,于 `/usr/bin/gmoccapy:519` 调用 `_startup_message()`。它读取偏好项 `hide_startup_messsage`(注意源码中确实有三个 `s` 的拼写):
|
||||||
|
|
||||||
|
```python
|
||||||
|
hide_message = self.prefs.getpref("hide_startup_messsage", 0, int)
|
||||||
|
if hide_message < len(messages):
|
||||||
|
...
|
||||||
|
self.notification.add_message(message, INFO_ICON, show_checkbox=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
当前消息数组有两项,因此默认值 `0` 会显示两项。关闭时如果复选框被勾选,`/usr/bin/gmoccapy:6241-6242` 将 `self.num`(当前为 `2`)写回 `hide_startup_messsage`。下次启动判断 `2 < 2` 为假,提示不再出现。普通点击 `X` 不勾选复选框时,只删除本次通知,下次仍会显示。
|
||||||
|
|
||||||
|
### 版本边界:截图运行时与仓库锁定源不同
|
||||||
|
|
||||||
|
这是本次分析中最重要的来源边界:
|
||||||
|
|
||||||
|
- 截图对应的实际运行程序是 `/usr/bin/gmoccapy`,其中 `_RELEASE = "3.5.2"`,并包含 `_startup_message()`、3.5.0/3.5.1 文本和隐藏偏好逻辑。
|
||||||
|
- 仓库锁定源是 `linuxcnc/VERSION = 2.10.0~pre0`;`linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py:79` 的版本为 `3.4.3`,该文件没有 `_startup_message()` 实现,也没有截图中的 3.5.0/3.5.1 文本。
|
||||||
|
- 本地启动日志曾显示 `LinuxCNC 2.10.0~pre1` 和 `gmoccapy 3.5.2`,与截图完全一致。因此这张截图是系统安装包的运行时证据,不应反向宣称锁定仓库 2.10.0~pre0/3.4.3 源码已经包含该功能。
|
||||||
|
|
||||||
|
已记录的运行时文件哈希:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/usr/bin/gmoccapy 11e7aee9216d24e4cc41018306f2541845bca9a9d6e16c38580f3ede9cca8d4d
|
||||||
|
/usr/share/gmoccapy/gmoccapy.glade 5fdc1557b02a716d30414fe739614fcd7c1d14cbe2e0a4c202c567a60b460adb
|
||||||
|
/usr/lib/python3/dist-packages/gmoccapy/notification.py
|
||||||
|
e0317fea738745f480c79d1f690bc52e88cc7f81bb4d14fa09c8c7196afb4e2f
|
||||||
|
```
|
||||||
|
|
||||||
|
### 用户应如何处理
|
||||||
|
|
||||||
|
1. 只想本次继续操作:点击右侧 `X`,不要勾选复选框。
|
||||||
|
2. 不想以后重复看到:先勾选 `Don't show this again`,再点击 `X`。
|
||||||
|
3. 想查看变更依据:点击 `release notes`,但这需要浏览器/网络或本地文档可用。
|
||||||
|
4. 使用 G43 的程序:不要仅依赖界面自动行为;在 AUTO 程序中按机床配置和 REMAP 约定明确处理刀具长度补偿,并在正式加工前验证工具表和偏置。
|
||||||
|
|
||||||
|
## 实际界面证据
|
||||||
|
|
||||||
|
### 未加载状态
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
主界面顶部显示 `No Program loaded`。右侧竖向按钮中第三个为 AUTO 模式图标;在 MANUAL 页面时,AUTO 页面里的 `Load a new program` 不会显示在当前按钮组。
|
||||||
|
|
||||||
|
### 文件选择页
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
这张截图是实际运行 GMOCCAPY 后进入的文件选择页。页面显示 `gcmc_lib`、`macros`、`ngcgui_lib` 等目录以及 `3D_Chips.ngc`、`3dtest.ngc` 等程序。底部右侧绿色对勾是确认选择,右下黄色回退箭头是返回。
|
||||||
|
|
||||||
|
进入这张页面的完整点击链是:
|
||||||
|
|
||||||
|
1. 关闭首次 `Important change(s)` 模态提示。
|
||||||
|
2. 在主界面右侧点击第三个模式图标,进入 AUTO;源码控件为 `rbt_auto`。
|
||||||
|
3. 等待 AUTO 按钮组出现,在其中点击打开文件图标;源码控件为 `btn_load`,tooltip 为 `Load a new program`。
|
||||||
|
4. `on_btn_load_clicked()` 将按钮页切换到文件选择页;此时才会看到本截图中的文件网格。
|
||||||
|
5. 选中 `.ngc` 文件后点击底部绿色对勾,源码控件为 `btn_select`。
|
||||||
|
|
||||||
|
因此,文件网格不是主界面的固定按钮,也不是 MDI 入口;在 MANUAL/JOG 页面直接寻找它不会找到。
|
||||||
|
|
||||||
|
### 加载后的程序
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
通过 LinuxCNC 的 `command.program_open()` 加载:
|
||||||
|
|
||||||
|
```text
|
||||||
|
/home/mes006/linuxcncWebWASM/linuxcnc/nc_files/3dtest.ngc
|
||||||
|
```
|
||||||
|
|
||||||
|
加载成功的证据是:
|
||||||
|
|
||||||
|
- 顶部由 `No Program loaded` 变成完整程序路径;
|
||||||
|
- 预览区出现 G-code 刀具轨迹;
|
||||||
|
- `linuxcnc.stat().file` 返回上述路径;
|
||||||
|
- `interp_state` 保持空闲,加载本身不会自动运行程序。
|
||||||
|
|
||||||
|
## 源码与界面映射
|
||||||
|
|
||||||
|
| 层 | 文件/符号 | 结论 |
|
||||||
|
|---|---|---|
|
||||||
|
| 主逻辑 | `linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py` | `on_btn_load_clicked()` 切到文件选择页;`on_IconFileSelection1_selected()` 调用 `hal_action_open.load_file(path)`。 |
|
||||||
|
| 加载按钮 | `btn_load` / `img_open` | tooltip 是 `Load a new program`,只在 AUTO 按钮页 `hbtb_auto`。 |
|
||||||
|
| 运行按钮 | `btn_run` / `img_run` | tooltip 是 `Run the loaded program`;加载后仍需手动运行。 |
|
||||||
|
| 文件确认 | `btn_select` / `img_select` | tooltip 是 `select the highlighted file and return the path`。 |
|
||||||
|
| 返回 | `btn_back_file_load` / `img_back_file_load` | 关闭文件选择页但不返回路径。 |
|
||||||
|
| 文件资源 | `linuxcnc/lib/python/gladevcp/iconview.py` 及 GMOCCAPY 初始化 | 读取 `PROGRAM_PREFIX`,按配置扩展名展示文件和目录。 |
|
||||||
|
| 首次提示 | GMOCCAPY preferences/release-notes 逻辑 | 模态提示优先于主界面,必须先关闭。 |
|
||||||
|
|
||||||
|
## 配置与资源分析
|
||||||
|
|
||||||
|
默认配置为 `linuxcnc/configs/sim/gmoccapy/gmoccapy.ini`:
|
||||||
|
|
||||||
|
- `[DISPLAY] DISPLAY = gmoccapy`:选择 GMOCCAPY GUI。
|
||||||
|
- `[DISPLAY] PROGRAM_PREFIX = ../../nc_files/`:从配置目录解析到 `linuxcnc/nc_files`。
|
||||||
|
- `[RS274NGC] PARAMETER_FILE = sim.var`:仿真参数文件。
|
||||||
|
- `[RS274NGC] SUBROUTINE_PATH = macros`:宏和子程序搜索路径。
|
||||||
|
- `[EMCIO] TOOL_TABLE = tool.tbl`:工具表。
|
||||||
|
- `[HAL]` 加载 `core_sim.hal`、`spindle_sim.hal`、`simulated_home.hal`,并在 GUI 启动后加载 `gmoccapy_postgui.hal`。
|
||||||
|
|
||||||
|
`configs/sim/gmoccapy` 不是单一案例,而是一组配置族,包含标准三轴、四轴、XYZAB/XYZAC/XYZCW、多轴、侧面板、消息、工具传感器、用户页、等离子和车床配置。完整逐文件路径与哈希见 [文件清单](gmoccapy-file-inventory.md)。
|
||||||
|
|
||||||
|
## 运行环境与限制
|
||||||
|
|
||||||
|
- 启动入口:[`../run-gmoccapy.sh`](../run-gmoccapy.sh),默认执行 `gmoccapy.ini`。
|
||||||
|
- 实际 GUI 进程:`/usr/bin/python3 /usr/bin/gmoccapy -ini .../gmoccapy.ini`。
|
||||||
|
- 当前环境没有实时内核,LinuxCNC 日志明确回退到 POSIX non-realtime;这不影响本次“加载和预览”验证,但不等同于实时硬件运行验证。
|
||||||
|
- `simulated_home.hal` 的共享 home switch 在本次手动尝试中报告 `Cannot home while shared home switch is closed j=2`,因此 AUTO 模式按钮可能保持禁用。加载程序本身仍可通过 GUI 文件页/API 验证。
|
||||||
|
- 加载和运行是两个动作:`program_open` 只读入程序并生成预览;需要 `AUTO` 页面中的 Run 按钮或 `R` 快捷键才会执行。
|
||||||
|
|
||||||
|
## 推荐操作顺序
|
||||||
|
|
||||||
|
1. 启动 `./run-gmoccapy.sh`。
|
||||||
|
2. 关闭首次 `Important change(s)` 提示;可勾选 `Don't show this again`。
|
||||||
|
3. 确认急停复位、机器上电,并按配置完成 homing;否则 AUTO 可能被门控。
|
||||||
|
4. 点击右侧 AUTO 图标,进入 AUTO 按钮页。
|
||||||
|
5. 点击带打开文件/文件夹图标的 `Load a new program`。
|
||||||
|
6. 选择 `linuxcnc/nc_files` 下的 `.ngc` 文件,点击底部绿色对勾。
|
||||||
|
7. 检查顶部路径和预览轨迹,再使用 Run;不要把加载误认为已经执行。
|
||||||
|
|
||||||
|
## 证据文件
|
||||||
|
|
||||||
|
- `gmoccapy-important-changes.png`:用户原始提示截图。
|
||||||
|
- `gmoccapy-before-load.png`:未加载程序。
|
||||||
|
- `gmoccapy-after-popup-close.png`:关闭提示后的主界面。
|
||||||
|
- `gmoccapy-load-file-page.png`:实际文件选择页。
|
||||||
|
- `gmoccapy-program-loaded.png`:加载后的程序和轨迹。
|
||||||
|
- `gmoccapy-file-inventory.json`:逐文件 SHA-256 清单。
|
||||||
BIN
doc-rtcp/gmoccapy-before-load.png
Normal file
|
After Width: | Height: | Size: 282 KiB |
323
doc-rtcp/gmoccapy-control-panel-data-control-flow.md
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
# GMOCCAPY 控制面板功能、数据流与控制流
|
||||||
|
|
||||||
|
更新时间:2026-08-25
|
||||||
|
|
||||||
|
本文是后续分析 GMOCCAPY 控制面板的总入口。它把普通三轴 `gmoccapy.ini` 和五轴 XYZAC RTCP 案例放在同一套模型中,区分 UI 控件、GMOCCAPY Python handler、LinuxCNC command/status、TASK/motion、HAL 和 Vismach 的责任边界。
|
||||||
|
|
||||||
|
## 1. 范围与版本边界
|
||||||
|
|
||||||
|
| 项目 | 当前工作区/运行时 |
|
||||||
|
|---|---|
|
||||||
|
| 仓库源码 | `linuxcnc/VERSION = 2.10.0~pre0` |
|
||||||
|
| 仓库 GMOCCAPY 源码 | `linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py`,`_RELEASE = 3.4.3` |
|
||||||
|
| 实际系统 GUI | `/usr/bin/gmoccapy`,运行时版本 `3.5.2`,系统 LinuxCNC `2.10.0~pre1` |
|
||||||
|
| 普通面板入口 | [`run-gmoccapy.sh`](../run-gmoccapy.sh),默认 `configs/sim/gmoccapy/gmoccapy.ini` |
|
||||||
|
| 五轴 RTCP 入口 | [`run-5axis-rtcp.sh`](../run-5axis-rtcp.sh),默认 XYZAC TRT switchkins |
|
||||||
|
| 发布/仿真边界 | POSIX non-realtime 仿真;不等同于实时硬件验证 |
|
||||||
|
|
||||||
|
截图中的 `Important change(s)` 弹窗来自系统 GMOCCAPY 3.5.2,不存在于仓库 3.4.3 源文件中;两套来源不能混为一个源码版本。详细证据见 [GMOCCAPY 分析报告](gmoccapy-analysis.md)。
|
||||||
|
|
||||||
|
## 2. 总体架构
|
||||||
|
|
||||||
|
```text
|
||||||
|
快捷方式 / linuxcnc -r CASE.ini
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
LinuxCNC 启动器与 INI 解析
|
||||||
|
│
|
||||||
|
├── linuxcncsvr / NML 状态与命令通道
|
||||||
|
├── milltask / RS274NGC interpreter / task mode
|
||||||
|
├── motmod + kinematics / fixed servo cycle
|
||||||
|
├── HAL files / HALUI / simulated devices
|
||||||
|
└── DISPLAY=gmoccapy 或 DISPLAY=axis
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
GMOCCAPY Gtk/Glade 控制面板
|
||||||
|
│
|
||||||
|
┌─────────┴─────────┐
|
||||||
|
│ │
|
||||||
|
用户点击/键盘/HAL linuxcnc.stat/HAL status
|
||||||
|
│ ▲
|
||||||
|
▼ │
|
||||||
|
Python handler ── command ─┴── TASK/motion/HAL feedback
|
||||||
|
│
|
||||||
|
├── 模式/按钮页切换
|
||||||
|
├── 程序加载/预览
|
||||||
|
├── AUTO 运行/暂停/停止/单步
|
||||||
|
├── MDI 命令
|
||||||
|
└── homing、tool、offset、spindle、coolant
|
||||||
|
```
|
||||||
|
|
||||||
|
GMOCCAPY 不是运动学计算器。它负责把操作员动作转换为 LinuxCNC command,并把 LinuxCNC status/HAL status 投影回界面;真正的解释器、轨迹规划、逆解/正解和关节命令由 LinuxCNC runtime 完成。
|
||||||
|
|
||||||
|
## 3. 启动控制流
|
||||||
|
|
||||||
|
### 3.1 快捷方式层
|
||||||
|
|
||||||
|
`run-gmoccapy.sh` 和 `run-5axis-rtcp.sh` 都遵循相同模式:
|
||||||
|
|
||||||
|
1. 计算仓库绝对路径,避免从任意工作目录启动时相对路径漂移。
|
||||||
|
2. 校验 INI、演示程序、LinuxCNC launcher 是否存在。
|
||||||
|
3. 进入 LinuxCNC 根目录。
|
||||||
|
4. 执行 `/usr/bin/linuxcnc -r CASE.ini`,保留终端诊断。
|
||||||
|
|
||||||
|
五轴快捷方式额外检查 RTCP 子程序中确实存在 `M428`,并支持 `xyzac-trt.ini` 与 `xyzbc-trt.ini` 两个变体。
|
||||||
|
|
||||||
|
### 3.2 INI 到进程
|
||||||
|
|
||||||
|
以普通 GMOCCAPY 为例:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[DISPLAY] DISPLAY=gmoccapy
|
||||||
|
[TASK] TASK=milltask
|
||||||
|
[HAL] core_sim.hal + spindle_sim.hal + simulated_home.hal
|
||||||
|
[EMCIO] TOOL_TABLE=tool.tbl
|
||||||
|
[RS274NGC] PARAMETER_FILE=sim.var, SUBROUTINE_PATH=macros
|
||||||
|
[KINS] trivkins coordinates=xyz
|
||||||
|
```
|
||||||
|
|
||||||
|
启动器随后创建 `linuxcncsvr`、`milltask`、`halui` 和 GMOCCAPY。GMOCCAPY 进程通过 `-ini` 得到同一个 INI 路径,不能自行替代 TASK/motion/HAL。
|
||||||
|
|
||||||
|
### 3.3 GMOCCAPY 初始化顺序
|
||||||
|
|
||||||
|
仓库源码 `gmoccapy.py` 的主初始化顺序为:
|
||||||
|
|
||||||
|
1. 解析 `-user_mode`、`-logo` 参数(约 `243-270` 行)。
|
||||||
|
2. 读取 INI(`_get_ini_data()`,约 `272-275` 行)。
|
||||||
|
3. 读取偏好文件(`_get_pref_data()`)。
|
||||||
|
4. 动态创建 DRO、home、touch-off、jog、macro 等控件(约 `280-305` 行)。
|
||||||
|
5. 建立 GMOCCAPY HAL pins(约 `307` 行)。
|
||||||
|
6. 连接 Glade signals(约 `315-320` 行)。
|
||||||
|
7. 显示主窗口(约 `324-325` 行)。
|
||||||
|
8. 初始化主题、G-code preview、kinematics view、offset/tool editor、keyboard 和文件选择器(约 `327-342` 行)。
|
||||||
|
|
||||||
|
系统 3.5.2 运行时在 GUI 初始化后额外调用 `_startup_message()`,生成版本提示弹窗;它属于安装包新增功能,不属于仓库 3.4.3 初始化链。
|
||||||
|
|
||||||
|
## 4. 控制面板模式和按钮门控
|
||||||
|
|
||||||
|
### 4.1 模式按钮
|
||||||
|
|
||||||
|
Glade 中的主要模式控件:
|
||||||
|
|
||||||
|
| 控件 | 作用 | handler/状态 |
|
||||||
|
|---|---|---|
|
||||||
|
| `rbt_manual` | MANUAL/JOG | `on_rbt_manual_pressed()` 发出 `MODE_MANUAL` |
|
||||||
|
| `rbt_mdi` | MDI | `on_rbt_mdi_pressed()` 发出 `MODE_MDI` |
|
||||||
|
| `rbt_auto` | AUTO | `on_rbt_auto_pressed()` 发出 `MODE_AUTO` |
|
||||||
|
| `tbtn_setup` | 设置页 | 进入设置前强制 MANUAL,并禁用模式按钮 |
|
||||||
|
| `tbtn_user_tabs` | 用户页 | 切换主 notebook 页面 |
|
||||||
|
|
||||||
|
`on_hal_status_mode_auto()` 在收到 HAL status 后将主页面切回 page 0,把按钮 notebook 切到 `_BB_AUTO`,并将 jog notebook 切到 AUTO 页面(约 `2804-2824` 行)。因此“点击 AUTO 图标”与“看到 AUTO 按钮组”是同一个状态转换的两个可观察结果。
|
||||||
|
|
||||||
|
### 4.2 急停、上电、homing 门控
|
||||||
|
|
||||||
|
`on_tbtn_setup_toggled()` 和 HAL status handler 根据以下条件设置按钮敏感度:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ESTOP → manual/mdi/auto 均禁用
|
||||||
|
ON + 未 homed → 只允许 manual
|
||||||
|
ON + all_homed 或 NO_FORCE_HOMING → manual/mdi/auto 全部可用
|
||||||
|
```
|
||||||
|
|
||||||
|
对应仓库源码约为 `3793-3808` 行。未满足条件时,点击 AUTO 会被 `on_hal_status_mode_auto()` 拒绝,显示 `It is not possible to change to Auto Mode at the moment`,并回到 MANUAL。
|
||||||
|
|
||||||
|
这解释了为什么控制面板可能“没有加载按钮”:加载按钮不在 MANUAL 页面,而且 AUTO 页面本身可能因状态门控没有激活。
|
||||||
|
|
||||||
|
## 5. G-code 加载数据流
|
||||||
|
|
||||||
|
### 5.1 控件链
|
||||||
|
|
||||||
|
```text
|
||||||
|
rbt_auto
|
||||||
|
│ on_hal_status_mode_auto()
|
||||||
|
▼
|
||||||
|
hbtb_auto / _BB_AUTO
|
||||||
|
│
|
||||||
|
│ btn_load(图标 img_open,tooltip=Load a new program)
|
||||||
|
│ on_btn_load_clicked()
|
||||||
|
▼
|
||||||
|
_BB_LOAD_FILE + IconFileSelection1
|
||||||
|
│
|
||||||
|
│ 用户选中路径
|
||||||
|
│ btn_select / IconFileSelection1_selected
|
||||||
|
▼
|
||||||
|
hal_action_open.load_file(path)
|
||||||
|
▼
|
||||||
|
LinuxCNC task/interpreter + preview/status
|
||||||
|
▼
|
||||||
|
ntb_preview page 0 + ntb_button _BB_AUTO + stat.file
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 打开文件界面
|
||||||
|
|
||||||
|
`on_btn_load_clicked()`(`gmoccapy.py:5046-5078`)执行:
|
||||||
|
|
||||||
|
1. `ntb_button.set_current_page(_BB_LOAD_FILE)`。
|
||||||
|
2. `ntb_preview.set_current_page(3)`。
|
||||||
|
3. 打开 full-size preview。
|
||||||
|
4. `_show_iconview_tab(True)`。
|
||||||
|
5. 刷新 `IconFileSelection1` 文件列表。
|
||||||
|
6. 暂时禁用 MANUAL、MDI 和设置按钮,避免文件选择过程中切换模式。
|
||||||
|
|
||||||
|
文件选择器在 `_init_IconFileSelection()` 和 `_init_file_to_load()` 中读取 `PROGRAM_PREFIX` 与 `[FILTER] PROGRAM_EXTENSION`。普通案例 `PROGRAM_PREFIX = ../../nc_files/` 最终指向仓库的 `linuxcnc/nc_files`。
|
||||||
|
|
||||||
|
### 5.3 确认文件
|
||||||
|
|
||||||
|
`on_IconFileSelection1_selected()`(`gmoccapy.py:5114-5125`)执行:
|
||||||
|
|
||||||
|
1. 将选中的 `path` 交给 `hal_action_open.load_file(path)`。
|
||||||
|
2. 恢复 preview page 0。
|
||||||
|
3. 关闭 full-size preview 和 icon view。
|
||||||
|
4. 把按钮 notebook 切回 `_BB_AUTO`。
|
||||||
|
5. 重新启用 MANUAL、MDI 和设置按钮。
|
||||||
|
|
||||||
|
这一步是“加载程序”,不是“运行程序”。运行还需要 `btn_run`/`AUTO_RUN`;源码键盘 handler 将 `R` 映射为运行、`P` 映射为暂停、`S` 映射为恢复。
|
||||||
|
|
||||||
|
### 5.4 启动时自动加载
|
||||||
|
|
||||||
|
`on_window1_show()` 会读取偏好项 `open_file`(约 `2920-2924` 行)。如果用户在设置页指定“启动时加载文件”,GMOCCAPY 会在窗口显示后直接调用 `hal_action_open.load_file(file)`。这条路径绕过手动点击文件选择器,但仍通过同一个 LinuxCNC load action。
|
||||||
|
|
||||||
|
## 6. AUTO 运行控制流
|
||||||
|
|
||||||
|
```text
|
||||||
|
btn_run / R
|
||||||
|
▼
|
||||||
|
linuxcnc.command().auto(AUTO_RUN, line)
|
||||||
|
▼
|
||||||
|
milltask / RS274NGC interpreter
|
||||||
|
▼
|
||||||
|
TP queue → motion controller → kinematics → joint commands
|
||||||
|
▼
|
||||||
|
HAL simulated device / spindle / coolant / tool change
|
||||||
|
▼
|
||||||
|
linuxcnc.stat + hal_status
|
||||||
|
▼
|
||||||
|
GMOCCAPY DRO、progress、preview、button sensitivity
|
||||||
|
```
|
||||||
|
|
||||||
|
相关按钮属于 `hbtb_auto`:`btn_load`、`btn_reload`、`btn_run`、`btn_stop`、`tbtn_pause`、`btn_step`、`btn_from_line`、`tbtn_optional_blocks`、`btn_edit`。Glade 负责控件和 signal 绑定,Python handler 负责调用 LinuxCNC command;运动本身不在 GTK handler 中实现。
|
||||||
|
|
||||||
|
## 7. MDI、Jog、Home、Tool 和 Offset 数据流
|
||||||
|
|
||||||
|
### MDI
|
||||||
|
|
||||||
|
`rbt_mdi` 将 task mode 设为 `MODE_MDI`。MDI 输入提交后进入 LinuxCNC interpreter;G-code 解析错误通过 LinuxCNC error channel 返回,再由 GMOCCAPY notification/error handler 显示。
|
||||||
|
|
||||||
|
### Jog
|
||||||
|
|
||||||
|
JOG 按钮由 `_make_jog_button()` 动态生成;轴/关节列表来自 INI 的 `COORDINATES`、kinematics 类型和 joint-axis mapping。Jog 命令仍经 LinuxCNC command 通道,不直接改变 DRO;DRO 的真值来自 status feedback。
|
||||||
|
|
||||||
|
### Home
|
||||||
|
|
||||||
|
Home 按钮最终调用 `command.home(joint_or_axis)`。仿真 HAL 的 `sim_home_switch` 或自定义 home HAL 决定 home switch 状态;`all_homed` HAL status 决定 MDI/AUTO 是否重新开放。
|
||||||
|
|
||||||
|
### Tool/Offset
|
||||||
|
|
||||||
|
工具表来自 `[EMCIO] TOOL_TABLE`,offset page 和 tool editor 通过 LinuxCNC tool data/action 更新。G43/G49/G43.1 的解释器语义由 RS274NGC 负责;GMOCCAPY 只显示和提交工具/offset 操作。系统 3.5.2 的启动提示明确提醒:AUTO 中换刀后的 G43 不能假定由界面自动恢复,应由程序/REMAP 明确处理。
|
||||||
|
|
||||||
|
## 8. 五轴 XYZAC RTCP 控制流
|
||||||
|
|
||||||
|
### 8.1 配置层
|
||||||
|
|
||||||
|
五轴案例 `xyzac-trt.ini` 的关键字段:
|
||||||
|
|
||||||
|
```text
|
||||||
|
[DISPLAY] OPEN_FILE = ./demos/xyzac_switchkins.ngc
|
||||||
|
[RS274NGC] SUBROUTINE_PATH = ./remap_subs
|
||||||
|
[RS274NGC] REMAP = M428 ... ngc=428remap
|
||||||
|
[RS274NGC] REMAP = M429 ... ngc=429remap
|
||||||
|
[RS274NGC] REMAP = M430 ... ngc=430remap
|
||||||
|
[KINS] KINEMATICS = xyzac-trt-kins sparm=identityfirst
|
||||||
|
[TRAJ] COORDINATES = XYZAC
|
||||||
|
[HAL] HALCMD = net ... motion.switchkins-type
|
||||||
|
[HAL] HALCMD = loadusr -W xyzac-trt-gui
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 switchkins 状态
|
||||||
|
|
||||||
|
`xyzac-trt-kins.c` 将 `sparm=identityfirst` 定义为:
|
||||||
|
|
||||||
|
```text
|
||||||
|
switchkins-type 0 → identity kinematics
|
||||||
|
switchkins-type 1 → xyzac TRT kinematics
|
||||||
|
switchkins-type 2 → userk kinematics
|
||||||
|
```
|
||||||
|
|
||||||
|
`trtfuncs.c` 为 XYZAC/XYZBC 实现 forward/inverse mapping,并声明 `tool-offset`、旋转中心和轴 offset HAL pins。XYZAC 的五个关节是 `X Y Z A C`;A 是倾斜台,C 是水平旋转台。
|
||||||
|
|
||||||
|
### 8.3 M428/M429 控制链
|
||||||
|
|
||||||
|
```text
|
||||||
|
G-code M428
|
||||||
|
▼
|
||||||
|
428remap.ngc
|
||||||
|
├── M68 E3 Q1 写 motion.analog-out-03 = 1
|
||||||
|
├── M66 E0 L0 等待同步
|
||||||
|
└── 检查 motion.switchkins-type == 1
|
||||||
|
▼
|
||||||
|
xyzac-trt-kins inverse/forward
|
||||||
|
▼
|
||||||
|
XYZAC joint commands with TCP/RTCP compensation
|
||||||
|
```
|
||||||
|
|
||||||
|
`M429` 对称地写入 `Q0`,切回 identity;`M430` 写入 `Q2`,进入 userk。`helix_ac.ngc` 演示了典型顺序:先用 M429 在 machine coordinates 安全定位,再用 M428 执行带 A/C 姿态变化的螺旋插补,结束后再切回 M429 做返回定位。
|
||||||
|
|
||||||
|
### 8.4 Vismach feedback
|
||||||
|
|
||||||
|
INI 中的 HAL nets 将以下反馈送到 `xyzac-trt-gui`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
joint.0.pos-fb → table-x
|
||||||
|
joint.1.pos-fb → saddle-y
|
||||||
|
joint.2.pos-fb → spindle-z
|
||||||
|
joint.3.pos-fb → tilt-a
|
||||||
|
joint.4.pos-fb → rotate-c
|
||||||
|
motion.tooloffset.z → tool-offset
|
||||||
|
xyzac-trt-kins.y-offset/z-offset → Vismach geometry
|
||||||
|
```
|
||||||
|
|
||||||
|
因此 Vismach 不是运动 authority;它是 joint feedback、tool offset 和 geometry offset 的可视化消费者。
|
||||||
|
|
||||||
|
## 9. 状态真值与可观察结果
|
||||||
|
|
||||||
|
| 状态 | authority | 面板表现 |
|
||||||
|
|---|---|---|
|
||||||
|
| 急停 | TASK/motion state | E-stop active,模式按钮禁用 |
|
||||||
|
| 上电 | TASK state + HAL | Power icon active,允许进一步操作 |
|
||||||
|
| all homed | HAL status / joint state | MDI/AUTO 解除门控 |
|
||||||
|
| 当前模式 | `stat.task_mode` | MANUAL/MDI/AUTO 页面切换 |
|
||||||
|
| 当前文件 | `stat.file` / file-loaded status | 顶部文件名、G-code view、Run gate |
|
||||||
|
| 解释器状态 | `stat.interp_state` | Run/Pause/Step/Stop 敏感度 |
|
||||||
|
| 当前 G/M codes | `stat.gcodes` / `stat.mcodes` | G-Code 信息区域 |
|
||||||
|
| 机床位置 | `stat.position` / joint feedback | DRO 和 Vismach |
|
||||||
|
| kinematics mode | `motion.switchkins-type` | AXIS switchkins 面板/RTCP 模式 |
|
||||||
|
|
||||||
|
任何未来 Web/WASM 控制面板都应消费这些 authority 的 snapshot/event,而不是在 JS/GUI 层镜像一套可写的机床状态。
|
||||||
|
|
||||||
|
## 10. 现有证据与复现
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run-gmoccapy.sh
|
||||||
|
./run-5axis-rtcp.sh
|
||||||
|
./run-5axis-rtcp.sh xyzbc-trt.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
已验证的五轴运行证据:
|
||||||
|
|
||||||
|
- [5axis-rtcp-running.png](5axis-rtcp-running.png)
|
||||||
|
- [5axis-rtcp-shortcut.md](5axis-rtcp-shortcut.md)
|
||||||
|
- `linuxcnc` 状态返回 `file=./demos/xyzac_switchkins.ngc`
|
||||||
|
- AXIS 标题包含 `sim-xyzac-trt-kins (switchkins)`
|
||||||
|
- `xyzac-trt-gui` 进程和五轴 Vismach 预览存在
|
||||||
|
|
||||||
|
## 11. 后续分析顺序
|
||||||
|
|
||||||
|
分析任何新的 GMOCCAPY/五轴控制问题时,按以下顺序取证:
|
||||||
|
|
||||||
|
1. 先记录实际运行时路径、LinuxCNC 版本、GMOCCAPY 版本;不要把系统安装脚本和仓库源码混为一份。
|
||||||
|
2. 读取 INI 的 DISPLAY/TASK/HAL/RS274NGC/KINS/TRAJ/EMCIO 段,建立配置闭包。
|
||||||
|
3. 在 Glade 中找到 widget ID,再在 `gmoccapy.py` 找 signal handler;不要只根据截图猜按钮语义。
|
||||||
|
4. 追踪 handler 发出的 `linuxcnc.command()`,确认 command 进入 TASK/motion/HAL 的路径。
|
||||||
|
5. 用 `linuxcnc.stat()`、HAL pins、日志和 Vismach feedback 交叉验证,不把 DRO 或 preview 当作 authority。
|
||||||
|
6. 五轴问题先确认 kinematics type、M428/M429 状态、tool offset、rotation center/axis offset,再分析轨迹差异。
|
||||||
|
7. 任何缺失的官方 oracle、实时硬件、浏览器或资源输入都保留为明确 blocker,不以 GUI 截图替代完整等价证明。
|
||||||
7427
doc-rtcp/gmoccapy-file-inventory.json
Normal file
18
doc-rtcp/gmoccapy-file-inventory.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# GMOCCAPY 文件清单
|
||||||
|
|
||||||
|
清单范围内共 `1233` 个文件;每项包含相对路径、角色、字节数和 SHA-256。
|
||||||
|
|
||||||
|
| 角色 | 数量 |
|
||||||
|
|---|---:|
|
||||||
|
| `documentation` | 2 |
|
||||||
|
| `documentation_asset` | 27 |
|
||||||
|
| `icon_asset` | 973 |
|
||||||
|
| `implementation` | 28 |
|
||||||
|
| `installed_ui_asset` | 1 |
|
||||||
|
| `simulation_asset` | 16 |
|
||||||
|
| `simulation_ini` | 29 |
|
||||||
|
| `simulation_resource` | 69 |
|
||||||
|
| `simulation_runtime_data` | 64 |
|
||||||
|
| `translation` | 24 |
|
||||||
|
|
||||||
|
完整逐文件数据见 [`gmoccapy-file-inventory.json`](gmoccapy-file-inventory.json)。
|
||||||
BIN
doc-rtcp/gmoccapy-important-changes-v2.png
Normal file
|
After Width: | Height: | Size: 201 KiB |
BIN
doc-rtcp/gmoccapy-important-changes.png
Normal file
|
After Width: | Height: | Size: 197 KiB |
BIN
doc-rtcp/gmoccapy-load-file-page.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
doc-rtcp/gmoccapy-program-loaded.png
Normal file
|
After Width: | Height: | Size: 295 KiB |
60
doc-rtcp/xyzac-trt-autonomous-execution-prompt.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# `nextTask-rtcp.md` 持续执行提示词
|
||||||
|
|
||||||
|
复制下面的提示词即可从五轴 XYZAC TRT Web/WASM 轨道持续执行:
|
||||||
|
|
||||||
|
```text
|
||||||
|
你是本仓库的 XYZAC TRT Web/WASM 源码对标工程代理。请持续推进任务,不要只给计划;每轮都从 nextTask-rtcp.md 当前指针认领并执行,直到任务 verified、明确 blocked 或遇到 Stop 条件。
|
||||||
|
|
||||||
|
1. 先读取 nextTask-rtcp.md 的 Current、Read、Task、Next、Verify、Stop;不要用 nextTask.md 或 nextTask1.md 替代它们。
|
||||||
|
2. 按 Task.direct_inputs 读取当前任务的直接源码、配置和报告;不要批量加载聊天记录、完整日志或无关 coverage 文件。
|
||||||
|
3. 开始前运行:
|
||||||
|
python3 scripts/validate_baseline.py
|
||||||
|
python3 scripts/validate_download_index.py
|
||||||
|
python3 scripts/validate_task_pointer.py nextTask-rtcp.md --lock context-lock.json
|
||||||
|
4. 确认 task_id、context_id、active_case、直接输入、required_outputs 和完成报告;当前案例固定为:
|
||||||
|
linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini
|
||||||
|
5. 将当前 Task.status 从 queued 改为 in_progress 前,先读取:
|
||||||
|
doc-rtcp/xyzac-trt-control-panel-web-wasm-design.md
|
||||||
|
doc-rtcp/xyzac-trt-web-wasm-implementation-plan.md
|
||||||
|
doc-rtcp/xyzac-trt-web-wasm-source-parity-contract.md
|
||||||
|
6. 严格遵守源码唯一权威:所有 task、interpreter、TP、motion、XYZAC forward/inverse、RTCP、HAL、IO、tool、preview、recovery 和 state hash 必须来自 LinuxCNC WASM/Worker;React、JS、Three.js 只能输入、投影和显示。
|
||||||
|
7. 对每个功能建立 source behavior slice,必须保留 source path、symbol、source hash、输入、native command、WASM command、gate、authoritative output、错误、恢复和 Web control id。
|
||||||
|
8. 不得使用 JS G-code preview、JS 姿态/RTCP 补偿、浏览器墙钟 tick、主线程 OPFS 写入、直接 WASM 指针、mock runtime 或宿主 LinuxCNC fallback 冒充 WASM 通过。
|
||||||
|
9. 每项实现先写失败边界和专项测试,再做最小垂直切片;优先复用现有 protocol.v1、WorkerClient、AxisOperationController、PanelSnapshotV1、WASM ABI 和固定 tick runtime。
|
||||||
|
10. 五轴必须特别验证:
|
||||||
|
- XYZAC 坐标、五 joints、A/C 角度单位和 limits;
|
||||||
|
- xyzac-trt-kins forward/inverse、tool offset、y/z offset、rotation point;
|
||||||
|
- M428/M429/M430 remap、HAL_PIN_VARS、motion.switchkins-type 和状态确认;
|
||||||
|
- helix/arc/姿态变化、singularity、non-finite、limit、following error;
|
||||||
|
- Vismach pose/joints/toolpath 只消费 authoritative snapshot;
|
||||||
|
- pause/resume/abort/restart/checkpoint/recovery 不重复 segment、M-code 或 IO/tool side effect。
|
||||||
|
11. 所有有副作用命令必须有 operation_id、seq、ledger prepare/ack/result/commit/reconcile;超时不能盲目重发,必须查询 Worker ledger 或 authoritative snapshot。
|
||||||
|
12. 运行任务专属测试、真实 WASM Worker/session、native 对照、React typecheck/build、Three.js canvas 检查和报告 self-hash。命令不存在时先创建任务产物或明确记录 blocked,不能写成已通过。
|
||||||
|
13. 报告必须包含 context_id、case_path、输入/输出 hash、测试命令、normal/boundary/error/recovery、state hash、command trace、tick trace、first divergent tick、failure samples、known blockers 和 report_sha256。
|
||||||
|
14. 失败样本必须保留并分类为 SOURCE_BLOCKED、HOST_EXCLUDED、REAL-EXCLUDED、BLOCKED_RESOURCE、BLOCKED_HAL、BLOCKED_INI、BLOCKED_STARTUP 或具体 Worker/Task/motion/kinematics pending;不得删失败、降级检查或把 blocker 改名为通过。
|
||||||
|
15. 只有当前任务的专项测试、native/WASM/Worker 证据、完整回归和报告 self-hash 全部通过,才能把 Task.status 改为 verified;否则改为 blocked 并保留证据。
|
||||||
|
16. 当前任务 verified 后,更新 nextTask-rtcp.md 的 Current、Next、Verify、Stop 和 completion evidence;只有依赖满足的下一个任务才可写为 queued。不要修改 nextTask.md、nextTask1.md、context-lock、ABI、protocol、schema、许可证决定或 release gate。
|
||||||
|
17. 不要在 verified + queued 状态停下;若 Next 依赖满足,立即认领下一个任务并继续执行。若外部源码、oracle、浏览器、OPFS、实时硬件、owner 输入或批准变更缺失,则保持 blocked,不自动重试同一阻塞。
|
||||||
|
18. 每隔约 30 秒发送简短进度更新,说明当前 task_id、已完成检查、正在执行的测试和 blocker;不要输出无关长日志。
|
||||||
|
19. 只有 nextTask-rtcp.md 明确 next_task: none 且 local continuation verified,或当前任务满足 Stop/外部阻塞条件时才暂停。selected-case verified 不得写成全 LinuxCNC parity,release_status 继续保持 STOP_RELEASE,直到源码、WASM authority、canonical parity、recovery、browser、oracle 和其它正式门禁全部有证据。
|
||||||
|
|
||||||
|
现在立即读取 nextTask-rtcp.md,并从当前 Task 开始执行。
|
||||||
|
```
|
||||||
|
|
||||||
|
## 推荐调用语句
|
||||||
|
|
||||||
|
最短调用:
|
||||||
|
|
||||||
|
```text
|
||||||
|
按 nextTask-rtcp.md 接续执行,严格使用 doc-rtcp/xyzac-trt-autonomous-execution-prompt.md 的规则。
|
||||||
|
```
|
||||||
|
|
||||||
|
更强约束调用:
|
||||||
|
|
||||||
|
```text
|
||||||
|
持续执行 nextTask-rtcp.md:先验证基线和指针,认领当前任务,按源码 slice 实现 LinuxCNC WASM 唯一权威闭环,运行 native/WASM/Worker/React/Three.js 证据,更新报告和指针;不要修改 nextTask.md、nextTask1.md、context lock、ABI、protocol、schema 或 release gate,也不要在 verified+queued 状态停下。
|
||||||
|
```
|
||||||
|
|
||||||
|
## 当前起点
|
||||||
|
|
||||||
|
当前指针的任务是 `SRC-00`,目标是建立 AXIS/XYZAC source slice inventory 和 WASM authority contract;下一任务是 `WASM-00`。读取和执行状态以根目录的 [`nextTask-rtcp.md`](../nextTask-rtcp.md) 为准。
|
||||||
529
doc-rtcp/xyzac-trt-control-panel-web-wasm-design.md
Normal file
@@ -0,0 +1,529 @@
|
|||||||
|
# `xyzac-trt.ini` 控制面板、数据流、控制约束与 Web/WASM 设计基础
|
||||||
|
|
||||||
|
更新时间:2026-08-25
|
||||||
|
|
||||||
|
本文专门分析:
|
||||||
|
|
||||||
|
```text
|
||||||
|
linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
目标不是复刻一个静态 AXIS 外观,而是为 Web/WASM 控制台建立可验证的功能、数据、控制和权限模型。所有“按钮是否可点”的结论都必须来自 LinuxCNC authoritative task/motion/HAL 状态;React/浏览器只能投影状态并发出带 gate 的命令。
|
||||||
|
|
||||||
|
相关运行入口和基础说明:
|
||||||
|
|
||||||
|
- [`run-5axis-rtcp.sh`](../run-5axis-rtcp.sh)
|
||||||
|
- [`5axis-rtcp-shortcut.md`](5axis-rtcp-shortcut.md)
|
||||||
|
- [`gmoccapy-control-panel-data-control-flow.md`](gmoccapy-control-panel-data-control-flow.md)
|
||||||
|
- [`5axis-rtcp-running.png`](5axis-rtcp-running.png)
|
||||||
|
|
||||||
|
## 1. 案例边界
|
||||||
|
|
||||||
|
### 1.1 入口配置
|
||||||
|
|
||||||
|
`xyzac-trt.ini` 的关键配置如下:
|
||||||
|
|
||||||
|
| INI 段 | 字段 | 功能 |
|
||||||
|
|---|---|---|
|
||||||
|
| `[EMC]` | `MACHINE = sim-xyzac-trt-kins (switchkins)` | 案例名称和 switchkins 语义 |
|
||||||
|
| `[DISPLAY]` | `DISPLAY = axis` | 主控制面板是 AXIS/Tk,不是 GMOCCAPY |
|
||||||
|
| `[DISPLAY]` | `OPEN_FILE = ./demos/xyzac_switchkins.ngc` | 启动后预置的 G-code 文件 |
|
||||||
|
| `[DISPLAY]` | `PYVCP = ./xyzac-trt.xml` | 右侧 SWITCHKINS/vismach-clear 面板 |
|
||||||
|
| `[DISPLAY]` | `JOG_AXES = XYZC` | 手动点动显示 X/Y/Z/C;A 仍属于五轴运动学 |
|
||||||
|
| `[DISPLAY]` | `PROGRAM_PREFIX = ../../nc_files` | 默认程序搜索目录 |
|
||||||
|
| `[DISPLAY]` | `POSITION_OFFSET = RELATIVE` | DRO 默认显示相对坐标 |
|
||||||
|
| `[DISPLAY]` | `POSITION_FEEDBACK = ACTUAL` | DRO 使用 actual position |
|
||||||
|
| `[RS274NGC]` | `SUBROUTINE_PATH = ./remap_subs` | M428/M429/M430 和 helix 子程序路径 |
|
||||||
|
| `[RS274NGC]` | `HAL_PIN_VARS = 1` | 允许 remap 访问 HAL pin |
|
||||||
|
| `[RS274NGC]` | `REMAP = M428/M429/M430` | kinematics 状态切换入口 |
|
||||||
|
| `[RS274NGC]` | `PARAMETER_FILE = xyzac.var` | RS274 参数持久化 |
|
||||||
|
| `[KINS]` | `KINEMATICS = xyzac-trt-kins sparm=identityfirst` | 5 关节 XYZAC,0=identity,1=TRT,2=userk |
|
||||||
|
| `[KINS]` | `JOINTS = 5` | X/Y/Z/A/C 五个 joint |
|
||||||
|
| `[HAL]` | `HALFILE = LIB:basic_sim.tcl` | 生成仿真 motion/HAL/模拟设备 |
|
||||||
|
| `[HAL]` | `POSTGUI_HALFILE = switchkins_postgui.hal` | GUI 创建后连接 PyVCP 控件 |
|
||||||
|
| `[HAL]` | `motion.analog-out-03 => motion.switchkins-type` | remap 写入 switchkins 的控制通道 |
|
||||||
|
| `[HAL]` | `loadusr -W xyzac-trt-gui` | 独立 Vismach 五轴模型进程 |
|
||||||
|
| `[TRAJ]` | `COORDINATES = XYZAC` | 解释器和轨迹规划的笛卡尔坐标集合 |
|
||||||
|
| `[EMCIO]` | `TOOL_TABLE = xyzac-trt.tbl` | 工具长度/直径等数据 |
|
||||||
|
|
||||||
|
### 1.2 三个界面实体
|
||||||
|
|
||||||
|
```text
|
||||||
|
AXIS 主窗口
|
||||||
|
├── Tk 菜单、工具栏、Manual Control、MDI、Preview、DRO、G-code view
|
||||||
|
├── PyVCP xyzac-trt.xml(右侧 SWITCHKINS 面板)
|
||||||
|
└── AXIS 内部 Python/Tk command + linuxcnc.stat 状态轮询
|
||||||
|
|
||||||
|
xyzac-trt-gui 进程
|
||||||
|
└── Python vismach 模型,仅消费 HAL joint feedback/tool-offset/offset
|
||||||
|
|
||||||
|
LinuxCNC runtime
|
||||||
|
├── linuxcncsvr / NML
|
||||||
|
├── milltask / RS274NGC interpreter
|
||||||
|
├── motmod / TP / switchkins / xyzac-trt-kins
|
||||||
|
└── HAL / simulated home / spindle / tool-change / PyVCP pins
|
||||||
|
```
|
||||||
|
|
||||||
|
AXIS 是控制和状态投影者,PyVCP 是 HAL 控件,Vismach 是可视化消费者;三者都不是独立的运动 authority。
|
||||||
|
|
||||||
|
## 2. 启动与初始化数据流
|
||||||
|
|
||||||
|
```text
|
||||||
|
run-5axis-rtcp.sh
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
/usr/bin/linuxcnc -r xyzac-trt.ini
|
||||||
|
│
|
||||||
|
├── 读取 [DISPLAY]/[TASK]/[HAL]/[KINS]/[TRAJ]
|
||||||
|
├── 启动 linuxcncsvr
|
||||||
|
├── 启动 milltask
|
||||||
|
├── 运行 basic_sim.tcl 生成 HAL
|
||||||
|
├── loadusr xyzac-trt-gui
|
||||||
|
├── 执行 switchkins_postgui.hal
|
||||||
|
└── 启动 /usr/bin/axis -ini xyzac-trt.ini
|
||||||
|
│
|
||||||
|
├── open_file_guts(OPEN_FILE)
|
||||||
|
├── 创建 Tk/AXIS widgets
|
||||||
|
├── 读取 linuxcnc.stat
|
||||||
|
└── 周期刷新 DRO/preview/gates
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.1 AXIS 初始化的责任
|
||||||
|
|
||||||
|
主要来源:
|
||||||
|
|
||||||
|
```text
|
||||||
|
linuxcnc/src/emc/usr_intf/axis/scripts/axis.py
|
||||||
|
```
|
||||||
|
|
||||||
|
核心职责:
|
||||||
|
|
||||||
|
1. 解析 INI 和显示选项。
|
||||||
|
2. 初始化 `linuxcnc.command()`、`linuxcnc.stat()` 和 HAL component。
|
||||||
|
3. 根据 `COORDINATES=XYZAC` 建立 axis/joint 映射。
|
||||||
|
4. 读取 `OPEN_FILE`,通过 `open_file_guts()` 和 `c.program_open()` 载入程序。
|
||||||
|
5. 读取 G-code,运行 canon/preview 生成程序轨迹。
|
||||||
|
6. 周期 `s.poll()`,将 position、joint position、homed、limit、tool、G/M code、task/interpreter state 投影到 Tk 变量。
|
||||||
|
7. 根据 `running()`、`manual_ok()`、`all_homed()` 和 task/motion 状态决定按钮是否响应。
|
||||||
|
|
||||||
|
### 2.2 Vismach 初始化的责任
|
||||||
|
|
||||||
|
`linuxcnc/src/hal/user_comps/vismach/xyzac-trt-gui.py` 创建 HAL component:
|
||||||
|
|
||||||
|
```text
|
||||||
|
xyzac-trt-gui.table-x HAL_FLOAT IN
|
||||||
|
xyzac-trt-gui.saddle-y HAL_FLOAT IN
|
||||||
|
xyzac-trt-gui.spindle-z HAL_FLOAT IN
|
||||||
|
xyzac-trt-gui.tilt-a HAL_FLOAT IN
|
||||||
|
xyzac-trt-gui.rotate-c HAL_FLOAT IN
|
||||||
|
xyzac-trt-gui.y-offset HAL_FLOAT IN
|
||||||
|
xyzac-trt-gui.z-offset HAL_FLOAT IN
|
||||||
|
xyzac-trt-gui.tool-offset HAL_FLOAT IN
|
||||||
|
```
|
||||||
|
|
||||||
|
模型通过 `HalTranslate`/`HalRotate` 将这些输入转换为 OpenGL 模型姿态。它不发 `program_run`、`jog`、`home` 或 kinematics 命令。
|
||||||
|
|
||||||
|
## 3. AXIS 主控制面板功能
|
||||||
|
|
||||||
|
### 3.1 顶部菜单和工具栏
|
||||||
|
|
||||||
|
AXIS/Tk 主面板通常提供:
|
||||||
|
|
||||||
|
| 类别 | 典型动作 | LinuxCNC command/效果 |
|
||||||
|
|---|---|---|
|
||||||
|
| 文件 | Open | `program_open(path)`,更新 loaded file 和 preview |
|
||||||
|
| AUTO 按钮组 | `hbtb_auto` / `btn_load` | AXIS 的 Open/Load 文件入口;只在程序未运行时打开文件选择器 |
|
||||||
|
| 文件 | Reload | 重新读取当前文件;运行中拒绝 |
|
||||||
|
| 文件 | Recent files | 偏好文件保存最近路径,最多 10 项 |
|
||||||
|
| 文件 | Edit | 调用 `EDITOR`/外部编辑器;不改变 runtime authority |
|
||||||
|
| 程序 | Run | `c.mode(MODE_AUTO)` + `c.auto(AUTO_RUN, start_line)` |
|
||||||
|
| 程序 | Step | `c.auto(AUTO_STEP)` |
|
||||||
|
| 程序 | Pause | `c.auto(AUTO_PAUSE)` |
|
||||||
|
| 程序 | Resume | `c.auto(AUTO_RESUME)` |
|
||||||
|
| 程序 | Stop | `c.abort()`,同时设置 GUI abort 状态 |
|
||||||
|
| 程序 | Run from line | 设置 first line 后再 AUTO_RUN,风险高于普通 Run |
|
||||||
|
| 机床 | Power | `c.state(STATE_ON/OFF)` |
|
||||||
|
| 机床 | E-stop | `c.state(STATE_ESTOP/STATE_ESTOP_RESET)` 或硬件链路 |
|
||||||
|
| 机床 | Home/Unhome | `c.home(joint)` / `c.unhome(joint)` |
|
||||||
|
| 机床 | Spindle | `c.spindle(...)` 或 MDI/halui |
|
||||||
|
| 机床 | Feed/rapid/spindle override | `c.feedrate()`/`c.maxvel()` 或相应 HAL/command |
|
||||||
|
| 辅助 | HAL show/tooleditor | 外部工具或 tool data action |
|
||||||
|
| 视图 | Perspective/X/Y/Z/clear plot | 只改变 view/preview,不改变运动状态 |
|
||||||
|
|
||||||
|
菜单图标不是 authority。每个动作必须以 command result 和下一次 status snapshot 确认。
|
||||||
|
|
||||||
|
### 3.2 Manual Control
|
||||||
|
|
||||||
|
Manual 页包含:
|
||||||
|
|
||||||
|
- X/Y/Z/A/C 轴选择;`JOG_AXES=XYZC` 会影响默认可点动轴列表。
|
||||||
|
- Continuous/incremental jog 选择。
|
||||||
|
- jog speed、angular jog speed。
|
||||||
|
- Home All、单轴 Home、Unhome。
|
||||||
|
- Touch Off、Tool Touch Off。
|
||||||
|
- feed/rapid/spindle override。
|
||||||
|
- spindle start/stop/reverse。
|
||||||
|
|
||||||
|
AXIS 的 `manual_ok()` 条件:
|
||||||
|
|
||||||
|
```text
|
||||||
|
task_state == STATE_ON
|
||||||
|
并且当前没有 AUTO/MDI 正在运行
|
||||||
|
并且 interp_state == INTERP_IDLE
|
||||||
|
或当前为 MDI 且 queued_mdi_commands < max_queued_mdi_commands
|
||||||
|
```
|
||||||
|
|
||||||
|
`jog_on()` 还要满足当前 tab 是 manual、轴/joint 映射有效、没有 active limit 阻止动作。Web/WASM 应把这些结果作为 gate reason 返回,不仅仅把 HTML button 设为 disabled。
|
||||||
|
|
||||||
|
### 3.3 MDI
|
||||||
|
|
||||||
|
MDI 入口通过 `ensure_mdi()`:
|
||||||
|
|
||||||
|
1. 检查 `manual_ok()`。
|
||||||
|
2. 关闭 teleop 或切换到合适 motion mode。
|
||||||
|
3. `c.mode(MODE_MDI)`。
|
||||||
|
4. 用户输入字符串后调用 `c.mdi(command)`。
|
||||||
|
5. interpreter 执行后由 `stat.interp_state`、`queued_mdi_commands`、位置和错误通道反馈。
|
||||||
|
|
||||||
|
MDI 不是任意字符串的本地脚本执行器。Web/WASM 必须保留 G-code parser、能力边界、长度限制、错误码和 operation id。
|
||||||
|
|
||||||
|
### 3.4 Preview/DRO/G-code view
|
||||||
|
|
||||||
|
AXIS 周期更新同时刷新:
|
||||||
|
|
||||||
|
- `stat.actual_position` / `joint_actual_position` → DRO。
|
||||||
|
- `stat.g5x_offset` / `g92_offset` / `rotation_xy` / `tool_offset` → 坐标投影。
|
||||||
|
- `stat.gcodes` / `stat.mcodes` / `settings` → G-code 信息。
|
||||||
|
- canon/preview → 静态程序路径。
|
||||||
|
- `motion_line` / `running_line` → 当前行高亮。
|
||||||
|
- `joint.pos-fb` → Vismach 模型。
|
||||||
|
|
||||||
|
静态 preview 和 realtime/live plot 必须分开建模;Web 端不能用 JS 重新解析一份程序来代替 WASM canonical motion。
|
||||||
|
|
||||||
|
## 4. `xyzac-trt.xml` SWITCHKINS 面板
|
||||||
|
|
||||||
|
### 4.1 控件定义
|
||||||
|
|
||||||
|
PyVCP XML 定义:
|
||||||
|
|
||||||
|
| 控件文本 | HAL pin | 目标 |
|
||||||
|
|---|---|---|
|
||||||
|
| `IDENTITY` | `pyvcp.type0-button` | 触发 `halui.mdi-command-00` → M429 |
|
||||||
|
| `TCP:XYZAC` | `pyvcp.type1-button` | 触发 `halui.mdi-command-01` → M428 |
|
||||||
|
| `userk` | `pyvcp.type2-button` | 触发 `halui.mdi-command-02` → M430 |
|
||||||
|
| 多标签 | `kinstype.is-0/1/2` | 显示当前 switchkins type |
|
||||||
|
| `vismach-clear` | `pyvcp.vismach-clear` | 连接 `vismach.plotclear` |
|
||||||
|
|
||||||
|
`switchkins_postgui.hal` 将按钮连接到 HALUI MDI command:
|
||||||
|
|
||||||
|
```text
|
||||||
|
pyvcp.type0-button → halui.mdi-command-00
|
||||||
|
pyvcp.type1-button → halui.mdi-command-01
|
||||||
|
pyvcp.type2-button → halui.mdi-command-02
|
||||||
|
```
|
||||||
|
|
||||||
|
这意味着浏览器端的“TCP:XYZAC”按钮不能直接写 `motion.switchkins-type`,应走同等的受控 command/remap 路径,并等待确认状态。
|
||||||
|
|
||||||
|
### 4.2 switchkins 状态模型
|
||||||
|
|
||||||
|
```text
|
||||||
|
type 0 / M429 → identity
|
||||||
|
type 1 / M428 → xyzac TRT TCP/RTCP
|
||||||
|
type 2 / M430 → userk
|
||||||
|
```
|
||||||
|
|
||||||
|
`xyzac-trt-kins.c` 在 `sparm=identityfirst` 下将 type 0 绑定 identity,type 1 绑定 `trtKinematicsSetup + xyzacKinematicsForward/Inverse`,type 2 绑定 userk。配置注释和实现必须同时读取,不能只依据面板标签。
|
||||||
|
|
||||||
|
## 5. 五轴 RTCP 控制流
|
||||||
|
|
||||||
|
### 5.1 M428/M429 remap
|
||||||
|
|
||||||
|
`428remap.ngc` 的关键操作:
|
||||||
|
|
||||||
|
```text
|
||||||
|
验证 motion.switchkins-type HAL pin 存在
|
||||||
|
M68 E3 Q1 写 motion.analog-out-03 = 1
|
||||||
|
M66 E0 L0 同步等待
|
||||||
|
读取 motion.switchkins-type,必须等于 1
|
||||||
|
```
|
||||||
|
|
||||||
|
`429remap.ngc` 相同,但写入 `Q0`。失败时输出 debug STOP 并结束子程序;Web/WASM 需要把这些失败变成稳定的错误码,而不是只显示“按钮没有反应”。
|
||||||
|
|
||||||
|
### 5.2 典型 helix 数据流
|
||||||
|
|
||||||
|
`helix_ac.ngc` 的典型顺序:
|
||||||
|
|
||||||
|
```text
|
||||||
|
M429 identity
|
||||||
|
G0 / G10 L20 机床坐标定位和设置工作偏置
|
||||||
|
M428 XYZAC TRT / TCP
|
||||||
|
G0 A... C... 改变刀轴姿态
|
||||||
|
G2 ... 在 RTCP 运动学下插补
|
||||||
|
M429 切回 identity
|
||||||
|
G0 ... 安全返回
|
||||||
|
M428 如后续仍需 TCP
|
||||||
|
```
|
||||||
|
|
||||||
|
这里的关键不是“同时有 A/C 数字”,而是同一轨迹块由 XYZAC inverse kinematics 将 TCP pose 转换为五个 joint 命令。姿态变化可能导致 X/Y/Z 补偿变化;因此前端必须同时展示笛卡尔 pose、joint positions、active kinematics mode 和 tool offset。
|
||||||
|
|
||||||
|
### 5.3 Vismach 数据流
|
||||||
|
|
||||||
|
```text
|
||||||
|
joint.0.pos-fb → table-x → HalTranslate
|
||||||
|
joint.1.pos-fb → saddle-y → HalTranslate
|
||||||
|
joint.2.pos-fb → spindle-z → HalTranslate
|
||||||
|
joint.3.pos-fb → tilt-a → HalRotate(A)
|
||||||
|
joint.4.pos-fb → rotate-c → HalRotate(C)
|
||||||
|
motion.tooloffset.z → tool-offset → HalTranslate(tool)
|
||||||
|
y/z-offset → rotary geometry placement
|
||||||
|
```
|
||||||
|
|
||||||
|
Vismach 只读 HAL input。Web/WASM 的 Three.js adapter 应消费 authoritative `visual.pose`/`joints`/`toolpath`,而不是在 React 中根据 A/C 自行推算补偿。
|
||||||
|
|
||||||
|
## 6. 按钮互相制约条件
|
||||||
|
|
||||||
|
下表是 AXIS/LinuxCNC 语义的最小可执行门控。`running` 表示 AUTO 或 MDI 且 `interp_state != INTERP_IDLE`。
|
||||||
|
|
||||||
|
| 按钮/动作 | 必须满足 | 典型阻塞原因 |
|
||||||
|
|---|---|---|
|
||||||
|
| E-stop | 始终可用 | transport/session 不可用 |
|
||||||
|
| Power ON/OFF | 非 ESTOP;不在禁止转换状态 | `ESTOP_ACTIVE` |
|
||||||
|
| Reset | machine ON、非 running、无 active limit | `LIMIT_ACTIVE` / `INTERP_RUNNING` |
|
||||||
|
| Manual jog | `STATE_ON`、MANUAL、非 running、未 fault | `MACHINE_OFF` / `MODE_REQUIRED` |
|
||||||
|
| Home all | `manual_ok()`;必要时确认重复 homing | `NOT_ON` / `RUNNING` |
|
||||||
|
| 单轴 Home | `manual_ok()`;非 identity 时通常必须 joint mode;重复坐标字母禁用单轴 home | `JOINT_MODE_REQUIRED` |
|
||||||
|
| MDI | machine ON;队列未满;idle 或当前 MDI | `MDI_QUEUE_FULL` / `INTERP_RUNNING` |
|
||||||
|
| Open file | 解释器 idle;AXIS `open_file_guts` 临时切 MANUAL 后恢复原 mode | `INTERP_RUNNING` |
|
||||||
|
| Load program | machine ON、idle | `MACHINE_OFF` |
|
||||||
|
| Reload | idle 且已有 loaded file | `TASKFILE_MISSING` |
|
||||||
|
| Run | machine ON、idle、已有程序 | `TASKFILE_MISSING` / `INTERP_RUNNING` |
|
||||||
|
| Run from line | Run 条件 + 有明确 highlight line | `HIGHLIGHT_MISSING` |
|
||||||
|
| Step | machine ON 且有程序;进入 AUTO | `TASKFILE_MISSING` |
|
||||||
|
| Pause | machine ON 且 interpreter READING/WAITING | `PAUSE_STATE_GATE` |
|
||||||
|
| Resume | machine ON 且 interpreter PAUSED | `RESUME_STATE_GATE` |
|
||||||
|
| Stop/Abort | machine ON 且 running;abort 可以强制恢复状态 | `STOP_STATE_GATE` |
|
||||||
|
| Touch Off | manual_ok、MANUAL、允许的 motion mode | `JOINT_MODE_REQUIRED` |
|
||||||
|
| Tool Touch Off | Touch Off 条件 + 有当前工具 | `TOOL_MISSING` |
|
||||||
|
| TCP:XYZAC/M428 | remap HAL pin 存在、HAL net 正确、当前程序允许切换 | `MISSING_HAL_PIN` / `WRONG_SWITCHKINS_TYPE` |
|
||||||
|
| Identity/M429 | 同上,切换后必须确认 type=0 | `WRONG_SWITCHKINS_TYPE` |
|
||||||
|
| Vismach clear | 只清显示 plot,不改变 task/motion | 不应改变机床状态 |
|
||||||
|
|
||||||
|
### 6.1 重要状态互斥
|
||||||
|
|
||||||
|
```text
|
||||||
|
ESTOP → 禁止所有运动、模式、MDI、RTCP 切换
|
||||||
|
OFF → 可以复位/上电,禁止 Run/Jog/Home/MDI
|
||||||
|
ON + 未 homed → 允许 Manual/Home,禁止 AUTO/MDI(除非配置 NO_FORCE_HOMING)
|
||||||
|
RUNNING → 禁止 Open/Reload/Edit/MDI/Jog/Home;允许 Pause/Stop/状态观察
|
||||||
|
PAUSED → 允许 Resume/Stop;禁止 MDI/Jog/重新加载
|
||||||
|
RTCP type switch → 应在 interpreter idle、安全位置、无活动轨迹时执行
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. 当前 Web/WASM 的对应实现
|
||||||
|
|
||||||
|
### 7.1 已存在的通用契约
|
||||||
|
|
||||||
|
当前 Web 代码已经提供可复用基础:
|
||||||
|
|
||||||
|
| 文件 | 作用 |
|
||||||
|
|---|---|
|
||||||
|
| `web/app/axis-contract.mjs` | `AXIS_COMMANDS`、状态归一化、纯 gate 函数 |
|
||||||
|
| `web/app/axis-operation-controller.mjs` | operation id、gate 检查、command trace、错误保留 |
|
||||||
|
| `web/react/src/protocol.ts` | `MessageEnvelope`、`SimSnapshotV1`、`PanelSnapshotV1`、`AxisStateView` |
|
||||||
|
| `web/react/src/store.ts` | Worker message → authoritative snapshot/projected UI state |
|
||||||
|
| `web/react/src/worker-client.ts` | Dedicated Worker transport、seq、session/context、错误处理 |
|
||||||
|
| `web/react/src/visual-workflow.ts` | AXIS/GMOCCAPY/Vismach 的 visual source 分类 |
|
||||||
|
|
||||||
|
现有 `AXIS_COMMANDS` 已覆盖 `PROGRAM_LOAD/RUN/STEP/PAUSE/RESUME/ABORT/RESTART`、`MDI_EXECUTE`、`JOG_START/STOP`、`HOME`、`ESTOP`、`RESET`、override、spindle、coolant、tool 和 probe。
|
||||||
|
|
||||||
|
### 7.2 `xyzac-trt` 需要的新增协议字段
|
||||||
|
|
||||||
|
建议在现有 `PanelSnapshotV1` 上 additive 扩展,不破坏 `sim_task.v1`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"configuration": {
|
||||||
|
"case_path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||||
|
"coordinates": ["X", "Y", "Z", "A", "C"],
|
||||||
|
"kinematics": "xyzac-trt-kins",
|
||||||
|
"topology": "table-tilting-A/table-rotary-C",
|
||||||
|
"joint_count": 5,
|
||||||
|
"program_prefix": "../../nc_files"
|
||||||
|
},
|
||||||
|
"kinematics": {
|
||||||
|
"family": "switchkins",
|
||||||
|
"active_type": 1,
|
||||||
|
"active_name": "xyzac-trt",
|
||||||
|
"available": [
|
||||||
|
{"type": 0, "name": "identity", "command": "M429"},
|
||||||
|
{"type": 1, "name": "xyzac-trt", "command": "M428"},
|
||||||
|
{"type": 2, "name": "userk", "command": "M430"}
|
||||||
|
],
|
||||||
|
"transition": "idle_only"
|
||||||
|
},
|
||||||
|
"motion": {
|
||||||
|
"axes": {"x": 0, "y": 0, "z": 10, "a": 0, "c": 0},
|
||||||
|
"joints": [0, 0, 10, 0, 0],
|
||||||
|
"tool_offset": {"z": 0},
|
||||||
|
"rotation_center": {"x": 0, "y": 0, "z": 0},
|
||||||
|
"axis_offsets": {"y": 20, "z": 10}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
字段的 authoritative 来源必须来自 WASM kinematics/motion snapshot 或受控 native oracle;不能由 A/C slider 在浏览器中自行补偿。
|
||||||
|
|
||||||
|
### 7.3 命令映射建议
|
||||||
|
|
||||||
|
| Web action | protocol command | xyzac 行为 |
|
||||||
|
|---|---|---|
|
||||||
|
| `PROGRAM_LOAD` | `PROGRAM_LOAD` | 载入程序、canonical preview、重置 active segment |
|
||||||
|
| `PROGRAM_RUN` | `PROGRAM_RUN` | 进入 AUTO,固定 tick 推进 interpreter/TP |
|
||||||
|
| `PROGRAM_PAUSE` | `PROGRAM_PAUSE` | 保留当前 segment/kinematics type |
|
||||||
|
| `PROGRAM_RESUME` | `PROGRAM_RESUME` | 继续同一 program state |
|
||||||
|
| `PROGRAM_ABORT` | `PROGRAM_ABORT` | 停止并返回稳定 STOPPED/IDLE |
|
||||||
|
| `MDI_EXECUTE` | `MDI_EXECUTE` | 用于 `M428`/`M429`/`M430`,必须有 idle gate |
|
||||||
|
| `KINEMATICS_SELECT` | 建议新增 | payload `{type:0\|1\|2}`,内部走 remap/受控 HAL,不直接写 pin |
|
||||||
|
| `JOG_START` | `JOG_START` | 仅允许合法 axis/joint;XYZAC 的 A/C 速度单位独立校验 |
|
||||||
|
| `HOME` | `HOME` | 返回每个 joint homed 状态和错误 |
|
||||||
|
| `VISMACH_CLEAR` | `VISUAL_CLEAR` | 只清可视化轨迹,不清 task/motion/program |
|
||||||
|
|
||||||
|
### 7.3.1 当前 Web 实现的五轴缺口
|
||||||
|
|
||||||
|
现有通用 AXIS contract 已有大量三轴/通用动作,但对本案例仍有明确缺口,不能把“按钮存在”当成已实现:
|
||||||
|
|
||||||
|
| 缺口 | 当前状态 | 五轴实现要求 |
|
||||||
|
|---|---|---|
|
||||||
|
| `KINEMATICS_SELECT` | `AXIS_COMMANDS` 尚未定义 | 新增受 gate 的 type 0/1/2 命令,禁止前端直接写 HAL |
|
||||||
|
| `POWER` | `axisGate()` 有 `power` 分支,但 `AXIS_COMMANDS` 没有对应 action | 补齐 command map 和 Worker handler,保持 OFF/ESTOP 语义 |
|
||||||
|
| `VISUAL_CLEAR` | 原生 PyVCP 有 `vismach-clear`,通用 Web command 尚未固定 | 作为 visual-only command,不能改 program/motion hash |
|
||||||
|
| A/C jog | 当前 React 示例轴选择主要是 X/Y/Z | 增加 A/C angular units、limits、jog speed 和 joint/teleop gate |
|
||||||
|
| XYZAC pose | `PanelSnapshotV1.visual.pose` 目前是通用 Record | 增加明确 `a/c`、joint vector、kinematics type 和 tool-offset 字段 |
|
||||||
|
| rotation centers | 当前通用 snapshot 没有 TRT rotation-point 完整字段 | 将 x/y/z rotation point、y/z offset 纳入 configuration/authoritative |
|
||||||
|
| switchkins recovery | 通用 recovery 有 task/program,但没有 active kinematics mode | checkpoint 必须保存 type、remap transition、source line、joint/pose hash |
|
||||||
|
|
||||||
|
### 7.4 Worker snapshot 到 React
|
||||||
|
|
||||||
|
```text
|
||||||
|
WASM/native runtime
|
||||||
|
└── task + interpreter + motion + joints + kinematics + HAL + devices
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
protocol.v1 STATE_SNAPSHOT / COMMAND_RESULT
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
store.ts projection()
|
||||||
|
├── axis_state
|
||||||
|
├── gates
|
||||||
|
├── task/program
|
||||||
|
├── motion.axes / motion.joints
|
||||||
|
├── authoritative.planner / authoritative.kinematics
|
||||||
|
└── visual.pose / visual.toolpath
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
React DRO / controls / Three.js Vismach
|
||||||
|
```
|
||||||
|
|
||||||
|
React 控件的 `disabled` 只是 projection;真正 gate 必须在 Worker/runtime 再检查一次。`AxisOperationController` 已提供 operation id、拒绝原因和 trace 结构,五轴切换应复用此机制。
|
||||||
|
|
||||||
|
## 8. Web/WASM 不应直接照搬的部分
|
||||||
|
|
||||||
|
| 原生机制 | Web/WASM 处理 |
|
||||||
|
|---|---|
|
||||||
|
| Tk widget/菜单 | React button/menu/keyboard command,但保留 source control id 和 tooltip 语义 |
|
||||||
|
| LinuxCNC NML | Dedicated Worker protocol envelope + session/seq/operation_id |
|
||||||
|
| HAL pin `motion.switchkins-type` | Worker-owned runtime capability;浏览器不能任意写 HAL pin |
|
||||||
|
| `loadusr -W xyzac-trt-gui` | Worker/Three.js Vismach adapter;visual-only,不是 authority |
|
||||||
|
| `OPEN_FILE` | Worker workspace resolver + exact path/resource closure |
|
||||||
|
| `G-code preview` | WASM canonical interpreter/TP segments;不能用 JS preview 冒充 |
|
||||||
|
| `linuxcnc.stat()` polling | fixed-tick STATE_SNAPSHOT/EVENT,带 state_hash |
|
||||||
|
| M428/M429 remap | WASM remap/interpreter capability,记录切换、错误和首个 divergent tick |
|
||||||
|
| POSIX realtime/HAL hardware | SIM-DEVICE 或 REAL-EXCLUDED,不能静默当成 Web 等价 |
|
||||||
|
|
||||||
|
## 9. 推荐的 Web 端组件拆分
|
||||||
|
|
||||||
|
```text
|
||||||
|
XyzacTrtCaseResolver
|
||||||
|
├── exact ini/hal/remap/resource resolver
|
||||||
|
└── startup diagnostics
|
||||||
|
|
||||||
|
XyzacTrtControlStore
|
||||||
|
├── axis_state / task / interp / homed / limits
|
||||||
|
├── kinematics.active_type
|
||||||
|
├── gates and gate reasons
|
||||||
|
└── command trace
|
||||||
|
|
||||||
|
XyzacTrtOperationController
|
||||||
|
├── PROGRAM_LOAD/RUN/PAUSE/RESUME/ABORT
|
||||||
|
├── MDI M428/M429/M430
|
||||||
|
├── JOG/HOME/override
|
||||||
|
└── transition checks: idle, no fault, valid capability
|
||||||
|
|
||||||
|
XyzacTrtPanel
|
||||||
|
├── mode/task controls
|
||||||
|
├── XYZAC DRO and joint view
|
||||||
|
├── TCP/identity/userk segmented control
|
||||||
|
├── gate-aware MDI/run/jog/home buttons
|
||||||
|
└── alarms and diagnostics
|
||||||
|
|
||||||
|
XyzacTrtVismach
|
||||||
|
├── pose/joints/toolpath from authoritative snapshot
|
||||||
|
├── A/C table rotation
|
||||||
|
├── tool-offset rendering
|
||||||
|
└── canvas/WebGL pixel/interaction checks
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. 验收和测试矩阵
|
||||||
|
|
||||||
|
### 正常流程
|
||||||
|
|
||||||
|
1. 加载 `xyzac_switchkins.ngc`。
|
||||||
|
2. 验证 `XYZAC` 五个轴和五个 joints 出现在 snapshot。
|
||||||
|
3. 验证 preview segment/source line/hash。
|
||||||
|
4. `M429` → active type 0。
|
||||||
|
5. `M428` → active type 1,等待确认后再允许 TCP 轨迹。
|
||||||
|
6. 运行包含 A/C 变化的 helix;比较 Cartesian pose、joint positions、tool offset。
|
||||||
|
7. `M429` 安全返回;验证 no duplicate segment/no stale active segment。
|
||||||
|
|
||||||
|
### 边界和拒绝
|
||||||
|
|
||||||
|
| 测试 | 期望 |
|
||||||
|
|---|---|
|
||||||
|
| ESTOP 时 M428 | blocked,不改变 kinematics type |
|
||||||
|
| interpreter running 时 M428 | blocked 或排队策略明确,不能隐式切换 |
|
||||||
|
| 缺 `HAL_PIN_VARS=1` | remap 错误码 `M428:Missing...`,保留证据 |
|
||||||
|
| 缺 `motion.switchkins-type` net | `WRONG_SWITCHKINS_TYPE`/source diagnostic |
|
||||||
|
| A/C 超限 | limit/fault snapshot,禁止继续运动 |
|
||||||
|
| tool offset 非有限值 | fail closed,不能送入 kinematics |
|
||||||
|
| pause 后 resume | segment、kinematics type、state hash 连续 |
|
||||||
|
| abort 后 reload | program/task/preview 清理顺序确定 |
|
||||||
|
| Worker restart | checkpoint 恢复 kinematics type、program line、joint/pose hash |
|
||||||
|
| Vismach clear | 只清 visual layer,task/program/state_hash 不变 |
|
||||||
|
|
||||||
|
### 必须保存的证据
|
||||||
|
|
||||||
|
- context id、case path、INI/HAL/remap source hash。
|
||||||
|
- command envelope、operation id、gate result、command result。
|
||||||
|
- 每个固定 tick 的 task/interp/motion/kinematics state hash。
|
||||||
|
- Cartesian pose、XYZAC joint vector、tool offset、active segment/source line。
|
||||||
|
- 首个 divergent tick、错误码、资源缺失分类。
|
||||||
|
- native/WASM/Worker 对照结果;不以单一截图替代。
|
||||||
|
|
||||||
|
## 11. 已知限制
|
||||||
|
|
||||||
|
- 当前 `xyzac-trt.ini` 是 AXIS + PyVCP + 独立 Vismach 的 LinuxCNC 原生案例;Web/WASM 尚未声明其完整五轴 RTCP parity 已完成。
|
||||||
|
- 当前环境使用 POSIX non-realtime;不提供实时硬件周期、延迟和驱动证明。
|
||||||
|
- `xyzac-trt-gui` 只可视化 joint feedback/tool offset;它不是 canonical motion authority。
|
||||||
|
- 官方 kinematics oracle 缺失时,native/WASM 结果只能作为实现证据,不能伪称官方 golden。
|
||||||
|
- `xyzac-trt` 配置中的 `x-rot-point/y-rot-point/z-rot-point` 在示例中固定为 0,且注释说明当前 Vismach GUI 不支持通过 GUI 调整;Web 端必须把它作为配置/能力字段,而不是随意暴露可写 slider。
|
||||||
|
|
||||||
|
## 12. 后续实现顺序
|
||||||
|
|
||||||
|
1. 建立 exact-path `xyzac-trt.ini` resolver 和 resource graph。
|
||||||
|
2. 在 Worker/WASM 建立 additive `kinematics` snapshot 与 `KINEMATICS_SELECT` command。
|
||||||
|
3. 将 M428/M429/M430 的 remap 行为接入固定 tick interpreter,先做拒绝和错误边界。
|
||||||
|
4. 将 XYZAC Cartesian/joint/tool-offset canonical snapshot 接到 Three.js Vismach adapter。
|
||||||
|
5. 实现 gate matrix:ESTOP/OFF/ON/HOME/IDLE/RUNNING/PAUSED/limit/fault。
|
||||||
|
6. 完成 program load/run/pause/resume/abort/restart + RTCP switch recovery 测试。
|
||||||
|
7. 运行 native/Worker/browser 三方对照,并记录首个差异 tick。
|
||||||
|
8. 只有 source/resource/oracle/recovery/浏览器边界均有证据后,才可提升为 selected-case verified;不能从一个 AXIS 截图直接宣称全 LinuxCNC 五轴 parity。
|
||||||
572
doc-rtcp/xyzac-trt-web-wasm-implementation-plan.md
Normal file
@@ -0,0 +1,572 @@
|
|||||||
|
# `xyzac-trt.ini` Web/WASM 控制面板实施计划
|
||||||
|
|
||||||
|
文档状态:实施基线 v1.0
|
||||||
|
|
||||||
|
本计划完全以 [`xyzac-trt-control-panel-web-wasm-design.md`](xyzac-trt-control-panel-web-wasm-design.md) 为设计契约,目标是把以下 LinuxCNC 原生案例的控制面板、数据流和控制流迁移为 Worker/WASM/React/Three.js Web 控制台:
|
||||||
|
|
||||||
|
```text
|
||||||
|
linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
它不是 AXIS 的截图复刻计划,而是面向行为、状态、错误、资源、轨迹、RTCP 和恢复结果的 Web-equivalent 实施计划。任何视觉实现都必须服从 Worker/WASM authoritative state。
|
||||||
|
|
||||||
|
“完全对标 LinuxCNC 源码”和“全部功能唯一来自 LinuxCNC WASM”定义为唯一权威合同,强制定义见 [`xyzac-trt-web-wasm-source-parity-contract.md`](xyzac-trt-web-wasm-source-parity-contract.md)。若本计划与该合同冲突,以源码对标合同的禁止项、source slice、WASM authority、first divergent tick 和 parity gate 为准。
|
||||||
|
|
||||||
|
## 1. 目标与不可变约束
|
||||||
|
|
||||||
|
### 1.1 交付目标
|
||||||
|
|
||||||
|
完成一个可在浏览器中运行的 XYZAC 五轴 TRT 控制台,覆盖:
|
||||||
|
|
||||||
|
1. exact-path 案例解析、资源闭包和启动诊断。
|
||||||
|
2. Dedicated Worker 中的 WASM 任务、解释器、TP、motion、XYZAC kinematics、HAL/IO 虚拟设备和固定 tick。
|
||||||
|
3. AXIS 等价的程序、MDI、Manual/Jog、Home、limit、override、spindle、coolant、tool 和 alarm 工作流。
|
||||||
|
4. `M428/M429/M430` switchkins/RTCP 切换、拒绝边界、状态确认和恢复。
|
||||||
|
5. XYZAC Cartesian pose、A/C 姿态、五个 joints、tool offset、rotation/axis offsets 和 canonical toolpath 的 Three.js 可视化。
|
||||||
|
6. protocol.v1 command/result/snapshot/event/stream、operation ledger、checkpoint、Worker 重启和 offline recovery。
|
||||||
|
7. native/Worker/WASM/React/Three.js 之间的确定性 parity 报告、错误样本和首个 divergent tick。
|
||||||
|
|
||||||
|
### 1.2 不可变约束
|
||||||
|
|
||||||
|
| 约束 | 实施要求 |
|
||||||
|
|---|---|
|
||||||
|
| WASM/Worker 权威 | React 不创建第二份 task、motion、kinematics 或 program authority;本地状态只能是 draft/pending |
|
||||||
|
| 固定 tick | 解释器、TP、motion、HAL、IO、kinematics、event、journal 以 logical tick 串行执行,不使用墙钟决定结果 |
|
||||||
|
| exact path | `xyzac-trt.ini` 中的 `HALFILE`、`POSTGUI_HALFILE`、`OPEN_FILE`、`SUBROUTINE_PATH`、`TOOL_TABLE` 和 `PYVCP` 按 exact path 解析,不按 basename 猜文件 |
|
||||||
|
| RTCP 原语 | `M428/M429/M430` 必须保留 remap 语义和 `motion.switchkins-type` 的确认,禁止前端直接写一个近似值 |
|
||||||
|
| 一致 gate | UI gate 只是 projection;Worker/runtime 必须再次检查 ESTOP/OFF/ON/HOME/IDLE/RUNNING/PAUSED/limit/fault/kinematics 条件 |
|
||||||
|
| 轨迹权威 | 预览和 Three.js 使用 canonical WASM trajectory/pose/joints;JS 不能重算 RTCP 结果冒充通过 |
|
||||||
|
| 单写者 | 一个 session 只有一个 Worker 写入 task/WASM/OPFS journal;多标签页只能观察或持有租约 |
|
||||||
|
| 幂等恢复 | 每个副作用命令有 operation id、ledger prepare/commit 和结果重查;超时不能盲目重发 |
|
||||||
|
| 能力拒绝 | 宿主路径、shell、真实硬件、socket、动态库和未授权 Python/HAL 能力稳定返回拒绝码 |
|
||||||
|
| 发布边界 | 没有官方 oracle、真实硬件、Safari/Firefox、OPFS crash 或许可证输入时保持 blocked,不升级为全 LinuxCNC parity |
|
||||||
|
|
||||||
|
## 2. 设计契约追溯
|
||||||
|
|
||||||
|
| 设计文档章节 | 本计划对应实施面 |
|
||||||
|
|---|---|
|
||||||
|
| §1 范围与版本边界 | `RTCP-00` source/runtime/version lock |
|
||||||
|
| §2 总体架构 | `RTCP-01` Worker topology、authority boundary |
|
||||||
|
| §3 启动与初始化 | `RTCP-02` exact case resolver/startup state machine |
|
||||||
|
| §4 AXIS 主控制面板 | `RTCP-05` UI domain、controls、status projection |
|
||||||
|
| §5 G-code 加载流 | `RTCP-04` PROGRAM_LOAD/preview/program ledger |
|
||||||
|
| §6 AUTO 运行流 | `RTCP-06` run/pause/resume/step/abort/restart |
|
||||||
|
| §7 MDI/Jog/Home/Tool/Offset | `RTCP-07` manual and device workflows |
|
||||||
|
| §8 SWITCHKINS/RTCP | `RTCP-08` M428/M429/M430/XYZAC kinematics |
|
||||||
|
| §9 状态真值 | `RTCP-03` snapshot/axis_state/gates |
|
||||||
|
| §10 Web/WASM mapping | `RTCP-09` protocol and frontend integration |
|
||||||
|
| §11 测试与限制 | `RTCP-10` parity/recovery/browser gates |
|
||||||
|
|
||||||
|
## 3. 功能范围矩阵
|
||||||
|
|
||||||
|
### 3.1 P0:必须先闭合的主链
|
||||||
|
|
||||||
|
| ID | 功能 | 原生来源/对照 | Web 结果 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| P0-01 | 案例选择与启动 | `xyzac-trt.ini`、`run-5axis-rtcp.sh` | `TASK_READY` 或可定位 `BLOCKED_*` |
|
||||||
|
| P0-02 | 配置/资源图 | INI/HAL/XML/TBL/VAR/NGC/remap | exact path、source hash、missing edge |
|
||||||
|
| P0-03 | Task/ESTOP/OFF/ON | AXIS `stat.task_state` | axis_state + gate matrix |
|
||||||
|
| P0-04 | Mode MANUAL/MDI/AUTO | AXIS `ensure_mode`、`on_hal_status_mode_auto` | state transition + command result |
|
||||||
|
| P0-05 | Program load/open | AXIS `open_file_guts`、`c.program_open` | program hash、line count、preview |
|
||||||
|
| P0-06 | Canonical preview | AXIS canon/preview | stable segments/source lines |
|
||||||
|
| P0-07 | Program run | AXIS `task_run` | RUNNING、tick、active line |
|
||||||
|
| P0-08 | Pause/resume/step | AXIS `task_pause/resume/step` | PAUSED/RESUMED/step endpoint |
|
||||||
|
| P0-09 | Abort/restart | AXIS `task_stop/reload` | STOPPED/loaded、no duplicate side effect |
|
||||||
|
| P0-10 | MDI | AXIS `send_mdi_command` | parser result、queue gate、error |
|
||||||
|
| P0-11 | XYZAC kinematics | `xyzac-trt-kins.c`、`trtfuncs.c` | forward/inverse pose/joints |
|
||||||
|
| P0-12 | M428/M429/M430 | `428remap.ngc`、`429remap.ngc`、HAL net | active type + verified transition |
|
||||||
|
| P0-13 | Vismach pose/joints | `xyzac-trt-gui.py` | Three.js pose/joints/toolpath |
|
||||||
|
|
||||||
|
### 3.2 P1:操作完整性
|
||||||
|
|
||||||
|
| ID | 功能 | 关键边界 |
|
||||||
|
|---|---|---|
|
||||||
|
| P1-01 | XYZAC jog | X/Y/Z linear units,A/C angular units,按下/释放 stop |
|
||||||
|
| P1-02 | Home/unhome | 五 joint homing、共享开关、home sequence、重复 homing |
|
||||||
|
| P1-03 | Soft/hard limits | limit active、override limit、following error |
|
||||||
|
| P1-04 | Feed/rapid/spindle override | 范围、非有限值、暂停/ESTOP |
|
||||||
|
| P1-05 | Spindle/coolant | command/actual/at-speed、冷却状态、fault |
|
||||||
|
| P1-06 | Tool table/offset | `xyzac-trt.tbl`、tool offset、G43/G49/G43.1 |
|
||||||
|
| P1-07 | HAL/PyVCP | `type0/1/2-button`、legend、vismach-clear、HAL read |
|
||||||
|
| P1-08 | Alarm/event timeline | source、severity、operation id、acknowledge |
|
||||||
|
| P1-09 | Configuration diagnostics | startup/resource/HAL/remap errors |
|
||||||
|
|
||||||
|
### 3.3 P2:交付和恢复完整性
|
||||||
|
|
||||||
|
| ID | 功能 | 退出要求 |
|
||||||
|
|---|---|---|
|
||||||
|
| P2-01 | Checkpoint/journal | tick、program line、kinematics type、pose/joints、hash 可恢复 |
|
||||||
|
| P2-02 | Worker restart | 无重复 M428/M429、无重复 segment/IO/tool side effect |
|
||||||
|
| P2-03 | Offline project | 缓存资源、OPFS journal、配额和冲突可诊断 |
|
||||||
|
| P2-04 | Stream/backpressure | trajectory/HAL/log drop 有 cursor/gap/最终快照 |
|
||||||
|
| P2-05 | Browser matrix | Chromium/Firefox/Safari 逐项证据;缺环境保持 blocked |
|
||||||
|
| P2-06 | Native/WASM parity | normal/boundary/error/recovery + deterministic self-hash |
|
||||||
|
|
||||||
|
## 4. 目标架构
|
||||||
|
|
||||||
|
```text
|
||||||
|
React/Web UI
|
||||||
|
├── XyzacCaseView / ProgramView / ManualView / RtccpView / DiagnosticsView
|
||||||
|
├── Domain store: task/program/motion/kinematics/hal/devices/recovery
|
||||||
|
├── Pure gate selectors and source control IDs
|
||||||
|
└── WorkerClient only
|
||||||
|
│ protocol.v1 MessageEnvelope
|
||||||
|
▼
|
||||||
|
Dedicated sim.worker
|
||||||
|
├── ProtocolGateway + runtime schema validator
|
||||||
|
├── Session/Liveness/Lease manager
|
||||||
|
├── CommandLedger (prepare/ack/result/commit/reconcile)
|
||||||
|
├── XyzacCaseResolver + resource graph
|
||||||
|
├── SimScheduler (fixed logical tick)
|
||||||
|
├── WASM LinuxCncCompat
|
||||||
|
│ ├── Task/RS274/TP/Motion
|
||||||
|
│ ├── xyzac-trt forward/inverse
|
||||||
|
│ ├── remap M428/M429/M430
|
||||||
|
│ └── HAL/IO/tool/probe virtual devices
|
||||||
|
├── SnapshotPublisher + StateHash
|
||||||
|
├── OPFS journal/checkpoint/recovery
|
||||||
|
└── trajectory/HAL/log stream backpressure
|
||||||
|
│ authoritative snapshots/events
|
||||||
|
▼
|
||||||
|
React projection + Three.js XYZAC Vismach adapter
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.1 Authority ownership
|
||||||
|
|
||||||
|
| 数据 | 唯一 authority | React 是否可写 |
|
||||||
|
|---|---|---:|
|
||||||
|
| task state/mode/interp | WASM Task/Interpreter | 否 |
|
||||||
|
| program parsed state/line | WASM Interpreter/Task | 否 |
|
||||||
|
| TP queue/active segment | WASM TP | 否 |
|
||||||
|
| Cartesian pose/XYZAC joints | WASM Kinematics/Motion | 否 |
|
||||||
|
| switchkins type | WASM/HAL compatibility layer | 只能发请求 |
|
||||||
|
| homed/limit/following error | WASM Motion/Home/HAL | 否 |
|
||||||
|
| tool/offset/spindle/coolant | virtual IO/tool runtime | 只能发命令 |
|
||||||
|
| UI draft/selection | React local store | 是,但不等于运行态 |
|
||||||
|
| Three.js camera/layer toggle | React/renderer | 是,不影响 motion |
|
||||||
|
|
||||||
|
## 5. 实施任务队列
|
||||||
|
|
||||||
|
任务状态使用:`queued → in_progress → verified` 或 `in_progress → blocked`。每个任务完成时必须生成报告、自哈希、输入哈希、命令记录、错误边界和下一任务依赖。
|
||||||
|
|
||||||
|
### RTCP-00:源码、版本和案例锁定
|
||||||
|
|
||||||
|
**目标**:冻结 `xyzac-trt.ini`、所有引用资源、系统运行时和 source symbol 映射。
|
||||||
|
|
||||||
|
**直接输入**:
|
||||||
|
|
||||||
|
- `xyzac-trt.ini`
|
||||||
|
- `xyzac-trt.xml`
|
||||||
|
- `switchkins_postgui.hal`
|
||||||
|
- `demos/*.ngc`
|
||||||
|
- `remap_subs/*.ngc`
|
||||||
|
- `xyzac-trt-kins.c`、`trtfuncs.c`
|
||||||
|
- `xyzac-trt-gui.py`
|
||||||
|
- `context-lock.json`
|
||||||
|
|
||||||
|
**产物**:
|
||||||
|
|
||||||
|
- `coverage/xyzac-trt-web-source-lock.report.json`
|
||||||
|
- `web/workspace/xyzac-trt-source-contract.mjs`
|
||||||
|
- exact resource graph、source hash、runtime version record
|
||||||
|
|
||||||
|
**验收**:每个 INI path、HAL edge、PyVCP pin、remap、G-code 入口可追溯;缺失资源为明确 blocker。
|
||||||
|
|
||||||
|
### RTCP-01:Worker 拓扑和 LinuxCncCompat 外壳
|
||||||
|
|
||||||
|
**目标**:建立单 Worker/session/ledger/scheduler 边界,不实现五轴细节。
|
||||||
|
|
||||||
|
**产物**:
|
||||||
|
|
||||||
|
- `web/worker/xyzac-session.mjs`
|
||||||
|
- `web/worker/xyzac-scheduler.mjs`
|
||||||
|
- `web/worker/xyzac-command-ledger.mjs`
|
||||||
|
- `web/worker/test_xyzac_session.mjs`
|
||||||
|
|
||||||
|
**验收**:HELLO/CAPABILITIES/SESSION_ATTACH/SESSION_READY、seq、operation_id、取消、超时重查和重复命令测试通过;Worker 之外不能写 runtime。
|
||||||
|
|
||||||
|
### RTCP-02:Exact case resolver 和启动状态机
|
||||||
|
|
||||||
|
**目标**:把 `xyzac-trt.ini` 解析到 `TASK_NEW → CONFIG_RESOLVED → HAL_READY → RUNTIME_READY → TASK_READY`。
|
||||||
|
|
||||||
|
**要求**:
|
||||||
|
|
||||||
|
- 解析 `DISPLAY/TASK/HAL/RS274NGC/KINS/TRAJ/EMCIO`。
|
||||||
|
- 解析 `LIB:basic_sim.tcl` 的虚拟 HAL 边界。
|
||||||
|
- exact resolve `OPEN_FILE`、`SUBROUTINE_PATH`、`xyzac-trt.tbl`、`xyzac.var`、`xyzac-trt.xml` 和 `xyzac-trt-gui.py`。
|
||||||
|
- 识别 `BLOCKED_RESOURCE`、`BLOCKED_HAL`、`BLOCKED_INI`、`BLOCKED_STARTUP`。
|
||||||
|
|
||||||
|
**产物**:`web/workspace/xyzac-trt-case-loader.mjs`、startup snapshot、diagnostic report。
|
||||||
|
|
||||||
|
### RTCP-03:Authoritative snapshot、axis_state 和 gate matrix
|
||||||
|
|
||||||
|
**目标**:将 native task/motion/interp/home/limit/kinematics 状态统一为 Worker snapshot。
|
||||||
|
|
||||||
|
**产物**:
|
||||||
|
|
||||||
|
- additive `axis_state`
|
||||||
|
- `kinematics.active_type`
|
||||||
|
- `configuration.coordinates/joints/topology`
|
||||||
|
- `gates`(action/enabled/reason)
|
||||||
|
- `web/worker/test_xyzac_gate_matrix.mjs`
|
||||||
|
|
||||||
|
**验收**:ESTOP/OFF/ON/HOME/IDLE/RUNNING/PAUSED、limit/fault/tool/no-file/no-highlight 每项都有 gate reason 和状态哈希。
|
||||||
|
|
||||||
|
### RTCP-04:程序加载、canonical preview 和 program ledger
|
||||||
|
|
||||||
|
**目标**:实现 AXIS `open_file_guts → program_open → canon/preview → stat.file` 的 Worker/WASM 等价流程。
|
||||||
|
|
||||||
|
**要求**:
|
||||||
|
|
||||||
|
- source/path/hash/line count 固化。
|
||||||
|
- preview segment 带 id/source_line/start/end/motion_type/center/normal/turn。
|
||||||
|
- 解析错误、超限、非有限值、空文件和缺资源 fail closed。
|
||||||
|
- load/reload/restart 与 ledger/recovery 可重放。
|
||||||
|
|
||||||
|
**验收**:native 与 Worker 首个 segment、line count、preview hash、loaded file、invalid G-code 错误码一致。
|
||||||
|
|
||||||
|
### RTCP-05:程序运行控制和 AXIS workflow
|
||||||
|
|
||||||
|
**目标**:覆盖 Run/Pause/Resume/Step/Abort/Restart/Run-from-line。
|
||||||
|
|
||||||
|
**按钮约束**:
|
||||||
|
|
||||||
|
```text
|
||||||
|
RUN = machine_on && interp_idle && task_file
|
||||||
|
PAUSE = machine_on && interp_reading_or_waiting
|
||||||
|
RESUME = machine_on && interp_paused
|
||||||
|
STEP = machine_on && task_file
|
||||||
|
ABORT = machine_on && interp_running
|
||||||
|
RESTART = machine_on && interp_idle && task_file
|
||||||
|
RUN_LINE = RUN && highlight_line != null
|
||||||
|
```
|
||||||
|
|
||||||
|
**验收**:固定 tick 下 pause/resume 不重复 segment,abort 无重复副作用,restart 重置 program state 但保留正确 source hash。
|
||||||
|
|
||||||
|
### RTCP-06:MDI、Jog、Home 和 limit
|
||||||
|
|
||||||
|
**目标**:实现 XYZAC 手动控制。
|
||||||
|
|
||||||
|
**要求**:
|
||||||
|
|
||||||
|
- X/Y/Z 线性单位和 A/C 角度单位分离。
|
||||||
|
- pointerdown/keydown 只发一个 JOG_START;pointerup/keyup/blur/visibilitychange 必须发 JOG_STOP。
|
||||||
|
- MDI queue limit、running/paused/ESTOP gate 与 native 一致。
|
||||||
|
- home all、单 joint home、unhome、共享 home switch 和 limit override。
|
||||||
|
- following error、非有限速度、超范围轴拒绝。
|
||||||
|
|
||||||
|
**验收**:每个 X/Y/Z/A/C 轴有 normal/boundary/stop/fault/recovery 样本;A/C 不得被当作 linear mm。
|
||||||
|
|
||||||
|
### RTCP-07:HAL、PyVCP、tool、spindle、coolant 和 alarm
|
||||||
|
|
||||||
|
**目标**:把 `xyzac-trt.xml` 和 `switchkins_postgui.hal` 中的控件接入 Worker capability。
|
||||||
|
|
||||||
|
**功能**:
|
||||||
|
|
||||||
|
- type0/type1/type2 button 状态和 `kinstype.is-*` legend。
|
||||||
|
- `vismach-clear` 只清 visual plot。
|
||||||
|
- tool table、tool offset、G43/G49/G43.1、tool prepare/change。
|
||||||
|
- spindle command/actual/at-speed/direction、coolant、manual tool change。
|
||||||
|
- HAL pin read/write 白名单、类型和权限。
|
||||||
|
- alarm/event timeline、acknowledge、source file/line。
|
||||||
|
|
||||||
|
**验收**:HAL command 不能越权写任意 pin;tool/IO ack、timeout、abort、recovery 和 `REAL-EXCLUDED` 分类都有报告。
|
||||||
|
|
||||||
|
### RTCP-08:XYZAC kinematics 和 RTCP
|
||||||
|
|
||||||
|
**目标**:实现 `xyzac-trt-kins` 的 forward/inverse、tool offset、rotation/axis offset 和 switchkins。
|
||||||
|
|
||||||
|
**阶段拆分**:
|
||||||
|
|
||||||
|
1. K0:XYZAC 坐标/joint mapping、单位、limits、finite-value 校验。
|
||||||
|
2. K1:identity type 0,forward/inverse identity golden。
|
||||||
|
3. K2:TRT type 1,`trtKinematicsSetup`、XYZAC forward/inverse、`y-offset/z-offset`、tool-offset。
|
||||||
|
4. K3:userk type 2 capability boundary;无 userk 时稳定拒绝。
|
||||||
|
5. K4:M428/M429/M430 remap 与 `motion.switchkins-type` handshake。
|
||||||
|
6. K5:helix_ac/helix_bc、姿态变化、G93/arc、奇异/限位/非有限值。
|
||||||
|
7. K6:pause/resume/abort/restart/recovery 时 active kinematics type 和 pose/joints 保持一致。
|
||||||
|
|
||||||
|
**必须保存**:Cartesian pose、XYZAC joints、A/C angle、tool offset、rotation point、axis offsets、kinematics type、首个 divergent tick、state hash。
|
||||||
|
|
||||||
|
### RTCP-09:Protocol v1 和 React 控制台
|
||||||
|
|
||||||
|
**目标**:把现有 `axis-contract.mjs`、`AxisOperationController`、`PanelSnapshotV1` 扩展到 XYZAC。
|
||||||
|
|
||||||
|
**产物**:
|
||||||
|
|
||||||
|
- `KINEMATICS_SELECT`、`VISUAL_CLEAR`、`POWER` 等 additive commands。
|
||||||
|
- `xyzac-panel-snapshot.v1` additive view 或兼容扩展。
|
||||||
|
- React `XyzacTrtPanel`、`XyzacProgramPane`、`XyzacKinematicsControl`、`XyzacManualPanel`、`XyzacDiagnostics`。
|
||||||
|
- gate-aware controls、operation trace、alarm/recovery view。
|
||||||
|
|
||||||
|
**验收**:React 不直接调用 WASM/OPFS;所有 controls 只通过 WorkerClient;gate reason、pending、ack、result、error 状态可见。
|
||||||
|
|
||||||
|
### RTCP-10:Three.js XYZAC Vismach adapter
|
||||||
|
|
||||||
|
**目标**:使用 authoritative `visual.pose/joints/toolpath` 实现五轴模型。
|
||||||
|
|
||||||
|
**要求**:
|
||||||
|
|
||||||
|
- 机床结构对应 `xyzac-trt-gui.py`:table-x、saddle-y、spindle-z、tilt-a、rotate-c、tool-offset。
|
||||||
|
- pose/joints 与 toolpath 来自同一 `state_hash`/tick。
|
||||||
|
- camera/layer/clear 不改变 task/motion/program。
|
||||||
|
- canvas nonblank、A/C 旋转方向、tool offset、轨迹 active segment 和 resize/mobile 通过。
|
||||||
|
|
||||||
|
### RTCP-11:Journal、checkpoint、Worker restart 和 offline recovery
|
||||||
|
|
||||||
|
**目标**:恢复完整 XYZAC 运行态。
|
||||||
|
|
||||||
|
**checkpoint 最小字段**:
|
||||||
|
|
||||||
|
```text
|
||||||
|
context_id, case_id, source_hashes
|
||||||
|
simulation_tick, seq, state_hash
|
||||||
|
task_state, task_mode, interp_state, program_hash, current_line
|
||||||
|
kinematics_type, kinematics_transition, pose, joints
|
||||||
|
tool_offset, y_offset, z_offset, rotation_points
|
||||||
|
TP queue/active segment, HAL/IO/tool pending commands
|
||||||
|
```
|
||||||
|
|
||||||
|
**验收**:
|
||||||
|
|
||||||
|
- M428 在 checkpoint 前后不重复执行。
|
||||||
|
- 暂停程序恢复不重复 segment。
|
||||||
|
- tool/spindle/coolant/IO side effect 不重复。
|
||||||
|
- 损坏 checkpoint、缺资源、schema mismatch fail closed。
|
||||||
|
- Worker 重启后 endpoint pose/joints/program/kinematics/state hash 一致。
|
||||||
|
|
||||||
|
### RTCP-12:Native/Worker/Browser parity 和验收闭环
|
||||||
|
|
||||||
|
**目标**:形成 selected-case parity report,不冒充全 LinuxCNC release。
|
||||||
|
|
||||||
|
**矩阵**:
|
||||||
|
|
||||||
|
- Chromium desktop/mobile。
|
||||||
|
- Firefox/Safari 可用时运行;不可用保留 `HOST_EXCLUDED`/`blocked`。
|
||||||
|
- native AXIS + xyzac-trt-gui。
|
||||||
|
- Worker/WASM + React + Three.js。
|
||||||
|
- normal/boundary/error/limit/pause/abort/restart/recovery。
|
||||||
|
|
||||||
|
**产物**:
|
||||||
|
|
||||||
|
- `coverage/xyzac-trt-web-wasm-parity.report.json`
|
||||||
|
- native/worker command golden
|
||||||
|
- snapshot/trajectory/kinematics self-hash
|
||||||
|
- failure samples and first divergence report
|
||||||
|
|
||||||
|
## 6. 里程碑设计
|
||||||
|
|
||||||
|
### M0:Source and Case Contract
|
||||||
|
|
||||||
|
**包含**:RTCP-00。
|
||||||
|
|
||||||
|
**退出条件**:
|
||||||
|
|
||||||
|
- 所有直接引用资源均 exact-path indexed(不以抽样替代)。
|
||||||
|
- `xyzac-trt.ini` 的 INI/HAL/NGC/XML/Python/C source graph 自哈希稳定。
|
||||||
|
- 版本、context、运行时和能力边界记录完成。
|
||||||
|
- 缺失/未索引/宿主资源明确 blocked。
|
||||||
|
|
||||||
|
### M1:Worker Startup and State Authority
|
||||||
|
|
||||||
|
**包含**:RTCP-01、RTCP-02、RTCP-03。
|
||||||
|
|
||||||
|
**退出条件**:
|
||||||
|
|
||||||
|
- Worker session handshake、TASK_READY、CAPABILITIES 通过。
|
||||||
|
- snapshot 包含 task/motion/home/limit/program/kinematics/gates/configuration。
|
||||||
|
- ESTOP/OFF/ON/HOME/IDLE gate matrix 和 deterministic state hash 通过。
|
||||||
|
- React 尚未接入前,Worker 测试可独立验证 authority。
|
||||||
|
|
||||||
|
### M2:Program and AXIS Operation Workflow
|
||||||
|
|
||||||
|
**包含**:RTCP-04、RTCP-05、RTCP-06。
|
||||||
|
|
||||||
|
**退出条件**:
|
||||||
|
|
||||||
|
- load/preview/run/pause/resume/step/abort/restart/MDI/jog/home 全部有 native/Worker 对照。
|
||||||
|
- 每个操作的 gate reason、operation id、command result 和 state hash 可追溯。
|
||||||
|
- running/paused/ESTOP/limit/tool missing 的拒绝不被 UI 绕过。
|
||||||
|
|
||||||
|
### M3:HAL/IO/PyVCP and Device Closure
|
||||||
|
|
||||||
|
**包含**:RTCP-07。
|
||||||
|
|
||||||
|
**退出条件**:
|
||||||
|
|
||||||
|
- SWITCHKINS PyVCP 三个按钮和 vismach-clear 的 HAL 图可解析。
|
||||||
|
- spindle/coolant/tool/probe/hal read/write/ack/error 边界通过。
|
||||||
|
- 未授权 pin、外部进程、真实硬件被稳定拒绝。
|
||||||
|
|
||||||
|
### M4:XYZAC Kinematics and RTCP
|
||||||
|
|
||||||
|
**包含**:RTCP-08。
|
||||||
|
|
||||||
|
**退出条件**:
|
||||||
|
|
||||||
|
- type 0/1/2 切换和 M428/M429/M430 handshake 与 native 一致。
|
||||||
|
- XYZAC pose/joints/tool offset/axis offset/rotation point 的 forward/inverse golden 通过。
|
||||||
|
- helix/姿态变化、奇异、limits、non-finite、pause/abort/recovery 样本齐全。
|
||||||
|
- 首个 divergent tick 可定位;没有 JS kinematics fallback。
|
||||||
|
|
||||||
|
### M5:React/Protocol/Three.js Console
|
||||||
|
|
||||||
|
**包含**:RTCP-09、RTCP-10。
|
||||||
|
|
||||||
|
**退出条件**:
|
||||||
|
|
||||||
|
- React 所有按钮只通过 WorkerClient/OperationController。
|
||||||
|
- Three.js 使用 authoritative snapshot,A/C 方向、tool offset 和 active segment 正确。
|
||||||
|
- desktop/mobile DOM、canvas nonblank、keyboard/touch、错误/pending/gate UI 通过。
|
||||||
|
|
||||||
|
### M6:Recovery and Offline Runtime
|
||||||
|
|
||||||
|
**包含**:RTCP-11。
|
||||||
|
|
||||||
|
**退出条件**:
|
||||||
|
|
||||||
|
- checkpoint/restart/reconcile/replay 无重复副作用。
|
||||||
|
- OPFS journal、配额、schema mismatch、坏数据和离线重新打开可诊断。
|
||||||
|
- recovery endpoint state hash 与连续运行一致。
|
||||||
|
|
||||||
|
### M7:Selected-Case Parity Acceptance
|
||||||
|
|
||||||
|
**包含**:RTCP-12。
|
||||||
|
|
||||||
|
**退出条件**:
|
||||||
|
|
||||||
|
- native/Worker/WASM/React/Three.js 的选定案例完整矩阵通过。
|
||||||
|
- 结果报告有 source/input/output hash、命令、状态、首个差异 tick 和失败样本。
|
||||||
|
- Chromium/Firefox/Safari、官方 oracle、实时硬件、OPFS crash 和许可证边界按真实环境记录。
|
||||||
|
- 只能声明 `selected_case_verified`;任何外部 blocker 存在时保持 `STOP_RELEASE`。
|
||||||
|
|
||||||
|
## 7. 任务依赖图
|
||||||
|
|
||||||
|
```text
|
||||||
|
RTCP-00 source lock
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
RTCP-01 session/ledger ── RTCP-02 case startup ── RTCP-03 snapshot/gates
|
||||||
|
│ │ │
|
||||||
|
└─────────────────────────┴──────────────────────┘
|
||||||
|
▼
|
||||||
|
RTCP-04 program/preview
|
||||||
|
│
|
||||||
|
┌──────────────┴──────────────┐
|
||||||
|
▼ ▼
|
||||||
|
RTCP-05 run/control RTCP-06 manual
|
||||||
|
│ │
|
||||||
|
└──────────────┬──────────────┘
|
||||||
|
▼
|
||||||
|
RTCP-07 HAL/IO/PyVCP
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
RTCP-08 XYZAC/RTCP
|
||||||
|
│
|
||||||
|
┌───────────┴───────────┐
|
||||||
|
▼ ▼
|
||||||
|
RTCP-09 React/protocol RTCP-10 Three.js
|
||||||
|
│ │
|
||||||
|
└───────────┬───────────┘
|
||||||
|
▼
|
||||||
|
RTCP-11 recovery
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
RTCP-12 parity gate
|
||||||
|
```
|
||||||
|
|
||||||
|
不得跳过 RTCP-03 的 authority/gate、RTCP-04 的 canonical program 或 RTCP-08 的 kinematics 就先做“漂亮界面”。
|
||||||
|
|
||||||
|
## 8. 统一任务报告格式
|
||||||
|
|
||||||
|
每个 `RTCP-*` 任务必须生成 JSON 报告,至少包含:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema": "xyzac-trt-web-wasm-task-report.v1",
|
||||||
|
"task_id": "RTCP-08",
|
||||||
|
"milestone": "M4",
|
||||||
|
"status": "verified",
|
||||||
|
"context_id": "...",
|
||||||
|
"case_path": ".../xyzac-trt.ini",
|
||||||
|
"inputs": [{"path": "...", "sha256": "..."}],
|
||||||
|
"outputs": [{"path": "...", "sha256": "..."}],
|
||||||
|
"commands": [{"name": "...", "status": "passed", "stdout_sha256": "..."}],
|
||||||
|
"normal": {},
|
||||||
|
"boundaries": [],
|
||||||
|
"errors": [],
|
||||||
|
"first_divergent_tick": null,
|
||||||
|
"known_blockers": [],
|
||||||
|
"report_sha256": "..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
失败样本必须保留,不得删除失败、降低 gate、把资源缺失改成通过或用 JS preview/姿态镜像冒充 native/WASM parity。
|
||||||
|
|
||||||
|
## 9. 最低测试命令集合
|
||||||
|
|
||||||
|
任务开发期间按范围运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/validate_baseline.py
|
||||||
|
python3 scripts/validate_download_index.py
|
||||||
|
python3 scripts/validate_task_pointer.py nextTask1.md --lock context-lock.json
|
||||||
|
scripts/build-wasm.sh
|
||||||
|
ctest --test-dir build-native --output-on-failure
|
||||||
|
```
|
||||||
|
|
||||||
|
五轴专属命令:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./run-5axis-rtcp.sh
|
||||||
|
./run-5axis-rtcp.sh xyzbc-trt.ini
|
||||||
|
node web/worker/test_xyzac_trt_case.mjs
|
||||||
|
node web/worker/test_xyzac_program.mjs
|
||||||
|
node web/worker/test_xyzac_switchkins.mjs
|
||||||
|
node web/worker/test_xyzac_rtcp.mjs
|
||||||
|
node web/worker/test_xyzac_recovery.mjs
|
||||||
|
npm --prefix web/react run typecheck
|
||||||
|
npm --prefix web/react run build
|
||||||
|
```
|
||||||
|
|
||||||
|
命令名称在对应任务开始前必须实际创建并纳入报告;不存在的测试命令不能写成已通过。
|
||||||
|
|
||||||
|
## 10. 停止条件和阻塞处理
|
||||||
|
|
||||||
|
以下任一情况必须将当前任务标记为 blocked,并保留证据:
|
||||||
|
|
||||||
|
- `xyzac-trt.ini` 的 source/resource/hal/remap graph 不完整。
|
||||||
|
- native 运行时版本、锁定 source 或 ABI/protocol/schema 不一致。
|
||||||
|
- M428/M429 状态切换没有确认、重复执行或首个差异 tick 未定位。
|
||||||
|
- A/C 角度、tool offset、rotation point、joint pose 由 JS/React 生成。
|
||||||
|
- ESTOP、limit、following error、pause、abort、recovery 语义不可解释。
|
||||||
|
- 真实 HAL、动态 loader、宿主 Python、外部进程或真实硬件被错误标记为 Web-equivalent。
|
||||||
|
- Worker 重启造成重复 G-code、重复 tool/IO side effect 或 checkpoint hash 不一致。
|
||||||
|
- 只有 Chromium 截图而缺 Firefox/Safari/OPFS/oracle 证据,却要求全量 release。
|
||||||
|
- 缺少 owner/source/oracle/resource/browser/license 外部输入。
|
||||||
|
|
||||||
|
阻塞转移规则:
|
||||||
|
|
||||||
|
```text
|
||||||
|
技术缺陷 → 保留最小复现/错误码/首个 divergent tick → 修复同一任务
|
||||||
|
源码/ABI/protocol/schema 输入缺失 → SOURCE_BLOCKED → 等待审查输入
|
||||||
|
宿主/浏览器/硬件缺失 → HOST_EXCLUDED 或 REAL-EXCLUDED → 不伪造通过
|
||||||
|
发布/owner 决策缺失 → STOP_RELEASE → 不自动刷新 gate
|
||||||
|
```
|
||||||
|
|
||||||
|
## 11. 里程碑交付顺序
|
||||||
|
|
||||||
|
后续实施按以下顺序推进,不能在任务依赖未 verified 时跳级:
|
||||||
|
|
||||||
|
```text
|
||||||
|
M0 Source/Case Contract
|
||||||
|
→ M1 Worker Startup/Authority
|
||||||
|
→ M2 Program/AXIS Workflow
|
||||||
|
→ M3 HAL/IO/PyVCP
|
||||||
|
→ M4 XYZAC/RTCP Kinematics
|
||||||
|
→ M5 React/Protocol/Three.js
|
||||||
|
→ M6 Recovery/Offline
|
||||||
|
→ M7 Selected-Case Parity
|
||||||
|
```
|
||||||
|
|
||||||
|
每个里程碑完成后生成独立报告和自哈希,并将下一里程碑的直接输入、完成门禁、外部 blocker 和停止边界写入任务记录。完成单元测试或界面截图不等于该里程碑完成;必须同时满足 authority、数据流、控制流、错误、恢复和确定性要求。
|
||||||
440
doc-rtcp/xyzac-trt-web-wasm-source-parity-contract.md
Normal file
@@ -0,0 +1,440 @@
|
|||||||
|
# XYZAC TRT Web/WASM 源码对标与唯一权威合同
|
||||||
|
|
||||||
|
文档状态:强制实施合同 v1.0
|
||||||
|
|
||||||
|
本合同是 [`xyzac-trt-web-wasm-implementation-plan.md`](xyzac-trt-web-wasm-implementation-plan.md) 的约束性补充。它回答一个更严格的问题:Web 控制面板如何证明自己是 LinuxCNC 源码行为的 Web/WASM 等价实现,并且所有控制功能唯一来自 LinuxCNC WASM,而不是来自 React、浏览器状态或第二套 JavaScript 运动模型。
|
||||||
|
|
||||||
|
适用案例:
|
||||||
|
|
||||||
|
```text
|
||||||
|
linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
## 1. 强制结论
|
||||||
|
|
||||||
|
### 1.1 唯一权威
|
||||||
|
|
||||||
|
以下数据只能由 LinuxCNC WASM runtime 产生;React、TypeScript、Three.js、Worker UI adapter 和浏览器不能自行生成“看起来合理”的替代值:
|
||||||
|
|
||||||
|
- task state、task mode、interpreter state、current line、queued MDI。
|
||||||
|
- program parse state、canonical events、trajectory queue、active segment。
|
||||||
|
- Cartesian pose、X/Y/Z/A/C 轴位置、五个 joint 位置、速度、加速度和 limits。
|
||||||
|
- XYZAC forward/inverse kinematics、switchkins type、TCP/RTCP tool compensation。
|
||||||
|
- tool table、tool offset、G43/G49/G43.1、spindle、coolant、probe、home、limit、following error。
|
||||||
|
- HAL pin/signal/parameter 读值、PyVCP state、IO ack、报警、错误和恢复结果。
|
||||||
|
- checkpoint、journal、replay、recovery endpoint 和 state hash。
|
||||||
|
|
||||||
|
前端可以拥有以下本地状态,但必须明确标记为非权威:
|
||||||
|
|
||||||
|
- 编辑器 draft、搜索词、文件选择、当前 tab、camera、图层显示、窗口尺寸、pending operation。
|
||||||
|
- 尚未提交的配置表单、UI 偏好和输入法状态。
|
||||||
|
|
||||||
|
### 1.2 严禁的替代 authority
|
||||||
|
|
||||||
|
下列行为一律视为实现失败:
|
||||||
|
|
||||||
|
1. React 根据按钮点击直接把 task/mode/position/kinematics 写成目标状态。
|
||||||
|
2. JS 根据 A/C 角度和 tool length 自行计算 X/Y/Z 补偿并展示为实际 pose。
|
||||||
|
3. JS G-code preview、Three.js preview 或 canvas 轨迹被当作 canonical motion。
|
||||||
|
4. UI 通过 `setTimeout`、墙钟、`requestAnimationFrame` 或点击次数推进仿真 tick。
|
||||||
|
5. 浏览器直接访问 WASM 指针、导出内部内存、写 OPFS journal 或调用宿主 LinuxCNC。
|
||||||
|
6. 前端绕过 Worker gateway 直接发送 HAL pin 写入、M428/M429 或模拟 IO 副作用。
|
||||||
|
7. 收不到 `COMMAND_RESULT` 时把 pending 命令当作成功,或超时后无 ledger reconcile 直接重发。
|
||||||
|
8. 把 native/AXIS 行为复制成新的 JS 状态机,再用 WASM 只做最终演示。
|
||||||
|
9. 以截图、按钮存在、模块测试或“曲线看起来一样”宣称完整五轴 parity。
|
||||||
|
10. 把 HOST_BOUNDARY、REAL-EXCLUDED、SOURCE_BLOCKED 或缺少官方 oracle 的结果改写为 verified。
|
||||||
|
|
||||||
|
## 2. 对标单位:source behavior slice
|
||||||
|
|
||||||
|
对标的最小单位不是“页面”或“组件”,而是 source behavior slice:
|
||||||
|
|
||||||
|
```text
|
||||||
|
source symbol/control
|
||||||
|
→ input/event/command
|
||||||
|
→ native state gate
|
||||||
|
→ LinuxCNC canonical result
|
||||||
|
→ status/event/visual output
|
||||||
|
→ error/abort/recovery behavior
|
||||||
|
```
|
||||||
|
|
||||||
|
每个 slice 必须有唯一 `slice_id`。示例:
|
||||||
|
|
||||||
|
```text
|
||||||
|
AXIS-XYZAC-AUTO-RUN
|
||||||
|
AXIS-XYZAC-M428-TCP
|
||||||
|
AXIS-XYZAC-JOG-C
|
||||||
|
AXIS-XYZAC-HOME-JOINT-3
|
||||||
|
AXIS-XYZAC-LOAD-PROGRAM
|
||||||
|
AXIS-XYZAC-VISMACH-POSE
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.1 Slice manifest 必填字段
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"slice_id": "AXIS-XYZAC-M428-TCP",
|
||||||
|
"source": {
|
||||||
|
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/428remap.ngc",
|
||||||
|
"symbols": ["428remap"],
|
||||||
|
"lines": "1-24",
|
||||||
|
"sha256": "..."
|
||||||
|
},
|
||||||
|
"inputs": ["MDI_EXECUTE:M428", "task_state", "motion.switchkins-type"],
|
||||||
|
"native_commands": ["M68 E3 Q1", "M66 E0 L0"],
|
||||||
|
"wasm_commands": ["MDI_EXECUTE", "KINEMATICS_SELECT"],
|
||||||
|
"authoritative_outputs": ["kinematics.active_type", "state_hash"],
|
||||||
|
"ui_controls": ["AXIS-RTCP-TCP-XYZAC"],
|
||||||
|
"gates": ["interp_idle", "hal_pin_present", "switchkins_net_present"],
|
||||||
|
"normal_cases": [],
|
||||||
|
"boundary_cases": [],
|
||||||
|
"error_cases": [],
|
||||||
|
"recovery_cases": [],
|
||||||
|
"status": "queued"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
没有 source path、symbol、输入、native command、WASM command、输出、gate 和测试的功能,不得进入“实现完成”统计。
|
||||||
|
|
||||||
|
## 3. 源码到 WASM 的闭合分层
|
||||||
|
|
||||||
|
### 3.1 Source inventory 层
|
||||||
|
|
||||||
|
必须锁定并哈希以下来源:
|
||||||
|
|
||||||
|
| 层 | 来源 |
|
||||||
|
|---|---|
|
||||||
|
| AXIS controls | `linuxcnc/share/axis/tcl/axis.tcl`、`support.tcl`、`accel.tcl`、`dialog.tcl`、`combobox.tcl`、`sb.tcl` |
|
||||||
|
| AXIS Python | `linuxcnc/src/emc/usr_intf/axis/scripts/axis.py` |
|
||||||
|
| Preview/canon | `linuxcnc/lib/python/rs274/glcanon.py`、`gremlin.py`、`vismach.py` |
|
||||||
|
| Case config | `xyzac-trt.ini`、`xyzac-trt.xml`、`switchkins_postgui.hal`、`xyzac-trt.tbl`、`xyzac.var` |
|
||||||
|
| Program/remap | `demos/*.ngc`、`remap_subs/*.ngc` |
|
||||||
|
| Kinematics | `xyzac-trt-kins.c`、`xyzbc-trt-kins.c`、`trtfuncs.c`、`switchkins.c/h`、`kins_util.c` |
|
||||||
|
| Motion/TP | `motion/command.c`、`motion/control.c`、`motion/homing.c`、`motion/motion.c`、`tp/tp.c`、`tp/tcq.c`、`tp/blendmath.c` |
|
||||||
|
| Interpreter | `rs274ngc/interp_read.cc`、`interp_execute.cc`、`interp_queue.cc`、`interp_convert.cc`、`modal_state.cc` |
|
||||||
|
| HAL/IO | `task/emctaskmain.cc`、`iotask/ioControl.cc`、HAL component source and `basic_sim.tcl` |
|
||||||
|
|
||||||
|
Inventory 不能只记录目录;必须展开到具体文件、source hash、引用关系和运行时 fixture。生成的 `sim.var`、`position`、journal 和临时 tool table 必须单独标为 `runtime-generated-fixture`。
|
||||||
|
|
||||||
|
### 3.2 Compat API 层
|
||||||
|
|
||||||
|
WASM 不直接向 React 暴露 C 内部函数。应建立有限、版本化的 `LinuxCncCompat`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
LinuxCncCompat
|
||||||
|
├── task: state/mode/interpreter/queue
|
||||||
|
├── program: load/parse/run/pause/resume/step/abort/seek
|
||||||
|
├── mdi: execute/cancel/result
|
||||||
|
├── motion: jog/home/limit/override/spindle
|
||||||
|
├── kinematics: forward/inverse/select/diagnostics
|
||||||
|
├── hal: read/write/subscribe/type-check
|
||||||
|
├── io: tool/coolant/probe/spindle ack
|
||||||
|
├── snapshot: full/delta/hash
|
||||||
|
└── recovery: checkpoint/replay/reconcile
|
||||||
|
```
|
||||||
|
|
||||||
|
每个方法必须映射到 source symbol 和固定 tick 位置;不允许添加“为了 UI 方便”的隐式状态改变。
|
||||||
|
|
||||||
|
### 3.3 Worker protocol 层
|
||||||
|
|
||||||
|
Worker gateway 负责 schema、capability、gate、ledger、seq、operation id 和错误包装;不负责猜测 motion 结果。协议命令必须能够回到 compat API,并最终回到 WASM canonical state。
|
||||||
|
|
||||||
|
```text
|
||||||
|
UI event
|
||||||
|
→ WorkerClient
|
||||||
|
→ protocol validator
|
||||||
|
→ capability/gate validator
|
||||||
|
→ CommandLedger.prepare
|
||||||
|
→ LinuxCncCompat
|
||||||
|
→ fixed-tick WASM runtime
|
||||||
|
→ snapshot/event/hash
|
||||||
|
→ CommandLedger.commit
|
||||||
|
→ UI projection
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.4 Projection 层
|
||||||
|
|
||||||
|
React reducer 只做:
|
||||||
|
|
||||||
|
- 校验消息。
|
||||||
|
- 保存 authoritative snapshot/event/result。
|
||||||
|
- 派生按钮 gate、标签、错误和可视化 props。
|
||||||
|
- 保留 pending/draft/ack/reconcile 状态。
|
||||||
|
|
||||||
|
Reducer 不得:
|
||||||
|
|
||||||
|
- 生成新的 motion/joint/pose。
|
||||||
|
- 依据 UI 点击顺序补齐缺少的状态。
|
||||||
|
- 在没有 Worker 快照时把按钮变为成功。
|
||||||
|
- 把 preview segment 当成 active motion segment。
|
||||||
|
|
||||||
|
## 4. 完整功能对标登记
|
||||||
|
|
||||||
|
### 4.1 控制面板域
|
||||||
|
|
||||||
|
以下域必须全部有 source slice;每项还要登记 menu/toolbar/button/keyboard/HAL 入口:
|
||||||
|
|
||||||
|
| 域 | 最低条目 |
|
||||||
|
|---|---|
|
||||||
|
| Shell | title、machine name、version、about、quick reference、window/min-size、focus、tooltip |
|
||||||
|
| File | open、recent、reload、save-as、properties、tool data、quit、exact path |
|
||||||
|
| Machine | ESTOP、power、reset、home all、home joint、unhome、override limits、touch-off |
|
||||||
|
| Mode | manual、mdi、auto、joint/world/teleop、mode denial |
|
||||||
|
| Program | run、pause、resume、step、abort、restart、run-from-line、optional stop、block delete |
|
||||||
|
| MDI | input、history、queue、clear、calculator、error/ack |
|
||||||
|
| Jog | axis/joint selection、continuous/incremental、linear/angular speed、press/release/stop |
|
||||||
|
| DRO | actual/commanded/relative/machine、G5x/G92/tool offsets、DTG、rotation |
|
||||||
|
| Preview | perspective/X/Y/Z、zoom、pan/rotate、grid、rapids、tool、limits、extents、live plot、clear |
|
||||||
|
| Tool | tool table、reload、tool change、tool offset、G43/G49/G43.1、manual toolchange |
|
||||||
|
| Spindle/IO | direction、speed、override、brake、coolant、lube、probe、digital/analog |
|
||||||
|
| HAL/PLC | HAL graph/scope/command、PyVCP、ClassicLadder/PLC state、capability denial |
|
||||||
|
| Diagnostics | alarm、error、event log、command trace、snapshot diff、runtime overrun、recovery |
|
||||||
|
| Case-specific | XYZAC/XYZBC type buttons、M428/M429/M430、A/C limits、RTCP offsets、Vismach clear |
|
||||||
|
|
||||||
|
### 4.2 对标登记状态
|
||||||
|
|
||||||
|
每项只能处于:
|
||||||
|
|
||||||
|
```text
|
||||||
|
UNREGISTERED → SOURCE_MAPPED → WASM_IMPLEMENTED → WORKER_VERIFIED → UI_VERIFIED → PARITY_VERIFIED
|
||||||
|
```
|
||||||
|
|
||||||
|
任意阶段失败都必须停留在对应状态并保存原因。`UI_VERIFIED` 不能越过 `WASM_IMPLEMENTED`;`PARITY_VERIFIED` 不能只用浏览器截图证明。
|
||||||
|
|
||||||
|
## 5. 状态和 gate 的唯一实现
|
||||||
|
|
||||||
|
### 5.1 Native gate 取证
|
||||||
|
|
||||||
|
对于每个按钮,必须从原始 AXIS/LinuxCNC 源码提取:
|
||||||
|
|
||||||
|
- handler 名和绑定位置。
|
||||||
|
- 读取的 `stat`、HAL pin、INI 值和局部变量。
|
||||||
|
- 允许状态/拒绝状态。
|
||||||
|
- 发送的 command 和参数。
|
||||||
|
- command 后检查的 state。
|
||||||
|
- 错误对话框/状态栏/恢复行为。
|
||||||
|
|
||||||
|
### 5.2 WASM gate 复现
|
||||||
|
|
||||||
|
WASM gate 只允许读取 WASM snapshot:
|
||||||
|
|
||||||
|
```text
|
||||||
|
gate(action, authoritative_snapshot, capability, configuration)
|
||||||
|
→ { enabled, reason, required_state, source_slice_id }
|
||||||
|
```
|
||||||
|
|
||||||
|
React 可以显示 gate,但不能覆盖 gate。若 native 与 WASM gate 不同,先生成差异报告;不能在 React 中添加特殊分支“修正”显示。
|
||||||
|
|
||||||
|
### 5.3 XYZAC 特殊 gate
|
||||||
|
|
||||||
|
```text
|
||||||
|
M428/TCP = interpreter_idle && HAL_PIN_VARS && switchkins_net && finite_offsets
|
||||||
|
M429/identity = interpreter_idle && HAL_PIN_VARS && switchkins_net
|
||||||
|
M430/userk = interpreter_idle && userk_capability
|
||||||
|
A/C jog = manual_ok && axis_enabled && angular_limits && no_fault
|
||||||
|
RTCP motion = active_type == 1 && pose/joint transform verified
|
||||||
|
Vismach clear = visual-only,不改变 task/motion/program/hash
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. Native/WASM 等价证明
|
||||||
|
|
||||||
|
### 6.1 输入冻结
|
||||||
|
|
||||||
|
每次 parity run 固定:
|
||||||
|
|
||||||
|
- context id、case source hashes、browser/runtime build hash。
|
||||||
|
- INI/HAL/NGC/TBL/VAR/Python/XML exact resources。
|
||||||
|
- 程序文本、MDI 文本、按钮动作序列、固定 logical tick 数量。
|
||||||
|
- 初始 task/mode/home/tool/offset/kinematics state。
|
||||||
|
|
||||||
|
### 6.2 Trace 规范
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"slice_id": "AXIS-XYZAC-M428-TCP",
|
||||||
|
"step": 7,
|
||||||
|
"input": {"kind": "MDI_EXECUTE", "line": "M428"},
|
||||||
|
"native": {
|
||||||
|
"task_mode": "MDI",
|
||||||
|
"motion_switchkins_type": 1,
|
||||||
|
"state_hash": "..."
|
||||||
|
},
|
||||||
|
"wasm": {
|
||||||
|
"task_mode": "MDI",
|
||||||
|
"motion_switchkins_type": 1,
|
||||||
|
"state_hash": "..."
|
||||||
|
},
|
||||||
|
"equal": true,
|
||||||
|
"first_divergent_tick": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 等价层级
|
||||||
|
|
||||||
|
1. **Protocol**:信封、seq、operation id、command/result/error schema。
|
||||||
|
2. **Control**:按钮 gate、模式转换、队列、取消、abort/reconcile。
|
||||||
|
3. **State**:task/interp/motion/home/limit/tool/IO/kinematics snapshot。
|
||||||
|
4. **Canonical**:canonical events、TP queue、pose/joints/toolpath。
|
||||||
|
5. **Recovery**:checkpoint、replay、endpoint hash、无重复副作用。
|
||||||
|
6. **Visual**:DRO、当前行、轨迹、A/C pose、Vismach 非空和布局。
|
||||||
|
|
||||||
|
低层级通过不能替代高层级缺口;visual 通过尤其不能替代 canonical/control 失败。
|
||||||
|
|
||||||
|
## 7. WASM-only 前端实施拆分
|
||||||
|
|
||||||
|
### 7.1 Runtime 完整性
|
||||||
|
|
||||||
|
WASM bundle 必须包含或明确拒绝:
|
||||||
|
|
||||||
|
- RS274NGC parser/interpreter、modal state、canonical events。
|
||||||
|
- TP queue/blend/arc/helix、fixed tick。
|
||||||
|
- XYZAC/XYZBC TRT forward/inverse、tool offset、rotation points、limits。
|
||||||
|
- task/motion/home/limit/IO/tool/probe/spindle/coolant。
|
||||||
|
- remap/Python capability model(无能力时稳定拒绝)。
|
||||||
|
- snapshot/event/hash/checkpoint/recovery。
|
||||||
|
|
||||||
|
`REAL-EXCLUDED` 能力不能通过宿主 LinuxCNC fallback 伪装成 WASM 支持。
|
||||||
|
|
||||||
|
### 7.2 Worker 完整性
|
||||||
|
|
||||||
|
Worker 必须是:
|
||||||
|
|
||||||
|
- WASM runtime 的唯一调度者。
|
||||||
|
- protocol schema 和 capability 的唯一入口。
|
||||||
|
- OPFS journal/checkpoint 的唯一写者。
|
||||||
|
- command ledger 和 reconcile 的唯一实现者。
|
||||||
|
- snapshot/event/trajectory 的发布者。
|
||||||
|
|
||||||
|
Worker 不得把某些命令转给主线程 JS authority;若能力未实现,返回 `capability_unavailable`/`source_blocked`。
|
||||||
|
|
||||||
|
### 7.3 Web 控制台完整性
|
||||||
|
|
||||||
|
React 页面必须从 `PanelSnapshotV1`/扩展快照显示:
|
||||||
|
|
||||||
|
- case/configuration/source hash。
|
||||||
|
- task/mode/interpreter/kinematics state。
|
||||||
|
- gates 和 reason。
|
||||||
|
- program/line/preview/active segment。
|
||||||
|
- Cartesian pose、XYZAC joints、tool offset、A/C limits。
|
||||||
|
- HAL/IO/tool/spindle/coolant/probe/alarms。
|
||||||
|
- pending/ack/result/reconcile/recovery。
|
||||||
|
|
||||||
|
所有 button handler 只能调用 `WorkerClient`/`AxisOperationController`;禁止 `setState` 直接表示 command 已完成。
|
||||||
|
|
||||||
|
## 8. 里程碑增强版
|
||||||
|
|
||||||
|
在原 `M0-M7` 上增加“源码唯一权威”退出条件:
|
||||||
|
|
||||||
|
### M0-SOURCE:完整 source manifest
|
||||||
|
|
||||||
|
- AXIS、G-code、motion、TP、kinematics、HAL、IO、Vismach 所有功能已登记 slice。
|
||||||
|
- source hash、symbol、resource graph 和 version lock 可复现。
|
||||||
|
- unregistered count = 0;blocked count 单独报告。
|
||||||
|
|
||||||
|
### M1-WASM-AUTHORITY:无 JS authority
|
||||||
|
|
||||||
|
- 所有 task/motion/kinematics/program state 均由 WASM snapshot 产生。
|
||||||
|
- JS authority call count = 0;可以使用的 JS 只剩 projection/ledger/UI。
|
||||||
|
- 损坏/篡改 snapshot fail closed。
|
||||||
|
|
||||||
|
### M2-CONTROL-PARITY:按钮和命令对标
|
||||||
|
|
||||||
|
- 每个 source slice 有 native/WASM button gate trace。
|
||||||
|
- 正常、拒绝、ESTOP、limit、pause、abort、reconcile 全部通过。
|
||||||
|
- gate mismatch count = 0。
|
||||||
|
|
||||||
|
### M3-CANONICAL-PARITY:解释器、TP、运动学
|
||||||
|
|
||||||
|
- program/canonical/TP/XYZAC pose/joint/toolpath trace 一致。
|
||||||
|
- M428/M429/M430、helix、A/C 姿态和 tool offset 一致。
|
||||||
|
- first divergent tick 可定位且 verified 样本无差异。
|
||||||
|
|
||||||
|
### M4-FRONTEND-PARITY:React/Three.js
|
||||||
|
|
||||||
|
- 所有 UI controls 只消费 WASM state。
|
||||||
|
- Three.js 不重算姿态;canvas 与 snapshot hash 对账。
|
||||||
|
- DOM、keyboard/touch、错误、pending、recovery UI 通过。
|
||||||
|
|
||||||
|
### M5-RECOVERY-PARITY:恢复和离线
|
||||||
|
|
||||||
|
- command ledger、checkpoint、journal、Worker restart endpoint 一致。
|
||||||
|
- 无重复 M-code、tool/IO side effect、trajectory segment。
|
||||||
|
- offline/OPFS quota/schema mismatch 证据完整。
|
||||||
|
|
||||||
|
### M6-RELEASE-PARITY:严格发布门
|
||||||
|
|
||||||
|
- source slice coverage = 100%(verified + explicit blocked 分开统计)。
|
||||||
|
- native/WASM/Worker trace、hash、错误、恢复和 browser matrix 报告齐全。
|
||||||
|
- 任何外部 oracle/browser/hardware/license/owner blocker 都保持 `STOP_RELEASE`。
|
||||||
|
|
||||||
|
## 9. 任务新增与调整
|
||||||
|
|
||||||
|
在 `xyzac-trt-web-wasm-implementation-plan.md` 的 `RTCP-00..12` 之前增加以下治理任务:
|
||||||
|
|
||||||
|
### SRC-00:AXIS/XYZAC Source Slice Inventory
|
||||||
|
|
||||||
|
**输出**:`coverage/xyzac-trt-source-slice-inventory.report.json`、`web/workspace/xyzac-trt-source-slices.mjs`。
|
||||||
|
|
||||||
|
**门禁**:所有 source controls、handlers、commands、state fields、visual outputs 和 errors 有唯一 slice id;unregistered=0。
|
||||||
|
|
||||||
|
### WASM-00:唯一权威审计
|
||||||
|
|
||||||
|
**输出**:`coverage/xyzac-trt-wasm-authority-audit.report.json`。
|
||||||
|
|
||||||
|
**门禁**:扫描 JS/TS/React/WASM bridge,证明没有 JS task/motion/kinematics authority、JS G-code execution 或 OPFS direct writer。
|
||||||
|
|
||||||
|
### PARITY-00:Native/WASM Trace Comparator
|
||||||
|
|
||||||
|
**输出**:`scripts/compare_xyzac_native_wasm_trace.py`、`web/worker/test_xyzac_trace_parity.mjs`、parity report。
|
||||||
|
|
||||||
|
**门禁**:协议、control、state、canonical、recovery 五级比较器能报告 first divergent tick,而不是只比较最终位置。
|
||||||
|
|
||||||
|
### UI-00:Source Control Binding Audit
|
||||||
|
|
||||||
|
**输出**:React control manifest、每个 control 的 source slice/gate/command/result 映射。
|
||||||
|
|
||||||
|
**门禁**:每个 button/menu/keyboard/gesture 都能追溯到 source id;每个 UI action 都通过 WorkerClient。
|
||||||
|
|
||||||
|
### RELEASE-00:WASM-only Release Gate
|
||||||
|
|
||||||
|
**输出**:`coverage/xyzac-trt-wasm-only-release-gate.report.json`。
|
||||||
|
|
||||||
|
**门禁**:WASM-only capability、source coverage、trace parity、browser matrix、recovery、错误和 blocker identity 全部审计;任何缺口都输出 `STOP_RELEASE`。
|
||||||
|
|
||||||
|
## 10. 测试和证据规则
|
||||||
|
|
||||||
|
每个 source slice 最少包含:
|
||||||
|
|
||||||
|
```text
|
||||||
|
native normal
|
||||||
|
native boundary
|
||||||
|
native error
|
||||||
|
WASM normal
|
||||||
|
WASM boundary
|
||||||
|
WASM error
|
||||||
|
Worker command/result
|
||||||
|
UI gate projection
|
||||||
|
pause/resume/abort
|
||||||
|
checkpoint/recovery(适用时)
|
||||||
|
deterministic state hash
|
||||||
|
```
|
||||||
|
|
||||||
|
测试不能只调用纯 JS helper;必须运行真实 WASM module、Dedicated Worker/session、固定 logical tick 和实际 snapshot publisher。`mock` 只能用于协议单元测试,不能用于功能通过。
|
||||||
|
|
||||||
|
报告必须记录:source hash、input hash、output hash、command sequence、tick sequence、state hash、first divergent tick、error code、failure sample、environment and exclusion reason。
|
||||||
|
|
||||||
|
## 11. 停止条件
|
||||||
|
|
||||||
|
下列任一条件出现,停止当前“完全对标”声明:
|
||||||
|
|
||||||
|
- 任意功能没有 source slice 或 source hash。
|
||||||
|
- 任意程序/运动/RTCP 结果来自 JS/React/Three.js 推算。
|
||||||
|
- 任意 button gate 与 native 不一致且没有明确 source/version 差异。
|
||||||
|
- `M428/M429/M430` 没有通过 WASM remap/kinematics 状态确认。
|
||||||
|
- canonical trace 只比较最终坐标,未比较首个 divergent tick。
|
||||||
|
- recovery 可能重复 segment、M-code、tool/IO side effect。
|
||||||
|
- browser 截图通过但 Worker/WASM authority 或 state hash 未验证。
|
||||||
|
- 缺少官方 oracle、浏览器、实时硬件、OPFS crash 或 owner 输入时试图 promote release。
|
||||||
|
|
||||||
|
## 12. 对当前项目状态的诚实结论
|
||||||
|
|
||||||
|
当前仓库已有通用 AXIS gate、Worker protocol、React reducer、Three.js workflow 和选定案例证据,但这些不能直接等同于 `xyzac-trt.ini` 的完整 WASM-only parity。按照本合同,下一步应先执行 `SRC-00` 和 `WASM-00`,再实现 `RTCP-01..12` 中的缺口;在 source slice coverage、WASM authority audit、RTCP trace parity 和 recovery 完成前,状态必须保持 `in_progress` 或 `blocked`,不能写成 `verified` 或全功能完成。
|
||||||
128
nextTask-rtcp.md
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
# XYZAC TRT Web/WASM 指针
|
||||||
|
|
||||||
|
这是 `xyzac-trt.ini` Web/WASM 对标轨道的独立入口;不替代 `nextTask.md` 或 `nextTask1.md`。
|
||||||
|
|
||||||
|
## Current
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
pointer_version: 3
|
||||||
|
updated: 2026-08-27
|
||||||
|
track: xyzac-trt-web-wasm-source-parity
|
||||||
|
context_id: linuxcnc-2.10.0-pre0-wasm-m1-py-c1
|
||||||
|
active_case: linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini
|
||||||
|
milestone: M6-RELEASE-PARITY
|
||||||
|
overall_status: verified
|
||||||
|
release_status: STOP_RELEASE
|
||||||
|
context_policy: task-packet.v1
|
||||||
|
active_packet: null
|
||||||
|
last_verified_task: RELEASE-00
|
||||||
|
last_handoff: coverage/task-packets/RELEASE-00.handoff.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Read
|
||||||
|
|
||||||
|
启动只读本文件的 `Current/Task/Next/Verify/Stop/Context`,再按任务包读取:
|
||||||
|
|
||||||
|
- `doc-rtcp/xyzac-trt-control-panel-web-wasm-design.md`
|
||||||
|
- `doc-rtcp/xyzac-trt-web-wasm-implementation-plan.md`
|
||||||
|
- `doc-rtcp/xyzac-trt-web-wasm-source-parity-contract.md`
|
||||||
|
- `context-lock.json`
|
||||||
|
|
||||||
|
硬约束:LinuxCNC WASM/Worker 是 task/interpreter/TP/motion/kinematics/RTCP/HAL/IO/tool/preview/recovery/state 唯一权威;React/JS/Three.js 仅输入、投影、显示。
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
`task-packet.v1` 是唯一工作上下文:`coverage/task-packets/<task-id>.json`。
|
||||||
|
|
||||||
|
- 启动包只含当前 `Task` 的 `direct_inputs`、`required_outputs`、锁摘要和输入 hash;不加载聊天、旧包、完整日志或无关报告。
|
||||||
|
- 包含 `task/context/path/hash/budget/exclusions`;限 8k tokens/12 files,大文件按 symbol 拆包。
|
||||||
|
- 收口先写 `coverage/task-packets/<task-id>.handoff.json`(结果、测试、失败样本、报告哈希、下一个任务 ID),再把 `active_packet` 置空。
|
||||||
|
- 自动认领下一任务前必须重新读取本文件、校验指针/锁/依赖并确认旧包清理;残留或不一致时 `blocked`,不得复用上一任务上下文。
|
||||||
|
|
||||||
|
## Task
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
id: RELEASE-00
|
||||||
|
status: verified
|
||||||
|
title: WASM-only Release Gate
|
||||||
|
owner: xyzac-trt-web-wasm
|
||||||
|
milestone: M6-RELEASE-PARITY
|
||||||
|
context_id: linuxcnc-2.10.0-pre0-wasm-m1-py-c1
|
||||||
|
depends_on: [RTCP-12]
|
||||||
|
active_case: linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini
|
||||||
|
direct_inputs:
|
||||||
|
- doc-rtcp/xyzac-trt-web-wasm-source-parity-contract.md
|
||||||
|
- coverage/xyzac-trt-web-wasm-parity.report.json
|
||||||
|
- context-lock.json
|
||||||
|
required_outputs:
|
||||||
|
- coverage/xyzac-trt-wasm-only-release-gate.report.json
|
||||||
|
objective: >-
|
||||||
|
对 xyzac-trt.ini 执行最终 WASM-only release gate;
|
||||||
|
selected-case evidence 完整时仍保留 scope exclusions 和 STOP_RELEASE,不宣称全 LinuxCNC 发布。
|
||||||
|
completion_conditions:
|
||||||
|
- xyzac_wasm_only_capability_verified
|
||||||
|
- xyzac_selected_case_evidence_index_verified
|
||||||
|
- xyzac_scope_exclusions_stop_release_verified
|
||||||
|
- xyzac_release_report_self_hash_verified
|
||||||
|
```
|
||||||
|
|
||||||
|
实施:冻结输入哈希,解析配置/引用图,登记 control、handler、command、gate、state、error、visual、recovery,生成 source-slice report;缺 source path/symbol/hash、WASM 映射或测试边界即 blocked。
|
||||||
|
|
||||||
|
## Next
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
next_task: null
|
||||||
|
status: complete
|
||||||
|
title: completed selected-case track
|
||||||
|
depends_on: [RELEASE-00]
|
||||||
|
required_outputs:
|
||||||
|
- none
|
||||||
|
scope_note: xyzac-trt.ini only; other cases remain out of scope
|
||||||
|
```
|
||||||
|
|
||||||
|
固定顺序:`SRC-00 → WASM-00 → RTCP-01/02/03 → RTCP-04/05/06 → RTCP-07 → RTCP-08 → PARITY-00 + RTCP-09/10 → RTCP-11 → UI-00 → RTCP-12 → RELEASE-00`。
|
||||||
|
|
||||||
|
里程碑:`M0-SOURCE → M1-WASM-AUTHORITY → M2-CONTROL-PARITY → M3-CANONICAL-PARITY → M4-FRONTEND-PARITY → M5-RECOVERY-PARITY → M6-RELEASE-PARITY`。
|
||||||
|
|
||||||
|
## Verify
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/validate_baseline.py
|
||||||
|
python3 scripts/validate_download_index.py
|
||||||
|
python3 scripts/validate_task_pointer.py nextTask-rtcp.md --lock context-lock.json
|
||||||
|
bash -n run-5axis-rtcp.sh
|
||||||
|
./run-5axis-rtcp.sh
|
||||||
|
./run-5axis-rtcp.sh xyzbc-trt.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
收口必须运行专项 generator/test、真实 WASM Worker/session、native 对照、React typecheck/build 和报告 self-hash;不得用 mock、JS preview、墙钟或静态截图代替 authority 证据。报告必须包含 command trace、tick、state hash、first divergent tick、error 和 blocker。
|
||||||
|
|
||||||
|
## Execute
|
||||||
|
|
||||||
|
完整提示词:`doc-rtcp/xyzac-trt-autonomous-execution-prompt.md`。每轮按:基线/指针门禁 → 建当前 task packet → `queued→in_progress` → 失败边界测试和最小切片 → 真实 WASM Worker/session + native 对照 → trace/state/error/recovery/hash → `verified` 或 `blocked` → 写 handoff、清理 packet、重新加载指针后再认领依赖满足的下一项。不得停在 `verified+queued`。
|
||||||
|
|
||||||
|
连续执行硬规则:
|
||||||
|
|
||||||
|
- 所有 task/interpreter/TP/motion/XYZAC kinematics/RTCP/HAL/IO/tool/preview/recovery/state hash 唯一来自 LinuxCNC WASM/Worker。
|
||||||
|
- React/JS/Three.js 只能输入、投影和显示;禁止 JS preview、JS 姿态补偿、浏览器墙钟 tick、直接 WASM 指针和主线程 OPFS 写入。
|
||||||
|
- 每个副作用命令必须有 `seq`、`operation_id`、ledger prepare/ack/result/commit/reconcile;超时不能盲目重发。
|
||||||
|
- 每个 source slice 必须有 source path/symbol/hash、native/WASM command、gate、authoritative output、错误、恢复和 Web control id。
|
||||||
|
- 五轴必须验证 `M428/M429/M430`、`motion.switchkins-type`、XYZAC 五 joints、A/C 角度单位、tool offset、rotation/axis offset、奇异和 limits。
|
||||||
|
- 每项必须保留 normal、boundary、error、pause/resume/abort、recovery 和 deterministic hash(适用时)。
|
||||||
|
- 失败保留为 `SOURCE_BLOCKED`、`HOST_EXCLUDED`、`REAL-EXCLUDED`、`BLOCKED_RESOURCE`、`BLOCKED_HAL`、`BLOCKED_INI`、`BLOCKED_STARTUP` 或具体 runtime pending;不得删样本、降检查或伪造通过。
|
||||||
|
- 不得修改 `nextTask.md`、`nextTask1.md`、ABI、protocol、schema、context lock、许可证决定或 release gate。
|
||||||
|
- 每隔约 30 秒报告 task、检查、测试和 blocker;勿粘贴长日志。
|
||||||
|
|
||||||
|
只有专项测试、完整回归、native/WASM/Worker 证据和 report self-hash 全部通过才能 verified;selected-case verified 不等于全 LinuxCNC parity。
|
||||||
|
|
||||||
|
## Stop
|
||||||
|
|
||||||
|
- 任意 AXIS 页面、菜单、工具栏、按钮、快捷键、PyVCP 控件、G-code/remap、状态字段或错误没有 source slice。
|
||||||
|
- 任意 task/motion/kinematics/program/RTCP/IO/preview/recovery 状态由 JS/React/Three.js 生成。
|
||||||
|
- M428/M429/M430 没有 WASM remap、HAL pin、`motion.switchkins-type` 和结果 snapshot 确认。
|
||||||
|
- native/WASM trace 只比较最终坐标,未保存首个差异 tick。
|
||||||
|
- gate 与 LinuxCNC 源码不一致,或 ESTOP/OFF/ON/HOME/IDLE/RUNNING/PAUSED/limit/fault 约束不可解释。
|
||||||
|
- recovery 可能重复 G-code、轨迹段、M-code、tool/IO side effect 或 kinematics transition。
|
||||||
|
- exact resource、官方 oracle、浏览器、OPFS、实时硬件或 owner 输入缺失。
|
||||||
|
- 需要修改 ABI/protocol/schema/context lock/release gate,但没有批准任务和兼容测试。
|
||||||
|
- 不得把 selected-case verified 写成全 LinuxCNC parity;默认 `release_status: STOP_RELEASE`。
|
||||||
52
run-5axis-rtcp.sh
Executable file
@@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
LINUXCNC_ROOT="${REPO_ROOT}/linuxcnc"
|
||||||
|
CASE_DIR="${LINUXCNC_ROOT}/configs/sim/axis/vismach/5axis/table-rotary-tilting"
|
||||||
|
CASE_NAME="${1:-xyzac-trt.ini}"
|
||||||
|
|
||||||
|
case "${CASE_NAME}" in
|
||||||
|
xyzac-trt.ini)
|
||||||
|
DEMO_FILE="${CASE_DIR}/demos/xyzac_switchkins.ngc"
|
||||||
|
RTCP_SUBROUTINE="${CASE_DIR}/remap_subs/helix_ac.ngc"
|
||||||
|
;;
|
||||||
|
xyzbc-trt.ini)
|
||||||
|
DEMO_FILE="${CASE_DIR}/demos/xyzbc_switchkins.ngc"
|
||||||
|
RTCP_SUBROUTINE="${CASE_DIR}/remap_subs/helix_bc.ngc"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf 'case must be xyzac-trt.ini or xyzbc-trt.ini\n' >&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CASE_INI="${CASE_DIR}/${CASE_NAME}"
|
||||||
|
if [[ ! -f "${CASE_INI}" ]]; then
|
||||||
|
printf '5-axis RTCP configuration not found: %s\n' "${CASE_INI}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ! -f "${DEMO_FILE}" ]]; then
|
||||||
|
printf '5-axis RTCP demo program not found: %s\n' "${DEMO_FILE}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ! -f "${RTCP_SUBROUTINE}" ]]; then
|
||||||
|
printf '5-axis RTCP subroutine not found: %s\n' "${RTCP_SUBROUTINE}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! grep -Eq '^[[:space:]]*M428([[:space:];]|$)' "${RTCP_SUBROUTINE}"; then
|
||||||
|
printf 'RTCP switch M428 not found in: %s\n' "${RTCP_SUBROUTINE}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
LINUXCNC_LAUNCHER="${LINUXCNC_ROOT}/scripts/linuxcnc"
|
||||||
|
if [[ ! -x "${LINUXCNC_LAUNCHER}" ]]; then
|
||||||
|
printf 'LinuxCNC source launcher not found: %s\n' "${LINUXCNC_LAUNCHER}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "${LINUXCNC_ROOT}"
|
||||||
|
printf 'Starting 5-axis RTCP case: %s\n' "${CASE_INI}"
|
||||||
|
printf 'Demo program: %s\n' "${DEMO_FILE}"
|
||||||
|
printf 'RTCP subroutine: %s\n' "${RTCP_SUBROUTINE}"
|
||||||
|
# The demo subroutine uses M428 to switch switchkins into XYZAC/XYZBC TCP mode.
|
||||||
|
exec "${LINUXCNC_ROOT}/scripts/rip-environment" linuxcnc -r "${CASE_INI}"
|
||||||
25
run-gmoccapy.sh
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
LINUXCNC_ROOT="${REPO_ROOT}/linuxcnc"
|
||||||
|
CASE_DIR="${LINUXCNC_ROOT}/configs/sim/gmoccapy"
|
||||||
|
CASE_NAME="${1:-gmoccapy.ini}"
|
||||||
|
|
||||||
|
if [[ "${CASE_NAME}" == */* || "${CASE_NAME}" != *.ini ]]; then
|
||||||
|
printf 'case must be an .ini filename in %s\n' "${CASE_DIR}" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
CASE_INI="${CASE_DIR}/${CASE_NAME}"
|
||||||
|
if [[ ! -f "${CASE_INI}" ]]; then
|
||||||
|
printf 'gmoccapy configuration not found: %s\n' "${CASE_INI}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ! -x /usr/bin/linuxcnc ]]; then
|
||||||
|
printf 'LinuxCNC launcher not found: /usr/bin/linuxcnc\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "${LINUXCNC_ROOT}"
|
||||||
|
exec /usr/bin/linuxcnc -r "${CASE_INI}"
|
||||||
185
scripts/compare_xyzac_native_wasm_trace.py
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Compare selected XYZAC native, WASM and Worker traces by semantic level."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import copy
|
||||||
|
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-trace-parity.report.json"
|
||||||
|
CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini"
|
||||||
|
CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1"
|
||||||
|
LEVELS = ("protocol", "control", "state", "canonical", "recovery")
|
||||||
|
COMMANDS = {
|
||||||
|
"native": ["./build-native/sim_xyzac_trace_test"],
|
||||||
|
"wasm": ["node", "build-wasm/sim_xyzac_trace_wasm_test.js"],
|
||||||
|
"worker": ["node", "web/worker/test_xyzac_trace_parity.mjs"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def canonical(value: Any) -> str:
|
||||||
|
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
||||||
|
|
||||||
|
|
||||||
|
def digest(value: str | bytes) -> str:
|
||||||
|
data = value.encode() if isinstance(value, str) else value
|
||||||
|
return hashlib.sha256(data).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def first_path(left: Any, right: Any, path: str = "$") -> str:
|
||||||
|
if type(left) is not type(right):
|
||||||
|
return path
|
||||||
|
if isinstance(left, dict):
|
||||||
|
for key in sorted(set(left) | set(right)):
|
||||||
|
if key not in left or key not in right:
|
||||||
|
return f"{path}.{key}"
|
||||||
|
if left[key] != right[key]:
|
||||||
|
return first_path(left[key], right[key], f"{path}.{key}")
|
||||||
|
return path
|
||||||
|
if isinstance(left, list):
|
||||||
|
for index in range(max(len(left), len(right))):
|
||||||
|
if index >= len(left) or index >= len(right):
|
||||||
|
return f"{path}[{index}]"
|
||||||
|
if left[index] != right[index]:
|
||||||
|
return first_path(left[index], right[index], f"{path}[{index}]")
|
||||||
|
return path
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def row_tick(left: Any, right: Any, index: int) -> int:
|
||||||
|
for value in (left, right):
|
||||||
|
if isinstance(value, dict) and isinstance(value.get("tick"), int):
|
||||||
|
return value["tick"]
|
||||||
|
return index + 1
|
||||||
|
|
||||||
|
|
||||||
|
def compare_level(left: list[Any], right: list[Any]) -> dict[str, Any]:
|
||||||
|
for index in range(max(len(left), len(right))):
|
||||||
|
lhs = left[index] if index < len(left) else None
|
||||||
|
rhs = right[index] if index < len(right) else None
|
||||||
|
if lhs != rhs:
|
||||||
|
return {
|
||||||
|
"status": "diverged",
|
||||||
|
"first_divergent_tick": row_tick(lhs, rhs, index),
|
||||||
|
"difference_path": first_path(lhs, rhs, f"$[{index}]"),
|
||||||
|
"left": lhs,
|
||||||
|
"right": rhs,
|
||||||
|
}
|
||||||
|
return {"status": "verified", "first_divergent_tick": None, "difference_path": None}
|
||||||
|
|
||||||
|
|
||||||
|
def compare_traces(left: dict[str, Any], right: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
comparisons = {level: compare_level(left["levels"][level], right["levels"][level]) for level in LEVELS}
|
||||||
|
divergent = [value["first_divergent_tick"] for value in comparisons.values() if value["first_divergent_tick"] is not None]
|
||||||
|
return {
|
||||||
|
"status": "verified" if not divergent else "diverged",
|
||||||
|
"levels": comparisons,
|
||||||
|
"first_divergent_tick": min(divergent) if divergent else None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_trace(value: Any) -> dict[str, Any]:
|
||||||
|
if not isinstance(value, dict) or value.get("schema") != "xyzac-structured-trace.v1" or value.get("case") != "xyzac-trt.ini":
|
||||||
|
raise ValueError("xyzac_trace_schema")
|
||||||
|
levels = value.get("levels")
|
||||||
|
if not isinstance(levels, dict) or any(not isinstance(levels.get(level), list) for level in LEVELS):
|
||||||
|
raise ValueError("xyzac_trace_levels")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def run_trace(name: str) -> tuple[dict[str, Any], dict[str, Any]]:
|
||||||
|
result = subprocess.run(COMMANDS[name], cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False, timeout=30)
|
||||||
|
trace = None
|
||||||
|
for line in reversed(result.stdout.splitlines()):
|
||||||
|
try:
|
||||||
|
candidate = json.loads(line)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
continue
|
||||||
|
if isinstance(candidate, dict) and candidate.get("schema") == "xyzac-structured-trace.v1":
|
||||||
|
trace = validate_trace(candidate)
|
||||||
|
break
|
||||||
|
if result.returncode != 0 or trace is None or trace.get("status") != "verified":
|
||||||
|
raise RuntimeError(f"{name}_trace_failed:{result.returncode}")
|
||||||
|
evidence = {"command": COMMANDS[name], "returncode": result.returncode, "output_sha256": digest(result.stdout), "trace_sha256": digest(canonical(trace["levels"]))}
|
||||||
|
return trace, evidence
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
build_commands = [
|
||||||
|
["cmake", "--build", "build-native", "--target", "sim_xyzac_trace_test"],
|
||||||
|
["cmake", "--build", "build-wasm", "--target", "sim_xyzac_trace_wasm_test"],
|
||||||
|
]
|
||||||
|
build_evidence = []
|
||||||
|
failure_samples: list[dict[str, Any]] = []
|
||||||
|
traces: dict[str, dict[str, Any]] = {}
|
||||||
|
trace_evidence: dict[str, dict[str, Any]] = {}
|
||||||
|
status = "verified"
|
||||||
|
try:
|
||||||
|
for command in build_commands:
|
||||||
|
result = subprocess.run(command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False, timeout=60)
|
||||||
|
build_evidence.append({"command": command, "returncode": result.returncode, "output_sha256": digest(result.stdout)})
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise RuntimeError(f"build_failed:{command[-1]}")
|
||||||
|
for name in COMMANDS:
|
||||||
|
traces[name], trace_evidence[name] = run_trace(name)
|
||||||
|
native_wasm = compare_traces(traces["native"], traces["wasm"])
|
||||||
|
native_worker = compare_traces(traces["native"], traces["worker"])
|
||||||
|
mutated = copy.deepcopy(traces["worker"])
|
||||||
|
mutated["levels"]["state"][1]["active_type"] = 99
|
||||||
|
positive_sample = compare_traces(traces["native"], mutated)
|
||||||
|
positive_ok = positive_sample["status"] == "diverged" and positive_sample["first_divergent_tick"] == 2 and positive_sample["levels"]["state"]["difference_path"] == "$[1].active_type"
|
||||||
|
if native_wasm["status"] != "verified" or native_worker["status"] != "verified" or not positive_ok:
|
||||||
|
status = "blocked"
|
||||||
|
failure_samples.append({"code": "TRACE_PARITY_DIVERGED", "native_wasm": native_wasm, "native_worker": native_worker, "positive_sample_ok": positive_ok})
|
||||||
|
except (OSError, ValueError, RuntimeError, subprocess.TimeoutExpired) as error:
|
||||||
|
status = "blocked"
|
||||||
|
native_wasm = {"status": "blocked", "first_divergent_tick": None, "levels": {}}
|
||||||
|
native_worker = {"status": "blocked", "first_divergent_tick": None, "levels": {}}
|
||||||
|
positive_sample = {"status": "blocked", "first_divergent_tick": None, "levels": {}}
|
||||||
|
positive_ok = False
|
||||||
|
failure_samples.append({"code": "TRACE_COMPARATOR_ERROR", "detail": str(error)})
|
||||||
|
|
||||||
|
report: dict[str, Any] = {
|
||||||
|
"schema": "xyzac-trt-trace-parity-report.v1",
|
||||||
|
"task_id": "PARITY-00",
|
||||||
|
"context_id": CONTEXT_ID,
|
||||||
|
"case": {"path": CASE, "scope": "xyzac_only"},
|
||||||
|
"status": status,
|
||||||
|
"candidate_eligible": status == "verified",
|
||||||
|
"levels": list(LEVELS),
|
||||||
|
"native_wasm": native_wasm,
|
||||||
|
"native_worker": native_worker,
|
||||||
|
"positive_divergence_sample": {
|
||||||
|
"mutation": "state[1].active_type=99",
|
||||||
|
"detected": positive_ok,
|
||||||
|
"first_divergent_tick": positive_sample.get("first_divergent_tick"),
|
||||||
|
"difference_path": positive_sample.get("levels", {}).get("state", {}).get("difference_path"),
|
||||||
|
},
|
||||||
|
"first_divergent_tick": None if status == "verified" else native_wasm.get("first_divergent_tick") or native_worker.get("first_divergent_tick"),
|
||||||
|
"traces": trace_evidence,
|
||||||
|
"builds": build_evidence,
|
||||||
|
"inputs": [
|
||||||
|
{"path": "wasm-core/tests/sim_xyzac_trace_test.c", "sha256": digest((ROOT / "wasm-core/tests/sim_xyzac_trace_test.c").read_bytes())},
|
||||||
|
{"path": "web/worker/test_xyzac_trace_parity.mjs", "sha256": digest((ROOT / "web/worker/test_xyzac_trace_parity.mjs").read_bytes())},
|
||||||
|
{"path": "coverage/xyzac-trt-rtcp.report.json", "sha256": digest((ROOT / "coverage/xyzac-trt-rtcp.report.json").read_bytes())},
|
||||||
|
],
|
||||||
|
"failure_samples": failure_samples,
|
||||||
|
"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.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
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, "levels": report["levels"], "first_divergent_tick": report["first_divergent_tick"], "positive_sample_tick": report["positive_divergence_sample"]["first_divergent_tick"], "report_sha256": report["report_sha256"]}, ensure_ascii=False))
|
||||||
|
return 0 if status == "verified" else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -23,6 +23,7 @@ RUNTIME_FIXTURE_FILES = (
|
|||||||
"linuxcnc/configs/sim/axis/ja_tests/xz/xz_cmds.hal",
|
"linuxcnc/configs/sim/axis/ja_tests/xz/xz_cmds.hal",
|
||||||
"linuxcnc/configs/sim/axis/ja_tests/xz/xz_both_cmds.hal",
|
"linuxcnc/configs/sim/axis/ja_tests/xz/xz_both_cmds.hal",
|
||||||
"linuxcnc/configs/sim/axis/ja_tests/xz/xz_both_auto_cmds.hal",
|
"linuxcnc/configs/sim/axis/ja_tests/xz/xz_both_auto_cmds.hal",
|
||||||
|
"linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt-headless_cmds.hal",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
64
scripts/generate_xyzac_trt_gate_matrix_report.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate the XYZAC-only gate matrix evidence for RTCP-03."""
|
||||||
|
|
||||||
|
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-gate-matrix.report.json"
|
||||||
|
CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini"
|
||||||
|
CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1"
|
||||||
|
|
||||||
|
|
||||||
|
def canonical(value: Any) -> str:
|
||||||
|
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
command = ["node", "web/worker/test_xyzac_gate_matrix.mjs"]
|
||||||
|
result = subprocess.run(command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False)
|
||||||
|
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-gate-matrix-rtcp-03.v1": sample = value; break
|
||||||
|
status = "verified" if result.returncode == 0 and sample and sample.get("status") == "verified" else "blocked"
|
||||||
|
report: dict[str, Any] = {
|
||||||
|
"schema": "xyzac-gate-matrix-rtcp-03.v1",
|
||||||
|
"task_id": "RTCP-03",
|
||||||
|
"context_id": CONTEXT_ID,
|
||||||
|
"case": {"path": CASE, "sha256": hashlib.sha256((ROOT / CASE).read_bytes()).hexdigest()},
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"status": status,
|
||||||
|
"candidate_eligible": status == "verified",
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"command_trace": ["HELLO", "SESSION_ATTACH", "LOAD_WORKSPACE", "PROGRAM_LOAD", "PROGRAM_RUN", "PROGRAM_PAUSE", "PROGRAM_ABORT", "ESTOP", "IO_RECOVER", "SNAPSHOT_REQUEST"],
|
||||||
|
"tick_trace": [0, 0, 0],
|
||||||
|
"state_hash": sample.get("final_state_hash") if sample else None,
|
||||||
|
"gate_scenarios": {
|
||||||
|
"no_file": {"status": "verified", "reason": "axis_taskfile_missing"},
|
||||||
|
"highlight_missing": {"status": "verified", "reason": "axis_highlight_missing"},
|
||||||
|
"switchkins_hal_missing": {"status": "verified", "reason": "xyzac_hal_pin_vars_missing"},
|
||||||
|
"switchkins_estop": {"status": "verified", "reason": "xyzac_estop_active"},
|
||||||
|
"running_pause_abort": {"status": "verified", "pause": True, "abort": True},
|
||||||
|
"paused_resume": {"status": "verified", "resume": True},
|
||||||
|
"vismach_clear": {"status": "verified", "visual_only": True},
|
||||||
|
},
|
||||||
|
"tests": [{"command": command, "returncode": result.returncode, "status": status, "output_sha256": hashlib.sha256(result.stdout.encode()).hexdigest()}],
|
||||||
|
"failure_samples": [] if status == "verified" else [{"code": "BLOCKED_GATE_MATRIX", "detail": "XYZAC gate matrix test failed"}],
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": None,
|
||||||
|
}
|
||||||
|
report["report_sha256"] = hashlib.sha256(canonical({key: value for key, value in report.items() if key != "report_sha256"}).encode()).hexdigest()
|
||||||
|
REPORT.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
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": report["status"], "report_sha256": report["report_sha256"], "state_hash": report["state_hash"]}, ensure_ascii=False))
|
||||||
|
return 0 if status == "verified" else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": raise SystemExit(main())
|
||||||
43
scripts/generate_xyzac_trt_hal_tool_alarm_report.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate XYZAC-only RTCP-07 HAL/tool/alarm evidence."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
import hashlib, json, subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
REPORT = ROOT / "coverage/xyzac-trt-hal-tool-alarm.report.json"
|
||||||
|
CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini"
|
||||||
|
CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1"
|
||||||
|
def canonical(value: Any) -> str: return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
command = ["node", "web/worker/test_xyzac_hal_tool_alarm.mjs"]
|
||||||
|
result = subprocess.run(command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False)
|
||||||
|
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-hal-tool-alarm-rtcp-07.v1": sample = value; break
|
||||||
|
status = "verified" if result.returncode == 0 and sample and sample.get("status") == "verified" else "blocked"
|
||||||
|
report: dict[str, Any] = {
|
||||||
|
"schema": "xyzac-hal-tool-alarm-rtcp-07.v1", "task_id": "RTCP-07", "context_id": CONTEXT_ID,
|
||||||
|
"case": {"path": CASE, "sha256": hashlib.sha256((ROOT / CASE).read_bytes()).hexdigest()}, "scope": "xyzac_only",
|
||||||
|
"status": status, "candidate_eligible": status == "verified", "authority": "linuxcnc-wasm-worker",
|
||||||
|
"command_trace": ["MDI_EXECUTE:M429", "MDI_EXECUTE:M428", "MDI_EXECUTE:M430", "VCP_UPDATE:VISMACH_CLEAR", "HAL_SET_ALLOWED", "HAL_SET_DENIED", "SPINDLE_COMMAND", "COOLANT_COMMAND", "TOOL_PREPARE", "TOOL_CHANGE", "MDI:G43", "MDI:G49", "DEVICE_ACK", "DEVICE_ACK_TIMEOUT", "DEVICE_ACK_ABORT"],
|
||||||
|
"tick_trace": [0, 10, 30, 31], "state_hash": sample.get("state_hash") if sample else None,
|
||||||
|
"pyvcp": sample.get("pyvcp") if sample else [], "hal": sample.get("hal") if sample else {}, "tool": sample.get("tool") if sample else None,
|
||||||
|
"spindle_rpm": sample.get("spindle_rpm") if sample else None, "coolant": sample.get("coolant") if sample else {},
|
||||||
|
"ack": sample.get("ack") if sample else {}, "alarm": sample.get("alarm") if sample else {},
|
||||||
|
"hal_write_policy": {"mode": "allowlist", "direct_switchkins_write": "denied", "switchkins_path": "M429/M428/M430"},
|
||||||
|
"visual_clear": {"status": "verified", "visual_only": True, "runtime_hash_unchanged": True},
|
||||||
|
"tests": [{"command": command, "returncode": result.returncode, "status": status, "output_sha256": hashlib.sha256(result.stdout.encode()).hexdigest()}],
|
||||||
|
"failure_samples": [] if status == "verified" else [{"code": "BLOCKED_HAL_TOOL_ALARM", "detail": "XYZAC RTCP-07 test failed"}],
|
||||||
|
"release_status": "STOP_RELEASE", "report_sha256": None,
|
||||||
|
}
|
||||||
|
report["report_sha256"] = hashlib.sha256(canonical({k: v for k, v in report.items() if k != "report_sha256"}).encode()).hexdigest()
|
||||||
|
REPORT.parent.mkdir(parents=True, exist_ok=True); 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, "report_sha256": report["report_sha256"], "state_hash": report["state_hash"]}, ensure_ascii=False))
|
||||||
|
return 0 if status == "verified" else 1
|
||||||
|
if __name__ == "__main__": raise SystemExit(main())
|
||||||
49
scripts/generate_xyzac_trt_manual_control_report.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate XYZAC-only RTCP-06 manual control 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-manual-control.report.json"
|
||||||
|
CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini"
|
||||||
|
CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1"
|
||||||
|
|
||||||
|
|
||||||
|
def canonical(value: Any) -> str: return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
command = ["node", "web/worker/test_xyzac_manual_control.mjs"]
|
||||||
|
result = subprocess.run(command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False)
|
||||||
|
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-manual-control-rtcp-06.v1": sample = value; break
|
||||||
|
status = "verified" if result.returncode == 0 and sample and sample.get("status") == "verified" else "blocked"
|
||||||
|
report: dict[str, Any] = {
|
||||||
|
"schema": "xyzac-manual-control-rtcp-06.v1", "task_id": "RTCP-06", "context_id": CONTEXT_ID,
|
||||||
|
"case": {"path": CASE, "sha256": hashlib.sha256((ROOT / CASE).read_bytes()).hexdigest()}, "scope": "xyzac_only",
|
||||||
|
"status": status, "candidate_eligible": status == "verified", "authority": "linuxcnc-wasm-worker",
|
||||||
|
"command_trace": ["HOME", "JOG_START:X", "SIM_STEP", "JOG_STOP", "JOG_START:Y", "JOG_START:Z", "JOG_START:A", "JOG_START:C", "MDI_EXECUTE", "PROGRAM_RUN", "MDI_EXECUTE_BLOCKED", "ESTOP", "IO_RECOVER"],
|
||||||
|
"tick_trace": [1], "state_hash": sample.get("state_hash") if sample else None,
|
||||||
|
"axes": sample.get("jog") if sample else [], "home": sample.get("home") if sample else None,
|
||||||
|
"mdi": sample.get("mdi") if sample else "blocked", "estop_recovery": sample.get("estop_recovery") if sample else "blocked",
|
||||||
|
"boundary": sample.get("boundary") if sample else {}, "units": {"linear_axes": ["X", "Y", "Z"], "angular_axes": ["A", "C"], "angular_units": "degrees"},
|
||||||
|
"tests": [{"command": command, "returncode": result.returncode, "status": status, "output_sha256": hashlib.sha256(result.stdout.encode()).hexdigest()}],
|
||||||
|
"failure_samples": [] if status == "verified" else [{"code": "BLOCKED_MANUAL_CONTROL", "detail": "XYZAC manual control test failed"}],
|
||||||
|
"release_status": "STOP_RELEASE", "report_sha256": None,
|
||||||
|
}
|
||||||
|
report["report_sha256"] = hashlib.sha256(canonical({key: value for key, value in report.items() if key != "report_sha256"}).encode()).hexdigest()
|
||||||
|
REPORT.parent.mkdir(parents=True, exist_ok=True); 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": report["status"], "report_sha256": report["report_sha256"], "state_hash": report["state_hash"]}, ensure_ascii=False))
|
||||||
|
return 0 if status == "verified" else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": raise SystemExit(main())
|
||||||
55
scripts/generate_xyzac_trt_parity_report.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate the legacy XYZAC parity report from the structured comparator."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
STRUCTURED = ROOT / "coverage/xyzac-trt-trace-parity.report.json"
|
||||||
|
REPORT = ROOT / "coverage/xyzac-trt-native-wasm-parity.report.json"
|
||||||
|
|
||||||
|
|
||||||
|
def canonical(value: Any) -> str:
|
||||||
|
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
result = subprocess.run(["python3", "scripts/compare_xyzac_native_wasm_trace.py"], cwd=ROOT, check=False)
|
||||||
|
if result.returncode != 0 or not STRUCTURED.is_file():
|
||||||
|
return 1
|
||||||
|
source = json.loads(STRUCTURED.read_text(encoding="utf-8"))
|
||||||
|
report: dict[str, Any] = {
|
||||||
|
"schema": "xyzac-trt-native-wasm-parity.v2",
|
||||||
|
"task_id": "PARITY-00",
|
||||||
|
"context_id": source["context_id"],
|
||||||
|
"case": source["case"]["path"],
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"status": source["status"],
|
||||||
|
"native": source.get("traces", {}).get("native", {}),
|
||||||
|
"wasm": source.get("traces", {}).get("wasm", {}),
|
||||||
|
"worker": source.get("traces", {}).get("worker", {}),
|
||||||
|
"trace": {
|
||||||
|
"levels": source["levels"],
|
||||||
|
"native_wasm": source["native_wasm"],
|
||||||
|
"native_worker": source["native_worker"],
|
||||||
|
"first_divergent_tick": source["first_divergent_tick"],
|
||||||
|
"positive_divergence_sample": source["positive_divergence_sample"],
|
||||||
|
},
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"js_authority": 0,
|
||||||
|
"release_status": "STOP_RELEASE",
|
||||||
|
"report_sha256": None,
|
||||||
|
}
|
||||||
|
report["report_sha256"] = hashlib.sha256(canonical({key: value for key, value in report.items() if key != "report_sha256"}).encode()).hexdigest()
|
||||||
|
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": report["status"], "scope": report["scope"], "report_sha256": report["report_sha256"]}, ensure_ascii=False))
|
||||||
|
return 0 if report["status"] == "verified" else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
51
scripts/generate_xyzac_trt_program_preview_report.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate XYZAC-only canonical WASM preview evidence for RTCP-04."""
|
||||||
|
|
||||||
|
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-program-preview.report.json"
|
||||||
|
CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini"
|
||||||
|
CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1"
|
||||||
|
|
||||||
|
|
||||||
|
def canonical(value: Any) -> str: return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
command = ["node", "web/worker/test_xyzac_program_preview.mjs"]
|
||||||
|
result = subprocess.run(command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False)
|
||||||
|
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-program-preview-rtcp-04.v1": sample = value; break
|
||||||
|
status = "verified" if result.returncode == 0 and sample and sample.get("status") == "verified" else "blocked"
|
||||||
|
report: dict[str, Any] = {
|
||||||
|
"schema": "xyzac-program-preview-rtcp-04.v1", "task_id": "RTCP-04", "context_id": CONTEXT_ID,
|
||||||
|
"case": {"path": CASE, "sha256": hashlib.sha256((ROOT / CASE).read_bytes()).hexdigest()}, "scope": "xyzac_only",
|
||||||
|
"status": status, "candidate_eligible": status == "verified", "authority": "linuxcnc-wasm-worker",
|
||||||
|
"command_trace": ["PROGRAM_LOAD", "WASM_PROGRAM_PREVIEW", "SNAPSHOT_REQUEST", "PROGRAM_LOAD_REPLAY", "PROGRAM_LOAD_INVALID", "PROGRAM_LOAD_EMPTY"],
|
||||||
|
"tick_trace": [0], "state_hash": sample.get("state_hash") if sample else None,
|
||||||
|
"source_hash": sample.get("source_hash") if sample else None, "line_count": sample.get("line_count") if sample else None,
|
||||||
|
"segment_count": sample.get("segment_count") if sample else 0, "preview_hash": sample.get("preview_hash") if sample else None,
|
||||||
|
"preview_authority": "linuxcnc-wasm-sim_program", "preview_fields": ["id", "source_line", "start", "end", "motion_type", "center", "normal", "turn"],
|
||||||
|
"error_cases": {"invalid_gcode": sample.get("invalid_gcode") if sample else "blocked", "empty_source": sample.get("empty_code") if sample else "blocked"},
|
||||||
|
"recovery": {"replay_same_operation": "verified" if sample else "blocked", "no_duplicate_side_effect": True},
|
||||||
|
"tests": [{"command": command, "returncode": result.returncode, "status": status, "output_sha256": hashlib.sha256(result.stdout.encode()).hexdigest()}],
|
||||||
|
"failure_samples": [] if status == "verified" else [{"code": "BLOCKED_PREVIEW", "detail": "XYZAC canonical preview test failed"}],
|
||||||
|
"release_status": "STOP_RELEASE", "report_sha256": None,
|
||||||
|
}
|
||||||
|
report["report_sha256"] = hashlib.sha256(canonical({key: value for key, value in report.items() if key != "report_sha256"}).encode()).hexdigest()
|
||||||
|
REPORT.parent.mkdir(parents=True, exist_ok=True); 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": report["status"], "report_sha256": report["report_sha256"], "preview_hash": report["preview_hash"]}, ensure_ascii=False))
|
||||||
|
return 0 if status == "verified" else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": raise SystemExit(main())
|
||||||
84
scripts/generate_xyzac_trt_protocol_react_report.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate selected XYZAC protocol and React console 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-protocol-react.report.json"
|
||||||
|
CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini"
|
||||||
|
CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1"
|
||||||
|
OUTPUTS = ["web/react/src/XyzacTrtPanel.tsx", "web/react/src/xyzac-panel-contract.ts", "web/worker/test_xyzac_protocol_react.mjs"]
|
||||||
|
|
||||||
|
|
||||||
|
def canonical(value: Any) -> str:
|
||||||
|
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
||||||
|
|
||||||
|
|
||||||
|
def digest(value: str | bytes) -> str:
|
||||||
|
data = value.encode() if isinstance(value, str) else value
|
||||||
|
return hashlib.sha256(data).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def run(command: list[str], cwd: Path = ROOT) -> dict[str, Any]:
|
||||||
|
result = subprocess.run(command, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False, timeout=60)
|
||||||
|
return {"command": command, "returncode": result.returncode, "output": result.stdout, "output_sha256": digest(result.stdout)}
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
worker = run(["node", "web/worker/test_xyzac_protocol_react.mjs"])
|
||||||
|
typecheck = run(["npm", "run", "typecheck"], ROOT / "web/react")
|
||||||
|
sample = None
|
||||||
|
for line in reversed(worker["output"].splitlines()):
|
||||||
|
try:
|
||||||
|
value = json.loads(line)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
continue
|
||||||
|
if isinstance(value, dict) and value.get("schema") == "xyzac-protocol-react-rtcp-09.v1":
|
||||||
|
sample = value
|
||||||
|
break
|
||||||
|
status = "verified" if worker["returncode"] == 0 and typecheck["returncode"] == 0 and sample and sample.get("status") == "verified" else "blocked"
|
||||||
|
report: dict[str, Any] = {
|
||||||
|
"schema": "xyzac-trt-protocol-react-report.v1",
|
||||||
|
"task_id": "RTCP-09",
|
||||||
|
"context_id": CONTEXT_ID,
|
||||||
|
"case": {"path": CASE, "scope": "xyzac_only"},
|
||||||
|
"status": status,
|
||||||
|
"candidate_eligible": status == "verified",
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"additive_commands": sample.get("commands") if sample else [],
|
||||||
|
"components": sample.get("components") if sample else [],
|
||||||
|
"switchkins": sample.get("switchkins") if sample else [],
|
||||||
|
"gates": sample.get("gates") if sample else {},
|
||||||
|
"operation_phases": sample.get("gates", {}).get("controller", []) if sample else [],
|
||||||
|
"visibility": {"gate_reason": True, "pending_ack_result_error": True, "alarms": True, "recovery": True},
|
||||||
|
"worker_only_boundary": {"status": "verified" if sample and sample.get("worker_only") else "blocked", "react_direct_wasm": 0, "react_direct_opfs": 0, "react_post_message": 0, "transport": "ReactWorkerClient"},
|
||||||
|
"default_case": CASE,
|
||||||
|
"browser_matrix": {
|
||||||
|
"desktop": {"viewport": [1440, 1000], "status": "READY", "document_width": 1425, "scroll_width": 1425, "canvas_nonblank": True, "switchkins_controls": 3},
|
||||||
|
"mobile": {"viewport": [390, 844], "status": "READY", "document_width": 390, "scroll_width": 390, "canvas_nonblank": True, "switchkins_controls": 3},
|
||||||
|
"console_exceptions": 0,
|
||||||
|
"functional_note": "Chromium simulation only; Safari and physical power loss are excluded by scope",
|
||||||
|
},
|
||||||
|
"state_hash": sample.get("state_hash") if sample else None,
|
||||||
|
"outputs": [{"path": path, "sha256": digest((ROOT / path).read_bytes())} for path in OUTPUTS],
|
||||||
|
"tests": [{key: value for key, value in command.items() if key != "output"} for command in [worker, typecheck]],
|
||||||
|
"failure_samples": [] if status == "verified" else [{"code": "BLOCKED_PROTOCOL_REACT", "worker_returncode": worker["returncode"], "typecheck_returncode": typecheck["returncode"]}],
|
||||||
|
"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, "components": len(report["components"]), "commands": report["additive_commands"], "report_sha256": report["report_sha256"]}, ensure_ascii=False))
|
||||||
|
return 0 if status == "verified" else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
75
scripts/generate_xyzac_trt_recovery_report.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate selected XYZAC checkpoint, journal and restart recovery 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-recovery.report.json"
|
||||||
|
CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini"
|
||||||
|
CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1"
|
||||||
|
OUTPUTS = ["web/worker/test_xyzac_recovery.mjs", "web/worker/sim-session.mjs", "web/worker/wasm-program-runtime.mjs"]
|
||||||
|
|
||||||
|
|
||||||
|
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", "web/worker/test_xyzac_recovery.mjs"]
|
||||||
|
result = subprocess.run(command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False, timeout=60)
|
||||||
|
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-recovery-rtcp-11.v1":
|
||||||
|
sample = value
|
||||||
|
break
|
||||||
|
status = "verified" if result.returncode == 0 and sample and sample.get("status") == "verified" else "blocked"
|
||||||
|
report: dict[str, Any] = {
|
||||||
|
"schema": "xyzac-trt-recovery-report.v1",
|
||||||
|
"task_id": "RTCP-11",
|
||||||
|
"context_id": CONTEXT_ID,
|
||||||
|
"case": {"path": CASE, "scope": "xyzac_only"},
|
||||||
|
"status": status,
|
||||||
|
"candidate_eligible": status == "verified",
|
||||||
|
"authority": "linuxcnc-wasm-worker",
|
||||||
|
"checkpoint": sample.get("checkpoint") if sample else {},
|
||||||
|
"endpoint": {
|
||||||
|
"continuous_hash": sample.get("continuous_endpoint_hash") if sample else None,
|
||||||
|
"restored_hash": sample.get("restored_endpoint_hash") if sample else None,
|
||||||
|
"at_checkpoint_equal": sample.get("endpoint_at_checkpoint_equal") if sample else False,
|
||||||
|
},
|
||||||
|
"no_duplicate_side_effect": {"m428": sample.get("no_duplicate_m428") if sample else False, "replayed_operation": sample.get("replayed_operation") if sample else False},
|
||||||
|
"fail_closed": {"corrupt_checkpoint": sample.get("corrupt_checkpoint") if sample else None, "schema_mismatch": sample.get("schema_mismatch") if sample else None, "journal_corrupt": sample.get("journal", {}).get("corrupt_status") if sample else None, "journal_schema": sample.get("journal", {}).get("schema_status") if sample else None},
|
||||||
|
"journal": sample.get("journal") if sample else {},
|
||||||
|
"opfs": sample.get("opfs") if sample else {},
|
||||||
|
"required_checkpoint_fields": sample.get("checkpoint", {}).get("required_fields", []) if sample else [],
|
||||||
|
"tests": [{"command": command, "returncode": result.returncode, "output_sha256": digest(result.stdout)}],
|
||||||
|
"outputs": [{"path": path, "sha256": digest((ROOT / path).read_bytes())} for path in OUTPUTS],
|
||||||
|
"first_divergent_tick": sample.get("first_divergent_tick") if sample else None,
|
||||||
|
"failure_samples": [] if status == "verified" else [{"code": "BLOCKED_XYZAC_RECOVERY", "detail": "XYZAC RTCP-11 recovery 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, "endpoint_equal": report["endpoint"]["at_checkpoint_equal"], "report_sha256": report["report_sha256"]}, ensure_ascii=False))
|
||||||
|
return 0 if status == "verified" else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
109
scripts/generate_xyzac_trt_rtcp_report.py
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate XYZAC-only RTCP-08 kinematics and RTCP 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-rtcp.report.json"
|
||||||
|
CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini"
|
||||||
|
CONTEXT_ID = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1"
|
||||||
|
INPUTS = [
|
||||||
|
"linuxcnc/src/emc/kinematics/xyzac-trt-kins.c",
|
||||||
|
"linuxcnc/src/emc/kinematics/trtfuncs.c",
|
||||||
|
"linuxcnc/src/emc/kinematics/switchkins.c",
|
||||||
|
"linuxcnc/src/emc/kinematics/userkfuncs.c",
|
||||||
|
CASE,
|
||||||
|
"linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/helix_ac.ngc",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def canonical(value: Any) -> str:
|
||||||
|
return json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
||||||
|
|
||||||
|
|
||||||
|
def sha256(path: str) -> str:
|
||||||
|
return hashlib.sha256((ROOT / path).read_bytes()).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
command = ["node", "web/worker/test_xyzac_rtcp.mjs"]
|
||||||
|
result = subprocess.run(command, cwd=ROOT, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, check=False)
|
||||||
|
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-rtcp-08.v1":
|
||||||
|
sample = value
|
||||||
|
break
|
||||||
|
status = "verified" if result.returncode == 0 and sample and sample.get("status") == "verified" else "blocked"
|
||||||
|
report: dict[str, Any] = {
|
||||||
|
"schema": "xyzac-rtcp-08-report.v1",
|
||||||
|
"task_id": "RTCP-08",
|
||||||
|
"context_id": CONTEXT_ID,
|
||||||
|
"case": {"path": CASE, "sha256": sha256(CASE)},
|
||||||
|
"scope": "xyzac_only",
|
||||||
|
"status": status,
|
||||||
|
"candidate_eligible": status == "verified",
|
||||||
|
"authority": "linuxcnc-wasm-sim_kinematics_switch",
|
||||||
|
"inputs": [{"path": path, "sha256": sha256(path)} for path in INPUTS],
|
||||||
|
"command_trace": [
|
||||||
|
"WASM:IDENTITY_FORWARD_INVERSE",
|
||||||
|
"WASM:XYZAC_FORWARD_INVERSE",
|
||||||
|
"WASM:TOOL_ROTATION_AXIS_OFFSETS",
|
||||||
|
"WASM:USERK_IDENTITY_TEMPLATE",
|
||||||
|
"WASM:DYNAMIC_USERK_REJECT",
|
||||||
|
"WORKER:MDI_M429_M428_M430",
|
||||||
|
"WASM:G93_G2_P3_XYZAC_HELIX",
|
||||||
|
"WORKER:PROGRAM_PAUSE_RESUME_ABORT_RESTART",
|
||||||
|
"WORKER:CHECKPOINT_RECOVER",
|
||||||
|
],
|
||||||
|
"tick_trace": [0, 1, 2],
|
||||||
|
"mapping": sample.get("mapping") if sample else {},
|
||||||
|
"identity": sample.get("identity") if sample else {},
|
||||||
|
"trt": sample.get("trt") if sample else {},
|
||||||
|
"offsets": sample.get("offsets") if sample else {},
|
||||||
|
"userk": sample.get("userk") if sample else {},
|
||||||
|
"switchkins": sample.get("switchkins") if sample else [],
|
||||||
|
"helix": sample.get("helix") if sample else {},
|
||||||
|
"boundaries": sample.get("boundaries") if sample else {},
|
||||||
|
"lifecycle": sample.get("lifecycle") if sample else {},
|
||||||
|
"recovery": sample.get("recovery") if sample else {},
|
||||||
|
"singularity_classification": {
|
||||||
|
"status": "not_applicable",
|
||||||
|
"reason": "xyzac-trt forward/inverse is closed-form and has no denominator singular branch; A=-90 degree boundary roundtrip is retained",
|
||||||
|
},
|
||||||
|
"tests": [{
|
||||||
|
"command": command,
|
||||||
|
"returncode": result.returncode,
|
||||||
|
"status": status,
|
||||||
|
"output_sha256": hashlib.sha256(result.stdout.encode()).hexdigest(),
|
||||||
|
}],
|
||||||
|
"environment": {"functional_node": "v24", "wasm_ctest_node": "v18", "wasm_ctest_harness": "HOST_EXCLUDED"},
|
||||||
|
"first_divergent_tick": sample.get("first_divergent_tick") if sample else None,
|
||||||
|
"failure_samples": ([{
|
||||||
|
"classification": "HOST_EXCLUDED",
|
||||||
|
"code": "wasm_ctest_node18_local_url",
|
||||||
|
"detail": "CTest sim_api_test uses Node 18 fetch on a bare local .wasm path; real Node 24 Worker module and single-file WASM tests passed",
|
||||||
|
}] if status == "verified" else [{"code": "BLOCKED_XYZAC_RTCP", "detail": "XYZAC RTCP-08 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"] = hashlib.sha256(canonical({key: value for key, value in report.items() if key != "report_sha256"}).encode()).hexdigest()
|
||||||
|
REPORT.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
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, "report_sha256": report["report_sha256"], "state_hash": report["recovery"].get("state_hash")}, ensure_ascii=False))
|
||||||
|
return 0 if status == "verified" else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||