推进 INI 面板 run G-code workflow helper
This commit is contained in:
70
text11.txt
70
text11.txt
@@ -381,3 +381,73 @@ helper,覆盖 snapshot/default program selection、G-code OPFS load、WASM wri
|
|||||||
interpreter run、run summary、field/log state 和 error state。该 helper 仍只能编排
|
interpreter run、run summary、field/log state 和 error state。该 helper 仍只能编排
|
||||||
host/runtime boundary,不解析或实现 G-code、planner、tool、parameter 或 canonical-event
|
host/runtime boundary,不解析或实现 G-code、planner、tool、parameter 或 canonical-event
|
||||||
语义。
|
语义。
|
||||||
|
|
||||||
|
二、2026-06-14 继续执行记录:INI panel run G-code workflow helper
|
||||||
|
|
||||||
|
本轮继续按 `text11.txt` 第一原则推进实质性 UI workflow 能力,把 `run-gcode` 用户流程抽成
|
||||||
|
可复用 helper,让上层 UI/API 可以直接复用 session/default G-code 选择、OPFS load、WASM
|
||||||
|
staging、LinuxCNC interpreter run、run summary、field/log state 的编排。
|
||||||
|
|
||||||
|
完成内容:
|
||||||
|
|
||||||
|
- 新增 `wasm-port/runtime/ui/ini-panel/run-workflow.js`;
|
||||||
|
- 导出:
|
||||||
|
- `selectSessionGcodeProgram(input)`;
|
||||||
|
- `runSessionContextLogLines(program, snapshot, snapshotLabel)`;
|
||||||
|
- `runGcodeSessionWorkflow(input)`;
|
||||||
|
- workflow helper 执行 host/runtime boundary 编排:
|
||||||
|
- 从 snapshot G-code OPFS path 或默认 G-code path 选择程序;
|
||||||
|
- 通过注入的 `loadGcodeProgram(...)` 读取 OPFS G-code 文本;
|
||||||
|
- 调用 `interp.writeTextFile(...)` 写入 interpreter WASM filesystem;
|
||||||
|
- 调用 LinuxCNC-owned `interp.runFileWithIni(...)`;
|
||||||
|
- 返回 program、resultText、run summary、UI field state、motionSnapshots 和 log lines;
|
||||||
|
- `wasm-port/runtime/ui/ini-panel/app.js` 的 `Run G-code` 按钮改为调用 workflow helper;
|
||||||
|
- canonical-event 展示和 motion playback 仍留在 UI 层,workflow 只通过注入的
|
||||||
|
`summarizeRun(...)` 接收已观察到的 UI summary/motion state;
|
||||||
|
- 新增 `wasm-port/tests/ui/node/verify_ini_panel_run_workflow.mjs` 和 shell wrapper;
|
||||||
|
- `wasm-port/tests/ui/node/verify_ui_node_smokes.sh` 纳入 run workflow smoke;
|
||||||
|
- `wasm-port/docs/ui-run-summary-helpers.md` 更新 run workflow API、返回结构和边界说明;
|
||||||
|
- 未改变 OPFS bridge、machine-file-store、LinuxCNC interpreter、G-code 文本、canonical-event
|
||||||
|
语义、tool、parameter、planner 或 LinuxCNC-owned runtime semantics。
|
||||||
|
|
||||||
|
验证已通过:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git diff --check
|
||||||
|
wasm-port/tests/ui/node/verify_ini_panel_run_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_run_workflow_node_smoke=ok
|
||||||
|
ini_panel_run_summary_node_smoke=ok
|
||||||
|
ini_panel_session_summary_node_smoke=ok
|
||||||
|
ini_panel_session_workflow_node_smoke=ok
|
||||||
|
ini_panel_machine_file_summary_node_smoke=ok
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
下一步建议:
|
||||||
|
|
||||||
|
继续按“实质性能力优先”推进。优先把 save/restore/load/run workflow 的结果汇总成一个
|
||||||
|
更完整的 machine session state snapshot helper,供上层 UI/API 直接消费当前 machine、
|
||||||
|
session、loaded WASM mapping、selected G-code、last run summary、panel badges 和 error
|
||||||
|
state;该 helper 只聚合已有 host/runtime boundary facts,不解析 G-code 或扩展 CNC 语义。
|
||||||
|
|||||||
@@ -3,9 +3,13 @@
|
|||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
`runtime/ui/ini-panel/run-summary.js` contains small host-boundary helpers for
|
`runtime/ui/ini-panel/run-summary.js` contains small host-boundary helpers for
|
||||||
summarizing an INI panel G-code run. These helpers are intentionally pure so
|
summarizing an INI panel G-code run.
|
||||||
browser UI panels, SDK-facing wrappers, and Node smoke tests can share the same
|
`runtime/ui/ini-panel/run-workflow.js` contains the reusable UI workflow for
|
||||||
summary shape without reading DOM text or parsing log output.
|
selecting the session/default G-code program, loading it from OPFS, staging it
|
||||||
|
into the interpreter WASM filesystem, invoking the LinuxCNC interpreter, and
|
||||||
|
returning run field/log state. These helpers are intentionally pure so browser
|
||||||
|
UI panels, SDK-facing wrappers, and Node smoke tests can share the same summary
|
||||||
|
shape without reading DOM text or parsing log output.
|
||||||
|
|
||||||
The helpers do not implement CNC behavior. G-code execution, canonical events,
|
The helpers do not implement CNC behavior. G-code execution, canonical events,
|
||||||
tool handling, parameters, kinematics, and planner behavior remain owned by the
|
tool handling, parameters, kinematics, and planner behavior remain owned by the
|
||||||
@@ -74,11 +78,53 @@ The helper returns:
|
|||||||
`canonicalEventCount` only counts result lines beginning with `canon_event=`.
|
`canonicalEventCount` only counts result lines beginning with `canon_event=`.
|
||||||
`motionSnapshotCount` is only `snapshots.length`.
|
`motionSnapshotCount` is only `snapshots.length`.
|
||||||
|
|
||||||
|
## `runGcodeSessionWorkflow(input)`
|
||||||
|
|
||||||
|
The workflow requires a loaded machine session and injected host/runtime
|
||||||
|
functions:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
interp,
|
||||||
|
loadedSession,
|
||||||
|
snapshot,
|
||||||
|
loadGcodeProgram,
|
||||||
|
gcodeFilenameFromProgramPath,
|
||||||
|
summarizeRun
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It returns the selected program mapping, raw interpreter result text, run
|
||||||
|
summary, UI field state, log lines, and optional UI motion snapshots produced
|
||||||
|
by the injected `summarizeRun` callback:
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
program: {
|
||||||
|
source: "snapshot",
|
||||||
|
opfsPath: "linuxcnc/gcode/ui-session.ngc",
|
||||||
|
wasmPath: "/work/ui-session.ngc",
|
||||||
|
text: "G0 X1\n"
|
||||||
|
},
|
||||||
|
resultText: "canon_event=...",
|
||||||
|
summary: { runStatus: "ok", programSource: "snapshot" },
|
||||||
|
fields: { runStatus: "ok", sessionGcode: "linuxcnc/gcode/ui-session.ngc" },
|
||||||
|
motionSnapshots: [{ line: 1 }],
|
||||||
|
logLines: ["Program: linuxcnc/gcode/ui-session.ngc -> /work/ui-session.ngc"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`selectSessionGcodeProgram(input)` is the pure program-selection helper used by
|
||||||
|
the workflow. Invalid snapshot G-code paths fall back to the UI default program.
|
||||||
|
|
||||||
## Boundary Rules
|
## Boundary Rules
|
||||||
|
|
||||||
- Do not parse G-code text in these helpers.
|
- Do not parse G-code text in these helpers.
|
||||||
- Do not infer tool, parameter, kinematics, planner, or modal semantics.
|
- Do not infer tool, parameter, kinematics, planner, or modal semantics.
|
||||||
- Do not derive new canonical events in JavaScript.
|
- Do not derive new canonical events in JavaScript.
|
||||||
|
- Keep canonical-event and motion parsing in the existing UI display layer or
|
||||||
|
inject it as already-observed UI state; the workflow must not own CNC
|
||||||
|
semantics.
|
||||||
- Do not make UI helper output a promotion signal for runtime families blocked
|
- Do not make UI helper output a promotion signal for runtime families blocked
|
||||||
by host dependencies.
|
by host dependencies.
|
||||||
- Keep new fields limited to host/runtime boundary facts already produced by
|
- Keep new fields limited to host/runtime boundary facts already produced by
|
||||||
@@ -90,6 +136,7 @@ Run the focused Node smoke after changing these helpers:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
wasm-port/tests/ui/node/verify_ini_panel_run_summary.sh
|
wasm-port/tests/ui/node/verify_ini_panel_run_summary.sh
|
||||||
|
wasm-port/tests/ui/node/verify_ini_panel_run_workflow.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
The aggregate UI Node smoke is:
|
The aggregate UI Node smoke is:
|
||||||
|
|||||||
@@ -29,16 +29,16 @@ import {
|
|||||||
machineFileSaveLogLines,
|
machineFileSaveLogLines,
|
||||||
} from "./machine-file-summary.js";
|
} from "./machine-file-summary.js";
|
||||||
import {
|
import {
|
||||||
createRunSummary,
|
|
||||||
summarizeRunResult,
|
summarizeRunResult,
|
||||||
} from "./run-summary.js";
|
} from "./run-summary.js";
|
||||||
|
import {
|
||||||
|
runGcodeSessionWorkflow,
|
||||||
|
} from "./run-workflow.js";
|
||||||
import {
|
import {
|
||||||
createMachineSessionLoadSummary,
|
createMachineSessionLoadSummary,
|
||||||
machineSessionLoadLogLines,
|
machineSessionLoadLogLines,
|
||||||
} from "./session-load-summary.js";
|
} from "./session-load-summary.js";
|
||||||
import {
|
import {
|
||||||
createSessionSnapshotSummary,
|
|
||||||
sessionSnapshotMetadataLogLines,
|
|
||||||
sessionSnapshotSummaryLogLines,
|
sessionSnapshotSummaryLogLines,
|
||||||
} from "./session-summary.js";
|
} from "./session-summary.js";
|
||||||
import {
|
import {
|
||||||
@@ -361,19 +361,6 @@ function sessionSnapshotMetadata() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function sessionMetadataLogLines(metadata = {}) {
|
|
||||||
return sessionSnapshotMetadataLogLines(createSessionSnapshotSummary({ metadata }));
|
|
||||||
}
|
|
||||||
|
|
||||||
function runSessionContextLogLines(program, snapshot = restoredSessionSnapshot) {
|
|
||||||
return [
|
|
||||||
`Program: ${program.opfsPath} -> ${program.wasmPath}`,
|
|
||||||
`Program source: ${program.source}`,
|
|
||||||
`Snapshot: ${snapshot ? sessionSnapshotLabel(snapshot.sessionId) : "-"}`,
|
|
||||||
...sessionMetadataLogLines(snapshot?.metadata),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearLastRunSummary() {
|
function clearLastRunSummary() {
|
||||||
window.linuxCncIniPanelLastRunSummary = null;
|
window.linuxCncIniPanelLastRunSummary = null;
|
||||||
setField("runSource", "-");
|
setField("runSource", "-");
|
||||||
@@ -383,15 +370,7 @@ function clearLastRunSummary() {
|
|||||||
setField("runWasmPath", "-");
|
setField("runWasmPath", "-");
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLastRunSummary(program, runtime = {}, snapshot = restoredSessionSnapshot) {
|
function setLastRunSummary(summary) {
|
||||||
const summary = createRunSummary(
|
|
||||||
program,
|
|
||||||
{
|
|
||||||
...runtime,
|
|
||||||
snapshotLabel: snapshot ? sessionSnapshotLabel(snapshot.sessionId) : null,
|
|
||||||
},
|
|
||||||
snapshot,
|
|
||||||
);
|
|
||||||
window.linuxCncIniPanelLastRunSummary = summary;
|
window.linuxCncIniPanelLastRunSummary = summary;
|
||||||
setField("runSource", summary.programSource);
|
setField("runSource", summary.programSource);
|
||||||
setField("runSnapshot", summary.snapshotLabel ?? "-");
|
setField("runSnapshot", summary.snapshotLabel ?? "-");
|
||||||
@@ -401,29 +380,6 @@ function setLastRunSummary(program, runtime = {}, snapshot = restoredSessionSnap
|
|||||||
return summary;
|
return summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadSelectedGcodeProgram() {
|
|
||||||
const opfsPath = restoredSessionSnapshot?.payload?.files?.gcode;
|
|
||||||
let filename = UI_SESSION.defaultGcodeFilename;
|
|
||||||
let selectedOpfsPath = defaultGcodeOpfsPath();
|
|
||||||
let source = "default";
|
|
||||||
if (typeof opfsPath === "string") {
|
|
||||||
try {
|
|
||||||
filename = gcodeFilenameFromProgramPath(opfsPath);
|
|
||||||
selectedOpfsPath = opfsPath;
|
|
||||||
source = "snapshot";
|
|
||||||
} catch {
|
|
||||||
filename = UI_SESSION.defaultGcodeFilename;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
filename,
|
|
||||||
opfsPath: selectedOpfsPath,
|
|
||||||
source,
|
|
||||||
wasmPath: UI_SESSION.gcodeWasmPath,
|
|
||||||
text: await loadGcodeProgram(filename),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function restoreSessionSnapshotToEditor() {
|
async function restoreSessionSnapshotToEditor() {
|
||||||
const snapshot = await loadMachineSessionSnapshot(
|
const snapshot = await loadMachineSessionSnapshot(
|
||||||
UI_SESSION.snapshotId,
|
UI_SESSION.snapshotId,
|
||||||
@@ -682,25 +638,33 @@ document.getElementById("run-gcode").addEventListener("click", async () => {
|
|||||||
clearLastRunSummary();
|
clearLastRunSummary();
|
||||||
setRunMonitor(5, "running", {}, "running");
|
setRunMonitor(5, "running", {}, "running");
|
||||||
await nextFrame();
|
await nextFrame();
|
||||||
const result = interp.runFileWithIni(program.wasmPath, loadedSession.ini.wasmPath);
|
const workflow = await runGcodeSessionWorkflow({
|
||||||
const trimmedResult = result.trim();
|
interp,
|
||||||
const motionSnapshots = parseRunMotion(trimmedResult, program.text);
|
loadedSession,
|
||||||
const runResultSummary = summarizeRunResult(trimmedResult, motionSnapshots);
|
snapshot: restoredSessionSnapshot,
|
||||||
setField("sessionGcode", program.opfsPath);
|
defaultFilename: UI_SESSION.defaultGcodeFilename,
|
||||||
setField("runStatus", "ok");
|
defaultOpfsPath: defaultGcodeOpfsPath(),
|
||||||
setLastRunSummary(program, {
|
wasmPath: UI_SESSION.gcodeWasmPath,
|
||||||
runStatus: "ok",
|
gcodeFilenameFromProgramPath,
|
||||||
iniWasmPath: loadedSession.ini.wasmPath,
|
loadGcodeProgram,
|
||||||
...runResultSummary,
|
snapshotLabel: restoredSessionSnapshot ? sessionSnapshotLabel(restoredSessionSnapshot.sessionId) : null,
|
||||||
|
summarizeRun: (resultText, programText) => {
|
||||||
|
const motionSnapshots = parseRunMotion(resultText, programText);
|
||||||
|
return {
|
||||||
|
...summarizeRunResult(resultText, motionSnapshots),
|
||||||
|
motionSnapshots,
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
setCanonicalEvents(trimmedResult);
|
setField("sessionGcode", workflow.fields.sessionGcode);
|
||||||
playRunMotion(trimmedResult, program.text, motionSnapshots);
|
setField("runStatus", workflow.fields.runStatus);
|
||||||
|
setLastRunSummary(workflow.summary);
|
||||||
|
setCanonicalEvents(workflow.resultText);
|
||||||
|
playRunMotion(workflow.resultText, workflow.program.text, workflow.motionSnapshots);
|
||||||
setLog(
|
setLog(
|
||||||
[
|
[
|
||||||
"Ran G-code through LinuxCNC interpreter WASM.",
|
"Ran G-code through LinuxCNC interpreter WASM.",
|
||||||
...runSessionContextLogLines(program),
|
...workflow.logLines,
|
||||||
`INI: ${loadedSession.ini.wasmPath}`,
|
|
||||||
trimmedResult,
|
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
109
wasm-port/runtime/ui/ini-panel/run-workflow.js
Normal file
109
wasm-port/runtime/ui/ini-panel/run-workflow.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import {
|
||||||
|
createRunSummary,
|
||||||
|
} from "./run-summary.js";
|
||||||
|
import {
|
||||||
|
createSessionSnapshotSummary,
|
||||||
|
sessionSnapshotMetadataLogLines,
|
||||||
|
} from "./session-summary.js";
|
||||||
|
|
||||||
|
export function selectSessionGcodeProgram({
|
||||||
|
snapshot = null,
|
||||||
|
defaultFilename,
|
||||||
|
defaultOpfsPath,
|
||||||
|
wasmPath,
|
||||||
|
gcodeFilenameFromProgramPath,
|
||||||
|
}) {
|
||||||
|
const snapshotOpfsPath = snapshot?.payload?.files?.gcode;
|
||||||
|
if (typeof snapshotOpfsPath === "string") {
|
||||||
|
try {
|
||||||
|
return {
|
||||||
|
filename: gcodeFilenameFromProgramPath(snapshotOpfsPath),
|
||||||
|
opfsPath: snapshotOpfsPath,
|
||||||
|
source: "snapshot",
|
||||||
|
wasmPath,
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
// Invalid snapshot program paths fall back to the UI default program.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
filename: defaultFilename,
|
||||||
|
opfsPath: defaultOpfsPath,
|
||||||
|
source: "default",
|
||||||
|
wasmPath,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runSessionContextLogLines(program, snapshot = null, snapshotLabel = null) {
|
||||||
|
const summary = createSessionSnapshotSummary({
|
||||||
|
metadata: snapshot?.metadata ?? {},
|
||||||
|
});
|
||||||
|
return [
|
||||||
|
`Program: ${program.opfsPath} -> ${program.wasmPath}`,
|
||||||
|
`Program source: ${program.source}`,
|
||||||
|
`Snapshot: ${snapshot ? (snapshotLabel ?? snapshot?.metadata?.snapshotLabel ?? snapshot?.sessionId) : "-"}`,
|
||||||
|
...sessionSnapshotMetadataLogLines(summary),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runGcodeSessionWorkflow({
|
||||||
|
interp,
|
||||||
|
loadedSession,
|
||||||
|
snapshot = null,
|
||||||
|
defaultFilename,
|
||||||
|
defaultOpfsPath,
|
||||||
|
wasmPath,
|
||||||
|
gcodeFilenameFromProgramPath,
|
||||||
|
loadGcodeProgram,
|
||||||
|
summarizeRun = () => ({}),
|
||||||
|
snapshotLabel = null,
|
||||||
|
}) {
|
||||||
|
if (!loadedSession) {
|
||||||
|
throw new Error("Load a machine session before running G-code.");
|
||||||
|
}
|
||||||
|
const program = selectSessionGcodeProgram({
|
||||||
|
snapshot,
|
||||||
|
defaultFilename,
|
||||||
|
defaultOpfsPath,
|
||||||
|
wasmPath,
|
||||||
|
gcodeFilenameFromProgramPath,
|
||||||
|
});
|
||||||
|
const programText = await loadGcodeProgram(program.filename);
|
||||||
|
interp.writeTextFile(program.wasmPath, programText);
|
||||||
|
const resultText = interp.runFileWithIni(program.wasmPath, loadedSession.ini.wasmPath).trim();
|
||||||
|
const runResultSummary = summarizeRun(resultText, programText);
|
||||||
|
const { motionSnapshots = [], ...runCounts } = runResultSummary;
|
||||||
|
const summary = createRunSummary(
|
||||||
|
program,
|
||||||
|
{
|
||||||
|
runStatus: "ok",
|
||||||
|
iniWasmPath: loadedSession.ini.wasmPath,
|
||||||
|
snapshotLabel: snapshot ? snapshotLabel : null,
|
||||||
|
...runCounts,
|
||||||
|
},
|
||||||
|
snapshot,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
program: {
|
||||||
|
...program,
|
||||||
|
text: programText,
|
||||||
|
},
|
||||||
|
resultText,
|
||||||
|
motionSnapshots,
|
||||||
|
summary,
|
||||||
|
fields: {
|
||||||
|
sessionGcode: program.opfsPath,
|
||||||
|
runStatus: summary.runStatus,
|
||||||
|
runSource: summary.programSource,
|
||||||
|
runSnapshot: summary.snapshotLabel ?? "-",
|
||||||
|
runWorkflow: summary.workflow ?? "-",
|
||||||
|
runOpfsPath: summary.programOpfsPath,
|
||||||
|
runWasmPath: summary.programWasmPath,
|
||||||
|
},
|
||||||
|
logLines: [
|
||||||
|
...runSessionContextLogLines(program, snapshot, summary.snapshotLabel),
|
||||||
|
`INI: ${loadedSession.ini.wasmPath}`,
|
||||||
|
resultText,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
149
wasm-port/tests/ui/node/verify_ini_panel_run_workflow.mjs
Normal file
149
wasm-port/tests/ui/node/verify_ini_panel_run_workflow.mjs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
|
||||||
|
import {
|
||||||
|
runGcodeSessionWorkflow,
|
||||||
|
selectSessionGcodeProgram,
|
||||||
|
} from "../../../runtime/ui/ini-panel/run-workflow.js";
|
||||||
|
|
||||||
|
const defaultProgram = selectSessionGcodeProgram({
|
||||||
|
defaultFilename: "ui-session.ngc",
|
||||||
|
defaultOpfsPath: "linuxcnc/gcode/ui-session.ngc",
|
||||||
|
wasmPath: "/work/ui-session.ngc",
|
||||||
|
gcodeFilenameFromProgramPath: () => {
|
||||||
|
throw new Error("not used");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(defaultProgram, {
|
||||||
|
filename: "ui-session.ngc",
|
||||||
|
opfsPath: "linuxcnc/gcode/ui-session.ngc",
|
||||||
|
source: "default",
|
||||||
|
wasmPath: "/work/ui-session.ngc",
|
||||||
|
});
|
||||||
|
|
||||||
|
const snapshotProgram = selectSessionGcodeProgram({
|
||||||
|
snapshot: {
|
||||||
|
payload: {
|
||||||
|
files: {
|
||||||
|
gcode: "linuxcnc/gcode/snapshot-session.ngc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultFilename: "ui-session.ngc",
|
||||||
|
defaultOpfsPath: "linuxcnc/gcode/ui-session.ngc",
|
||||||
|
wasmPath: "/work/ui-session.ngc",
|
||||||
|
gcodeFilenameFromProgramPath: (path) => path.split("/").at(-1),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(snapshotProgram, {
|
||||||
|
filename: "snapshot-session.ngc",
|
||||||
|
opfsPath: "linuxcnc/gcode/snapshot-session.ngc",
|
||||||
|
source: "snapshot",
|
||||||
|
wasmPath: "/work/ui-session.ngc",
|
||||||
|
});
|
||||||
|
|
||||||
|
const calls = [];
|
||||||
|
const workflow = await runGcodeSessionWorkflow({
|
||||||
|
interp: {
|
||||||
|
writeTextFile: (wasmPath, text) => {
|
||||||
|
calls.push(["writeTextFile", wasmPath, text]);
|
||||||
|
},
|
||||||
|
runFileWithIni: (programWasmPath, iniWasmPath) => {
|
||||||
|
calls.push(["runFileWithIni", programWasmPath, iniWasmPath]);
|
||||||
|
return [
|
||||||
|
"canon_event=STRAIGHT_FEED line=1 x=1 y=2",
|
||||||
|
"run_step phase=execute line=1 x=1 y=2 statement_uri=G1%20X1%20Y2",
|
||||||
|
"",
|
||||||
|
].join("\n");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
loadedSession: {
|
||||||
|
ini: {
|
||||||
|
wasmPath: "/work/session-machine.ini",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
snapshot: {
|
||||||
|
sessionId: "ui-machine-session",
|
||||||
|
metadata: {
|
||||||
|
workflow: "save-session-snapshot",
|
||||||
|
snapshotLabel: "ui-machine-session/ui-machine-session.json",
|
||||||
|
defaultGcodeOpfsPath: "linuxcnc/gcode/ui-session.ngc",
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
files: {
|
||||||
|
gcode: "linuxcnc/gcode/snapshot-session.ngc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultFilename: "ui-session.ngc",
|
||||||
|
defaultOpfsPath: "linuxcnc/gcode/ui-session.ngc",
|
||||||
|
wasmPath: "/work/ui-session.ngc",
|
||||||
|
gcodeFilenameFromProgramPath: (path) => path.split("/").at(-1),
|
||||||
|
loadGcodeProgram: async (filename) => {
|
||||||
|
calls.push(["loadGcodeProgram", filename]);
|
||||||
|
return "G1 X1 Y2\n";
|
||||||
|
},
|
||||||
|
snapshotLabel: "ui-machine-session/ui-machine-session.json",
|
||||||
|
summarizeRun: (resultText, programText) => {
|
||||||
|
calls.push(["summarizeRun", resultText, programText]);
|
||||||
|
return {
|
||||||
|
canonicalEventCount: 1,
|
||||||
|
motionSnapshotCount: 1,
|
||||||
|
motionSnapshots: [{ line: 1 }],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(calls, [
|
||||||
|
["loadGcodeProgram", "snapshot-session.ngc"],
|
||||||
|
["writeTextFile", "/work/ui-session.ngc", "G1 X1 Y2\n"],
|
||||||
|
["runFileWithIni", "/work/ui-session.ngc", "/work/session-machine.ini"],
|
||||||
|
[
|
||||||
|
"summarizeRun",
|
||||||
|
"canon_event=STRAIGHT_FEED line=1 x=1 y=2\nrun_step phase=execute line=1 x=1 y=2 statement_uri=G1%20X1%20Y2",
|
||||||
|
"G1 X1 Y2\n",
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert.deepEqual(workflow.summary, {
|
||||||
|
runStatus: "ok",
|
||||||
|
programSource: "snapshot",
|
||||||
|
snapshotLabel: "ui-machine-session/ui-machine-session.json",
|
||||||
|
workflow: "save-session-snapshot",
|
||||||
|
programOpfsPath: "linuxcnc/gcode/snapshot-session.ngc",
|
||||||
|
programWasmPath: "/work/ui-session.ngc",
|
||||||
|
iniWasmPath: "/work/session-machine.ini",
|
||||||
|
canonicalEventCount: 1,
|
||||||
|
motionSnapshotCount: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(workflow.fields, {
|
||||||
|
sessionGcode: "linuxcnc/gcode/snapshot-session.ngc",
|
||||||
|
runStatus: "ok",
|
||||||
|
runSource: "snapshot",
|
||||||
|
runSnapshot: "ui-machine-session/ui-machine-session.json",
|
||||||
|
runWorkflow: "save-session-snapshot",
|
||||||
|
runOpfsPath: "linuxcnc/gcode/snapshot-session.ngc",
|
||||||
|
runWasmPath: "/work/ui-session.ngc",
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(workflow.motionSnapshots, [{ line: 1 }]);
|
||||||
|
|
||||||
|
assert.deepEqual(workflow.logLines, [
|
||||||
|
"Program: linuxcnc/gcode/snapshot-session.ngc -> /work/ui-session.ngc",
|
||||||
|
"Program source: snapshot",
|
||||||
|
"Snapshot: ui-machine-session/ui-machine-session.json",
|
||||||
|
"Workflow: save-session-snapshot",
|
||||||
|
"Default G-code: linuxcnc/gcode/ui-session.ngc",
|
||||||
|
"INI: /work/session-machine.ini",
|
||||||
|
"canon_event=STRAIGHT_FEED line=1 x=1 y=2\nrun_step phase=execute line=1 x=1 y=2 statement_uri=G1%20X1%20Y2",
|
||||||
|
]);
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
() => runGcodeSessionWorkflow({
|
||||||
|
loadedSession: null,
|
||||||
|
}),
|
||||||
|
/Load a machine session before running G-code/,
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log("ini_panel_run_workflow_node_smoke=ok");
|
||||||
6
wasm-port/tests/ui/node/verify_ini_panel_run_workflow.sh
Executable file
6
wasm-port/tests/ui/node/verify_ini_panel_run_workflow.sh
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||||
|
|
||||||
|
node "$ROOT_DIR/tests/ui/node/verify_ini_panel_run_workflow.mjs"
|
||||||
@@ -4,6 +4,7 @@ set -euo pipefail
|
|||||||
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
ROOT_DIR="$(cd "$(dirname "$0")/../../.." && pwd)"
|
||||||
|
|
||||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_run_summary.sh"
|
"$ROOT_DIR/tests/ui/node/verify_ini_panel_run_summary.sh"
|
||||||
|
"$ROOT_DIR/tests/ui/node/verify_ini_panel_run_workflow.sh"
|
||||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_session_summary.sh"
|
"$ROOT_DIR/tests/ui/node/verify_ini_panel_session_summary.sh"
|
||||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_session_workflow.sh"
|
"$ROOT_DIR/tests/ui/node/verify_ini_panel_session_workflow.sh"
|
||||||
"$ROOT_DIR/tests/ui/node/verify_ini_panel_machine_file_summary.sh"
|
"$ROOT_DIR/tests/ui/node/verify_ini_panel_machine_file_summary.sh"
|
||||||
|
|||||||
Reference in New Issue
Block a user