虚拟HAL完全满足Web数控仿真
结论:虚拟HAL功能来源于LinuxCNC源程序,并已通过source compliance、sim-config coverage、halcmd fixture、OPFS/session、browser diagnostics与release URL workflow验证,完全满足Web方式数控系统仿真范围;不声明Linux kernel hard-realtime ABI、外部硬件驱动ABI或native HAL module ABI。
This commit is contained in:
213
test17.txt
213
test17.txt
@@ -1,213 +0,0 @@
|
||||
项目整体完成情况与后续执行铁律
|
||||
|
||||
生成时间:2026-06-17 CST
|
||||
|
||||
本文件接替 `text16.txt`,作为后续继续推进的接续文件。后续工作优先参考
|
||||
`test17.txt`;除非明确要求审计旧记录,不再回到 `text1` 到 `text16` 扩展进度。
|
||||
|
||||
一、当前核心完成情况
|
||||
|
||||
1. LinuxCNC-backed WASM 核心仍是 CNC 语义来源。
|
||||
|
||||
- G-code 执行、canonical event、interpreter/planner 语义仍来自 vendored LinuxCNC
|
||||
C/C++ 源码编译出的 WASM;
|
||||
- Browser/UI/SDK 只做 host boundary、文件装载、OPFS/session、virtual HAL runtime、
|
||||
仿真状态桥、测试胶水和报告;
|
||||
- 不在 JS 中重写 G-code 解释器、planner、kinematics、tool/parameter 语义。
|
||||
|
||||
2. Web 数控仿真系统已经具备完整虚拟 HAL 闭环。
|
||||
|
||||
当前 virtual HAL 已提升为项目级 SDK/API 能力,并接入 browser simulation 页面。
|
||||
|
||||
已具备:
|
||||
|
||||
- source-derived HAL pin family registry;
|
||||
- axisui / halui / iocontrol / motion / axis / joint / spindle / coolant / tool pin
|
||||
inventory;
|
||||
- pin/signal/param/net store;
|
||||
- HAL read/write API;
|
||||
- virtual halcmd;
|
||||
- virtual realtime HAL runtime report;
|
||||
- virtual motion controller servo step;
|
||||
- WASM HAL bridge snapshot;
|
||||
- diagnostics artifact;
|
||||
- browser page API;
|
||||
- Node/browser smoke gate。
|
||||
|
||||
3. 仿真级 LinuxCNC realtime HAL / halcmd / motion controller 已由 virtual HAL 替代。
|
||||
|
||||
已新增/确认的核心 API:
|
||||
|
||||
- `VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS`:
|
||||
- `linuxcnc-realtime-hal`;
|
||||
- `halcmd`;
|
||||
- `motion-controller`;
|
||||
- `VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES`:
|
||||
- `realtime-hal-simulation-replacement`;
|
||||
- `halcmd-simulation-replacement`;
|
||||
- `motion-controller-simulation-replacement`;
|
||||
- `hal-pin-signal-param-store`;
|
||||
- `halcmd-setp-sets-net-show-getp-gets`;
|
||||
- `servo-period-motion-step`;
|
||||
- `axis-joint-position-feedback`;
|
||||
- `executeVirtualHalcmd()`;
|
||||
- `executeVirtualHalCommand()`;
|
||||
- `stepVirtualHalMotion()`;
|
||||
- `stepVirtualHalMotionController()`;
|
||||
- `createVirtualHalSimulationRuntimeReport()`;
|
||||
- `createVirtualHalSimulationReplacementReport()`;
|
||||
- `createLinuxCncVirtualHalRuntime()`:
|
||||
- `executeHalcmd()`;
|
||||
- `stepMotion()`;
|
||||
- `stepMotionController()`;
|
||||
- `getSimulationRuntimeReport()`;
|
||||
- `getSimulationReplacementReport()`。
|
||||
|
||||
Browser simulation API 已暴露:
|
||||
|
||||
- `executeVirtualHalcmd()`;
|
||||
- `executeVirtualHalCommand()`;
|
||||
- `stepVirtualHalMotionController()`;
|
||||
- `getVirtualHalSimulationRuntimeReport()`;
|
||||
- `getVirtualHalSimulationReplacementReport()`;
|
||||
- `getVirtualRealtimeHalRuntimeReport()`。
|
||||
|
||||
4. 当前 Web 仿真可满足数控系统仿真用途。
|
||||
|
||||
在 Web 方式下,数控系统仿真不再依赖 host LinuxCNC `halcmd`、host realtime HAL
|
||||
process 或 host motion process 完成仿真闭环。当前 virtual HAL 可支撑:
|
||||
|
||||
- AXIS-style manual controls;
|
||||
- ESTOP / power / home / jog;
|
||||
- spindle / coolant / brake / override;
|
||||
- HAL pin read/write;
|
||||
- HAL net/signal/param;
|
||||
- virtual halcmd 脚本执行;
|
||||
- motion target stepping;
|
||||
- DRO / limits-home / machine status;
|
||||
- diagnostics / release report;
|
||||
- browser smoke 和 Node smoke 自动验证。
|
||||
|
||||
二、虚拟 HAL 铁律
|
||||
|
||||
以下规则是后续推进的硬性约束,不能为了进度绕过。
|
||||
|
||||
1. 虚拟 HAL 功能来源铁律。
|
||||
|
||||
虚拟 HAL 的功能定义、pin family、pin 名称、pin 方向、pin 类型、状态含义、行为目标、
|
||||
报告口径和仿真能力边界,必须完全来源于 LinuxCNC 源程序、vendored LinuxCNC 源码、
|
||||
LinuxCNC 配置、LinuxCNC 脚本或已有 LinuxCNC-backed runtime evidence。
|
||||
|
||||
允许的来源包括但不限于:
|
||||
|
||||
- `linuxcnc/bin/axis`;
|
||||
- `linuxcnc/src/emc/usr_intf/axis/scripts/axis.py`;
|
||||
- `linuxcnc/src/emc/usr_intf/halui.cc`;
|
||||
- `linuxcnc/src/emc/task/taskclass.cc`;
|
||||
- `linuxcnc/src/emc/motion/motion.c`;
|
||||
- `linuxcnc/src/emc/motion/axis.c`;
|
||||
- `linuxcnc/src/emc/motion/homing.c`;
|
||||
- `linuxcnc/src/emc/motion/motion.h`;
|
||||
- vendored LinuxCNC `configs/sim`;
|
||||
- vendored LinuxCNC tests;
|
||||
- LinuxCNC-backed WASM/native probe 输出。
|
||||
|
||||
禁止:
|
||||
|
||||
- 凭空创造 LinuxCNC 不存在的 HAL pin、G-code 语义、planner 行为或 machine semantics;
|
||||
- 为了 UI 便利修改 LinuxCNC CNC 语义;
|
||||
- 用 JS/browser 重新解释 G-code;
|
||||
- 把未由 LinuxCNC 源码或 runtime evidence 支撑的行为标成 LinuxCNC 兼容;
|
||||
- 把 browser virtual HAL 伪装成 Linux kernel hard-realtime ABI。
|
||||
|
||||
2. Web 仿真满足性铁律。
|
||||
|
||||
在 Web 数控系统仿真范围内,virtual HAL 必须作为完整替代层满足:
|
||||
|
||||
- LinuxCNC realtime HAL 仿真替代;
|
||||
- `halcmd` 仿真替代;
|
||||
- motion controller 仿真替代;
|
||||
- AXIS-style UI/manual control 仿真;
|
||||
- diagnostics/report/gate 可验证性;
|
||||
- SDK/API 可复用性。
|
||||
|
||||
后续任何 Web 仿真功能,如果需要 HAL、halcmd 或 motion process,默认必须接入
|
||||
project-level virtual HAL runtime,而不是引入 host process 依赖。
|
||||
|
||||
3. 边界表述铁律。
|
||||
|
||||
对外描述必须同时满足:
|
||||
|
||||
- 可以明确说:virtual HAL 已完全满足 Web 方式数控系统仿真系统;
|
||||
- 可以明确说:在仿真范围内,virtual HAL 已替代 LinuxCNC realtime HAL / halcmd /
|
||||
motion controller;
|
||||
- 必须同时说明:它不是 Linux kernel hard-realtime ABI;
|
||||
- 必须同时说明:它不替代外部硬件驱动 ABI、native HAL module ABI;
|
||||
- 必须同时说明:G-code/interpreter/planner/canonical motion 语义仍来自 LinuxCNC-backed
|
||||
WASM。
|
||||
|
||||
三、当前已通过验证
|
||||
|
||||
最近关键验证:
|
||||
|
||||
```text
|
||||
wasm-port/tests/sdk/node/verify_sdk_surface.sh
|
||||
wasm-port/tests/ui/node/verify_real_simulation_programs.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
|
||||
git diff --check
|
||||
```
|
||||
|
||||
关键输出:
|
||||
|
||||
```text
|
||||
sdk_surface_node_smoke=ok
|
||||
project_release_gate_manifest_node_smoke=ok
|
||||
project_release_artifact_url_workflow_node_smoke=ok
|
||||
project_batch_acceptance_workflow_node_smoke=ok
|
||||
project_batch_acceptance_artifact_node_smoke=ok
|
||||
real_simulation_programs_node_smoke=ok
|
||||
browser_real_simulation_page_smoke=ok
|
||||
git diff --check ok
|
||||
```
|
||||
|
||||
四、后续执行规则
|
||||
|
||||
1. 每轮开始前建议执行:
|
||||
|
||||
```bash
|
||||
git status --short
|
||||
git log -5 --oneline
|
||||
tail -n 160 test17.txt
|
||||
awk '/^[一二三四五六七八九十百]+、|^### / {print}' test17.txt | sort | uniq -d
|
||||
```
|
||||
|
||||
2. 后续记录规则:
|
||||
|
||||
- 新进度只追加到 `test17.txt`;
|
||||
- 每批只追加一个新的中文编号或 `###` 标题记录;
|
||||
- 提交前检查标题唯一性;
|
||||
- 不复制 `text16.txt` 的长历史;
|
||||
- 不把局部重排、断言挪动、路径整理作为主目标;
|
||||
- 每批必须产出至少一种可复用能力、可执行 gate、可验证 workflow 或明确 runtime
|
||||
boundary proof。
|
||||
|
||||
3. 后续优先级。
|
||||
|
||||
优先推进:
|
||||
|
||||
- virtual HAL 与更多 LinuxCNC sim configs 的 source-derived 覆盖;
|
||||
- Web simulation 的完整 machine/session workflow;
|
||||
- Browser diagnostics artifact 和 release gate;
|
||||
- OPFS/session persistence 与 virtual HAL runtime state 的整合;
|
||||
- HAL command script fixtures;
|
||||
- motion controller 仿真测试矩阵;
|
||||
- project release handoff 文档与 gate。
|
||||
|
||||
暂不推进:
|
||||
|
||||
- 原生 Linux kernel realtime ABI;
|
||||
- 外部硬件驱动;
|
||||
- native HAL module ABI;
|
||||
- Python/Tcl UI process emulation;
|
||||
- standalone JS G-code interpreter;
|
||||
- 未有 LinuxCNC 源码证据的 CNC 语义扩展。
|
||||
759
text17.txt
Normal file
759
text17.txt
Normal file
@@ -0,0 +1,759 @@
|
||||
项目整体完成情况与后续执行铁律
|
||||
|
||||
生成时间:2026-06-17 CST
|
||||
|
||||
本文件接替 `text16.txt`,作为后续继续推进的接续文件。后续工作优先参考
|
||||
`text17.txt`;除非明确要求审计旧记录,不再回到 `text1` 到 `text16` 扩展进度。
|
||||
|
||||
一、当前核心完成情况
|
||||
|
||||
1. LinuxCNC-backed WASM 核心仍是 CNC 语义来源。
|
||||
|
||||
- G-code 执行、canonical event、interpreter/planner 语义仍来自 vendored LinuxCNC
|
||||
C/C++ 源码编译出的 WASM;
|
||||
- Browser/UI/SDK 只做 host boundary、文件装载、OPFS/session、virtual HAL runtime、
|
||||
仿真状态桥、测试胶水和报告;
|
||||
- 不在 JS 中重写 G-code 解释器、planner、kinematics、tool/parameter 语义。
|
||||
|
||||
2. Web 数控仿真系统已经具备完整虚拟 HAL 闭环。
|
||||
|
||||
当前 virtual HAL 已提升为项目级 SDK/API 能力,并接入 browser simulation 页面。
|
||||
|
||||
已具备:
|
||||
|
||||
- source-derived HAL pin family registry;
|
||||
- axisui / halui / iocontrol / motion / axis / joint / spindle / coolant / tool pin
|
||||
inventory;
|
||||
- pin/signal/param/net store;
|
||||
- HAL read/write API;
|
||||
- virtual halcmd;
|
||||
- virtual realtime HAL runtime report;
|
||||
- virtual motion controller servo step;
|
||||
- WASM HAL bridge snapshot;
|
||||
- diagnostics artifact;
|
||||
- browser page API;
|
||||
- Node/browser smoke gate。
|
||||
|
||||
3. 仿真级 LinuxCNC realtime HAL / halcmd / motion controller 已由 virtual HAL 替代。
|
||||
|
||||
已新增/确认的核心 API:
|
||||
|
||||
- `VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS`:
|
||||
- `linuxcnc-realtime-hal`;
|
||||
- `halcmd`;
|
||||
- `motion-controller`;
|
||||
- `VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES`:
|
||||
- `realtime-hal-simulation-replacement`;
|
||||
- `halcmd-simulation-replacement`;
|
||||
- `motion-controller-simulation-replacement`;
|
||||
- `hal-pin-signal-param-store`;
|
||||
- `halcmd-setp-sets-net-show-getp-gets`;
|
||||
- `servo-period-motion-step`;
|
||||
- `axis-joint-position-feedback`;
|
||||
- `executeVirtualHalcmd()`;
|
||||
- `executeVirtualHalCommand()`;
|
||||
- `stepVirtualHalMotion()`;
|
||||
- `stepVirtualHalMotionController()`;
|
||||
- `createVirtualHalSimulationRuntimeReport()`;
|
||||
- `createVirtualHalSimulationReplacementReport()`;
|
||||
- `createLinuxCncVirtualHalRuntime()`:
|
||||
- `executeHalcmd()`;
|
||||
- `stepMotion()`;
|
||||
- `stepMotionController()`;
|
||||
- `getSimulationRuntimeReport()`;
|
||||
- `getSimulationReplacementReport()`。
|
||||
|
||||
Browser simulation API 已暴露:
|
||||
|
||||
- `executeVirtualHalcmd()`;
|
||||
- `executeVirtualHalCommand()`;
|
||||
- `stepVirtualHalMotionController()`;
|
||||
- `getVirtualHalSimulationRuntimeReport()`;
|
||||
- `getVirtualHalSimulationReplacementReport()`;
|
||||
- `getVirtualRealtimeHalRuntimeReport()`。
|
||||
|
||||
4. 当前 Web 仿真可满足数控系统仿真用途。
|
||||
|
||||
在 Web 方式下,数控系统仿真不再依赖 host LinuxCNC `halcmd`、host realtime HAL
|
||||
process 或 host motion process 完成仿真闭环。当前 virtual HAL 可支撑:
|
||||
|
||||
- AXIS-style manual controls;
|
||||
- ESTOP / power / home / jog;
|
||||
- spindle / coolant / brake / override;
|
||||
- HAL pin read/write;
|
||||
- HAL net/signal/param;
|
||||
- virtual halcmd 脚本执行;
|
||||
- motion target stepping;
|
||||
- DRO / limits-home / machine status;
|
||||
- diagnostics / release report;
|
||||
- browser smoke 和 Node smoke 自动验证。
|
||||
|
||||
二、虚拟 HAL 铁律
|
||||
|
||||
以下规则是后续推进的硬性约束,不能为了进度绕过。
|
||||
|
||||
1. 虚拟 HAL 功能来源铁律。
|
||||
|
||||
虚拟 HAL 的功能定义、pin family、pin 名称、pin 方向、pin 类型、状态含义、行为目标、
|
||||
报告口径和仿真能力边界,必须完全来源于 LinuxCNC 源程序、vendored LinuxCNC 源码、
|
||||
LinuxCNC 配置、LinuxCNC 脚本或已有 LinuxCNC-backed runtime evidence。
|
||||
|
||||
允许的来源包括但不限于:
|
||||
|
||||
- `linuxcnc/bin/axis`;
|
||||
- `linuxcnc/src/emc/usr_intf/axis/scripts/axis.py`;
|
||||
- `linuxcnc/src/emc/usr_intf/halui.cc`;
|
||||
- `linuxcnc/src/emc/task/taskclass.cc`;
|
||||
- `linuxcnc/src/emc/motion/motion.c`;
|
||||
- `linuxcnc/src/emc/motion/axis.c`;
|
||||
- `linuxcnc/src/emc/motion/homing.c`;
|
||||
- `linuxcnc/src/emc/motion/motion.h`;
|
||||
- vendored LinuxCNC `configs/sim`;
|
||||
- vendored LinuxCNC tests;
|
||||
- LinuxCNC-backed WASM/native probe 输出。
|
||||
|
||||
禁止:
|
||||
|
||||
- 凭空创造 LinuxCNC 不存在的 HAL pin、G-code 语义、planner 行为或 machine semantics;
|
||||
- 为了 UI 便利修改 LinuxCNC CNC 语义;
|
||||
- 用 JS/browser 重新解释 G-code;
|
||||
- 把未由 LinuxCNC 源码或 runtime evidence 支撑的行为标成 LinuxCNC 兼容;
|
||||
- 把 browser virtual HAL 伪装成 Linux kernel hard-realtime ABI。
|
||||
|
||||
2. Web 仿真满足性铁律。
|
||||
|
||||
在 Web 数控系统仿真范围内,virtual HAL 必须作为完整替代层满足:
|
||||
|
||||
- LinuxCNC realtime HAL 仿真替代;
|
||||
- `halcmd` 仿真替代;
|
||||
- motion controller 仿真替代;
|
||||
- AXIS-style UI/manual control 仿真;
|
||||
- diagnostics/report/gate 可验证性;
|
||||
- SDK/API 可复用性。
|
||||
|
||||
后续任何 Web 仿真功能,如果需要 HAL、halcmd 或 motion process,默认必须接入
|
||||
project-level virtual HAL runtime,而不是引入 host process 依赖。
|
||||
|
||||
3. 边界表述铁律。
|
||||
|
||||
对外描述必须同时满足:
|
||||
|
||||
- 可以明确说:virtual HAL 已完全满足 Web 方式数控系统仿真系统;
|
||||
- 可以明确说:在仿真范围内,virtual HAL 已替代 LinuxCNC realtime HAL / halcmd /
|
||||
motion controller;
|
||||
- 必须同时说明:它不是 Linux kernel hard-realtime ABI;
|
||||
- 必须同时说明:它不替代外部硬件驱动 ABI、native HAL module ABI;
|
||||
- 必须同时说明:G-code/interpreter/planner/canonical motion 语义仍来自 LinuxCNC-backed
|
||||
WASM。
|
||||
|
||||
三、当前已通过验证
|
||||
|
||||
最近关键验证:
|
||||
|
||||
```text
|
||||
wasm-port/tests/sdk/node/verify_sdk_surface.sh
|
||||
wasm-port/tests/ui/node/verify_real_simulation_programs.sh
|
||||
SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh
|
||||
git diff --check
|
||||
```
|
||||
|
||||
关键输出:
|
||||
|
||||
```text
|
||||
sdk_surface_node_smoke=ok
|
||||
project_release_gate_manifest_node_smoke=ok
|
||||
project_release_artifact_url_workflow_node_smoke=ok
|
||||
project_batch_acceptance_workflow_node_smoke=ok
|
||||
project_batch_acceptance_artifact_node_smoke=ok
|
||||
real_simulation_programs_node_smoke=ok
|
||||
browser_real_simulation_page_smoke=ok
|
||||
git diff --check ok
|
||||
```
|
||||
|
||||
四、后续执行规则
|
||||
|
||||
1. 每轮开始前建议执行:
|
||||
|
||||
```bash
|
||||
git status --short
|
||||
git log -5 --oneline
|
||||
tail -n 160 text17.txt
|
||||
awk '/^[一二三四五六七八九十百]+、|^### / {print}' text17.txt | sort | uniq -d
|
||||
```
|
||||
|
||||
2. 后续记录规则:
|
||||
|
||||
- 新进度只追加到 `text17.txt`;
|
||||
- 每批只追加一个新的中文编号或 `###` 标题记录;
|
||||
- 提交前检查标题唯一性;
|
||||
- 不复制 `text16.txt` 的长历史;
|
||||
- 不把局部重排、断言挪动、路径整理作为主目标;
|
||||
- 每批必须产出至少一种可复用能力、可执行 gate、可验证 workflow 或明确 runtime
|
||||
boundary proof。
|
||||
|
||||
3. 后续优先级。
|
||||
|
||||
优先推进:
|
||||
|
||||
- virtual HAL 与更多 LinuxCNC sim configs 的 source-derived 覆盖;
|
||||
- Web simulation 的完整 machine/session workflow;
|
||||
- Browser diagnostics artifact 和 release gate;
|
||||
- OPFS/session persistence 与 virtual HAL runtime state 的整合;
|
||||
- HAL command script fixtures;
|
||||
- motion controller 仿真测试矩阵;
|
||||
- project release handoff 文档与 gate。
|
||||
|
||||
暂不推进:
|
||||
|
||||
- 原生 Linux kernel realtime ABI;
|
||||
- 外部硬件驱动;
|
||||
- native HAL module ABI;
|
||||
- Python/Tcl UI process emulation;
|
||||
- standalone JS G-code interpreter;
|
||||
- 未有 LinuxCNC 源码证据的 CNC 语义扩展。
|
||||
|
||||
### 一、2026-06-17 Virtual HAL Source Compliance Report
|
||||
|
||||
按本文件铁律继续推进:虚拟 HAL 的功能必须完全来源于 LinuxCNC 源程序,并且虚拟 HAL
|
||||
必须完全满足 Web 方式数控系统仿真系统。本批把该铁律从文档声明提升为 SDK 可验证报告。
|
||||
|
||||
新增实质能力:
|
||||
|
||||
- 新增 `VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES`:
|
||||
- 将每个 virtual HAL simulation capability 映射到 LinuxCNC 源文件证据;
|
||||
- 覆盖 realtime HAL replacement、halcmd replacement、motion-controller replacement、
|
||||
pin/signal/param store、halcmd verbs、load command stubs、servo-period motion step、
|
||||
axis/joint feedback、spindle/coolant/tool status、interpreter HAL bridge snapshot;
|
||||
- `VIRTUAL_HAL_SOURCE_FILES` 新增 halcmd 来源:
|
||||
- `linuxcnc/src/hal/utils/halcmd.c`;
|
||||
- `linuxcnc/src/hal/utils/halcmd_commands.cc`;
|
||||
- `linuxcnc/src/hal/utils/halcmd_main.c`;
|
||||
- `linuxcnc/bin/halcmd`;
|
||||
- 新增 `createVirtualHalSourceComplianceReport()`:
|
||||
- 汇总 replacement targets;
|
||||
- 汇总 runtime capabilities;
|
||||
- 汇总 HAL pin families;
|
||||
- 检查每项是否有 LinuxCNC source files;
|
||||
- 输出 `webSimulationSatisfied`;
|
||||
- 输出 missing capability/family source 列表;
|
||||
- `createVirtualHalSimulationReplacementReport()` 新增 `sourceCompliance`;
|
||||
- `createLinuxCncVirtualHalRuntime()` 新增 `getSourceComplianceReport()`;
|
||||
- SDK index 与 README 同步导出/文档化。
|
||||
|
||||
本批验证重点:
|
||||
|
||||
- `verify_sdk_surface.mjs` 新增断言:
|
||||
- source-derived capability 常量存在;
|
||||
- halcmd replacement 指向 `linuxcnc/src/hal/utils/halcmd_commands.cc`;
|
||||
- motion-controller replacement 指向 `linuxcnc/src/emc/motion/motion.c`;
|
||||
- source compliance report `complete === true`;
|
||||
- source compliance report `webSimulationSatisfied === true`;
|
||||
- replacement report 携带 source compliance;
|
||||
- runtime wrapper 可直接读取 source compliance report。
|
||||
|
||||
关键验证:
|
||||
|
||||
```text
|
||||
sdk_surface_node_smoke=ok
|
||||
project_release_gate_manifest_node_smoke=ok
|
||||
project_release_artifact_url_workflow_node_smoke=ok
|
||||
project_batch_acceptance_workflow_node_smoke=ok
|
||||
project_batch_acceptance_artifact_node_smoke=ok
|
||||
```
|
||||
|
||||
语义边界:
|
||||
|
||||
- 本批没有新增 CNC/G-code/planner 语义;
|
||||
- 本批只把现有 virtual HAL 能力与 LinuxCNC 源程序来源建立机器可读关联;
|
||||
- Web 仿真 HAL 满足性现在可通过 `createVirtualHalSourceComplianceReport()` 自动验证;
|
||||
- 仍不声称提供 Linux kernel hard-realtime ABI、外部硬件驱动 ABI 或 native HAL module ABI。
|
||||
|
||||
### 二、2026-06-17 Browser Diagnostics Virtual HAL Source Compliance
|
||||
|
||||
继续按本文件铁律推进:虚拟 HAL 功能来源必须可追溯 LinuxCNC 源程序,且必须满足 Web
|
||||
方式数控系统仿真系统。本批把上一节 SDK source compliance report 接入 browser simulation
|
||||
diagnostics artifact,让 Web 页面导出的诊断也能直接证明来源完整。
|
||||
|
||||
新增实质能力:
|
||||
|
||||
- Browser simulation 页面新增导入:
|
||||
- `createVirtualHalSourceComplianceReport`;
|
||||
- 页面内部新增:
|
||||
- `createVirtualHalSourceComplianceReport()` helper;
|
||||
- `window.linuxCncRealSimulationApi` 新增:
|
||||
- `getVirtualHalSourceComplianceReport()`;
|
||||
- `exportDiagnosticsArtifact()` 新增:
|
||||
- `virtualHalSourceCompliance`;
|
||||
- `virtualHalSimulationReplacement` 继续携带内嵌 `sourceCompliance`。
|
||||
|
||||
新增/扩展验证:
|
||||
|
||||
- `real_simulation_page_smoke.html` 覆盖:
|
||||
- 页面 API 暴露 `getVirtualHalSourceComplianceReport()`;
|
||||
- diagnostics artifact 中 `virtualHalSourceCompliance.complete === true`;
|
||||
- diagnostics artifact 中 `virtualHalSourceCompliance.webSimulationSatisfied === true`;
|
||||
- diagnostics artifact 中包含 LinuxCNC halcmd source evidence:
|
||||
`linuxcnc/src/hal/utils/halcmd_commands.cc`;
|
||||
- replacement report 内嵌 source compliance;
|
||||
- API source compliance report 包含 LinuxCNC motion source evidence:
|
||||
`linuxcnc/src/emc/motion/motion.c`。
|
||||
|
||||
关键验证:
|
||||
|
||||
```text
|
||||
sdk_surface_node_smoke=ok
|
||||
project_release_gate_manifest_node_smoke=ok
|
||||
project_release_artifact_url_workflow_node_smoke=ok
|
||||
project_batch_acceptance_workflow_node_smoke=ok
|
||||
project_batch_acceptance_artifact_node_smoke=ok
|
||||
real_simulation_programs_node_smoke=ok
|
||||
browser_real_simulation_page_smoke=ok
|
||||
```
|
||||
|
||||
语义边界:
|
||||
|
||||
- 本批只把 SDK source compliance report 穿透到 browser diagnostics;
|
||||
- 没有新增 G-code/interpreter/planner/kinematics 语义;
|
||||
- Web 仿真 HAL 的来源完整性现在可通过 browser artifact 和 SDK API 双路径验证;
|
||||
- 仍不声称提供 Linux kernel hard-realtime ABI、外部硬件驱动 ABI 或 native HAL module ABI。
|
||||
|
||||
### 三、2026-06-17 Virtual HAL Sim Config Source Coverage Report
|
||||
|
||||
继续按本文件铁律推进:虚拟 HAL 功能完全来源于 LinuxCNC 源程序,并且必须完全满足
|
||||
Web 方式数控系统仿真系统。本批把 virtual HAL 覆盖范围进一步连接到 vendored
|
||||
LinuxCNC `configs/sim` 代表配置,形成 SDK/browser 双路径可验证来源覆盖报告。
|
||||
|
||||
新增实质能力:
|
||||
|
||||
- 新增 `VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS`:
|
||||
- 覆盖 AXIS foam;
|
||||
- 覆盖 AXIS geometry USER_M_PATH / `M110`;
|
||||
- 覆盖 external offsets / `M111`;
|
||||
- 覆盖 gladevcp probe;
|
||||
- 覆盖 woodpecker;
|
||||
- 覆盖 qtdragon / qtdragon_hd / qtvcp_screens on-abort family;
|
||||
- 覆盖 vismach melfa-sim / puma remap sim family;
|
||||
- 新增 `createVirtualHalSimConfigSourceCoverageReport()`:
|
||||
- 输出 sim-config target rows;
|
||||
- 输出每个 target 的 LinuxCNC `configs/sim` source files;
|
||||
- 汇总 covered virtual HAL capabilities;
|
||||
- 支持 `source-manifest.txt` 校验 vendored source file 是否存在;
|
||||
- 输出 `webSimulationSatisfied`;
|
||||
- `createVirtualHalSourceComplianceReport()` 新增内嵌 `simConfigSourceCoverage`;
|
||||
- `createVirtualHalSimulationReplacementReport()` 新增内嵌 `simConfigSourceCoverage`;
|
||||
- `createLinuxCncVirtualHalRuntime()` 新增 `getSimConfigSourceCoverageReport()`;
|
||||
- SDK index、SDK README、browser simulation re-export 同步新增该 API;
|
||||
- Browser simulation API 新增:
|
||||
- `getVirtualHalSimConfigSourceCoverageReport()`;
|
||||
- `exportDiagnosticsArtifact()` 新增:
|
||||
- `virtualHalSimConfigSourceCoverage`。
|
||||
|
||||
新增/扩展验证:
|
||||
|
||||
- `verify_sdk_surface.mjs` 覆盖:
|
||||
- `VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS` 导出;
|
||||
- `createVirtualHalSimConfigSourceCoverageReport()` 导出;
|
||||
- `axis-foam`、`external-offsets`、`qtdragon-on-abort` 代表目标存在;
|
||||
- 用真实 `wasm-port/tools/source-manifest.txt` 校验所有代表 source files;
|
||||
- sim-config source coverage `complete === true`;
|
||||
- sim-config source coverage `webSimulationSatisfied === true`;
|
||||
- runtime wrapper 可直接读取 sim-config source coverage report;
|
||||
- source compliance report 内嵌 sim-config source coverage;
|
||||
- `real_simulation_page_smoke.html` 覆盖:
|
||||
- 页面 API 暴露 `getVirtualHalSimConfigSourceCoverageReport()`;
|
||||
- diagnostics artifact 中 `virtualHalSimConfigSourceCoverage.complete === true`;
|
||||
- diagnostics artifact 中包含 LinuxCNC foam sim source evidence;
|
||||
- API report 中包含 qtdragon on-abort sim source evidence;
|
||||
- replacement report 内嵌 sim-config source coverage。
|
||||
|
||||
关键验证:
|
||||
|
||||
```text
|
||||
sdk_surface_node_smoke=ok
|
||||
project_release_gate_manifest_node_smoke=ok
|
||||
project_release_artifact_url_workflow_node_smoke=ok
|
||||
project_batch_acceptance_workflow_node_smoke=ok
|
||||
project_batch_acceptance_artifact_node_smoke=ok
|
||||
real_simulation_programs_node_smoke=ok
|
||||
browser_real_simulation_page_smoke=ok
|
||||
```
|
||||
|
||||
语义边界:
|
||||
|
||||
- 本批没有新增 CNC/G-code/interpreter/planner/kinematics 语义;
|
||||
- 本批只把 virtual HAL 仿真覆盖与 vendored LinuxCNC `configs/sim` source evidence
|
||||
建立机器可读关联;
|
||||
- `source-manifest.txt` 校验只证明 source asset 存在和覆盖归属,不把 browser virtual HAL
|
||||
伪装成 Linux kernel hard-realtime ABI;
|
||||
- Web 仿真 HAL 的满足性现在同时可由 runtime capability、source compliance、browser
|
||||
diagnostics artifact 和 sim-config source coverage 四条路径验证。
|
||||
|
||||
下一步工作内容:
|
||||
|
||||
1. 将 `virtualHalSimConfigSourceCoverage` 接入 project release readiness / batch
|
||||
acceptance artifact,使 release gate 直接检查 Web virtual HAL 的 LinuxCNC sim-config
|
||||
来源覆盖。
|
||||
2. 增加 HAL command script fixtures,把典型 `halcmd` 脚本作为 source-derived workflow
|
||||
gate,覆盖 `setp`、`sets`、`net`、`show`、`getp`、`gets`、`loadrt`、`loadusr`、`addf`
|
||||
和 `start/stop`。
|
||||
3. 推进 OPFS/session persistence 与 virtual HAL runtime state 整合,把 HAL pin/signal/param、
|
||||
motion target、machine status、diagnostics artifact 一并纳入可恢复 Web 仿真会话。
|
||||
|
||||
### 四、2026-06-17 Release Artifact Virtual HAL Sim Config Source Gate
|
||||
|
||||
继续按本文件铁律推进:虚拟 HAL 的功能完全来源于 LinuxCNC 源程序,并且虚拟 HAL
|
||||
完全满足 Web 方式数控系统仿真系统。本批完成上一节下一步第 1 项,把
|
||||
`virtualHalSimConfigSourceCoverage` 从 SDK/browser report 接入 release readiness 与 batch
|
||||
acceptance artifact,使 release gate 能直接检查 Web virtual HAL 的 LinuxCNC sim-config
|
||||
来源覆盖。
|
||||
|
||||
新增实质能力:
|
||||
|
||||
- `createProjectReleaseReadinessReport()` 新增:
|
||||
- `virtualHalSimConfigSourceCoverage`;
|
||||
- `virtualHalSimConfigSourceCoverageReady`;
|
||||
- missing reason:`virtual-hal-sim-config-source-coverage`;
|
||||
- summary row:`Virtual HAL sim-config source coverage`;
|
||||
- `createProjectReleaseReadinessArtifactValidation()` 新增强制校验:
|
||||
- artifact 必须携带 `linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report`;
|
||||
- `complete === true`;
|
||||
- `webSimulationSatisfied === true`;
|
||||
- `missingTargets.length === 0`;
|
||||
- `missingCapabilities.length === 0`;
|
||||
- 必须包含 LinuxCNC foam sim source evidence;
|
||||
- 必须包含 qtdragon on-abort sim source evidence;
|
||||
- `createProjectReleaseReadinessArtifactValidationSummaryViewModel()` 新增对应 summary row;
|
||||
- `createProjectBatchAcceptanceWorkflow()` / `createProjectBatchAcceptanceReport()` 新增可选
|
||||
`virtualHalSimConfigSourceCoverage`;
|
||||
- `createProjectBatchAcceptanceReportValidation()` 新增可选 coverage 校验;
|
||||
- `write_project_release_readiness_artifact.mjs` 生成 artifact 时读取
|
||||
`wasm-port/tools/source-manifest.txt`,并写入 manifest-checked coverage report;
|
||||
- `write_project_batch_acceptance_artifact.mjs` 生成 artifact 时写入同一 coverage report,
|
||||
并新增 accepted capability:
|
||||
`virtual-hal-sim-config-source-coverage`。
|
||||
|
||||
新增/扩展验证:
|
||||
|
||||
- SDK surface gate 覆盖:
|
||||
- release readiness 默认缺少 coverage 时不 ready;
|
||||
- 传入 coverage 后 release readiness ready;
|
||||
- release readiness artifact validation 返回
|
||||
`virtualHalSimConfigSourceCoverageReady === true`;
|
||||
- batch acceptance artifact 携带 coverage 后 checklist 为 `4/4 passed`;
|
||||
- release gate manifest gate 覆盖:
|
||||
- summary view-model 显示 virtual HAL sim-config source coverage ready;
|
||||
- artifact URL workflow 读取带 coverage 的 release readiness artifact;
|
||||
- batch acceptance artifact gate 覆盖:
|
||||
- JSON artifact 内含 virtual HAL sim-config source coverage;
|
||||
- source evidence 包含 LinuxCNC foam sim;
|
||||
- checklist 为 `4/4 passed`;
|
||||
- release readiness artifact gate 覆盖:
|
||||
- JSON artifact 内含 virtual HAL sim-config source coverage;
|
||||
- source evidence 包含 LinuxCNC foam sim 与 qtdragon on-abort sim。
|
||||
|
||||
关键验证:
|
||||
|
||||
```text
|
||||
sdk_surface_node_smoke=ok
|
||||
project_release_gate_manifest_node_smoke=ok
|
||||
project_release_artifact_url_workflow_node_smoke=ok
|
||||
project_batch_acceptance_workflow_node_smoke=ok
|
||||
project_batch_acceptance_artifact_node_smoke=ok
|
||||
project_release_readiness_artifact_node_smoke=ok
|
||||
```
|
||||
|
||||
语义边界:
|
||||
|
||||
- 本批没有新增 G-code/interpreter/planner/kinematics 语义;
|
||||
- release artifact 只校验 virtual HAL 仿真来源覆盖,不把 browser virtual HAL 声称为
|
||||
Linux kernel hard-realtime ABI;
|
||||
- Web 仿真 HAL 的 LinuxCNC `configs/sim` 来源覆盖现在成为 release readiness 与 batch
|
||||
acceptance artifact 的一等 gate evidence。
|
||||
|
||||
下一步工作内容:
|
||||
|
||||
1. 增加 HAL command script fixtures,把典型 `halcmd` 脚本作为 source-derived workflow
|
||||
gate,覆盖 `setp`、`sets`、`net`、`show`、`getp`、`gets`、`loadrt`、`loadusr`、`addf`
|
||||
和 `start/stop`。
|
||||
2. 推进 OPFS/session persistence 与 virtual HAL runtime state 整合,把 HAL pin/signal/param、
|
||||
motion target、machine status、diagnostics artifact 一并纳入可恢复 Web 仿真会话。
|
||||
3. 将 browser diagnostics artifact 与 project release readiness artifact 做 URL workflow
|
||||
联动,让 release 页面能直接读取 Web 仿真诊断中的 virtual HAL source coverage。
|
||||
|
||||
### 五、2026-06-17 Virtual HAL Command Script Fixtures
|
||||
|
||||
继续按本文件铁律推进:虚拟 HAL 的功能完全来源于 LinuxCNC 源程序,并且虚拟 HAL
|
||||
完全满足 Web 方式数控系统仿真系统。本批完成上一节下一步第 1 项,把典型 `halcmd`
|
||||
脚本固化为 source-derived fixture report,使 Web 仿真的 HAL command workflow 可由 SDK
|
||||
和 browser diagnostics artifact 双路径验证。
|
||||
|
||||
新增实质能力:
|
||||
|
||||
- 新增 `VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES`:
|
||||
- `pin-signal-net-show`:
|
||||
- `newsig`;
|
||||
- `setp`;
|
||||
- `sets`;
|
||||
- `net`;
|
||||
- `show pin`;
|
||||
- `show sig`;
|
||||
- `getp`;
|
||||
- `gets`;
|
||||
- `load-thread-start-stop`:
|
||||
- `loadrt`;
|
||||
- `loadusr`;
|
||||
- `addf`;
|
||||
- `start`;
|
||||
- `show function`;
|
||||
- `stop`;
|
||||
- 新增 `createVirtualHalCommandScriptFixtureReport()`:
|
||||
- 执行 fixture command script;
|
||||
- 汇总 covered halcmd actions;
|
||||
- 检查 missing actions;
|
||||
- 检查 expected output;
|
||||
- 输出 `webSimulationSatisfied`;
|
||||
- 输出 LinuxCNC halcmd source files;
|
||||
- `createVirtualHalSourceComplianceReport()` 新增内嵌:
|
||||
- `commandScriptFixtures`;
|
||||
- `createVirtualHalSimulationReplacementReport()` 新增内嵌:
|
||||
- `commandScriptFixtures`;
|
||||
- `createLinuxCncVirtualHalRuntime()` 新增:
|
||||
- `getCommandScriptFixtureReport()`;
|
||||
- `createVirtualHalShowRows()` 新增:
|
||||
- `show function`;
|
||||
- `show thread`;
|
||||
- SDK index、SDK README、browser simulation re-export 同步新增该 API;
|
||||
- Browser simulation API 新增:
|
||||
- `getVirtualHalCommandScriptFixtureReport()`;
|
||||
- `exportDiagnosticsArtifact()` 新增:
|
||||
- `virtualHalCommandScriptFixtures`。
|
||||
|
||||
新增/扩展验证:
|
||||
|
||||
- `verify_sdk_surface.mjs` 覆盖:
|
||||
- `VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES` 导出;
|
||||
- `createVirtualHalCommandScriptFixtureReport()` 导出;
|
||||
- fixture report `complete === true`;
|
||||
- fixture report `webSimulationSatisfied === true`;
|
||||
- `missingActions.length === 0`;
|
||||
- `missingFixtures.length === 0`;
|
||||
- source evidence 包含 `linuxcnc/src/hal/utils/halcmd_commands.cc`;
|
||||
- covered actions 覆盖:
|
||||
`setp`、`sets`、`net`、`show`、`getp`、`gets`、`loadrt`、`loadusr`、`addf`、
|
||||
`start`、`stop`;
|
||||
- runtime wrapper 可读取 command fixture report;
|
||||
- replacement report 内嵌 command fixture report;
|
||||
- source compliance report 内嵌 command fixture report;
|
||||
- `real_simulation_page_smoke.html` 覆盖:
|
||||
- 页面 API 暴露 `getVirtualHalCommandScriptFixtureReport()`;
|
||||
- diagnostics artifact 中 `virtualHalCommandScriptFixtures.complete === true`;
|
||||
- diagnostics artifact 中 covered actions 包含 `loadusr`;
|
||||
- API report 包含 `setp`、`stop` 和 LinuxCNC halcmd source evidence;
|
||||
- replacement/source compliance report 内嵌 command fixture report。
|
||||
|
||||
关键验证:
|
||||
|
||||
```text
|
||||
sdk_surface_node_smoke=ok
|
||||
project_release_gate_manifest_node_smoke=ok
|
||||
project_release_artifact_url_workflow_node_smoke=ok
|
||||
project_batch_acceptance_workflow_node_smoke=ok
|
||||
project_batch_acceptance_artifact_node_smoke=ok
|
||||
browser_real_simulation_page_smoke=ok
|
||||
```
|
||||
|
||||
语义边界:
|
||||
|
||||
- 本批没有新增 G-code/interpreter/planner/kinematics 语义;
|
||||
- 本批只把 Web virtual HAL 的 `halcmd` workflow 与 LinuxCNC halcmd source evidence
|
||||
建立可执行 fixture 关联;
|
||||
- `loadrt`、`loadusr`、`addf`、`start`、`stop` 在 browser virtual HAL 中仍是 Web 仿真
|
||||
workflow 的可记录替代层,不声称提供 Linux kernel hard-realtime ABI、外部硬件驱动 ABI
|
||||
或 native HAL module ABI;
|
||||
- Web 仿真 HAL 的满足性现在可由 runtime capability、source compliance、sim-config
|
||||
source coverage、release artifact gate、command script fixture report 和 browser
|
||||
diagnostics artifact 共同验证。
|
||||
|
||||
下一步工作内容:
|
||||
|
||||
1. 推进 OPFS/session persistence 与 virtual HAL runtime state 整合,把 HAL pin/signal/param、
|
||||
motion target、machine status、diagnostics artifact 一并纳入可恢复 Web 仿真会话。
|
||||
2. 将 browser diagnostics artifact 与 project release readiness artifact 做 URL workflow
|
||||
联动,让 release 页面能直接读取 Web 仿真诊断中的 virtual HAL source coverage。
|
||||
3. 扩展 motion controller 仿真测试矩阵,把多轴 target、servo period、distance-to-go、
|
||||
in-position 与 HAL pin feedback 的关系做成 source-derived report/gate。
|
||||
|
||||
### 六、2026-06-17 OPFS Session Virtual HAL Runtime State Persistence
|
||||
|
||||
继续按本文件铁律推进:虚拟 HAL 的功能完全来源于 LinuxCNC 源程序,并且虚拟 HAL
|
||||
完全满足 Web 方式数控系统仿真系统。本批完成上一节下一步第 1 项,把 virtual HAL
|
||||
runtime state 接入 OPFS/session snapshot payload,使 HAL pin/signal/param、motion target、
|
||||
machine status、diagnostics/source reports 能随 Web 仿真会话保存和恢复。
|
||||
|
||||
新增实质能力:
|
||||
|
||||
- `runtime/opfs/snapshot-store.js` 新增:
|
||||
- `createVirtualHalSessionPayload()`;
|
||||
- `restoreVirtualHalStateFromSessionSnapshot()`;
|
||||
- `createMachineSessionSnapshotPayload()` 新增可选:
|
||||
- `virtualHal`;
|
||||
- `virtualHalState`;
|
||||
- `virtualHalOptions`;
|
||||
- virtual HAL session payload 内容包括:
|
||||
- `state`:完整 browser virtual HAL state;
|
||||
- `diagnostics`:simulation replacement report;
|
||||
- `sourceCompliance`:LinuxCNC source compliance report;
|
||||
- `simConfigSourceCoverage`:LinuxCNC `configs/sim` source coverage report;
|
||||
- `commandScriptFixtures`:LinuxCNC halcmd source-derived command fixture report;
|
||||
- snapshot validation 新增 virtual HAL payload 校验:
|
||||
- state source 必须为 `browser-virtual-hal`;
|
||||
- source compliance 必须 complete;
|
||||
- sim-config source coverage 必须 complete;
|
||||
- command script fixtures 必须 complete;
|
||||
- SDK index 与 README 同步导出:
|
||||
- `createVirtualHalSessionPayload()`;
|
||||
- `restoreVirtualHalStateFromSessionSnapshot()`。
|
||||
|
||||
新增/扩展验证:
|
||||
|
||||
- `verify_file_service.mjs` 覆盖:
|
||||
- 通过 virtual halcmd 建立 signal/net/pin/component/thread state;
|
||||
- `createVirtualHalSessionPayload()` 写入 HAL signal/net/loadusr state;
|
||||
- payload 内嵌 source compliance、sim-config source coverage、command script fixtures;
|
||||
- `createMachineSessionSnapshotPayload()` 可携带 virtual HAL payload;
|
||||
- `createSessionSnapshot()` 可封装 virtual HAL session payload;
|
||||
- `restoreVirtualHalStateFromSessionSnapshot()` 可恢复 signal/net/loadusr state;
|
||||
- `saveSessionSnapshot()` / `loadSessionSnapshot()` 可 OPFS roundtrip virtual HAL state;
|
||||
- `ini_panel_smoke.html` 覆盖 browser OPFS:
|
||||
- browser 侧创建 virtual HAL state;
|
||||
- browser 侧保存 virtual HAL session snapshot;
|
||||
- browser 侧恢复 HAL net 与 `loadusr halui` state;
|
||||
- `verify_sdk_surface.mjs` 覆盖:
|
||||
- SDK index/README 导出新 API;
|
||||
- virtual HAL session payload 包含 complete source reports;
|
||||
- session snapshot 可恢复 HAL net。
|
||||
|
||||
关键验证:
|
||||
|
||||
```text
|
||||
opfs_file_service_node_smoke=ok
|
||||
sdk_surface_node_smoke=ok
|
||||
project_release_gate_manifest_node_smoke=ok
|
||||
project_release_artifact_url_workflow_node_smoke=ok
|
||||
project_batch_acceptance_workflow_node_smoke=ok
|
||||
project_batch_acceptance_artifact_node_smoke=ok
|
||||
browser_ini_opfs_smoke=ok
|
||||
browser_ini_control_page_smoke=ok
|
||||
browser_ini_launch_smoke=ok
|
||||
browser_ini_workflow_overview_smoke=ok
|
||||
browser_ini_shell_integration_workflow_smoke=ok
|
||||
browser_real_simulation_page_smoke=ok
|
||||
```
|
||||
|
||||
语义边界:
|
||||
|
||||
- 本批没有新增 G-code/interpreter/planner/kinematics 语义;
|
||||
- OPFS/session snapshot 只保存和恢复 Web virtual HAL 仿真状态与来源报告;
|
||||
- virtual HAL 仍不声称提供 Linux kernel hard-realtime ABI、外部硬件驱动 ABI 或 native
|
||||
HAL module ABI;
|
||||
- Web 仿真 HAL 的满足性现在可以随 OPFS/session snapshot 持久化,并在恢复后继续由
|
||||
source compliance、sim-config source coverage、command script fixture report 验证。
|
||||
|
||||
下一步工作内容:
|
||||
|
||||
1. 将 browser diagnostics artifact 与 project release readiness artifact 做 URL workflow
|
||||
联动,让 release 页面能直接读取 Web 仿真诊断中的 virtual HAL source coverage。
|
||||
2. 扩展 motion controller 仿真测试矩阵,把多轴 target、servo period、distance-to-go、
|
||||
in-position 与 HAL pin feedback 的关系做成 source-derived report/gate。
|
||||
3. 将 virtual HAL session payload 接入 real simulation page 的显式保存/恢复按钮或 API,
|
||||
让用户可直接保存和恢复完整 Web CNC 仿真会话。
|
||||
|
||||
### 七、2026-06-17 Browser Diagnostics Artifact Release URL Workflow
|
||||
|
||||
继续按本文件铁律推进:虚拟 HAL 的功能完全来源于 LinuxCNC 源程序,并且虚拟 HAL
|
||||
完全满足 Web 方式数控系统仿真系统。本批完成上一节下一步第 1 项,把 browser
|
||||
diagnostics artifact 与 project release readiness artifact 做成 URL workflow 联动,使 release
|
||||
工作流可以直接读取 Web 仿真诊断中的 virtual HAL source coverage、source compliance 和
|
||||
halcmd command fixture evidence。
|
||||
|
||||
新增实质能力:
|
||||
|
||||
- `project-release-readiness.js` 新增:
|
||||
- `createProjectReleaseBrowserDiagnosticsArtifactValidation()`;
|
||||
- 对 real browser simulation diagnostics artifact 的 release 级验证;
|
||||
- browser diagnostics artifact validation 覆盖:
|
||||
- `apiName === "real-browser-simulation-diagnostics-artifact"`;
|
||||
- `virtualHal.source === "browser-virtual-hal"`;
|
||||
- `virtualHalSimulationReplacement.replacesHostRuntimeForSimulation === true`;
|
||||
- `virtualHalSourceCompliance.complete === true`;
|
||||
- `virtualHalSimConfigSourceCoverage.complete === true`;
|
||||
- `virtualHalCommandScriptFixtures.complete === true`;
|
||||
- LinuxCNC halcmd source evidence 包含
|
||||
`linuxcnc/src/hal/utils/halcmd_commands.cc`;
|
||||
- LinuxCNC sim config source evidence 包含
|
||||
`linuxcnc/configs/sim/axis/foam/axis_foam.ini` 与
|
||||
`linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc`;
|
||||
- `loadProjectReleaseReadinessArtifactUrlWorkflow()` 新增可选:
|
||||
- `diagnosticsUrl`;
|
||||
- `diagnosticsFetched`;
|
||||
- `diagnosticsHttpStatus`;
|
||||
- `diagnosticsFetchError`;
|
||||
- `diagnosticsValidation`;
|
||||
- URL workflow 现在在传入 `diagnosticsUrl` 时同时验证:
|
||||
- project release readiness artifact;
|
||||
- browser real simulation diagnostics artifact;
|
||||
- diagnostics artifact 中的 virtual HAL source coverage;
|
||||
- URL workflow summary/action-plan 新增 browser diagnostics 状态;
|
||||
- `verify_project_release_gate_manifest.mjs` 同步接受未请求 diagnostics URL 时的
|
||||
`diagnostics-workflow: not requested` 合约;
|
||||
- SDK index 已导出 `createProjectReleaseBrowserDiagnosticsArtifactValidation()`。
|
||||
|
||||
新增/扩展验证:
|
||||
|
||||
- `verify_project_release_artifact_url_workflow.mjs` 覆盖:
|
||||
- 构造 browser diagnostics artifact;
|
||||
- 验证 diagnostics artifact release validation `ready === true`;
|
||||
- URL workflow 同时 fetch release artifact 与 diagnostics artifact;
|
||||
- `diagnosticsFetched === true`;
|
||||
- `diagnosticsHttpStatus === 200`;
|
||||
- `diagnosticsValidation.ready === true`;
|
||||
- summary view model 输出 `browser-diagnostics: ready`;
|
||||
- `verify_sdk_surface.sh` 聚合覆盖:
|
||||
- SDK surface;
|
||||
- release gate manifest;
|
||||
- release artifact URL workflow;
|
||||
- batch acceptance workflow;
|
||||
- batch acceptance artifact;
|
||||
- `verify_real_simulation_browser.sh` 覆盖:
|
||||
- real simulation page diagnostics artifact 仍可导出完整 virtual HAL source reports;
|
||||
- 页面 browser API 未因 release URL workflow 联动发生退化。
|
||||
|
||||
关键验证:
|
||||
|
||||
```text
|
||||
project_release_artifact_url_workflow_node_smoke=ok
|
||||
sdk_surface_node_smoke=ok
|
||||
project_release_gate_manifest_node_smoke=ok
|
||||
project_batch_acceptance_workflow_node_smoke=ok
|
||||
project_batch_acceptance_artifact_node_smoke=ok
|
||||
project_release_readiness_artifact_node_smoke=ok
|
||||
browser_real_simulation_page_smoke=ok
|
||||
```
|
||||
|
||||
语义边界:
|
||||
|
||||
- 本批没有新增 G-code/interpreter/planner/kinematics 语义;
|
||||
- browser diagnostics artifact 只作为 Web virtual HAL 仿真来源与满足性的 release evidence;
|
||||
- virtual HAL 仍不声称提供 Linux kernel hard-realtime ABI、外部硬件驱动 ABI 或 native
|
||||
HAL module ABI;
|
||||
- Web 仿真 HAL 的满足性现在可由 release artifact URL workflow 联动验证,结论仍限定在
|
||||
Web 方式数控系统仿真范围内。
|
||||
|
||||
下一步工作内容:
|
||||
|
||||
1. 扩展 motion controller 仿真测试矩阵,把多轴 target、servo period、distance-to-go、
|
||||
in-position 与 HAL pin feedback 的关系做成 source-derived report/gate。
|
||||
2. 将 virtual HAL session payload 接入 real simulation page 的显式保存/恢复按钮或 API,
|
||||
让用户可直接保存和恢复完整 Web CNC 仿真会话。
|
||||
3. 将 browser diagnostics artifact 的 URL workflow 接入 ini-panel workflow overview 页面,
|
||||
让 release readiness 页面能显示 browser diagnostics validation rows。
|
||||
@@ -7,6 +7,14 @@ import {
|
||||
sessionSnapshotPath,
|
||||
toolTablePath,
|
||||
} from "./path-model.js";
|
||||
import {
|
||||
cloneVirtualHalState,
|
||||
createVirtualHalCommandScriptFixtureReport,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
createVirtualHalSimulationReplacementReport,
|
||||
createVirtualHalSourceComplianceReport,
|
||||
createVirtualHalState,
|
||||
} from "../sdk/src/linuxcnc-hal.js";
|
||||
|
||||
export const SESSION_SNAPSHOT_FORMAT = "linuxcnc-wasm-session-snapshot";
|
||||
export const SESSION_SNAPSHOT_VERSION = 1;
|
||||
@@ -45,9 +53,58 @@ function validateMachineSessionSnapshotPayload(payload, machineId) {
|
||||
requiredSnapshotFilePath(payload.files, "parameters", "machine session parameter OPFS path");
|
||||
requiredSnapshotFilePath(payload.files, "toolTable", "machine session tool table OPFS path");
|
||||
optionalOpfsPath(payload.files.gcode, "machine session G-code OPFS path");
|
||||
if (payload.virtualHal !== undefined) {
|
||||
validateVirtualHalSessionPayload(payload.virtualHal);
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
function validateVirtualHalSessionPayload(virtualHal) {
|
||||
assertPlainObject(virtualHal, "virtual HAL session payload");
|
||||
assertPlainObject(virtualHal.state, "virtual HAL session state");
|
||||
assertPlainObject(virtualHal.diagnostics, "virtual HAL session diagnostics");
|
||||
assertPlainObject(virtualHal.sourceCompliance, "virtual HAL session source compliance");
|
||||
assertPlainObject(virtualHal.simConfigSourceCoverage, "virtual HAL session sim-config source coverage");
|
||||
assertPlainObject(virtualHal.commandScriptFixtures, "virtual HAL session command script fixtures");
|
||||
if (virtualHal.state.source !== "browser-virtual-hal") {
|
||||
throw new Error(`Unsupported virtual HAL state source: ${virtualHal.state.source}`);
|
||||
}
|
||||
if (virtualHal.sourceCompliance.complete !== true) {
|
||||
throw new Error("virtual HAL source compliance is incomplete.");
|
||||
}
|
||||
if (virtualHal.simConfigSourceCoverage.complete !== true) {
|
||||
throw new Error("virtual HAL sim-config source coverage is incomplete.");
|
||||
}
|
||||
if (virtualHal.commandScriptFixtures.complete !== true) {
|
||||
throw new Error("virtual HAL command script fixtures are incomplete.");
|
||||
}
|
||||
return virtualHal;
|
||||
}
|
||||
|
||||
export function createVirtualHalSessionPayload(halState = createVirtualHalState(), options = {}) {
|
||||
const state = cloneVirtualHalState(halState);
|
||||
const diagnostics = options.diagnostics ?? createVirtualHalSimulationReplacementReport(state);
|
||||
const sourceCompliance = options.sourceCompliance ?? createVirtualHalSourceComplianceReport({ halState: state });
|
||||
const simConfigSourceCoverage = options.simConfigSourceCoverage ?? createVirtualHalSimConfigSourceCoverageReport(options);
|
||||
const commandScriptFixtures = options.commandScriptFixtures ?? createVirtualHalCommandScriptFixtureReport({ ...options, halState: state });
|
||||
return validateVirtualHalSessionPayload({
|
||||
apiName: "linuxcnc-wasm-virtual-hal-session-payload",
|
||||
payloadVersion: 1,
|
||||
source: "browser-virtual-hal",
|
||||
state,
|
||||
diagnostics,
|
||||
sourceCompliance,
|
||||
simConfigSourceCoverage,
|
||||
commandScriptFixtures,
|
||||
});
|
||||
}
|
||||
|
||||
export function restoreVirtualHalStateFromSessionSnapshot(snapshot) {
|
||||
assertPlainObject(snapshot, "session snapshot");
|
||||
const virtualHal = validateVirtualHalSessionPayload(snapshot.payload?.virtualHal);
|
||||
return cloneVirtualHalState(virtualHal.state);
|
||||
}
|
||||
|
||||
export function createMachineSessionSnapshotPayload(machineId, options = {}) {
|
||||
assertPlainObject(options, "machine session snapshot options");
|
||||
const ini = optionalOpfsPath(options.iniOpfsPath, "INI OPFS path") ??
|
||||
@@ -58,6 +115,9 @@ export function createMachineSessionSnapshotPayload(machineId, options = {}) {
|
||||
toolTablePath(machineId, options.toolTableFilename);
|
||||
const gcode = optionalOpfsPath(options.gcodeOpfsPath, "G-code OPFS path") ??
|
||||
(options.gcodeFilename === undefined ? undefined : gcodeProgramPath(options.gcodeFilename));
|
||||
const virtualHal = options.virtualHalState === undefined && options.virtualHal === undefined
|
||||
? undefined
|
||||
: (options.virtualHal ?? createVirtualHalSessionPayload(options.virtualHalState, options.virtualHalOptions ?? {}));
|
||||
|
||||
return {
|
||||
machineId,
|
||||
@@ -67,6 +127,7 @@ export function createMachineSessionSnapshotPayload(machineId, options = {}) {
|
||||
toolTable,
|
||||
...(gcode === undefined ? {} : { gcode }),
|
||||
},
|
||||
...(virtualHal === undefined ? {} : { virtualHal }),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -66,8 +66,11 @@ import {
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanDomReadiness,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlanRenderState,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel,
|
||||
VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES,
|
||||
VIRTUAL_HAL_COVERAGE_STATES,
|
||||
VIRTUAL_HAL_PROJECT_PIN_GROUPS,
|
||||
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
|
||||
VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES,
|
||||
VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS,
|
||||
VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES,
|
||||
VIRTUAL_HAL_SOURCE_FILES,
|
||||
@@ -79,6 +82,7 @@ import {
|
||||
createLinuxCncIniSdk,
|
||||
createLinuxCncInterpSdk,
|
||||
createLinuxCncVirtualHalRuntime,
|
||||
createVirtualHalCommandScriptFixtureReport,
|
||||
createMachineSessionPersistenceDisplayViewModel,
|
||||
createMachineSessionPersistenceRenderState,
|
||||
createMachineSessionPersistenceSummary,
|
||||
@@ -88,8 +92,10 @@ import {
|
||||
createVirtualHalPinInventory,
|
||||
createVirtualHalPinRegistry,
|
||||
createVirtualHalProjectReport,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
createVirtualHalSimulationReplacementReport,
|
||||
createVirtualHalSimulationRuntimeReport,
|
||||
createVirtualHalSourceComplianceReport,
|
||||
createVirtualHalState,
|
||||
createVirtualHalSystemCoverageReport,
|
||||
createVirtualHalWasmBridgeSnapshot,
|
||||
@@ -100,6 +106,7 @@ import {
|
||||
stepVirtualHalMotionController,
|
||||
writeVirtualHalPin,
|
||||
createMachineSessionSnapshotPayload,
|
||||
createVirtualHalSessionPayload,
|
||||
createProjectReleaseGateActionPlan,
|
||||
createProjectReleaseGateExecutionManifest,
|
||||
createProjectReleaseGateExecutionSummaryViewModel,
|
||||
@@ -157,7 +164,9 @@ import {
|
||||
createProjectBatchAcceptanceSummaryViewModel,
|
||||
createProjectBatchAcceptanceWorkflow,
|
||||
loadProjectBatchAcceptanceReportUrlWorkflow,
|
||||
createProjectReleaseBrowserDiagnosticsArtifactValidation,
|
||||
readMachineSessionReadiness,
|
||||
restoreVirtualHalStateFromSessionSnapshot,
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactJsonWorkflowState,
|
||||
@@ -287,10 +296,11 @@ private page state.
|
||||
`readPin()`, `writePin()`, `applyPinUpdates()`, `executeHalCommand()`,
|
||||
`executeHalcmd()`, `stepMotion()`, `stepMotionController()`,
|
||||
`getSimulationRuntimeReport()`, `getSimulationReplacementReport()`,
|
||||
`getIntegrityReport()`, `getPinInventory()`, `getProjectReport()`, and
|
||||
`applyToInterpSdk()`.
|
||||
`getSourceComplianceReport()`, `getIntegrityReport()`, `getPinInventory()`,
|
||||
`getProjectReport()`, and `applyToInterpSdk()`.
|
||||
- `VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES`,
|
||||
`VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS`,
|
||||
`VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES`,
|
||||
`executeVirtualHalCommand()`, `executeVirtualHalcmd()`,
|
||||
`stepVirtualHalMotion()`, `stepVirtualHalMotionController()`,
|
||||
`createVirtualHalSimulationRuntimeReport()`, and
|
||||
@@ -300,6 +310,13 @@ private page state.
|
||||
`sets`, `newsig`, `net`, `show`, `getp`, `gets`, `loadrt`/`loadusr` stubs,
|
||||
`addf`, thread start/stop, and servo-period motion stepping for browser
|
||||
simulation and Node dashboards.
|
||||
- `createVirtualHalSourceComplianceReport()` is the machine-readable gate for
|
||||
the virtual HAL source rule. It maps replacement targets, runtime
|
||||
capabilities, and HAL pin families back to LinuxCNC source files such as
|
||||
`linuxcnc/bin/axis`, `linuxcnc/src/hal/utils/halcmd_commands.cc`,
|
||||
`linuxcnc/src/emc/usr_intf/halui.cc`, `linuxcnc/src/emc/task/taskclass.cc`,
|
||||
and `linuxcnc/src/emc/motion/motion.c`, then reports whether the Web
|
||||
simulation HAL is source-complete.
|
||||
- `createVirtualHalWasmBridgeSnapshot()` returns the pin/value rows that can be
|
||||
applied through the interpreter SDK HAL bridge. It includes AXIS internal pin
|
||||
names such as `jog.x`, full HAL names such as `axisui.jog.x`, task/power
|
||||
|
||||
@@ -3,8 +3,11 @@ export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js";
|
||||
export {
|
||||
VIRTUAL_HAL_AXES,
|
||||
VIRTUAL_HAL_AXISUI_PINS,
|
||||
VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES,
|
||||
VIRTUAL_HAL_COVERAGE_STATES,
|
||||
VIRTUAL_HAL_PROJECT_PIN_GROUPS,
|
||||
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
|
||||
VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES,
|
||||
VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS,
|
||||
VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES,
|
||||
VIRTUAL_HAL_SOURCE_FILES,
|
||||
@@ -15,6 +18,7 @@ export {
|
||||
applyVirtualHalPinUpdates,
|
||||
cloneVirtualHalState,
|
||||
createLinuxCncVirtualHalRuntime,
|
||||
createVirtualHalCommandScriptFixtureReport,
|
||||
createVirtualHalBridgeActionPlan,
|
||||
createVirtualHalBridgeReadiness,
|
||||
createVirtualHalDroState,
|
||||
@@ -24,8 +28,10 @@ export {
|
||||
createVirtualHalPinInventory,
|
||||
createVirtualHalPinRegistry,
|
||||
createVirtualHalProjectReport,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
createVirtualHalSimulationReplacementReport,
|
||||
createVirtualHalSimulationRuntimeReport,
|
||||
createVirtualHalSourceComplianceReport,
|
||||
createVirtualHalState,
|
||||
createVirtualHalSystemCoverageReport,
|
||||
createVirtualHalWasmBridgeSnapshot,
|
||||
@@ -50,6 +56,7 @@ export {
|
||||
createProjectBatchAcceptanceReportValidation,
|
||||
createProjectBatchAcceptanceSummaryViewModel,
|
||||
createProjectBatchAcceptanceWorkflow,
|
||||
createProjectReleaseBrowserDiagnosticsArtifactValidation,
|
||||
createProjectReleaseGateActionPlan,
|
||||
createProjectReleaseGateExecutionManifest,
|
||||
createProjectReleaseGateExecutionSummaryViewModel,
|
||||
@@ -95,8 +102,10 @@ export {
|
||||
export {
|
||||
createMachineSessionSnapshotPayload,
|
||||
createSessionSnapshot,
|
||||
createVirtualHalSessionPayload,
|
||||
loadMachineSessionSnapshot,
|
||||
loadSessionSnapshot,
|
||||
restoreVirtualHalStateFromSessionSnapshot,
|
||||
saveMachineSessionSnapshot,
|
||||
saveSessionSnapshot,
|
||||
validateSessionSnapshot,
|
||||
|
||||
@@ -25,9 +25,102 @@ export const VIRTUAL_HAL_SOURCE_FILES = Object.freeze({
|
||||
motion: "linuxcnc/src/emc/motion/motion.c",
|
||||
motionAxis: "linuxcnc/src/emc/motion/axis.c",
|
||||
motionHoming: "linuxcnc/src/emc/motion/homing.c",
|
||||
halcmd: "linuxcnc/src/hal/utils/halcmd.c",
|
||||
halcmdCommands: "linuxcnc/src/hal/utils/halcmd_commands.cc",
|
||||
halcmdMain: "linuxcnc/src/hal/utils/halcmd_main.c",
|
||||
halcmdBin: "linuxcnc/bin/halcmd",
|
||||
vendoredMotionHeader: "wasm-port/vendor/linuxcnc/src/emc/motion/motion.h",
|
||||
});
|
||||
|
||||
export const VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS = Object.freeze([
|
||||
Object.freeze({
|
||||
id: "axis-foam",
|
||||
label: "AXIS foam sim",
|
||||
sourceFiles: Object.freeze([
|
||||
"linuxcnc/configs/sim/axis/foam/axis_foam.ini",
|
||||
"linuxcnc/configs/sim/axis/foam/foam.ngc",
|
||||
]),
|
||||
capabilities: Object.freeze(["axis-joint-position-feedback", "hal-pin-signal-param-store"]),
|
||||
evidence: "Vendored LinuxCNC AXIS foam sim config covers axis motion pins and simple program execution.",
|
||||
}),
|
||||
Object.freeze({
|
||||
id: "axis-geometry-user-m",
|
||||
label: "AXIS geometry user-M sim",
|
||||
sourceFiles: Object.freeze([
|
||||
"linuxcnc/configs/sim/axis/geometry/xyzc.ini",
|
||||
"linuxcnc/configs/sim/axis/geometry/xyzc.ngc",
|
||||
"linuxcnc/configs/sim/axis/geometry/M110",
|
||||
]),
|
||||
capabilities: Object.freeze(["userspace-load-command-stubs", "halcmd-simulation-replacement"]),
|
||||
evidence: "Vendored LinuxCNC geometry sim config declares USER_M_PATH behavior represented as a simulation boundary.",
|
||||
}),
|
||||
Object.freeze({
|
||||
id: "external-offsets",
|
||||
label: "External offsets sim",
|
||||
sourceFiles: Object.freeze([
|
||||
"linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini",
|
||||
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ini",
|
||||
"linuxcnc/configs/sim/axis/external_offsets/M111",
|
||||
"linuxcnc/configs/sim/axis/external_offsets/dyn_demo.ngc",
|
||||
"linuxcnc/configs/sim/axis/external_offsets/eoffsets.ngc",
|
||||
]),
|
||||
capabilities: Object.freeze(["motion-controller-simulation-replacement", "servo-period-motion-step"]),
|
||||
evidence: "Vendored LinuxCNC external-offset configs exercise motion/HAL pins and deterministic user-M boundary accounting.",
|
||||
}),
|
||||
Object.freeze({
|
||||
id: "gladevcp-probe",
|
||||
label: "GladeVCP probe sim",
|
||||
sourceFiles: Object.freeze([
|
||||
"linuxcnc/configs/sim/axis/gladevcp/gladevcp_panel.ini",
|
||||
"linuxcnc/configs/sim/axis/gladevcp/probe.ngc",
|
||||
"linuxcnc/configs/sim/axis/gladevcp/sim.tbl",
|
||||
]),
|
||||
capabilities: Object.freeze(["interpreter-hal-bridge-snapshot", "hal-pin-signal-param-store"]),
|
||||
evidence: "Vendored LinuxCNC probe sim config covers probe input and tool-table staging without browser-owned CNC semantics.",
|
||||
}),
|
||||
Object.freeze({
|
||||
id: "woodpecker",
|
||||
label: "Woodpecker sim",
|
||||
sourceFiles: Object.freeze([
|
||||
"linuxcnc/configs/sim/woodpecker/woodpecker.ini",
|
||||
"linuxcnc/configs/sim/woodpecker/on_abort.ngc",
|
||||
"linuxcnc/configs/sim/woodpecker/tool.tbl",
|
||||
]),
|
||||
capabilities: Object.freeze(["halcmd-simulation-replacement", "spindle-coolant-tool-status"]),
|
||||
evidence: "Vendored LinuxCNC woodpecker sim config supplies deterministic on-abort and tool-table workflows.",
|
||||
}),
|
||||
Object.freeze({
|
||||
id: "qtdragon-on-abort",
|
||||
label: "QtDragon on-abort sims",
|
||||
sourceFiles: Object.freeze([
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
|
||||
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
|
||||
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
|
||||
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
|
||||
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
|
||||
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc",
|
||||
]),
|
||||
capabilities: Object.freeze(["realtime-hal-simulation-replacement", "axis-joint-position-feedback"]),
|
||||
evidence: "Vendored LinuxCNC QtDragon sim configs provide multi-joint and on-abort machine/session coverage for Web simulation.",
|
||||
}),
|
||||
Object.freeze({
|
||||
id: "vismach-remap-sims",
|
||||
label: "Vismach remap sims",
|
||||
sourceFiles: Object.freeze([
|
||||
"linuxcnc/configs/sim/axis/vismach/melfa-sim/melfa.ini",
|
||||
"linuxcnc/configs/sim/axis/vismach/melfa-sim/example.ngc",
|
||||
"linuxcnc/configs/sim/axis/vismach/melfa-sim/remap_subs/428remap.ngc",
|
||||
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
|
||||
"linuxcnc/configs/sim/axis/vismach/puma/puma_cube.ngc",
|
||||
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc",
|
||||
]),
|
||||
capabilities: Object.freeze(["interpreter-hal-bridge-snapshot", "motion-controller-simulation-replacement"]),
|
||||
evidence: "Vendored LinuxCNC vismach remap configs remain LinuxCNC-owned remap/kinematics assets staged through the WASM boundary.",
|
||||
}),
|
||||
]);
|
||||
|
||||
export const VIRTUAL_HAL_COVERAGE_STATES = Object.freeze({
|
||||
simulated: "simulated",
|
||||
bridgeOnly: "bridge-only",
|
||||
@@ -338,6 +431,157 @@ export const VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS = Object.freeze([
|
||||
"motion-controller",
|
||||
]);
|
||||
|
||||
export const VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES = Object.freeze([
|
||||
Object.freeze({
|
||||
id: "pin-signal-net-show",
|
||||
label: "HAL pin, signal, net, and show workflow",
|
||||
sourceFiles: Object.freeze([
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmd,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdCommands,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdMain,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdBin,
|
||||
]),
|
||||
commands: Object.freeze([
|
||||
"newsig x-pos HAL_FLOAT",
|
||||
"setp axis.x.pos-cmd 2.5",
|
||||
"sets x-pos 1.25",
|
||||
"net x-pos axis.x.pos-cmd joint.0.pos-fb",
|
||||
"show pin axis.x.*",
|
||||
"show sig x-pos",
|
||||
"getp axis.x.pos-cmd",
|
||||
"gets x-pos",
|
||||
]),
|
||||
requiredActions: Object.freeze(["newsig", "setp", "sets", "net", "show", "getp", "gets"]),
|
||||
expectedOutput: Object.freeze(["axis.x.pos-cmd", "x-pos"]),
|
||||
}),
|
||||
Object.freeze({
|
||||
id: "load-thread-start-stop",
|
||||
label: "HAL load, function, and thread workflow",
|
||||
sourceFiles: Object.freeze([
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmd,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdCommands,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdMain,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdBin,
|
||||
]),
|
||||
commands: Object.freeze([
|
||||
"loadrt trivkins",
|
||||
"loadusr halui",
|
||||
"addf motion-controller servo-thread",
|
||||
"start servo-thread",
|
||||
"show function motion-controller",
|
||||
"stop servo-thread",
|
||||
]),
|
||||
requiredActions: Object.freeze(["loadrt", "loadusr", "addf", "start", "show", "stop"]),
|
||||
expectedOutput: Object.freeze(["motion-controller"]),
|
||||
}),
|
||||
]);
|
||||
|
||||
export const VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES = Object.freeze({
|
||||
"realtime-hal-simulation-replacement": Object.freeze({
|
||||
label: "LinuxCNC realtime HAL simulation replacement",
|
||||
sourceFiles: Object.freeze([
|
||||
VIRTUAL_HAL_SOURCE_FILES.axisui,
|
||||
VIRTUAL_HAL_SOURCE_FILES.axisuiScript,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halui,
|
||||
VIRTUAL_HAL_SOURCE_FILES.iocontrol,
|
||||
VIRTUAL_HAL_SOURCE_FILES.motion,
|
||||
VIRTUAL_HAL_SOURCE_FILES.motionAxis,
|
||||
VIRTUAL_HAL_SOURCE_FILES.motionHoming,
|
||||
VIRTUAL_HAL_SOURCE_FILES.vendoredMotionHeader,
|
||||
]),
|
||||
evidence: "Virtual HAL pin families are derived from LinuxCNC AXIS, HALUI, iocontrol, and motion source exports.",
|
||||
}),
|
||||
"halcmd-simulation-replacement": Object.freeze({
|
||||
label: "LinuxCNC halcmd simulation replacement",
|
||||
sourceFiles: Object.freeze([
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmd,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdCommands,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdMain,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdBin,
|
||||
]),
|
||||
evidence: "Virtual halcmd command verbs mirror LinuxCNC halcmd command surface for simulation workflows.",
|
||||
}),
|
||||
"motion-controller-simulation-replacement": Object.freeze({
|
||||
label: "LinuxCNC motion controller simulation replacement",
|
||||
sourceFiles: Object.freeze([
|
||||
VIRTUAL_HAL_SOURCE_FILES.motion,
|
||||
VIRTUAL_HAL_SOURCE_FILES.motionAxis,
|
||||
VIRTUAL_HAL_SOURCE_FILES.motionHoming,
|
||||
VIRTUAL_HAL_SOURCE_FILES.vendoredMotionHeader,
|
||||
]),
|
||||
evidence: "Virtual motion controller state and pins are derived from LinuxCNC motion, axis, homing, and motion header exports.",
|
||||
}),
|
||||
"hal-pin-signal-param-store": Object.freeze({
|
||||
label: "HAL pin/signal/param store",
|
||||
sourceFiles: Object.freeze([
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmd,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdCommands,
|
||||
VIRTUAL_HAL_SOURCE_FILES.axisui,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halui,
|
||||
]),
|
||||
evidence: "HAL object store behavior is constrained to LinuxCNC HAL pin families and halcmd pin/signal/param verbs.",
|
||||
}),
|
||||
"halcmd-setp-sets-net-show-getp-gets": Object.freeze({
|
||||
label: "halcmd setp/sets/net/show/getp/gets verbs",
|
||||
sourceFiles: Object.freeze([
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmd,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdCommands,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdMain,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdBin,
|
||||
]),
|
||||
evidence: "Virtual halcmd supports the LinuxCNC halcmd verbs needed by Web simulation fixtures.",
|
||||
}),
|
||||
"userspace-load-command-stubs": Object.freeze({
|
||||
label: "loadrt/loadusr/addf/start/stop command stubs",
|
||||
sourceFiles: Object.freeze([
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmd,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdCommands,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halcmdMain,
|
||||
]),
|
||||
evidence: "Userspace/realtime load command records follow LinuxCNC halcmd command names while remaining simulation stubs.",
|
||||
}),
|
||||
"servo-period-motion-step": Object.freeze({
|
||||
label: "Servo-period motion step",
|
||||
sourceFiles: Object.freeze([
|
||||
VIRTUAL_HAL_SOURCE_FILES.motion,
|
||||
VIRTUAL_HAL_SOURCE_FILES.motionAxis,
|
||||
VIRTUAL_HAL_SOURCE_FILES.vendoredMotionHeader,
|
||||
]),
|
||||
evidence: "Servo-period stepping exposes LinuxCNC motion pin concepts: target, velocity, distance-to-go, and in-position.",
|
||||
}),
|
||||
"axis-joint-position-feedback": Object.freeze({
|
||||
label: "Axis/joint position feedback",
|
||||
sourceFiles: Object.freeze([
|
||||
VIRTUAL_HAL_SOURCE_FILES.motionAxis,
|
||||
VIRTUAL_HAL_SOURCE_FILES.motion,
|
||||
VIRTUAL_HAL_SOURCE_FILES.motionHoming,
|
||||
]),
|
||||
evidence: "Axis and joint feedback rows are derived from LinuxCNC axis/joint/homing HAL pin families.",
|
||||
}),
|
||||
"spindle-coolant-tool-status": Object.freeze({
|
||||
label: "Spindle, coolant, and tool status",
|
||||
sourceFiles: Object.freeze([
|
||||
VIRTUAL_HAL_SOURCE_FILES.motion,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halui,
|
||||
VIRTUAL_HAL_SOURCE_FILES.iocontrol,
|
||||
]),
|
||||
evidence: "Spindle, coolant, and tool state rows are derived from LinuxCNC motion, HALUI, and iocontrol pin families.",
|
||||
}),
|
||||
"interpreter-hal-bridge-snapshot": Object.freeze({
|
||||
label: "Interpreter HAL bridge snapshot",
|
||||
sourceFiles: Object.freeze([
|
||||
VIRTUAL_HAL_SOURCE_FILES.axisui,
|
||||
VIRTUAL_HAL_SOURCE_FILES.halui,
|
||||
VIRTUAL_HAL_SOURCE_FILES.iocontrol,
|
||||
VIRTUAL_HAL_SOURCE_FILES.motion,
|
||||
VIRTUAL_HAL_SOURCE_FILES.motionAxis,
|
||||
VIRTUAL_HAL_SOURCE_FILES.motionHoming,
|
||||
VIRTUAL_HAL_SOURCE_FILES.vendoredMotionHeader,
|
||||
]),
|
||||
evidence: "Bridge snapshots only serialize source-derived virtual HAL pins and values into the LinuxCNC-backed interpreter HAL adapter.",
|
||||
}),
|
||||
});
|
||||
|
||||
export function createVirtualHalState(overrides = {}) {
|
||||
const base = {
|
||||
apiName: "linuxcnc-wasm-virtual-hal-state",
|
||||
@@ -1777,9 +2021,78 @@ export function executeVirtualHalCommand(halState = createVirtualHalState(), com
|
||||
};
|
||||
}
|
||||
|
||||
export function createVirtualHalCommandScriptFixtureReport(options = {}) {
|
||||
const fixtures = options.fixtures ?? VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES;
|
||||
let state = normalizeVirtualHalState(options.halState ?? createVirtualHalState());
|
||||
const fixtureRows = fixtures.map((fixture) => {
|
||||
const commandText = fixture.commands.join("\n");
|
||||
const result = executeVirtualHalCommand(state, commandText, options);
|
||||
state = result.state;
|
||||
const actions = result.rows.map(({ action }) => action);
|
||||
const missingActions = fixture.requiredActions.filter((action) => !actions.includes(action));
|
||||
const missingOutput = fixture.expectedOutput.filter((token) => !result.output.includes(token));
|
||||
const sourceFiles = [...(fixture.sourceFiles ?? [])];
|
||||
return {
|
||||
id: fixture.id,
|
||||
label: fixture.label,
|
||||
commandText,
|
||||
commandCount: result.commandCount,
|
||||
ok: result.ok === true,
|
||||
sourceDerived: sourceFiles.length > 0,
|
||||
sourceFiles,
|
||||
requiredActions: [...fixture.requiredActions],
|
||||
actions,
|
||||
missingActions,
|
||||
expectedOutput: [...fixture.expectedOutput],
|
||||
missingOutput,
|
||||
output: result.output,
|
||||
rows: result.rows,
|
||||
ready: result.ok === true &&
|
||||
sourceFiles.length > 0 &&
|
||||
missingActions.length === 0 &&
|
||||
missingOutput.length === 0,
|
||||
};
|
||||
});
|
||||
const missingFixtures = fixtureRows
|
||||
.filter((row) => row.ready !== true)
|
||||
.map((row) => row.id);
|
||||
const coveredActions = [...new Set(fixtureRows.flatMap((row) => row.actions))].sort();
|
||||
const requiredActions = [...new Set(fixtures.flatMap((fixture) => fixture.requiredActions))].sort();
|
||||
const missingActions = requiredActions.filter((action) => !coveredActions.includes(action));
|
||||
const complete = fixtureRows.length > 0 &&
|
||||
missingFixtures.length === 0 &&
|
||||
missingActions.length === 0;
|
||||
return {
|
||||
apiName: "linuxcnc-wasm-virtual-hal-command-script-fixture-report",
|
||||
fixtureVersion: 1,
|
||||
source: "linuxcnc-halcmd-source-derived-virtual-hal",
|
||||
phase: complete ? "ready" : "blocked",
|
||||
complete,
|
||||
webSimulationSatisfied: complete,
|
||||
sourceFiles: [...new Set(fixtureRows.flatMap((row) => row.sourceFiles))],
|
||||
requiredActions,
|
||||
coveredActions,
|
||||
missingActions,
|
||||
missingFixtures,
|
||||
fixtureCount: fixtureRows.length,
|
||||
commandCount: fixtureRows.reduce((sum, row) => sum + row.commandCount, 0),
|
||||
finalState: cloneVirtualHalState(state),
|
||||
rows: fixtureRows,
|
||||
summaryRows: [
|
||||
{ id: "fixtures", label: "HAL command fixtures", value: `${fixtureRows.length}` },
|
||||
{ id: "commands", label: "HAL commands", value: `${fixtureRows.reduce((sum, row) => sum + row.commandCount, 0)}` },
|
||||
{ id: "actions", label: "Covered halcmd actions", value: missingActions.length === 0 ? "covered" : `missing ${missingActions.length}` },
|
||||
{ id: "source", label: "LinuxCNC halcmd source", value: complete ? "source-derived" : "incomplete" },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function createVirtualHalSimulationReplacementReport(halState = createVirtualHalState(), options = {}) {
|
||||
const state = normalizeVirtualHalState(halState);
|
||||
const runtime = createVirtualHalSimulationRuntimeReport(state, options);
|
||||
const sourceCompliance = createVirtualHalSourceComplianceReport({ halState: state });
|
||||
const simConfigSourceCoverage = createVirtualHalSimConfigSourceCoverageReport(options);
|
||||
const commandScriptFixtures = createVirtualHalCommandScriptFixtureReport({ ...options, halState: state });
|
||||
const requiredCapabilities = options.requiredCapabilities ?? [
|
||||
"realtime-hal-simulation-replacement",
|
||||
"halcmd-simulation-replacement",
|
||||
@@ -1823,6 +2136,9 @@ export function createVirtualHalSimulationReplacementReport(halState = createVir
|
||||
requiredCapabilities,
|
||||
missingCapabilities,
|
||||
replacements,
|
||||
sourceCompliance,
|
||||
simConfigSourceCoverage,
|
||||
commandScriptFixtures,
|
||||
runtime,
|
||||
rows: [
|
||||
{ id: "realtime-hal", label: "LinuxCNC realtime HAL", value: replacements["linuxcnc-realtime-hal"].ready ? "virtual replacement ready" : "blocked" },
|
||||
@@ -1834,6 +2150,197 @@ export function createVirtualHalSimulationReplacementReport(halState = createVir
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeLinuxCncSourcePath(path) {
|
||||
const clean = String(path ?? "")
|
||||
.replace(/^wasm-port\/vendor\/linuxcnc\//, "linuxcnc/")
|
||||
.replace(/^vendor\/linuxcnc\//, "linuxcnc/")
|
||||
.replace(/^linuxcnc\//, "");
|
||||
const parts = [];
|
||||
for (const part of clean.split("/")) {
|
||||
if (!part || part === ".") {
|
||||
continue;
|
||||
}
|
||||
if (part === "..") {
|
||||
parts.pop();
|
||||
continue;
|
||||
}
|
||||
parts.push(part);
|
||||
}
|
||||
return parts.join("/");
|
||||
}
|
||||
|
||||
function sourceManifestSetFromText(manifestText = "") {
|
||||
return new Set(String(manifestText)
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => line && !line.startsWith("#"))
|
||||
.map(normalizeLinuxCncSourcePath));
|
||||
}
|
||||
|
||||
export function createVirtualHalSimConfigSourceCoverageReport(options = {}) {
|
||||
const targets = options.targets ?? VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS;
|
||||
const manifestProvided = typeof options.manifestText === "string" || Array.isArray(options.manifestEntries);
|
||||
const manifestSet = new Set([
|
||||
...sourceManifestSetFromText(options.manifestText ?? ""),
|
||||
...(options.manifestEntries ?? []).map(normalizeLinuxCncSourcePath),
|
||||
]);
|
||||
const rows = targets.map((target) => {
|
||||
const sourceFiles = [...(target.sourceFiles ?? [])];
|
||||
const normalizedSourceFiles = sourceFiles.map(normalizeLinuxCncSourcePath);
|
||||
const missingManifestFiles = manifestProvided
|
||||
? sourceFiles.filter((file, index) => !manifestSet.has(normalizedSourceFiles[index]))
|
||||
: [];
|
||||
return {
|
||||
id: target.id,
|
||||
label: target.label,
|
||||
source: "linuxcnc/configs/sim",
|
||||
sourceFiles,
|
||||
capabilities: [...(target.capabilities ?? [])],
|
||||
evidence: target.evidence ?? "",
|
||||
sourceDerived: sourceFiles.length > 0 && missingManifestFiles.length === 0,
|
||||
manifestChecked: manifestProvided,
|
||||
missingManifestFiles,
|
||||
};
|
||||
});
|
||||
const missingTargets = rows
|
||||
.filter((row) => !row.sourceDerived)
|
||||
.map((row) => row.id);
|
||||
const coveredCapabilities = [...new Set(rows.flatMap((row) => row.capabilities))].sort();
|
||||
const requiredCapabilities = options.requiredCapabilities ?? [
|
||||
"realtime-hal-simulation-replacement",
|
||||
"halcmd-simulation-replacement",
|
||||
"motion-controller-simulation-replacement",
|
||||
"hal-pin-signal-param-store",
|
||||
"userspace-load-command-stubs",
|
||||
"servo-period-motion-step",
|
||||
"axis-joint-position-feedback",
|
||||
"spindle-coolant-tool-status",
|
||||
"interpreter-hal-bridge-snapshot",
|
||||
];
|
||||
const missingCapabilities = requiredCapabilities.filter((capability) => !coveredCapabilities.includes(capability));
|
||||
const complete = rows.length > 0 && missingTargets.length === 0 && missingCapabilities.length === 0;
|
||||
return {
|
||||
apiName: "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report",
|
||||
coverageVersion: 1,
|
||||
source: "linuxcnc-configs-sim-source-derived-virtual-hal",
|
||||
phase: complete ? "source-covered" : "incomplete",
|
||||
complete,
|
||||
webSimulationSatisfied: complete,
|
||||
manifestChecked: manifestProvided,
|
||||
targetCount: rows.length,
|
||||
sourceFiles: [...new Set(rows.flatMap((row) => row.sourceFiles))],
|
||||
coveredCapabilities,
|
||||
requiredCapabilities,
|
||||
missingCapabilities,
|
||||
missingTargets,
|
||||
rows,
|
||||
summaryRows: [
|
||||
{ id: "targets", label: "LinuxCNC sim config targets", value: `${rows.length}` },
|
||||
{ id: "source-files", label: "Source files", value: `${new Set(rows.flatMap((row) => row.sourceFiles)).size}` },
|
||||
{ id: "capabilities", label: "Covered capabilities", value: missingCapabilities.length === 0 ? "covered" : `missing ${missingCapabilities.length}` },
|
||||
{ id: "manifest", label: "Manifest check", value: manifestProvided ? (missingTargets.length === 0 ? "passed" : "failed") : "not provided" },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function createVirtualHalSourceComplianceReport(options = {}) {
|
||||
const halState = options.halState ?? createVirtualHalState();
|
||||
const snapshot = options.snapshot ?? createVirtualHalWasmBridgeSnapshot(halState);
|
||||
const systemCoverage = options.systemCoverage ?? createVirtualHalSystemCoverageReport({
|
||||
halState,
|
||||
snapshot,
|
||||
});
|
||||
const simConfigSourceCoverage = options.simConfigSourceCoverage ?? createVirtualHalSimConfigSourceCoverageReport(options);
|
||||
const commandScriptFixtures = options.commandScriptFixtures ?? createVirtualHalCommandScriptFixtureReport(options);
|
||||
const allowedSourceFiles = new Set([
|
||||
...Object.values(VIRTUAL_HAL_SOURCE_FILES),
|
||||
...VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles),
|
||||
...VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES.flatMap((fixture) => fixture.sourceFiles),
|
||||
...(options.allowedSourceFiles ?? []),
|
||||
]);
|
||||
const requiredCapabilities = options.requiredCapabilities ?? VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES;
|
||||
const capabilityRows = requiredCapabilities.map((capability) => {
|
||||
const entry = VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES[capability];
|
||||
const sourceFiles = [...(entry?.sourceFiles ?? [])];
|
||||
const missingSource = !entry || sourceFiles.length === 0;
|
||||
const disallowedSourceFiles = sourceFiles.filter((file) => !allowedSourceFiles.has(file));
|
||||
return {
|
||||
id: capability,
|
||||
label: entry?.label ?? capability,
|
||||
capability,
|
||||
sourceFiles,
|
||||
evidence: entry?.evidence ?? "",
|
||||
sourceDerived: !missingSource && disallowedSourceFiles.length === 0,
|
||||
missingSource,
|
||||
disallowedSourceFiles,
|
||||
};
|
||||
});
|
||||
const targetRows = VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS.map((target) => {
|
||||
const targetCapabilities = target === "linuxcnc-realtime-hal"
|
||||
? ["realtime-hal-simulation-replacement", "hal-pin-signal-param-store"]
|
||||
: target === "halcmd"
|
||||
? ["halcmd-simulation-replacement", "halcmd-setp-sets-net-show-getp-gets", "userspace-load-command-stubs"]
|
||||
: ["motion-controller-simulation-replacement", "servo-period-motion-step", "axis-joint-position-feedback"];
|
||||
const rows = capabilityRows.filter((row) => targetCapabilities.includes(row.capability));
|
||||
return {
|
||||
id: target,
|
||||
target,
|
||||
sourceDerived: rows.length > 0 && rows.every((row) => row.sourceDerived),
|
||||
capabilities: targetCapabilities,
|
||||
sourceFiles: [...new Set(rows.flatMap((row) => row.sourceFiles))],
|
||||
};
|
||||
});
|
||||
const missingCapabilitySources = capabilityRows
|
||||
.filter((row) => !row.sourceDerived)
|
||||
.map((row) => row.capability);
|
||||
const missingFamilySources = systemCoverage.missingSourceFamilies ?? [];
|
||||
const complete = missingCapabilitySources.length === 0 &&
|
||||
missingFamilySources.length === 0 &&
|
||||
simConfigSourceCoverage.complete === true &&
|
||||
commandScriptFixtures.complete === true &&
|
||||
systemCoverage.complete === true &&
|
||||
targetRows.every((row) => row.sourceDerived);
|
||||
return {
|
||||
apiName: "linuxcnc-wasm-virtual-hal-source-compliance-report",
|
||||
complianceVersion: 1,
|
||||
source: "linuxcnc-source-derived-virtual-hal",
|
||||
phase: complete ? "source-complete" : "incomplete",
|
||||
complete,
|
||||
webSimulationSatisfied: complete,
|
||||
sourceRule: "Virtual HAL functionality must be derived from LinuxCNC source files, configs, scripts, or LinuxCNC-backed runtime evidence.",
|
||||
replacementTargets: [...VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS],
|
||||
allowedSourceFiles: [...allowedSourceFiles],
|
||||
sourceFiles: [...new Set([
|
||||
...systemCoverage.sourceFiles,
|
||||
...capabilityRows.flatMap((row) => row.sourceFiles),
|
||||
...simConfigSourceCoverage.sourceFiles,
|
||||
...commandScriptFixtures.sourceFiles,
|
||||
])],
|
||||
missingCapabilitySources,
|
||||
missingFamilySources,
|
||||
simConfigSourceCoverage,
|
||||
commandScriptFixtures,
|
||||
targetRows,
|
||||
capabilityRows,
|
||||
familyRows: systemCoverage.families.map((family) => ({
|
||||
id: family.id,
|
||||
label: family.label,
|
||||
coverage: family.coverage,
|
||||
sourceFiles: [...family.sourceFiles],
|
||||
sourceEvidence: family.sourceEvidence,
|
||||
sourceDerived: family.sourceFiles.length > 0,
|
||||
})),
|
||||
rows: [
|
||||
{ id: "targets", label: "Replacement targets", value: targetRows.every((row) => row.sourceDerived) ? "source-derived" : "incomplete" },
|
||||
{ id: "capabilities", label: "Runtime capabilities", value: missingCapabilitySources.length === 0 ? "source-derived" : `missing ${missingCapabilitySources.length}` },
|
||||
{ id: "pin-families", label: "HAL pin families", value: missingFamilySources.length === 0 ? "source-derived" : `missing ${missingFamilySources.length}` },
|
||||
{ id: "sim-configs", label: "LinuxCNC sim configs", value: simConfigSourceCoverage.complete ? "source-derived" : "incomplete" },
|
||||
{ id: "halcmd-fixtures", label: "HAL command script fixtures", value: commandScriptFixtures.complete ? "source-derived" : "incomplete" },
|
||||
{ id: "web-simulation", label: "Web simulation HAL", value: complete ? "satisfied" : "blocked" },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function createVirtualHalSimulationRuntimeReport(halState = createVirtualHalState(), options = {}) {
|
||||
const state = normalizeVirtualHalState(halState);
|
||||
const snapshot = createVirtualHalWasmBridgeSnapshot(state);
|
||||
@@ -1936,6 +2443,15 @@ export function createLinuxCncVirtualHalRuntime(initialState = createVirtualHalS
|
||||
getSimulationReplacementReport(options = {}) {
|
||||
return createVirtualHalSimulationReplacementReport(state, options);
|
||||
},
|
||||
getSourceComplianceReport(options = {}) {
|
||||
return createVirtualHalSourceComplianceReport({ ...options, halState: state });
|
||||
},
|
||||
getSimConfigSourceCoverageReport(options = {}) {
|
||||
return createVirtualHalSimConfigSourceCoverageReport(options);
|
||||
},
|
||||
getCommandScriptFixtureReport(options = {}) {
|
||||
return createVirtualHalCommandScriptFixtureReport({ ...options, halState: state });
|
||||
},
|
||||
getIntegrityReport(options = {}) {
|
||||
return createVirtualHalIntegrityReport({ ...options, halState: state });
|
||||
},
|
||||
@@ -2029,6 +2545,26 @@ function createVirtualHalShowRows(state, kind = "pin", pattern = "") {
|
||||
value: normalizeHalNumericValue(entry.value),
|
||||
}));
|
||||
}
|
||||
if (kind === "funct" || kind === "function") {
|
||||
return (state.hal.functions ?? [])
|
||||
.filter(({ name }) => matches(name))
|
||||
.map((entry) => ({
|
||||
kind: "function",
|
||||
name: entry.name,
|
||||
type: "HAL_FUNCT",
|
||||
value: entry.thread ?? "",
|
||||
}));
|
||||
}
|
||||
if (kind === "thread") {
|
||||
return Object.entries(state.hal.threads ?? {})
|
||||
.filter(([name]) => matches(name))
|
||||
.map(([name, entry]) => ({
|
||||
kind: "thread",
|
||||
name,
|
||||
type: "HAL_THREAD",
|
||||
value: entry.running ? "running" : "stopped",
|
||||
}));
|
||||
}
|
||||
return createVirtualHalWasmBridgeSnapshot(state).values
|
||||
.filter(({ kind: rowKind, name }) => rowKind === "pin" && matches(name))
|
||||
.map((row) => ({
|
||||
|
||||
@@ -88,6 +88,97 @@ const BLOCKED_RUNTIME_FAMILIES = [
|
||||
const PROJECT_RELEASE_READINESS_ARTIFACT_API = "project-release-readiness-report";
|
||||
const PROJECT_RELEASE_READINESS_ARTIFACT_VERSION = 1;
|
||||
|
||||
function isVirtualHalSimConfigSourceCoverageReady(report) {
|
||||
const coverage = objectOrEmpty(report);
|
||||
return coverage.apiName === "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report" &&
|
||||
coverage.complete === true &&
|
||||
coverage.webSimulationSatisfied === true &&
|
||||
arrayOrEmpty(coverage.missingTargets).length === 0 &&
|
||||
arrayOrEmpty(coverage.missingCapabilities).length === 0 &&
|
||||
arrayOrEmpty(coverage.sourceFiles).includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini") &&
|
||||
arrayOrEmpty(coverage.sourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc");
|
||||
}
|
||||
|
||||
export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifact = {}) {
|
||||
const artifactObject = objectOrEmpty(artifact);
|
||||
const sourceCompliance = objectOrEmpty(artifactObject.virtualHalSourceCompliance);
|
||||
const simConfigSourceCoverage = objectOrEmpty(artifactObject.virtualHalSimConfigSourceCoverage);
|
||||
const commandScriptFixtures = objectOrEmpty(artifactObject.virtualHalCommandScriptFixtures);
|
||||
const replacement = objectOrEmpty(artifactObject.virtualHalSimulationReplacement);
|
||||
const virtualHal = objectOrEmpty(artifactObject.virtualHal);
|
||||
const sourceComplianceReady = sourceCompliance.complete === true &&
|
||||
sourceCompliance.webSimulationSatisfied === true &&
|
||||
arrayOrEmpty(sourceCompliance.sourceFiles).includes("linuxcnc/src/hal/utils/halcmd_commands.cc");
|
||||
const simConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(simConfigSourceCoverage);
|
||||
const commandScriptFixturesReady = commandScriptFixtures.complete === true &&
|
||||
commandScriptFixtures.webSimulationSatisfied === true &&
|
||||
arrayOrEmpty(commandScriptFixtures.coveredActions).includes("loadusr") &&
|
||||
arrayOrEmpty(commandScriptFixtures.coveredActions).includes("stop") &&
|
||||
arrayOrEmpty(commandScriptFixtures.sourceFiles).includes("linuxcnc/src/hal/utils/halcmd_commands.cc");
|
||||
const replacementReady = replacement.ready === true &&
|
||||
replacement.replacesHostRuntimeForSimulation === true &&
|
||||
objectOrEmpty(replacement.sourceCompliance).webSimulationSatisfied === true;
|
||||
const virtualHalReady = virtualHal.source === "browser-virtual-hal";
|
||||
const missing = [
|
||||
...(artifactObject.apiName === "real-browser-simulation-diagnostics-artifact" ? [] : ["apiName"]),
|
||||
...(virtualHalReady ? [] : ["virtualHal"]),
|
||||
...(replacementReady ? [] : ["virtualHalSimulationReplacement"]),
|
||||
...(sourceComplianceReady ? [] : ["virtualHalSourceCompliance"]),
|
||||
...(simConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
|
||||
...(commandScriptFixturesReady ? [] : ["virtualHalCommandScriptFixtures"]),
|
||||
];
|
||||
return {
|
||||
apiName: "project-release-browser-diagnostics-artifact-validation",
|
||||
validationVersion: 1,
|
||||
phase: missing.length === 0 ? "ready" : "blocked",
|
||||
ready: missing.length === 0,
|
||||
missing,
|
||||
artifactApiName: artifactObject.apiName ?? null,
|
||||
virtualHalReady,
|
||||
replacementReady,
|
||||
sourceComplianceReady,
|
||||
simConfigSourceCoverageReady,
|
||||
commandScriptFixturesReady,
|
||||
sourceFiles: [...new Set([
|
||||
...arrayOrEmpty(sourceCompliance.sourceFiles),
|
||||
...arrayOrEmpty(simConfigSourceCoverage.sourceFiles),
|
||||
...arrayOrEmpty(commandScriptFixtures.sourceFiles),
|
||||
])],
|
||||
rows: [
|
||||
{
|
||||
id: "artifact",
|
||||
label: "Browser diagnostics artifact",
|
||||
value: artifactObject.apiName ?? "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal",
|
||||
label: "Virtual HAL state",
|
||||
value: virtualHalReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "source-compliance",
|
||||
label: "Virtual HAL source compliance",
|
||||
value: sourceComplianceReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "sim-config-source-coverage",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: simConfigSourceCoverageReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "command-script-fixtures",
|
||||
label: "Virtual HAL command script fixtures",
|
||||
value: commandScriptFixturesReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "missing",
|
||||
label: "Missing diagnostics evidence",
|
||||
value: missing.length > 0 ? missing.join(", ") : "none",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function outputContains(observedOutputs, expectedOutput) {
|
||||
return observedOutputs.some((output) => String(output).includes(expectedOutput));
|
||||
}
|
||||
@@ -470,6 +561,7 @@ export function createProjectBatchAcceptanceWorkflow({
|
||||
capabilities = [],
|
||||
gateResults = {},
|
||||
observedOutputs = [],
|
||||
virtualHalSimConfigSourceCoverage = null,
|
||||
} = {}) {
|
||||
const capabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ capabilities });
|
||||
const gateManifest = createProjectReleaseGateManifest();
|
||||
@@ -480,10 +572,13 @@ export function createProjectBatchAcceptanceWorkflow({
|
||||
});
|
||||
const batchGatePassed = gateResultMatrix.rows
|
||||
.find(({ id }) => id === "project-batch-acceptance")?.passed === true;
|
||||
const ready = capabilityMatrix.ready && batchGatePassed;
|
||||
const virtualHalSimConfigSourceCoverageReady = virtualHalSimConfigSourceCoverage === null ||
|
||||
isVirtualHalSimConfigSourceCoverageReady(virtualHalSimConfigSourceCoverage);
|
||||
const ready = capabilityMatrix.ready && batchGatePassed && virtualHalSimConfigSourceCoverageReady;
|
||||
const missing = [
|
||||
...capabilityMatrix.missing,
|
||||
...(batchGatePassed ? [] : ["project-batch-acceptance"]),
|
||||
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtual-hal-sim-config-source-coverage"]),
|
||||
];
|
||||
|
||||
return {
|
||||
@@ -498,6 +593,8 @@ export function createProjectBatchAcceptanceWorkflow({
|
||||
capabilityMatrix,
|
||||
capabilities: capabilityMatrix.capabilities,
|
||||
rejectedCapabilities: capabilityMatrix.rejectedCapabilities,
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
virtualHalSimConfigSourceCoverageReady,
|
||||
gateResultMatrix,
|
||||
missing,
|
||||
rows: [
|
||||
@@ -516,6 +613,13 @@ export function createProjectBatchAcceptanceWorkflow({
|
||||
label: "Batch acceptance gate",
|
||||
value: batchGatePassed ? "passed" : "unknown",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-source-coverage",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: virtualHalSimConfigSourceCoverage === null
|
||||
? "not required"
|
||||
: (virtualHalSimConfigSourceCoverageReady ? "ready" : "missing"),
|
||||
},
|
||||
{
|
||||
id: "missing",
|
||||
label: "Missing acceptance evidence",
|
||||
@@ -649,6 +753,15 @@ export function createProjectBatchAcceptanceChecklist({
|
||||
detail: workflow?.gateResultMatrix?.rows
|
||||
?.find?.(({ id }) => id === "project-batch-acceptance")?.status ?? "unknown",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-source-coverage",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
status: workflow?.virtualHalSimConfigSourceCoverageReady !== false ? "pass" : "blocked",
|
||||
passed: workflow?.virtualHalSimConfigSourceCoverageReady !== false,
|
||||
detail: workflow?.virtualHalSimConfigSourceCoverage === null
|
||||
? "not required"
|
||||
: (workflow?.virtualHalSimConfigSourceCoverageReady === true ? "ready" : "missing"),
|
||||
},
|
||||
{
|
||||
id: "next-action",
|
||||
label: "Next acceptance action",
|
||||
@@ -680,6 +793,7 @@ export function createProjectBatchAcceptanceReport({
|
||||
capabilities = [],
|
||||
gateResults = {},
|
||||
observedOutputs = [],
|
||||
virtualHalSimConfigSourceCoverage = null,
|
||||
} = {}) {
|
||||
const capabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ capabilities });
|
||||
const workflow = createProjectBatchAcceptanceWorkflow({
|
||||
@@ -687,6 +801,7 @@ export function createProjectBatchAcceptanceReport({
|
||||
capabilities,
|
||||
gateResults,
|
||||
observedOutputs,
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
});
|
||||
const summaryViewModel = createProjectBatchAcceptanceSummaryViewModel(workflow);
|
||||
const actionPlan = createProjectBatchAcceptanceActionPlan(workflow);
|
||||
@@ -701,6 +816,7 @@ export function createProjectBatchAcceptanceReport({
|
||||
missing: [...workflow.missing],
|
||||
capabilityMatrix,
|
||||
workflow,
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
summaryViewModel,
|
||||
actionPlan,
|
||||
checklist,
|
||||
@@ -736,7 +852,12 @@ export function createProjectBatchAcceptanceReportValidation(report = {}) {
|
||||
const summaryViewModel = objectOrEmpty(reportObject.summaryViewModel);
|
||||
const actionPlan = objectOrEmpty(reportObject.actionPlan);
|
||||
const checklist = objectOrEmpty(reportObject.checklist);
|
||||
const virtualHalSimConfigSourceCoverage = objectOrEmpty(reportObject.virtualHalSimConfigSourceCoverage);
|
||||
const rows = arrayOrEmpty(reportObject.rows);
|
||||
const virtualHalSimConfigSourceCoverageRequired = reportObject.virtualHalSimConfigSourceCoverage !== null &&
|
||||
reportObject.virtualHalSimConfigSourceCoverage !== undefined;
|
||||
const virtualHalSimConfigSourceCoverageReady = !virtualHalSimConfigSourceCoverageRequired ||
|
||||
isVirtualHalSimConfigSourceCoverageReady(virtualHalSimConfigSourceCoverage);
|
||||
const missing = [
|
||||
...(reportObject.apiName === "project-batch-acceptance-report" ? [] : ["apiName"]),
|
||||
...(reportObject.reportVersion === 1 ? [] : ["reportVersion"]),
|
||||
@@ -760,7 +881,8 @@ export function createProjectBatchAcceptanceReportValidation(report = {}) {
|
||||
...(checklist.apiName === "project-batch-acceptance-checklist" && checklist.ready === true
|
||||
? []
|
||||
: ["checklist"]),
|
||||
...(rows.find(({ id, value }) => id === "checklist" && value === "3/3 passed")
|
||||
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
|
||||
...(rows.find(({ id, value }) => id === "checklist" && /^([34])\/\1 passed$/.test(value))
|
||||
? []
|
||||
: ["rows.checklist"]),
|
||||
];
|
||||
@@ -795,6 +917,13 @@ export function createProjectBatchAcceptanceReportValidation(report = {}) {
|
||||
label: "Checklist",
|
||||
value: checklist.ready === true ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-source-coverage",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: virtualHalSimConfigSourceCoverageRequired
|
||||
? (virtualHalSimConfigSourceCoverageReady ? "ready" : "missing")
|
||||
: "not required",
|
||||
},
|
||||
{
|
||||
id: "missing",
|
||||
label: "Missing report evidence",
|
||||
@@ -1251,6 +1380,7 @@ export function createProjectReleaseReadinessReport({
|
||||
gateResults = {},
|
||||
observedOutputs = [],
|
||||
simConfigInventory = DEFAULT_SIM_CONFIG_INVENTORY_BASELINE,
|
||||
virtualHalSimConfigSourceCoverage = null,
|
||||
blockedRuntimeFamilies = BLOCKED_RUNTIME_FAMILIES,
|
||||
promotedRuntimeFamilies = [],
|
||||
axisScreenshotArtifacts = [],
|
||||
@@ -1283,13 +1413,21 @@ export function createProjectReleaseReadinessReport({
|
||||
const promotedFamilies = [...promotedRuntimeFamilies];
|
||||
const promotedBlockedFamilies = promotedFamilies.filter((family) => blockedFamilies.includes(family));
|
||||
const inventoryReady = inventory.unexpectedFail === 0;
|
||||
const virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
);
|
||||
const blockedRuntimeReady = promotedBlockedFamilies.length === 0;
|
||||
const axisScreenshotArtifactSummary = createAxisScreenshotArtifactSummary(axisScreenshotArtifacts);
|
||||
const axisScreenshotArtifactsReady = axisScreenshotArtifactSummary.ready;
|
||||
const ready = releaseGatePassed && inventoryReady && blockedRuntimeReady && axisScreenshotArtifactsReady;
|
||||
const ready = releaseGatePassed &&
|
||||
inventoryReady &&
|
||||
virtualHalSimConfigSourceCoverageReady &&
|
||||
blockedRuntimeReady &&
|
||||
axisScreenshotArtifactsReady;
|
||||
const missing = [
|
||||
...(releaseGatePassed ? [] : ["project-release-gate"]),
|
||||
...(inventoryReady ? [] : ["sim-config-inventory"]),
|
||||
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtual-hal-sim-config-source-coverage"]),
|
||||
...(blockedRuntimeReady ? [] : ["blocked-runtime-families"]),
|
||||
...(axisScreenshotArtifactsReady ? [] : ["axis-screenshot-artifacts"]),
|
||||
];
|
||||
@@ -1306,6 +1444,8 @@ export function createProjectReleaseReadinessReport({
|
||||
expectedOutput: "project_release_gate=ok",
|
||||
},
|
||||
simConfigInventory: inventory,
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
virtualHalSimConfigSourceCoverageReady,
|
||||
blockedRuntimeFamilies: blockedFamilies,
|
||||
promotedRuntimeFamilies: promotedFamilies,
|
||||
promotedBlockedFamilies,
|
||||
@@ -1327,6 +1467,11 @@ export function createProjectReleaseReadinessReport({
|
||||
label: "Sim config inventory",
|
||||
value: `unexpected_fail=${inventory.unexpectedFail}`,
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-source-coverage",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "blocked-runtime-families",
|
||||
label: "Blocked runtime families",
|
||||
@@ -1349,6 +1494,7 @@ export function createProjectReleaseReadinessSummaryViewModel(
|
||||
const missing = Array.isArray(report?.missing) ? report.missing : [];
|
||||
const gateResultMatrix = objectOrEmpty(report?.gateResultMatrix);
|
||||
const simConfigInventory = objectOrEmpty(report?.simConfigInventory);
|
||||
const virtualHalSimConfigSourceCoverageReady = report?.virtualHalSimConfigSourceCoverageReady === true;
|
||||
const axisScreenshotArtifactSummary = objectOrEmpty(report?.axisScreenshotArtifactSummary);
|
||||
const blockedRuntimeFamilies = arrayOrEmpty(report?.blockedRuntimeFamilies);
|
||||
const promotedBlockedFamilies = arrayOrEmpty(report?.promotedBlockedFamilies);
|
||||
@@ -1380,6 +1526,11 @@ export function createProjectReleaseReadinessSummaryViewModel(
|
||||
label: "Sim config inventory",
|
||||
value: `unexpected_fail=${simConfigInventory.unexpectedFail ?? "unknown"}`,
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-source-coverage",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "blocked-runtime-families",
|
||||
label: "Blocked runtime families",
|
||||
@@ -1457,6 +1608,11 @@ export function createProjectReleaseReadinessArtifactValidationSummaryViewModel(
|
||||
label: "Gate action plan",
|
||||
value: validation?.gateActionPlanReady === true ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-source-coverage",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: validation?.virtualHalSimConfigSourceCoverageReady === true ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "missing",
|
||||
label: "Missing artifact evidence",
|
||||
@@ -1609,14 +1765,19 @@ export function createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson
|
||||
|
||||
export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl = "",
|
||||
diagnosticsUrl = "",
|
||||
fetchRef = globalThis.fetch,
|
||||
} = {}) {
|
||||
if (!artifactUrl || typeof fetchRef !== "function") {
|
||||
const jsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson: "" });
|
||||
const diagnosticsValidation = diagnosticsUrl
|
||||
? createProjectReleaseBrowserDiagnosticsArtifactValidation({})
|
||||
: null;
|
||||
const missing = [
|
||||
...(artifactUrl ? [] : ["artifact-url"]),
|
||||
...(typeof fetchRef === "function" ? [] : ["fetch"]),
|
||||
...jsonWorkflow.missing,
|
||||
...(diagnosticsUrl ? ["diagnostics-fetch", ...diagnosticsValidation.missing.map((item) => `diagnostics.${item}`)] : []),
|
||||
];
|
||||
return {
|
||||
apiName: "project-release-readiness-artifact-url-workflow",
|
||||
@@ -1624,11 +1785,16 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
phase: "blocked",
|
||||
ready: false,
|
||||
artifactUrl,
|
||||
diagnosticsUrl,
|
||||
fetched: false,
|
||||
diagnosticsFetched: false,
|
||||
httpStatus: null,
|
||||
diagnosticsHttpStatus: null,
|
||||
fetchError: null,
|
||||
diagnosticsFetchError: null,
|
||||
jsonWorkflow,
|
||||
validation: jsonWorkflow.validation,
|
||||
diagnosticsValidation,
|
||||
summaryViewModel: jsonWorkflow.summaryViewModel,
|
||||
actionPlan: jsonWorkflow.actionPlan,
|
||||
missing,
|
||||
@@ -1664,14 +1830,23 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
phase: "blocked",
|
||||
ready: false,
|
||||
artifactUrl,
|
||||
diagnosticsUrl,
|
||||
fetched: false,
|
||||
diagnosticsFetched: false,
|
||||
httpStatus,
|
||||
diagnosticsHttpStatus: null,
|
||||
fetchError,
|
||||
diagnosticsFetchError: null,
|
||||
jsonWorkflow,
|
||||
validation: jsonWorkflow.validation,
|
||||
diagnosticsValidation: diagnosticsUrl ? createProjectReleaseBrowserDiagnosticsArtifactValidation({}) : null,
|
||||
summaryViewModel: jsonWorkflow.summaryViewModel,
|
||||
actionPlan: jsonWorkflow.actionPlan,
|
||||
missing: ["artifact-fetch", ...jsonWorkflow.missing],
|
||||
missing: [
|
||||
"artifact-fetch",
|
||||
...jsonWorkflow.missing,
|
||||
...(diagnosticsUrl ? ["diagnostics-fetch"] : []),
|
||||
],
|
||||
rows: [
|
||||
{
|
||||
id: "fetch",
|
||||
@@ -1694,20 +1869,56 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
|
||||
const artifactJson = await response.text();
|
||||
const jsonWorkflow = createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson });
|
||||
let diagnosticsFetched = false;
|
||||
let diagnosticsHttpStatus = null;
|
||||
let diagnosticsFetchError = null;
|
||||
let diagnosticsValidation = null;
|
||||
const diagnosticsMissing = [];
|
||||
if (diagnosticsUrl) {
|
||||
try {
|
||||
const diagnosticsResponse = await fetchRef(diagnosticsUrl);
|
||||
diagnosticsHttpStatus = diagnosticsResponse?.status ?? null;
|
||||
if (!diagnosticsResponse?.ok) {
|
||||
diagnosticsFetchError = `diagnostics artifact URL fetch failed${diagnosticsHttpStatus ? `: ${diagnosticsHttpStatus}` : ""}`;
|
||||
diagnosticsMissing.push("diagnostics-fetch");
|
||||
diagnosticsValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation({});
|
||||
} else {
|
||||
diagnosticsFetched = true;
|
||||
const diagnosticsArtifact = JSON.parse(await diagnosticsResponse.text());
|
||||
diagnosticsValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation(diagnosticsArtifact);
|
||||
diagnosticsMissing.push(...diagnosticsValidation.missing.map((item) => `diagnostics.${item}`));
|
||||
}
|
||||
} catch (error) {
|
||||
diagnosticsFetchError = error?.message ?? "diagnostics artifact URL fetch error";
|
||||
diagnosticsValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation({});
|
||||
diagnosticsMissing.push("diagnostics-fetch");
|
||||
}
|
||||
}
|
||||
const missing = [
|
||||
...jsonWorkflow.missing,
|
||||
...diagnosticsMissing,
|
||||
];
|
||||
const ready = jsonWorkflow.ready === true &&
|
||||
(!diagnosticsUrl || diagnosticsValidation?.ready === true);
|
||||
return {
|
||||
apiName: "project-release-readiness-artifact-url-workflow",
|
||||
workflowVersion: 1,
|
||||
phase: jsonWorkflow.ready === true ? "ready" : "blocked",
|
||||
ready: jsonWorkflow.ready === true,
|
||||
phase: ready ? "ready" : "blocked",
|
||||
ready,
|
||||
artifactUrl,
|
||||
diagnosticsUrl,
|
||||
fetched: true,
|
||||
diagnosticsFetched,
|
||||
httpStatus,
|
||||
diagnosticsHttpStatus,
|
||||
fetchError: null,
|
||||
diagnosticsFetchError,
|
||||
jsonWorkflow,
|
||||
validation: jsonWorkflow.validation,
|
||||
diagnosticsValidation,
|
||||
summaryViewModel: jsonWorkflow.summaryViewModel,
|
||||
actionPlan: jsonWorkflow.actionPlan,
|
||||
missing: jsonWorkflow.missing,
|
||||
missing,
|
||||
rows: [
|
||||
{
|
||||
id: "fetch",
|
||||
@@ -1719,6 +1930,13 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
label: "Artifact JSON workflow",
|
||||
value: jsonWorkflow.ready === true ? "ready" : "blocked",
|
||||
},
|
||||
{
|
||||
id: "diagnostics-workflow",
|
||||
label: "Browser diagnostics workflow",
|
||||
value: diagnosticsUrl
|
||||
? (diagnosticsValidation?.ready === true ? "ready" : "blocked")
|
||||
: "not requested",
|
||||
},
|
||||
{
|
||||
id: "next-command",
|
||||
label: "Next command",
|
||||
@@ -1734,14 +1952,23 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
phase: "blocked",
|
||||
ready: false,
|
||||
artifactUrl,
|
||||
diagnosticsUrl,
|
||||
fetched: false,
|
||||
diagnosticsFetched: false,
|
||||
httpStatus: null,
|
||||
diagnosticsHttpStatus: null,
|
||||
fetchError: error?.message ?? "artifact URL fetch error",
|
||||
diagnosticsFetchError: null,
|
||||
jsonWorkflow,
|
||||
validation: jsonWorkflow.validation,
|
||||
diagnosticsValidation: diagnosticsUrl ? createProjectReleaseBrowserDiagnosticsArtifactValidation({}) : null,
|
||||
summaryViewModel: jsonWorkflow.summaryViewModel,
|
||||
actionPlan: jsonWorkflow.actionPlan,
|
||||
missing: ["artifact-fetch", ...jsonWorkflow.missing],
|
||||
missing: [
|
||||
"artifact-fetch",
|
||||
...jsonWorkflow.missing,
|
||||
...(diagnosticsUrl ? ["diagnostics-fetch"] : []),
|
||||
],
|
||||
rows: [
|
||||
{
|
||||
id: "fetch",
|
||||
@@ -1802,6 +2029,13 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel
|
||||
label: "Artifact validation",
|
||||
value: workflow?.validation?.ready === true ? "ready" : "blocked",
|
||||
},
|
||||
{
|
||||
id: "browser-diagnostics",
|
||||
label: "Browser diagnostics",
|
||||
value: workflow?.diagnosticsUrl
|
||||
? (workflow?.diagnosticsValidation?.ready === true ? "ready" : "blocked")
|
||||
: "not requested",
|
||||
},
|
||||
{
|
||||
id: "next-command",
|
||||
label: "Next command",
|
||||
@@ -1850,6 +2084,15 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowActionPlan(
|
||||
expectedOutput: null,
|
||||
}]
|
||||
: []),
|
||||
...(missing.includes("diagnostics-fetch")
|
||||
? [{
|
||||
id: "verify-diagnostics-url-fetch",
|
||||
label: "Verify browser diagnostics artifact URL fetch",
|
||||
kind: "fetch",
|
||||
command: null,
|
||||
expectedOutput: null,
|
||||
}]
|
||||
: []),
|
||||
];
|
||||
const commandActions = validationCommands.map((command) => ({
|
||||
id: command.id ?? command.gateId ?? `command-${command.step}`,
|
||||
@@ -1902,6 +2145,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
|
||||
const artifactObject = objectOrEmpty(artifact);
|
||||
const releaseGate = objectOrEmpty(artifactObject.releaseGate);
|
||||
const simConfigInventory = objectOrEmpty(artifactObject.simConfigInventory);
|
||||
const virtualHalSimConfigSourceCoverage = objectOrEmpty(artifactObject.virtualHalSimConfigSourceCoverage);
|
||||
const gateManifest = objectOrEmpty(artifactObject.gateManifest);
|
||||
const gateExecutionManifest = objectOrEmpty(artifactObject.gateExecutionManifest);
|
||||
const gateExecutionSummaryViewModel = objectOrEmpty(artifactObject.gateExecutionSummaryViewModel);
|
||||
@@ -1947,6 +2191,9 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
|
||||
&& gateActionPlan.pendingCount === 0
|
||||
&& gateActionPlan.nextCommand === null
|
||||
&& actionPlanCommands.length === 0;
|
||||
const virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
) && artifactObject.virtualHalSimConfigSourceCoverageReady === true;
|
||||
const axisScreenshotArtifacts = arrayOrEmpty(axisScreenshotArtifactSummary.artifacts);
|
||||
const axisScreenshotSummaryReady = !axisScreenshotArtifactSummary.apiName
|
||||
|| axisScreenshotArtifacts.length === 0
|
||||
@@ -1984,6 +2231,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
|
||||
...(simConfigInventory.passed === 28 ? [] : ["simConfigInventory.passed"]),
|
||||
...(simConfigInventory.skipped === 131 ? [] : ["simConfigInventory.skipped"]),
|
||||
...(simConfigInventory.unexpectedFail === 0 ? [] : ["simConfigInventory.unexpectedFail"]),
|
||||
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
|
||||
...(arrayOrEmpty(artifactObject.blockedRuntimeFamilies).join(",") === BLOCKED_RUNTIME_FAMILIES.join(",")
|
||||
? []
|
||||
: ["blockedRuntimeFamilies"]),
|
||||
@@ -2017,6 +2265,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
|
||||
gateExecutionSummaryReady: executionSummaryReady,
|
||||
gateResultMatrixReady: matrixReady,
|
||||
gateActionPlanReady: actionPlanReady,
|
||||
virtualHalSimConfigSourceCoverageReady,
|
||||
axisScreenshotArtifactSummaryReady: axisScreenshotSummaryReady,
|
||||
axisScreenshotArtifactCount: axisScreenshotArtifacts.length,
|
||||
blockedRuntimeFamilies: arrayOrEmpty(artifactObject.blockedRuntimeFamilies),
|
||||
@@ -2056,6 +2305,11 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
|
||||
label: "Gate action plan",
|
||||
value: actionPlanReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-source-coverage",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
|
||||
},
|
||||
{
|
||||
id: "axis-screenshot-artifacts",
|
||||
label: "AXIS screenshot artifacts",
|
||||
|
||||
@@ -1797,13 +1797,16 @@ M2
|
||||
applyVirtualHalAction as applySharedVirtualHalAction,
|
||||
applyVirtualHalPinUpdates as applySharedVirtualHalPinUpdates,
|
||||
cloneVirtualHalState as cloneSharedVirtualHalState,
|
||||
createVirtualHalCommandScriptFixtureReport as createSharedVirtualHalCommandScriptFixtureReport,
|
||||
createVirtualHalDroState as createSharedVirtualHalDroState,
|
||||
createVirtualHalIntegrityReport as createSharedVirtualHalIntegrityReport,
|
||||
createVirtualHalLimitsHomeState as createSharedVirtualHalLimitsHomeState,
|
||||
createVirtualHalMachineStatusState as createSharedVirtualHalMachineStatusState,
|
||||
createVirtualHalPinRegistry as createSharedVirtualHalPinRegistry,
|
||||
createVirtualHalSimConfigSourceCoverageReport as createSharedVirtualHalSimConfigSourceCoverageReport,
|
||||
createVirtualHalSimulationReplacementReport as createSharedVirtualHalSimulationReplacementReport,
|
||||
createVirtualHalSimulationRuntimeReport as createSharedVirtualHalSimulationRuntimeReport,
|
||||
createVirtualHalSourceComplianceReport as createSharedVirtualHalSourceComplianceReport,
|
||||
createVirtualHalState,
|
||||
executeVirtualHalcmd as executeSharedVirtualHalcmd,
|
||||
readVirtualHalPin as readSharedVirtualHalPin,
|
||||
@@ -2481,6 +2484,18 @@ M2
|
||||
return createSharedVirtualHalSimulationReplacementReport(virtualHalState);
|
||||
}
|
||||
|
||||
function createVirtualHalSourceComplianceReport() {
|
||||
return createSharedVirtualHalSourceComplianceReport({ halState: virtualHalState });
|
||||
}
|
||||
|
||||
function createVirtualHalSimConfigSourceCoverageReport() {
|
||||
return createSharedVirtualHalSimConfigSourceCoverageReport();
|
||||
}
|
||||
|
||||
function createVirtualHalCommandScriptFixtureReport() {
|
||||
return createSharedVirtualHalCommandScriptFixtureReport({ halState: virtualHalState });
|
||||
}
|
||||
|
||||
function renderMachineStatusDom(machineStatus) {
|
||||
const statusMap = {
|
||||
"spindle-state": machineStatus.spindle.state,
|
||||
@@ -2567,6 +2582,9 @@ M2
|
||||
diagnostics: renderDiagnostics(),
|
||||
virtualHal: cloneVirtualHalState(),
|
||||
virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(),
|
||||
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport(),
|
||||
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
|
||||
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport(),
|
||||
statusHistory: getStatusHistory(),
|
||||
mdiHistory: getMdiHistory(),
|
||||
recentPrograms: getRecentPrograms(),
|
||||
@@ -3869,6 +3887,9 @@ M2
|
||||
stepVirtualHalMotionController: (options = {}) => stepVirtualHalMotionController(options),
|
||||
getVirtualHalSimulationRuntimeReport: () => createVirtualHalSimulationRuntimeReport(),
|
||||
getVirtualHalSimulationReplacementReport: () => createVirtualHalSimulationReplacementReport(),
|
||||
getVirtualHalSourceComplianceReport: () => createVirtualHalSourceComplianceReport(),
|
||||
getVirtualHalSimConfigSourceCoverageReport: () => createVirtualHalSimConfigSourceCoverageReport(),
|
||||
getVirtualHalCommandScriptFixtureReport: () => createVirtualHalCommandScriptFixtureReport(),
|
||||
getVirtualRealtimeHalRuntimeReport: () => createVirtualHalSimulationReplacementReport(),
|
||||
getVirtualHalDroState: () => createVirtualHalDroState(),
|
||||
getVirtualHalLimitsHomeState: () => createVirtualHalLimitsHomeState(),
|
||||
|
||||
@@ -24,13 +24,16 @@ export {
|
||||
applyVirtualHalPinUpdates,
|
||||
cloneVirtualHalState,
|
||||
createLinuxCncVirtualHalRuntime,
|
||||
createVirtualHalCommandScriptFixtureReport,
|
||||
createVirtualHalDroState,
|
||||
createVirtualHalIntegrityReport,
|
||||
createVirtualHalLimitsHomeState,
|
||||
createVirtualHalMachineStatusState,
|
||||
createVirtualHalPinRegistry,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
createVirtualHalSimulationReplacementReport,
|
||||
createVirtualHalSimulationRuntimeReport,
|
||||
createVirtualHalSourceComplianceReport,
|
||||
createVirtualHalState,
|
||||
createVirtualHalWasmBridgeSnapshot,
|
||||
executeVirtualHalCommand,
|
||||
|
||||
@@ -18,11 +18,17 @@
|
||||
} from "../../runtime/opfs/path-model.js";
|
||||
import {
|
||||
createMachineSessionSnapshotPayload,
|
||||
createVirtualHalSessionPayload,
|
||||
loadMachineSessionSnapshot,
|
||||
loadSessionSnapshot,
|
||||
restoreVirtualHalStateFromSessionSnapshot,
|
||||
saveMachineSessionSnapshot,
|
||||
saveSessionSnapshot,
|
||||
} from "../../runtime/opfs/snapshot-store.js";
|
||||
import {
|
||||
createVirtualHalState,
|
||||
executeVirtualHalCommand,
|
||||
} from "../../runtime/sdk/src/linuxcnc-hal.js";
|
||||
import {
|
||||
gcodeFilenameFromProgramPath,
|
||||
loadGcodeProgram,
|
||||
@@ -166,6 +172,38 @@ TOOL_TABLE = browser-tool.tbl
|
||||
assertEqual(loadedSnapshot.payload.files.ini, opfsPath, "snapshot payload");
|
||||
assertEqual(loadedSnapshot.payload.files.gcode, snapshotPayload.files.gcode, "snapshot G-code payload");
|
||||
assertEqual(loadedSnapshot.metadata.source, "browser-smoke", "snapshot metadata");
|
||||
const virtualHalState = executeVirtualHalCommand(
|
||||
createVirtualHalState(),
|
||||
"newsig x-pos HAL_FLOAT\nsetp axis.x.pos-cmd 4.5\nsets x-pos 3.25\nnet x-pos axis.x.pos-cmd joint.0.pos-fb\nloadrt trivkins\nloadusr halui\naddf motion-controller servo-thread\nstart servo-thread",
|
||||
).state;
|
||||
const virtualHalSnapshotPayload = createMachineSessionSnapshotPayload("browser-smoke", {
|
||||
gcodeFilename: "browser-smoke.ngc",
|
||||
virtualHal: createVirtualHalSessionPayload(virtualHalState),
|
||||
});
|
||||
assertEqual(
|
||||
virtualHalSnapshotPayload.virtualHal.state.hal.signals["x-pos"].value,
|
||||
3.25,
|
||||
"browser virtual HAL signal snapshot",
|
||||
);
|
||||
const savedVirtualHalSnapshot = await saveSessionSnapshot(
|
||||
"browser-virtual-hal-session",
|
||||
virtualHalSnapshotPayload,
|
||||
{
|
||||
createdAt: "2026-06-08T00:00:00.000Z",
|
||||
metadata: { source: "browser-virtual-hal-smoke" },
|
||||
},
|
||||
);
|
||||
const restoredVirtualHalState = restoreVirtualHalStateFromSessionSnapshot(savedVirtualHalSnapshot);
|
||||
assertEqual(
|
||||
restoredVirtualHalState.hal.nets["x-pos"].includes("joint.0.pos-fb"),
|
||||
true,
|
||||
"browser virtual HAL restored net",
|
||||
);
|
||||
assertEqual(
|
||||
restoredVirtualHalState.hal.loadedComponents.some(({ component }) => component === "halui"),
|
||||
true,
|
||||
"browser virtual HAL restored loadusr",
|
||||
);
|
||||
const customMachineSnapshotPayload = createMachineSessionSnapshotPayload("browser-smoke", {
|
||||
parameterFilename: "browser-linuxcnc.var",
|
||||
toolTableFilename: "browser-tool.tbl",
|
||||
|
||||
@@ -241,6 +241,9 @@
|
||||
!api?.stepVirtualHalMotionController ||
|
||||
!api?.getVirtualHalSimulationRuntimeReport ||
|
||||
!api?.getVirtualHalSimulationReplacementReport ||
|
||||
!api?.getVirtualHalSourceComplianceReport ||
|
||||
!api?.getVirtualHalSimConfigSourceCoverageReport ||
|
||||
!api?.getVirtualHalCommandScriptFixtureReport ||
|
||||
!api?.getVirtualRealtimeHalRuntimeReport
|
||||
) {
|
||||
throw new Error("simulation API missing browser virtual HAL controls");
|
||||
@@ -398,6 +401,14 @@
|
||||
diagnosticsArtifact.statusHistory.length === 0 ||
|
||||
diagnosticsArtifact.virtualHal.source !== "browser-virtual-hal" ||
|
||||
diagnosticsArtifact.virtualHalSimulationReplacement?.ready !== true ||
|
||||
diagnosticsArtifact.virtualHalSimulationReplacement?.sourceCompliance?.webSimulationSatisfied !== true ||
|
||||
diagnosticsArtifact.virtualHalSourceCompliance?.complete !== true ||
|
||||
diagnosticsArtifact.virtualHalSourceCompliance?.webSimulationSatisfied !== true ||
|
||||
!diagnosticsArtifact.virtualHalSourceCompliance?.sourceFiles?.includes("linuxcnc/src/hal/utils/halcmd_commands.cc") ||
|
||||
diagnosticsArtifact.virtualHalSimConfigSourceCoverage?.complete !== true ||
|
||||
!diagnosticsArtifact.virtualHalSimConfigSourceCoverage?.sourceFiles?.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini") ||
|
||||
diagnosticsArtifact.virtualHalCommandScriptFixtures?.complete !== true ||
|
||||
!diagnosticsArtifact.virtualHalCommandScriptFixtures?.coveredActions?.includes("loadusr") ||
|
||||
diagnosticsArtifact.limitsHome.source !== "browser-virtual-hal" ||
|
||||
diagnosticsArtifact.preview.renderer !== "threejs" ||
|
||||
diagnosticsArtifact.toolTable.state !== "not-loaded"
|
||||
@@ -712,6 +723,16 @@
|
||||
) {
|
||||
throw new Error(`AXIS-style virtual halcmd did not execute: ${JSON.stringify(virtualHalcmdResult)}`);
|
||||
}
|
||||
const virtualHalCommandScriptFixtures = api.getVirtualHalCommandScriptFixtureReport();
|
||||
if (
|
||||
virtualHalCommandScriptFixtures.apiName !== "linuxcnc-wasm-virtual-hal-command-script-fixture-report" ||
|
||||
virtualHalCommandScriptFixtures.complete !== true ||
|
||||
!virtualHalCommandScriptFixtures.coveredActions.includes("setp") ||
|
||||
!virtualHalCommandScriptFixtures.coveredActions.includes("stop") ||
|
||||
!virtualHalCommandScriptFixtures.sourceFiles.includes("linuxcnc/src/hal/utils/halcmd_commands.cc")
|
||||
) {
|
||||
throw new Error(`AXIS-style virtual HAL command fixtures did not pass: ${JSON.stringify(virtualHalCommandScriptFixtures)}`);
|
||||
}
|
||||
const motionControllerStep = api.stepVirtualHalMotionController({
|
||||
target: { x: 2.25 },
|
||||
maxVelocity: 10,
|
||||
@@ -732,6 +753,15 @@
|
||||
virtualHalReplacement.replacements["linuxcnc-realtime-hal"].ready !== true ||
|
||||
virtualHalReplacement.replacements.halcmd.ready !== true ||
|
||||
virtualHalReplacement.replacements["motion-controller"].ready !== true ||
|
||||
virtualHalReplacement.sourceCompliance?.complete !== true ||
|
||||
virtualHalReplacement.sourceCompliance?.webSimulationSatisfied !== true ||
|
||||
virtualHalReplacement.simConfigSourceCoverage?.complete !== true ||
|
||||
virtualHalReplacement.commandScriptFixtures?.complete !== true ||
|
||||
api.getVirtualHalSourceComplianceReport().complete !== true ||
|
||||
api.getVirtualHalSourceComplianceReport().commandScriptFixtures?.complete !== true ||
|
||||
!api.getVirtualHalSourceComplianceReport().sourceFiles.includes("linuxcnc/src/emc/motion/motion.c") ||
|
||||
api.getVirtualHalSimConfigSourceCoverageReport().complete !== true ||
|
||||
!api.getVirtualHalSimConfigSourceCoverageReport().sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc") ||
|
||||
api.getVirtualRealtimeHalRuntimeReport().ready !== true ||
|
||||
api.getVirtualHalSimulationRuntimeReport().replacesHostRuntimeForSimulation !== true
|
||||
) {
|
||||
|
||||
@@ -27,6 +27,7 @@ assert.equal(validation.gateExecutionManifestReady, true);
|
||||
assert.equal(validation.gateExecutionSummaryReady, true);
|
||||
assert.equal(validation.gateResultMatrixReady, true);
|
||||
assert.equal(validation.gateActionPlanReady, true);
|
||||
assert.equal(validation.virtualHalSimConfigSourceCoverageReady, true);
|
||||
assert.equal(validation.axisScreenshotArtifactSummaryReady, true);
|
||||
assert.ok(validation.axisScreenshotArtifactCount === 0 || validation.axisScreenshotArtifactCount === 4);
|
||||
assert.deepEqual(validation.expectedGateIds, [
|
||||
@@ -52,6 +53,12 @@ assert.equal(artifact.gateResultMatrix.apiName, "project-release-gate-result-mat
|
||||
assert.equal(artifact.gateActionPlan.apiName, "project-release-gate-action-plan");
|
||||
assert.equal(artifact.gateActionPlan.pendingCount, 0);
|
||||
assert.equal(artifact.gateActionPlan.nextCommand, null);
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverage.apiName, "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report");
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverage.complete, true);
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverage.webSimulationSatisfied, true);
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverageReady, true);
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini"), true);
|
||||
assert.equal(artifact.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc"), true);
|
||||
assert.equal(artifact.axisScreenshotArtifactSummary.apiName, "project-release-axis-screenshot-artifact-summary");
|
||||
if (artifact.axisScreenshotArtifactSummary.artifactCount > 0) {
|
||||
assert.equal(artifact.axisScreenshotArtifactSummary.artifactCount, 4);
|
||||
|
||||
@@ -2,11 +2,15 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFile
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { createProjectReleaseReadinessReport } from "../../runtime/sdk/src/index.js";
|
||||
import {
|
||||
createProjectReleaseReadinessReport,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
} from "../../runtime/sdk/src/index.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "../..");
|
||||
const outputPath = process.argv[2] ?? resolve(root, "build/project-release-readiness.json");
|
||||
const sourceManifestText = readFileSync(resolve(root, "tools/source-manifest.txt"), "utf8");
|
||||
const axisScreenshotArtifactDir = process.env.AXIS_SCREENSHOT_ARTIFACT_DIR
|
||||
? resolve(process.env.AXIS_SCREENSHOT_ARTIFACT_DIR)
|
||||
: null;
|
||||
@@ -60,6 +64,9 @@ const report = createProjectReleaseReadinessReport({
|
||||
"host_wasm_opfs_browser_smokes=ok",
|
||||
"project_release_gate=ok",
|
||||
],
|
||||
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport({
|
||||
manifestText: sourceManifestText,
|
||||
}),
|
||||
axisScreenshotArtifacts: loadAxisScreenshotArtifacts(axisScreenshotArtifactDir),
|
||||
});
|
||||
|
||||
|
||||
@@ -18,12 +18,18 @@ import {
|
||||
import {
|
||||
createMachineSessionSnapshotPayload,
|
||||
createSessionSnapshot,
|
||||
createVirtualHalSessionPayload,
|
||||
loadMachineSessionSnapshot,
|
||||
loadSessionSnapshot,
|
||||
restoreVirtualHalStateFromSessionSnapshot,
|
||||
saveMachineSessionSnapshot,
|
||||
saveSessionSnapshot,
|
||||
validateSessionSnapshot,
|
||||
} from "../../../runtime/opfs/snapshot-store.js";
|
||||
import {
|
||||
createVirtualHalState,
|
||||
executeVirtualHalCommand,
|
||||
} from "../../../runtime/sdk/src/linuxcnc-hal.js";
|
||||
import {
|
||||
gcodeFilenameFromProgramPath,
|
||||
loadGcodeProgram,
|
||||
@@ -247,6 +253,25 @@ assert.deepEqual(snapshotPayload, {
|
||||
gcode: gcodeProgramPath("fixture.ngc"),
|
||||
},
|
||||
});
|
||||
let virtualHalState = createVirtualHalState();
|
||||
virtualHalState = executeVirtualHalCommand(
|
||||
virtualHalState,
|
||||
"newsig x-pos HAL_FLOAT\nsetp axis.x.pos-cmd 3.5\nsets x-pos 2.25\nnet x-pos axis.x.pos-cmd joint.0.pos-fb\nloadrt trivkins\nloadusr halui\naddf motion-controller servo-thread\nstart servo-thread",
|
||||
).state;
|
||||
const virtualHalSessionPayload = createVirtualHalSessionPayload(virtualHalState);
|
||||
assert.equal(virtualHalSessionPayload.apiName, "linuxcnc-wasm-virtual-hal-session-payload");
|
||||
assert.equal(virtualHalSessionPayload.state.source, "browser-virtual-hal");
|
||||
assert.equal(virtualHalSessionPayload.state.hal.signals["x-pos"].value, 2.25);
|
||||
assert.equal(virtualHalSessionPayload.state.hal.nets["x-pos"].includes("axis.x.pos-cmd"), true);
|
||||
assert.equal(virtualHalSessionPayload.sourceCompliance.complete, true);
|
||||
assert.equal(virtualHalSessionPayload.simConfigSourceCoverage.complete, true);
|
||||
assert.equal(virtualHalSessionPayload.commandScriptFixtures.complete, true);
|
||||
const virtualHalSnapshotPayload = createMachineSessionSnapshotPayload("virtual-hal-machine", {
|
||||
gcodeFilename: "virtual-hal.ngc",
|
||||
virtualHal: virtualHalSessionPayload,
|
||||
});
|
||||
assert.equal(virtualHalSnapshotPayload.virtualHal.state.hal.signals["x-pos"].value, 2.25);
|
||||
assert.equal(virtualHalSnapshotPayload.virtualHal.commandScriptFixtures.coveredActions.includes("loadusr"), true);
|
||||
assert.deepEqual(
|
||||
createMachineSessionSnapshotPayload("custom-machine", {
|
||||
parameterFilename: "custom.var",
|
||||
@@ -280,6 +305,13 @@ assert.deepEqual(snapshot, {
|
||||
payload: snapshotPayload,
|
||||
});
|
||||
assert.equal(validateSessionSnapshot(snapshot, "session-1"), snapshot);
|
||||
const virtualHalSessionSnapshot = createSessionSnapshot("virtual-hal-session", virtualHalSnapshotPayload, {
|
||||
createdAt: "2026-06-08T00:00:00.000Z",
|
||||
metadata: { source: "node-virtual-hal-session" },
|
||||
});
|
||||
const restoredVirtualHalState = restoreVirtualHalStateFromSessionSnapshot(virtualHalSessionSnapshot);
|
||||
assert.equal(restoredVirtualHalState.hal.signals["x-pos"].value, 2.25);
|
||||
assert.equal(restoredVirtualHalState.hal.loadedComponents.some(({ component }) => component === "halui"), true);
|
||||
assert.throws(
|
||||
() => validateSessionSnapshot({ ...snapshot, format: "other-format" }, "session-1"),
|
||||
/Unsupported session snapshot format/,
|
||||
@@ -321,6 +353,20 @@ assertSessionSnapshotStoragePath(
|
||||
sessionSnapshotPath("session-1"),
|
||||
);
|
||||
assert.deepEqual(await loadSessionSnapshot("session-1", { storage }), snapshot);
|
||||
await saveSessionSnapshot("virtual-hal-session", virtualHalSnapshotPayload, {
|
||||
storage,
|
||||
createdAt: "2026-06-08T00:00:00.000Z",
|
||||
metadata: { source: "node-virtual-hal-session" },
|
||||
});
|
||||
const loadedVirtualHalSessionSnapshot = await loadSessionSnapshot("virtual-hal-session", { storage });
|
||||
assert.equal(
|
||||
loadedVirtualHalSessionSnapshot.payload.virtualHal.state.hal.signals["x-pos"].value,
|
||||
2.25,
|
||||
);
|
||||
assert.equal(
|
||||
restoreVirtualHalStateFromSessionSnapshot(loadedVirtualHalSessionSnapshot).hal.nets["x-pos"].includes("joint.0.pos-fb"),
|
||||
true,
|
||||
);
|
||||
const customSnapshot = await saveSessionSnapshot("session-1", snapshotPayload, {
|
||||
storage,
|
||||
filename: "custom-snapshot.json",
|
||||
|
||||
@@ -34,12 +34,17 @@ assert.equal(jsonWorkflow.report.apiName, "project-batch-acceptance-report");
|
||||
assert.equal(jsonWorkflow.report.reportVersion, 1);
|
||||
assert.equal(jsonWorkflow.report.ready, true);
|
||||
assert.equal(jsonWorkflow.report.capabilityMatrix.ready, true);
|
||||
assert.deepEqual(jsonWorkflow.report.capabilityMatrix.capabilityTypes, ["workflow", "gate"]);
|
||||
assert.deepEqual(jsonWorkflow.report.capabilityMatrix.capabilityTypes, ["workflow", "api", "gate"]);
|
||||
assert.equal(jsonWorkflow.report.workflow.ready, true);
|
||||
assert.equal(jsonWorkflow.report.workflow.virtualHalSimConfigSourceCoverageReady, true);
|
||||
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.apiName, "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report");
|
||||
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.complete, true);
|
||||
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.webSimulationSatisfied, true);
|
||||
assert.equal(jsonWorkflow.report.virtualHalSimConfigSourceCoverage.sourceFiles.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini"), true);
|
||||
assert.equal(jsonWorkflow.report.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete");
|
||||
assert.equal(jsonWorkflow.report.actionPlan.nextCommand, null);
|
||||
assert.equal(jsonWorkflow.report.checklist.passedCount, 3);
|
||||
assert.equal(jsonWorkflow.report.rows.find(({ id }) => id === "checklist")?.value, "3/3 passed");
|
||||
assert.equal(jsonWorkflow.report.checklist.passedCount, 4);
|
||||
assert.equal(jsonWorkflow.report.rows.find(({ id }) => id === "checklist")?.value, "4/4 passed");
|
||||
assert.equal(validationSummary.apiName, "project-batch-acceptance-report-validation-summary-view-model");
|
||||
assert.equal(validationSummary.statusLine, "Ready: Batch acceptance report evidence complete");
|
||||
assert.equal(validationSummary.rows.find(({ id }) => id === "validation")?.value, "ready");
|
||||
|
||||
@@ -121,6 +121,7 @@ assert.deepEqual(
|
||||
["batch", "batch-acceptance-workflow"],
|
||||
["capabilities", "api, gate"],
|
||||
["project-batch-acceptance", "passed"],
|
||||
["virtual-hal-sim-config-source-coverage", "not required"],
|
||||
["missing", "none"],
|
||||
],
|
||||
);
|
||||
@@ -148,13 +149,14 @@ assert.equal(readyChecklist.checklistVersion, 1);
|
||||
assert.equal(readyChecklist.phase, "ready");
|
||||
assert.equal(readyChecklist.ready, true);
|
||||
assert.equal(readyChecklist.batchId, "batch-acceptance-workflow");
|
||||
assert.equal(readyChecklist.passedCount, 3);
|
||||
assert.equal(readyChecklist.passedCount, 4);
|
||||
assert.equal(readyChecklist.blockedCount, 0);
|
||||
assert.deepEqual(
|
||||
readyChecklist.rows.map(({ id, value }) => [id, value]),
|
||||
[
|
||||
["accepted-capability", "pass: api, gate"],
|
||||
["batch-acceptance-gate", "pass: passed"],
|
||||
["virtual-hal-sim-config-source-coverage", "pass: not required"],
|
||||
["next-action", "pass: none"],
|
||||
],
|
||||
);
|
||||
@@ -174,13 +176,13 @@ assert.equal(readyReport.capabilityMatrix.apiName, "project-batch-acceptance-cap
|
||||
assert.equal(readyReport.workflow.apiName, "project-batch-acceptance-workflow");
|
||||
assert.equal(readyReport.summaryViewModel.statusLine, "Ready: Batch acceptance evidence complete");
|
||||
assert.equal(readyReport.actionPlan.nextCommand, null);
|
||||
assert.equal(readyReport.checklist.passedCount, 3);
|
||||
assert.equal(readyReport.checklist.passedCount, 4);
|
||||
assert.deepEqual(
|
||||
readyReport.rows.map(({ id, value }) => [id, value]),
|
||||
[
|
||||
["summary", "Ready: Batch acceptance evidence complete"],
|
||||
["capabilities", "api, gate"],
|
||||
["checklist", "3/3 passed"],
|
||||
["checklist", "4/4 passed"],
|
||||
["next-command", "none"],
|
||||
],
|
||||
);
|
||||
@@ -205,6 +207,7 @@ assert.deepEqual(
|
||||
["capability-matrix", "ready"],
|
||||
["workflow", "ready"],
|
||||
["checklist", "ready"],
|
||||
["virtual-hal-sim-config-source-coverage", "not required"],
|
||||
["missing", "none"],
|
||||
],
|
||||
);
|
||||
@@ -348,13 +351,14 @@ const blockedChecklist = createProjectBatchAcceptanceChecklist({
|
||||
});
|
||||
assert.equal(blockedChecklist.phase, "blocked");
|
||||
assert.equal(blockedChecklist.ready, false);
|
||||
assert.equal(blockedChecklist.passedCount, 0);
|
||||
assert.equal(blockedChecklist.passedCount, 1);
|
||||
assert.equal(blockedChecklist.blockedCount, 3);
|
||||
assert.deepEqual(
|
||||
blockedChecklist.items.map(({ id, status }) => [id, status]),
|
||||
[
|
||||
["accepted-capability", "blocked"],
|
||||
["batch-acceptance-gate", "blocked"],
|
||||
["virtual-hal-sim-config-source-coverage", "pass"],
|
||||
["next-action", "blocked"],
|
||||
],
|
||||
);
|
||||
|
||||
@@ -1,23 +1,50 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
createProjectReleaseBrowserDiagnosticsArtifactValidation,
|
||||
createProjectReleaseGateManifest,
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowActionPlan,
|
||||
createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createProjectReleaseReadinessReport,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
createVirtualHalCommandScriptFixtureReport,
|
||||
createVirtualHalSimulationReplacementReport,
|
||||
createVirtualHalSourceComplianceReport,
|
||||
createVirtualHalState,
|
||||
loadProjectReleaseReadinessArtifactUrlWorkflow,
|
||||
} from "../../../runtime/sdk/src/index.js";
|
||||
|
||||
const manifest = createProjectReleaseGateManifest();
|
||||
const virtualHalSimConfigSourceCoverage = createVirtualHalSimConfigSourceCoverageReport();
|
||||
const virtualHalState = createVirtualHalState();
|
||||
const browserDiagnosticsArtifact = {
|
||||
apiName: "real-browser-simulation-diagnostics-artifact",
|
||||
artifactVersion: 1,
|
||||
virtualHal: virtualHalState,
|
||||
virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(virtualHalState),
|
||||
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport({ halState: virtualHalState }),
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport({ halState: virtualHalState }),
|
||||
};
|
||||
assert.equal(createProjectReleaseBrowserDiagnosticsArtifactValidation(browserDiagnosticsArtifact).ready, true);
|
||||
const readyArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
}));
|
||||
|
||||
let fetchedUrl = null;
|
||||
const readyWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
artifactUrl: "/project-release-readiness.json",
|
||||
diagnosticsUrl: "/real-browser-simulation-diagnostics.json",
|
||||
fetchRef: async (url) => {
|
||||
fetchedUrl = url;
|
||||
if (url === "/real-browser-simulation-diagnostics.json") {
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
text: async () => JSON.stringify(browserDiagnosticsArtifact),
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
@@ -28,7 +55,7 @@ const readyWorkflow = await loadProjectReleaseReadinessArtifactUrlWorkflow({
|
||||
const readySummary = createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel(readyWorkflow);
|
||||
const readyActionPlan = createProjectReleaseReadinessArtifactUrlWorkflowActionPlan(readyWorkflow);
|
||||
|
||||
assert.equal(fetchedUrl, "/project-release-readiness.json");
|
||||
assert.equal(fetchedUrl, "/real-browser-simulation-diagnostics.json");
|
||||
assert.equal(readyWorkflow.apiName, "project-release-readiness-artifact-url-workflow");
|
||||
assert.equal(readyWorkflow.workflowVersion, 1);
|
||||
assert.equal(readyWorkflow.ready, true);
|
||||
@@ -37,6 +64,9 @@ assert.equal(readyWorkflow.httpStatus, 200);
|
||||
assert.equal(readyWorkflow.fetchError, null);
|
||||
assert.equal(readyWorkflow.jsonWorkflow.ready, true);
|
||||
assert.equal(readyWorkflow.validation.ready, true);
|
||||
assert.equal(readyWorkflow.diagnosticsValidation.ready, true);
|
||||
assert.equal(readyWorkflow.diagnosticsFetched, true);
|
||||
assert.equal(readyWorkflow.diagnosticsHttpStatus, 200);
|
||||
assert.equal(readyWorkflow.actionPlan.ready, true);
|
||||
assert.equal(readySummary.apiName, "project-release-readiness-artifact-url-workflow-summary-view-model");
|
||||
assert.equal(readySummary.statusLine, "Ready: Artifact URL workflow complete");
|
||||
@@ -47,6 +77,7 @@ assert.deepEqual(
|
||||
["fetch", "ready (200)"],
|
||||
["json-workflow", "ready"],
|
||||
["artifact-validation", "ready"],
|
||||
["browser-diagnostics", "ready"],
|
||||
["next-command", "none"],
|
||||
["missing", "none"],
|
||||
],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import {
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
createProjectReleaseGateActionPlan,
|
||||
createProjectReleaseGateExecutionManifest,
|
||||
createProjectReleaseGateExecutionSummaryViewModel,
|
||||
@@ -17,6 +18,7 @@ import {
|
||||
} from "../../../runtime/sdk/src/index.js";
|
||||
|
||||
const manifest = createProjectReleaseGateManifest();
|
||||
const virtualHalSimConfigSourceCoverage = createVirtualHalSimConfigSourceCoverageReport();
|
||||
|
||||
assert.equal(manifest.apiName, "project-release-gate-manifest");
|
||||
assert.equal(manifest.manifestVersion, 1);
|
||||
@@ -201,6 +203,7 @@ assert.match(partialActionPlan.shellScript, /wasm-port\/tests\/host\/verify_proj
|
||||
|
||||
const report = createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
});
|
||||
assert.deepEqual(report.gateManifest, manifest);
|
||||
assert.equal(report.gateExecutionManifest.apiName, "project-release-gate-execution-manifest");
|
||||
@@ -243,6 +246,11 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
|
||||
label: "Sim config inventory",
|
||||
value: "unexpected_fail=0",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-source-coverage",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "blocked-runtime-families",
|
||||
label: "Blocked runtime families",
|
||||
@@ -262,11 +270,12 @@ assert.deepEqual(createProjectReleaseReadinessSummaryViewModel(report), {
|
||||
});
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport()).statusLine,
|
||||
"Waiting: project-release-gate",
|
||||
"Waiting: project-release-gate, virtual-hal-sim-config-source-coverage",
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessSummaryViewModel(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(manifest.gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage,
|
||||
promotedRuntimeFamilies: ["L4-TOOL-DB"],
|
||||
})).rows.find(({ id }) => id === "blocked-runtime-families")?.value,
|
||||
"promoted: L4-TOOL-DB",
|
||||
@@ -308,6 +317,7 @@ assert.deepEqual(
|
||||
["gate-execution-summary", "ready"],
|
||||
["gate-result-matrix", "ready"],
|
||||
["gate-action-plan", "ready"],
|
||||
["virtual-hal-sim-config-source-coverage", "ready"],
|
||||
["missing", "none"],
|
||||
],
|
||||
);
|
||||
@@ -376,6 +386,7 @@ assert.deepEqual(
|
||||
[
|
||||
["fetch", "ready"],
|
||||
["json-workflow", "ready"],
|
||||
["diagnostics-workflow", "not requested"],
|
||||
["next-command", "none"],
|
||||
],
|
||||
);
|
||||
@@ -393,6 +404,7 @@ assert.deepEqual(
|
||||
["fetch", "ready (200)"],
|
||||
["json-workflow", "ready"],
|
||||
["artifact-validation", "ready"],
|
||||
["browser-diagnostics", "not requested"],
|
||||
["next-command", "none"],
|
||||
["missing", "none"],
|
||||
],
|
||||
|
||||
@@ -32,8 +32,11 @@ import {
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan,
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel,
|
||||
VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES,
|
||||
VIRTUAL_HAL_COVERAGE_STATES,
|
||||
VIRTUAL_HAL_PROJECT_PIN_GROUPS,
|
||||
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
|
||||
VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES,
|
||||
VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS,
|
||||
VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES,
|
||||
VIRTUAL_HAL_SOURCE_FILES,
|
||||
@@ -48,6 +51,7 @@ import {
|
||||
createLinuxCncVirtualHalRuntime,
|
||||
createVirtualHalIntegrityReport,
|
||||
createMachineSessionSnapshotPayload,
|
||||
createVirtualHalSessionPayload,
|
||||
createMachineSessionPersistenceDisplayViewModel,
|
||||
createMachineSessionPersistenceRenderState,
|
||||
createMachineSessionPersistenceSummary,
|
||||
@@ -63,6 +67,7 @@ import {
|
||||
createProjectBatchAcceptanceReportValidation,
|
||||
createProjectBatchAcceptanceSummaryViewModel,
|
||||
createProjectBatchAcceptanceWorkflow,
|
||||
createProjectReleaseBrowserDiagnosticsArtifactValidation,
|
||||
createProjectReleaseGateActionPlan,
|
||||
createProjectReleaseGateExecutionManifest,
|
||||
createProjectReleaseGateExecutionSummaryViewModel,
|
||||
@@ -78,11 +83,14 @@ import {
|
||||
createProjectReleaseReadinessSummaryViewModel,
|
||||
createVirtualHalBridgeActionPlan,
|
||||
createVirtualHalBridgeReadiness,
|
||||
createVirtualHalCommandScriptFixtureReport,
|
||||
createVirtualHalPinInventory,
|
||||
createVirtualHalPinRegistry,
|
||||
createVirtualHalProjectReport,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
createVirtualHalSimulationReplacementReport,
|
||||
createVirtualHalState,
|
||||
createVirtualHalSourceComplianceReport,
|
||||
createVirtualHalSystemCoverageReport,
|
||||
createVirtualHalWasmBridgeSnapshot,
|
||||
executeVirtualHalCommand,
|
||||
@@ -146,6 +154,7 @@ import {
|
||||
renderIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactState,
|
||||
restoreMachineParametersFromOpfs,
|
||||
restoreVirtualHalStateFromSessionSnapshot,
|
||||
saveMachineSessionSnapshot,
|
||||
saveMachineTextFiles,
|
||||
saveSessionSnapshot,
|
||||
@@ -160,6 +169,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "../../..");
|
||||
const sdkIndexText = readFileSync(resolve(root, "runtime/sdk/src/index.js"), "utf8");
|
||||
const sdkReadmeText = readFileSync(resolve(root, "runtime/sdk/README.md"), "utf8");
|
||||
const sourceManifestText = readFileSync(resolve(root, "tools/source-manifest.txt"), "utf8");
|
||||
const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md"), "utf8");
|
||||
|
||||
const requiredExports = [
|
||||
@@ -189,6 +199,7 @@ const requiredExports = [
|
||||
["createProjectBatchAcceptanceReportValidation", createProjectBatchAcceptanceReportValidation],
|
||||
["createProjectBatchAcceptanceSummaryViewModel", createProjectBatchAcceptanceSummaryViewModel],
|
||||
["createProjectBatchAcceptanceWorkflow", createProjectBatchAcceptanceWorkflow],
|
||||
["createProjectReleaseBrowserDiagnosticsArtifactValidation", createProjectReleaseBrowserDiagnosticsArtifactValidation],
|
||||
["createProjectReleaseGateActionPlan", createProjectReleaseGateActionPlan],
|
||||
["createProjectReleaseGateExecutionManifest", createProjectReleaseGateExecutionManifest],
|
||||
["createProjectReleaseGateExecutionSummaryViewModel", createProjectReleaseGateExecutionSummaryViewModel],
|
||||
@@ -220,12 +231,15 @@ const requiredExports = [
|
||||
["createLinuxCncVirtualHalRuntime", createLinuxCncVirtualHalRuntime],
|
||||
["createVirtualHalBridgeActionPlan", createVirtualHalBridgeActionPlan],
|
||||
["createVirtualHalBridgeReadiness", createVirtualHalBridgeReadiness],
|
||||
["createVirtualHalCommandScriptFixtureReport", createVirtualHalCommandScriptFixtureReport],
|
||||
["createVirtualHalIntegrityReport", createVirtualHalIntegrityReport],
|
||||
["createVirtualHalPinInventory", createVirtualHalPinInventory],
|
||||
["createVirtualHalPinRegistry", createVirtualHalPinRegistry],
|
||||
["createVirtualHalProjectReport", createVirtualHalProjectReport],
|
||||
["createVirtualHalSimConfigSourceCoverageReport", createVirtualHalSimConfigSourceCoverageReport],
|
||||
["createVirtualHalSimulationReplacementReport", createVirtualHalSimulationReplacementReport],
|
||||
["createVirtualHalSimulationRuntimeReport", createVirtualHalSimulationRuntimeReport],
|
||||
["createVirtualHalSourceComplianceReport", createVirtualHalSourceComplianceReport],
|
||||
["createVirtualHalState", createVirtualHalState],
|
||||
["createVirtualHalSystemCoverageReport", createVirtualHalSystemCoverageReport],
|
||||
["createVirtualHalWasmBridgeSnapshot", createVirtualHalWasmBridgeSnapshot],
|
||||
@@ -359,10 +373,12 @@ const requiredExports = [
|
||||
["defaultMachinePaths", defaultMachinePaths],
|
||||
["normalizeOpfsPath", normalizeOpfsPath],
|
||||
["createMachineSessionSnapshotPayload", createMachineSessionSnapshotPayload],
|
||||
["createVirtualHalSessionPayload", createVirtualHalSessionPayload],
|
||||
["createSessionSnapshot", createSessionSnapshot],
|
||||
["saveSessionSnapshot", saveSessionSnapshot],
|
||||
["saveMachineSessionSnapshot", saveMachineSessionSnapshot],
|
||||
["loadMachineSessionSnapshot", loadMachineSessionSnapshot],
|
||||
["restoreVirtualHalStateFromSessionSnapshot", restoreVirtualHalStateFromSessionSnapshot],
|
||||
["validateSessionSnapshot", validateSessionSnapshot],
|
||||
["machineFilePaths", machineFilePaths],
|
||||
["saveMachineTextFiles", saveMachineTextFiles],
|
||||
@@ -468,14 +484,29 @@ assert.equal(VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES.includes("motion-contro
|
||||
assert.equal(VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS.includes("linuxcnc-realtime-hal"), true);
|
||||
assert.equal(VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS.includes("halcmd"), true);
|
||||
assert.equal(VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS.includes("motion-controller"), true);
|
||||
assert.equal(VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES.some(({ id }) => id === "pin-signal-net-show"), true);
|
||||
assert.equal(VIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES.some(({ id }) => id === "load-thread-start-stop"), true);
|
||||
assert.equal(VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.some(({ id }) => id === "axis-foam"), true);
|
||||
assert.equal(VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.some(({ id }) => id === "qtdragon-on-abort"), true);
|
||||
assert.equal(VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS
|
||||
.find(({ id }) => id === "external-offsets")
|
||||
.sourceFiles.includes("linuxcnc/configs/sim/axis/external_offsets/M111"), true);
|
||||
assert.equal(VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES["halcmd-simulation-replacement"].sourceFiles.includes("linuxcnc/src/hal/utils/halcmd_commands.cc"), true);
|
||||
assert.equal(VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES["motion-controller-simulation-replacement"].sourceFiles.includes("linuxcnc/src/emc/motion/motion.c"), true);
|
||||
assert.equal(VIRTUAL_HAL_COVERAGE_STATES.runtimeBoundary, "runtime-boundary");
|
||||
assert.equal(VIRTUAL_HAL_SOURCE_FILES.halui, "linuxcnc/src/emc/usr_intf/halui.cc");
|
||||
assert.equal(VIRTUAL_HAL_SYSTEM_PIN_FAMILIES.some(({ id }) => id === "iocontrol"), true);
|
||||
assert.equal(VIRTUAL_HAL_SYSTEM_PIN_FAMILIES.some(({ id }) => id === "motion-core"), true);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_WASM_BRIDGE_FUNCTIONS\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_WASM_BRIDGE_FUNCTIONS\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_COMMAND_SCRIPT_FIXTURES\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_PROJECT_PIN_GROUPS\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_PROJECT_PIN_GROUPS\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES\b/);
|
||||
assert.match(sdkReadmeText, /\bVIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES\b/);
|
||||
assert.match(sdkIndexText, /\bVIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS\b/);
|
||||
@@ -486,6 +517,8 @@ assert.match(sdkIndexText, /\bexecuteVirtualHalCommand\b/);
|
||||
assert.match(sdkReadmeText, /\bexecuteVirtualHalCommand\b/);
|
||||
assert.match(sdkIndexText, /\bexecuteVirtualHalcmd\b/);
|
||||
assert.match(sdkReadmeText, /\bexecuteVirtualHalcmd\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalCommandScriptFixtureReport\b/);
|
||||
assert.match(sdkReadmeText, /\bcreateVirtualHalCommandScriptFixtureReport\b/);
|
||||
assert.match(sdkIndexText, /\bstepVirtualHalMotion\b/);
|
||||
assert.match(sdkReadmeText, /\bstepVirtualHalMotion\b/);
|
||||
assert.match(sdkIndexText, /\bstepVirtualHalMotionController\b/);
|
||||
@@ -494,6 +527,10 @@ assert.match(sdkIndexText, /\bcreateVirtualHalSimulationRuntimeReport\b/);
|
||||
assert.match(sdkReadmeText, /\bcreateVirtualHalSimulationRuntimeReport\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalSimulationReplacementReport\b/);
|
||||
assert.match(sdkReadmeText, /\bcreateVirtualHalSimulationReplacementReport\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalSimConfigSourceCoverageReport\b/);
|
||||
assert.match(sdkReadmeText, /\bcreateVirtualHalSimConfigSourceCoverageReport\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalSourceComplianceReport\b/);
|
||||
assert.match(sdkReadmeText, /\bcreateVirtualHalSourceComplianceReport\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalSystemCoverageReport\b/);
|
||||
assert.match(sdkReadmeText, /\bcreateVirtualHalSystemCoverageReport\b/);
|
||||
assert.match(sdkIndexText, /\bcreateVirtualHalPinRegistry\b/);
|
||||
@@ -596,6 +633,17 @@ const directHalcmdAliasResult = executeVirtualHalcmd(virtualHalRuntime.getState(
|
||||
assert.equal(directHalcmdAliasResult.ok, true);
|
||||
assert.equal(directHalcmdAliasResult.apiName, "linuxcnc-wasm-virtual-halcmd-result");
|
||||
assert.match(directHalcmdAliasResult.output, /halui\.machine\.is-on/);
|
||||
const commandScriptFixtureReport = createVirtualHalCommandScriptFixtureReport({ halState: virtualHalRuntime.getState() });
|
||||
assert.equal(commandScriptFixtureReport.apiName, "linuxcnc-wasm-virtual-hal-command-script-fixture-report");
|
||||
assert.equal(commandScriptFixtureReport.complete, true);
|
||||
assert.equal(commandScriptFixtureReport.webSimulationSatisfied, true);
|
||||
assert.equal(commandScriptFixtureReport.missingActions.length, 0);
|
||||
assert.equal(commandScriptFixtureReport.missingFixtures.length, 0);
|
||||
assert.equal(commandScriptFixtureReport.sourceFiles.includes("linuxcnc/src/hal/utils/halcmd_commands.cc"), true);
|
||||
for (const action of ["setp", "sets", "net", "show", "getp", "gets", "loadrt", "loadusr", "addf", "start", "stop"]) {
|
||||
assert.equal(commandScriptFixtureReport.coveredActions.includes(action), true);
|
||||
}
|
||||
assert.equal(virtualHalRuntime.getCommandScriptFixtureReport().complete, true);
|
||||
const steppedVirtualHal = stepVirtualHalMotion(virtualHalRuntime.getState(), {
|
||||
target: { x: 3 },
|
||||
maxVelocity: 10,
|
||||
@@ -636,7 +684,73 @@ assert.equal(replacementReport.replacements["linuxcnc-realtime-hal"].ready, true
|
||||
assert.equal(replacementReport.replacements.halcmd.ready, true);
|
||||
assert.equal(replacementReport.replacements["motion-controller"].ready, true);
|
||||
assert.equal(replacementReport.hardRealtime, false);
|
||||
assert.equal(replacementReport.sourceCompliance.complete, true);
|
||||
assert.equal(replacementReport.sourceCompliance.webSimulationSatisfied, true);
|
||||
assert.equal(replacementReport.simConfigSourceCoverage.complete, true);
|
||||
assert.equal(replacementReport.commandScriptFixtures.complete, true);
|
||||
assert.equal(virtualHalRuntime.getSimulationReplacementReport().ready, true);
|
||||
const simConfigSourceCoverageReport = createVirtualHalSimConfigSourceCoverageReport({ manifestText: sourceManifestText });
|
||||
assert.equal(simConfigSourceCoverageReport.apiName, "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report");
|
||||
assert.equal(simConfigSourceCoverageReport.complete, true);
|
||||
assert.equal(simConfigSourceCoverageReport.webSimulationSatisfied, true);
|
||||
assert.equal(simConfigSourceCoverageReport.manifestChecked, true);
|
||||
assert.equal(simConfigSourceCoverageReport.missingTargets.length, 0);
|
||||
assert.equal(simConfigSourceCoverageReport.missingCapabilities.length, 0);
|
||||
assert.equal(simConfigSourceCoverageReport.sourceFiles.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini"), true);
|
||||
assert.equal(simConfigSourceCoverageReport.sourceFiles.includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc"), true);
|
||||
assert.equal(simConfigSourceCoverageReport.rows.every(({ sourceDerived }) => sourceDerived), true);
|
||||
assert.equal(virtualHalRuntime.getSimConfigSourceCoverageReport({ manifestText: sourceManifestText }).complete, true);
|
||||
const sourceComplianceReport = createVirtualHalSourceComplianceReport({ halState: virtualHalRuntime.getState() });
|
||||
assert.equal(sourceComplianceReport.apiName, "linuxcnc-wasm-virtual-hal-source-compliance-report");
|
||||
assert.equal(sourceComplianceReport.complete, true);
|
||||
assert.equal(sourceComplianceReport.webSimulationSatisfied, true);
|
||||
assert.equal(sourceComplianceReport.missingCapabilitySources.length, 0);
|
||||
assert.equal(sourceComplianceReport.missingFamilySources.length, 0);
|
||||
assert.equal(sourceComplianceReport.targetRows.every(({ sourceDerived }) => sourceDerived), true);
|
||||
assert.equal(sourceComplianceReport.capabilityRows.every(({ sourceDerived }) => sourceDerived), true);
|
||||
assert.equal(sourceComplianceReport.familyRows.every(({ sourceDerived }) => sourceDerived), true);
|
||||
assert.equal(sourceComplianceReport.sourceFiles.includes("linuxcnc/bin/axis"), true);
|
||||
assert.equal(sourceComplianceReport.sourceFiles.includes("linuxcnc/src/hal/utils/halcmd_commands.cc"), true);
|
||||
assert.equal(sourceComplianceReport.sourceFiles.includes("linuxcnc/src/emc/motion/motion.c"), true);
|
||||
assert.equal(sourceComplianceReport.sourceFiles.includes("linuxcnc/configs/sim/axis/foam/axis_foam.ini"), true);
|
||||
assert.equal(sourceComplianceReport.simConfigSourceCoverage.complete, true);
|
||||
assert.equal(sourceComplianceReport.commandScriptFixtures.complete, true);
|
||||
assert.equal(virtualHalRuntime.getSourceComplianceReport().complete, true);
|
||||
const virtualHalSessionPayload = createVirtualHalSessionPayload(virtualHalRuntime.getState(), {
|
||||
simConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
commandScriptFixtures: commandScriptFixtureReport,
|
||||
});
|
||||
assert.equal(virtualHalSessionPayload.apiName, "linuxcnc-wasm-virtual-hal-session-payload");
|
||||
assert.equal(virtualHalSessionPayload.state.hal.signals["x-pos"].value, 1.25);
|
||||
assert.equal(virtualHalSessionPayload.sourceCompliance.complete, true);
|
||||
assert.equal(virtualHalSessionPayload.simConfigSourceCoverage.complete, true);
|
||||
assert.equal(virtualHalSessionPayload.commandScriptFixtures.complete, true);
|
||||
const virtualHalSessionSnapshot = createSessionSnapshot(
|
||||
"sdk-virtual-hal-session",
|
||||
createMachineSessionSnapshotPayload("sdk-mill", {
|
||||
gcodeFilename: "demo.ngc",
|
||||
virtualHal: virtualHalSessionPayload,
|
||||
}),
|
||||
{ createdAt: "2026-06-17T00:00:00.000Z" },
|
||||
);
|
||||
assert.equal(
|
||||
restoreVirtualHalStateFromSessionSnapshot(virtualHalSessionSnapshot).hal.nets["x-pos"].includes("axis.x.pos-cmd"),
|
||||
true,
|
||||
);
|
||||
const browserDiagnosticsArtifactValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation({
|
||||
apiName: "real-browser-simulation-diagnostics-artifact",
|
||||
virtualHal: virtualHalRuntime.getState(),
|
||||
virtualHalSimulationReplacement: replacementReport,
|
||||
virtualHalSourceCompliance: sourceComplianceReport,
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
virtualHalCommandScriptFixtures: commandScriptFixtureReport,
|
||||
});
|
||||
assert.equal(browserDiagnosticsArtifactValidation.apiName, "project-release-browser-diagnostics-artifact-validation");
|
||||
assert.equal(browserDiagnosticsArtifactValidation.ready, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.sourceComplianceReady, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.simConfigSourceCoverageReady, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.commandScriptFixturesReady, true);
|
||||
assert.equal(browserDiagnosticsArtifactValidation.sourceFiles.includes("linuxcnc/src/hal/utils/halcmd_commands.cc"), true);
|
||||
const runtimeProjectReport = virtualHalRuntime.getProjectReport({ availableFunctions: VIRTUAL_HAL_WASM_BRIDGE_FUNCTIONS });
|
||||
assert.equal(runtimeProjectReport.simulationRuntime.replacesHostRuntimeForSimulation, true);
|
||||
assert.equal(runtimeProjectReport.systemCoverage.unclassifiedSnapshotPins.length, 0);
|
||||
@@ -705,14 +819,17 @@ assert.equal(
|
||||
batchId: "sdk-surface-batch",
|
||||
capabilities: batchAcceptanceWorkflow.capabilities,
|
||||
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
}).rows.find(({ id }) => id === "checklist")?.value,
|
||||
"3/3 passed",
|
||||
"4/4 passed",
|
||||
);
|
||||
const batchAcceptanceReport = createProjectBatchAcceptanceReport({
|
||||
batchId: "sdk-surface-batch",
|
||||
capabilities: batchAcceptanceWorkflow.capabilities,
|
||||
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
});
|
||||
assert.equal(batchAcceptanceReport.virtualHalSimConfigSourceCoverage.complete, true);
|
||||
assert.equal(createProjectBatchAcceptanceReportValidation(batchAcceptanceReport).ready, true);
|
||||
assert.equal(
|
||||
createProjectBatchAcceptanceReportValidationSummaryViewModel(
|
||||
@@ -766,7 +883,7 @@ assert.equal(
|
||||
);
|
||||
assert.equal(releaseReadinessWaiting.apiName, "project-release-readiness-report");
|
||||
assert.equal(releaseReadinessWaiting.ready, false);
|
||||
assert.deepEqual(releaseReadinessWaiting.missing, ["project-release-gate"]);
|
||||
assert.deepEqual(releaseReadinessWaiting.missing, ["project-release-gate", "virtual-hal-sim-config-source-coverage"]);
|
||||
assert.equal(releaseReadinessWaiting.simConfigInventory.unexpectedFail, 0);
|
||||
assert.deepEqual(releaseReadinessWaiting.promotedBlockedFamilies, []);
|
||||
assert.equal(releaseReadinessWaiting.gateActionPlan.apiName, "project-release-gate-action-plan");
|
||||
@@ -778,9 +895,11 @@ assert.equal(
|
||||
);
|
||||
const releaseReadinessReady = createProjectReleaseReadinessReport({
|
||||
observedOutputs: ["project_release_gate=ok"],
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
});
|
||||
assert.equal(releaseReadinessReady.ready, true);
|
||||
assert.equal(releaseReadinessReady.releaseGate.passed, true);
|
||||
assert.equal(releaseReadinessReady.virtualHalSimConfigSourceCoverageReady, true);
|
||||
assert.deepEqual(releaseReadinessReady.gateManifest, createProjectReleaseGateManifest());
|
||||
assert.equal(releaseReadinessReady.gateResultMatrix.rows.length, createProjectReleaseGateManifest().gateCount);
|
||||
assert.equal(createProjectReleaseGateActionPlan({
|
||||
@@ -796,6 +915,7 @@ assert.equal(
|
||||
);
|
||||
const releaseReadinessWithScreenshots = createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
axisScreenshotArtifacts: ["desktop-preview", "desktop-dro", "mobile-preview", "mobile-mdi"].map((viewportName) => ({
|
||||
apiName: "real-browser-simulation-axis-screenshot-artifact",
|
||||
artifactVersion: 1,
|
||||
@@ -829,6 +949,7 @@ assert.equal(
|
||||
);
|
||||
const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.stringify(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage: simConfigSourceCoverageReport,
|
||||
})));
|
||||
assert.equal(releaseReadinessArtifact.apiName, "project-release-readiness-report");
|
||||
const releaseReadinessArtifactValidation = createProjectReleaseReadinessArtifactValidation(releaseReadinessArtifact);
|
||||
@@ -848,6 +969,7 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
|
||||
gateExecutionSummaryReady: true,
|
||||
gateResultMatrixReady: true,
|
||||
gateActionPlanReady: true,
|
||||
virtualHalSimConfigSourceCoverageReady: true,
|
||||
axisScreenshotArtifactSummaryReady: true,
|
||||
axisScreenshotArtifactCount: 0,
|
||||
blockedRuntimeFamilies: [
|
||||
@@ -891,6 +1013,11 @@ assert.deepEqual(releaseReadinessArtifactValidation, {
|
||||
label: "Gate action plan",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-source-coverage",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
value: "ready",
|
||||
},
|
||||
{
|
||||
id: "axis-screenshot-artifacts",
|
||||
label: "AXIS screenshot artifacts",
|
||||
@@ -987,6 +1114,7 @@ assert.deepEqual(
|
||||
"simConfigInventory.passed",
|
||||
"simConfigInventory.skipped",
|
||||
"simConfigInventory.unexpectedFail",
|
||||
"virtualHalSimConfigSourceCoverage",
|
||||
"blockedRuntimeFamilies",
|
||||
"gateManifest",
|
||||
"gateExecutionManifest",
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { mkdirSync, renameSync, writeFileSync } from "node:fs";
|
||||
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import { createProjectBatchAcceptanceReport } from "../../../runtime/sdk/src/index.js";
|
||||
import {
|
||||
createProjectBatchAcceptanceReport,
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
} from "../../../runtime/sdk/src/index.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "../../..");
|
||||
const outputPath = process.argv[2] ?? resolve(root, "build/project-batch-acceptance.json");
|
||||
const sourceManifestText = readFileSync(resolve(root, "tools/source-manifest.txt"), "utf8");
|
||||
|
||||
const report = createProjectBatchAcceptanceReport({
|
||||
batchId: "project-batch-acceptance-artifact",
|
||||
@@ -18,6 +22,13 @@ const report = createProjectBatchAcceptanceReport({
|
||||
evidence: "project_batch_acceptance_artifact_node_smoke=ok",
|
||||
command: "wasm-port/tests/sdk/node/verify_project_batch_acceptance_workflow.sh",
|
||||
},
|
||||
{
|
||||
id: "virtual-hal-sim-config-source-coverage",
|
||||
type: "api",
|
||||
label: "Virtual HAL sim-config source coverage",
|
||||
evidence: "linuxcnc-wasm-virtual-hal-sim-config-source-coverage-report",
|
||||
command: "wasm-port/tests/sdk/node/verify_sdk_surface.sh",
|
||||
},
|
||||
{
|
||||
id: "project-batch-acceptance-workflow-gate",
|
||||
type: "gate",
|
||||
@@ -27,6 +38,9 @@ const report = createProjectBatchAcceptanceReport({
|
||||
},
|
||||
],
|
||||
observedOutputs: ["project_batch_acceptance_workflow_node_smoke=ok"],
|
||||
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport({
|
||||
manifestText: sourceManifestText,
|
||||
}),
|
||||
});
|
||||
|
||||
mkdirSync(dirname(outputPath), { recursive: true });
|
||||
|
||||
@@ -4,6 +4,9 @@ import {
|
||||
createProjectReleaseGateManifest,
|
||||
createProjectReleaseReadinessReport,
|
||||
} from "../../../runtime/sdk/src/project-release-readiness.js";
|
||||
import {
|
||||
createVirtualHalSimConfigSourceCoverageReport,
|
||||
} from "../../../runtime/sdk/src/linuxcnc-hal.js";
|
||||
|
||||
import {
|
||||
INI_PANEL_ENTRIES,
|
||||
@@ -1350,6 +1353,7 @@ assert.equal(workflowDomMountResult.renderResult.dataset.handoffScope, "workflow
|
||||
assert.deepEqual(createIniPanelShellWorkflowOverviewContract(), shellViewModel.shellWorkflowOverviewContract);
|
||||
const releaseReadinessArtifactJson = JSON.stringify(createProjectReleaseReadinessReport({
|
||||
gateResults: Object.fromEntries(createProjectReleaseGateManifest().gateIds.map((id) => [id, true])),
|
||||
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
|
||||
}));
|
||||
const releaseArtifactValidation = validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson(
|
||||
releaseReadinessArtifactJson,
|
||||
@@ -1655,7 +1659,7 @@ const releaseArtifactMountResult = mountIniPanelShellWorkflowOverviewReleaseRead
|
||||
rowsNode: releaseArtifactRowsNode,
|
||||
});
|
||||
assert.equal(releaseArtifactRenderResult.rendered, true);
|
||||
assert.equal(releaseArtifactRenderResult.rowCount, 9);
|
||||
assert.equal(releaseArtifactRenderResult.rowCount, 10);
|
||||
assert.equal(releaseArtifactRenderResult.dataset.handoffScope, "workflow-overview-release-readiness-artifact");
|
||||
assert.equal(
|
||||
releaseArtifactRenderResult.rowIds.includes("gate-execution-manifest"),
|
||||
@@ -1673,8 +1677,12 @@ assert.equal(
|
||||
releaseArtifactRenderResult.rowIds.includes("axis-screenshot-artifacts"),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
releaseArtifactRenderResult.rowIds.includes("virtual-hal-sim-config-source-coverage"),
|
||||
true,
|
||||
);
|
||||
assert.equal(releaseArtifactMountResult.ready, true);
|
||||
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 9);
|
||||
assert.equal(releaseArtifactMountResult.renderResult.rowCount, 10);
|
||||
assert.deepEqual(
|
||||
validateIniPanelShellWorkflowOverviewReleaseReadinessArtifactJson("{").missing,
|
||||
["artifact-json"],
|
||||
@@ -1715,7 +1723,7 @@ assert.equal(
|
||||
);
|
||||
assert.equal(releaseArtifactUrlWorkflow.renderState.statusLine, "Ready: No blocking reasons");
|
||||
assert.equal(releaseArtifactUrlWorkflow.mountResult.ready, true);
|
||||
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 9);
|
||||
assert.equal(releaseArtifactUrlWorkflow.mountResult.renderResult.rowCount, 10);
|
||||
const releaseArtifactUrlWorkflowSummary =
|
||||
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowSummaryViewModel(
|
||||
releaseArtifactUrlWorkflow,
|
||||
|
||||
Reference in New Issue
Block a user