From deb8b7cdd3b63531fa157b94381d7828889a92c7 Mon Sep 17 00:00:00 2001 From: wangdequan Date: Mon, 15 Jun 2026 05:32:09 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A8=E8=BF=9B=20INI=20=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=20workflow=20overview=20helper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- text12.txt | 72 +++++++++++++++ wasm-port/docs/ui-panel-state-summary.md | 26 +++++- .../ui/ini-panel/panel-state-summary.js | 58 ++++++++++++ ...ni_panel_control_page_session_workflow.mjs | 6 ++ .../node/verify_ini_panel_state_summary.mjs | 89 ++++++++++++++++++- 5 files changed, 247 insertions(+), 4 deletions(-) diff --git a/text12.txt b/text12.txt index a7671e3..ee3c480 100644 --- a/text12.txt +++ b/text12.txt @@ -515,3 +515,75 @@ host_wasm_opfs_browser_smokes=ok `restore-load-session` 的 field/log 路径再做一次轻量整合,优先把“已加载 session、快照恢复、 run readiness、run 结果”汇总成一个更上层的只读工作流状态对象;仍保持只描述 UI/host boundary facts,不在 JS 中解释 G-code、canonical events 或 CNC 运行语义。 + +五、2026-06-15 继续执行记录:INI panel workflow overview helper + +本轮按“第一原则:加快实质性推进”继续推进更上层只读工作流状态对象,将已归一化的 +report、run readiness、session snapshot/load、run result 和 control page 需要的状态字段 +再压成单一 workflow overview,供 state bundle、workflow status 和 read-only control view +共享。 + +完成内容: + +- 新增 `wasm-port/runtime/ui/ini-panel/panel-state-summary.js` 的 + `createMachineSessionWorkflowOverview(report)`; +- `createMachineSessionWorkflowStatus()` 现在额外返回 `overview`; +- `createMachineSessionStateBundle()` 现在额外返回 `overview`; +- `createMachineSessionControlView()` 新增 `Workflow` 区,显示: + - `Phase` + - `Missing` + - `Last action` + - `Error` +- overview 只组合已有的 readiness、session、loaded、runReadiness、run 和 status 信息; +- `Missing` 现在优先基于 loaded session / run readiness 的已知边界事实判断,不再依赖 DOM; +- `wasm-port/tests/ui/node/verify_ini_panel_state_summary.mjs` 覆盖 overview helper、bundle、 + workflow status 和 control view; +- `wasm-port/tests/ui/node/verify_ini_panel_control_page_session_workflow.mjs` 同步带上 overview; +- `wasm-port/docs/ui-panel-state-summary.md` 同步说明 overview helper; +- 未新增控制按钮; +- 未解析 G-code; +- 未解释 canonical events; +- 未改变 OPFS/session persistence、LinuxCNC interpreter、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_ini_panel_control_page_session_workflow.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 +ini_panel_control_page_session_workflow_node_smoke=ok +ui_node_smokes=ok +browser_ini_opfs_smoke=ok +browser_ini_control_page_smoke=ok +browser_ini_launch_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 +browser_interp_smoke=ok +host_wasm_opfs_browser_smokes=ok +``` + +下一步建议: + +继续推进 OPFS/session persistence 的实质链路。下一批建议把 `load-session`、`restore-load-session` +和 `run` 的状态入口再向上收拢成一个稳定的只读状态 API,优先服务 control page 和 shell 的 +统一读取;仍保持只描述 UI/host boundary facts,不在 JS 中解释 G-code、canonical events 或 +CNC 运行语义。 diff --git a/wasm-port/docs/ui-panel-state-summary.md b/wasm-port/docs/ui-panel-state-summary.md index dc59754..defa1d5 100644 --- a/wasm-port/docs/ui-panel-state-summary.md +++ b/wasm-port/docs/ui-panel-state-summary.md @@ -132,6 +132,7 @@ This helper derives a lightweight status object for upper-level control pages: ```js { readiness: { ini: "...", interp: "...", opfs: "..." }, + overview: { phase: "ran", missing: [], status: { lastAction: "run-gcode" } }, runReadiness: { phase: "ready", canRun: true, buttonEnabled: true }, lastAction: "run-gcode", error: null, @@ -143,6 +144,27 @@ 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 state without parsing logs, DOM nodes, or transient window state. +## `createMachineSessionWorkflowOverview(report)` + +This helper folds the already-normalized report into a single read-only +workflow object for shell and control-page consumers: + +```js +{ + phase: "waiting" | "ready" | "ran", + missing: [], + readiness: { ini: "...", interp: "...", opfs: "..." }, + session: { snapshotLabel: "...", workflow: "...", ini: "...", gcode: "..." }, + loaded: { iniWasmPath: "...", parameterWasmPath: "...", toolTableWasmPath: "..." }, + runReadiness: { phase: "ready", canRun: true }, + run: { status: "ok", canonicalEventCount: 2, motionSnapshotCount: 2 }, + status: { lastAction: "run-gcode", error: null } +} +``` + +It combines existing UI/host boundary facts only. It does not inspect G-code +or derive CNC behavior. + ## `createMachineSessionStateBundle(state, options)` This helper packages the current report, export payload, and workflow status @@ -151,8 +173,9 @@ into one read-only object: ```js { report: { ... }, + overview: { phase: "ran", missing: [] }, export: { filename: "...", mediaType: "application/json", report: { ... }, text: "{...}\n" }, - workflowStatus: { readiness: { ... }, lastAction: "...", error: null, session: { ... }, run: { ... } } + workflowStatus: { readiness: { ... }, overview: { ... }, lastAction: "...", session: { ... }, run: { ... } } } ``` @@ -170,6 +193,7 @@ This helper maps a bundle into UI-ready status and sections: sections: [ { id: "readiness", title: "Readiness", rows: [{ label: "INI", value: "..." }] }, { id: "session", title: "Session", rows: [{ label: "Workflow", value: "..." }] }, + { id: "workflow", title: "Workflow", rows: [{ label: "Phase", value: "ran" }] }, { id: "run", title: "Run", rows: [{ label: "Ready", value: "ready" }] } ] } diff --git a/wasm-port/runtime/ui/ini-panel/panel-state-summary.js b/wasm-port/runtime/ui/ini-panel/panel-state-summary.js index 7fd2bac..784510e 100644 --- a/wasm-port/runtime/ui/ini-panel/panel-state-summary.js +++ b/wasm-port/runtime/ui/ini-panel/panel-state-summary.js @@ -137,8 +137,10 @@ export function createMachineSessionStateReportExport(state = {}, options = {}) export function createMachineSessionWorkflowStatus(state = {}) { const report = createMachineSessionStateReport(state); + const overview = createMachineSessionWorkflowOverview(report); return { readiness: report.readiness, + overview, runReadiness: report.runReadiness, lastAction: report.status.lastAction, error: report.status.error, @@ -147,12 +149,57 @@ export function createMachineSessionWorkflowStatus(state = {}) { }; } +export function createMachineSessionWorkflowOverview(report = {}) { + const readiness = report.readiness ?? {}; + const loaded = report.loaded ?? {}; + const session = report.session ?? {}; + const runReadiness = report.runReadiness ?? {}; + const run = report.run ?? {}; + const status = report.status ?? {}; + const loadedIniWasmPath = loaded.iniWasmPath ?? runReadiness.iniWasmPath ?? null; + const missing = []; + if (!loadedIniWasmPath) { + missing.push("loaded-session"); + } + if (!session.snapshotLabel) { + missing.push("session-snapshot"); + } + if (!runReadiness.canRun) { + missing.push("run-readiness"); + } + return { + phase: run.status ? "ran" : (missing.length === 0 ? "ready" : "waiting"), + missing, + readiness, + session: { + snapshotLabel: session.snapshotLabel ?? null, + workflow: session.workflow ?? null, + ini: session.ini ?? null, + gcode: session.gcode ?? null, + }, + loaded, + runReadiness, + run: { + status: run.status ?? null, + canonicalEventCount: run.canonicalEventCount ?? 0, + motionSnapshotCount: run.motionSnapshotCount ?? 0, + iniWasmPath: run.iniWasmPath ?? null, + }, + status: { + lastAction: status.lastAction ?? null, + error: status.error ?? null, + }, + }; +} + export function createMachineSessionStateBundle(state = {}, options = {}) { const report = createMachineSessionStateReport(state); const exportPayload = createMachineSessionStateReportExport(state, options); const workflowStatus = createMachineSessionWorkflowStatus(state); + const overview = createMachineSessionWorkflowOverview(report); return { report, + overview, export: exportPayload, workflowStatus, }; @@ -161,6 +208,7 @@ export function createMachineSessionStateBundle(state = {}, options = {}) { export function createMachineSessionControlView(bundle = {}) { const report = bundle.report ?? {}; const workflowStatus = bundle.workflowStatus ?? {}; + const overview = bundle.overview ?? workflowStatus.overview ?? {}; const session = workflowStatus.session ?? report.session ?? {}; const runReadiness = workflowStatus.runReadiness ?? report.runReadiness ?? {}; const run = workflowStatus.run ?? report.run ?? {}; @@ -190,6 +238,16 @@ export function createMachineSessionControlView(bundle = {}) { { label: "G-code", value: session.gcode ?? null }, ], }, + { + id: "workflow", + title: "Workflow", + rows: [ + { label: "Phase", value: overview.phase ?? null }, + { label: "Missing", value: overview.missing?.join(", ") || null }, + { label: "Last action", value: overview.status?.lastAction ?? null }, + { label: "Error", value: overview.status?.error ?? null }, + ], + }, { id: "run", title: "Run", diff --git a/wasm-port/tests/ui/node/verify_ini_panel_control_page_session_workflow.mjs b/wasm-port/tests/ui/node/verify_ini_panel_control_page_session_workflow.mjs index 99d5044..46a9632 100644 --- a/wasm-port/tests/ui/node/verify_ini_panel_control_page_session_workflow.mjs +++ b/wasm-port/tests/ui/node/verify_ini_panel_control_page_session_workflow.mjs @@ -13,8 +13,14 @@ const stateBundle = { opfs: "OPFS: ready", }, }, + overview: { + phase: "ready", + }, }; const workflowStatus = { + overview: { + phase: "ready", + }, lastAction: "load-session", error: null, }; diff --git a/wasm-port/tests/ui/node/verify_ini_panel_state_summary.mjs b/wasm-port/tests/ui/node/verify_ini_panel_state_summary.mjs index f040be1..426333d 100644 --- a/wasm-port/tests/ui/node/verify_ini_panel_state_summary.mjs +++ b/wasm-port/tests/ui/node/verify_ini_panel_state_summary.mjs @@ -7,6 +7,7 @@ import { createMachineSessionStateBundle, createMachineSessionStateReport, createMachineSessionStateReportExport, + createMachineSessionWorkflowOverview, createMachineSessionWorkflowStatus, } from "../../../runtime/ui/ini-panel/panel-state-summary.js"; import { @@ -67,6 +68,38 @@ const expectedEmptyRunReadinessReport = { iniWasmPath: null, }; +const expectedWorkflowOverview = { + phase: "ran", + missing: [], + 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", + gcode: "linuxcnc/gcode/ui-session.ngc", + }, + loaded: { + iniWasmPath: null, + parameterWasmPath: null, + toolTableWasmPath: null, + }, + runReadiness: expectedRunReadinessReport, + run: { + status: "ok", + canonicalEventCount: 2, + motionSnapshotCount: 2, + iniWasmPath: null, + }, + status: { + lastAction: "run-gcode", + error: null, + }, +}; + const summary = createIniPanelStateSummary({ badges: { ini: "INI WASM: ready", @@ -427,6 +460,37 @@ assert.deepEqual( interp: "Interpreter WASM: ready", opfs: "OPFS: ready", }, + overview: { + phase: "ran", + missing: [], + 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", + gcode: "linuxcnc/gcode/ui-session.ngc", + }, + loaded: { + iniWasmPath: null, + parameterWasmPath: null, + toolTableWasmPath: null, + }, + runReadiness: expectedRunReadinessReport, + run: { + status: "ok", + canonicalEventCount: 2, + motionSnapshotCount: 2, + iniWasmPath: null, + }, + status: { + lastAction: "run-gcode", + error: null, + }, + }, runReadiness: expectedRunReadinessReport, lastAction: "run-gcode", error: null, @@ -485,6 +549,11 @@ const expectedBundleReport = { }, }; +assert.deepEqual( + createMachineSessionWorkflowOverview(expectedBundleReport), + expectedWorkflowOverview, +); + assert.deepEqual( createMachineSessionStateBundle({ badges: { @@ -510,6 +579,7 @@ assert.deepEqual( }), { report: expectedBundleReport, + overview: expectedWorkflowOverview, export: { filename: "machine-session-state-report.json", mediaType: "application/json", @@ -522,6 +592,7 @@ assert.deepEqual( interp: "Interpreter WASM: ready", opfs: "OPFS: ready", }, + overview: expectedWorkflowOverview, lastAction: "run-gcode", error: null, runReadiness: expectedRunReadinessReport, @@ -560,6 +631,7 @@ assert.deepEqual( }, lastAction: "run-gcode", error: null, + overview: expectedWorkflowOverview, session: { snapshotLabel: "ui-machine-session/ui-machine-session.json", workflow: "save-session-snapshot", @@ -603,6 +675,16 @@ assert.deepEqual( { label: "G-code", value: "linuxcnc/gcode/ui-session.ngc" }, ], }, + { + id: "workflow", + title: "Workflow", + rows: [ + { label: "Phase", value: "ran" }, + { label: "Missing", value: null }, + { label: "Last action", value: "run-gcode" }, + { label: "Error", value: null }, + ], + }, { id: "run", title: "Run", @@ -645,6 +727,7 @@ const renderedControlView = renderMachineSessionControlView( report: expectedBundleReport, workflowStatus: { readiness: expectedBundleReport.readiness, + overview: expectedWorkflowOverview, lastAction: "run-gcode", error: null, session: expectedBundleReport.session, @@ -655,10 +738,10 @@ const renderedControlView = renderMachineSessionControlView( ); assert.equal(renderedControlView.statusNode.textContent, "run-gcode | ok | -"); -assert.equal(renderedControlView.sectionNodes.length, 3); -assert.equal(renderedControlView.sectionNodes[2].dataset.sectionId, "run"); +assert.equal(renderedControlView.sectionNodes.length, 4); +assert.equal(renderedControlView.sectionNodes[3].dataset.sectionId, "run"); assert.equal( - renderedControlView.sectionNodes[2].children[1].children[3].textContent, + renderedControlView.sectionNodes[3].children[1].children[3].textContent, "enabled", );