Align Web XYZAC command gates with LinuxCNC

This commit is contained in:
2026-08-28 21:33:11 +08:00
parent 78d884bc67
commit cbd0bc1003
30 changed files with 337 additions and 150 deletions

View File

@@ -22,7 +22,7 @@
"ABORT", "ABORT",
"ESTOP" "ESTOP"
], ],
"state_hash": "fnv1a64:7c18597c9e7091d7", "state_hash": "fnv1a64:71e86a767aa1a074",
"status": "verified" "status": "verified"
}, },
"candidate_eligible": true, "candidate_eligible": true,
@@ -97,10 +97,10 @@
} }
}, },
"release_status": "STOP_RELEASE", "release_status": "STOP_RELEASE",
"report_sha256": "fd24448f6f46d484a2e27f711383db6d0695feafb73c18365082975ed33c8f97", "report_sha256": "7fc97babb800be213cb6fc8be5bde213dec2d16905198bfaa5fbb9b1a5f2d24a",
"schema": "xyzac-gate-matrix-rtcp-03.v1", "schema": "xyzac-gate-matrix-rtcp-03.v1",
"scope": "xyzac_only", "scope": "xyzac_only",
"state_hash": "fnv1a64:7b5f8d0ed1c175dd", "state_hash": "fnv1a64:7014ca3ef3a4a3dd",
"status": "verified", "status": "verified",
"task_id": "RTCP-03", "task_id": "RTCP-03",
"tests": [ "tests": [
@@ -109,7 +109,7 @@
"node", "node",
"web/worker/test_xyzac_gate_matrix.mjs" "web/worker/test_xyzac_gate_matrix.mjs"
], ],
"output_sha256": "c0b5ad1449a1bc463f2c282a1b783092ffcadef9bf20b93a56eff285033d971c", "output_sha256": "0b798dd40940ed1f4482b21d07b15bbffd9bd66bb3f198bb8cf0da207da55636",
"returncode": 0, "returncode": 0,
"status": "verified" "status": "verified"
}, },
@@ -118,7 +118,7 @@
"node", "node",
"web/worker/test_xyzac_button_constraints.mjs" "web/worker/test_xyzac_button_constraints.mjs"
], ],
"output_sha256": "2e105b80c6548ef53a2ebc656d6da39fe3bd7a5905f906188a3655e57e345b8e", "output_sha256": "b9e6345ebba8dd794f8d407c68e208565a2b849f9fbfb184b3cc00b38927834e",
"returncode": 0, "returncode": 0,
"status": "verified" "status": "verified"
} }

View File

@@ -54,11 +54,11 @@
"vismach-clear" "vismach-clear"
], ],
"release_status": "STOP_RELEASE", "release_status": "STOP_RELEASE",
"report_sha256": "37026591b5875615e8a68374672fe7a7b433ef53504a3d52bc922a735f6fd140", "report_sha256": "bb76f472e65cec8226cd07e00fcf00921ac44350873df4f65a2a00032925e915",
"schema": "xyzac-hal-tool-alarm-rtcp-07.v1", "schema": "xyzac-hal-tool-alarm-rtcp-07.v1",
"scope": "xyzac_only", "scope": "xyzac_only",
"spindle_rpm": 1200, "spindle_rpm": 1200,
"state_hash": "fnv1a64:198fc8da045d3e0f", "state_hash": "fnv1a64:7ffad38ea9175625",
"status": "verified", "status": "verified",
"task_id": "RTCP-07", "task_id": "RTCP-07",
"tests": [ "tests": [
@@ -67,7 +67,7 @@
"node", "node",
"web/worker/test_xyzac_hal_tool_alarm.mjs" "web/worker/test_xyzac_hal_tool_alarm.mjs"
], ],
"output_sha256": "831953b1e6712c0187d8029facbc07cacf0a1290e17165019801a2000b8b4e40", "output_sha256": "e77d1995a53250530bb5f3a5e4b9babc147a83cf89f989ac5e78f41c22d3aa2f",
"returncode": 0, "returncode": 0,
"status": "verified" "status": "verified"
} }

View File

@@ -61,10 +61,10 @@
}, },
"mdi": "verified", "mdi": "verified",
"release_status": "STOP_RELEASE", "release_status": "STOP_RELEASE",
"report_sha256": "a4ca2d5637e68c387e2ca5be460a2c9340d6b7198715cb1aa95d90055c623058", "report_sha256": "4d4f011724dcbcfacb03e082116d978f1aeba9dcfe8e55402b37a627b082d6cc",
"schema": "xyzac-manual-control-rtcp-06.v1", "schema": "xyzac-manual-control-rtcp-06.v1",
"scope": "xyzac_only", "scope": "xyzac_only",
"state_hash": "fnv1a64:8e9acc7cf9c8cec1", "state_hash": "fnv1a64:4332c2ad8a89898d",
"status": "verified", "status": "verified",
"task_id": "RTCP-06", "task_id": "RTCP-06",
"tests": [ "tests": [
@@ -73,7 +73,7 @@
"node", "node",
"web/worker/test_xyzac_manual_control.mjs" "web/worker/test_xyzac_manual_control.mjs"
], ],
"output_sha256": "c484daae09f135544f0a55376c79377e251e416651ab7d338e8d4d20f0d20768", "output_sha256": "957df6509e77a68a20be63dd82dc06154d7e86c4b0c30b99574227d4b50b91de",
"returncode": 0, "returncode": 0,
"status": "verified" "status": "verified"
} }

View File

@@ -37,12 +37,12 @@
"replay_same_operation": "verified" "replay_same_operation": "verified"
}, },
"release_status": "STOP_RELEASE", "release_status": "STOP_RELEASE",
"report_sha256": "f44c782f09851b87a90e90583657a95c7ea30bffc5fd254fa8e3e16f87ed985e", "report_sha256": "dced60b3901a793dad7d33882f08985febfa11ac38d863020b0137350dfac4e3",
"schema": "xyzac-program-preview-rtcp-04.v1", "schema": "xyzac-program-preview-rtcp-04.v1",
"scope": "xyzac_only", "scope": "xyzac_only",
"segment_count": 2, "segment_count": 2,
"source_hash": "fnv1a64:b7f4678312207b31", "source_hash": "fnv1a64:b7f4678312207b31",
"state_hash": "fnv1a64:dadc751fbbb70835", "state_hash": "fnv1a64:dd298967c8e59be1",
"status": "verified", "status": "verified",
"task_id": "RTCP-04", "task_id": "RTCP-04",
"tests": [ "tests": [
@@ -51,7 +51,7 @@
"node", "node",
"web/worker/test_xyzac_program_preview.mjs" "web/worker/test_xyzac_program_preview.mjs"
], ],
"output_sha256": "96e5c2c6203d26162a49206d5e9416b532bf880065a4a3418c863ea0c32bdb28", "output_sha256": "71682d480c4e9c65128aa87f60c308878dbfe1787f42694def040dd9233fa835",
"returncode": 0, "returncode": 0,
"status": "verified" "status": "verified"
} }

View File

@@ -82,32 +82,32 @@
}, },
{ {
"path": "web/worker/test_xyzac_protocol_react.mjs", "path": "web/worker/test_xyzac_protocol_react.mjs",
"sha256": "daa74b5091de4579b7ce5eb283b802e226e275bf51209af339e4cf6334fd6b5b" "sha256": "b4c50561dbe90ea67bd6153098c9f8dce8480ccafd51d26bf167de3b18a0971d"
} }
], ],
"release_status": "STOP_RELEASE", "release_status": "STOP_RELEASE",
"report_sha256": "f25c81c8aa94762e563bdfacceef0bf8b211ca632b2eed5556c88fd408b1c5b5", "report_sha256": "2a68bfc0f2620b1d4abae70b18ca261802e5450125d37aa7c1e512adfd20ba99",
"schema": "xyzac-trt-protocol-react-report.v1", "schema": "xyzac-trt-protocol-react-report.v1",
"state_hash": "fnv1a64:4cb77e59ba75a666", "state_hash": "fnv1a64:30f3ddcdf9544695",
"status": "verified", "status": "verified",
"switchkins": [ "switchkins": [
{ {
"operation_id": "select-0-4", "operation_id": "select-0-5",
"phase": "committed", "phase": "committed",
"type": 0 "type": 0
}, },
{ {
"operation_id": "select-1-5", "operation_id": "select-1-6",
"phase": "committed", "phase": "committed",
"type": 1 "type": 1
}, },
{ {
"operation_id": "select-2-6", "operation_id": "select-2-7",
"phase": "committed", "phase": "committed",
"type": 2 "type": 2
}, },
{ {
"operation_id": "select-1-7", "operation_id": "select-1-8",
"phase": "committed", "phase": "committed",
"type": 1 "type": 1
} }
@@ -119,7 +119,7 @@
"node", "node",
"web/worker/test_xyzac_protocol_react.mjs" "web/worker/test_xyzac_protocol_react.mjs"
], ],
"output_sha256": "862f3c71bccb137c1551a5bfb4ed85014a7cbb2a2506f265193bbe29944c36a1", "output_sha256": "b2d01a0bdee9b31f6680d0265c4537efd0a1fe12b650383089ad52d1148382bc",
"returncode": 0 "returncode": 0
}, },
{ {

View File

@@ -24,8 +24,8 @@
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1", "context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
"endpoint": { "endpoint": {
"at_checkpoint_equal": true, "at_checkpoint_equal": true,
"continuous_hash": "fnv1a64:b105b6fc1bcc8a70", "continuous_hash": "fnv1a64:dc228a5abee74af6",
"restored_hash": "fnv1a64:857caa278d03c259" "restored_hash": "fnv1a64:6c4a6b85b192d794"
}, },
"exclusions": [ "exclusions": [
"xyzbc", "xyzbc",
@@ -61,11 +61,11 @@
"outputs": [ "outputs": [
{ {
"path": "web/worker/test_xyzac_recovery.mjs", "path": "web/worker/test_xyzac_recovery.mjs",
"sha256": "d25dc8f5de748b37ca231ee7e5a0df049878511f4c0fec98061208730f57b145" "sha256": "d875ef92ef58e591110fdd842e33a081143623b60b92450476b288ce2c72e3e0"
}, },
{ {
"path": "web/worker/sim-session.mjs", "path": "web/worker/sim-session.mjs",
"sha256": "adf00f12f4408bcbe00e296ce3d8003f764e4cdd0849c86039586fecdc822af1" "sha256": "4f674200bfcca9af6bc2c83017329355b88307d0749ebb5c9d0fc8eaecaa2349"
}, },
{ {
"path": "web/worker/wasm-program-runtime.mjs", "path": "web/worker/wasm-program-runtime.mjs",
@@ -73,7 +73,7 @@
} }
], ],
"release_status": "STOP_RELEASE", "release_status": "STOP_RELEASE",
"report_sha256": "be2d59e86990e26858d11e3220cfa63d4b8f5d84f8b81f1067d89b645f78a9ae", "report_sha256": "51beb4f92ebaa633816bdbdd1bea3bc0bd918488bb7173c4082683f06f3ed88d",
"required_checkpoint_fields": [ "required_checkpoint_fields": [
"context_id", "context_id",
"simulation_tick", "simulation_tick",
@@ -95,7 +95,7 @@
"node", "node",
"web/worker/test_xyzac_recovery.mjs" "web/worker/test_xyzac_recovery.mjs"
], ],
"output_sha256": "3649f6971bb8ed8857dd5cab886834485a3d82c8dec71365853481c0f12a46c9", "output_sha256": "43068de07ef8448d4bff5afb300400fd0358a697396d7616c1737dbb733f058d",
"returncode": 0 "returncode": 0
} }
] ]

View File

@@ -74,7 +74,7 @@
}, },
{ {
"path": "web/worker/sim-session.mjs", "path": "web/worker/sim-session.mjs",
"sha256": "adf00f12f4408bcbe00e296ce3d8003f764e4cdd0849c86039586fecdc822af1" "sha256": "4f674200bfcca9af6bc2c83017329355b88307d0749ebb5c9d0fc8eaecaa2349"
}, },
{ {
"path": "web/worker/test_xyzac_exact_preview.mjs", "path": "web/worker/test_xyzac_exact_preview.mjs",
@@ -102,7 +102,7 @@
}, },
{ {
"path": "web/react/src/App.tsx", "path": "web/react/src/App.tsx",
"sha256": "31692dd2df5d9a1a26503e78627c5d10524426c975d8ffb0dfbd3f7cf5c0f8af" "sha256": "2c9aaa252c734a3885d41364cf87f969bf9b58780137e8240906f5be0745b22c"
}, },
{ {
"path": "web/react/src/styles.css", "path": "web/react/src/styles.css",
@@ -110,7 +110,7 @@
}, },
{ {
"path": "wasm-core/src/sim_task.c", "path": "wasm-core/src/sim_task.c",
"sha256": "06aa4ba015d863e1749cfe11877a82507fd6aff2c6922a114f30f6c8871b0b5b" "sha256": "b6f532a725d34a14a0f804c99fd6fd993c59a0626e203d0a3f3af9e8dc68d64b"
}, },
{ {
"path": "wasm-core/src/sim_program.c", "path": "wasm-core/src/sim_program.c",
@@ -289,7 +289,7 @@
"schema": "xyzac-exact-tool-preview.v1", "schema": "xyzac-exact-tool-preview.v1",
"segment_count": 29, "segment_count": 29,
"source_hash": "fnv1a64:522e219683194ad2", "source_hash": "fnv1a64:522e219683194ad2",
"state_hash": "fnv1a64:1b46405fa36a5abe", "state_hash": "fnv1a64:300c582b009b292a",
"status": "verified", "status": "verified",
"visual_segments": 29 "visual_segments": 29
}, },
@@ -350,7 +350,7 @@
"PROGRAM_RUN", "PROGRAM_RUN",
"SIM_STEP" "SIM_STEP"
], ],
"state_hash": "fnv1a64:3fe83d3f4768868c", "state_hash": "fnv1a64:48bf3ba1b27a3272",
"status": "verified" "status": "verified"
}, },
"static": { "static": {
@@ -383,7 +383,7 @@
} }
}, },
"release_status": "STOP_RELEASE", "release_status": "STOP_RELEASE",
"report_sha256": "0127822355f55c04898dcb0f9333d291d5fa8fbb116d7199cbdf02694b97fe87", "report_sha256": "d7447aab0398ac572599ecd7b50f24bf29a1751c0f1723f89eac9dadd1308302",
"schema": "xyzac-trt-tool-preview-report.v1", "schema": "xyzac-trt-tool-preview-report.v1",
"status": "verified", "status": "verified",
"task_id": "RTCP-10-TOOL-PREVIEW", "task_id": "RTCP-10-TOOL-PREVIEW",
@@ -402,7 +402,7 @@
"node", "node",
"web/worker/test_xyzac_exact_preview.mjs" "web/worker/test_xyzac_exact_preview.mjs"
], ],
"output_sha256": "625aaa1f95578666d70390a782745ff1e74862f6ef11bbf2911b1870a2b864dd", "output_sha256": "f4764d5350353402e40de503e040b882a66666888e11a8cf17ca696431d89efd",
"returncode": 0 "returncode": 0
}, },
{ {
@@ -410,7 +410,7 @@
"node", "node",
"web/worker/test_xyzac_machine_sequence.mjs" "web/worker/test_xyzac_machine_sequence.mjs"
], ],
"output_sha256": "a361d9e4f695d048093e7ff4b7b5e6688534071c5b3811a40bbe69e00d114133", "output_sha256": "57599a2a43b0e5af610955e1c54d0f154999175cca25fa0b827d0cff661119ad",
"returncode": 0 "returncode": 0
} }
], ],

View File

@@ -46,7 +46,7 @@
}, },
{ {
"path": "web/worker/test_xyzac_trace_parity.mjs", "path": "web/worker/test_xyzac_trace_parity.mjs",
"sha256": "209254396d3ec828936ec5c2ad34de9044706f7d0309fd5ad4cacae347f15247" "sha256": "564414a776ae502901edeac717b2ae06bcfb1fbdfb06a570c6f0001ac6282328"
}, },
{ {
"path": "coverage/xyzac-trt-rtcp.report.json", "path": "coverage/xyzac-trt-rtcp.report.json",
@@ -130,7 +130,7 @@
"mutation": "state[1].active_type=99" "mutation": "state[1].active_type=99"
}, },
"release_status": "STOP_RELEASE", "release_status": "STOP_RELEASE",
"report_sha256": "234a362308c9caae71955a606d1779f45ce4fe170f476e49a6c5a482dd4351d5", "report_sha256": "4bba1c060681cd39dbaaa1eb3ff3bfe42843581150780d1c5ddd7a896f19118d",
"schema": "xyzac-trt-trace-parity-report.v1", "schema": "xyzac-trt-trace-parity-report.v1",
"status": "verified", "status": "verified",
"task_id": "PARITY-00", "task_id": "PARITY-00",

View File

@@ -128,10 +128,10 @@
"source_slice": "AXIS-XYZAC-PROGRAM-MDI", "source_slice": "AXIS-XYZAC-PROGRAM-MDI",
"sources": [ "sources": [
{ {
"lines": "107-833", "lines": "122-846",
"path": "web/worker/sim-session.mjs", "path": "web/worker/sim-session.mjs",
"role": "session-authority-routing", "role": "session-authority-routing",
"sha256": "adf00f12f4408bcbe00e296ce3d8003f764e4cdd0849c86039586fecdc822af1", "sha256": "4f674200bfcca9af6bc2c83017329355b88307d0749ebb5c9d0fc8eaecaa2349",
"status": "resolved", "status": "resolved",
"symbol": "new ProgramRunner" "symbol": "new ProgramRunner"
}, },
@@ -161,10 +161,10 @@
"symbol": "export class MotionState" "symbol": "export class MotionState"
}, },
{ {
"lines": "107-833", "lines": "122-846",
"path": "web/worker/sim-session.mjs", "path": "web/worker/sim-session.mjs",
"role": "session-authority-routing", "role": "session-authority-routing",
"sha256": "adf00f12f4408bcbe00e296ce3d8003f764e4cdd0849c86039586fecdc822af1", "sha256": "4f674200bfcca9af6bc2c83017329355b88307d0749ebb5c9d0fc8eaecaa2349",
"status": "resolved", "status": "resolved",
"symbol": "new ProgramRunner" "symbol": "new ProgramRunner"
} }
@@ -186,10 +186,10 @@
"symbol": "export class AuthoritativeRuntime" "symbol": "export class AuthoritativeRuntime"
}, },
{ {
"lines": "107-833", "lines": "122-846",
"path": "web/worker/sim-session.mjs", "path": "web/worker/sim-session.mjs",
"role": "session-authority-routing", "role": "session-authority-routing",
"sha256": "adf00f12f4408bcbe00e296ce3d8003f764e4cdd0849c86039586fecdc822af1", "sha256": "4f674200bfcca9af6bc2c83017329355b88307d0749ebb5c9d0fc8eaecaa2349",
"status": "resolved", "status": "resolved",
"symbol": "new ProgramRunner" "symbol": "new ProgramRunner"
} }
@@ -205,7 +205,7 @@
"recovery": "Chromium SIGKILL/profile-reopen, corruption detection and Firefox OPFS round-trip are verified" "recovery": "Chromium SIGKILL/profile-reopen, corruption detection and Firefox OPFS round-trip are verified"
}, },
"release_status": "STOP_RELEASE", "release_status": "STOP_RELEASE",
"report_sha256": "6bf29284946b658ca58c76c91b2dbad3c33f8af7afb74b91aecb5a2842f3b650", "report_sha256": "2ca53b1aa5e691f44280473301772ac7dcace0f492fc309ebacb1f66677bfdff",
"scans": { "scans": {
"js_gcode_matches": [ "js_gcode_matches": [
{ {
@@ -274,12 +274,12 @@
"text": "const result = call(this.core, \"sim_interpreter_execute\", \"number\", [\"number\", \"number\", \"number\"], [this.interpreter, this.blockBuffer, bytes.length]);" "text": "const result = call(this.core, \"sim_interpreter_execute\", \"number\", [\"number\", \"number\", \"number\"], [this.interpreter, this.blockBuffer, bytes.length]);"
}, },
{ {
"line": 1526, "line": 1553,
"path": "web/worker/sim-session.mjs", "path": "web/worker/sim-session.mjs",
"text": "const result = this.xyzacProgram.executeBlock(block);" "text": "const result = this.xyzacProgram.executeBlock(block);"
}, },
{ {
"line": 1545, "line": 1572,
"path": "web/worker/sim-session.mjs", "path": "web/worker/sim-session.mjs",
"text": "const result = this.program.executeBlock(block);" "text": "const result = this.program.executeBlock(block);"
}, },
@@ -292,7 +292,7 @@
"opfs_writer_matches": [ "opfs_writer_matches": [
{ {
"line": 1, "line": 1,
"path": "web/react/dist/assets/sim-worker-entry-BiqKYM9j.js", "path": "web/react/dist/assets/sim-worker-entry-DUl-hrwT.js",
"text": "(function(){var e=(()=>{var e=self.location.href;return(function(t){t||={};var t=t===void 0?{}:t,n,r;t.ready=new Promise(function(e,t){n=e,r=t});var i=Object.assign({},t),a=[],o=typeof window==`object`,s=typeof importScripts==`function`;typ" "text": "(function(){var e=(()=>{var e=self.location.href;return(function(t){t||={};var t=t===void 0?{}:t,n,r;t.ready=new Promise(function(e,t){n=e,r=t});var i=Object.assign({},t),a=[],o=typeof window==`object`,s=typeof importScripts==`function`;typ"
}, },
{ {
@@ -1496,7 +1496,7 @@
"files": [ "files": [
{ {
"path": "web/react/src/App.tsx", "path": "web/react/src/App.tsx",
"sha256": "31692dd2df5d9a1a26503e78627c5d10524426c975d8ffb0dfbd3f7cf5c0f8af" "sha256": "2c9aaa252c734a3885d41364cf87f969bf9b58780137e8240906f5be0745b22c"
}, },
{ {
"path": "web/react/src/ThreeMachineView.tsx", "path": "web/react/src/ThreeMachineView.tsx",
@@ -1652,11 +1652,11 @@
}, },
{ {
"path": "web/worker/sim-session.mjs", "path": "web/worker/sim-session.mjs",
"sha256": "adf00f12f4408bcbe00e296ce3d8003f764e4cdd0849c86039586fecdc822af1" "sha256": "4f674200bfcca9af6bc2c83017329355b88307d0749ebb5c9d0fc8eaecaa2349"
}, },
{ {
"path": "web/worker/sim-task.mjs", "path": "web/worker/sim-task.mjs",
"sha256": "4b4b3aa4b86aba08e5afb8534d98d6ab11c845eeb891cf4793b5906a01de1599" "sha256": "2e055b365b4b3ce2b47de56c987e326183c8f16a4746f5fbe324268a5c681a24"
}, },
{ {
"path": "web/worker/sim-worker.mjs", "path": "web/worker/sim-worker.mjs",
@@ -1968,7 +1968,7 @@
}, },
{ {
"path": "web/worker/test_xyzac_button_constraints.mjs", "path": "web/worker/test_xyzac_button_constraints.mjs",
"sha256": "e5206c37f959d0214f83892dcd2b57abf6d16335ac1f6eedb36e9bbad5a45d5f" "sha256": "faf7aa191e66b4c7a2f4642db19ee7b1712e7781ab474502ab1b01585554c1d0"
}, },
{ {
"path": "web/worker/test_xyzac_exact_preview.mjs", "path": "web/worker/test_xyzac_exact_preview.mjs",
@@ -1976,11 +1976,11 @@
}, },
{ {
"path": "web/worker/test_xyzac_gate_matrix.mjs", "path": "web/worker/test_xyzac_gate_matrix.mjs",
"sha256": "176da9a86c17cef2fb1f17bb65c5854af107524ac26871fa830e17e62fc022cf" "sha256": "7113f7fed0c7f8a66b1b6fc73960d5e84427fce932edb527bb721838aedbfdfc"
}, },
{ {
"path": "web/worker/test_xyzac_hal_tool_alarm.mjs", "path": "web/worker/test_xyzac_hal_tool_alarm.mjs",
"sha256": "8da3024cf0a8fc29f77c481204bce0ed3672cf411a5b52e553cb60c92213b0aa" "sha256": "95bfec604348b3d9a41faa457ff9a60df4ade47e46cabf38be391be2352b9c68"
}, },
{ {
"path": "web/worker/test_xyzac_machine_sequence.mjs", "path": "web/worker/test_xyzac_machine_sequence.mjs",
@@ -1988,7 +1988,7 @@
}, },
{ {
"path": "web/worker/test_xyzac_manual_control.mjs", "path": "web/worker/test_xyzac_manual_control.mjs",
"sha256": "6219a55c4c1470193f984d94fea528e697479903e2623abf57a4d5b5274fd797" "sha256": "113710cb0d89651dfed8d7806ed98ad88b8817bea70aa674f8b039ace45f5187"
}, },
{ {
"path": "web/worker/test_xyzac_program_preview.mjs", "path": "web/worker/test_xyzac_program_preview.mjs",
@@ -1996,15 +1996,15 @@
}, },
{ {
"path": "web/worker/test_xyzac_protocol_react.mjs", "path": "web/worker/test_xyzac_protocol_react.mjs",
"sha256": "daa74b5091de4579b7ce5eb283b802e226e275bf51209af339e4cf6334fd6b5b" "sha256": "b4c50561dbe90ea67bd6153098c9f8dce8480ccafd51d26bf167de3b18a0971d"
}, },
{ {
"path": "web/worker/test_xyzac_recovery.mjs", "path": "web/worker/test_xyzac_recovery.mjs",
"sha256": "d25dc8f5de748b37ca231ee7e5a0df049878511f4c0fec98061208730f57b145" "sha256": "d875ef92ef58e591110fdd842e33a081143623b60b92450476b288ce2c72e3e0"
}, },
{ {
"path": "web/worker/test_xyzac_rtcp.mjs", "path": "web/worker/test_xyzac_rtcp.mjs",
"sha256": "7e8be744ba11def666f3cca70252d9ba64f6801d6436f624e4b8c81738a46ec5" "sha256": "bb62b28ea9ecae7328c16ed65ba013a68cabd421bea58929d30fe9271c4e6d77"
}, },
{ {
"path": "web/worker/test_xyzac_session.mjs", "path": "web/worker/test_xyzac_session.mjs",
@@ -2016,11 +2016,11 @@
}, },
{ {
"path": "web/worker/test_xyzac_trace_parity.mjs", "path": "web/worker/test_xyzac_trace_parity.mjs",
"sha256": "209254396d3ec828936ec5c2ad34de9044706f7d0309fd5ad4cacae347f15247" "sha256": "564414a776ae502901edeac717b2ae06bcfb1fbdfb06a570c6f0001ac6282328"
}, },
{ {
"path": "web/worker/test_xyzac_trt_case.mjs", "path": "web/worker/test_xyzac_trt_case.mjs",
"sha256": "e1ab517d11a70e0682453c7fa82f9d1bcda8de0c7fd8547e6db24ef03fe4ad16" "sha256": "0294493d482a6b476be0c941e2cd96eef36537500ea400d06e0f151ecb4c7c47"
}, },
{ {
"path": "web/worker/test_xyzac_trt_io_authority.mjs", "path": "web/worker/test_xyzac_trt_io_authority.mjs",
@@ -2032,7 +2032,7 @@
}, },
{ {
"path": "web/worker/test_xyzac_workflow.mjs", "path": "web/worker/test_xyzac_workflow.mjs",
"sha256": "4dc92c0185bb6f1c4c4f925bc6e5e6800acd0c1ee5c3c4c194776fda03d656d9" "sha256": "200554ccae0ca29ec4841ec58cea90895c93f91b64eb9c1dffeb01145cef7465"
}, },
{ {
"path": "web/worker/trace-export.mjs", "path": "web/worker/trace-export.mjs",
@@ -2086,10 +2086,10 @@
"scope": "selected_xyzac_simulation_only", "scope": "selected_xyzac_simulation_only",
"sources": [ "sources": [
{ {
"lines": "107-833", "lines": "122-846",
"path": "web/worker/sim-session.mjs", "path": "web/worker/sim-session.mjs",
"role": "session-authority-routing", "role": "session-authority-routing",
"sha256": "adf00f12f4408bcbe00e296ce3d8003f764e4cdd0849c86039586fecdc822af1", "sha256": "4f674200bfcca9af6bc2c83017329355b88307d0749ebb5c9d0fc8eaecaa2349",
"status": "resolved", "status": "resolved",
"symbol": "new ProgramRunner" "symbol": "new ProgramRunner"
}, },
@@ -2129,7 +2129,7 @@
"lines": "31-31", "lines": "31-31",
"path": "web/worker/sim-task.mjs", "path": "web/worker/sim-task.mjs",
"role": "task-authority-wrapper", "role": "task-authority-wrapper",
"sha256": "4b4b3aa4b86aba08e5afb8534d98d6ab11c845eeb891cf4793b5906a01de1599", "sha256": "2e055b365b4b3ce2b47de56c987e326183c8f16a4746f5fbe324268a5c681a24",
"status": "resolved", "status": "resolved",
"symbol": "export class SimTask" "symbol": "export class SimTask"
}, },
@@ -2207,9 +2207,9 @@
} }
], ],
"state_hashes": [ "state_hashes": [
"fnv1a64:8bb613bfa975ae2e", "fnv1a64:209531d9fc283248",
"fnv1a64:051e2337103b3b9b", "fnv1a64:ca3f86c22f00a1b8",
"fnv1a64:7aaf47cbc3c37b54" "fnv1a64:08d380ff6ff129bd"
], ],
"status": "verified", "status": "verified",
"task_id": "WASM-00", "task_id": "WASM-00",
@@ -2227,7 +2227,7 @@
"control_count": 110, "control_count": 110,
"first_divergent_tick": null, "first_divergent_tick": null,
"motion_authority": "wasm", "motion_authority": "wasm",
"output_sha256": "de69f1a6f2f608f9d156713e50addd1677e17957b760261de7e0092a4e031a82", "output_sha256": "e9d4aceca4c2c88882593ead1339f12d0ef81af51f257403c22f8c17e15e2f18",
"returncode": 0, "returncode": 0,
"state_hash": "fnv1a64:e862c9e59d9ffd8c", "state_hash": "fnv1a64:e862c9e59d9ffd8c",
"status": "verified", "status": "verified",
@@ -2235,19 +2235,19 @@
{ {
"active_type": 0, "active_type": 0,
"code": "M429", "code": "M429",
"state_hash": "fnv1a64:8bb613bfa975ae2e", "state_hash": "fnv1a64:209531d9fc283248",
"tick": 0 "tick": 0
}, },
{ {
"active_type": 1, "active_type": 1,
"code": "M428", "code": "M428",
"state_hash": "fnv1a64:051e2337103b3b9b", "state_hash": "fnv1a64:ca3f86c22f00a1b8",
"tick": 0 "tick": 0
}, },
{ {
"active_type": 2, "active_type": 2,
"code": "M430", "code": "M430",
"state_hash": "fnv1a64:7aaf47cbc3c37b54", "state_hash": "fnv1a64:08d380ff6ff129bd",
"tick": 0 "tick": 0
} }
], ],

View File

@@ -96,7 +96,7 @@
"failure_samples": [], "failure_samples": [],
"input": { "input": {
"path": "coverage/xyzac-trt-web-wasm-parity.report.json", "path": "coverage/xyzac-trt-web-wasm-parity.report.json",
"sha256": "7f88767d4b0b3b70bde27e8d5165a901618dfa2fb5076eceeb117f443bd305df" "sha256": "6e4935ebe55b771bb9a7f831b50cadda9ba7b10e09cb4b495d29faee6cf86f80"
}, },
"known_blockers": [ "known_blockers": [
"STOP_RELEASE is intentional until an owner authorizes scope expansion" "STOP_RELEASE is intentional until an owner authorizes scope expansion"
@@ -104,7 +104,7 @@
"outputs": [], "outputs": [],
"release_decision": "selected_case_verified_but_release_stopped_by_scope_exclusions", "release_decision": "selected_case_verified_but_release_stopped_by_scope_exclusions",
"release_status": "STOP_RELEASE", "release_status": "STOP_RELEASE",
"report_sha256": "712fa17edbff4c78aa58700637b54b4974b3a2c0104f4c91de566d4ff27ac5c4", "report_sha256": "971e5b6ec19213d5bbdf414e65868fd440391e9e27579d3d3bf5b3186c741816",
"schema": "xyzac-trt-wasm-only-release-gate.v1", "schema": "xyzac-trt-wasm-only-release-gate.v1",
"status": "verified", "status": "verified",
"task_id": "RELEASE-00" "task_id": "RELEASE-00"

View File

@@ -91,7 +91,7 @@
}, },
"release_status": "STOP_RELEASE", "release_status": "STOP_RELEASE",
"report_hashes": { "report_hashes": {
"coverage/xyzac-trt-gate-matrix.report.json": "c3a5bc3eabd6631a8645a28df997ef605f911230c55527dbda016edf5a37ee6f", "coverage/xyzac-trt-gate-matrix.report.json": "b2a74bc5e8440728dbbbfb917ae1949e6acae9c4c1f973649e4b19edd8755034",
"coverage/xyzac-trt-hal-tool-alarm.report.json": "99ac60c9a2d58f08063ffc07f0c2eea62d8ec97212d6d2b74d6c139f176b592e", "coverage/xyzac-trt-hal-tool-alarm.report.json": "99ac60c9a2d58f08063ffc07f0c2eea62d8ec97212d6d2b74d6c139f176b592e",
"coverage/xyzac-trt-manual-control.report.json": "9ebb8ef2c97a57d77666c92e210ad1415a63852730b37993f995ca95cbc275b5", "coverage/xyzac-trt-manual-control.report.json": "9ebb8ef2c97a57d77666c92e210ad1415a63852730b37993f995ca95cbc275b5",
"coverage/xyzac-trt-program-preview.report.json": "b95a59986c0bd0cb098c49fbc8c9ced80bbe024888dcbcc14c15dd6e67432814", "coverage/xyzac-trt-program-preview.report.json": "b95a59986c0bd0cb098c49fbc8c9ced80bbe024888dcbcc14c15dd6e67432814",
@@ -101,12 +101,12 @@
"coverage/xyzac-trt-source-slice-inventory.report.json": "bf5f3fd36e1943e0dc42bafb2ed195fad147bcb3e56a8d208d7f01a3641829de", "coverage/xyzac-trt-source-slice-inventory.report.json": "bf5f3fd36e1943e0dc42bafb2ed195fad147bcb3e56a8d208d7f01a3641829de",
"coverage/xyzac-trt-startup.report.json": "56e040b645bcefc3d6c57244cb2d3aba16f421ff5137089d9c3336970dfd968b", "coverage/xyzac-trt-startup.report.json": "56e040b645bcefc3d6c57244cb2d3aba16f421ff5137089d9c3336970dfd968b",
"coverage/xyzac-trt-threejs.report.json": "5172d7118c555fca5f92bcb443b9c259ea0225346482558d4c33ef8a4f8da5cb", "coverage/xyzac-trt-threejs.report.json": "5172d7118c555fca5f92bcb443b9c259ea0225346482558d4c33ef8a4f8da5cb",
"coverage/xyzac-trt-tool-preview.report.json": "bc39dec83f71f9e164faed098d281ca1b2390b34b225fb4fa81ebf10bc860ee8", "coverage/xyzac-trt-tool-preview.report.json": "e7093b1039a26fd62c3352f9260989a120902e3a88b4c68b751f47f05f495b05",
"coverage/xyzac-trt-trace-parity.report.json": "73b2fd46c66d0c5910c746db9d8892e0ea713714576cf793f510bee35e9a7c00", "coverage/xyzac-trt-trace-parity.report.json": "6c6b3500bc1189ff68513b74f0e5144879c3af7641349cb61737191303b76950",
"coverage/xyzac-trt-wasm-authority-audit.report.json": "a970d9980b99d8b64315088e3f182d9d99aad3787831e28feb6127c9e854c6e8", "coverage/xyzac-trt-wasm-authority-audit.report.json": "19fd46cca7043ed633f55cae0d7cdd3bf8bda96567fdc93cc047da39e766f9f9",
"coverage/xyzac-trt-workflow.report.json": "acbb6dcfdff385dc839ddb94724f9fdcebc391ccde995881ba6be14dd5a6b5c4" "coverage/xyzac-trt-workflow.report.json": "acbb6dcfdff385dc839ddb94724f9fdcebc391ccde995881ba6be14dd5a6b5c4"
}, },
"report_sha256": "09fa8fc47cd34237f97b7b74707cd57721874960809f627d1d49fdc0319c627a", "report_sha256": "888838e4d4242ff562530eb547fa3ed6dafc940502e822b182d8abf4a9ddb4d2",
"schema": "xyzac-trt-web-wasm-parity-report.v1", "schema": "xyzac-trt-web-wasm-parity-report.v1",
"source_coverage": { "source_coverage": {
"axis_keyboard": 10, "axis_keyboard": 10,

View File

@@ -28,12 +28,12 @@
"pause_cursor_stable": true, "pause_cursor_stable": true,
"recovery_status": "recovered", "recovery_status": "recovered",
"release_status": "STOP_RELEASE", "release_status": "STOP_RELEASE",
"report_sha256": "beaae5ac83afda17ceae23a7b999ae79154035ddf29e20b8c96707d7ccf4d808", "report_sha256": "a0ccd6430435fc27c7cab05fa4f76c0b442bb3cef3295d92ec2d99ef626c5eab",
"restart_source_hash_preserved": true, "restart_source_hash_preserved": true,
"run_from_line": 4, "run_from_line": 4,
"schema": "xyzac-workflow-rtcp-05.v1", "schema": "xyzac-workflow-rtcp-05.v1",
"scope": "xyzac_only", "scope": "xyzac_only",
"state_hash": "fnv1a64:cb67e9edef5a4bc8", "state_hash": "fnv1a64:ad99d63136807bb3",
"status": "verified", "status": "verified",
"task_id": "RTCP-05", "task_id": "RTCP-05",
"tests": [ "tests": [
@@ -42,7 +42,7 @@
"node", "node",
"web/worker/test_xyzac_workflow.mjs" "web/worker/test_xyzac_workflow.mjs"
], ],
"output_sha256": "13e39016ef9db1817514bb70f6c26ca5116f3fd84c30e78887fd93e5fcad2cc6", "output_sha256": "d850873f16ced32a4dfa5bf2e36379b22c68649afafb2a5164693edd76b4ccae",
"returncode": 0, "returncode": 0,
"status": "verified" "status": "verified"
} }

File diff suppressed because one or more lines are too long

View File

@@ -53,6 +53,47 @@ enum sim_task_program_value {
SIM_TASK_PROGRAM_FAULT = 6, SIM_TASK_PROGRAM_FAULT = 6,
}; };
/* LinuxCNC emctaskmain command classes used by the Web XYZAC surface. */
enum sim_task_gate_command {
SIM_TASK_GATE_ESTOP = 1,
SIM_TASK_GATE_RESET = 2,
SIM_TASK_GATE_POWER = 3,
SIM_TASK_GATE_HOME = 4,
SIM_TASK_GATE_JOG = 5,
SIM_TASK_GATE_MDI = 6,
SIM_TASK_GATE_RUN = 7,
SIM_TASK_GATE_PAUSE = 8,
SIM_TASK_GATE_RESUME = 9,
SIM_TASK_GATE_ABORT = 10,
SIM_TASK_GATE_RESTART = 11,
SIM_TASK_GATE_KINEMATICS = 12,
SIM_TASK_GATE_SET_MODE = 13,
SIM_TASK_GATE_PROGRAM_LOAD = 14,
SIM_TASK_GATE_STEP = 15,
};
enum sim_task_gate_result {
SIM_TASK_GATE_ALLOWED = 0,
SIM_TASK_GATE_ESTOP_ACTIVE = 1,
SIM_TASK_GATE_MACHINE_OFF = 2,
SIM_TASK_GATE_MACHINE_ON = 3,
SIM_TASK_GATE_INTERP_ACTIVE = 4,
SIM_TASK_GATE_TASKFILE_MISSING = 5,
SIM_TASK_GATE_ESTOPOFF_REQUIRED = 6,
SIM_TASK_GATE_PAUSE_STATE = 7,
SIM_TASK_GATE_RESUME_STATE = 8,
SIM_TASK_GATE_ABORT_STATE = 9,
SIM_TASK_GATE_RESTART_STATE = 10,
SIM_TASK_GATE_KINEMATICS_INVALID = 11,
SIM_TASK_GATE_MODE_STATE = 12,
SIM_TASK_GATE_MODE_AUTO_ACTIVE = 13,
SIM_TASK_GATE_JOG_STATE = 14,
SIM_TASK_GATE_MDI_STATE = 15,
SIM_TASK_GATE_RESET_STATE = 16,
SIM_TASK_GATE_UNKNOWN_COMMAND = 17,
SIM_TASK_GATE_UNHOMED = 18,
};
typedef struct sim_task_state { typedef struct sim_task_state {
char context_id[SIM_TASK_CONTEXT_CAPACITY]; char context_id[SIM_TASK_CONTEXT_CAPACITY];
char session_id[SIM_TASK_SESSION_CAPACITY]; char session_id[SIM_TASK_SESSION_CAPACITY];
@@ -81,6 +122,11 @@ int32_t sim_task_transition(sim_task *task, uint32_t state);
int32_t sim_task_fault(sim_task *task, int32_t error_code); int32_t sim_task_fault(sim_task *task, int32_t error_code);
int32_t sim_task_estop(sim_task *task, int32_t error_code); int32_t sim_task_estop(sim_task *task, int32_t error_code);
int32_t sim_task_recover(sim_task *task); int32_t sim_task_recover(sim_task *task);
int32_t sim_task_command_gate(const sim_task *task, uint32_t command,
uint32_t requested, uint32_t io_ready,
uint32_t io_estop, uint32_t homed,
uint32_t queue_depth,
uint32_t queue_limit);
int32_t sim_task_restore(sim_task *task, uint32_t state, uint32_t mode, int32_t sim_task_restore(sim_task *task, uint32_t state, uint32_t mode,
uint32_t program_status, uint32_t seq, uint32_t program_status, uint32_t seq,
uint32_t simulation_tick, uint32_t current_line, uint32_t simulation_tick, uint32_t current_line,

View File

@@ -456,7 +456,6 @@ int sim_io_set_estop(sim_io *io, uint32_t estop) {
int sim_io_set_power(sim_io *io, uint32_t active) { int sim_io_set_power(sim_io *io, uint32_t active) {
if (!valid_io(io) || active > 1U) return SIM_IO_ERR_ARGUMENT; if (!valid_io(io) || active > 1U) return SIM_IO_ERR_ARGUMENT;
if (io->state.estop) { set_last(io, SIM_IO_ERR_ESTOP); return SIM_IO_ERR_ESTOP; }
if (!active) { if (!active) {
io->state.ready = 0; io->state.ready = 0;
io->state.tool_prepare = 0; io->state.tool_prepare = 0;
@@ -470,6 +469,7 @@ int sim_io_set_power(sim_io *io, uint32_t active) {
io->state.spindles[index].brake = 1; io->state.spindles[index].brake = 1;
} }
} else { } else {
if (io->state.estop) { set_last(io, SIM_IO_ERR_ESTOP); return SIM_IO_ERR_ESTOP; }
if (io->state.fault || io->state.toolchanger_fault) { set_last(io, SIM_IO_ERR_FAULT); return SIM_IO_ERR_FAULT; } if (io->state.fault || io->state.toolchanger_fault) { set_last(io, SIM_IO_ERR_FAULT); return SIM_IO_ERR_FAULT; }
if (!spindle_stopped(io)) { set_last(io, SIM_IO_ERR_BUSY); return SIM_IO_ERR_BUSY; } if (!spindle_stopped(io)) { set_last(io, SIM_IO_ERR_BUSY); return SIM_IO_ERR_BUSY; }
io->state.ready = 1; io->state.ready = 1;

View File

@@ -165,6 +165,90 @@ int32_t sim_task_recover(sim_task *task) {
return result; return result;
} }
int32_t sim_task_command_gate(const sim_task *task, uint32_t command,
uint32_t requested, uint32_t io_ready,
uint32_t io_estop, uint32_t homed,
uint32_t queue_depth,
uint32_t queue_limit) {
int machine_on;
int active;
int idle;
if (!valid_task(task) || io_ready > 1U || io_estop > 1U || homed > 1U)
return SIM_TASK_GATE_UNKNOWN_COMMAND;
machine_on = io_ready != 0U && io_estop == 0U;
active = task->state.state == SIM_TASK_RUNNING ||
task->state.state == SIM_TASK_PAUSED ||
task->state.program_status == SIM_TASK_PROGRAM_RUNNING ||
task->state.program_status == SIM_TASK_PROGRAM_PAUSED;
idle = !active;
switch (command) {
case SIM_TASK_GATE_ESTOP:
/* LinuxCNC accepts ESTOP from every task state; reset is only the
inverse edge while the auxiliary estop input is asserted. */
return requested == 1U || io_estop ? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_ESTOPOFF_REQUIRED;
case SIM_TASK_GATE_POWER:
if (requested == 1U) return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : machine_on ? SIM_TASK_GATE_MACHINE_ON : SIM_TASK_GATE_ALLOWED;
/* emcTaskSetState(OFF) aborts motion and is accepted while running. */
return machine_on ? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_MACHINE_OFF;
case SIM_TASK_GATE_PROGRAM_LOAD:
/* PLAN_OPEN is an immediate task command and is accepted in the
OFF/ESTOP/ESTOP_RESET branches when the interpreter is idle. */
return idle ? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_INTERP_ACTIVE;
case SIM_TASK_GATE_RUN:
if (io_estop || !machine_on) return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
if (!idle) return SIM_TASK_GATE_INTERP_ACTIVE;
if (!homed) return SIM_TASK_GATE_UNHOMED;
return task->state.program_status == SIM_TASK_PROGRAM_IDLE ? SIM_TASK_GATE_TASKFILE_MISSING : SIM_TASK_GATE_ALLOWED;
case SIM_TASK_GATE_STEP:
if (io_estop || !machine_on) return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
if (!homed) return SIM_TASK_GATE_UNHOMED;
return task->state.program_status != SIM_TASK_PROGRAM_IDLE &&
(idle || task->state.mode == SIM_TASK_MODE_AUTO)
? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_RESET_STATE;
case SIM_TASK_GATE_PAUSE:
return machine_on && task->state.mode == SIM_TASK_MODE_AUTO && task->state.program_status == SIM_TASK_PROGRAM_RUNNING
? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_PAUSE_STATE;
case SIM_TASK_GATE_RESUME:
return machine_on && (task->state.mode == SIM_TASK_MODE_AUTO || task->state.mode == SIM_TASK_MODE_MDI) && task->state.program_status == SIM_TASK_PROGRAM_PAUSED
? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_RESUME_STATE;
case SIM_TASK_GATE_ABORT:
/* EMC_TASK_ABORT_TYPE is an immediate command in every emctaskmain
state branch, including OFF/ESTOP and an idle interpreter. */
return SIM_TASK_GATE_ALLOWED;
case SIM_TASK_GATE_RESTART:
if (!idle) return SIM_TASK_GATE_INTERP_ACTIVE;
return task->state.program_status == SIM_TASK_PROGRAM_IDLE ? SIM_TASK_GATE_TASKFILE_MISSING : SIM_TASK_GATE_ALLOWED;
case SIM_TASK_GATE_HOME:
return machine_on && idle ? SIM_TASK_GATE_ALLOWED : machine_on ? SIM_TASK_GATE_INTERP_ACTIVE : io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
case SIM_TASK_GATE_JOG:
/* AXIS allows jog commands while idle in all three task modes; the
manual tab changes mode before sending the command when required. */
return machine_on && idle ? SIM_TASK_GATE_ALLOWED : io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : machine_on ? SIM_TASK_GATE_INTERP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
case SIM_TASK_GATE_MDI:
if (io_estop || !machine_on) return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
if (!idle) return SIM_TASK_GATE_INTERP_ACTIVE;
if (!homed) return SIM_TASK_GATE_UNHOMED;
if (queue_limit != 0U && queue_depth >= queue_limit) return SIM_TASK_GATE_MDI_STATE;
return SIM_TASK_GATE_ALLOWED;
case SIM_TASK_GATE_KINEMATICS:
if (requested > 2U) return SIM_TASK_GATE_KINEMATICS_INVALID;
if (io_estop || !machine_on) return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
if (!homed) return SIM_TASK_GATE_UNHOMED;
return idle ? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_INTERP_ACTIVE;
case SIM_TASK_GATE_SET_MODE:
if (requested > SIM_TASK_MODE_AUTO || io_estop || !machine_on) return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : io_ready ? SIM_TASK_GATE_MODE_STATE : SIM_TASK_GATE_MACHINE_OFF;
/* emcTaskSetMode explicitly refuses AUTO -> MANUAL/MDI while the
AUTO interpreter is non-idle. */
if (active && task->state.mode == SIM_TASK_MODE_AUTO && requested != SIM_TASK_MODE_AUTO)
return SIM_TASK_GATE_MODE_AUTO_ACTIVE;
return SIM_TASK_GATE_ALLOWED;
case SIM_TASK_GATE_RESET:
return machine_on && idle ? SIM_TASK_GATE_ALLOWED : io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : !machine_on ? SIM_TASK_GATE_MACHINE_OFF : SIM_TASK_GATE_RESET_STATE;
default:
return SIM_TASK_GATE_UNKNOWN_COMMAND;
}
}
int32_t sim_task_restore(sim_task *task, uint32_t state, uint32_t mode, int32_t sim_task_restore(sim_task *task, uint32_t state, uint32_t mode,
uint32_t program_status, uint32_t seq, uint32_t program_status, uint32_t seq,
uint32_t simulation_tick, uint32_t current_line, uint32_t simulation_tick, uint32_t current_line,

View File

@@ -17,9 +17,20 @@ int main(void) {
assert(sim_task_attach(&task) == SIM_TASK_OK); assert(sim_task_attach(&task) == SIM_TASK_OK);
assert(sim_task_set_command(&task, 3, 0) == SIM_TASK_ERR_SEQUENCE); assert(sim_task_set_command(&task, 3, 0) == SIM_TASK_ERR_SEQUENCE);
assert(sim_task_set_command(&task, 2, 0) == SIM_TASK_OK); assert(sim_task_set_command(&task, 2, 0) == SIM_TASK_OK);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_PROGRAM_LOAD, 0, 0, 0, 0, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_RUN, 0, 0, 0, 0, 0, 10) == SIM_TASK_GATE_MACHINE_OFF);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_POWER, 1, 0, 0, 0, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_POWER, 0, 0, 0, 0, 0, 10) == SIM_TASK_GATE_MACHINE_OFF);
assert(sim_task_set_program(&task, SIM_TASK_PROGRAM_LOADED, 0) == SIM_TASK_OK); assert(sim_task_set_program(&task, SIM_TASK_PROGRAM_LOADED, 0) == SIM_TASK_OK);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_RUN, 0, 1, 0, 1, 1, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_transition(&task, SIM_TASK_RUNNING) == SIM_TASK_OK); assert(sim_task_transition(&task, SIM_TASK_RUNNING) == SIM_TASK_OK);
assert(sim_task_set_program(&task, SIM_TASK_PROGRAM_RUNNING, 1) == SIM_TASK_OK); assert(sim_task_set_program(&task, SIM_TASK_PROGRAM_RUNNING, 1) == SIM_TASK_OK);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_PAUSE, 0, 1, 0, 1, 0, 10) == SIM_TASK_GATE_PAUSE_STATE);
assert(sim_task_set_mode(&task, SIM_TASK_MODE_AUTO) == SIM_TASK_OK);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_PAUSE, 0, 1, 0, 1, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_POWER, 0, 1, 0, 1, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_SET_MODE, SIM_TASK_MODE_MANUAL, 1, 0, 1, 0, 10) == SIM_TASK_GATE_MODE_AUTO_ACTIVE);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_ESTOP, 1, 1, 0, 1, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_set_command(&task, 3, 4) == SIM_TASK_OK); assert(sim_task_set_command(&task, 3, 4) == SIM_TASK_OK);
assert(sim_task_transition(&task, SIM_TASK_PAUSED) == SIM_TASK_OK); assert(sim_task_transition(&task, SIM_TASK_PAUSED) == SIM_TASK_OK);
assert(sim_task_set_command(&task, 4, 3) == SIM_TASK_ERR_TICK); assert(sim_task_set_command(&task, 4, 3) == SIM_TASK_ERR_TICK);

View File

@@ -45,7 +45,7 @@ function MachineSequence({ isEstop, machineOn, allHomed, programLoaded, programR
<div className="machine-sequence-actions" role="group" aria-label="Emergency stop, power, homing and program controls"> <div className="machine-sequence-actions" role="group" aria-label="Emergency stop, power, homing and program controls">
<TextButton danger disabled={isEstop} onClick={onEstop} ariaLabel="Emergency stop">Emergency Stop</TextButton> <TextButton danger disabled={isEstop} onClick={onEstop} ariaLabel="Emergency stop">Emergency Stop</TextButton>
<TextButton disabled={!isEstop} onClick={onRelease} ariaLabel="Release emergency stop">Release E-stop</TextButton> <TextButton disabled={!isEstop} onClick={onRelease} ariaLabel="Release emergency stop">Release E-stop</TextButton>
<TextButton disabled={isEstop || programRunning || !canPower} onClick={onPower} ariaLabel={machineOn ? "Power off" : "Power on"}>{machineOn ? "Power Off" : "Power On"}</TextButton> <TextButton disabled={!canPower} onClick={onPower} ariaLabel={machineOn ? "Power off" : "Power on"}>{machineOn ? "Power Off" : "Power On"}</TextButton>
<TextButton disabled={!canHome} onClick={onHomeAll} ariaLabel="Home all axes">HOME ALL</TextButton> <TextButton disabled={!canHome} onClick={onHomeAll} ariaLabel="Home all axes">HOME ALL</TextButton>
<TextButton disabled={!canRun} onClick={onRun} ariaLabel="Run program">Run Program</TextButton> <TextButton disabled={!canRun} onClick={onRun} ariaLabel="Run program">Run Program</TextButton>
<TextButton feature="AXIS-MACHINE-SEQUENCE" disabled={!programLoaded || programRunning} onClick={onSequence} ariaLabel="Run startup sequence">Run Startup Sequence</TextButton> <TextButton feature="AXIS-MACHINE-SEQUENCE" disabled={!programLoaded || programRunning} onClick={onSequence} ariaLabel="Run startup sequence">Run Startup Sequence</TextButton>
@@ -172,7 +172,7 @@ export function App() {
const emergencyStop = () => { stopRunTimer(); stopJogTimer(); void dispatch("ESTOP", { active: true }, false).catch(() => undefined); }; const emergencyStop = () => { stopRunTimer(); stopJogTimer(); void dispatch("ESTOP", { active: true }, false).catch(() => undefined); };
const releaseEstop = () => dispatch("ESTOP", { active: false }, false); const releaseEstop = () => dispatch("ESTOP", { active: false }, false);
const toggleEstop = () => { if (!isEstop) { emergencyStop(); return; } void releaseEstop().catch(() => undefined); }; const toggleEstop = () => { if (!isEstop) { emergencyStop(); return; } void releaseEstop().catch(() => undefined); };
const powerOn = () => { if (isEstop || programRunning || !gates.power.enabled) return; void dispatch("POWER", { active: !machineOn }, false).catch(() => undefined); }; const powerOn = () => { if (!gates.power.enabled) return; void dispatch("POWER", { active: isEstop ? false : !machineOn }, false).catch(() => undefined); };
const resetMachine = () => { stopRunTimer(); stopJogTimer(); if (isEstop || programRunning || !machineOn) return; void dispatch("RESET").catch(() => undefined); }; const resetMachine = () => { stopRunTimer(); stopJogTimer(); if (isEstop || programRunning || !machineOn) return; void dispatch("RESET").catch(() => undefined); };
const homeAll = () => { void dispatch("HOME", { all: true }).catch(() => undefined); }; const homeAll = () => { void dispatch("HOME", { all: true }).catch(() => undefined); };
const runStartupSequence = () => { const runStartupSequence = () => {
@@ -225,8 +225,8 @@ export function App() {
const position = `X${Number(xyzacView.pose?.x ?? axes[0] ?? 0).toFixed(3)} Y${Number(xyzacView.pose?.y ?? axes[1] ?? 0).toFixed(3)} Z${Number(xyzacView.pose?.z ?? axes[2] ?? 0).toFixed(3)} A${Number(xyzacView.pose?.a ?? 0).toFixed(3)} C${Number(xyzacView.pose?.c ?? 0).toFixed(3)}`; const position = `X${Number(xyzacView.pose?.x ?? axes[0] ?? 0).toFixed(3)} Y${Number(xyzacView.pose?.y ?? axes[1] ?? 0).toFixed(3)} Z${Number(xyzacView.pose?.z ?? axes[2] ?? 0).toFixed(3)} A${Number(xyzacView.pose?.a ?? 0).toFixed(3)} C${Number(xyzacView.pose?.c ?? 0).toFixed(3)}`;
return <main className="axis-app" data-axis-feature="AXIS-UI-01" data-axis-run-enabled={String(gates.run.enabled)} data-axis-homed={JSON.stringify(state.snapshot?.motion?.homed_axes ?? state.snapshot?.motion?.homed ?? null)} data-axis-program-status={String(state.program?.program_status ?? "idle")}> return <main className="axis-app" data-axis-feature="AXIS-UI-01" data-axis-run-enabled={String(gates.run.enabled)} data-axis-homed={JSON.stringify(state.snapshot?.motion?.homed_axes ?? state.snapshot?.motion?.homed ?? null)} data-axis-program-status={String(state.program?.program_status ?? "idle")}>
<header className="axis-window-header" data-axis-feature="AXIS-SHELL-01"><div className="axis-brand"><img src={axisIcon("axis-24x24.png")} alt="AXIS" /> <strong>AXIS</strong><span>LinuxCNC Web/WASM</span></div><span className={`connection-state ${state.status}`} data-axis-feature="AXIS-SHELL-01" role="status">{state.status.toUpperCase()}</span><span className="axis-state-badge" data-axis-state={taskState} aria-label={`Machine ${machineState}, operation ${operationState}`}>{taskState}</span></header> <header className="axis-window-header" data-axis-feature="AXIS-SHELL-01"><div className="axis-brand"><img src={axisIcon("axis-24x24.png")} alt="AXIS" /> <strong>AXIS</strong><span>LinuxCNC Web/WASM</span></div><span className={`connection-state ${state.status}`} data-axis-feature="AXIS-SHELL-01" role="status">{state.status.toUpperCase()}</span><span className="axis-state-badge" data-axis-state={taskState} aria-label={`Machine ${machineState}, operation ${operationState}`}>{taskState}</span></header>
<nav className="axis-menubar" data-axis-feature="AXIS-UI-02" aria-label="AXIS menu"><Menu label="File"><MenuAction onClick={openProgramFile} disabled={programRunning}>Open G-code...</MenuAction><MenuAction onClick={() => setProgramText(DEFAULT_PROGRAM)} disabled={programRunning}>Reload editor</MenuAction><MenuAction onClick={() => setCommandStatus("Tool data editor is browser-backed")}>Edit tool data...</MenuAction><MenuAction onClick={() => setCommandStatus("Quit is unavailable in a browser tab")}>Quit</MenuAction></Menu><Menu label="Machine"><MenuAction shortcut="F1" onClick={toggleEstop}>{isEstop ? "Cancel Emergency Stop" : "Emergency Stop"}</MenuAction><MenuAction shortcut="F2" onClick={powerOn} disabled={isEstop || programRunning || !gates.power.enabled}>{machineOn ? "Power off" : "Power on"}</MenuAction><MenuAction onClick={resetMachine} disabled={!gates.reset.enabled}>Reset machine</MenuAction><MenuAction shortcut="R" onClick={runProgram} disabled={!gates.run.enabled}>Run program</MenuAction><MenuAction shortcut="T" onClick={() => execute("PROGRAM_STEP")} disabled={!gates.step.enabled}>Step</MenuAction><MenuAction shortcut="P" onClick={() => execute("PROGRAM_PAUSE")} disabled={!gates.pause.enabled}>Pause</MenuAction><MenuAction shortcut="S" onClick={() => execute("PROGRAM_RESUME")} disabled={!gates.resume.enabled}>Resume</MenuAction><MenuAction shortcut="Esc" onClick={stopProgram} disabled={!gates.abort.enabled}>Stop</MenuAction></Menu><Menu label="View"><MenuAction onClick={() => setRightTab("preview")}>Preview</MenuAction><MenuAction onClick={() => setRightTab("dro")}>DRO</MenuAction><MenuAction onClick={enterManualMode} disabled={programRunning}>Manual Control</MenuAction><MenuAction onClick={() => setLeftTab("mdi")} disabled={programRunning}>MDI</MenuAction></Menu><Menu label="Help"><MenuAction onClick={() => setCommandStatus("LinuxCNC AXIS source-compatible controls")}>About AXIS</MenuAction></Menu></nav> <nav className="axis-menubar" data-axis-feature="AXIS-UI-02" aria-label="AXIS menu"><Menu label="File"><MenuAction onClick={openProgramFile} disabled={programRunning}>Open G-code...</MenuAction><MenuAction onClick={() => setProgramText(DEFAULT_PROGRAM)} disabled={programRunning}>Reload editor</MenuAction><MenuAction onClick={() => setCommandStatus("Tool data editor is browser-backed")}>Edit tool data...</MenuAction><MenuAction onClick={() => setCommandStatus("Quit is unavailable in a browser tab")}>Quit</MenuAction></Menu><Menu label="Machine"><MenuAction shortcut="F1" onClick={toggleEstop}>{isEstop ? "Cancel Emergency Stop" : "Emergency Stop"}</MenuAction><MenuAction shortcut="F2" onClick={powerOn} disabled={!gates.power.enabled}>{machineOn ? "Power off" : "Power on"}</MenuAction><MenuAction onClick={resetMachine} disabled={!gates.reset.enabled}>Reset machine</MenuAction><MenuAction shortcut="R" onClick={runProgram} disabled={!gates.run.enabled}>Run program</MenuAction><MenuAction shortcut="T" onClick={() => execute("PROGRAM_STEP")} disabled={!gates.step.enabled}>Step</MenuAction><MenuAction shortcut="P" onClick={() => execute("PROGRAM_PAUSE")} disabled={!gates.pause.enabled}>Pause</MenuAction><MenuAction shortcut="S" onClick={() => execute("PROGRAM_RESUME")} disabled={!gates.resume.enabled}>Resume</MenuAction><MenuAction shortcut="Esc" onClick={stopProgram} disabled={!gates.abort.enabled}>Stop</MenuAction></Menu><Menu label="View"><MenuAction onClick={() => setRightTab("preview")}>Preview</MenuAction><MenuAction onClick={() => setRightTab("dro")}>DRO</MenuAction><MenuAction onClick={enterManualMode} disabled={programRunning}>Manual Control</MenuAction><MenuAction onClick={() => setLeftTab("mdi")} disabled={programRunning}>MDI</MenuAction></Menu><Menu label="Help"><MenuAction onClick={() => setCommandStatus("LinuxCNC AXIS source-compatible controls")}>About AXIS</MenuAction></Menu></nav>
<section className="axis-toolbar" data-axis-feature="AXIS-UI-05" aria-label="AXIS toolbar"><IconButton feature="AXIS-MACHINE-01" label={isEstop ? "Cancel Emergency Stop [F1]" : "Emergency Stop [F1]"} asset="tool_estop.gif" danger onClick={toggleEstop} pressed={isEstop} /><IconButton feature="AXIS-MACHINE-02" label={machineOn ? "Power off [F2]" : "Power on [F2]"} asset="tool_power.gif" disabled={isEstop || programRunning || !gates.power.enabled} onClick={powerOn} /><IconButton feature="AXIS-MACHINE-03" label="Reset machine" asset="tool_reload.gif" disabled={!gates.reset.enabled} onClick={resetMachine} /><Separator /><IconButton feature="AXIS-FILE-01" label="Open G-Code file [O]" asset="tool_open.gif" onClick={openProgramFile} /><IconButton feature="AXIS-FILE-02" label="Load exact XYZAC TRT case" asset="tool_verify.gif" disabled={!gates.load.enabled} onClick={loadCase} /><IconButton feature="AXIS-FILE-04" label="Reopen editor defaults" asset="tool_reload.gif" onClick={() => { setProgramText(DEFAULT_PROGRAM); setPlotCleared(false); }} /><Separator /><IconButton feature="AXIS-MACHINE-03" label="Begin executing current file [R]" asset="tool_run.gif" disabled={!gates.run.enabled} onClick={runProgram} /><IconButton feature="AXIS-MACHINE-05" label="Execute next line [T]" asset="tool_step.gif" disabled={!gates.step.enabled} onClick={() => execute("PROGRAM_STEP")} /><IconButton feature="AXIS-MACHINE-06" label={programPaused ? "Resume execution [S]" : "Pause execution [P]"} asset={programPaused ? "resume_inhibit.gif" : "tool_pause.gif"} disabled={programPaused ? !gates.resume.enabled : !gates.pause.enabled} onClick={() => execute(programPaused ? "PROGRAM_RESUME" : "PROGRAM_PAUSE")} /><IconButton feature="AXIS-MACHINE-07" label="Stop program execution [ESC]" asset="tool_stop.gif" disabled={!gates.abort.enabled} onClick={stopProgram} /><Separator /><IconButton feature="AXIS-MACHINE-09" label="Toggle skip lines with slash" asset="tool_blockdelete.gif" pressed={blockDelete} onClick={() => setBlockDelete((value) => !value)} /><IconButton feature="AXIS-MACHINE-08" label="Toggle optional pause" asset="tool_optpause.gif" pressed={optionalStop} onClick={() => setOptionalStop((value) => !value)} /><Separator /><IconButton feature="AXIS-VIEW-01" label="Zoom in" asset="tool_zoomin.gif" onClick={() => setZoom((value) => Math.min(2, value + .2))} /><IconButton feature="AXIS-VIEW-01" label="Zoom out" asset="tool_zoomout.gif" onClick={() => setZoom((value) => Math.max(.5, value - .2))} /><IconButton feature="AXIS-VIEW-01" label="Top view" asset="tool_axis_z.gif" pressed={view === "z"} onClick={() => { setView("z"); setRightTab("preview"); }} /><IconButton feature="AXIS-VIEW-01" label="Rotated top view" asset="tool_axis_z2.gif" pressed={view === "z2"} onClick={() => { setView("z2"); setRightTab("preview"); }} /><IconButton feature="AXIS-VIEW-01" label="Side view" asset="tool_axis_x.gif" pressed={view === "x"} onClick={() => { setView("x"); setRightTab("preview"); }} /><IconButton feature="AXIS-VIEW-01" label="Front view" asset="tool_axis_y.gif" pressed={view === "y"} onClick={() => { setView("y"); setRightTab("preview"); }} /><IconButton feature="AXIS-VIEW-01" label="Inverted front view" asset="tool_axis_y2.png" pressed={view === "y2"} onClick={() => { setView("y2"); setRightTab("preview"); }} /><IconButton feature="AXIS-VIEW-01" label="Perspective view" asset="tool_axis_p.gif" pressed={view === "p"} onClick={() => { setView("p"); setRightTab("preview"); }} /><IconButton feature="AXIS-VIEW-02" label="Drag / rotate mode" asset="tool_rotate.gif" pressed={rotateMode} onClick={() => setRotateMode((value) => !value)} /><Separator /><IconButton feature="AXIS-VIEW-15" label="Clear live plot [Ctrl-K]" asset="tool_clear.gif" onClick={() => xyzacCommand("VISUAL_CLEAR", {}, "vismachClear")} /></section> <section className="axis-toolbar" data-axis-feature="AXIS-UI-05" aria-label="AXIS toolbar"><IconButton feature="AXIS-MACHINE-01" label={isEstop ? "Cancel Emergency Stop [F1]" : "Emergency Stop [F1]"} asset="tool_estop.gif" danger onClick={toggleEstop} pressed={isEstop} /><IconButton feature="AXIS-MACHINE-02" label={machineOn ? "Power off [F2]" : "Power on [F2]"} asset="tool_power.gif" disabled={!gates.power.enabled} onClick={powerOn} /><IconButton feature="AXIS-MACHINE-03" label="Reset machine" asset="tool_reload.gif" disabled={!gates.reset.enabled} onClick={resetMachine} /><Separator /><IconButton feature="AXIS-FILE-01" label="Open G-Code file [O]" asset="tool_open.gif" onClick={openProgramFile} /><IconButton feature="AXIS-FILE-02" label="Load exact XYZAC TRT case" asset="tool_verify.gif" disabled={!gates.load.enabled} onClick={loadCase} /><IconButton feature="AXIS-FILE-04" label="Reopen editor defaults" asset="tool_reload.gif" onClick={() => { setProgramText(DEFAULT_PROGRAM); setPlotCleared(false); }} /><Separator /><IconButton feature="AXIS-MACHINE-03" label="Begin executing current file [R]" asset="tool_run.gif" disabled={!gates.run.enabled} onClick={runProgram} /><IconButton feature="AXIS-MACHINE-05" label="Execute next line [T]" asset="tool_step.gif" disabled={!gates.step.enabled} onClick={() => execute("PROGRAM_STEP")} /><IconButton feature="AXIS-MACHINE-06" label={programPaused ? "Resume execution [S]" : "Pause execution [P]"} asset={programPaused ? "resume_inhibit.gif" : "tool_pause.gif"} disabled={programPaused ? !gates.resume.enabled : !gates.pause.enabled} onClick={() => execute(programPaused ? "PROGRAM_RESUME" : "PROGRAM_PAUSE")} /><IconButton feature="AXIS-MACHINE-07" label="Stop program execution [ESC]" asset="tool_stop.gif" disabled={!gates.abort.enabled} onClick={stopProgram} /><Separator /><IconButton feature="AXIS-MACHINE-09" label="Toggle skip lines with slash" asset="tool_blockdelete.gif" pressed={blockDelete} onClick={() => setBlockDelete((value) => !value)} /><IconButton feature="AXIS-MACHINE-08" label="Toggle optional pause" asset="tool_optpause.gif" pressed={optionalStop} onClick={() => setOptionalStop((value) => !value)} /><Separator /><IconButton feature="AXIS-VIEW-01" label="Zoom in" asset="tool_zoomin.gif" onClick={() => setZoom((value) => Math.min(2, value + .2))} /><IconButton feature="AXIS-VIEW-01" label="Zoom out" asset="tool_zoomout.gif" onClick={() => setZoom((value) => Math.max(.5, value - .2))} /><IconButton feature="AXIS-VIEW-01" label="Top view" asset="tool_axis_z.gif" pressed={view === "z"} onClick={() => { setView("z"); setRightTab("preview"); }} /><IconButton feature="AXIS-VIEW-01" label="Rotated top view" asset="tool_axis_z2.gif" pressed={view === "z2"} onClick={() => { setView("z2"); setRightTab("preview"); }} /><IconButton feature="AXIS-VIEW-01" label="Side view" asset="tool_axis_x.gif" pressed={view === "x"} onClick={() => { setView("x"); setRightTab("preview"); }} /><IconButton feature="AXIS-VIEW-01" label="Front view" asset="tool_axis_y.gif" pressed={view === "y"} onClick={() => { setView("y"); setRightTab("preview"); }} /><IconButton feature="AXIS-VIEW-01" label="Inverted front view" asset="tool_axis_y2.png" pressed={view === "y2"} onClick={() => { setView("y2"); setRightTab("preview"); }} /><IconButton feature="AXIS-VIEW-01" label="Perspective view" asset="tool_axis_p.gif" pressed={view === "p"} onClick={() => { setView("p"); setRightTab("preview"); }} /><IconButton feature="AXIS-VIEW-02" label="Drag / rotate mode" asset="tool_rotate.gif" pressed={rotateMode} onClick={() => setRotateMode((value) => !value)} /><Separator /><IconButton feature="AXIS-VIEW-15" label="Clear live plot [Ctrl-K]" asset="tool_clear.gif" onClick={() => xyzacCommand("VISUAL_CLEAR", {}, "vismachClear")} /></section>
<section className="axis-case-strip" data-axis-feature="AXIS-EDIT-03"><label>Simulation case<select data-axis-feature="AXIS-EDIT-03" value={casePath} onChange={(event) => setCasePath(event.target.value)}>{SIM_CASE_CATALOG.filter((entry) => entry.path === SELECTED_XYZAC_CASE).map((entry) => <option key={entry.path} value={entry.path}>{entry.label} | XYZAC TRT</option>)}</select></label><TextButton feature="AXIS-EDIT-03" onClick={loadCase} disabled={!gates.load.enabled}>Load case</TextButton><span className="case-status" role="status">{caseStatus}</span><span className="command-status" role="status">{commandStatus}</span></section> <section className="axis-case-strip" data-axis-feature="AXIS-EDIT-03"><label>Simulation case<select data-axis-feature="AXIS-EDIT-03" value={casePath} onChange={(event) => setCasePath(event.target.value)}>{SIM_CASE_CATALOG.filter((entry) => entry.path === SELECTED_XYZAC_CASE).map((entry) => <option key={entry.path} value={entry.path}>{entry.label} | XYZAC TRT</option>)}</select></label><TextButton feature="AXIS-EDIT-03" onClick={loadCase} disabled={!gates.load.enabled}>Load case</TextButton><span className="case-status" role="status">{caseStatus}</span><span className="command-status" role="status">{commandStatus}</span></section>
<MachineSequence isEstop={isEstop} machineOn={machineOn} allHomed={allHomed} programLoaded={programLoaded} programRunning={programRunning} canPower={gates.power.enabled} canHome={gates.home.enabled} canRun={gates.run.enabled} onEstop={emergencyStop} onRelease={() => { void releaseEstop().catch(() => undefined); }} onPower={powerOn} onHomeAll={homeAll} onRun={runProgram} onSequence={runStartupSequence} /> <MachineSequence isEstop={isEstop} machineOn={machineOn} allHomed={allHomed} programLoaded={programLoaded} programRunning={programRunning} canPower={gates.power.enabled} canHome={gates.home.enabled} canRun={gates.run.enabled} onEstop={emergencyStop} onRelease={() => { void releaseEstop().catch(() => undefined); }} onPower={powerOn} onHomeAll={homeAll} onRun={runProgram} onSequence={runStartupSequence} />
<XyzacTrtPanel view={xyzacView} command={xyzacCommand} /> <XyzacTrtPanel view={xyzacView} command={xyzacCommand} />

View File

@@ -56,6 +56,21 @@ const PANEL_GATE_ACTIONS = Object.freeze({
POWER: "power", POWER: "power",
}); });
const WASM_GATE_COMMANDS = Object.freeze({
ESTOP: 1, RESET: 2, POWER: 3, HOME: 4, JOG: 5, MDI: 6, RUN: 7,
PAUSE: 8, RESUME: 9, ABORT: 10, RESTART: 11, KINEMATICS: 12,
SET_MODE: 13, PROGRAM_LOAD: 14, STEP: 15,
});
const WASM_GATE_REASONS = Object.freeze({
1: "xyzac_estop_active", 2: "xyzac_machine_off", 3: "xyzac_power_already_on",
4: "xyzac_interp_running", 5: "axis_taskfile_missing", 6: "linuxcnc_wasm_estop_inactive",
7: "xyzac_pause_state_gate", 8: "xyzac_resume_state_gate", 9: "xyzac_abort_state_gate",
10: "xyzac_restart_state_gate", 11: "xyzac_kinematics_type_invalid", 12: "linuxcnc_wasm_mode_unavailable",
13: "linuxcnc_wasm_auto_interp_active", 14: "xyzac_jog_state_gate", 15: "xyzac_mdi_queue_full",
16: "xyzac_reset_state_gate", 17: "linuxcnc_wasm_gate_unknown_command", 18: "xyzac_not_all_homed",
});
function text(value) { function text(value) {
return typeof value === "string" && value.length > 0 ? value : null; return typeof value === "string" && value.length > 0 ? value : null;
} }
@@ -216,6 +231,21 @@ export class SimSession {
return { status: "verified", count: rows.length }; return { status: "verified", count: rows.length };
} }
resetXyzacProgramToLoaded() {
if (!this.xyzacProgram?.source) return { status: "verified" };
const source = this.xyzacProgram.source;
const path = this.xyzacProgram.path;
const loaded = this.xyzacProgram.load(source, path);
if (loaded.status !== "verified") return loaded;
this.xyzacProgram.setRapidVelocity(this.xyzacRapidVelocity);
const subroutines = this.loadXyzacSubroutines(path);
if (subroutines.status !== "verified") return subroutines;
const preview = this.xyzacProgram.preview();
if (preview.status !== "verified") return preview;
this.previewState = preview;
return { status: "verified" };
}
panelSnapshot({ task = this.task.export(), program = this.xyzacProgram ? this.xyzacProgram.snapshot() : this.program.loaded ? this.program.snapshot() : null, input = {}, tick = this.tick, motionTrace = null, includeToolpath = input.kind !== "SIM_STEP" || !this.xyzacProgram, includeConfiguration = input.kind !== "SIM_STEP" || !this.xyzacProgram, includeStableRuntime = input.kind !== "SIM_STEP" || !this.xyzacProgram } = {}) { panelSnapshot({ task = this.task.export(), program = this.xyzacProgram ? this.xyzacProgram.snapshot() : this.program.loaded ? this.program.snapshot() : null, input = {}, tick = this.tick, motionTrace = null, includeToolpath = input.kind !== "SIM_STEP" || !this.xyzacProgram, includeConfiguration = input.kind !== "SIM_STEP" || !this.xyzacProgram, includeStableRuntime = input.kind !== "SIM_STEP" || !this.xyzacProgram } = {}) {
const motion = this.xyzacProgram ? this.wasmMotionProjection() : this.motion.export(); const motion = this.xyzacProgram ? this.wasmMotionProjection() : this.motion.export();
const wasmMotion = this.wasmMotion.read(); const wasmMotion = this.wasmMotion.read();
@@ -247,7 +277,7 @@ export class SimSession {
home: this.xyzacCommandGate({ kind: "HOME", payload: {} }), home: this.xyzacCommandGate({ kind: "HOME", payload: {} }),
estop: this.xyzacCommandGate({ kind: "ESTOP", payload: { active: true } }), estop: this.xyzacCommandGate({ kind: "ESTOP", payload: { active: true } }),
recover: this.xyzacCommandGate({ kind: "IO_RECOVER", payload: {} }), recover: this.xyzacCommandGate({ kind: "IO_RECOVER", payload: {} }),
power: this.xyzacCommandGate({ kind: "POWER", payload: { active: !Boolean(ioState.ready) } }), power: this.xyzacCommandGate({ kind: "POWER", payload: { active: ioState.estop ? false : !Boolean(ioState.ready) } }),
powerOn: this.xyzacCommandGate({ kind: "POWER", payload: { active: true } }), powerOn: this.xyzacCommandGate({ kind: "POWER", payload: { active: true } }),
powerOff: this.xyzacCommandGate({ kind: "POWER", payload: { active: false } }), powerOff: this.xyzacCommandGate({ kind: "POWER", payload: { active: false } }),
reset: this.xyzacCommandGate({ kind: "RESET", payload: {} }), reset: this.xyzacCommandGate({ kind: "RESET", payload: {} }),
@@ -360,64 +390,47 @@ export class SimSession {
const program = this.xyzacProgram.snapshot(); const program = this.xyzacProgram.snapshot();
const io = this.xyzacIo?.canonical?.() ?? {}; const io = this.xyzacIo?.canonical?.() ?? {};
const wasmMotion = this.wasmMotion.read(); const wasmMotion = this.wasmMotion.read();
const active = task.state === "RUNNING" || task.state === "PAUSED" || program.status === 2 || program.status === 3; const homeLimit = this.wasmHomeLimit.read();
const estop = Boolean(io.estop); const estop = Boolean(io.estop);
const ready = Boolean(io.ready) && !estop; const ready = Boolean(io.ready) && !estop;
const loaded = program.status !== 0; const loaded = program.status !== 0;
const action = PANEL_GATE_ACTIONS[input.kind] ?? null; const action = PANEL_GATE_ACTIONS[input.kind] ?? null;
let enabled = true; const command = input.kind === "JOG_STOP" || input.kind === "JOG_START" || input.kind === "SET_OVERRIDE" ? WASM_GATE_COMMANDS.JOG
let reason = "linuxcnc_wasm_authority"; : input.kind === "PROGRAM_LOAD" ? WASM_GATE_COMMANDS.PROGRAM_LOAD
if (input.kind === "KINEMATICS_SELECT" && ![0, 1, 2].includes(Number(input.payload?.type))) { : input.kind === "PROGRAM_RESTART" ? WASM_GATE_COMMANDS.RESTART
enabled = false; : input.kind === "PROGRAM_RUN" ? WASM_GATE_COMMANDS.RUN
reason = "xyzac_kinematics_type_invalid"; : input.kind === "PROGRAM_PAUSE" ? WASM_GATE_COMMANDS.PAUSE
} else if (input.kind === "ESTOP") { : input.kind === "PROGRAM_RESUME" ? WASM_GATE_COMMANDS.RESUME
enabled = input.payload?.active === true || estop; : input.kind === "PROGRAM_ABORT" ? WASM_GATE_COMMANDS.ABORT
reason = enabled ? "linuxcnc_wasm_estop_transition" : "linuxcnc_wasm_estop_inactive"; : input.kind === "PROGRAM_STEP" ? WASM_GATE_COMMANDS.STEP
} else if (input.kind === "POWER") { : input.kind === "MDI_EXECUTE" ? WASM_GATE_COMMANDS.MDI
const requested = input.payload?.active === true; : input.kind === "KINEMATICS_SELECT" ? WASM_GATE_COMMANDS.KINEMATICS
enabled = requested ? !estop && !ready && !active : !estop && ready && !active; : input.kind === "SET_MODE" ? WASM_GATE_COMMANDS.SET_MODE
reason = estop ? "xyzac_estop_active" : active ? "xyzac_interp_running" : enabled ? "linuxcnc_wasm_power_transition" : requested ? "xyzac_power_already_on" : "xyzac_power_already_off"; : input.kind === "RESET" ? WASM_GATE_COMMANDS.RESET
} else if (input.kind === "IO_RECOVER") { : input.kind === "HOME" ? WASM_GATE_COMMANDS.HOME
enabled = !estop && ready && !active; : input.kind === "ESTOP" ? WASM_GATE_COMMANDS.ESTOP
reason = enabled ? "linuxcnc_wasm_io_recover" : estop ? "xyzac_estop_active" : !ready ? "xyzac_power_required" : "xyzac_interp_running"; : input.kind === "POWER" ? WASM_GATE_COMMANDS.POWER
} else if (input.kind === "LOAD_WORKSPACE") { : input.kind === "IO_RECOVER" ? WASM_GATE_COMMANDS.POWER : null;
enabled = !estop && !active; if (!command) return { action, enabled: true, reason: "linuxcnc_wasm_authority" };
reason = enabled ? "linuxcnc_wasm_workspace_available" : estop ? "xyzac_estop_active" : "xyzac_interp_running"; const requested = input.kind === "POWER" || input.kind === "ESTOP" ? (input.payload?.active === true ? 1 : 0)
} else if (["PROGRAM_LOAD", "PROGRAM_RESTART"].includes(input.kind)) { : input.kind === "KINEMATICS_SELECT" ? Number(input.payload?.type)
enabled = !estop && !active && (input.kind !== "PROGRAM_RESTART" || loaded); : input.kind === "SET_MODE" ? ({ MANUAL: 0, MDI: 1, AUTO: 2 }[String(input.payload?.mode ?? "").toUpperCase()] ?? 3) : 0;
reason = estop ? "xyzac_estop_active" : active ? "xyzac_interp_running" : enabled ? "linuxcnc_wasm_program_idle" : "xyzac_taskfile_missing"; const result = this.task.commandGate(command, {
} else if (input.kind === "PROGRAM_RUN") { requested,
enabled = ready && !active && loaded; ioReady: ready,
reason = !ready ? (estop ? "xyzac_estop_active" : "xyzac_machine_off") : active ? "xyzac_interp_running" : loaded ? "linuxcnc_wasm_run_available" : "axis_taskfile_missing"; ioEstop: estop,
} else if (input.kind === "PROGRAM_PAUSE") { homed: Boolean(homeLimit.state?.homed),
enabled = ready && program.status === 2; queueDepth: Number(task.queued_mdi_commands ?? 0),
reason = enabled ? "linuxcnc_wasm_pause_available" : "xyzac_pause_state_gate"; queueLimit: Number(task.max_queued_mdi_commands ?? 10),
} else if (input.kind === "PROGRAM_RESUME") { });
enabled = ready && program.status === 3; if (result === null) return { action, enabled: false, reason: "linuxcnc_wasm_gate_unavailable", taskState: task.state, interpState: "UNKNOWN" };
reason = enabled ? "linuxcnc_wasm_resume_available" : "xyzac_resume_state_gate"; let reason = result === 0 ? "linuxcnc_wasm_authority" : WASM_GATE_REASONS[result] ?? "linuxcnc_wasm_gate_unknown_command";
} else if (input.kind === "PROGRAM_ABORT") { if (result === 2 && input.kind === "POWER" && input.payload?.active !== true) reason = "xyzac_power_already_off";
enabled = active; if (result === 3 && input.kind === "POWER") reason = "xyzac_power_already_on";
reason = enabled ? "linuxcnc_wasm_abort_available" : "xyzac_abort_state_gate"; if (result === 4 && input.kind === "MDI_EXECUTE") reason = "mdi_while_program_active";
} else if (input.kind === "PROGRAM_STEP") { if (result === 4 && input.kind === "PROGRAM_STEP") reason = "xyzac_interp_running";
enabled = ready && loaded && !active; if (result === 5 && input.kind === "PROGRAM_RESTART") reason = "xyzac_taskfile_missing";
reason = !ready ? "xyzac_machine_off" : loaded ? "linuxcnc_wasm_step_available" : "axis_taskfile_missing"; return { action, enabled: result === 0, reason, taskState: task.state, interpState: task.state === "PAUSED" || program.status === 3 ? "PAUSED" : task.state === "RUNNING" || program.status === 2 ? "READING" : "IDLE", wasm: { io_ready: ready, io_estop: estop, program_status: program.program_status, queue_depth: wasmMotion.state.queue_depth, gate_result: result } };
} else if (input.kind === "SET_MODE") {
enabled = ready && !active;
reason = enabled ? "linuxcnc_wasm_mode_available" : estop ? "xyzac_estop_active" : active ? "xyzac_interp_running" : "xyzac_machine_off";
} else if (input.kind === "RESET") {
enabled = ready && !active;
reason = enabled ? "linuxcnc_wasm_reset_available" : estop ? "xyzac_estop_active" : !ready ? "xyzac_machine_off" : "xyzac_interp_running";
} else if (input.kind === "HOME" || ["JOG_START", "JOG_STOP", "SET_OVERRIDE"].includes(input.kind)) {
enabled = ready && !active;
reason = enabled ? "linuxcnc_wasm_manual_available" : estop ? "xyzac_estop_active" : active ? "xyzac_interp_running" : "xyzac_machine_off";
} else if (input.kind === "MDI_EXECUTE") {
enabled = ready && !active;
reason = enabled ? "linuxcnc_wasm_mdi_available" : estop ? "xyzac_estop_active" : active ? "mdi_while_program_active" : "xyzac_machine_off";
} else if (["KINEMATICS_SELECT", "SPINDLE_COMMAND", "SPINDLE_FAULT", "SPINDLE_INHIBIT", "SPINDLE_ORIENT", "SPINDLE_ORIENTED", "TOOL_PREPARE", "TOOL_PREPARED", "TOOL_CHANGE", "TOOL_CHANGED", "TOOL_ABORT", "TOOL_FAULT", "TOOL_CLEAR_FAULT", "PROBE_CONFIG"].includes(input.kind)) {
enabled = ready && !active;
reason = enabled ? "linuxcnc_wasm_io_available" : estop ? "xyzac_estop_active" : active ? "xyzac_interp_running" : "xyzac_machine_off";
}
return { action, enabled, reason, taskState: task.state, interpState: active ? (program.status === 3 ? "PAUSED" : "READING") : "IDLE", wasm: { io_ready: ready, io_estop: estop, program_status: program.program_status, queue_depth: wasmMotion.state.queue_depth } };
} }
stateForCheckpoint() { stateForCheckpoint() {
@@ -971,13 +984,15 @@ export class SimSession {
const deviceResponse = (kind, payload) => { const deviceResponse = (kind, payload) => {
const result = this.xyzacIo ? this.xyzacIo.command(kind, payload ?? {}) : this.devices.command(kind, payload ?? {}); const result = this.xyzacIo ? this.xyzacIo.command(kind, payload ?? {}) : this.devices.command(kind, payload ?? {});
if (result.status === "verified" && this.xyzacProgram && kind === "ESTOP" && payload?.active === true) { if (result.status === "verified" && this.xyzacProgram && kind === "ESTOP" && payload?.active === true) {
// The WASM IO edge owns emergency stop. Stop the WASM trajectory and // LinuxCNC emcTaskSetState(ESTOP) calls emcTaskAbort: clear the
// pause the canonical program so no compatibility state can continue // interpreter command and trajectory, then leave the taskfile loaded
// feeding motion while the machine is stopped. // for a later AUTO run after ESTOP_RESET and POWER ON.
const programState = this.xyzacProgram.snapshot(); this.xyzacProgram.abort();
if (programState.status === 2) this.xyzacProgram.pause();
this.wasmMotion.abort(); this.wasmMotion.abort();
this.wasmMotion.setPaused(true); this.wasmMotion.setPaused(true);
const resetProgram = this.resetXyzacProgramToLoaded();
if (resetProgram.status !== "verified") return finish(this.error(input, "program_restart", resetProgram.code ?? "program_reload_failed"), "failed");
this.task.setProgram("loaded", 0);
} }
try { try {
if (kind === "ESTOP" && payload?.active === true) this.task.estop(-904); if (kind === "ESTOP" && payload?.active === true) this.task.estop(-904);
@@ -1053,10 +1068,20 @@ export class SimSession {
if (this.xyzacProgram) { if (this.xyzacProgram) {
const result = this.xyzacIo.command("POWER", input.payload ?? {}); const result = this.xyzacIo.command("POWER", input.payload ?? {});
if (result.status === "verified") { if (result.status === "verified") {
if (input.payload?.active === false) {
/* LinuxCNC emcTaskSetState(OFF) aborts motion, clears the
interpreter command and leaves the loaded taskfile intact. */
this.xyzacProgram.abort();
this.wasmMotion.abort();
this.wasmMotion.setPaused(true);
const resetProgram = this.resetXyzacProgramToLoaded();
if (resetProgram.status !== "verified") return finish(this.error(input, "program_restart", resetProgram.code ?? "program_reload_failed"), "failed");
this.task.setProgram("loaded", 0);
}
try { try {
if (input.payload?.active === true) this.task.recover(); if (input.payload?.active === true) this.task.recover();
else if (["READY", "STOPPED"].includes(this.task.state.state)) { else if (["READY", "RUNNING", "PAUSED", "STOPPED"].includes(this.task.state.state)) {
if (this.task.state.state === "READY") this.task.transition("STOPPED"); if (["READY", "RUNNING", "PAUSED"].includes(this.task.state.state)) this.task.transition("STOPPED");
} }
} catch { /* the WASM IO state remains authoritative */ } } catch { /* the WASM IO state remains authoritative */ }
if (this.xyzacDeviceIo) this.xyzacDeviceIo.command("INTERLOCK_RESET", input.payload ?? {}, input.operation_id); if (this.xyzacDeviceIo) this.xyzacDeviceIo.command("INTERLOCK_RESET", input.payload ?? {}, input.operation_id);
@@ -1468,6 +1493,7 @@ export class SimSession {
case "PROGRAM_STEP": { case "PROGRAM_STEP": {
if (this.xyzacProgram) { if (this.xyzacProgram) {
const current = this.xyzacProgram.snapshot(); const current = this.xyzacProgram.snapshot();
if (current.status === 1 && this.task.state.mode !== "AUTO") this.task.setMode("AUTO");
if (current.status === 1) this.xyzacProgram.start(); if (current.status === 1) this.xyzacProgram.start();
const result = this.xyzacProgram.step(); const result = this.xyzacProgram.step();
if (result.status === "verified") { if (result.motion) this.wasmMotion.enqueue(result.motion); this.wasmMotion.setPaused(true); this.task.setProgram(result.program_status === 4 ? "completed" : "paused", result.line); this.task.transition(result.program_status === 4 ? "STOPPED" : "PAUSED"); } if (result.status === "verified") { if (result.motion) this.wasmMotion.enqueue(result.motion); this.wasmMotion.setPaused(true); this.task.setProgram(result.program_status === 4 ? "completed" : "paused", result.line); this.task.transition(result.program_status === 4 ? "STOPPED" : "PAUSED"); }
@@ -1523,6 +1549,7 @@ export class SimSession {
if (this.task.state.state === "STOPPED") this.task.recover(); if (this.task.state.state === "STOPPED") this.task.recover();
const block = input.payload?.block ?? input.payload?.command; const block = input.payload?.block ?? input.payload?.command;
if (this.xyzacProgram) { if (this.xyzacProgram) {
this.task.setMode("MDI");
const result = this.xyzacProgram.executeBlock(block); const result = this.xyzacProgram.executeBlock(block);
if (result.status === "verified") { if (result.status === "verified") {
const controlCode = Number(result.program?.control_code ?? 0); const controlCode = Number(result.program?.control_code ?? 0);
@@ -1555,6 +1582,7 @@ export class SimSession {
if (!this.xyzacKinematics) return finish(this.error(input, "kinematics", "xyzac_kinematics_unavailable"), "failed"); if (!this.xyzacKinematics) return finish(this.error(input, "kinematics", "xyzac_kinematics_unavailable"), "failed");
const mode = Number(input.payload?.type); const mode = Number(input.payload?.type);
if (![XYZAC_MODES.identity, XYZAC_MODES.xyzac, XYZAC_MODES.userk].includes(mode)) return finish(this.error(input, "kinematics", "xyzac_kinematics_type_invalid"), "failed"); if (![XYZAC_MODES.identity, XYZAC_MODES.xyzac, XYZAC_MODES.userk].includes(mode)) return finish(this.error(input, "kinematics", "xyzac_kinematics_type_invalid"), "failed");
this.task.setMode("MDI");
const result = this.xyzacKinematics.select(mode, this.xyzacKinematics.snapshot().joints); const result = this.xyzacKinematics.select(mode, this.xyzacKinematics.snapshot().joints);
const code = mode === XYZAC_MODES.xyzac ? "M428" : mode === XYZAC_MODES.identity ? "M429" : "M430"; const code = mode === XYZAC_MODES.xyzac ? "M428" : mode === XYZAC_MODES.identity ? "M429" : "M430";
return finish(this.envelope(result.status === "verified" ? "COMMAND_RESULT" : "ERROR", input, { ...result, command: input.kind, remap: { code, hal_pin: "motion.switchkins-type", hal_pin_vars: true, sync: "M66 E0 L0" }, kinematics: this.xyzacKinematics.snapshot(), task: this.task.export(), operation_id: input.operation_id ?? null }), result.status === "verified" ? "verified" : "failed"); return finish(this.envelope(result.status === "verified" ? "COMMAND_RESULT" : "ERROR", input, { ...result, command: input.kind, remap: { code, hal_pin: "motion.switchkins-type", hal_pin_vars: true, sync: "M66 E0 L0" }, kinematics: this.xyzacKinematics.snapshot(), task: this.task.export(), operation_id: input.operation_id ?? null }), result.status === "verified" ? "verified" : "failed");

View File

@@ -170,6 +170,12 @@ export class SimTask {
this.lastError = 0; this.lastError = 0;
} }
commandGate(command, { requested = 0, ioReady = false, ioEstop = false, homed = false, queueDepth = 0, queueLimit = 10 } = {}) {
this.ensureCore();
if (!this.coreTask || (typeof this.core?._sim_task_command_gate !== "function" && typeof this.core?.sim_task_command_gate !== "function")) return null;
return coreCall(this.core, "sim_task_command_gate", "number", ["number", "number", "number", "number", "number", "number", "number", "number"], [this.coreTask, command, requested, ioReady ? 1 : 0, ioEstop ? 1 : 0, homed ? 1 : 0, queueDepth, queueLimit]);
}
export() { export() {
this.syncFromCore(); this.syncFromCore();
const unsigned = clone(this.state); const unsigned = clone(this.state);

View File

@@ -30,20 +30,22 @@ for (const [kind, payload, reason] of [["PROGRAM_RUN", {}, "xyzac_machine_off"],
const powered = send("POWER", { active: true }); const powered = send("POWER", { active: true });
assert.equal(powered.payload.panel_snapshot.axis_state.machine_state, "ON"); assert.equal(powered.payload.panel_snapshot.axis_state.machine_state, "ON");
assert.equal(send("KINEMATICS_SELECT", { type: 1 }).payload.kinematics.active_type, 1);
assert.equal(send("HOME", { all: true }).payload.wasm_home_limit.state.homed, true); assert.equal(send("HOME", { all: true }).payload.wasm_home_limit.state.homed, true);
assert.equal(send("KINEMATICS_SELECT", { type: 1 }).payload.kinematics.active_type, 1);
assert.equal(send("POWER", { active: false }).payload.panel_snapshot.axis_state.machine_state, "OFF"); assert.equal(send("POWER", { active: false }).payload.panel_snapshot.axis_state.machine_state, "OFF");
assert.equal(send("PROGRAM_RUN").payload.gate_reason, "xyzac_machine_off"); assert.equal(send("PROGRAM_RUN").payload.gate_reason, "xyzac_machine_off");
assert.equal(send("POWER", { active: true }).payload.panel_snapshot.axis_state.machine_state, "ON"); assert.equal(send("POWER", { active: true }).payload.panel_snapshot.axis_state.machine_state, "ON");
assert.equal(send("PROGRAM_RUN").payload.task.state, "RUNNING"); assert.equal(send("PROGRAM_RUN").payload.task.state, "RUNNING");
for (const [kind, payload, reason] of [["HOME", { all: true }, "xyzac_interp_running"], ["MDI_EXECUTE", { block: "G0 X1" }, "mdi_while_program_active"], ["KINEMATICS_SELECT", { type: 0 }, "xyzac_interp_running"], ["POWER", { active: false }, "xyzac_interp_running"]]) { for (const [kind, payload, reason] of [["HOME", { all: true }, "xyzac_interp_running"], ["MDI_EXECUTE", { block: "G0 X1" }, "mdi_while_program_active"], ["KINEMATICS_SELECT", { type: 0 }, "xyzac_interp_running"]]) {
const blocked = send(kind, payload); const blocked = send(kind, payload);
assert.equal(blocked.kind, "ERROR", kind); assert.equal(blocked.kind, "ERROR", kind);
assert.equal(blocked.payload.gate_reason, reason, kind); assert.equal(blocked.payload.gate_reason, reason, kind);
} }
assert.equal(send("PROGRAM_PAUSE").payload.task.state, "PAUSED"); assert.equal(send("PROGRAM_PAUSE").payload.task.state, "PAUSED");
assert.equal(send("PROGRAM_RESUME").payload.task.state, "RUNNING"); assert.equal(send("PROGRAM_RESUME").payload.task.state, "RUNNING");
assert.equal(send("POWER", { active: false }).payload.task.state, "STOPPED");
assert.equal(send("POWER", { active: true }).payload.panel_snapshot.axis_state.machine_state, "ON");
assert.equal(send("PROGRAM_ABORT").payload.task.state, "STOPPED"); assert.equal(send("PROGRAM_ABORT").payload.task.state, "STOPPED");
assert.equal(send("ESTOP", { active: true }).payload.panel_snapshot.axis_state.machine_state, "ESTOP"); assert.equal(send("ESTOP", { active: true }).payload.panel_snapshot.axis_state.machine_state, "ESTOP");

View File

@@ -28,6 +28,7 @@ assert.equal(initial.gates.switchkinsXyzac.enabled, false);
assert.equal(initial.gates.switchkinsUserk.enabled, false); assert.equal(initial.gates.switchkinsUserk.enabled, false);
assert.equal(initial.gates.vismachClear.visual_only, true); assert.equal(initial.gates.vismachClear.visual_only, true);
assert.equal(send("POWER", "power-on", { active: true }).payload.panel_snapshot.axis_state.machine_state, "ON"); assert.equal(send("POWER", "power-on", { active: true }).payload.panel_snapshot.axis_state.machine_state, "ON");
assert.equal(send("HOME", "home-all", { all: true }).payload.wasm_home_limit.state.homed, true);
const powered = send("SNAPSHOT_REQUEST", "powered-snapshot").payload.panel_snapshot; const powered = send("SNAPSHOT_REQUEST", "powered-snapshot").payload.panel_snapshot;
assert.equal(powered.gates.switchkinsIdentity.enabled, true); assert.equal(powered.gates.switchkinsIdentity.enabled, true);
assert.equal(powered.gates.switchkinsXyzac.enabled, true); assert.equal(powered.gates.switchkinsXyzac.enabled, true);

View File

@@ -15,6 +15,7 @@ const send = (kind, operation_id, payload = {}) => session.accept({ ...base, kin
send("HELLO", "hello"); send("SESSION_ATTACH", "attach"); send("HELLO", "hello"); send("SESSION_ATTACH", "attach");
assert.equal(send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }).payload.status, "verified"); assert.equal(send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }).payload.status, "verified");
assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified"); assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified");
assert.equal(send("HOME", "home-all", { all: true }).payload.wasm_home_limit.state.homed, true);
const xml = await readFile(new URL("../../linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.xml", import.meta.url), "utf8"); const xml = await readFile(new URL("../../linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.xml", import.meta.url), "utf8");
const postgui = await readFile(new URL("../../linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/switchkins_postgui.hal", import.meta.url), "utf8"); const postgui = await readFile(new URL("../../linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/switchkins_postgui.hal", import.meta.url), "utf8");

View File

@@ -17,6 +17,7 @@ function setup(id) {
send("HELLO", "hello"); send("SESSION_ATTACH", "attach"); send("HELLO", "hello"); send("SESSION_ATTACH", "attach");
assert.equal(send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }).payload.status, "verified"); assert.equal(send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }).payload.status, "verified");
assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified"); assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified");
assert.equal(send("HOME", "home-all", { all: true }).payload.wasm_home_limit.state.homed, true);
return { session, send }; return { session, send };
} }

View File

@@ -30,6 +30,7 @@ assert.equal(send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", conf
const power = send("POWER", "power-on", { active: true }); const power = send("POWER", "power-on", { active: true });
assert.equal(power.kind, "COMMAND_RESULT"); assert.equal(power.kind, "COMMAND_RESULT");
assert.equal(power.payload.command, "POWER"); assert.equal(power.payload.command, "POWER");
assert.equal(send("HOME", "home-all", { all: true }).payload.wasm_home_limit.state.homed, true);
const switches = []; const switches = [];
for (const type of [0, 1, 2, 1]) { for (const type of [0, 1, 2, 1]) {
const response = send("KINEMATICS_SELECT", `select-${type}-${seq}`, { type }); const response = send("KINEMATICS_SELECT", `select-${type}-${seq}`, { type });

View File

@@ -28,6 +28,7 @@ async function create(id) {
const loaded = send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }); const loaded = send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) });
assert.equal(loaded.payload.status, "verified"); assert.equal(loaded.payload.status, "verified");
assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified"); assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified");
assert.equal(send("HOME", "home-all", { all: true }).payload.wasm_home_limit.state.homed, true);
return { core, session, send }; return { core, session, send };
} }

View File

@@ -96,6 +96,7 @@ send("HELLO", "hello");
send("SESSION_ATTACH", "attach"); send("SESSION_ATTACH", "attach");
assert.equal(send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }).payload.status, "verified"); assert.equal(send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }).payload.status, "verified");
assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified"); assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified");
assert.equal(send("HOME", "home-all", { all: true }).payload.wasm_home_limit.state.homed, true);
const switchRows = []; const switchRows = [];
for (const [code, expected] of [["M429", 0], ["M428", 1], ["M430", 2], ["M428", 1]]) { for (const [code, expected] of [["M429", 0], ["M428", 1], ["M430", 2], ["M428", 1]]) {

View File

@@ -29,6 +29,7 @@ assert.equal(send("HELLO", "hello").kind, "CAPABILITIES");
assert.equal(send("SESSION_ATTACH", "attach").kind, "SESSION_READY"); assert.equal(send("SESSION_ATTACH", "attach").kind, "SESSION_READY");
assert.equal(send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }).payload.status, "verified"); assert.equal(send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }).payload.status, "verified");
assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified"); assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified");
assert.equal(send("HOME", "home-all", { all: true }).payload.wasm_home_limit.state.homed, true);
assert.equal(session.xyzacKinematics.forward([12, -5, 20, 30, 45]).status, "verified"); assert.equal(session.xyzacKinematics.forward([12, -5, 20, 30, 45]).status, "verified");
const levels = { protocol: [], control: [], state: [], canonical: [], recovery: [] }; const levels = { protocol: [], control: [], state: [], canonical: [], recovery: [] };

View File

@@ -35,6 +35,7 @@ assert.equal(loaded.payload.motion_authority, "wasm");
assert.equal(loaded.payload.configuration.state, "TASK_READY"); assert.equal(loaded.payload.configuration.state, "TASK_READY");
assert.equal(loaded.payload.panel_snapshot.kinematics.joint_count, 5); assert.equal(loaded.payload.panel_snapshot.kinematics.joint_count, 5);
assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified"); assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified");
assert.equal(send("HOME", "home-all", { all: true }).payload.wasm_home_limit.state.homed, true);
const switchRows = []; const switchRows = [];
for (const [code, expected] of [["M429", 0], ["M428", 1], ["M430", 2]]) { for (const [code, expected] of [["M429", 0], ["M428", 1], ["M430", 2]]) {

View File

@@ -15,6 +15,7 @@ const send = (kind, operation_id, payload = {}) => session.accept({ ...base, kin
send("HELLO", "hello"); send("SESSION_ATTACH", "attach"); send("HELLO", "hello"); send("SESSION_ATTACH", "attach");
assert.equal(send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }).payload.status, "verified"); assert.equal(send("LOAD_WORKSPACE", "workspace", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }).payload.status, "verified");
assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified"); assert.equal(send("POWER", "power-on", { active: true }).payload.status, "verified");
assert.equal(send("HOME", "home-all", { all: true }).payload.wasm_home_limit.state.homed, true);
const source = "G21 G90\nG0 X10 Y5 Z200 A30 C45\nG1 X20 Y10 Z180 A35 C50 F100\nG1 X30 Y15 Z160 A40 C55 F100\nM2\n"; const source = "G21 G90\nG0 X10 Y5 Z200 A30 C45\nG1 X20 Y10 Z180 A35 C50 F100\nG1 X30 Y15 Z160 A40 C55 F100\nM2\n";
const loaded = send("PROGRAM_LOAD", "program-load", { source, path: "workflow.ngc" }); const loaded = send("PROGRAM_LOAD", "program-load", { source, path: "workflow.ngc" });
assert.equal(loaded.kind, "COMMAND_RESULT"); assert.equal(loaded.kind, "COMMAND_RESULT");