Validate AXIS task state flow

This commit is contained in:
wangdequan
2026-07-09 18:12:01 -04:00
parent f99ba2bbe8
commit f4b9911d45
57 changed files with 195248 additions and 647 deletions

View File

@@ -1064,7 +1064,7 @@ The validation fails if:
| `tests/wasm/node/verify_sim_configs_inventory_wasm.sh` | Validates the first machine-readable Node inventory layer for `configs/sim`. It ensures native `build/native/sim-configs/summary.tsv`, generated `class-summary.tsv`, and generated `path-matrix.tsv` exist; verifies that generated `path-matrix.tsv` and tracked `docs/sim-configs-coverage-matrix.md` contain the same 159 paths as native `summary.tsv`; checks tracked matrix fields for class, native status, expected-failure reason, and blocked kind drift against generated `path-matrix.tsv`; and checks that generated `class-summary.tsv` matches class/status/expected-failure counts derived from `summary.tsv`. It then executes only entries that are currently vendored and have a defined standalone/WASM runtime path, and writes the sim-config inventory TSV artifacts. The boundary summary records one row per native inventory path with the matrix block, SDK classifier recommendation, declared HAL/UI/HALUI/Python process dependencies, `[EMCIO]DB_PROGRAM`, and execution-chain user-M codes; vendored INI rows must have an available classifier report, vendored hard blocks fail on classifier/matrix drift, safe process-declaring representatives must remain Node/browser `REP` rows with the expected process flag combinations rather than full-process promotions, and the designed full-process blocked rows must remain non-`REP` until their proof criteria are met. The generated inventory summary also requires all hard-blocked rows to remain `SKIP` with matching reasons, and `skip-summary.tsv` must match the matrix-derived skip counts. The blocked-dependency and boundary summaries record hard blocked Python-remap, tool-database, and external user-M rows from source `linuxcnc/configs/sim` INI files for dependency accounting only, including LinuxCNC source/config ownership fields, user-M script side effects, tool DB protocol evidence, Python runtime owner evidence, and completion criteria that all remain non-executing. Current gate: `executed=82`, `passed=82`, `skipped=77`, `unexpected_fail=0`; current skip/block counts are `ASSET_ONLY=65`, `L4_USER_M_PROCESS=1`, `NON_MAIN_CLASS=10`, and `UPSTREAM_DEMO=1`. The executed set now includes the deterministic `woodpecker` plus `qtdragon` / `qtdragon_hd` / `qtvcp_screens` `on_abort.ngc` family, `axis/gladevcp/probe.ngc`, `axis/rose_engine/rcone_demo.ngc`, `axis/vismach/melfa-sim/example.ngc`, and the `axis/vismach/puma` sample programs after vendoring the required machine INI, tool-table, and remap-subroutine inputs. When an upstream INI declares a missing local tool table but the native harness resolved a valid fallback table, the Node inventory stages that native-selected table at the INI-declared path so WASM execution uses the same machine context. The skip reasons are explicit: `NON_MAIN_CLASS`, `L4-TOOL-DB`, `L4-USER-M-PROCESS`, `L4-PYTHON-REMAP`, and `UPSTREAM-DEMO`; an eligible row that lacks vendored machine context is reported as an inventory failure. This runner is an inventory source-of-truth for Node WASM and is intentionally narrower than a browser full inventory. |
| `tests/wasm/node/verify_nc_files_wasm.sh` | Validates representative vendored LinuxCNC `nc_files` examples in Node WASM by copying `3D_Chips.ngc`, `arcspiral.ngc`, `hole-circle.ngc`, `factorial.ngc`, and `m6demo.ngc` into the Emscripten filesystem and forwarding to the LinuxCNC-backed `Interp::open()`/`read()`/`execute()` path. `3D_Chips.ngc` is staged with a minimal INI-declared `tool.tbl` because the upstream program contains `T1 M6`; JavaScript only stages files and checks LinuxCNC output, including `run_step` status records, and does not implement G-code, O-word, tool-change, or M-code behavior. |
| `tests/wasm/node/verify_tp_wasm.sh` | Validates a standalone trajectory-planner WASM module can be built from vendored LinuxCNC TP/TC/Ruckig support source, loaded in Node, and run the same linear, arc, and queued-line planner probe paths covered by the native TP harness. |
| `tests/wasm/node/verify_task_hal_wasm.sh` | Validates the task/motion/HAL WASM runtime-edge adapter. It builds the task-HAL module, checks task-cycle motion snapshots, command buffering, plan/execute motion issue, pause/resume/step, wait-for-motion queue behavior, motion abort/error/soft-limit subordinate sync, top/task/motion/io RCS DONE/EXEC/ERROR aggregation through `rcsStatus`, T-007 `StandaloneEmcStatus` status container export through `statusSource`, `emcStatus`, `task`, and `motionStatus`, source reuse evidence for the narrow `emctask.cc`, `taskintf.cc`, and `emccanon.cc` subsets, T-033 `taskintfMotionBridge` evidence for `emcMotionInit()`/`emcMotionUpdate()`/`emcMotionAbort()` over `lcmot_*`, T-034 `taskintf` traj control evidence for enable/disable/abort/pause/step/resume/set-motion-id, T-035 structured linear move issue evidence for `emcTrajLinearMove()` through `lcmot_write_linear_move()`, T-036 structured jog/home/switchkins issue evidence for `emcJogIncr()``emcJointHome/Unhome()` and `emcMotionSetAout()`, T-037 structured `emctask.cc` abort/state/mode command-result evidence for `emcTaskAbort()``emcTaskSetMode()` and `emcTaskSetState()`, T-038 snapshot-fed `determineMode()``determineState()` and `emcTaskUpdate()` evidence through `updateInputSource`, T-039 plan wait/open/synch/reset evidence through `planAnchors` and `taskPlanOpen`, T-040/T-046 plan read/execute evidence through `planReadExecuteAnchors` plus a staged-program main RUN path that does not call `lctask_load_program_motion_plan_json()`, T-041 canon init/finish/unit/endpoint evidence through `initFinishUnitAnchors`, T-042 canon straight motion evidence through `straightMotionAnchors` and `lastInterpListCommand=EMC_TRAJ_LINEAR_MOVE`, T-043 canon dwell/path-control evidence through `dwellPathControlAnchors`, `delayAppendCount`, and `termCondAppendCount`, T-044 canon spindle/tool command evidence through `spindleToolAnchors`, `spindleAppendCount`, and `toolAppendCount`, and T-045 canon motion output/switchkins evidence through `motionOutputAnchors`, `motionOutputAppendCount`, `motionOutputTaskintfAoutCount`, and `waitInputCount`. It also asserts `nativeTaskReady=false`, `nativeHalSyncReady=false`, and `fullLinuxCncProgramExecutionReady=false`. |
| `tests/wasm/node/verify_task_hal_wasm.sh` | Validates the task/motion/HAL WASM runtime-edge adapter. It builds the task-HAL module, checks task-cycle motion snapshots, command buffering, plan/execute motion issue, pause/resume/step, wait-for-motion queue behavior, motion abort/error/soft-limit subordinate sync, top/task/motion/io RCS DONE/EXEC/ERROR aggregation through `emcStatus`, T-007 `StandaloneEmcStatus` status container export through `statusSource` and `emcStatus`, T-056 absence of legacy task status top-level fields, source reuse evidence for the narrow `emctask.cc`, `taskintf.cc`, and `emccanon.cc` subsets, T-033 `taskintfMotionBridge` evidence for `emcMotionInit()`/`emcMotionUpdate()`/`emcMotionAbort()` over `lcmot_*`, T-034 `taskintf` traj control evidence for enable/disable/abort/pause/step/resume/set-motion-id, T-035 structured linear move issue evidence for `emcTrajLinearMove()` through `lcmot_write_linear_move()`, T-036 structured jog/home/switchkins issue evidence for `emcJogIncr()``emcJointHome/Unhome()` and `emcMotionSetAout()`, T-037 structured `emctask.cc` abort/state/mode command-result evidence for `emcTaskAbort()``emcTaskSetMode()` and `emcTaskSetState()`, T-038 snapshot-fed `determineMode()``determineState()` and `emcTaskUpdate()` evidence through `updateInputSource`, T-039 plan wait/open/synch/reset evidence through `planAnchors` and `taskPlanOpen`, T-040/T-046 plan read/execute evidence through `planReadExecuteAnchors` plus a staged-program main RUN path that does not call `lctask_load_program_motion_plan_json()`, T-041 canon init/finish/unit/endpoint evidence through `initFinishUnitAnchors`, T-042 canon straight motion evidence through `straightMotionAnchors` and `lastInterpListCommand=EMC_TRAJ_LINEAR_MOVE`, T-043 canon dwell/path-control evidence through `dwellPathControlAnchors`, `delayAppendCount`, and `termCondAppendCount`, T-044 canon spindle/tool command evidence through `spindleToolAnchors`, `spindleAppendCount`, and `toolAppendCount`, and T-045 canon motion output/switchkins evidence through `motionOutputAnchors`, `motionOutputAppendCount`, `motionOutputTaskintfAoutCount`, and `waitInputCount`. It also asserts `nativeTaskReady=false`, `nativeHalSyncReady=false`, and `fullLinuxCncProgramExecutionReady=false`. |
| `tests/wasm/node/verify_task_hal_sdk.sh` | Validates the JavaScript task-HAL SDK wrapper around the same C ABI without promoting native task/HAL readiness. |
| `tools/verify_task_hal_readiness_contract.sh` | Validates that the task-HAL manifest, runtime status, WASM smoke, and `docs/source-reuse-map.md` all agree that task/HAL readiness is not promoted. |
| `tools/verify_task_emc_nml_reuse_plan.sh` | Validates the T-029 decision that full upstream `emc_nml.hh` is evaluated but not directly included or vendored yet, and that the phased `StandaloneEmcStatus` / typedef route remains documented. |
@@ -1083,10 +1083,10 @@ The validation fails if:
| `tools/verify_task_emccanon_spindle_tool.sh` | Validates T-044 implementation anchors: upstream spindle/tool command functions, `EMC_SPINDLE_*``EMC_TOOL_*` and `interp_list`, the `emccanon_wasm_subset` spindle/tool command boundary, wrapper status JSON `spindleCommandCount`/`spindleAppendCount`/`toolCommandCount`/`toolAppendCount`, `emccanon_spindle_tool_status=ok`, and the T-044/T-045 matrix transition. |
| `tools/verify_task_emccanon_motion_output.sh` | Validates T-045 implementation anchors: upstream motion output and wait command functions, `EMC_MOTION_SET_DOUT``EMC_MOTION_SET_AOUT``EMC_AUX_INPUT_WAIT` and `interp_list`, the `emccanon_wasm_subset` motion-output boundary, wrapper status JSON `motionOutputCount`/`motionOutputAppendCount`/`motionOutputTaskintfAoutCount`/`waitInputCount`, `emccanon_motion_output_status=ok`, removal of the old `M428` string special-case, and the T-045/T-046 matrix transition. |
| `tools/verify_task_no_json_motion_plan_main_path.sh` | Validates T-046 implementation anchors: primary `verify_task_hal_wasm.mjs` does not call `lctask_load_program_motion_plan_json()`, staged RUN status keeps `motionPlanLoaded=false` and `planId=0`, SDK/state-matrix tests keep `loadProgramMotionPlan()` only as a compatibility/debug entry, smoke output `task_hal_no_json_main_path_status=ok`, and the T-046/T-007 matrix transition. |
| `tools/verify_task_standalone_emc_status.sh` | Validates T-007 implementation anchors: `StandaloneEmcStatus``StandaloneEmcTaskStatus``StandaloneEmcMotionStatus` and `StandaloneEmcIoStatus` exist in the task wrapper, `write_status_snapshot()` synchronizes the container before JSON export, `statusSource=StandaloneEmcStatus` and `emcStatus` are emitted, WASM/SDK/state-matrix tests assert container compatibility with `task``motionStatus` and `rcsStatus`, smoke output `standalone_emc_status_container=ok`, and the T-007 matrix completion. |
| `tools/verify_task_status_json_contract.sh` | Validates T-051 through T-055 status JSON contract anchors: `schemaVersion=1`, `statusSource=StandaloneEmcStatus`, `emcStatus.motion.traj`, task current/read/motion line fields, `emcStatus.motion.axis[]` and `joint[]`, IO aux/tool/coolant shim objects, no `lctask_read_status_json()` call to motion JSON, SDK boundary-only behavior, smoke output `task_status_json_contract=ok`, and full-closure inclusion. |
| `tools/verify_task_working_closure.sh` | Validates T-047/T-048 working closure anchors: task matrix has no `待办``进行中` or `阻塞` rows, next priority is `无`, README/function/NML evaluation documents reflect the T-007/T-046 closed state, and ledger/evidence/decision records include the closure decision. |
| `tools/verify_task_full_closure.sh` | Runs the final task-HAL closure bundle: build task-HAL WASM once, run task runtime smoke, SDK smoke, state matrix, motion/HAL sync, T-007 `StandaloneEmcStatus`, T-046 no-JSON main RUN path, T-051-T-055 status JSON contract, T-047/T-048 working closure, source reuse/drift docs, and readiness contract gates; emits `task_full_closure_status=ok`. |
| `tools/verify_task_standalone_emc_status.sh` | Validates T-007 implementation anchors: `StandaloneEmcStatus``StandaloneEmcTaskStatus``StandaloneEmcMotionStatus` and `StandaloneEmcIoStatus` exist in the task wrapper, `write_status_snapshot()` synchronizes the container before JSON export, `statusSource=StandaloneEmcStatus` and `emcStatus` are emitted, smoke output `standalone_emc_status_container=ok`, and the T-007 matrix completion. |
| `tools/verify_task_status_json_contract.sh` | Validates T-051 through T-056 status JSON contract anchors: `schemaVersion=1`, `statusSource=StandaloneEmcStatus`, `emcStatus.motion.traj`, task current/read/motion line fields, `emcStatus.motion.axis[]` and `joint[]`, IO aux/tool/coolant shim objects, absence of legacy task status top-level fields, no `lctask_read_status_json()` call to motion JSON, SDK boundary-only behavior, smoke output `task_status_json_contract=ok`, and full-closure inclusion. |
| `tools/verify_task_working_closure.sh` | Validates T-047/T-048/T-056 working closure anchors: task matrix has no `待办``进行中` or `阻塞` rows, next priority is `无`, README/function/NML evaluation documents reflect the T-007/T-046 closed state, and ledger/evidence/decision records include the closure decisions. |
| `tools/verify_task_full_closure.sh` | Runs the final task-HAL closure bundle: build task-HAL WASM once, run task runtime smoke, SDK smoke, state matrix, motion/HAL sync, T-007 `StandaloneEmcStatus`, T-046 no-JSON main RUN path, T-051-T-056 status JSON contract, T-047/T-048/T-056 working closure, source reuse/drift docs, and readiness contract gates; emits `task_full_closure_status=ok`. |
| `tests/opfs/node/verify_file_service.sh` | Validates the host-owned OPFS text-file adapter, path model, session snapshot store including custom filenames and envelope/path rejection paths, machine file store, G-code text store including filename rejection paths, OPFS-to-WASM parameter/tool-table bridges, and grouped machine-session loading without moving file persistence, parameter semantics, or tool-table semantics into the WASM core. |
| `tests/browser/verify_ini_panel_browser.sh` | Validates the INI SDK, INI/interpreter WASM module loading, LinuxCNC-backed INI machine-session file-name string lookup, OPFS text-file round trip, generic session snapshot round trip plus custom filename and envelope/path rejection paths, machine file text round trip, G-code text round trip plus filename rejection paths, and the INI panel UI's machine-session load with default OPFS parameter/tool-table file mapping, G-code run, `run_step`-backed progress/line/statement/axis display, canonical-event display paths, and 5-axis remap demo action in a real browser runtime. |
| `tests/browser/verify_interp_browser.sh` | Validates the interpreter-core WASM module loads through the interpreter JS SDK in a real browser runtime and runs selected positive and negative canonical fixtures plus vendored upstream `tests/interp` regression files through vendored LinuxCNC `Interp::execute()` and `Interp::open()`/`read()`/`execute()` via the exported C ABI, including direct browser SDK and OPFS-backed parameter-file restore/save, non-random/random tool-table load/save through vendored LinuxCNC source, vendored `xyzac-trt`/`xyzbc-trt` table-rotary-tilting and `xyzab-tdr` table-dual-rotary switchkins remap demo execution, representative vendored `configs/sim` `foam`, `geometry`, `external_offsets`, `axis/gladevcp/probe.ngc`, deterministic `woodpecker/on_abort.ngc`, bridge-mill, `melfa-sim`, and `puma_cube.ngc` programs through `runSimConfigProgram()`, representative vendored `nc_files` examples through `runFile()` or `runFileWithIni()` when the upstream file requires INI/tool-table context, and a synthetic browser assertion that `planIniFileContextStaging()` can collect INI, program, tool-table, parameter, multi-directory subroutine, executable user-M, and remap-NGC files using only manifest text. |

View File

@@ -51,8 +51,8 @@ semantic rewrites:
| Browser storage | OPFS remains outside the native core; `runtime/opfs/file-service.js` owns browser text-file persistence, `runtime/opfs/path-model.js` owns host-side storage paths for INI, tool table, parameter, G-code, preview-cache, and session-snapshot content, `runtime/opfs/snapshot-store.js` owns generic JSON session snapshot persistence, `runtime/opfs/machine-file-store.js` owns pure-text machine-file and G-code persistence, `runtime/opfs/linuxcnc-parameter-bridge.js` only copies parameter files between OPFS text storage and the LinuxCNC-backed WASM parameter-file ABI, `runtime/opfs/linuxcnc-tool-table-bridge.js` only copies tool tables between OPFS text storage and the LinuxCNC-backed WASM tool-table ABI, and `runtime/opfs/linuxcnc-machine-session-bridge.js` groups INI, parameter, and tool-table loading while using the LinuxCNC-backed INI SDK for `[EMCIO]RANDOM_TOOLCHANGER`, `[RS274NGC]PARAMETER_FILE`, and `[EMCIO]TOOL_TABLE` when available; explicit host session file-name options take precedence over INI-derived file names, missing INI file-name values fall back to host default parameter/tool-table paths, and path validation remains owned by the OPFS path model, including rejection of traversal or nested path segments from INI-derived file names. |
| INI-context SDK staging | `runtime/sdk/src/sim-config-staging.js` exposes `planIniFileContextStaging()` as a generic host-boundary file planner and `planSimConfigStaging()` as the representative vendored `configs/sim` wrapper. The planner reads INI text plus `tools/source-manifest.txt` to collect `[DISPLAY]OPEN_FILE`, `[EMCIO]TOOL_TABLE`, `[RS274NGC]PARAMETER_FILE`, one or more `[RS274NGC]SUBROUTINE_PATH` entries, one or more `[RS274NGC]USER_M_PATH` entries, and remap-NGC files for staging without relying on browser directory enumeration. `runtime/sdk/src/linuxcnc-interp.js` exposes `runSimConfigProgram()` as the execution convenience only: it writes planned text files into the Emscripten filesystem, applies executable bits for user M-code files, and forwards to existing LinuxCNC-backed C ABI entry points such as `runFileWithIni()` or `runFiveAxisRemapFile()`; neither helper implements interpreter, axis, remap, tool, parameter, or user M-code semantics. |
| Task/motion/HAL WASM runtime | `runtime/core/linuxcnc_wrap/linuxcnc_task_hal_wasm.cpp`, `linuxcnc_motion_runtime.c`, and the phase 2 HAL runtime form a deterministic Web simulation adapter. The current boundary has task-cycle motion snapshots, command buffering, plan/execute/motion-update/subordinate-sync/status-write stages, source-anchored `emctask.cc`/`taskintf.cc`/`emccanon.cc` subsets, a T-033 `taskintf.cc` motion bridge for `emcMotionInit()`/`emcMotionUpdate()`/`emcMotionAbort()` over `lcmot_*`, T-034 traj control mapping for enable/disable/abort/pause/step/resume/set-motion-id, T-035 linear move mapping from `emcTrajLinearMove()` to structured `lcmot_write_linear_move()`, T-036 jog/home/switchkins mapping from `emcJogIncr()``emcJointHome/Unhome()` and `emcMotionSetAout()` to structured `lcmot_write_*` calls, T-037 abort/state/mode mapping from `emcTaskAbort()``emcTaskSetMode()` and `emcTaskSetState()` to a structured `emctask.cc` command result, T-038 determine/update snapshot mapping from `LcmotStatusSnapshot` and the IO estop/error latch into `determineMode()``determineState()` and `emcTaskUpdate()`, T-039 plan wait/open/synch/reset mapping from `emcTaskPlanSetWait/IsWait/ClearWait/Synch/Open/Close/Reset()` into source-anchored plan results over staged FS, T-040 plan read/execute/line/level/command mapping from `emcTaskPlanRead/Execute/Line/Level/Command()` into source-anchored plan IO results that can append staged program work to the interp_list path without host JSON motion plans, config/error read snapshots, motion ERROR and soft-limit injection coverage, and top/task/motion/io DONE/EXEC/ERROR aggregation aligned with `emctaskmain.cc`. It is still not a full native task/HAL promotion: `nativeTaskReady=false`, `nativeHalSyncReady=false`, `fullLinuxCncProgramExecutionReady=false`, and `task_hal_runtime_promoted=0` remain required. |
| Task/motion/HAL WASM runtime T-007 StandaloneEmcStatus | T-007 centralizes the required top/task/motion/io status fields into `StandaloneEmcStatus`, a phased `EMC_STAT` equivalent. `write_status_snapshot()` synchronizes task state/mode/interp/exec, motion snapshot fields, IO error/estop state, and RCS aggregation before status JSON is written; compatibility JSON fields `task`, `motionStatus`, `rcsStatus`, and `taskTopLevelStatus` are exported from the same container through `statusSource=StandaloneEmcStatus` and `emcStatus`. Full upstream `emc_nml.hh` and NML transport remain intentionally unpromoted. |
| Task/motion/HAL WASM runtime T-051-T-055 status JSON contract | T-051 through T-055 extend the same `StandaloneEmcStatus` boundary rather than adding a JSON-owned state machine. `emcStatus.motion.traj` maps `LcmotStatusSnapshot` queue/inpos/id/pause/velocity/pose fields to LinuxCNC `EMC_TRAJ_STAT` names; `emcStatus.task.currentLine/readLine/motionLine/callLevel` come from staged plan and motion snapshot evidence; `emcStatus.motion.axis[]` and `joint[]` expose structured axis/joint arrays while legacy `motionStatus.axis` and `joint0` remain compatibility views; `emcStatus.io.aux/tool/coolant` exposes only shim/unsupported IO boundaries. `tools/verify_task_status_json_contract.sh` fixes this schema and confirms `lctask_read_status_json()` does not call motion JSON or advance motion state. |
| Task/motion/HAL WASM runtime T-007 StandaloneEmcStatus | T-007 centralizes the required top/task/motion/io status fields into `StandaloneEmcStatus`, a phased `EMC_STAT` equivalent. `write_status_snapshot()` synchronizes task state/mode/interp/exec, motion snapshot fields, IO error/estop state, and RCS aggregation before status JSON is written; `statusSource=StandaloneEmcStatus` and `emcStatus` are exported from the same container. Full upstream `emc_nml.hh` and NML transport remain intentionally unpromoted. |
| Task/motion/HAL WASM runtime T-051-T-056 status JSON contract | T-051 through T-055 extend the same `StandaloneEmcStatus` boundary rather than adding a JSON-owned state machine. `emcStatus.motion.traj` maps `LcmotStatusSnapshot` queue/inpos/id/pause/velocity/pose fields to LinuxCNC `EMC_TRAJ_STAT` names; `emcStatus.task.currentLine/readLine/motionLine/callLevel` come from staged plan and motion snapshot evidence; `emcStatus.motion.axis[]` and `joint[]` expose structured axis/joint arrays with `axisByName` and `joint0` as same-object helpers; `emcStatus.io.aux/tool/coolant` exposes only shim/unsupported IO boundaries. T-056 removes legacy task status top-level fields. `tools/verify_task_status_json_contract.sh` fixes this schema, gates legacy-field absence, and confirms `lctask_read_status_json()` does not call motion JSON or advance motion state. |
| Task/motion/HAL WASM runtime T-041 canon state | T-041 canon init/finish/unit/endpoint mapping brings `INIT_CANON()``ON_RESET()``FINISH()``USE_LENGTH_UNITS()` and external unit/position getters into the source-anchored `emccanon.cc` subset. Drift remains bounded to deterministic status evidence: counters, length/angle units, and endpoint snapshots are surfaced through task status JSON, while full interpreter/canon process ownership is still unpromoted. |
| Task/motion/HAL WASM runtime T-042 canon straight motion | T-042 straight traverse/feed mapping brings `generate_fast_move()``generate_move()``STRAIGHT_TRAVERSE()` and `STRAIGHT_FEED()` into the source-anchored `emccanon.cc` subset. Drift remains bounded to `EMC_TRAJ_LINEAR_MOVE` / `interp_list` evidence and the existing structured `taskintf.cc` motion command boundary; full native canon offsets, tags, feed modes, and NML queue ownership remain unpromoted. |
| Task/motion/HAL WASM runtime T-043 canon dwell/path-control | T-043 dwell/path-control mapping brings `DWELL()` and `SET_MOTION_CONTROL_MODE()` into the source-anchored `emccanon.cc` subset. Drift remains bounded to `EMC_TRAJ_DELAY` / `EMC_TRAJ_SET_TERM_COND` / `interp_list` evidence and task status counters; full native blend planning, queue ownership, and NML transport remain unpromoted. |

View File

@@ -45,7 +45,7 @@ families, and drift report.
| Native task / motion / HAL sync phase 0 references | `tools/task-hal-source-manifest.txt` lists `src/emc/task/*`, selected `src/emc/motion/*`, `src/hal/*`, `src/emc/nml_intf/emc.hh`, and `src/libnml/posemath/*` | Reference proof for task/motion/HAL Web simulation runtime | `tools/verify_task_hal_source_manifest.sh` compares the local LinuxCNC reference tree and vendored tree where present, emits task/HAL/motion/NML/libnml counts. `tests/native/probe_trt_task_hal_runtime.sh` is opt-in for exclusive host LinuxCNC runtime probing and does not promote by default; Web simulation validation is handled by task/HAL WASM and browser gates while readiness remains false. | `tests/native/verify_task_hal_phase0.sh` |
| HAL runtime phase 2 minimal boundary | `src/hal/hal_lib.c`, `src/hal/hal_priv.h`, `src/hal/components/threads.c`, `src/hal/utils/halcmd_commands.cc` as source references; `runtime/core/shims/hal.h` type boundary | Runtime-edge adapter, not full native HAL promotion | `runtime/core/linuxcnc_wrap/linuxcnc_hal_runtime.cpp` owns C/C++ HAL pin, signal, param, net, and thread scheduler state behind LinuxCNC-style `hal_*` APIs and `lchal_*` C ABI; `loadusr` is blocked evidence; task runtime and native HAL comparison remain future work | `tests/wasm/node/verify_hal_runtime.sh` WASM Node smoke; native fallback only when `emcc` is unavailable |
| Motion/HAL sync phase 3 minimal boundary | `src/emc/motion/motion.h`, `src/emc/motion/command.c`, `src/emc/motion/control.c`, `src/emc/motion/mot_priv.h`, `src/emc/task/taskintf.cc` as source references | Runtime-edge adapter, not full native motion promotion | `runtime/core/linuxcnc_wrap/linuxcnc_motion_runtime.c` exposes the planned `lcmot_*` C ABI, accepts LinuxCNC-style motion command JSON, advances deterministic servo cycles, and synchronizes `motion.*`, `axis.*`, and `joint.*` HAL pins through the phase 2 HAL runtime; full LinuxCNC `emcmotController()` and task/NML queue integration remain future work | `tests/wasm/node/verify_motion_hal_sync.sh` WASM Node smoke |
| Task/motion/HAL simulation runtime | `src/emc/task/task.hh`, `src/emc/task/emctask.cc`, `src/emc/task/emctaskmain.cc`, `src/emc/task/taskintf.cc`, `src/emc/task/emccanon.cc`, `src/emc/nml_intf/emc.hh`, and upstream `src/emc/nml_intf/emc_nml.hh` as an evaluated but not vendored status-container reference | Runtime-edge adapter, not full native task/HAL promotion | `runtime/core/linuxcnc_wrap/linuxcnc_task_hal_wasm.cpp` exposes the planned `lctask_*` C ABI, stages files, opens a program, tracks task state/mode/interp/exec status, and routes RUN/PAUSE/RESUME/ABORT/MDI/JOG/HOME through the phase 3 motion command queue. The current WASM simulation path uses narrow `emctask.cc`, `taskintf.cc`, and `emccanon.cc` source-anchored subsets for status update, task-to-motion command envelopes, and canonical straight motion envelopes. T-007 adds `StandaloneEmcStatus` as the current `EMC_STAT`-equivalent container for top/task/motion/io required fields; `statusSource=StandaloneEmcStatus`, `emcStatus`, `task`, `motionStatus`, and `rcsStatus` are written from that centralized status snapshot. T-051 through T-055 close the status JSON contract batches: `emcStatus.motion.traj`, task current/read/motion line fields, `emcStatus.motion.axis[]`/`joint[]`, IO aux/tool/coolant shim boundaries, and the dedicated schema gate. T-033 adds a narrow `taskintf.cc` motion bridge for `emcMotionInit()`/`emcMotionUpdate()`/`emcMotionAbort()` over `lcmot_*`, including `lcmot_read_config_snapshot()` and `lcmot_read_error_message()` for config/error reads. T-034 extends the `taskintf.cc` traj control subset so `emcTrajEnable()`/`emcTrajDisable()`/`emcTrajAbort()`/`emcTrajPause()`/`emcTrajStep()`/`emcTrajResume()`/`emcTrajSetMotionId()` map to `lcmot` command/state. T-035 maps `emcTrajLinearMove()` to structured `lcmot_write_linear_move()` instead of task-owned linear move JSON on the taskintf path. T-036 maps `emcJogIncr()``emcJointHome()``emcJointUnhome()` and `emcMotionSetAout()` to structured `lcmot_write_jog_incr()``lcmot_write_joint_home()``lcmot_write_joint_unhome()` and `lcmot_write_aout()`. T-037 maps `emcTaskAbort()``emcTaskSetMode()` and `emcTaskSetState()` into the `emctask.cc` subset so abort/state/mode decisions come from a source-anchored command result while task-cycle command buffering remains intact. T-038 feeds `determineMode()``determineState()` and `emcTaskUpdate()` from `LcmotStatusSnapshot` plus the IO estop/error latch instead of deriving update inputs from task-owned state strings. T-039 maps `emcTaskPlanSetWait()``emcTaskPlanIsWait()``emcTaskPlanClearWait()``emcTaskPlanSynch()``emcTaskPlanOpen()``emcTaskPlanClose()` and `emcTaskPlanReset()` into the `emctask.cc` subset so plan wait/open/synch/reset state is driven by source-anchored plan results over the staged FS boundary. T-040 maps `emcTaskPlanRead()``emcTaskPlanExecute()``emcTaskPlanLine()``emcTaskPlanLevel()` and `emcTaskPlanCommand()` into the `emctask.cc` subset so staged program lines can drive the interp_list/taskintf path without requiring host-provided JSON motion plans. It now has task-cycle motion snapshots, WAITING_FOR_MOTION/queue semantics, motion ERROR and soft-limit injection coverage, and top/task/motion/io DONE/EXEC/ERROR aggregation matching the `emctaskmain.cc` top-level status order. T-029 deliberately keeps full `emc_nml.hh` out of the vendor tree and records a phased `StandaloneEmcStatus`/typedef path while NML transport remains unpromoted. Full `emctaskmain.cc`/NML/native HAL/native motion process topology is not promoted. `task_hal_runtime_promoted=0`, `nativeTaskReady=false`, `nativeHalSyncReady=false`, and `fullLinuxCncProgramExecutionReady=false` are the current readiness contract. Hardware drive, host realtime kernel, external user-M process, full tool DB process, and full LinuxCNC program execution remain future work. | `tools/verify_task_full_closure.sh`; `tools/verify_task_status_json_contract.sh`; `tools/verify_task_standalone_emc_status.sh`; `tools/verify_task_working_closure.sh`; `tools/verify_task_hal_readiness_contract.sh`; `tools/verify_task_emc_nml_reuse_plan.sh`; `tools/verify_task_source_reuse_drift_docs.sh`; `tools/verify_task_emctask_state_mode.sh`; `tools/verify_task_emctask_update_snapshot.sh`; `tools/verify_task_emctask_plan_open_wait.sh`; `tools/verify_task_emctask_plan_read_execute.sh`; `tools/verify_task_taskintf_motion_bridge.sh`; `tools/verify_task_taskintf_traj_control.sh`; `tools/verify_task_taskintf_linear_move.sh`; `tools/verify_task_taskintf_jog_home_switchkins.sh`; `tests/wasm/node/verify_task_status_json_contract.mjs`; `tests/wasm/node/verify_task_hal_wasm.sh`; `tests/wasm/node/verify_task_hal_sdk.sh`; `web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_task_hal_runtime.mjs`; browser smoke |
| Task/motion/HAL simulation runtime | `src/emc/task/task.hh`, `src/emc/task/emctask.cc`, `src/emc/task/emctaskmain.cc`, `src/emc/task/taskintf.cc`, `src/emc/task/emccanon.cc`, `src/emc/nml_intf/emc.hh`, and upstream `src/emc/nml_intf/emc_nml.hh` as an evaluated but not vendored status-container reference | Runtime-edge adapter, not full native task/HAL promotion | `runtime/core/linuxcnc_wrap/linuxcnc_task_hal_wasm.cpp` exposes the planned `lctask_*` C ABI, stages files, opens a program, tracks task state/mode/interp/exec status, and routes RUN/PAUSE/RESUME/ABORT/MDI/JOG/HOME through the phase 3 motion command queue. The current WASM simulation path uses narrow `emctask.cc`, `taskintf.cc`, and `emccanon.cc` source-anchored subsets for status update, task-to-motion command envelopes, and canonical straight motion envelopes. T-007 adds `StandaloneEmcStatus` as the current `EMC_STAT`-equivalent container for top/task/motion/io required fields; `statusSource=StandaloneEmcStatus` and `emcStatus` are written from that centralized status snapshot. T-051 through T-055 close the status JSON contract batches: `emcStatus.motion.traj`, task current/read/motion line fields, `emcStatus.motion.axis[]`/`joint[]`, IO aux/tool/coolant shim boundaries, and the dedicated schema gate; T-056 removes legacy task status top-level fields and gates their absence. T-033 adds a narrow `taskintf.cc` motion bridge for `emcMotionInit()`/`emcMotionUpdate()`/`emcMotionAbort()` over `lcmot_*`, including `lcmot_read_config_snapshot()` and `lcmot_read_error_message()` for config/error reads. T-034 extends the `taskintf.cc` traj control subset so `emcTrajEnable()`/`emcTrajDisable()`/`emcTrajAbort()`/`emcTrajPause()`/`emcTrajStep()`/`emcTrajResume()`/`emcTrajSetMotionId()` map to `lcmot` command/state. T-035 maps `emcTrajLinearMove()` to structured `lcmot_write_linear_move()` instead of task-owned linear move JSON on the taskintf path. T-036 maps `emcJogIncr()``emcJointHome()``emcJointUnhome()` and `emcMotionSetAout()` to structured `lcmot_write_jog_incr()``lcmot_write_joint_home()``lcmot_write_joint_unhome()` and `lcmot_write_aout()`. T-037 maps `emcTaskAbort()``emcTaskSetMode()` and `emcTaskSetState()` into the `emctask.cc` subset so abort/state/mode decisions come from a source-anchored command result while task-cycle command buffering remains intact. T-038 feeds `determineMode()``determineState()` and `emcTaskUpdate()` from `LcmotStatusSnapshot` plus the IO estop/error latch instead of deriving update inputs from task-owned state strings. T-039 maps `emcTaskPlanSetWait()``emcTaskPlanIsWait()``emcTaskPlanClearWait()``emcTaskPlanSynch()``emcTaskPlanOpen()``emcTaskPlanClose()` and `emcTaskPlanReset()` into the `emctask.cc` subset so plan wait/open/synch/reset state is driven by source-anchored plan results over the staged FS boundary. T-040 maps `emcTaskPlanRead()``emcTaskPlanExecute()``emcTaskPlanLine()``emcTaskPlanLevel()` and `emcTaskPlanCommand()` into the `emctask.cc` subset so staged program lines can drive the interp_list/taskintf path without requiring host-provided JSON motion plans. It now has task-cycle motion snapshots, WAITING_FOR_MOTION/queue semantics, motion ERROR and soft-limit injection coverage, and top/task/motion/io DONE/EXEC/ERROR aggregation matching the `emctaskmain.cc` top-level status order. T-029 deliberately keeps full `emc_nml.hh` out of the vendor tree and records a phased `StandaloneEmcStatus`/typedef path while NML transport remains unpromoted. Full `emctaskmain.cc`/NML/native HAL/native motion process topology is not promoted. `task_hal_runtime_promoted=0`, `nativeTaskReady=false`, `nativeHalSyncReady=false`, and `fullLinuxCncProgramExecutionReady=false` are the current readiness contract. Hardware drive, host realtime kernel, external user-M process, full tool DB process, and full LinuxCNC program execution remain future work. | `tools/verify_task_full_closure.sh`; `tools/verify_task_status_json_contract.sh`; `tools/verify_task_standalone_emc_status.sh`; `tools/verify_task_working_closure.sh`; `tools/verify_task_hal_readiness_contract.sh`; `tools/verify_task_emc_nml_reuse_plan.sh`; `tools/verify_task_source_reuse_drift_docs.sh`; `tools/verify_task_emctask_state_mode.sh`; `tools/verify_task_emctask_update_snapshot.sh`; `tools/verify_task_emctask_plan_open_wait.sh`; `tools/verify_task_emctask_plan_read_execute.sh`; `tools/verify_task_taskintf_motion_bridge.sh`; `tools/verify_task_taskintf_traj_control.sh`; `tools/verify_task_taskintf_linear_move.sh`; `tools/verify_task_taskintf_jog_home_switchkins.sh`; `tests/wasm/node/verify_task_status_json_contract.mjs`; `tests/wasm/node/verify_task_hal_wasm.sh`; `tests/wasm/node/verify_task_hal_sdk.sh`; `web-rtcp-5axis-sim-plan/tests/node/verify_linuxcnc_task_hal_runtime.mjs`; browser smoke |
| Task/motion/HAL simulation runtime T-041 canon state | `src/emc/task/emccanon.cc` init/finish/unit/getter references | Runtime-edge adapter, not full native canon promotion | T-041 maps `INIT_CANON()``ON_RESET()``FINISH()``USE_LENGTH_UNITS()``GET_EXTERNAL_LENGTH_UNITS()``GET_EXTERNAL_ANGLE_UNITS()` and `GET_EXTERNAL_POSITION*()` into the `emccanon.cc` subset so canonical units, reset/finish counters, and external endpoint state are tracked from motion/config snapshots and surfaced through task status JSON without promoting full interpreter/canon ownership. | `tools/verify_task_emccanon_init_finish_unit.sh`; `tests/wasm/node/verify_task_hal_wasm.sh` |
| Task/motion/HAL simulation runtime T-042 canon straight motion | `src/emc/task/emccanon.cc` straight traverse/feed references | Runtime-edge adapter, not full native canon promotion | T-042 maps `generate_fast_move()``generate_move()``STRAIGHT_TRAVERSE()` and `STRAIGHT_FEED()` into the `emccanon.cc` subset so canonical straight traverse/feed calls produce `EMC_TRAJ_LINEAR_MOVE` evidence on the task `interp_list` path before crossing into the structured `taskintf.cc` / `lcmot_write_linear_move()` motion boundary. | `tools/verify_task_emccanon_straight_motion.sh`; `tests/wasm/node/verify_task_hal_wasm.sh` |
| Task/motion/HAL simulation runtime T-043 canon dwell/path-control | `src/emc/task/emccanon.cc` dwell and term-condition references | Runtime-edge adapter, not full native canon promotion | T-043 maps `DWELL()` and `SET_MOTION_CONTROL_MODE()` into the `emccanon.cc` subset so canonical dwell emits `EMC_TRAJ_DELAY` evidence and path-control emits `EMC_TRAJ_SET_TERM_COND` evidence on the task `interp_list` path without promoting full native canon blending, queue, or NML ownership. | `tools/verify_task_emccanon_dwell_path_control.sh`; `tests/wasm/node/verify_task_hal_wasm.sh` |

View File

@@ -284,6 +284,19 @@ static void queue_error_message(const char *message)
state->error_count += 1;
}
static void clear_motion_fault_latch(LcmotRuntime *state)
{
if (!state) {
return;
}
state->aborted = 0;
state->motion_error = 0;
state->on_soft_limit = 0;
state->error_head = 0;
state->error_tail = 0;
state->error_count = 0;
}
static void sync_hal_pins(void)
{
int i;
@@ -360,6 +373,7 @@ static void apply_command(const LcmotCommand *command)
switch (command->type) {
case LCMOT_CMD_ENABLE:
state->motion_enabled = 1;
clear_motion_fault_latch(state);
break;
case LCMOT_CMD_DISABLE:
state->motion_enabled = 0;

View File

@@ -2215,17 +2215,16 @@ std::string status_json()
out << ",\"hardwareDrive\":false";
out << ",\"statusSource\":\"" << json_escape(emc_status.source) << "\"";
out << ",\"schemaVersion\":1";
out << ",\"taskTopLevelStatus\":\"" << json_escape(emc_status.top_rcs_status) << "\"";
out << ",\"rcsStatus\":{\"top\":\"" << json_escape(emc_status.top_rcs_status) << "\"";
out << ",\"task\":\"" << json_escape(task_status.rcs_status) << "\"";
out << ",\"motion\":\"" << json_escape(motion.rcs_status) << "\"";
out << ",\"io\":\"" << json_escape(emc_status.io.rcs_status) << "\"}";
out << ",\"task\":{\"state\":\"" << json_escape(task_status.state) << "\"";
out << ",\"emcStatus\":{\"source\":\"" << json_escape(emc_status.source) << "\"";
out << ",\"sourcePath\":\"" << json_escape(emc_status.source_path) << "\"";
out << ",\"valid\":" << (emc_status.valid ? "true" : "false");
out << ",\"top\":{\"status\":\"" << json_escape(emc_status.top_rcs_status) << "\"}";
out << ",\"task\":{\"status\":\"" << json_escape(task_status.rcs_status) << "\"";
out << ",\"state\":\"" << json_escape(task_status.state) << "\"";
out << ",\"mode\":\"" << json_escape(task_status.mode) << "\"";
out << ",\"interpState\":\"" << json_escape(task_status.interp_state) << "\"";
out << ",\"interpResumeState\":\"" << json_escape(task_status.interp_resume_state) << "\"";
out << ",\"execState\":\"" << json_escape(task_status.exec_state) << "\"";
out << ",\"status\":\"" << json_escape(task_status.rcs_status) << "\"";
out << ",\"taskPaused\":" << (task_status.task_paused ? "true" : "false");
out << ",\"singleStepping\":" << (task_status.single_stepping ? "true" : "false");
out << ",\"cycle\":" << task_status.cycle;
@@ -2255,84 +2254,30 @@ std::string status_json()
out << ",\"readLine\":" << task_status.read_line;
out << ",\"motionLine\":" << task_status.motion_line;
out << ",\"callLevel\":" << task_status.call_level << "}";
out << ",\"servoCycle\":" << state.servo_cycle;
out << ",\"motionStatus\":";
if (motion.valid) {
out << "{\"semanticBoundary\":\"linuxcnc_motion_runtime_phase3_minimal\"";
out << ",\"motionRuntimeReady\":true";
out << ",\"nativeHalSyncReady\":false";
out << ",\"cycle\":" << motion.cycle;
out << ",\"motion\":{\"programLine\":" << motion.program_line;
out << ",\"id\":" << motion.motion_id;
out << ",\"status\":" << motion.status;
out << ",\"enabled\":" << (motion.enabled ? "true" : "false");
out << ",\"nextMotionId\":" << motion.next_motion_id;
out << ",\"motionType\":" << motion.motion_type;
out << ",\"coordMode\":" << motion.coord_mode;
out << ",\"teleopMode\":" << motion.teleop_mode;
out << ",\"inPosition\":" << (motion.in_position ? "true" : "false");
out << ",\"paused\":" << (motion.paused ? "true" : "false");
out << ",\"aborted\":" << (motion.aborted ? "true" : "false");
out << ",\"motionError\":" << (motion.motion_error ? "true" : "false");
out << ",\"onSoftLimit\":" << (motion.on_soft_limit ? "true" : "false");
out << ",\"stepping\":" << (motion.stepping ? "true" : "false");
out << ",\"idForStep\":" << motion.id_for_step;
out << ",\"motionId\":" << motion.motion_id;
out << ",\"switchkinsType\":" << motion.switchkins_type;
out << ",\"requestedVel\":" << motion.requested_vel;
out << ",\"currentVel\":" << motion.current_vel;
out << ",\"queueDepth\":" << motion.queue_depth;
out << ",\"queueCapacity\":" << motion.queue_capacity;
out << ",\"queueFull\":" << (motion.queue_full ? "true" : "false");
out << ",\"activeDepth\":" << motion.active_depth;
out << ",\"commandQueueDepth\":" << motion.command_queue_depth << "}";
out << ",\"axis\":{\"x\":" << motion.axis_fb[0];
out << ",\"y\":" << motion.axis_fb[1];
out << ",\"z\":" << motion.axis_fb[2];
out << ",\"a\":" << motion.axis_fb[3];
out << ",\"b\":" << motion.axis_fb[4];
out << ",\"c\":" << motion.axis_fb[5] << "}";
out << ",\"joint0\":{\"motorPosCmd\":" << motion.joint_cmd[0];
out << ",\"motorPosFb\":" << motion.joint_fb[0] << "}";
out << ",\"queueDepth\":" << motion.queue_depth;
out << ",\"activeDepth\":" << motion.active_depth;
out << ",\"commandQueueDepth\":" << motion.command_queue_depth;
out << "}";
} else {
out << "{}";
}
out << ",\"emcStatus\":{\"source\":\"" << json_escape(emc_status.source) << "\"";
out << ",\"sourcePath\":\"" << json_escape(emc_status.source_path) << "\"";
out << ",\"valid\":" << (emc_status.valid ? "true" : "false");
out << ",\"top\":{\"status\":\"" << json_escape(emc_status.top_rcs_status) << "\"}";
out << ",\"task\":{\"status\":\"" << json_escape(task_status.rcs_status) << "\"";
out << ",\"state\":\"" << json_escape(task_status.state) << "\"";
out << ",\"mode\":\"" << json_escape(task_status.mode) << "\"";
out << ",\"interpState\":\"" << json_escape(task_status.interp_state) << "\"";
out << ",\"execState\":\"" << json_escape(task_status.exec_state) << "\"";
out << ",\"cycle\":" << task_status.cycle;
out << ",\"programOpen\":" << (task_status.program_open ? "true" : "false");
out << ",\"taskPlanOpen\":" << (task_status.task_plan_open ? "true" : "false");
out << ",\"planId\":" << task_status.plan_id;
out << ",\"motionPlanLoaded\":" << (task_status.motion_plan_loaded ? "true" : "false");
out << ",\"pendingCommandDepth\":" << task_status.pending_command_depth;
out << ",\"pendingExecuteMotionDepth\":" << task_status.pending_execute_motion_depth;
out << ",\"allHomed\":" << (task_status.all_homed ? "true" : "false");
out << ",\"nextProgramLine\":" << task_status.next_program_line;
out << ",\"currentLine\":" << task_status.current_line;
out << ",\"readLine\":" << task_status.read_line;
out << ",\"motionLine\":" << task_status.motion_line;
out << ",\"callLevel\":" << task_status.call_level << "}";
out << ",\"motion\":{\"valid\":" << (motion.valid ? "true" : "false");
out << ",\"status\":\"" << json_escape(motion.rcs_status) << "\"";
out << ",\"programLine\":" << motion.program_line;
out << ",\"motionId\":" << motion.motion_id;
out << ",\"enabled\":" << (motion.enabled ? "true" : "false");
out << ",\"nextMotionId\":" << motion.next_motion_id;
out << ",\"motionType\":" << motion.motion_type;
out << ",\"coordMode\":" << motion.coord_mode;
out << ",\"teleopMode\":" << motion.teleop_mode;
out << ",\"inPosition\":" << (motion.in_position ? "true" : "false");
out << ",\"paused\":" << (motion.paused ? "true" : "false");
out << ",\"aborted\":" << (motion.aborted ? "true" : "false");
out << ",\"motionError\":" << (motion.motion_error ? "true" : "false");
out << ",\"queueDepth\":" << motion.queue_depth;
out << ",\"queueCapacity\":" << motion.queue_capacity;
out << ",\"queueFull\":" << (motion.queue_full ? "true" : "false");
out << ",\"activeDepth\":" << motion.active_depth;
out << ",\"commandQueueDepth\":" << motion.command_queue_depth;
out << ",\"onSoftLimit\":" << (motion.on_soft_limit ? "true" : "false");
out << ",\"stepping\":" << (motion.stepping ? "true" : "false");
out << ",\"idForStep\":" << motion.id_for_step;
out << ",\"switchkinsType\":" << motion.switchkins_type;
out << ",\"requestedVel\":" << motion.requested_vel;
out << ",\"currentVel\":" << motion.current_vel;
out << ",\"traj\":{\"enabled\":" << (motion.enabled ? "true" : "false");
out << ",\"inpos\":" << (motion.in_position ? "true" : "false");
out << ",\"queue\":" << motion.queue_depth;

View File

@@ -32,6 +32,14 @@ function prepareHomedAuto() {
assert.equal(sdk.sendCommand({ type: "EMC_TASK_SET_MODE", mode: "AUTO" }), 0);
}
function assertNoLegacyStatusFields(status) {
assert.equal("taskTopLevelStatus" in status, false);
assert.equal("rcsStatus" in status, false);
assert.equal("task" in status, false);
assert.equal("servoCycle" in status, false);
assert.equal("motionStatus" in status, false);
}
sdk.initSession({
iniPath: "xyzac-trt.ini",
iniText: "[TRAJ]\nCOORDINATES = X Y Z A C\n",
@@ -43,42 +51,43 @@ assert.equal(sdk.sendCommand({ type: "EMC_TASK_PLAN_RUN", line: 0 }), 0);
assert.equal(sdk.runCycles({ taskCycles: 1 }), 0);
let status = sdk.readStatus();
assertNoLegacyStatusFields(status);
assert.equal(status.semanticBoundary, "linuxcnc_task_motion_hal_wasm_phase4_minimal");
assert.equal(status.taskRuntimeReady, true);
assert.equal(status.motionStatus.motion.programLine, 1);
assert.equal(status.motionStatus.axis.x, 3);
assert.equal(status.emcStatus.motion.programLine, 1);
assert.equal(status.emcStatus.motion.axisByName.x, 3);
assert.equal(status.halSnapshot.pins["motion.program-line"].value, 1);
assert.equal(status.task.motionPlanLoaded, false);
assert.equal(status.task.planId, 0);
assert.equal(status.emcStatus.task.motionPlanLoaded, false);
assert.equal(status.emcStatus.task.planId, 0);
assert.equal(status.emctaskSourceReuse.planReadCount > 0, true);
assert.equal(status.statusSource, "StandaloneEmcStatus");
assert.equal(status.schemaVersion, 1);
assert.equal(status.emcStatus.source, "StandaloneEmcStatus");
assert.equal(status.emcStatus.top.status, status.taskTopLevelStatus);
assert.equal(status.emcStatus.task.status, status.rcsStatus.task);
assert.equal(status.emcStatus.task.state, status.task.state);
assert.equal(status.emcStatus.task.mode, status.task.mode);
assert.equal(status.emcStatus.task.currentLine, status.task.currentLine);
assert.equal(status.emcStatus.task.readLine, status.task.readLine);
assert.equal(status.emcStatus.task.motionLine, status.task.motionLine);
assert.equal(status.emcStatus.motion.status, status.rcsStatus.motion);
assert.equal(status.emcStatus.motion.programLine, status.motionStatus.motion.programLine);
assert.equal(status.emcStatus.motion.traj.queue, status.motionStatus.motion.queueDepth);
assert.equal(status.emcStatus.motion.traj.inpos, status.motionStatus.motion.inPosition);
assert.equal(status.emcStatus.motion.traj.id, status.motionStatus.motion.id);
assert.equal(status.emcStatus.motion.traj.actualPosition.x, status.motionStatus.axis.x);
assert.equal(status.emcStatus.motion.axisByName.x, status.motionStatus.axis.x);
assert.equal(typeof status.emcStatus.top.status, "string");
assert.equal(typeof status.emcStatus.task.status, "string");
assert.equal(status.emcStatus.task.state, "ON");
assert.equal(status.emcStatus.task.mode, "AUTO");
assert.equal(typeof status.emcStatus.task.currentLine, "number");
assert.equal(typeof status.emcStatus.task.readLine, "number");
assert.equal(typeof status.emcStatus.task.motionLine, "number");
assert.equal(typeof status.emcStatus.motion.status, "string");
assert.equal(typeof status.emcStatus.motion.programLine, "number");
assert.equal(status.emcStatus.motion.traj.queue, status.emcStatus.motion.queueDepth);
assert.equal(status.emcStatus.motion.traj.inpos, status.emcStatus.motion.inPosition);
assert.equal(status.emcStatus.motion.traj.id, status.emcStatus.motion.motionId);
assert.equal(status.emcStatus.motion.traj.actualPosition.x, status.emcStatus.motion.axisByName.x);
assert.equal(typeof status.emcStatus.motion.axisByName.x, "number");
assert.equal(Array.isArray(status.emcStatus.motion.axis), true);
assert.equal(status.emcStatus.motion.axis[0].posFb, status.motionStatus.axis.x);
assert.equal(status.emcStatus.motion.joint[0].motorPosCmd, status.motionStatus.joint0.motorPosCmd);
assert.equal(status.emcStatus.io.status, status.rcsStatus.io);
assert.equal(status.emcStatus.motion.axis[0].posFb, status.emcStatus.motion.axisByName.x);
assert.equal(status.emcStatus.motion.joint[0].motorPosCmd, status.emcStatus.motion.joint0.motorPosCmd);
assert.equal(typeof status.emcStatus.io.status, "string");
assert.equal(status.emcStatus.io.tool.supported, false);
assert.equal(status.emcStatus.io.coolant.supported, false);
assert.equal(sdk.sendCommand({ type: "EMC_TASK_PLAN_EXECUTE", mdi: "M429" }), 0);
assert.equal(sdk.runCycles({ taskCycles: 1 }), 0);
status = sdk.readStatus();
assert.equal(status.motionStatus.motion.switchkinsType, 0);
assert.equal(status.emcStatus.motion.switchkinsType, 0);
assert.equal(status.emccanonSourceReuse.lastMotionOutputCommand, "SET_AUX_OUTPUT_VALUE");
assert.equal(status.emccanonSourceReuse.lastMotionOutputMessage, "EMC_MOTION_SET_AOUT");
assert.equal(status.emccanonSourceReuse.motionOutputTaskintfAoutCount > 0, true);
@@ -126,15 +135,15 @@ prepareHomedAuto();
assert.equal(sdk.sendCommand({ type: "EMC_TASK_PLAN_RUN", line: 0 }), 0);
assert.equal(sdk.runCycles({ taskCycles: 1, taskPeriodNs: 1000000000, servoPeriodNs: 1000000 }), 0);
status = sdk.readStatus();
assert.equal(status.motionStatus.motion.programLine, 1);
assert.equal(status.motionStatus.axis.x > 0 && status.motionStatus.axis.x < 2, true);
assert.equal(status.motionStatus.motion.currentVel, 1);
assert.equal(status.motionStatus.motion.currentVel !== 60, true);
assert.equal(status.emcStatus.motion.programLine, 1);
assert.equal(status.emcStatus.motion.axisByName.x > 0 && status.emcStatus.motion.axisByName.x < 2, true);
assert.equal(status.emcStatus.motion.currentVel, 1);
assert.equal(status.emcStatus.motion.currentVel !== 60, true);
assert.equal(sdk.runCycles({ taskCycles: 119, taskPeriodNs: 1000000000, servoPeriodNs: 1000000 }), 0);
status = sdk.readStatus();
assert.equal(status.motionStatus.motion.programLine, 2);
assert.equal(status.motionStatus.motion.currentVel, 10);
assert.equal(status.motionStatus.axis.x >= 120, true);
assert.equal(status.emcStatus.motion.programLine, 2);
assert.equal(status.emcStatus.motion.currentVel, 10);
assert.equal(status.emcStatus.motion.axisByName.x >= 120, true);
sdk.resetSession();
sdk.initSession({
@@ -181,19 +190,19 @@ prepareHomedAuto();
assert.equal(sdk.sendCommand({ type: "EMC_TASK_PLAN_RUN", line: 0 }), 0);
assert.equal(sdk.runCycles({ taskCycles: 1, taskPeriodNs: 500000000, servoPeriodNs: 1000000 }), 0);
status = sdk.readStatus();
assert.equal(status.task.openedLineCount, 6);
assert.equal(status.task.openedSourceLineCount, 6);
assert.equal(status.task.executableLineCount, 3);
assert.equal(status.motionStatus.motion.programLine, 3);
assert.equal(status.emcStatus.task.openedLineCount, 6);
assert.equal(status.emcStatus.task.openedSourceLineCount, 6);
assert.equal(status.emcStatus.task.executableLineCount, 3);
assert.equal(status.emcStatus.motion.programLine, 3);
assert.equal(status.halSnapshot.pins["motion.program-line"].value, 3);
assert.equal(sdk.runCycles({ taskCycles: 1, taskPeriodNs: 600000000, servoPeriodNs: 1000000 }), 0);
status = sdk.readStatus();
assert.equal(status.motionStatus.motion.programLine, 5);
assert.equal(status.emcStatus.motion.programLine, 5);
assert.equal(status.halSnapshot.pins["motion.program-line"].value, 5);
assert.equal(status.task.nextProgramLine, 5);
assert.equal(status.emcStatus.task.nextProgramLine, 5);
assert.equal(sdk.runCycles({ taskCycles: 1, taskPeriodNs: 1000000000, servoPeriodNs: 1000000 }), 0);
status = sdk.readStatus();
assert.equal(status.task.nextProgramLine, 6);
assert.equal(status.emcStatus.task.nextProgramLine, 6);
console.log("linuxcnc_task_hal_sdk=ok");
console.log("task_hal_sdk_status_snapshot=ok");

View File

@@ -59,7 +59,9 @@ function sendMotion(command) {
}
function status() {
return readJson((outPtr, outLen) => runtime._lctask_read_status_json(outPtr, outLen));
const snapshot = readJson((outPtr, outLen) => runtime._lctask_read_status_json(outPtr, outLen));
assertNoLegacyStatusFields(snapshot);
return snapshot;
}
function motionStatus() {
@@ -70,6 +72,14 @@ function events() {
return readJson((outPtr, outLen) => runtime._lctask_read_events_json(outPtr, outLen)).events;
}
function assertNoLegacyStatusFields(snapshot) {
assert.equal("taskTopLevelStatus" in snapshot, false);
assert.equal("rcsStatus" in snapshot, false);
assert.equal("task" in snapshot, false);
assert.equal("servoCycle" in snapshot, false);
assert.equal("motionStatus" in snapshot, false);
}
const session = {
iniPath: "xyzac-trt.ini",
iniText: "[TRAJ]\nCOORDINATES = X Y Z A C\n",
@@ -112,48 +122,45 @@ assert.equal(snapshot.taskCommandsDriveMotionRuntime, true);
assert.equal(snapshot.nativeTaskReady, false);
assert.equal(snapshot.nativeHalSyncReady, false);
assert.equal(snapshot.fullLinuxCncProgramExecutionReady, false);
assert.equal(snapshot.taskTopLevelStatus, "EXEC");
assert.equal(snapshot.rcsStatus.top, "EXEC");
assert.equal(snapshot.rcsStatus.task, "EXEC");
assert.equal(snapshot.rcsStatus.motion, "DONE");
assert.equal(snapshot.rcsStatus.io, "DONE");
assert.equal(snapshot.emcStatus.top.status, "EXEC");
assert.equal(snapshot.emcStatus.task.status, "EXEC");
assert.equal(snapshot.emcStatus.motion.status, "DONE");
assert.equal(snapshot.emcStatus.io.status, "DONE");
assert.equal(snapshot.statusSource, "StandaloneEmcStatus");
assert.equal(snapshot.schemaVersion, 1);
assert.equal(snapshot.emcStatus.source, "StandaloneEmcStatus");
assert.equal(snapshot.emcStatus.valid, true);
assert.equal(snapshot.emcStatus.top.status, snapshot.taskTopLevelStatus);
assert.equal(snapshot.emcStatus.task.status, snapshot.rcsStatus.task);
assert.equal(snapshot.emcStatus.task.state, snapshot.task.state);
assert.equal(snapshot.emcStatus.task.mode, snapshot.task.mode);
assert.equal(snapshot.emcStatus.task.execState, snapshot.task.execState);
assert.equal(snapshot.emcStatus.task.currentLine, snapshot.task.currentLine);
assert.equal(snapshot.emcStatus.task.readLine, snapshot.task.readLine);
assert.equal(snapshot.emcStatus.task.motionLine, snapshot.task.motionLine);
assert.equal(snapshot.emcStatus.task.callLevel, snapshot.task.callLevel);
assert.equal(snapshot.emcStatus.task.pendingCommandDepth, snapshot.task.pendingCommandDepth);
assert.equal(snapshot.emcStatus.task.pendingExecuteMotionDepth, snapshot.task.pendingExecuteMotionDepth);
assert.equal(snapshot.emcStatus.task.state, "ON");
assert.equal(snapshot.emcStatus.task.mode, "AUTO");
assert.equal(typeof snapshot.emcStatus.task.execState, "string");
assert.equal(typeof snapshot.emcStatus.task.currentLine, "number");
assert.equal(typeof snapshot.emcStatus.task.readLine, "number");
assert.equal(typeof snapshot.emcStatus.task.motionLine, "number");
assert.equal(typeof snapshot.emcStatus.task.callLevel, "number");
assert.equal(typeof snapshot.emcStatus.task.pendingCommandDepth, "number");
assert.equal(typeof snapshot.emcStatus.task.pendingExecuteMotionDepth, "number");
assert.equal(snapshot.emcStatus.motion.valid, true);
assert.equal(snapshot.emcStatus.motion.status, snapshot.rcsStatus.motion);
assert.equal(snapshot.emcStatus.motion.programLine, snapshot.motionStatus.motion.programLine);
assert.equal(snapshot.emcStatus.motion.motionId, snapshot.motionStatus.motion.motionId);
assert.equal(snapshot.emcStatus.motion.queueDepth, snapshot.motionStatus.queueDepth);
assert.equal(snapshot.emcStatus.motion.traj.queue, snapshot.motionStatus.motion.queueDepth);
assert.equal(snapshot.emcStatus.motion.traj.inpos, snapshot.motionStatus.motion.inPosition);
assert.equal(snapshot.emcStatus.motion.traj.id, snapshot.motionStatus.motion.id);
assert.equal(snapshot.emcStatus.motion.traj.activeQueue, snapshot.motionStatus.activeDepth);
assert.equal(snapshot.emcStatus.motion.traj.queueFull, snapshot.motionStatus.motion.queueFull);
assert.equal(snapshot.emcStatus.motion.traj.paused, snapshot.motionStatus.motion.paused);
assert.equal(snapshot.emcStatus.motion.traj.singleStepping, snapshot.motionStatus.motion.stepping);
assert.equal(snapshot.emcStatus.motion.traj.currentVel, snapshot.motionStatus.motion.currentVel);
assert.equal(snapshot.emcStatus.motion.traj.actualPosition.x, snapshot.motionStatus.axis.x);
assert.equal(snapshot.emcStatus.motion.axisByName.x, snapshot.motionStatus.axis.x);
assert.equal(typeof snapshot.emcStatus.motion.status, "string");
assert.equal(typeof snapshot.emcStatus.motion.programLine, "number");
assert.equal(typeof snapshot.emcStatus.motion.motionId, "number");
assert.equal(typeof snapshot.emcStatus.motion.queueDepth, "number");
assert.equal(snapshot.emcStatus.motion.traj.queue, snapshot.emcStatus.motion.queueDepth);
assert.equal(snapshot.emcStatus.motion.traj.inpos, snapshot.emcStatus.motion.inPosition);
assert.equal(snapshot.emcStatus.motion.traj.id, snapshot.emcStatus.motion.motionId);
assert.equal(snapshot.emcStatus.motion.traj.activeQueue, snapshot.emcStatus.motion.activeDepth);
assert.equal(snapshot.emcStatus.motion.traj.queueFull, snapshot.emcStatus.motion.queueFull);
assert.equal(snapshot.emcStatus.motion.traj.paused, snapshot.emcStatus.motion.paused);
assert.equal(snapshot.emcStatus.motion.traj.singleStepping, snapshot.emcStatus.motion.stepping);
assert.equal(snapshot.emcStatus.motion.traj.currentVel, snapshot.emcStatus.motion.currentVel);
assert.equal(snapshot.emcStatus.motion.traj.actualPosition.x, snapshot.emcStatus.motion.axisByName.x);
assert.equal(typeof snapshot.emcStatus.motion.axisByName.x, "number");
assert.equal(Array.isArray(snapshot.emcStatus.motion.axis), true);
assert.equal(Array.isArray(snapshot.emcStatus.motion.joint), true);
assert.equal(snapshot.emcStatus.motion.axis.length >= 5, true);
assert.equal(snapshot.emcStatus.motion.axis[0].posFb, snapshot.motionStatus.axis.x);
assert.equal(snapshot.emcStatus.motion.joint[0].motorPosCmd, snapshot.motionStatus.joint0.motorPosCmd);
assert.equal(snapshot.emcStatus.motion.joint[0].motorPosFb, snapshot.motionStatus.joint0.motorPosFb);
assert.equal(snapshot.emcStatus.io.status, snapshot.rcsStatus.io);
assert.equal(snapshot.emcStatus.motion.axis[0].posFb, snapshot.emcStatus.motion.axisByName.x);
assert.equal(snapshot.emcStatus.motion.joint[0].motorPosCmd, snapshot.emcStatus.motion.joint0.motorPosCmd);
assert.equal(snapshot.emcStatus.motion.joint[0].motorPosFb, snapshot.emcStatus.motion.joint0.motorPosFb);
assert.equal(typeof snapshot.emcStatus.io.status, "string");
assert.equal(snapshot.emcStatus.io.error, false);
assert.equal(snapshot.emcStatus.io.fault, false);
assert.equal(snapshot.emcStatus.io.reason, "");
@@ -178,7 +185,7 @@ assert.equal(
);
assert.equal(snapshot.emctaskSourceReuse.snapshotUpdateCount > 0, true);
assert.equal(snapshot.emctaskSourceReuse.updateInputSource, "motionSnapshot,ioSnapshot");
assert.equal(snapshot.task.taskPlanOpen, true);
assert.equal(snapshot.emcStatus.task.taskPlanOpen, true);
assert.equal(snapshot.emctaskSourceReuse.planIssueCount > 0, true);
assert.equal(snapshot.emctaskSourceReuse.planSynchCount > 0, true);
assert.equal(snapshot.emctaskSourceReuse.planOpenCount > 0, true);
@@ -253,97 +260,97 @@ assert.equal(
snapshot.emccanonSourceReuse.straightMotionAnchors,
"generate_fast_move,generate_move,STRAIGHT_TRAVERSE,STRAIGHT_FEED,EMC_TRAJ_LINEAR_MOVE,interp_list",
);
assert.equal(snapshot.task.state, "ON");
assert.equal(snapshot.task.mode, "AUTO");
assert.equal(snapshot.task.openedLineCount, 4);
assert.equal(snapshot.task.programOpen, true);
assert.equal(snapshot.task.motionPlanLoaded, false);
assert.equal(snapshot.task.planId, 0);
assert.equal(snapshot.task.allHomed, true);
assert.equal(Array.isArray(snapshot.task.homed), true);
assert.equal(snapshot.task.homed.every(Boolean), true);
assert.equal(snapshot.task.nextProgramLine >= 1, true);
assert.equal(snapshot.task.interpState, "READING");
assert.equal(snapshot.task.interpResumeState, "READING");
assert.equal(snapshot.emcStatus.task.state, "ON");
assert.equal(snapshot.emcStatus.task.mode, "AUTO");
assert.equal(snapshot.emcStatus.task.openedLineCount, 4);
assert.equal(snapshot.emcStatus.task.programOpen, true);
assert.equal(snapshot.emcStatus.task.motionPlanLoaded, false);
assert.equal(snapshot.emcStatus.task.planId, 0);
assert.equal(snapshot.emcStatus.task.allHomed, true);
assert.equal(Array.isArray(snapshot.emcStatus.task.homed), true);
assert.equal(snapshot.emcStatus.task.homed.every(Boolean), true);
assert.equal(snapshot.emcStatus.task.nextProgramLine >= 1, true);
assert.equal(snapshot.emcStatus.task.interpState, "READING");
assert.equal(snapshot.emcStatus.task.interpResumeState, "READING");
assert.equal(snapshot.emctaskSourceReuse.planReadCount > 0, true);
assert.equal(snapshot.emctaskSourceReuse.planExecuteCount > 0, true);
assert.equal(snapshot.emctaskSourceReuse.planCommandCount > 0, true);
assert.equal(snapshot.motionStatus.motion.programLine >= 1, true);
assert.equal(snapshot.motionStatus.motion.enabled, true);
assert.equal(snapshot.motionStatus.motion.nextMotionId >= 1, true);
assert.equal(snapshot.motionStatus.motion.queueDepth >= 0, true);
assert.equal(snapshot.motionStatus.motion.id > 0, true);
assert.equal(snapshot.motionStatus.motion.currentVel > 0, true);
assert.equal(snapshot.halSnapshot.pins["motion.program-line"].value, snapshot.motionStatus.motion.programLine);
assert.equal(snapshot.halSnapshot.pins["joint.0.motor-pos-cmd"].value, snapshot.motionStatus.axis.x);
assert.equal(snapshot.emcStatus.motion.programLine >= 1, true);
assert.equal(snapshot.emcStatus.motion.enabled, true);
assert.equal(snapshot.emcStatus.motion.nextMotionId >= 1, true);
assert.equal(snapshot.emcStatus.motion.queueDepth >= 0, true);
assert.equal(snapshot.emcStatus.motion.motionId > 0, true);
assert.equal(snapshot.emcStatus.motion.currentVel > 0, true);
assert.equal(snapshot.halSnapshot.pins["motion.program-line"].value, snapshot.emcStatus.motion.programLine);
assert.equal(snapshot.halSnapshot.pins["joint.0.motor-pos-cmd"].value, snapshot.emcStatus.motion.axisByName.x);
send({ type: "EMC_TASK_PLAN_PAUSE" });
snapshot = status();
assert.equal(snapshot.task.pendingCommandDepth, 1);
assert.equal(snapshot.task.interpState, "READING");
assert.equal(snapshot.motionStatus.motion.paused, false);
assert.equal(snapshot.emcStatus.task.pendingCommandDepth, 1);
assert.equal(snapshot.emcStatus.task.interpState, "READING");
assert.equal(snapshot.emcStatus.motion.paused, false);
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.task.interpState, "PAUSED");
assert.equal(["READING", "IDLE"].includes(snapshot.task.interpResumeState), true);
assert.equal(snapshot.task.taskPaused, true);
assert.equal(snapshot.task.singleStepping, false);
assert.equal(snapshot.motionStatus.motion.paused, true);
assert.equal(snapshot.motionStatus.motion.currentVel, 0);
const pausedTaskLine = snapshot.task.nextProgramLine;
const pausedMotionLine = snapshot.motionStatus.motion.programLine;
const pausedMotionId = snapshot.motionStatus.motion.motionId;
const pausedQueueDepth = snapshot.motionStatus.commandQueueDepth;
const pausedAxis = { ...snapshot.motionStatus.axis };
assert.equal(snapshot.emcStatus.task.interpState, "PAUSED");
assert.equal(["READING", "IDLE"].includes(snapshot.emcStatus.task.interpResumeState), true);
assert.equal(snapshot.emcStatus.task.taskPaused, true);
assert.equal(snapshot.emcStatus.task.singleStepping, false);
assert.equal(snapshot.emcStatus.motion.paused, true);
assert.equal(snapshot.emcStatus.motion.currentVel, 0);
const pausedTaskLine = snapshot.emcStatus.task.nextProgramLine;
const pausedMotionLine = snapshot.emcStatus.motion.programLine;
const pausedMotionId = snapshot.emcStatus.motion.motionId;
const pausedQueueDepth = snapshot.emcStatus.motion.commandQueueDepth;
const pausedAxis = { ...snapshot.emcStatus.motion.axisByName };
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 20), 0);
snapshot = status();
assert.equal(snapshot.task.interpState, "PAUSED");
assert.equal(["READING", "IDLE"].includes(snapshot.task.interpResumeState), true);
assert.equal(snapshot.task.nextProgramLine, pausedTaskLine);
assert.equal(snapshot.motionStatus.motion.paused, true);
assert.equal(snapshot.motionStatus.motion.programLine, pausedMotionLine);
assert.equal(snapshot.motionStatus.motion.motionId, pausedMotionId);
assert.equal(snapshot.motionStatus.commandQueueDepth, pausedQueueDepth);
assert.deepEqual(snapshot.motionStatus.axis, pausedAxis);
assert.equal(snapshot.emcStatus.task.interpState, "PAUSED");
assert.equal(["READING", "IDLE"].includes(snapshot.emcStatus.task.interpResumeState), true);
assert.equal(snapshot.emcStatus.task.nextProgramLine, pausedTaskLine);
assert.equal(snapshot.emcStatus.motion.paused, true);
assert.equal(snapshot.emcStatus.motion.programLine, pausedMotionLine);
assert.equal(snapshot.emcStatus.motion.motionId, pausedMotionId);
assert.equal(snapshot.emcStatus.motion.commandQueueDepth, pausedQueueDepth);
assert.deepEqual(snapshot.emcStatus.motion.axisByName, pausedAxis);
send({ type: "EMC_TASK_PLAN_RESUME" });
snapshot = status();
assert.equal(snapshot.task.pendingCommandDepth, 1);
assert.equal(snapshot.motionStatus.motion.paused, true);
assert.equal(snapshot.emcStatus.task.pendingCommandDepth, 1);
assert.equal(snapshot.emcStatus.motion.paused, true);
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(["READING", "IDLE"].includes(snapshot.task.interpState), true);
assert.equal(["READING", "IDLE"].includes(snapshot.task.interpResumeState), true);
assert.equal(snapshot.task.taskPaused, false);
assert.equal(snapshot.motionStatus.motion.paused, false);
assert.equal(snapshot.motionStatus.motion.programLine >= pausedMotionLine, true);
assert.equal(["READING", "IDLE"].includes(snapshot.emcStatus.task.interpState), true);
assert.equal(["READING", "IDLE"].includes(snapshot.emcStatus.task.interpResumeState), true);
assert.equal(snapshot.emcStatus.task.taskPaused, false);
assert.equal(snapshot.emcStatus.motion.paused, false);
assert.equal(snapshot.emcStatus.motion.programLine >= pausedMotionLine, true);
const motionIdBeforeStep = snapshot.motionStatus.motion.motionId;
const motionIdBeforeStep = snapshot.emcStatus.motion.motionId;
send({ type: "EMC_TASK_PLAN_PAUSE" });
send({ type: "EMC_TASK_PLAN_STEP" });
snapshot = status();
assert.equal(snapshot.task.pendingCommandDepth, 2);
assert.equal(snapshot.motionStatus.motion.motionId, motionIdBeforeStep);
assert.equal(snapshot.emcStatus.task.pendingCommandDepth, 2);
assert.equal(snapshot.emcStatus.motion.motionId, motionIdBeforeStep);
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.task.interpState, "PAUSED");
assert.equal(["READING", "IDLE"].includes(snapshot.task.interpResumeState), true);
assert.equal(snapshot.task.taskPaused, true);
assert.equal(snapshot.task.singleStepping, false);
assert.equal(snapshot.motionStatus.motion.paused, true);
assert.equal(typeof snapshot.motionStatus.motion.stepping, "boolean");
assert.equal(snapshot.motionStatus.motion.programLine >= pausedMotionLine, true);
assert.equal(snapshot.motionStatus.motion.motionId > pausedMotionId, true);
assert.equal(snapshot.emcStatus.task.interpState, "PAUSED");
assert.equal(["READING", "IDLE"].includes(snapshot.emcStatus.task.interpResumeState), true);
assert.equal(snapshot.emcStatus.task.taskPaused, true);
assert.equal(snapshot.emcStatus.task.singleStepping, false);
assert.equal(snapshot.emcStatus.motion.paused, true);
assert.equal(typeof snapshot.emcStatus.motion.stepping, "boolean");
assert.equal(snapshot.emcStatus.motion.programLine >= pausedMotionLine, true);
assert.equal(snapshot.emcStatus.motion.motionId > pausedMotionId, true);
send({ type: "EMC_TASK_PLAN_RESUME" });
snapshot = status();
assert.equal(snapshot.task.pendingCommandDepth, 1);
assert.equal(snapshot.motionStatus.motion.paused, true);
assert.equal(snapshot.emcStatus.task.pendingCommandDepth, 1);
assert.equal(snapshot.emcStatus.motion.paused, true);
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.motionStatus.motion.paused, false);
assert.equal(snapshot.motionStatus.motion.programLine >= pausedMotionLine, true);
assert.equal(snapshot.emcStatus.motion.paused, false);
assert.equal(snapshot.emcStatus.motion.programLine >= pausedMotionLine, true);
sendMotion({ type: "EMC_TRAJ_LINEAR_MOVE", line: 10, x: 10, y: 0, z: 0, velocity: 1 });
sendMotion({ type: "EMC_TRAJ_LINEAR_MOVE", line: 11, x: 11, y: 0, z: 0, velocity: 1 });
@@ -375,14 +382,14 @@ assert.equal(motionSnapshot.commandQueueDepth, 0);
send({ type: "EMC_TASK_PLAN_EXECUTE", mdi: "M428" });
snapshot = status();
assert.equal(snapshot.task.pendingCommandDepth, 1);
assert.equal(snapshot.task.pendingExecuteMotionDepth, 0);
assert.equal(snapshot.emcStatus.task.pendingCommandDepth, 1);
assert.equal(snapshot.emcStatus.task.pendingExecuteMotionDepth, 0);
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.task.pendingCommandDepth, 0);
assert.equal(snapshot.task.pendingExecuteMotionDepth, 0);
assert.equal(snapshot.motionStatus.motion.switchkinsType, 1);
assert.equal(snapshot.task.execState, "DONE");
assert.equal(snapshot.emcStatus.task.pendingCommandDepth, 0);
assert.equal(snapshot.emcStatus.task.pendingExecuteMotionDepth, 0);
assert.equal(snapshot.emcStatus.motion.switchkinsType, 1);
assert.equal(snapshot.emcStatus.task.execState, "DONE");
assert.equal(snapshot.halSnapshot.pins["motion.switchkins-type"].value, 1);
assert.equal(snapshot.taskintfSourceReuse.jogHomeSwitchkinsStructuredIssueCount >= 2, true);
assert.equal(snapshot.emccanonSourceReuse.motionOutputCount > 0, true);
@@ -396,11 +403,11 @@ assert.equal(snapshot.emccanonSourceReuse.lastMotionOutputNow, 1);
send({ type: "EMC_TASK_PLAN_EXECUTE", mdi: "M429" });
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.motionStatus.motion.switchkinsType, 0);
assert.equal(snapshot.emcStatus.motion.switchkinsType, 0);
send({ type: "EMC_TASK_PLAN_EXECUTE", mdi: "M430" });
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.motionStatus.motion.switchkinsType, 2);
assert.equal(snapshot.emcStatus.motion.switchkinsType, 2);
assert.equal(snapshot.emccanonSourceReuse.lastMotionOutputCommand, "SET_AUX_OUTPUT_VALUE");
assert.equal(snapshot.emccanonSourceReuse.lastMotionOutputValue, 2);
send({ type: "EMC_TASK_PLAN_EXECUTE", mdi: "M62 P1" });
@@ -470,25 +477,25 @@ assert.equal(
snapshot.emccanonSourceReuse.spindleToolAnchors,
"SET_SPINDLE_SPEED,START_SPINDLE_CLOCKWISE,START_SPINDLE_COUNTERCLOCKWISE,STOP_SPINDLE_TURNING,EMC_SPINDLE_SPEED,EMC_SPINDLE_ON,EMC_SPINDLE_OFF,SELECT_TOOL,CHANGE_TOOL,CHANGE_TOOL_NUMBER,RELOAD_TOOLDATA,EMC_TOOL_PREPARE,EMC_TOOL_LOAD,EMC_TOOL_SET_NUMBER,EMC_TOOL_LOAD_TOOL_TABLE,interp_list",
);
const xBeforeJog = snapshot.motionStatus.axis.x;
const xBeforeJog = snapshot.emcStatus.motion.axisByName.x;
send({ type: "EMC_JOG_INCR", axis: "X", distance: 0.25, velocity: 60 });
snapshot = status();
assert.equal(snapshot.task.pendingCommandDepth, 1);
assert.equal(snapshot.motionStatus.axis.x, xBeforeJog);
assert.equal(snapshot.emcStatus.task.pendingCommandDepth, 1);
assert.equal(snapshot.emcStatus.motion.axisByName.x, xBeforeJog);
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.task.pendingCommandDepth, 0);
assert.equal(snapshot.task.pendingExecuteMotionDepth, 0);
assert.equal(snapshot.motionStatus.axis.x, xBeforeJog + 0.25);
assert.equal(snapshot.motionStatus.motion.teleopMode, 1);
assert.equal(snapshot.task.execState, "DONE");
assert.equal(snapshot.emcStatus.task.pendingCommandDepth, 0);
assert.equal(snapshot.emcStatus.task.pendingExecuteMotionDepth, 0);
assert.equal(snapshot.emcStatus.motion.axisByName.x, xBeforeJog + 0.25);
assert.equal(snapshot.emcStatus.motion.teleopMode, 1);
assert.equal(snapshot.emcStatus.task.execState, "DONE");
send({ type: "EMC_TASK_ABORT" });
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.task.execState, "DONE");
assert.equal(snapshot.motionStatus.motion.aborted, true);
assert.equal(snapshot.emcStatus.task.execState, "DONE");
assert.equal(snapshot.emcStatus.motion.aborted, true);
assert.equal(snapshot.taskintfMotionBridge.abortCount > 0, true);
const eventLog = events();
@@ -518,10 +525,10 @@ motionSnapshot = motionStatus();
assert.equal(motionSnapshot.motion.programLine, 21);
assert.equal(motionSnapshot.motion.motionId, 21);
snapshot = status();
assert.equal(snapshot.motionStatus.motion.programLine, idleTaskSnapshot.motionStatus.motion.programLine);
assert.equal(snapshot.emcStatus.motion.programLine, idleTaskSnapshot.emcStatus.motion.programLine);
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.motionStatus.motion.programLine, 21);
assert.equal(snapshot.emcStatus.motion.programLine, 21);
assert.equal(runtime._lcmot_write_aout(0, 2, 2, 1), 0);
assert.equal(runtime._lcmot_step_servo(1000000, 1), 0);
motionSnapshot = motionStatus();
@@ -550,33 +557,32 @@ motionSnapshot = motionStatus();
assert.equal(motionSnapshot.commandQueueDepth, 64);
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.motionStatus.motion.queueFull, true);
assert.equal(snapshot.emcStatus.motion.queueFull, true);
send({ type: "EMC_JOG_INCR", axis: "X", distance: 0.25, velocity: 60 });
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.task.execState, "WAITING_FOR_MOTION_QUEUE");
assert.equal(snapshot.task.pendingExecuteMotionDepth, 1);
assert.equal(snapshot.motionStatus.motion.queueFull, true);
assert.equal(snapshot.emcStatus.task.execState, "WAITING_FOR_MOTION_QUEUE");
assert.equal(snapshot.emcStatus.task.pendingExecuteMotionDepth, 1);
assert.equal(snapshot.emcStatus.motion.queueFull, true);
sendMotion({ type: "EMCMOT_RESUME" });
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.task.execState, "WAITING_FOR_MOTION");
assert.equal(snapshot.task.pendingExecuteMotionDepth, 1);
assert.equal(snapshot.motionStatus.motion.queueFull, false);
assert.equal(snapshot.emcStatus.task.execState, "WAITING_FOR_MOTION");
assert.equal(snapshot.emcStatus.task.pendingExecuteMotionDepth, 1);
assert.equal(snapshot.emcStatus.motion.queueFull, false);
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 50), 0);
snapshot = status();
assert.equal(snapshot.task.execState, "DONE");
assert.equal(snapshot.task.status, "DONE");
assert.equal(snapshot.taskTopLevelStatus, "DONE");
assert.equal(snapshot.rcsStatus.top, "DONE");
assert.equal(snapshot.rcsStatus.task, "DONE");
assert.equal(snapshot.rcsStatus.motion, "DONE");
assert.equal(snapshot.rcsStatus.io, "DONE");
assert.equal(snapshot.task.pendingExecuteMotionDepth, 0);
assert.equal(snapshot.motionStatus.commandQueueDepth, 0);
assert.equal(snapshot.emcStatus.task.execState, "DONE");
assert.equal(snapshot.emcStatus.task.status, "DONE");
assert.equal(snapshot.emcStatus.top.status, "DONE");
assert.equal(snapshot.emcStatus.task.status, "DONE");
assert.equal(snapshot.emcStatus.motion.status, "DONE");
assert.equal(snapshot.emcStatus.io.status, "DONE");
assert.equal(snapshot.emcStatus.task.pendingExecuteMotionDepth, 0);
assert.equal(snapshot.emcStatus.motion.commandQueueDepth, 0);
const queueEvents = events();
assert.equal(queueEvents.includes("task_waiting_for_motion_queue"), true);
assert.equal(queueEvents.includes("task_motion_queue_available"), true);
@@ -590,14 +596,13 @@ assert.equal(
sendMotion({ type: "EMCMOT_ABORT" });
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.task.execState, "ERROR");
assert.equal(snapshot.task.errorText, "MOTION_ABORTED");
assert.equal(snapshot.taskTopLevelStatus, "ERROR");
assert.equal(snapshot.rcsStatus.top, "ERROR");
assert.equal(snapshot.rcsStatus.task, "ERROR");
assert.equal(snapshot.rcsStatus.motion, "ERROR");
assert.equal(snapshot.rcsStatus.io, "DONE");
assert.equal(snapshot.motionStatus.motion.aborted, true);
assert.equal(snapshot.emcStatus.task.execState, "ERROR");
assert.equal(snapshot.emcStatus.task.errorText, "MOTION_ABORTED");
assert.equal(snapshot.emcStatus.top.status, "ERROR");
assert.equal(snapshot.emcStatus.task.status, "ERROR");
assert.equal(snapshot.emcStatus.motion.status, "ERROR");
assert.equal(snapshot.emcStatus.io.status, "DONE");
assert.equal(snapshot.emcStatus.motion.aborted, true);
assert.equal(snapshot.taskintfMotionBridge.lastErrorText, "MOTION_ABORTED");
const motionErrorEvents = events();
assert.equal(motionErrorEvents.includes("task_motion_error:MOTION_ABORTED"), true);
@@ -610,15 +615,13 @@ assert.equal(
sendMotion({ type: "EMCMOT_INJECT_ERROR" });
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.task.execState, "ERROR");
assert.equal(snapshot.task.errorText, "MOTION_ERROR");
assert.equal(snapshot.taskTopLevelStatus, "ERROR");
assert.equal(snapshot.rcsStatus.top, "ERROR");
assert.equal(snapshot.rcsStatus.task, "ERROR");
assert.equal(snapshot.rcsStatus.motion, "ERROR");
assert.equal(snapshot.rcsStatus.io, "DONE");
assert.equal(snapshot.motionStatus.motion.status, 2);
assert.equal(snapshot.motionStatus.motion.motionError, true);
assert.equal(snapshot.emcStatus.task.execState, "ERROR");
assert.equal(snapshot.emcStatus.task.errorText, "MOTION_ERROR");
assert.equal(snapshot.emcStatus.top.status, "ERROR");
assert.equal(snapshot.emcStatus.task.status, "ERROR");
assert.equal(snapshot.emcStatus.motion.status, "ERROR");
assert.equal(snapshot.emcStatus.io.status, "DONE");
assert.equal(snapshot.emcStatus.motion.motionError, true);
assert.equal(snapshot.taskintfMotionBridge.lastErrorText, "MOTION_ERROR");
const injectedMotionErrorEvents = events();
assert.equal(injectedMotionErrorEvents.includes("task_motion_error:MOTION_ERROR"), true);
@@ -631,15 +634,13 @@ assert.equal(
sendMotion({ type: "EMCMOT_INJECT_SOFT_LIMIT", axis: "X" });
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.task.execState, "ERROR");
assert.equal(snapshot.task.errorText, "MOTION_SOFT_LIMIT");
assert.equal(snapshot.taskTopLevelStatus, "ERROR");
assert.equal(snapshot.rcsStatus.top, "ERROR");
assert.equal(snapshot.rcsStatus.task, "ERROR");
assert.equal(snapshot.rcsStatus.motion, "ERROR");
assert.equal(snapshot.rcsStatus.io, "DONE");
assert.equal(snapshot.motionStatus.motion.status, 2);
assert.equal(snapshot.motionStatus.motion.onSoftLimit, true);
assert.equal(snapshot.emcStatus.task.execState, "ERROR");
assert.equal(snapshot.emcStatus.task.errorText, "MOTION_SOFT_LIMIT");
assert.equal(snapshot.emcStatus.top.status, "ERROR");
assert.equal(snapshot.emcStatus.task.status, "ERROR");
assert.equal(snapshot.emcStatus.motion.status, "ERROR");
assert.equal(snapshot.emcStatus.io.status, "DONE");
assert.equal(snapshot.emcStatus.motion.onSoftLimit, true);
assert.equal(snapshot.taskintfMotionBridge.lastErrorText, "MOTION_SOFT_LIMIT");
const softLimitEvents = events();
assert.equal(softLimitEvents.includes("task_motion_error:MOTION_SOFT_LIMIT"), true);
@@ -651,16 +652,15 @@ assert.equal(
);
send({ type: "EMC_IO_INJECT_ERROR" });
snapshot = status();
assert.equal(snapshot.task.pendingCommandDepth, 1);
assert.equal(snapshot.emcStatus.task.pendingCommandDepth, 1);
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 1), 0);
snapshot = status();
assert.equal(snapshot.task.execState, "ERROR");
assert.equal(snapshot.task.errorText, "IO_ERROR");
assert.equal(snapshot.taskTopLevelStatus, "ERROR");
assert.equal(snapshot.rcsStatus.top, "ERROR");
assert.equal(snapshot.rcsStatus.task, "ERROR");
assert.equal(snapshot.rcsStatus.motion, "DONE");
assert.equal(snapshot.rcsStatus.io, "ERROR");
assert.equal(snapshot.emcStatus.task.execState, "ERROR");
assert.equal(snapshot.emcStatus.task.errorText, "IO_ERROR");
assert.equal(snapshot.emcStatus.top.status, "ERROR");
assert.equal(snapshot.emcStatus.task.status, "ERROR");
assert.equal(snapshot.emcStatus.motion.status, "DONE");
assert.equal(snapshot.emcStatus.io.status, "ERROR");
const ioErrorEvents = events();
assert.equal(ioErrorEvents.includes("task_io_error_injected"), true);
assert.equal(ioErrorEvents.includes("task_io_error:IO_ERROR"), true);
@@ -687,9 +687,9 @@ send({ type: "EMC_TASK_SET_MODE", mode: "AUTO" });
send({ type: "EMC_TASK_PLAN_RUN", line: 0 });
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, 6), 0);
snapshot = status();
assert.equal(snapshot.task.motionPlanLoaded, false);
assert.equal(snapshot.task.execState, "DONE");
assert.equal(snapshot.task.nextProgramLine >= 1, true);
assert.equal(snapshot.emcStatus.task.motionPlanLoaded, false);
assert.equal(snapshot.emcStatus.task.execState, "DONE");
assert.equal(snapshot.emcStatus.task.nextProgramLine >= 1, true);
assert.equal(snapshot.emctaskSourceReuse.planReadCount > 0, true);
assert.equal(snapshot.emctaskSourceReuse.planExecuteCount > 0, true);
assert.equal(snapshot.emctaskSourceReuse.planLineCount > 0, true);

View File

@@ -43,7 +43,9 @@ function readStatus() {
const ptr = runtime._malloc(bytes);
try {
assert.equal(runtime._lctask_read_status_json(ptr, bytes), 0);
return JSON.parse(runtime.UTF8ToString(ptr));
const status = JSON.parse(runtime.UTF8ToString(ptr));
assertNoLegacyStatusFields(status);
return status;
} finally {
runtime._free(ptr);
}
@@ -103,29 +105,38 @@ function runTaskCycles(cycles = 1) {
assert.equal(runtime._lctask_run_cycles(10000000, 1000000, cycles), 0);
}
function assertNoLegacyStatusFields(status) {
assert.equal("taskTopLevelStatus" in status, false);
assert.equal("rcsStatus" in status, false);
assert.equal("task" in status, false);
assert.equal("servoCycle" in status, false);
assert.equal("motionStatus" in status, false);
}
function assertStandaloneEmcStatus(status) {
assertNoLegacyStatusFields(status);
assert.equal(status.statusSource, "StandaloneEmcStatus");
assert.equal(status.schemaVersion, 1);
assert.equal(status.emcStatus.source, "StandaloneEmcStatus");
assert.equal(status.emcStatus.valid, true);
assert.equal(status.emcStatus.top.status, status.taskTopLevelStatus);
assert.equal(status.emcStatus.task.status, status.rcsStatus.task);
assert.equal(status.emcStatus.task.state, status.task.state);
assert.equal(status.emcStatus.task.mode, status.task.mode);
assert.equal(status.emcStatus.task.currentLine, status.task.currentLine);
assert.equal(status.emcStatus.task.readLine, status.task.readLine);
assert.equal(status.emcStatus.task.motionLine, status.task.motionLine);
assert.equal(status.emcStatus.task.pendingCommandDepth, status.task.pendingCommandDepth);
assert.equal(status.emcStatus.motion.status, status.rcsStatus.motion);
assert.equal(typeof status.emcStatus.top.status, "string");
assert.equal(typeof status.emcStatus.task.status, "string");
assert.equal(typeof status.emcStatus.task.state, "string");
assert.equal(typeof status.emcStatus.task.mode, "string");
assert.equal(typeof status.emcStatus.task.currentLine, "number");
assert.equal(typeof status.emcStatus.task.readLine, "number");
assert.equal(typeof status.emcStatus.task.motionLine, "number");
assert.equal(typeof status.emcStatus.task.pendingCommandDepth, "number");
assert.equal(typeof status.emcStatus.motion.status, "string");
assert.equal(status.emcStatus.motion.valid, true);
assert.equal(status.emcStatus.motion.programLine, status.motionStatus.motion.programLine);
assert.equal(status.emcStatus.motion.queueDepth, status.motionStatus.queueDepth);
assert.equal(status.emcStatus.motion.traj.queue, status.motionStatus.motion.queueDepth);
assert.equal(status.emcStatus.motion.traj.inpos, status.motionStatus.motion.inPosition);
assert.equal(status.emcStatus.motion.traj.id, status.motionStatus.motion.id);
assert.equal(typeof status.emcStatus.motion.programLine, "number");
assert.equal(typeof status.emcStatus.motion.queueDepth, "number");
assert.equal(status.emcStatus.motion.traj.queue, status.emcStatus.motion.queueDepth);
assert.equal(status.emcStatus.motion.traj.inpos, status.emcStatus.motion.inPosition);
assert.equal(status.emcStatus.motion.traj.id, status.emcStatus.motion.motionId);
assert.equal(Array.isArray(status.emcStatus.motion.axis), true);
assert.equal(Array.isArray(status.emcStatus.motion.joint), true);
assert.equal(status.emcStatus.io.status, status.rcsStatus.io);
assert.equal(typeof status.emcStatus.io.status, "string");
assert.equal(typeof status.emcStatus.io.aux.estop, "boolean");
assert.equal(status.emcStatus.io.tool.supported, false);
assert.equal(status.emcStatus.io.coolant.supported, false);
@@ -137,24 +148,24 @@ function assertRejected(name, setup, expectedError) {
const rc = commandRc({ type: "EMC_TASK_PLAN_RUN", line: 0 });
assert.equal(rc, 0, name);
runTaskCycles();
assert.equal(readStatus().task.errorText, expectedError, name);
assert.equal(readStatus().emcStatus.task.errorText, expectedError, name);
}
reset();
let queuedStatus = readStatus();
assertStandaloneEmcStatus(queuedStatus);
assert.equal(queuedStatus.task.state, "ESTOP_RESET");
assert.equal(queuedStatus.emcStatus.task.state, "ESTOP_RESET");
assert.equal(commandRc({ type: "EMC_TASK_SET_STATE", state: "ON" }), 0);
queuedStatus = readStatus();
assert.equal(queuedStatus.task.state, "ESTOP_RESET");
assert.equal(queuedStatus.task.pendingCommandDepth, 1);
assert.equal(queuedStatus.emcStatus.task.state, "ESTOP_RESET");
assert.equal(queuedStatus.emcStatus.task.pendingCommandDepth, 1);
assert.equal(commandRc({ type: "UNSUPPORTED_HOST_COMMAND" }), -1);
queuedStatus = readStatus();
assert.equal(queuedStatus.task.pendingCommandDepth, 1);
assert.equal(queuedStatus.emcStatus.task.pendingCommandDepth, 1);
runTaskCycles();
queuedStatus = readStatus();
assert.equal(queuedStatus.task.state, "ON");
assert.equal(queuedStatus.task.pendingCommandDepth, 0);
assert.equal(queuedStatus.emcStatus.task.state, "ON");
assert.equal(queuedStatus.emcStatus.task.pendingCommandDepth, 0);
assertRejected("ESTOP blocks run", () => {
stageAndOpenProgram();
@@ -197,7 +208,7 @@ runTaskCycles();
assert.equal(commandRc({ type: "EMC_TASK_SET_MODE", mode: "AUTO" }), 0);
assert.equal(commandRc({ type: "EMC_TASK_PLAN_RUN", line: 0 }), 0);
runTaskCycles();
assert.equal(readStatus().task.errorText, "PROGRAM_OPEN_REQUIRED");
assert.equal(readStatus().emcStatus.task.errorText, "PROGRAM_OPEN_REQUIRED");
reset();
stageAndOpenProgram();
@@ -208,8 +219,8 @@ assert.equal(commandRc({ type: "EMC_TASK_SET_MODE", mode: "AUTO" }), 0);
assert.equal(commandRc({ type: "EMC_TASK_PLAN_RUN", line: 0 }), 0);
runTaskCycles(4);
const noJsonPlanStatus = readStatus();
assert.equal(noJsonPlanStatus.task.errorText, "");
assert.equal(noJsonPlanStatus.task.motionPlanLoaded, false);
assert.equal(noJsonPlanStatus.emcStatus.task.errorText, "");
assert.equal(noJsonPlanStatus.emcStatus.task.motionPlanLoaded, false);
assert.equal(noJsonPlanStatus.emctaskSourceReuse.planReadCount > 0, true);
assert.equal(noJsonPlanStatus.emctaskSourceReuse.planExecuteCount > 0, true);
assert.equal(noJsonPlanStatus.emctaskSourceReuse.interpListAppendCount > 0, true);
@@ -223,17 +234,17 @@ assert.equal(commandRc({ type: "EMC_TASK_SET_MODE", mode: "AUTO" }), 0);
assert.equal(commandRc({ type: "EMC_TASK_PLAN_RUN", line: 0 }), 0);
runTaskCycles();
let status = readStatus();
assert.equal(status.task.mode, "AUTO");
assert.equal(["READING", "IDLE"].includes(status.task.interpState), true);
assert.equal(status.task.taskPaused, false);
assert.equal(status.task.singleStepping, false);
assert.equal(status.task.programOpen, true);
assert.equal(status.task.motionPlanLoaded, false);
assert.equal(status.task.allHomed, true);
assert.equal(Array.isArray(status.task.homed), true);
assert.equal(status.task.homed.every(Boolean), true);
assert.equal(status.task.planId, 0);
assert.equal(status.task.errorText, "");
assert.equal(status.emcStatus.task.mode, "AUTO");
assert.equal(["READING", "IDLE"].includes(status.emcStatus.task.interpState), true);
assert.equal(status.emcStatus.task.taskPaused, false);
assert.equal(status.emcStatus.task.singleStepping, false);
assert.equal(status.emcStatus.task.programOpen, true);
assert.equal(status.emcStatus.task.motionPlanLoaded, false);
assert.equal(status.emcStatus.task.allHomed, true);
assert.equal(Array.isArray(status.emcStatus.task.homed), true);
assert.equal(status.emcStatus.task.homed.every(Boolean), true);
assert.equal(status.emcStatus.task.planId, 0);
assert.equal(status.emcStatus.task.errorText, "");
reset();
stageAndOpenProgram({ loadPlan: true });
@@ -244,8 +255,8 @@ assert.equal(commandRc({ type: "EMC_TASK_SET_MODE", mode: "AUTO" }), 0);
assert.equal(commandRc({ type: "EMC_TASK_PLAN_RUN", line: 0 }), 0);
runTaskCycles();
status = readStatus();
assert.equal(status.task.motionPlanLoaded, true);
assert.equal(status.task.planId > 0, true);
assert.equal(status.emcStatus.task.motionPlanLoaded, true);
assert.equal(status.emcStatus.task.planId > 0, true);
reset({ noForceHoming: true });
stageAndOpenProgram();
@@ -255,10 +266,10 @@ assert.equal(commandRc({ type: "EMC_TASK_PLAN_RUN", line: 0 }), 0);
runTaskCycles();
status = readStatus();
assertStandaloneEmcStatus(status);
assert.equal(status.task.noForceHoming, true);
assert.equal(status.task.allHomed, false);
assert.equal(status.task.homed.every(Boolean), false);
assert.equal(["READING", "IDLE"].includes(status.task.interpState), true);
assert.equal(status.emcStatus.task.noForceHoming, true);
assert.equal(status.emcStatus.task.allHomed, false);
assert.equal(status.emcStatus.task.homed.every(Boolean), false);
assert.equal(["READING", "IDLE"].includes(status.emcStatus.task.interpState), true);
console.log("task_state_matrix=ok");
console.log("run_gate_estop_rejected=ok");

View File

@@ -38,12 +38,22 @@ function readStatus() {
const ptr = runtime._malloc(bytes);
try {
assert.equal(runtime._lctask_read_status_json(ptr, bytes), 0);
return JSON.parse(runtime.UTF8ToString(ptr));
const status = JSON.parse(runtime.UTF8ToString(ptr));
assertNoLegacyStatusFields(status);
return status;
} finally {
runtime._free(ptr);
}
}
function assertNoLegacyStatusFields(status) {
assert.equal("taskTopLevelStatus" in status, false);
assert.equal("rcsStatus" in status, false);
assert.equal("task" in status, false);
assert.equal("servoCycle" in status, false);
assert.equal("motionStatus" in status, false);
}
assert.equal(callJson("lctask_init_session", {
iniPath: "status-json-contract.ini",
iniText: "[TRAJ]\nCOORDINATES = X Y Z A B C\n",
@@ -71,35 +81,35 @@ const status = readStatus();
assert.equal(status.statusSource, "StandaloneEmcStatus");
assert.equal(status.schemaVersion, 1);
assert.equal(status.emcStatus.source, "StandaloneEmcStatus");
assert.equal(status.emcStatus.top.status, status.taskTopLevelStatus);
assert.equal(typeof status.emcStatus.top.status, "string");
assert.equal(status.emcStatus.task.currentLine, status.task.currentLine);
assert.equal(status.emcStatus.task.readLine, status.task.readLine);
assert.equal(status.emcStatus.task.motionLine, status.task.motionLine);
assert.equal(status.emcStatus.task.callLevel, status.task.callLevel);
assert.equal(typeof status.emcStatus.task.currentLine, "number");
assert.equal(typeof status.emcStatus.task.readLine, "number");
assert.equal(typeof status.emcStatus.task.motionLine, "number");
assert.equal(typeof status.emcStatus.task.callLevel, "number");
assert.equal(status.emcStatus.motion.traj.queue, status.motionStatus.motion.queueDepth);
assert.equal(status.emcStatus.motion.traj.inpos, status.motionStatus.motion.inPosition);
assert.equal(status.emcStatus.motion.traj.id, status.motionStatus.motion.id);
assert.equal(status.emcStatus.motion.traj.activeQueue, status.motionStatus.activeDepth);
assert.equal(status.emcStatus.motion.traj.queueFull, status.motionStatus.motion.queueFull);
assert.equal(status.emcStatus.motion.traj.paused, status.motionStatus.motion.paused);
assert.equal(status.emcStatus.motion.traj.singleStepping, status.motionStatus.motion.stepping);
assert.equal(status.emcStatus.motion.traj.actualPosition.x, status.motionStatus.axis.x);
assert.equal(status.emcStatus.motion.traj.queue, status.emcStatus.motion.queueDepth);
assert.equal(status.emcStatus.motion.traj.inpos, status.emcStatus.motion.inPosition);
assert.equal(status.emcStatus.motion.traj.id, status.emcStatus.motion.motionId);
assert.equal(status.emcStatus.motion.traj.activeQueue, status.emcStatus.motion.activeDepth);
assert.equal(status.emcStatus.motion.traj.queueFull, status.emcStatus.motion.queueFull);
assert.equal(status.emcStatus.motion.traj.paused, status.emcStatus.motion.paused);
assert.equal(status.emcStatus.motion.traj.singleStepping, status.emcStatus.motion.stepping);
assert.equal(status.emcStatus.motion.traj.actualPosition.x, status.emcStatus.motion.axisByName.x);
assert.equal(status.emcStatus.motion.traj.position.x, status.emcStatus.motion.axis[0].posCmd);
assert.equal(status.emcStatus.motion.traj.currentVel, status.motionStatus.motion.currentVel);
assert.equal(status.emcStatus.motion.traj.currentVel, status.emcStatus.motion.currentVel);
assert.equal(Array.isArray(status.emcStatus.motion.axis), true);
assert.equal(Array.isArray(status.emcStatus.motion.joint), true);
assert.equal(status.emcStatus.motion.axis.length >= 6, true);
assert.equal(status.emcStatus.motion.joint.length >= 6, true);
assert.equal(status.emcStatus.motion.axis[0].letter, "X");
assert.equal(status.emcStatus.motion.axis[0].posFb, status.motionStatus.axis.x);
assert.equal(status.emcStatus.motion.axis[1].posFb, status.motionStatus.axis.y);
assert.equal(status.emcStatus.motion.joint[0].motorPosCmd, status.motionStatus.joint0.motorPosCmd);
assert.equal(status.emcStatus.motion.joint[0].motorPosFb, status.motionStatus.joint0.motorPosFb);
assert.equal(status.emcStatus.motion.axis[0].posFb, status.emcStatus.motion.axisByName.x);
assert.equal(status.emcStatus.motion.axis[1].posFb, status.emcStatus.motion.axisByName.y);
assert.equal(status.emcStatus.motion.joint[0].motorPosCmd, status.emcStatus.motion.joint0.motorPosCmd);
assert.equal(status.emcStatus.motion.joint[0].motorPosFb, status.emcStatus.motion.joint0.motorPosFb);
assert.equal(status.emcStatus.io.status, status.rcsStatus.io);
assert.equal(typeof status.emcStatus.io.status, "string");
assert.equal(status.emcStatus.io.fault, status.emcStatus.io.error);
assert.equal(status.emcStatus.io.reason, "");
assert.equal(typeof status.emcStatus.io.aux.estop, "boolean");

View File

@@ -51,6 +51,7 @@ require_text source_full_program_false "$SOURCE_REUSE" "fullLinuxCncProgramExecu
require_text source_rcs_aggregation "$SOURCE_REUSE" "top/task/motion/io DONE/EXEC/ERROR aggregation"
require_text source_standalone_emc_status "$SOURCE_REUSE" 'T-007 adds `StandaloneEmcStatus`'
require_text source_status_json_contract "$SOURCE_REUSE" "T-051 through T-055 close the status JSON contract batches"
require_text source_status_json_legacy_removed "$SOURCE_REUSE" "T-056 removes legacy task status top-level fields"
require_text source_taskintf_motion_bridge "$SOURCE_REUSE" 'T-033 adds a narrow `taskintf.cc` motion bridge'
require_text source_taskintf_traj_control "$SOURCE_REUSE" 'T-034 extends the `taskintf.cc` traj control subset'
require_text source_taskintf_linear_move "$SOURCE_REUSE" 'T-035 maps `emcTrajLinearMove()` to structured `lcmot_write_linear_move()`'
@@ -92,7 +93,8 @@ reject_text source_native_hal_true "$SOURCE_REUSE" "nativeHalSyncReady=true"
require_text drift_task_boundary "$DRIFT" "Task/motion/HAL WASM runtime"
require_text drift_standalone_emc_status "$DRIFT" "T-007 StandaloneEmcStatus"
require_text drift_status_json_contract "$DRIFT" "T-051-T-055 status JSON contract"
require_text drift_status_json_contract "$DRIFT" "T-051-T-056 status JSON contract"
require_text drift_status_json_legacy_removed "$DRIFT" "T-056 removes legacy task status top-level fields"
require_text drift_emc_nml_boundary "$DRIFT" '`emc_nml.hh` status container'
require_text drift_rcs_aggregation "$DRIFT" "top/task/motion/io DONE/EXEC/ERROR aggregation"
require_text drift_taskintf_motion_bridge "$DRIFT" 'T-033 `taskintf.cc` motion bridge'
@@ -136,6 +138,7 @@ require_text compat_emccanon_motion_output_gate "$COMPAT" "tools/verify_task_emc
require_text compat_no_json_main_path_gate "$COMPAT" "tools/verify_task_no_json_motion_plan_main_path.sh"
require_text compat_standalone_emc_status_gate "$COMPAT" "tools/verify_task_standalone_emc_status.sh"
require_text compat_status_json_contract_gate "$COMPAT" "tools/verify_task_status_json_contract.sh"
require_text compat_status_json_legacy_absence "$COMPAT" "absence of legacy task status top-level fields"
require_text compat_taskintf_motion_bridge_status "$COMPAT" "taskintfMotionBridge"
require_text compat_taskintf_traj_control_status "$COMPAT" "taskintf_traj_control_status=ok"
require_text compat_taskintf_linear_move_status "$COMPAT" "taskintf_linear_move_status=ok"
@@ -150,7 +153,7 @@ require_text compat_standalone_emc_status "$COMPAT" "standalone_emc_status_conta
require_text compat_status_json_contract "$COMPAT" "task_status_json_contract=ok"
require_text compat_working_closure "$COMPAT" "working closure gate"
require_text compat_full_closure "$COMPAT" "task_full_closure_status=ok"
require_text compat_rcs_status "$COMPAT" 'RCS DONE/EXEC/ERROR aggregation through `rcsStatus`'
require_text compat_rcs_status "$COMPAT" 'RCS DONE/EXEC/ERROR aggregation'
require_text compat_readiness_false "$COMPAT" "nativeTaskReady=false"
require_text matrix_t030_done "$MATRIX" "| T-030 | 更新 source reuse map 与 drift 文档 | 完成 |"
@@ -162,5 +165,6 @@ require_text matrix_t052_done "$MATRIX" "| T-052 | SJ-2 规范 task line 和 int
require_text matrix_t053_done "$MATRIX" "| T-053 | SJ-3 规范 \`emcStatus.motion.axis[]\` 和 \`joint[]\` | 完成 |"
require_text matrix_t054_done "$MATRIX" "| T-054 | SJ-4 规范 IO/aux/tool/coolant 边界 | 完成 |"
require_text matrix_t055_done "$MATRIX" "| T-055 | SJ-5 引入 status JSON contract gate | 完成 |"
require_text matrix_t056_done "$MATRIX" "| T-056 | 源头取消旧 status 字段输入兼容 | 完成 |"
printf 'task_source_reuse_drift_docs_status=ok\n'

View File

@@ -53,13 +53,16 @@ require_text wrapper_motion_from_container "$TASK_WRAPPER" "const StandaloneEmcM
require_text wasm_status "$WASM_TEST" "standalone_emc_status_container=ok"
require_text wasm_source "$WASM_TEST" 'snapshot.statusSource, "StandaloneEmcStatus"'
require_text wasm_top_match "$WASM_TEST" "snapshot.emcStatus.top.status, snapshot.taskTopLevelStatus"
require_text wasm_motion_match "$WASM_TEST" "snapshot.emcStatus.motion.programLine, snapshot.motionStatus.motion.programLine"
require_text wasm_legacy_absent "$WASM_TEST" 'assertNoLegacyStatusFields(snapshot)'
require_text wasm_top_emc_status "$WASM_TEST" 'snapshot.emcStatus.top.status'
require_text wasm_motion_emc_status "$WASM_TEST" 'snapshot.emcStatus.motion.programLine'
require_text sdk_status "$SDK_TEST" "task_hal_sdk_standalone_emc_status=ok"
require_text sdk_source "$SDK_TEST" 'status.statusSource, "StandaloneEmcStatus"'
require_text sdk_legacy_absent "$SDK_TEST" 'assertNoLegacyStatusFields(status)'
require_text state_matrix_status "$STATE_MATRIX" "standalone_emc_status_matrix=ok"
require_text state_matrix_helper "$STATE_MATRIX" "function assertStandaloneEmcStatus(status)"
require_text state_matrix_legacy_absent "$STATE_MATRIX" 'assertNoLegacyStatusFields(status)'
require_text docs_source "$SOURCE_REUSE" 'T-007 adds `StandaloneEmcStatus`'
require_text docs_drift "$DRIFT" "T-007 StandaloneEmcStatus"

View File

@@ -59,6 +59,7 @@ require_text matrix_t052_done "$MATRIX" '| T-052 | SJ-2 规范 task line 和 int
require_text matrix_t053_done "$MATRIX" '| T-053 | SJ-3 规范 `emcStatus.motion.axis[]` 和 `joint[]` | 完成 |'
require_text matrix_t054_done "$MATRIX" '| T-054 | SJ-4 规范 IO/aux/tool/coolant 边界 | 完成 |'
require_text matrix_t055_done "$MATRIX" '| T-055 | SJ-5 引入 status JSON contract gate | 完成 |'
require_text matrix_t056_done "$MATRIX" '| T-056 | 源头取消旧 status 字段输入兼容 | 完成 |'
require_text matrix_next_none "$MATRIX" '下一条优先任务:无'
reject_regex matrix_unclosed_tasks "$MATRIX" '^\| T-[0-9]+ \|.*\| (待办|进行中|阻塞) \|'
@@ -81,6 +82,7 @@ require_text ledger_t052 "$LEDGER" 'T-052'
require_text ledger_t053 "$LEDGER" 'T-053'
require_text ledger_t054 "$LEDGER" 'T-054'
require_text ledger_t055 "$LEDGER" 'T-055'
require_text ledger_t056 "$LEDGER" 'T-056'
require_text evidence_t007 "$EVIDENCE" 'T-007 验收通过'
require_text evidence_t047 "$EVIDENCE" 'T-047 验收通过'
require_text evidence_t048 "$EVIDENCE" 'T-048 验收通过'
@@ -91,11 +93,13 @@ require_text evidence_t052 "$EVIDENCE" 'T-052'
require_text evidence_t053 "$EVIDENCE" 'T-053'
require_text evidence_t054 "$EVIDENCE" 'T-054'
require_text evidence_t055 "$EVIDENCE" 'T-055'
require_text evidence_t056 "$EVIDENCE" 'T-056 验收通过'
require_text decision_t007 "$DECISIONS" 'D-039'
require_text decision_t047 "$DECISIONS" 'D-040'
require_text decision_t048 "$DECISIONS" 'D-041'
require_text decision_t049 "$DECISIONS" 'D-042'
require_text decision_t050 "$DECISIONS" 'D-044'
require_text decision_t051_t055 "$DECISIONS" 'D-045'
require_text decision_t056 "$DECISIONS" 'D-046'
printf 'task_working_closure_status=ok\n'

View File

@@ -65,7 +65,7 @@
因此,新增状态应优先进入 C/C++ 结构体、枚举、队列和周期函数status JSON 只能从最后一次 status write 或 `StandaloneEmcStatus` 导出。
`status JSON` 的具体 LinuxCNC 对标方案详见 `12-status-json-LinuxCNC对标方案.md`。该方案要求 `emcStatus` 成为新增 LinuxCNC 对标字段的主对象,`task``motionStatus` 等旧字段只作为兼容视图
`status JSON` 的具体 LinuxCNC 对标方案详见 `12-status-json-LinuxCNC对标方案.md`。该方案要求 `emcStatus` 成为 LinuxCNC 对标字段的唯一 task/motion/io 状态入口;`task``motionStatus``rcsStatus``taskTopLevelStatus` 等旧顶层字段不再输出
## 功能范围

View File

@@ -135,7 +135,7 @@ static void write_status_snapshot(TaskRuntime &state);
- command JSON 只描述 host command envelope。
- status JSON 只导出最后一次 status write。
- 不允许新增依赖 JSON 文本推进 task cycle 的路径。
- 新增 status 字段优先进入 `emcStatus` 对标对象,再按需提供旧字段兼容视图。
- 新增 status 字段进入 `emcStatus` 对标对象,再提供旧字段兼容视图。
4. 每次迁移新语义时优先查找 vendored LinuxCNC source
- 能接入 source-anchored subset 时接入 subset。
- 不能直接接入时建立窄 shim并在 `06-决策记录.md` 写明原因。

View File

@@ -1,5 +1,88 @@
# 03-推进台账
## 2026-07-08 10:18 EDT
### 本轮目标
按用户要求从源头 `/home/mes123456/cnc_wams/wasm-port` 取消旧字段输入兼容,不再让 task status JSON 输出旧顶层字段。
### 本轮做了什么
1. 修改 `linuxcnc_task_hal_wasm.cpp``status_json()`,移除 `taskTopLevelStatus``rcsStatus`、顶层 `task``servoCycle` 和顶层 `motionStatus` 输出。
2. 将旧 `task``motionStatus.motion` 中仍需要的字段收拢到 `emcStatus.task``emcStatus.motion`
3. 更新 WASM、SDK、status contract 和 state matrix 测试,所有 task status 读取路径均只消费 `emcStatus`,并对旧顶层字段做负向断言。
4. 同步更新 `01``02``04``11``12` 文档口径,新增 T-056源头取消旧 status 字段兼容输出。
5. 重建 `task-hal` wasm并运行四个直接相关 Node 验证。
### 影响文件
- `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_task_hal_wasm.cpp`
- `wasm-port/tests/wasm/node/verify_task_status_json_contract.mjs`
- `wasm-port/tests/wasm/node/verify_task_hal_sdk.mjs`
- `wasm-port/tests/wasm/node/verify_task_hal_wasm.mjs`
- `wasm-port/tests/wasm/node/verify_task_state_matrix.mjs`
- `wasm-port/working/01-项目功能内容.md`
- `wasm-port/working/02-项目程序开发详细步骤.md`
- `wasm-port/working/03-推进台账.md`
- `wasm-port/working/04-任务矩阵.md`
- `wasm-port/working/05-验收证据.md`
- `wasm-port/working/11-WASM核心状态机边界与后续完善路线.md`
- `wasm-port/working/12-status-json-LinuxCNC对标方案.md`
### 当前结论
T-056 已完成。`lctask_read_status_json()` 的 task/motion/io/top 状态入口收口为 `emcStatus`,旧顶层字段不再输出;直接相关 Node 验证均通过。
## 2026-07-08 09:30 EDT
### 本轮目标
按用户明确指定的两个文件完成并复核:
- `/home/mes123456/cnc_wams/wasm-port/working/11-WASM核心状态机边界与后续完善路线.md`
- `/home/mes123456/cnc_wams/wasm-port/working/12-status-json-LinuxCNC对标方案.md`
### 本轮做了什么
1. 读取两个目标文档,确认 `12-status-json-LinuxCNC对标方案.md` 中 SJ-1 到 SJ-5 已进入 `04-任务矩阵.md` 的 T-051 到 T-055`11-WASM核心状态机边界与后续完善路线.md` 已记录这些批次闭合。
2. 复核 `04-任务矩阵.md`,确认 T-001 到 T-055 均为完成,当前推进指针为“下一条优先任务:无”。
3. 检查 `tools/verify_task_status_json_contract.sh``tools/verify_task_full_closure.sh`,确认 status JSON contract gate 已纳入 full closure。
4. 扫描 working、docs、tools、runtime、tests 中的 `StandaloneEmcStatus``emcStatus`、SJ/T 编号和 contract gate 锚点,确认 runtime、测试、文档均有对应覆盖。
5. 执行 `./tools/verify_task_full_closure.sh`,验证 task-HAL WASM build、WASM smoke、SDK smoke、state matrix、motion sync、StandaloneEmcStatus、no-JSON 主路径、status JSON contract、working closure、source reuse/drift docs 和 readiness contract。
6. 执行 `git diff --check`,确认当前差异无 whitespace error。
### 影响文件
- `wasm-port/working/03-推进台账.md`
- `wasm-port/working/05-验收证据.md`
### 当前结论
目标文件 `11``12` 中定义的当前全部工作已完成并通过复核SJ-1 到 SJ-5 对应 T-051 到 T-055status JSON contract gate 已纳入 full closure全量闭合脚本最终输出 `task_full_closure_status=ok``git diff --check` 无输出。
## 2026-07-08 09:26 EDT
### 本轮目标
按用户要求复核并完成 `/home/mes123456/cnc_wams/wasm-port/working` 全部工作,确认当前任务矩阵、闭合 gate 和执行证据均无未闭合项。
### 本轮做了什么
1. 读取 `wasm-port/working/README.md``04-任务矩阵.md``03-推进台账.md``05-验收证据.md`,确认当前文档声明 T-001 到 T-055 均为完成,下一条优先任务为“无”。
2. 检查仓库根目录和 `wasm-port/AGENTS.md`,确认本轮同时受根目录中文日志规则和 `wasm-port` LinuxCNC source reuse / 额外日志规则约束。
3. 扫描 `wasm-port/working``tools``docs``runtime``tests` 中的开放标记,确认出现的“后续”主要是已文档化的保守边界或新增任务入口,不是当前矩阵未完成任务;矩阵任务行均为“完成”。
4. 执行 `./tools/verify_task_full_closure.sh`,完成 task-HAL WASM 构建、WASM smoke、SDK smoke、state matrix、motion sync、StandaloneEmcStatus、no-JSON 主路径、status JSON contract、working closure、source reuse/drift docs 和 readiness contract 验证。
5. 全量闭合脚本通过,最终输出 `task_full_closure_status=ok`
### 影响文件
- `wasm-port/working/03-推进台账.md`
- `wasm-port/working/05-验收证据.md`
### 当前结论
`wasm-port/working` 当前全部工作已完成并复核通过。任务矩阵无待办、进行中或阻塞项T-051 到 T-055 的 status JSON contract 已纳入 full closure全量闭合 gate 输出 `task_full_closure_status=ok`
## 2026-07-08 09:12 EDT
### 本轮目标

View File

@@ -61,12 +61,13 @@
| T-048 | 将 working closure gate 纳入全局文档一致性验证 | 完成 | `source-reuse-map.md``compatibility-validation.md``verify_task_source_reuse_drift_docs.sh` 均记录并检查 `verify_task_working_closure.sh`。 |
| T-049 | 建立 task-HAL 全量闭合验证入口 | 完成 | `tools/verify_task_full_closure.sh` 一次性执行 build、WASM smoke、SDK、state matrix、motion sync、T-007/T-046/T-047/T-048/docs/readiness gates。 |
| T-050 | 建立 status JSON LinuxCNC 对标方案 | 完成 | `12-status-json-LinuxCNC对标方案.md` 存在README、功能内容、开发步骤、台账、证据和决策记录均记录 `StandaloneEmcStatus -> status JSON` 方案working closure gate 不回退。 |
| T-051 | SJ-1 规范 `emcStatus.motion.traj` | 完成 | `emcStatus.motion.traj` 导出 enabled/inpos/queue/activeQueue/queueFull/id/paused/singleStepping/position/actualPosition/currentVelWASM/SDK/state matrix 和 status JSON contract gate 断言其与兼容 `motionStatus` 同源。 |
| T-052 | SJ-2 规范 task line 和 interpreter 字段 | 完成 | `StandaloneEmcTaskStatus` 集中 currentLine/readLine/motionLine/callLevel`task` 兼容视图和 `emcStatus.task` 同源;staged RUN 后 contract gate 验证 line 字段。 |
| T-053 | SJ-3 规范 `emcStatus.motion.axis[]``joint[]` | 完成 | `StandaloneEmcMotionStatus` 保存 axis command/feedback 与 joint command/feedback 数组;`emcStatus.motion.axis[]``joint[]` 导出至少 6 轴/关节,`motionStatus.axis``joint0` 与新数组一致。 |
| T-051 | SJ-1 规范 `emcStatus.motion.traj` | 完成 | `emcStatus.motion.traj` 导出 enabled/inpos/queue/activeQueue/queueFull/id/paused/singleStepping/position/actualPosition/currentVelWASM/SDK/state matrix 和 status JSON contract gate 断言其与 `emcStatus.motion` 同源。 |
| T-052 | SJ-2 规范 task line 和 interpreter 字段 | 完成 | `StandaloneEmcTaskStatus` 集中 currentLine/readLine/motionLine/callLevelstaged RUN 后 contract gate 验证 `emcStatus.task` line 字段。 |
| T-053 | SJ-3 规范 `emcStatus.motion.axis[]``joint[]` | 完成 | `StandaloneEmcMotionStatus` 保存 axis command/feedback 与 joint command/feedback 数组;`emcStatus.motion.axis[]``joint[]` 导出至少 6 轴/关节,`emcStatus.motion.axisByName``joint0` 与新数组一致。 |
| T-054 | SJ-4 规范 IO/aux/tool/coolant 边界 | 完成 | `emcStatus.io` 导出 status/error/fault/reason/aux.estoptool/coolant 以 `supported=false` 和 shim reason 显式标记未接 native IOreadiness 仍为 false。 |
| T-055 | SJ-5 引入 status JSON contract gate | 完成 | 新增 `tools/verify_task_status_json_contract.sh` 和 Node contract 测试;检查 `statusSource=StandaloneEmcStatus``lctask_read_status_json()` 不调用 motion JSON、SDK 不实现 task/motion 语义;该 gate 纳入 full closure。 |
| T-056 | 源头取消旧 status 字段输入兼容 | 完成 | `lctask_read_status_json()` 不再输出 `taskTopLevelStatus``rcsStatus`、顶层 `task``servoCycle`、顶层 `motionStatus`WASM/SDK/state matrix/status contract 读取时均做负向断言。 |
## 当前推进指针
下一条优先任务:无。任务矩阵当前已闭合;`11-WASM核心状态机边界与后续完善路线.md``12-status-json-LinuxCNC对标方案.md` 中 SJ-1 到 SJ-5 已补入 T-051 到 T-055 并闭合。后续新增对标项需先补入本矩阵。
下一条优先任务:无。任务矩阵当前已闭合;`11-WASM核心状态机边界与后续完善路线.md``12-status-json-LinuxCNC对标方案.md` 中 SJ-1 到 SJ-5 已补入 T-051 到 T-055 并闭合,旧 status 字段源头收口已作为 T-056 闭合。后续新增对标项需先补入本矩阵。

View File

@@ -1,5 +1,121 @@
# 05-验收证据
## 2026-07-08 10:18 EDT T-056 旧 status 字段源头收口
### 验收对象
`lctask_read_status_json()` 输出契约和直接消费 task status JSON 的 WASM/SDK/state matrix 测试。
### 证据命令
```bash
source /home/mes123456/emsdk/emsdk_env.sh >/dev/null && bash wasm-port/tools/build_task_hal_wasm.sh
node wasm-port/tests/wasm/node/verify_task_status_json_contract.mjs
node wasm-port/tests/wasm/node/verify_task_hal_sdk.mjs
node wasm-port/tests/wasm/node/verify_task_state_matrix.mjs
node wasm-port/tests/wasm/node/verify_task_hal_wasm.mjs
```
### 实际输出摘要
```text
linuxcnc_task_hal_wasm_build=ok
task_status_json_contract=ok
linuxcnc_task_hal_sdk=ok
task_hal_sdk_status_json_contract=ok
task_state_matrix=ok
task_status_json_contract_matrix=ok
linuxcnc_task_runtime_smoke=ok
task_status_json_contract=ok
```
### 结论
T-056 验收通过。task status JSON 不再输出 `taskTopLevelStatus``rcsStatus`、顶层 `task``servoCycle`、顶层 `motionStatus`;测试读取路径均从 `emcStatus` 取值,并对旧字段做负向断言。
## 2026-07-08 09:30 EDT 目标文件 11/12 全部工作复核
### 验收对象
- `11-WASM核心状态机边界与后续完善路线.md`
- `12-status-json-LinuxCNC对标方案.md`
- T-051 到 T-055 / SJ-1 到 SJ-5 当前闭合状态。
### 证据命令
```bash
cd /home/mes123456/cnc_wams/wasm-port
./tools/verify_task_full_closure.sh
cd /home/mes123456/cnc_wams
git diff --check
```
### 实际输出摘要
`./tools/verify_task_full_closure.sh`
```text
linuxcnc_task_hal_wasm_build=ok
task_status_json_contract=ok
linuxcnc_task_hal_sdk=ok
task_state_matrix=ok
motion_hal_sync_smoke=ok
task_standalone_emc_status_status=ok
task_no_json_motion_plan_main_path_status=ok
task_status_json_contract_status=ok
task_working_closure_status=ok
task_source_reuse_drift_docs_status=ok
task_hal_readiness_contract_status=ok
task_full_closure_status=ok
```
`git diff --check`:无输出,以 0 退出。
### 结论
目标文件 `11``12` 当前定义的全部可执行工作已闭合。后续新增状态或 LinuxCNC 对标字段仍需先补入任务矩阵、验收标准和 gate。
## 2026-07-08 09:26 EDT working 全部工作闭合复核
### 验收对象
`/home/mes123456/cnc_wams/wasm-port/working` 当前全部任务闭合状态。
### 验收目标
- `04-任务矩阵.md` 中 T-001 到 T-055 均为完成。
- 当前推进指针为“下一条优先任务:无”。
- T-051 到 T-055 的 status JSON contract gate 已纳入 full closure。
- 全量闭合验证通过。
### 证据命令
```bash
cd /home/mes123456/cnc_wams/wasm-port
./tools/verify_task_full_closure.sh
```
### 实际输出摘要
```text
linuxcnc_task_hal_wasm_build=ok
linuxcnc_task_runtime_smoke=ok
linuxcnc_task_hal_sdk=ok
task_state_matrix=ok
motion_hal_sync_smoke=ok
task_standalone_emc_status_status=ok
task_no_json_motion_plan_main_path_status=ok
task_status_json_contract_status=ok
task_working_closure_status=ok
task_source_reuse_drift_docs_status=ok
task_hal_readiness_contract_status=ok
task_full_closure_status=ok
```
### 结论
`wasm-port/working` 全部工作闭合复核通过。后续新增对标项仍需先补入任务矩阵、验收标准和 gate再实施代码或文档变更。
## 2026-07-08 09:12 EDT SJ-1 到 SJ-5 status JSON contract 验收
### 验收对象

View File

@@ -1,5 +1,28 @@
# 06-决策记录
## D-046task status JSON 不再保留旧顶层字段兼容视图
日期2026-07-08
状态:已采纳
### 决策
`lctask_read_status_json()` 只通过 `emcStatus` 导出 task/motion/io/top 状态,不再输出 `taskTopLevelStatus``rcsStatus`、顶层 `task``servoCycle`、顶层 `motionStatus`
本决策取代 D-044 中“既有旧字段保留为兼容视图”的部分D-044 中“status JSON 对标 LinuxCNC status buffer、JSON 不能成为事实源、read status 只读”的部分继续有效。
### 理由
上层项目已经迁移到 `emcStatus` 契约。继续在源头输出旧字段会扩大状态表面,导致 SDK/UI 继续依赖旧字段,并让后续 LinuxCNC `EMC_STAT` 对标和契约测试同时维护两套入口。
### 影响
- `status_json()` 移除旧顶层字段输出。
- `emcStatus.task``emcStatus.motion` 补齐旧视图中仍需要的字段。
- WASM、SDK、state matrix 和 status contract 测试均对旧顶层字段做负向断言。
- 后续新增字段只能进入 `emcStatus` 或底层 C/C++ status buffer不新增旧字段兼容视图。
## D-045SJ-1 到 SJ-5 必须进入任务矩阵并由 contract gate 固定
日期2026-07-08

View File

@@ -198,7 +198,7 @@ WASM 需要引入 task status buffer 等价物:
- status read 不直接调用 `lcmot_read_status_json()` 改变观察口径。
- debug/event log 可以单独存在,但不能替代 status buffer。
status JSON 只是 status buffer 的 host/browser 序列化格式。后续新增 LinuxCNC 对标字段时,字段应先进入 `StandaloneEmcStatus` 或后续 `EMC_STAT` 等价容器,再导出到 `emcStatus` JSON 对象;旧 `task``motionStatus` 字段只作为兼容视图。具体字段映射和 SJ 批次见 `12-status-json-LinuxCNC对标方案.md`
status JSON 只是 status buffer 的 host/browser 序列化格式。后续新增 LinuxCNC 对标字段时,字段应先进入 `StandaloneEmcStatus` 或后续 `EMC_STAT` 等价容器,再导出到 `emcStatus` JSON 对象;旧 `task``motionStatus` 等顶层状态字段不再作为兼容视图输出。具体字段映射和 SJ 批次见 `12-status-json-LinuxCNC对标方案.md`
## 替换上游源码路线

View File

@@ -21,7 +21,7 @@
3. `lctask_read_status_json()` 只导出最后一次 status write不推进 cycle、不重新读取 motion、不触发 command 执行。
4. 核心状态保存在 C/C++ 结构体、枚举、队列和周期函数中;当前集中容器为 `StandaloneEmcStatus`
5. motion 状态通过 `LcmotStatusSnapshot` 在 task 周期内读入,再映射到 `StandaloneEmcStatus.motion`
6. JSON 字段可以兼容旧 SDK/UI但必须从核心结构体导出不能成为新的事实源
6. status JSON 只通过 `emcStatus` 暴露 task/motion/io/top 状态;旧顶层状态字段不再作为兼容视图输出
7. 若 LinuxCNC 上游源码已有对应语义,优先接入 vendored source 或窄 shim不在 JS/JSON 中重写。
## 当前事实源
@@ -34,7 +34,7 @@
- RUN 主路径staged program -> `emcTaskPlanRead()` -> `emcTaskPlanCommand()` -> `emcTaskPlanExecute()` -> `emccanon.cc` command envelope -> `taskintf.cc` motion issue。
- JSON status`statusSource=StandaloneEmcStatus` 的外部导出视图。
status JSON 的 LinuxCNC `EMC_STAT` 字段映射、兼容策略和后续 SJ 批次见 `12-status-json-LinuxCNC对标方案.md`
status JSON 的 LinuxCNC `EMC_STAT` 字段映射、旧字段收口策略和后续 SJ 批次见 `12-status-json-LinuxCNC对标方案.md`
当前 `12-status-json-LinuxCNC对标方案.md` 中 SJ-1 到 SJ-5 已补入 `04-任务矩阵.md` 的 T-051 到 T-055并已闭合。后续不得再只用“后续路线”描述实际任务凡是出现可执行实现项必须同步进入任务矩阵。
@@ -57,6 +57,7 @@ status JSON 的 LinuxCNC `EMC_STAT` 字段映射、兼容策略和后续 SJ 批
- T-053 / SJ-3`emcStatus.motion.axis[]``joint[]`
- T-054 / SJ-4`emcStatus.io.aux/tool/coolant` 窄边界。
- T-055 / SJ-5`tools/verify_task_status_json_contract.sh`
- T-056源头取消旧 status 字段兼容输出。
## 代码修改检查清单
@@ -65,7 +66,7 @@ status JSON 的 LinuxCNC `EMC_STAT` 字段映射、兼容策略和后续 SJ 批
- 新状态是否有 C/C++ 结构体字段,而不是只新增 JSON 字段。
- 新 command 是否先入队,再由 `lctask_run_cycles()` 消费。
- 新 status JSON 字段是否从 `StandaloneEmcStatus` 或明确的 C/C++ status buffer 导出。
- JS/SDK 是否只做边界包装和兼容 API
- JS/SDK 是否只做边界包装,不重新引入旧 status 字段兼容读取
- 是否新增或更新 `tools/verify_task_*` gate。
- 是否同步更新 `03-推进台账.md``05-验收证据.md``06-决策记录.md`

View File

@@ -59,9 +59,9 @@ lctask_read_status_json() -> step servo / read fresh motion / consume command
`status JSON` 只承担四类职责:
1. 把最后一次 `write_status_snapshot()` 中的状态导出给 JS/SDK/UI。
2. 给浏览器 UI 提供稳定、可版本化、可兼容的字段名。
2. 给浏览器 UI 提供稳定、可版本化的字段名。
3. 给 Node/WASM/browser 测试提供可断言的观察面。
4. 保留旧 SDK/UI 兼容字段,同时新增更接近 `EMC_STAT``emcStatus` 对象。
4. 更接近 `EMC_STAT``emcStatus` 对象作为唯一 task/motion/io 状态入口
`status JSON` 不承担:
@@ -72,21 +72,12 @@ lctask_read_status_json() -> step servo / read fresh motion / consume command
## 推荐JSON结构
顶层保留 runtime/readiness 和兼容字段,同时以 `emcStatus` 作为 LinuxCNC 对标主对象
顶层保留 runtime/readiness/source/schema 等边界字段task/motion/io/top 状态只进入 `emcStatus`
```json
{
"statusSource": "StandaloneEmcStatus",
"schemaVersion": 1,
"taskTopLevelStatus": "DONE",
"rcsStatus": {
"top": "DONE",
"task": "DONE",
"motion": "DONE",
"io": "DONE"
},
"task": {},
"motionStatus": {},
"emcStatus": {
"source": "StandaloneEmcStatus",
"top": { "status": "DONE" },
@@ -99,27 +90,26 @@ lctask_read_status_json() -> step servo / read fresh motion / consume command
要求:
- `task``motionStatus` 是兼容视图,字段必须来自 `emcStatus` 或同一份 C/C++ status buffer。
- `emcStatus` 是后续新增 LinuxCNC 对标字段的默认入口。
- 新字段优先落在 `emcStatus.task``emcStatus.motion.traj``emcStatus.motion.joint[]``emcStatus.motion.axis[]``emcStatus.io` 中。
- 若必须保留旧字段名,应在测试中断言旧字段与 `emcStatus` 同源一致
- `taskTopLevelStatus``rcsStatus`、顶层 `task``servoCycle`、顶层 `motionStatus` 禁止再从 `lctask_read_status_json()` 输出
## 字段映射方案
| LinuxCNC字段 | WASM事实源 | JSON位置 | 状态 |
| --- | --- | --- | --- |
| `EMC_STAT.status` / top RCS status | `StandaloneEmcStatus.top_rcs_status` | `emcStatus.top.status``taskTopLevelStatus``rcsStatus.top` | 已有 |
| `EMC_STAT.task.status` | `StandaloneEmcTaskStatus.rcs_status` | `emcStatus.task.status``task.status``rcsStatus.task` | 已有 |
| `EMC_STAT.task.state` | `StandaloneEmcTaskStatus.state` | `emcStatus.task.state``task.state` | 已有 |
| `EMC_STAT.task.mode` | `StandaloneEmcTaskStatus.mode` | `emcStatus.task.mode``task.mode` | 已有 |
| `EMC_STAT.task.execState` | `StandaloneEmcTaskStatus.exec_state` | `emcStatus.task.execState``task.execState` | 已有 |
| `EMC_STAT.task.interpState` | `StandaloneEmcTaskStatus.interp_state` | `emcStatus.task.interpState``task.interpState` | 已有 |
| `EMC_STAT.task.task_paused` | `StandaloneEmcTaskStatus.task_paused` | `emcStatus.task.taskPaused``task.taskPaused` | 已有 |
| `EMC_STAT.task.file` | `StandaloneEmcTaskStatus.open_program` | `emcStatus.task.file``task.file` | 已有 |
| `EMC_STAT.status` / top RCS status | `StandaloneEmcStatus.top_rcs_status` | `emcStatus.top.status` | 已有 |
| `EMC_STAT.task.status` | `StandaloneEmcTaskStatus.rcs_status` | `emcStatus.task.status` | 已有 |
| `EMC_STAT.task.state` | `StandaloneEmcTaskStatus.state` | `emcStatus.task.state` | 已有 |
| `EMC_STAT.task.mode` | `StandaloneEmcTaskStatus.mode` | `emcStatus.task.mode` | 已有 |
| `EMC_STAT.task.execState` | `StandaloneEmcTaskStatus.exec_state` | `emcStatus.task.execState` | 已有 |
| `EMC_STAT.task.interpState` | `StandaloneEmcTaskStatus.interp_state` | `emcStatus.task.interpState` | 已有 |
| `EMC_STAT.task.task_paused` | `StandaloneEmcTaskStatus.task_paused` | `emcStatus.task.taskPaused` | 已有 |
| `EMC_STAT.task.file` | `StandaloneEmcTaskStatus.open_program` | `emcStatus.task.file` | 已有 |
| `EMC_STAT.task.currentLine/readLine/motionLine` | staged interpreter + motion snapshot | `emcStatus.task.currentLine/readLine/motionLine` | 已有 |
| `EMC_STAT.task.activeGCodes/activeMCodes/activeSettings` | vendored interpreter/canon state | `emcStatus.task.activeGCodes/activeMCodes/activeSettings` | 后续 |
| `EMC_STAT.task.g5x_offset/g92_offset/toolOffset` | vendored interpreter/canon state | `emcStatus.task.offsets` | 后续 |
| `EMC_STAT.motion.status` | `StandaloneEmcMotionStatus.rcs_status` | `emcStatus.motion.status``rcsStatus.motion` | 已有 |
| `EMC_STAT.motion.status` | `StandaloneEmcMotionStatus.rcs_status` | `emcStatus.motion.status` | 已有 |
| `EMC_STAT.motion.traj.enabled` | `LcmotStatusSnapshot.motion_enabled` | `emcStatus.motion.traj.enabled` | 已有 |
| `EMC_STAT.motion.traj.inpos` | `LcmotStatusSnapshot.in_position` | `emcStatus.motion.traj.inpos` | 已有 |
| `EMC_STAT.motion.traj.queue` | `LcmotStatusSnapshot.queue_count` | `emcStatus.motion.traj.queue` | 已有 |
@@ -133,7 +123,7 @@ lctask_read_status_json() -> step servo / read fresh motion / consume command
| `EMC_STAT.motion.on_soft_limit` | `LcmotStatusSnapshot.on_soft_limit` | `emcStatus.motion.onSoftLimit` | 已有 |
| `EMC_STAT.motion.joint[]` | `LcmotStatusSnapshot.joint_cmd/joint_fb` | `emcStatus.motion.joint[]` | 已有 |
| `EMC_STAT.motion.axis[]` | `LcmotStatusSnapshot.axis_cmd/axis_fb` | `emcStatus.motion.axis[]` | 已有 |
| `EMC_STAT.io.status` | `StandaloneEmcIoStatus.rcs_status` | `emcStatus.io.status``rcsStatus.io` | 已有 |
| `EMC_STAT.io.status` | `StandaloneEmcIoStatus.rcs_status` | `emcStatus.io.status` | 已有 |
| `EMC_STAT.io.aux.estop` | task/io shim | `emcStatus.io.aux.estop` | 已有 |
| `EMC_STAT.io.fault/reason` | `StandaloneEmcIoStatus.error/reason` | `emcStatus.io.fault/reason` | 已有 |
@@ -143,13 +133,13 @@ lctask_read_status_json() -> step servo / read fresh motion / consume command
状态:已完成,对应 T-051。
目标:把当前 `motionStatus.motion.*` 中已有的 queue、inpos、id、paused、stepping、currentVel 等字段,以 LinuxCNC `EMC_TRAJ_STAT` 名称归入 `emcStatus.motion.traj`
目标:把 motion snapshot 中已有的 queue、inpos、id、paused、stepping、currentVel 等字段,以 LinuxCNC `EMC_TRAJ_STAT` 名称归入 `emcStatus.motion.traj`
验收:
- `emcStatus.motion.traj.queue` 等于兼容字段 `motionStatus.motion.queueDepth`
- `emcStatus.motion.traj.inpos` 等于兼容字段 `motionStatus.motion.inPosition`
- `emcStatus.motion.traj.id` 等于兼容字段 `motionStatus.motion.id`
- `emcStatus.motion.traj.queue` 等于 `emcStatus.motion.queueDepth`
- `emcStatus.motion.traj.inpos` 等于 `emcStatus.motion.inPosition`
- `emcStatus.motion.traj.id` 等于 `emcStatus.motion.motionId`
- `lctask_read_status_json()` 不新增 motion read 或 servo step。
### SJ-2规范 task line 和 interpreter字段
@@ -161,18 +151,18 @@ lctask_read_status_json() -> step servo / read fresh motion / consume command
验收:
- staged program RUN 后,`emcStatus.task.readLine/currentLine/motionLine` 与现有 plan read/execute/motion id evidence 一致。
- 兼容字段若存在,必须与 `emcStatus.task.*Line` 同源
- 顶层 `task` 兼容视图不得再存在
### SJ-3规范 `motion.axis[]` 和 `motion.joint[]`
状态:已完成,对应 T-053。`emcStatus.motion.axis``emcStatus.motion.joint` 为数组;按名字访问的兼容视图保留在 `motionStatus.axis``emcStatus.motion.axisByName`辅助迁移
状态:已完成,对应 T-053。`emcStatus.motion.axis``emcStatus.motion.joint` 为数组;`emcStatus.motion.axisByName`提供按名字读取的同对象辅助视图
目标:把当前 axis x/y/z/a/b/c 和 joint0 字段扩展成数组结构,对标 `EMC_MOTION_STAT.axis[]``joint[]`
验收:
- 至少导出存在的 5/6 轴位置和 joint0 command/feedback。
- `motionStatus.axis``motionStatus.joint0` 与新数组值一致
- 顶层 `motionStatus` 兼容视图不得再存在
### SJ-4规范 IO/aux/tool/coolant边界
@@ -189,22 +179,24 @@ lctask_read_status_json() -> step servo / read fresh motion / consume command
状态:已完成,对应 T-055。
目标:新增专用 gate例如 `tools/verify_task_status_json_contract.sh`,固定 status JSON 字段来自 `StandaloneEmcStatus`,并检查 `emcStatus` 与兼容字段的一致性
目标:新增专用 gate例如 `tools/verify_task_status_json_contract.sh`,固定 status JSON 字段来自 `StandaloneEmcStatus`,并检查旧顶层状态字段不存在
验收:
- gate 检查 `statusSource=StandaloneEmcStatus`
- gate 检查 `taskTopLevelStatus``rcsStatus`、顶层 `task``servoCycle`、顶层 `motionStatus` 不存在。
- gate 检查 `lctask_read_status_json()` 不调用 `lcmot_read_status_json()`
- gate 检查 JS/SDK 没有实现 task/motion 语义。
- gate 纳入 `tools/verify_task_full_closure.sh`
## 兼容策略
## 旧字段收口策略
短期保留
禁止 `lctask_read_status_json()` 输出
- `taskTopLevelStatus`
- `rcsStatus`
- `task`
- `servoCycle`
- `motionStatus`
新增字段默认进入:
@@ -217,7 +209,7 @@ lctask_read_status_json() -> step servo / read fresh motion / consume command
- `emcStatus.motion.joint`
- `emcStatus.io`
旧字段只能作为兼容视图,不能作为后续实现的主字段。新增测试优先断言 `emcStatus`再断言兼容字段与其一致
旧字段不再作为兼容视图保留。新增测试必须优先断言 `emcStatus`并在契约测试中继续对旧字段做负向断言
## 文档和测试要求