推进 INI 面板 workflow overview helper

This commit is contained in:
2026-06-15 05:32:09 +08:00
parent df1f39974f
commit deb8b7cdd3
5 changed files with 247 additions and 4 deletions

View File

@@ -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" }] }
]
}

View File

@@ -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",

View File

@@ -13,8 +13,14 @@ const stateBundle = {
opfs: "OPFS: ready",
},
},
overview: {
phase: "ready",
},
};
const workflowStatus = {
overview: {
phase: "ready",
},
lastAction: "load-session",
error: null,
};

View File

@@ -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",
);