推进 INI 面板状态 bundle helper

This commit is contained in:
2026-06-14 22:34:38 +08:00
parent f537a8fa42
commit e6f55d775f
6 changed files with 216 additions and 0 deletions

View File

@@ -896,3 +896,65 @@ host_wasm_opfs_browser_smokes=ok
继续沿 `window.linuxCncIniPanelApi` 收敛控制面。下一批建议把当前散落的 report/export/ 继续沿 `window.linuxCncIniPanelApi` 收敛控制面。下一批建议把当前散落的 report/export/
status getter 再包成一个明确的 `getMachineSessionStateBundle()` 或 `getMachineSessionView()` status getter 再包成一个明确的 `getMachineSessionStateBundle()` 或 `getMachineSessionView()`
只读入口,给控制网页界面一个单点轮询对象;仍保持只读,不加按钮,不扩大 CNC 语义边界。 只读入口,给控制网页界面一个单点轮询对象;仍保持只读,不加按钮,不扩大 CNC 语义边界。
九、2026-06-14 继续执行记录INI panel state bundle helper
本轮继续沿 `window.linuxCncIniPanelApi` 收敛控制面,把现有 report/export/status 入口
聚合成一个单点只读 bundle helper给控制网页界面或上层 SDK 一个更稳的轮询对象。
完成内容:
- `wasm-port/runtime/ui/ini-panel/panel-state-summary.js` 新增
`createMachineSessionStateBundle(state, options)`
- bundle helper 返回:
- `report`
- `export`
- `workflowStatus`
- `wasm-port/runtime/ui/ini-panel/app.js` 在
`window.linuxCncIniPanelApi`、`window.getMachineSessionStateBundle()` 下暴露该 helper
- `wasm-port/tests/ui/node/verify_ini_panel_state_summary.mjs` 覆盖 bundle 与单项 getter 的
结构一致性;
- `wasm-port/tests/browser/ini_panel_smoke.html` 在真实 save、restore-load、run 流程后验证
bundle确认其 report/export/status 与单项 getter 一致;
- `wasm-port/docs/ui-panel-state-summary.md` 补充 bundle helper 说明,并明确它是控制页的
推荐单点只读入口;
- 未新增按钮;
- 未解析 G-code
- 未改变 OPFS/session persistence、LinuxCNC interpreter、canonical-event 语义、tool、
parameter、planner 或 LinuxCNC-owned runtime semantics。
验证已通过:
```bash
git diff --check
wasm-port/tests/ui/node/verify_ini_panel_state_summary.sh
wasm-port/tests/ui/node/verify_ui_node_smokes.sh
SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh
wasm-port/tools/verify_vendor_sync.sh
wasm-port/tools/verify_no_standalone_cnc_semantics.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh
SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh
wasm-port/tests/host/verify_host_smokes.sh
```
关键输出:
```text
ini_panel_state_summary_node_smoke=ok
ui_node_smokes=ok
browser_ini_opfs_smoke=ok
vendor sync up to date
standalone CNC semantics guard complete
interp_wasm_node_smoke=ok
sim_configs_wasm_node_inventory_executed=28
sim_configs_wasm_node_inventory_passed=28
sim_configs_wasm_node_inventory_skipped=131
sim_configs_wasm_node_inventory_unexpected_fail=0
host_wasm_opfs_browser_smokes=ok
```
下一步建议:
继续沿“单点只读入口”推进,把 `window.linuxCncIniPanelApi` 再收敛成一个更明确的
`getMachineSessionView()` 或 `getMachineSessionStateBundle()` 的稳定消费者契约,并开始只
在这个 bundle 上面叠控制页展示逻辑;仍不新增按钮、不把 CNC 语义搬进 JS。

View File

@@ -21,6 +21,7 @@ shims.
For control-page polling, use For control-page polling, use
`window.linuxCncIniPanelApi.getMachineSessionWorkflowStatus()`. It returns a `window.linuxCncIniPanelApi.getMachineSessionWorkflowStatus()`. It returns a
smaller workflow-facing object derived from the same report data. smaller workflow-facing object derived from the same report data.
For a single read call, use `window.linuxCncIniPanelApi.getMachineSessionStateBundle()`.
## `createIniPanelStateSummary(input)` ## `createIniPanelStateSummary(input)`
@@ -124,6 +125,23 @@ This helper derives a lightweight status object for upper-level control pages:
It is intended for polling or status panels that need current UI workflow It is intended for polling or status panels that need current UI workflow
state without parsing logs, DOM nodes, or transient window state. state without parsing logs, DOM nodes, or transient window state.
## `createMachineSessionStateBundle(state, options)`
This helper packages the current report, export payload, and workflow status
into one read-only object:
```js
{
report: { ... },
export: { filename: "...", mediaType: "application/json", report: { ... }, text: "{...}\n" },
workflowStatus: { readiness: { ... }, lastAction: "...", error: null, session: { ... }, run: { ... } }
}
```
It is the preferred single-entry helper for control-page polling and other
upper-level UI consumers that want one stable read instead of stitching the
three views together themselves.
## Boundary Rules ## Boundary Rules
- Do not parse G-code text in this helper. - Do not parse G-code text in this helper.

View File

@@ -47,6 +47,7 @@ import {
} from "./session-workflow.js"; } from "./session-workflow.js";
import { import {
createMachineSessionStateSnapshot, createMachineSessionStateSnapshot,
createMachineSessionStateBundle,
createMachineSessionStateReport, createMachineSessionStateReport,
createMachineSessionStateReportExport, createMachineSessionStateReportExport,
createMachineSessionWorkflowStatus, createMachineSessionWorkflowStatus,
@@ -166,6 +167,10 @@ function getMachineSessionWorkflowStatus() {
return createMachineSessionWorkflowStatus(window.linuxCncIniPanelMachineSessionState); return createMachineSessionWorkflowStatus(window.linuxCncIniPanelMachineSessionState);
} }
function getMachineSessionStateBundle() {
return createMachineSessionStateBundle(window.linuxCncIniPanelMachineSessionState);
}
function updatePanelMachineSessionState(patch = {}) { function updatePanelMachineSessionState(patch = {}) {
panelMachineSessionState = createMachineSessionStateSnapshot({ panelMachineSessionState = createMachineSessionStateSnapshot({
...panelMachineSessionState, ...panelMachineSessionState,
@@ -180,10 +185,12 @@ function updatePanelMachineSessionState(patch = {}) {
} }
window.linuxCncIniPanelApi = { window.linuxCncIniPanelApi = {
getMachineSessionStateBundle,
getMachineSessionStateReport, getMachineSessionStateReport,
getMachineSessionStateReportExport, getMachineSessionStateReportExport,
getMachineSessionWorkflowStatus, getMachineSessionWorkflowStatus,
}; };
window.getMachineSessionStateBundle = getMachineSessionStateBundle;
window.getMachineSessionStateReport = getMachineSessionStateReport; window.getMachineSessionStateReport = getMachineSessionStateReport;
window.getMachineSessionStateReportExport = getMachineSessionStateReportExport; window.getMachineSessionStateReportExport = getMachineSessionStateReportExport;
window.getMachineSessionWorkflowStatus = getMachineSessionWorkflowStatus; window.getMachineSessionWorkflowStatus = getMachineSessionWorkflowStatus;

View File

@@ -129,3 +129,14 @@ export function createMachineSessionWorkflowStatus(state = {}) {
run: report.run, run: report.run,
}; };
} }
export function createMachineSessionStateBundle(state = {}, options = {}) {
const report = createMachineSessionStateReport(state);
const exportPayload = createMachineSessionStateReportExport(state, options);
const workflowStatus = createMachineSessionWorkflowStatus(state);
return {
report,
export: exportPayload,
workflowStatus,
};
}

View File

@@ -369,6 +369,7 @@ TOOL_TABLE = browser-tool.tbl
); );
const uiApi = uiDocument.defaultView.linuxCncIniPanelApi; const uiApi = uiDocument.defaultView.linuxCncIniPanelApi;
if ( if (
!uiApi?.getMachineSessionStateBundle ||
!uiApi?.getMachineSessionStateReport || !uiApi?.getMachineSessionStateReport ||
!uiApi?.getMachineSessionStateReportExport || !uiApi?.getMachineSessionStateReportExport ||
!uiApi?.getMachineSessionWorkflowStatus !uiApi?.getMachineSessionWorkflowStatus
@@ -808,6 +809,27 @@ TOOL_TABLE = browser-tool.tbl
2, 2,
"UI machine session workflow status canonical event count", "UI machine session workflow status canonical event count",
); );
const machineSessionStateBundle = uiApi.getMachineSessionStateBundle();
assertEqual(
machineSessionStateBundle.report.status.lastAction,
machineSessionStateReport.status.lastAction,
"UI machine session state bundle report last action",
);
assertEqual(
machineSessionStateBundle.export.text,
machineSessionStateReportExport.text,
"UI machine session state bundle export text",
);
assertEqual(
machineSessionStateBundle.workflowStatus.run.canonicalEventCount,
machineSessionWorkflowStatus.run.canonicalEventCount,
"UI machine session state bundle workflow status canonical event count",
);
assertEqual(
uiDocument.defaultView.getMachineSessionStateBundle().workflowStatus.lastAction,
machineSessionStateBundle.workflowStatus.lastAction,
"UI legacy machine session state bundle getter matches API namespace",
);
assertEqual( assertEqual(
uiDocument.defaultView.getMachineSessionStateReport().status.lastAction, uiDocument.defaultView.getMachineSessionStateReport().status.lastAction,
machineSessionStateReport.status.lastAction, machineSessionStateReport.status.lastAction,

View File

@@ -3,6 +3,7 @@ import assert from "node:assert/strict";
import { import {
createIniPanelStateSummary, createIniPanelStateSummary,
createMachineSessionStateSnapshot, createMachineSessionStateSnapshot,
createMachineSessionStateBundle,
createMachineSessionStateReport, createMachineSessionStateReport,
createMachineSessionStateReportExport, createMachineSessionStateReportExport,
createMachineSessionWorkflowStatus, createMachineSessionWorkflowStatus,
@@ -378,16 +379,111 @@ assert.deepEqual(
}, },
); );
const expectedBundleReport = {
readiness: {
ini: "INI WASM: ready",
interp: "Interpreter WASM: ready",
opfs: "OPFS: ready",
},
session: {
snapshotLabel: "ui-machine-session/ui-machine-session.json",
workflow: "save-session-snapshot",
ini: "/work/session-machine.ini",
parameters: null,
toolTable: null,
gcode: "linuxcnc/gcode/ui-session.ngc",
},
loaded: {
iniWasmPath: null,
parameterWasmPath: null,
toolTableWasmPath: null,
},
program: {
source: null,
opfsPath: null,
wasmPath: null,
},
run: {
status: "ok",
canonicalEventCount: 2,
motionSnapshotCount: 2,
iniWasmPath: null,
},
status: {
lastAction: "run-gcode",
error: null,
fiveaxisRemap: null,
},
};
assert.deepEqual(
createMachineSessionStateBundle({
badges: {
ini: "INI WASM: ready",
interp: "Interpreter WASM: ready",
opfs: "OPFS: ready",
},
fields: {
sessionSnapshot: "ui-machine-session/ui-machine-session.json",
sessionIni: "/work/session-machine.ini",
sessionGcode: "linuxcnc/gcode/ui-session.ngc",
runStatus: "ok",
runWorkflow: "save-session-snapshot",
},
run: {
runStatus: "ok",
canonicalEventCount: 2,
motionSnapshotCount: 2,
},
lastAction: "run-gcode",
error: null,
}),
{
report: expectedBundleReport,
export: {
filename: "machine-session-state-report.json",
mediaType: "application/json",
report: expectedBundleReport,
text: `${JSON.stringify(expectedBundleReport, null, 2)}\n`,
},
workflowStatus: {
readiness: {
ini: "INI WASM: ready",
interp: "Interpreter WASM: ready",
opfs: "OPFS: ready",
},
lastAction: "run-gcode",
error: null,
session: {
snapshotLabel: "ui-machine-session/ui-machine-session.json",
workflow: "save-session-snapshot",
ini: "/work/session-machine.ini",
parameters: null,
toolTable: null,
gcode: "linuxcnc/gcode/ui-session.ngc",
},
run: {
status: "ok",
canonicalEventCount: 2,
motionSnapshotCount: 2,
iniWasmPath: null,
},
},
},
);
assert.deepEqual( assert.deepEqual(
{ {
getMachineSessionStateReport: typeof globalThis.window?.linuxCncIniPanelApi?.getMachineSessionStateReport, getMachineSessionStateReport: typeof globalThis.window?.linuxCncIniPanelApi?.getMachineSessionStateReport,
getMachineSessionStateReportExport: typeof globalThis.window?.linuxCncIniPanelApi?.getMachineSessionStateReportExport, getMachineSessionStateReportExport: typeof globalThis.window?.linuxCncIniPanelApi?.getMachineSessionStateReportExport,
getMachineSessionWorkflowStatus: typeof globalThis.window?.linuxCncIniPanelApi?.getMachineSessionWorkflowStatus, getMachineSessionWorkflowStatus: typeof globalThis.window?.linuxCncIniPanelApi?.getMachineSessionWorkflowStatus,
getMachineSessionStateBundle: typeof globalThis.window?.linuxCncIniPanelApi?.getMachineSessionStateBundle,
}, },
{ {
getMachineSessionStateReport: "undefined", getMachineSessionStateReport: "undefined",
getMachineSessionStateReportExport: "undefined", getMachineSessionStateReportExport: "undefined",
getMachineSessionWorkflowStatus: "undefined", getMachineSessionWorkflowStatus: "undefined",
getMachineSessionStateBundle: "undefined",
}, },
); );