接入 INI panel session readiness workflow
This commit is contained in:
@@ -9,7 +9,7 @@ turn-by-turn continuation notes and next-batch execution records.
|
|||||||
## Current Status
|
## Current Status
|
||||||
|
|
||||||
- Active continuation file: `text14.txt`
|
- Active continuation file: `text14.txt`
|
||||||
- Latest completed batch: project release gate wrapper
|
- Latest completed batch: INI panel session readiness workflow
|
||||||
- Latest relevant commit: use `git log -1 --oneline` after each committed batch
|
- Latest relevant commit: use `git log -1 --oneline` after each committed batch
|
||||||
- Working tree at tracker creation: clean
|
- Working tree at tracker creation: clean
|
||||||
- Required policy: LinuxCNC remains the only CNC semantic source
|
- Required policy: LinuxCNC remains the only CNC semantic source
|
||||||
@@ -44,7 +44,7 @@ The project is complete for the current scope when all of the following are true
|
|||||||
| Native sim configs baseline | Stable | Last recorded Layer 2: `total 159`, `pass 151`, `expected_fail 8`, `unexpected_fail 0`. |
|
| Native sim configs baseline | Stable | Last recorded Layer 2: `total 159`, `pass 151`, `expected_fail 8`, `unexpected_fail 0`. |
|
||||||
| OPFS/session persistence | Stable for current scope | Browser smoke passes, SDK re-exports OPFS/session helpers including `readMachineSessionReadiness()`, and `docs/opfs-session-persistence.md` defines the project-level release gate. |
|
| OPFS/session persistence | Stable for current scope | Browser smoke passes, SDK re-exports OPFS/session helpers including `readMachineSessionReadiness()`, and `docs/opfs-session-persistence.md` defines the project-level release gate. |
|
||||||
| SDK/API surface | Stable for current scope | SDK re-exports core, OPFS/session readiness/load helpers, sim-config staging, and INI panel shell/workflow overview helpers with a Node surface smoke. |
|
| SDK/API surface | Stable for current scope | SDK re-exports core, OPFS/session readiness/load helpers, sim-config staging, and INI panel shell/workflow overview helpers with a Node surface smoke. |
|
||||||
| Browser/UI workflow | Stable for current scope | Workflow overview embedding mount DOM closure is complete and documented for external shells. |
|
| Browser/UI workflow | Stable for current scope | Workflow overview embedding mount DOM closure is complete, and INI panel session workflows expose OPFS/session readiness before loading WASM session state. |
|
||||||
| Workflow overview embedding | Stable for current scope | Report/display/render, DOM contract/readiness/renderer, mount result/display/render, mount DOM contract/readiness/renderer/wrapper exist. |
|
| Workflow overview embedding | Stable for current scope | Report/display/render, DOM contract/readiness/renderer, mount result/display/render, mount DOM contract/readiness/renderer/wrapper exist. |
|
||||||
| Host/runtime boundary proof | Stable blocked state | Host smoke passes; runtime families remain blocked and `docs/host-runtime-boundary-handoff.md` records host readiness, promotion blockers, dispatch, and evidence gates. |
|
| Host/runtime boundary proof | Stable blocked state | Host smoke passes; runtime families remain blocked and `docs/host-runtime-boundary-handoff.md` records host readiness, promotion blockers, dispatch, and evidence gates. |
|
||||||
| Documentation/release gate | Stable for current scope | `docs/project-release-handoff.md` links README, panel entry, SDK, OPFS/session, sim-config, host/runtime, source reuse, drift, and tracker acceptance paths. |
|
| Documentation/release gate | Stable for current scope | `docs/project-release-handoff.md` links README, panel entry, SDK, OPFS/session, sim-config, host/runtime, source reuse, drift, and tracker acceptance paths. |
|
||||||
|
|||||||
72
text14.txt
72
text14.txt
@@ -1704,3 +1704,75 @@ opfs_session_docs_node_smoke=ok
|
|||||||
继续以实质能力推进为主。下一批建议把 `readMachineSessionReadiness()` 接入 INI panel/session
|
继续以实质能力推进为主。下一批建议把 `readMachineSessionReadiness()` 接入 INI panel/session
|
||||||
workflow 或 external shell handoff view-model,让 UI 在加载 session 前显示结构化 ready/blocked
|
workflow 或 external shell handoff view-model,让 UI 在加载 session 前显示结构化 ready/blocked
|
||||||
原因;仍保持只读,不新增控制按钮,不解析 G-code,不扩大 LinuxCNC 语义边界。
|
原因;仍保持只读,不新增控制按钮,不解析 G-code,不扩大 LinuxCNC 语义边界。
|
||||||
|
|
||||||
|
二十一、2026-06-16 继续执行记录:INI panel session readiness workflow
|
||||||
|
|
||||||
|
本轮继续以功能实质性推进为主,把上一批新增的 `readMachineSessionReadiness()` 接入 INI panel/session
|
||||||
|
workflow 层。现在 UI/session workflow 和外部 shell 入口可以在加载 machine session 到 WASM 前获取
|
||||||
|
结构化 ready/blocked 原因。
|
||||||
|
|
||||||
|
完成内容:
|
||||||
|
|
||||||
|
- `wasm-port/runtime/ui/ini-panel/session-workflow.js` 新增:
|
||||||
|
- `createMachineSessionReadinessFieldState()`;
|
||||||
|
- `machineSessionReadinessLogLines()`;
|
||||||
|
- `readMachineSessionReadinessWorkflow()`;
|
||||||
|
- `restoreMachineSessionSnapshotWorkflow()` 支持可选 `readReadiness`;
|
||||||
|
- restore snapshot workflow 会把 readiness report 合并进:
|
||||||
|
- `readiness`;
|
||||||
|
- `fields.sessionReadinessPhase`;
|
||||||
|
- `fields.sessionReadinessReady`;
|
||||||
|
- `fields.sessionReadinessMissing`;
|
||||||
|
- `logLines`;
|
||||||
|
- restore+load workflow 会在 load session 前保留 readiness log 与字段;
|
||||||
|
- `wasm-port/runtime/ui/ini-panel/app.js` 接入 `readMachineSessionReadiness()`:
|
||||||
|
- restore session snapshot 时读取 readiness;
|
||||||
|
- load session 时先读取 readiness 再加载 WASM session;
|
||||||
|
- restore+load session 时读取 readiness;
|
||||||
|
- panel state 中保存 `sessionReadiness`;
|
||||||
|
- `wasm-port/runtime/ui/ini-panel/ui-shell.js` re-export session readiness workflow helpers;
|
||||||
|
- `wasm-port/docs/panel-entry.md` 记录 external shell 可用的 session readiness workflow helper;
|
||||||
|
- `wasm-port/tests/ui/node/verify_ini_panel_session_workflow.mjs` 覆盖 ready/blocked readiness、
|
||||||
|
restore readiness、restore+load readiness;
|
||||||
|
- `wasm-port/tests/ui/node/verify_ini_panel_entry_docs.mjs` 覆盖 ui-shell export 与 docs drift;
|
||||||
|
- `PROJECT_COMPLETION_TRACKER.md` 同步 latest completed batch 与 Browser/UI workflow 状态;
|
||||||
|
- 未新增控制按钮;
|
||||||
|
- 未执行 action-plan;
|
||||||
|
- 未解析 G-code;
|
||||||
|
- 未解释 canonical events;
|
||||||
|
- 未新增 JS CNC 语义;
|
||||||
|
- 未运行 opt-in runtime probes;
|
||||||
|
- 未 promotion blocked runtime family;
|
||||||
|
- 未改变 LinuxCNC interpreter、tool、parameter、planner、kinematics、remap 或 LinuxCNC-owned
|
||||||
|
runtime semantics。
|
||||||
|
|
||||||
|
验证已通过:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git diff --check
|
||||||
|
wasm-port/tests/ui/node/verify_ini_panel_session_workflow.sh
|
||||||
|
wasm-port/tests/ui/node/verify_ini_panel_entry_docs.sh
|
||||||
|
wasm-port/tests/ui/node/verify_ini_panel_ui_shell.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
|
||||||
|
```
|
||||||
|
|
||||||
|
关键输出:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ini_panel_session_workflow_node_smoke=ok
|
||||||
|
ini_panel_entry_docs_node_smoke=ok
|
||||||
|
ini_panel_ui_shell_node_smoke=ok
|
||||||
|
ui_node_smokes=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
|
||||||
|
```
|
||||||
|
|
||||||
|
下一步工作计划:
|
||||||
|
|
||||||
|
继续做实质能力推进。下一批建议把 session readiness report 纳入 read-only control page status 或
|
||||||
|
shell handoff package/display view-model,让外部 shell 不进入 editor 页面也能读取 session
|
||||||
|
ready/blocked 原因;仍保持只读,不新增控制按钮,不解析 G-code,不扩大 LinuxCNC 语义边界。
|
||||||
|
|||||||
@@ -77,6 +77,9 @@ import {
|
|||||||
createMachineSessionControlPageController,
|
createMachineSessionControlPageController,
|
||||||
createMachineSessionReadonlyStatus,
|
createMachineSessionReadonlyStatus,
|
||||||
createMachineSessionReadonlyStatusApiManifest,
|
createMachineSessionReadonlyStatusApiManifest,
|
||||||
|
createMachineSessionReadinessFieldState,
|
||||||
|
machineSessionReadinessLogLines,
|
||||||
|
readMachineSessionReadinessWorkflow,
|
||||||
readMachineSessionReadonlyStatus,
|
readMachineSessionReadonlyStatus,
|
||||||
loadMachineSessionForControlPageWorkflow,
|
loadMachineSessionForControlPageWorkflow,
|
||||||
createControlPageSessionLoadState,
|
createControlPageSessionLoadState,
|
||||||
@@ -127,6 +130,11 @@ outer shell needs one stable read of `stateBundle`, `workflowStatus`,
|
|||||||
Use `createMachineSessionReadonlyStatusApiManifest()` or
|
Use `createMachineSessionReadonlyStatusApiManifest()` or
|
||||||
`controlPage.readonlyStatusApiManifest` when you only need the API shape and
|
`controlPage.readonlyStatusApiManifest` when you only need the API shape and
|
||||||
method names.
|
method names.
|
||||||
|
Use `readMachineSessionReadinessWorkflow()` when an outer shell or panel flow
|
||||||
|
needs a read-only `ready`/`blocked` readiness report before loading a machine
|
||||||
|
session into WASM. `createMachineSessionReadinessFieldState()` and
|
||||||
|
`machineSessionReadinessLogLines()` turn that report into stable fields and
|
||||||
|
status text without parsing G-code or executing LinuxCNC behavior.
|
||||||
Use `createIniPanelShellControlPageContract()` or `controlPage.contract` when
|
Use `createIniPanelShellControlPageContract()` or `controlPage.contract` when
|
||||||
an outer shell needs a fast contract check for `createController`,
|
an outer shell needs a fast contract check for `createController`,
|
||||||
`loadSessionState`, `readStatus`, `refresh`, `renderView`, the readonly status
|
`loadSessionState`, `readStatus`, `refresh`, `renderView`, the readonly status
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ import {
|
|||||||
import {
|
import {
|
||||||
loadMachineSessionFromOpfs,
|
loadMachineSessionFromOpfs,
|
||||||
} from "../../opfs/linuxcnc-machine-session-bridge.js";
|
} from "../../opfs/linuxcnc-machine-session-bridge.js";
|
||||||
|
import {
|
||||||
|
readMachineSessionReadiness,
|
||||||
|
} from "../../opfs/machine-session-readiness.js";
|
||||||
import {
|
import {
|
||||||
loadMachineSessionSnapshot,
|
loadMachineSessionSnapshot,
|
||||||
saveMachineSessionSnapshot,
|
saveMachineSessionSnapshot,
|
||||||
@@ -37,6 +40,7 @@ import {
|
|||||||
} from "./run-workflow.js";
|
} from "./run-workflow.js";
|
||||||
import {
|
import {
|
||||||
loadMachineSessionIntoWasmWorkflow,
|
loadMachineSessionIntoWasmWorkflow,
|
||||||
|
readMachineSessionReadinessWorkflow,
|
||||||
restoreMachineSessionSnapshotWorkflow,
|
restoreMachineSessionSnapshotWorkflow,
|
||||||
saveMachineSessionSnapshotWorkflow,
|
saveMachineSessionSnapshotWorkflow,
|
||||||
} from "./session-workflow.js";
|
} from "./session-workflow.js";
|
||||||
@@ -486,6 +490,14 @@ async function loadMachineSessionIntoWasm() {
|
|||||||
return loadedSession;
|
return loadedSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readCurrentMachineSessionReadiness() {
|
||||||
|
return readMachineSessionReadiness(MACHINE_ID, {
|
||||||
|
sessionId: UI_SESSION.snapshotId,
|
||||||
|
snapshotFilename: UI_SESSION.snapshotFilename,
|
||||||
|
gcodeFilename: UI_SESSION.defaultGcodeFilename,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function loadSample() {
|
async function loadSample() {
|
||||||
setLog(`Fetching ${SAMPLE_PATH}`);
|
setLog(`Fetching ${SAMPLE_PATH}`);
|
||||||
const response = await fetch(SAMPLE_PATH);
|
const response = await fetch(SAMPLE_PATH);
|
||||||
@@ -674,6 +686,7 @@ document.getElementById("restore-session-snapshot").addEventListener("click", as
|
|||||||
snapshotLabel: sessionSnapshotLabel(UI_SESSION),
|
snapshotLabel: sessionSnapshotLabel(UI_SESSION),
|
||||||
loadMachineSessionSnapshot,
|
loadMachineSessionSnapshot,
|
||||||
loadMachineTextFiles,
|
loadMachineTextFiles,
|
||||||
|
readReadiness: readCurrentMachineSessionReadiness,
|
||||||
});
|
});
|
||||||
editor.value = workflow.editorIniText;
|
editor.value = workflow.editorIniText;
|
||||||
setField("sessionIni", workflow.fields.sessionIni);
|
setField("sessionIni", workflow.fields.sessionIni);
|
||||||
@@ -684,6 +697,7 @@ document.getElementById("restore-session-snapshot").addEventListener("click", as
|
|||||||
restoredSessionSnapshot = workflow.snapshot;
|
restoredSessionSnapshot = workflow.snapshot;
|
||||||
updatePanelMachineSessionState({
|
updatePanelMachineSessionState({
|
||||||
sessionSnapshot: workflow.summary,
|
sessionSnapshot: workflow.summary,
|
||||||
|
sessionReadiness: workflow.readiness,
|
||||||
sessionLoad: null,
|
sessionLoad: null,
|
||||||
selectedProgram: null,
|
selectedProgram: null,
|
||||||
fields: workflow.fields,
|
fields: workflow.fields,
|
||||||
@@ -707,17 +721,27 @@ document.getElementById("restore-session-snapshot").addEventListener("click", as
|
|||||||
|
|
||||||
document.getElementById("load-session").addEventListener("click", async () => {
|
document.getElementById("load-session").addEventListener("click", async () => {
|
||||||
try {
|
try {
|
||||||
|
const readinessWorkflow = await readMachineSessionReadinessWorkflow({
|
||||||
|
readReadiness: readCurrentMachineSessionReadiness,
|
||||||
|
});
|
||||||
const workflow = await loadMachineSessionIntoWasmWorkflow({
|
const workflow = await loadMachineSessionIntoWasmWorkflow({
|
||||||
loadSession: () => loadMachineSessionIntoWasm(),
|
loadSession: () => loadMachineSessionIntoWasm(),
|
||||||
});
|
});
|
||||||
loadedSession = workflow.loadedSession;
|
loadedSession = workflow.loadedSession;
|
||||||
updatePanelMachineSessionState({
|
updatePanelMachineSessionState({
|
||||||
|
sessionReadiness: readinessWorkflow.readiness,
|
||||||
sessionLoad: workflow.loadSummary,
|
sessionLoad: workflow.loadSummary,
|
||||||
fields: workflow.fields,
|
fields: {
|
||||||
|
...readinessWorkflow.fields,
|
||||||
|
...workflow.fields,
|
||||||
|
},
|
||||||
lastAction: "load-session",
|
lastAction: "load-session",
|
||||||
error: null,
|
error: null,
|
||||||
});
|
});
|
||||||
setLog(workflow.logLines.join("\n"));
|
setLog([
|
||||||
|
...readinessWorkflow.logLines,
|
||||||
|
...workflow.logLines,
|
||||||
|
].join("\n"));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
updatePanelMachineSessionState({
|
updatePanelMachineSessionState({
|
||||||
lastAction: "load-session",
|
lastAction: "load-session",
|
||||||
@@ -736,6 +760,7 @@ document.getElementById("restore-load-session").addEventListener("click", async
|
|||||||
snapshotLabel: sessionSnapshotLabel(UI_SESSION),
|
snapshotLabel: sessionSnapshotLabel(UI_SESSION),
|
||||||
loadMachineSessionSnapshot,
|
loadMachineSessionSnapshot,
|
||||||
loadMachineTextFiles,
|
loadMachineTextFiles,
|
||||||
|
readReadiness: readCurrentMachineSessionReadiness,
|
||||||
loadSession: () => loadMachineSessionIntoWasm(),
|
loadSession: () => loadMachineSessionIntoWasm(),
|
||||||
});
|
});
|
||||||
editor.value = workflow.editorIniText;
|
editor.value = workflow.editorIniText;
|
||||||
@@ -750,6 +775,7 @@ document.getElementById("restore-load-session").addEventListener("click", async
|
|||||||
}
|
}
|
||||||
updatePanelMachineSessionState({
|
updatePanelMachineSessionState({
|
||||||
sessionSnapshot: workflow.summary,
|
sessionSnapshot: workflow.summary,
|
||||||
|
sessionReadiness: workflow.readiness,
|
||||||
sessionLoad: workflow.loadSummary,
|
sessionLoad: workflow.loadSummary,
|
||||||
selectedProgram: null,
|
selectedProgram: null,
|
||||||
fields: workflow.fields,
|
fields: workflow.fields,
|
||||||
|
|||||||
@@ -52,6 +52,47 @@ export function machineSessionLoadWorkflowLogLines(summary) {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createMachineSessionReadinessFieldState(readiness) {
|
||||||
|
return {
|
||||||
|
sessionReadinessPhase: readiness.phase,
|
||||||
|
sessionReadinessReady: readiness.ready ? "ready" : "blocked",
|
||||||
|
sessionReadinessMissing: readiness.missing.length === 0 ? "none" : readiness.missing.join(", "),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function machineSessionReadinessLogLines(readiness) {
|
||||||
|
const checks = Array.isArray(readiness.checks) ? readiness.checks : [];
|
||||||
|
return [
|
||||||
|
`Session readiness: ${readiness.ready ? "ready" : "blocked"}`,
|
||||||
|
...checks.map((check) => {
|
||||||
|
const status = check.ok ? "ok" : `blocked (${check.error})`;
|
||||||
|
return `${check.name}: ${check.path} -> ${status}`;
|
||||||
|
}),
|
||||||
|
...(readiness.snapshotCheck
|
||||||
|
? [
|
||||||
|
`snapshot: ${readiness.snapshotCheck.path} -> ${
|
||||||
|
readiness.snapshotCheck.ok ? "ok" : `blocked (${readiness.snapshotCheck.error})`
|
||||||
|
}`,
|
||||||
|
]
|
||||||
|
: []),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function readMachineSessionReadinessWorkflow({
|
||||||
|
readiness,
|
||||||
|
readReadiness,
|
||||||
|
} = {}) {
|
||||||
|
const sessionReadiness = readiness ?? await readReadiness?.();
|
||||||
|
if (!sessionReadiness) {
|
||||||
|
throw new Error("A machine session readiness reader is required.");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
readiness: sessionReadiness,
|
||||||
|
fields: createMachineSessionReadinessFieldState(sessionReadiness),
|
||||||
|
logLines: machineSessionReadinessLogLines(sessionReadiness),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function loadMachineSessionIntoWasmWorkflow({ loadSession }) {
|
export async function loadMachineSessionIntoWasmWorkflow({ loadSession }) {
|
||||||
if (typeof loadSession !== "function") {
|
if (typeof loadSession !== "function") {
|
||||||
throw new Error("A machine session loader is required.");
|
throw new Error("A machine session loader is required.");
|
||||||
@@ -114,6 +155,7 @@ export async function restoreMachineSessionSnapshotWorkflow({
|
|||||||
snapshotLabel,
|
snapshotLabel,
|
||||||
loadMachineSessionSnapshot,
|
loadMachineSessionSnapshot,
|
||||||
loadMachineTextFiles,
|
loadMachineTextFiles,
|
||||||
|
readReadiness = null,
|
||||||
loadSession = null,
|
loadSession = null,
|
||||||
}) {
|
}) {
|
||||||
const snapshot = await loadMachineSessionSnapshot(
|
const snapshot = await loadMachineSessionSnapshot(
|
||||||
@@ -129,21 +171,36 @@ export async function restoreMachineSessionSnapshotWorkflow({
|
|||||||
editorIniText: files.ini,
|
editorIniText: files.ini,
|
||||||
summary,
|
summary,
|
||||||
fields,
|
fields,
|
||||||
|
readiness: null,
|
||||||
loadSummary: null,
|
loadSummary: null,
|
||||||
loadedSession: null,
|
loadedSession: null,
|
||||||
logLines: sessionSnapshotRestoreLogLines(summary),
|
logLines: sessionSnapshotRestoreLogLines(summary),
|
||||||
};
|
};
|
||||||
|
if (readReadiness) {
|
||||||
|
const readinessWorkflow = await readMachineSessionReadinessWorkflow({ readReadiness });
|
||||||
|
result.readiness = readinessWorkflow.readiness;
|
||||||
|
result.fields = {
|
||||||
|
...result.fields,
|
||||||
|
...readinessWorkflow.fields,
|
||||||
|
};
|
||||||
|
result.logLines = [
|
||||||
|
...result.logLines,
|
||||||
|
...readinessWorkflow.logLines,
|
||||||
|
];
|
||||||
|
}
|
||||||
if (loadSession) {
|
if (loadSession) {
|
||||||
const loadWorkflow = await loadMachineSessionIntoWasmWorkflow({ loadSession });
|
const loadWorkflow = await loadMachineSessionIntoWasmWorkflow({ loadSession });
|
||||||
result.loadedSession = loadWorkflow.loadedSession;
|
result.loadedSession = loadWorkflow.loadedSession;
|
||||||
result.loadSummary = loadWorkflow.loadSummary;
|
result.loadSummary = loadWorkflow.loadSummary;
|
||||||
result.fields = {
|
result.fields = {
|
||||||
...fields,
|
...fields,
|
||||||
|
...(result.readiness ? createMachineSessionReadinessFieldState(result.readiness) : {}),
|
||||||
...loadWorkflow.fields,
|
...loadWorkflow.fields,
|
||||||
sessionGcode: summary.gcodeOpfsPath,
|
sessionGcode: summary.gcodeOpfsPath,
|
||||||
};
|
};
|
||||||
result.logLines = [
|
result.logLines = [
|
||||||
...sessionSnapshotSummaryLogLines(summary),
|
...sessionSnapshotSummaryLogLines(summary),
|
||||||
|
...(result.readiness ? machineSessionReadinessLogLines(result.readiness) : []),
|
||||||
...loadWorkflow.logLines,
|
...loadWorkflow.logLines,
|
||||||
`G-code: ${summary.gcodeOpfsPath ?? "-"}`,
|
`G-code: ${summary.gcodeOpfsPath ?? "-"}`,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ import {
|
|||||||
createControlPageSessionLoadState,
|
createControlPageSessionLoadState,
|
||||||
loadMachineSessionForControlPageWorkflow,
|
loadMachineSessionForControlPageWorkflow,
|
||||||
} from "./control-page-session-workflow.js";
|
} from "./control-page-session-workflow.js";
|
||||||
|
import {
|
||||||
|
createMachineSessionReadinessFieldState,
|
||||||
|
machineSessionReadinessLogLines,
|
||||||
|
readMachineSessionReadinessWorkflow,
|
||||||
|
} from "./session-workflow.js";
|
||||||
import {
|
import {
|
||||||
createMachineSessionReadonlyStatus,
|
createMachineSessionReadonlyStatus,
|
||||||
createMachineSessionReadonlyStatusApiManifest,
|
createMachineSessionReadonlyStatusApiManifest,
|
||||||
@@ -32,6 +37,11 @@ export {
|
|||||||
createControlPageSessionLoadState,
|
createControlPageSessionLoadState,
|
||||||
loadMachineSessionForControlPageWorkflow,
|
loadMachineSessionForControlPageWorkflow,
|
||||||
} from "./control-page-session-workflow.js";
|
} from "./control-page-session-workflow.js";
|
||||||
|
export {
|
||||||
|
createMachineSessionReadinessFieldState,
|
||||||
|
machineSessionReadinessLogLines,
|
||||||
|
readMachineSessionReadinessWorkflow,
|
||||||
|
} from "./session-workflow.js";
|
||||||
export {
|
export {
|
||||||
createMachineSessionReadonlyStatus,
|
createMachineSessionReadonlyStatus,
|
||||||
createMachineSessionReadonlyStatusApiManifest,
|
createMachineSessionReadonlyStatusApiManifest,
|
||||||
|
|||||||
@@ -66,6 +66,9 @@ import {
|
|||||||
createIniPanelShellViewModel,
|
createIniPanelShellViewModel,
|
||||||
createMachineSessionReadonlyStatus,
|
createMachineSessionReadonlyStatus,
|
||||||
createMachineSessionReadonlyStatusApiManifest,
|
createMachineSessionReadonlyStatusApiManifest,
|
||||||
|
createMachineSessionReadinessFieldState,
|
||||||
|
machineSessionReadinessLogLines,
|
||||||
|
readMachineSessionReadinessWorkflow,
|
||||||
loadMachineSessionForControlPageWorkflow,
|
loadMachineSessionForControlPageWorkflow,
|
||||||
readMachineSessionReadonlyStatus,
|
readMachineSessionReadonlyStatus,
|
||||||
refreshMachineSessionControlPage,
|
refreshMachineSessionControlPage,
|
||||||
@@ -96,6 +99,10 @@ const sessionWorkflowText = readFileSync(
|
|||||||
resolve(root, "runtime/ui/ini-panel/control-page-session-workflow.js"),
|
resolve(root, "runtime/ui/ini-panel/control-page-session-workflow.js"),
|
||||||
"utf8",
|
"utf8",
|
||||||
);
|
);
|
||||||
|
const iniPanelSessionWorkflowText = readFileSync(
|
||||||
|
resolve(root, "runtime/ui/ini-panel/session-workflow.js"),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
|
||||||
const requiredShellExports = [
|
const requiredShellExports = [
|
||||||
"INI_PANEL_ENTRIES",
|
"INI_PANEL_ENTRIES",
|
||||||
@@ -168,6 +175,9 @@ const requiredShellExports = [
|
|||||||
"createMachineSessionControlPageController",
|
"createMachineSessionControlPageController",
|
||||||
"createMachineSessionReadonlyStatus",
|
"createMachineSessionReadonlyStatus",
|
||||||
"createMachineSessionReadonlyStatusApiManifest",
|
"createMachineSessionReadonlyStatusApiManifest",
|
||||||
|
"createMachineSessionReadinessFieldState",
|
||||||
|
"machineSessionReadinessLogLines",
|
||||||
|
"readMachineSessionReadinessWorkflow",
|
||||||
"loadMachineSessionForControlPageWorkflow",
|
"loadMachineSessionForControlPageWorkflow",
|
||||||
"readMachineSessionReadonlyStatus",
|
"readMachineSessionReadonlyStatus",
|
||||||
"refreshMachineSessionControlPage",
|
"refreshMachineSessionControlPage",
|
||||||
@@ -196,6 +206,15 @@ for (const exportName of [
|
|||||||
assert.match(docText, new RegExp(`\\b${exportName}\\b`));
|
assert.match(docText, new RegExp(`\\b${exportName}\\b`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const exportName of [
|
||||||
|
"createMachineSessionReadinessFieldState",
|
||||||
|
"machineSessionReadinessLogLines",
|
||||||
|
"readMachineSessionReadinessWorkflow",
|
||||||
|
]) {
|
||||||
|
assert.match(iniPanelSessionWorkflowText, new RegExp(`\\bexport (?:async )?function ${exportName}\\b`));
|
||||||
|
assert.match(docText, new RegExp(`\\b${exportName}\\b`));
|
||||||
|
}
|
||||||
|
|
||||||
const requiredPages = [
|
const requiredPages = [
|
||||||
["Launch", "runtime/ui/ini-panel/launch.html"],
|
["Launch", "runtime/ui/ini-panel/launch.html"],
|
||||||
["Edit and run", "runtime/ui/ini-panel/index.html"],
|
["Edit and run", "runtime/ui/ini-panel/index.html"],
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
createMachineSessionReadinessFieldState,
|
||||||
createMachineSessionLoadFieldState,
|
createMachineSessionLoadFieldState,
|
||||||
loadMachineSessionIntoWasmWorkflow,
|
loadMachineSessionIntoWasmWorkflow,
|
||||||
machineSessionLoadWorkflowLogLines,
|
machineSessionLoadWorkflowLogLines,
|
||||||
|
machineSessionReadinessLogLines,
|
||||||
|
readMachineSessionReadinessWorkflow,
|
||||||
restoreMachineSessionSnapshotWorkflow,
|
restoreMachineSessionSnapshotWorkflow,
|
||||||
saveMachineSessionSnapshotWorkflow,
|
saveMachineSessionSnapshotWorkflow,
|
||||||
} from "../../../runtime/ui/ini-panel/session-workflow.js";
|
} from "../../../runtime/ui/ini-panel/session-workflow.js";
|
||||||
@@ -63,6 +66,101 @@ await assert.rejects(
|
|||||||
/machine session loader/,
|
/machine session loader/,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const readySessionReadiness = {
|
||||||
|
ready: true,
|
||||||
|
phase: "ready",
|
||||||
|
missing: [],
|
||||||
|
checks: [
|
||||||
|
{
|
||||||
|
name: "ini",
|
||||||
|
path: "linuxcnc/machines/xyzab-tdr/machine.ini",
|
||||||
|
ok: true,
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parameters",
|
||||||
|
path: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
|
||||||
|
ok: true,
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "toolTable",
|
||||||
|
path: "linuxcnc/machines/xyzab-tdr/tool.tbl",
|
||||||
|
ok: true,
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
snapshotCheck: {
|
||||||
|
name: "snapshot",
|
||||||
|
path: "linuxcnc/sessions/ui-machine-session/ui-machine-session.json",
|
||||||
|
ok: true,
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const readinessWorkflowCalls = [];
|
||||||
|
const readinessWorkflow = await readMachineSessionReadinessWorkflow({
|
||||||
|
readReadiness: async () => {
|
||||||
|
readinessWorkflowCalls.push(["readReadiness"]);
|
||||||
|
return readySessionReadiness;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
assert.deepEqual(readinessWorkflowCalls, [["readReadiness"]]);
|
||||||
|
assert.equal(readinessWorkflow.readiness, readySessionReadiness);
|
||||||
|
assert.deepEqual(createMachineSessionReadinessFieldState(readySessionReadiness), {
|
||||||
|
sessionReadinessPhase: "ready",
|
||||||
|
sessionReadinessReady: "ready",
|
||||||
|
sessionReadinessMissing: "none",
|
||||||
|
});
|
||||||
|
assert.deepEqual(readinessWorkflow.fields, {
|
||||||
|
sessionReadinessPhase: "ready",
|
||||||
|
sessionReadinessReady: "ready",
|
||||||
|
sessionReadinessMissing: "none",
|
||||||
|
});
|
||||||
|
assert.deepEqual(machineSessionReadinessLogLines(readySessionReadiness), [
|
||||||
|
"Session readiness: ready",
|
||||||
|
"ini: linuxcnc/machines/xyzab-tdr/machine.ini -> ok",
|
||||||
|
"parameters: linuxcnc/machines/xyzab-tdr/linuxcnc.var -> ok",
|
||||||
|
"toolTable: linuxcnc/machines/xyzab-tdr/tool.tbl -> ok",
|
||||||
|
"snapshot: linuxcnc/sessions/ui-machine-session/ui-machine-session.json -> ok",
|
||||||
|
]);
|
||||||
|
assert.deepEqual(readinessWorkflow.logLines, machineSessionReadinessLogLines(readySessionReadiness));
|
||||||
|
await assert.rejects(
|
||||||
|
() => readMachineSessionReadinessWorkflow({}),
|
||||||
|
/machine session readiness reader/,
|
||||||
|
);
|
||||||
|
|
||||||
|
const blockedSessionReadiness = {
|
||||||
|
ready: false,
|
||||||
|
phase: "blocked",
|
||||||
|
missing: ["parameters", "toolTable"],
|
||||||
|
checks: [
|
||||||
|
{
|
||||||
|
name: "parameters",
|
||||||
|
path: "linuxcnc/machines/missing/linuxcnc.var",
|
||||||
|
ok: false,
|
||||||
|
error: "missing file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "toolTable",
|
||||||
|
path: "linuxcnc/machines/missing/tool.tbl",
|
||||||
|
ok: false,
|
||||||
|
error: "missing file",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
snapshotCheck: null,
|
||||||
|
};
|
||||||
|
assert.deepEqual(createMachineSessionReadinessFieldState(blockedSessionReadiness), {
|
||||||
|
sessionReadinessPhase: "blocked",
|
||||||
|
sessionReadinessReady: "blocked",
|
||||||
|
sessionReadinessMissing: "parameters, toolTable",
|
||||||
|
});
|
||||||
|
assert.deepEqual(machineSessionReadinessLogLines(blockedSessionReadiness), [
|
||||||
|
"Session readiness: blocked",
|
||||||
|
"parameters: linuxcnc/machines/missing/linuxcnc.var -> blocked (missing file)",
|
||||||
|
"toolTable: linuxcnc/machines/missing/tool.tbl -> blocked (missing file)",
|
||||||
|
]);
|
||||||
|
|
||||||
const calls = [];
|
const calls = [];
|
||||||
const saveWorkflow = await saveMachineSessionSnapshotWorkflow({
|
const saveWorkflow = await saveMachineSessionSnapshotWorkflow({
|
||||||
machineId: "xyzab-tdr",
|
machineId: "xyzab-tdr",
|
||||||
@@ -224,6 +322,75 @@ assert.deepEqual(restoreWorkflow.logLines, [
|
|||||||
"G-code: linuxcnc/gcode/ui-session.ngc",
|
"G-code: linuxcnc/gcode/ui-session.ngc",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const restoreReadinessCalls = [];
|
||||||
|
const restoreReadinessWorkflow = await restoreMachineSessionSnapshotWorkflow({
|
||||||
|
machineId: "xyzab-tdr",
|
||||||
|
snapshotId: "ui-machine-session",
|
||||||
|
snapshotFilename: "ui-machine-session.json",
|
||||||
|
snapshotLabel: "ui-machine-session/ui-machine-session.json",
|
||||||
|
loadMachineSessionSnapshot: async (snapshotId, machineId, options) => {
|
||||||
|
restoreReadinessCalls.push(["loadMachineSessionSnapshot", snapshotId, machineId, options]);
|
||||||
|
return {
|
||||||
|
sessionId: snapshotId,
|
||||||
|
metadata: {
|
||||||
|
workflow: "save-session-snapshot",
|
||||||
|
snapshotLabel: "ui-machine-session/ui-machine-session.json",
|
||||||
|
defaultGcodeOpfsPath: "linuxcnc/gcode/ui-session.ngc",
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
files: {
|
||||||
|
ini: "linuxcnc/machines/xyzab-tdr/machine.ini",
|
||||||
|
parameters: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
|
||||||
|
toolTable: "linuxcnc/machines/xyzab-tdr/tool.tbl",
|
||||||
|
gcode: "linuxcnc/gcode/ui-session.ngc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
loadMachineTextFiles: async (machineId) => {
|
||||||
|
restoreReadinessCalls.push(["loadMachineTextFiles", machineId]);
|
||||||
|
return {
|
||||||
|
ini: "[EMC]\nMACHINE = xyzab-tdr\n",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
readReadiness: async () => {
|
||||||
|
restoreReadinessCalls.push(["readReadiness"]);
|
||||||
|
return readySessionReadiness;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(restoreReadinessCalls, [
|
||||||
|
[
|
||||||
|
"loadMachineSessionSnapshot",
|
||||||
|
"ui-machine-session",
|
||||||
|
"xyzab-tdr",
|
||||||
|
{ filename: "ui-machine-session.json" },
|
||||||
|
],
|
||||||
|
["loadMachineTextFiles", "xyzab-tdr"],
|
||||||
|
["readReadiness"],
|
||||||
|
]);
|
||||||
|
assert.equal(restoreReadinessWorkflow.readiness, readySessionReadiness);
|
||||||
|
assert.deepEqual(restoreReadinessWorkflow.fields, {
|
||||||
|
sessionIni: "linuxcnc/machines/xyzab-tdr/machine.ini",
|
||||||
|
sessionParameters: "linuxcnc/machines/xyzab-tdr/linuxcnc.var",
|
||||||
|
sessionToolTable: "linuxcnc/machines/xyzab-tdr/tool.tbl",
|
||||||
|
sessionGcode: "linuxcnc/gcode/ui-session.ngc",
|
||||||
|
sessionSnapshot: "ui-machine-session/ui-machine-session.json",
|
||||||
|
sessionReadinessPhase: "ready",
|
||||||
|
sessionReadinessReady: "ready",
|
||||||
|
sessionReadinessMissing: "none",
|
||||||
|
});
|
||||||
|
assert.deepEqual(restoreReadinessWorkflow.logLines, [
|
||||||
|
"Snapshot: ui-machine-session/ui-machine-session.json",
|
||||||
|
"Workflow: save-session-snapshot",
|
||||||
|
"Default G-code: linuxcnc/gcode/ui-session.ngc",
|
||||||
|
"INI: linuxcnc/machines/xyzab-tdr/machine.ini",
|
||||||
|
"Parameter file: linuxcnc/machines/xyzab-tdr/linuxcnc.var",
|
||||||
|
"Tool table file: linuxcnc/machines/xyzab-tdr/tool.tbl",
|
||||||
|
"G-code: linuxcnc/gcode/ui-session.ngc",
|
||||||
|
...machineSessionReadinessLogLines(readySessionReadiness),
|
||||||
|
]);
|
||||||
|
|
||||||
const restoreLoadCalls = [];
|
const restoreLoadCalls = [];
|
||||||
const restoreLoadWorkflow = await restoreMachineSessionSnapshotWorkflow({
|
const restoreLoadWorkflow = await restoreMachineSessionSnapshotWorkflow({
|
||||||
machineId: "xyzab-tdr",
|
machineId: "xyzab-tdr",
|
||||||
|
|||||||
Reference in New Issue
Block a user