Files
cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan/tools/collect-web-xyzbc-trt-evidence.mjs
2026-07-02 08:01:34 -04:00

168 lines
6.5 KiB
JavaScript

import { access, mkdir, readFile, writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { createMemorySessionStorage } from "../app/src/runtime/five-axis-session.js";
import { parseLinuxCncIni } from "../app/src/runtime/linuxcnc-ini-runtime.js";
import {
selectMachineFileProgram,
stageProfileMachineFiles,
} from "../app/src/runtime/linuxcnc-machine-file-staging.js";
import { createSimulationStore } from "../app/src/state/store.js";
import { getFiveAxisProfile } from "../app/src/profiles/index.js";
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
const projectRoot = resolve(repoRoot, "web-rtcp-5axis-xyzbc-trt-sim-plan");
const outputPath = process.argv[2]
|| resolve(projectRoot, "working/evidence/web-xyzbc-trt-evidence.json");
const sourceRel = "configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc";
const profile = getFiveAxisProfile("xyzbc-trt");
const wasmArtifacts = await inspectWasmArtifacts();
const iniText = await readFile(
resolve(repoRoot, "wasm-port/vendor/linuxcnc", profile.iniPath),
"utf8",
);
const ini = parseLinuxCncIni(iniText, {
path: profile.iniPath,
profileId: profile.id,
});
const storage = createMemorySessionStorage();
const staged = await stageProfileMachineFiles(profile, { storage });
const selectedPlan = selectMachineFileProgram(staged.plan, staged.save, sourceRel);
const store = createSimulationStore();
const storeStage = await store.stageMachineFiles({ storage: createMemorySessionStorage() });
store.dispatch({ type: "LOAD_LINUXCNC_GCODE_SOURCE", sourceRel });
const state = store.getState();
const evidence = {
apiName: "xyzbc-trt-web-opfs-wasm-evidence",
status: wasmArtifacts.ready ? "ready-for-wasm-runtime" : "blocked",
collectedAt: new Date().toISOString(),
profile: {
id: profile.id,
machineName: profile.machineName,
iniPath: profile.iniPath,
pyvcpXmlPath: profile.pyvcpXmlPath,
toolTablePath: profile.toolTablePath,
coordinates: profile.coordinates,
kinematics: profile.kinematics,
kinematicsModuleId: profile.kinematicsModuleId,
defaultProgramFilename: profile.machineFileStaging?.defaultProgramFilename,
switchkinsTypes: profile.kinematicsParameters?.switchkinsTypes,
halPins: profile.halPins,
},
ini: {
ready: ini.validation.ready,
machineName: ini.machineName,
coordinates: ini.traj.coordinates,
kinematicsName: ini.kinematics.name,
kinematicsModuleId: ini.kinematicsModuleId,
remaps: ini.rs274ngc.remaps,
haluiMdiCommands: ini.halui.mdiCommands,
toolTable: ini.emcio.toolTable,
parameterFile: ini.rs274ngc.parameterFile,
jointConfig: ini.jointConfig,
},
opfsStaging: {
storageMode: staged.save.storageMode,
opfsRoot: staged.save.opfsRoot,
fileCount: staged.save.fileCount,
summary: staged.save.summary,
files: staged.save.files.map((file) => ({
sourceRel: file.sourceRel,
opfsPath: file.opfsPath,
wasmPath: file.wasmPath,
kind: file.kind,
bytes: file.bytes,
executable: file.executable,
})),
gcodeSources: staged.save.gcodeSources,
selectedProgram: {
sourceRel: selectedPlan.selectedProgramSourceRel,
filename: selectedPlan.selectedProgramFilename,
wasmProgramPath: selectedPlan.wasmProgramPath,
},
},
store: {
machineProfile: state.machineProfile,
sessionName: state.sessionName,
machineProjectRoot: state.machineProject?.projectRoot || storeStage.save.opfsRoot,
activeProgram: state.activeProgram,
programSource: state.programSource,
programLineCount: state.programLines.length,
selectedGcodeSourceRel: state.machineFileStaging.selectedGcodeSourceRel,
},
wasm: wasmArtifacts,
coverage: {
profileDefaultXyzbc: profile.id === "xyzbc-trt",
iniReady: ini.validation.ready,
opfsStaged: staged.save.status === "saved" && staged.save.fileCount > 0,
pyvcpXmlStaged: staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc-trt.xml")),
remapsStaged: ["428remap.ngc", "429remap.ngc", "430remap.ngc"].every((name) => (
staged.save.files.some((file) => file.sourceRel.endsWith(`/remap_subs/${name}`))
)),
toolTableStaged: staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc-trt.tbl")),
parameterFileStaged: staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc.var")),
defaultProgramStaged: staged.save.gcodeSources.some((source) => source.filename === "xyzbc_switchkins.ngc"),
wasmArtifactsReady: wasmArtifacts.ready,
},
blockers: [
...(wasmArtifacts.ready ? [] : [{
id: "missing-wasm-artifacts",
detail: "wasm-port/build/wasm does not contain all required kinematics/core/tp/task-hal artifacts.",
required: wasmArtifacts.required,
missing: wasmArtifacts.missing,
}]),
...(staged.save.files.some((file) => file.sourceRel.endsWith("xyzbc.var")) ? [] : [{
id: "missing-parameter-file-staging",
detail: "xyzbc.var is referenced by INI but absent from wasm-port/vendor manifest in this workspace.",
}]),
],
semanticBoundary: "web_opfs_wasm_runtime_readiness_for_linuxcnc_xyzbc_trt",
};
await mkdir(dirname(outputPath), { recursive: true });
await writeFile(outputPath, JSON.stringify(evidence, null, 2) + "\n", "utf8");
console.log(`web_xyzbc_trt_evidence=${outputPath}`);
async function inspectWasmArtifacts() {
const required = [
"wasm-port/build/wasm/kinematics/linuxcnc_xyzbc_trt_kinematics.js",
"wasm-port/build/wasm/kinematics/linuxcnc_xyzbc_trt_kinematics.wasm",
"wasm-port/build/wasm/core/linuxcnc_interp.js",
"wasm-port/build/wasm/core/linuxcnc_interp.wasm",
"wasm-port/build/wasm/tp/linuxcnc_tp.js",
"wasm-port/build/wasm/tp/linuxcnc_tp.wasm",
"wasm-port/build/wasm/task-hal/linuxcnc_task_hal.js",
"wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm",
];
const files = [];
const missing = [];
for (const rel of required) {
const abs = resolve(repoRoot, rel);
try {
await access(abs);
files.push(rel);
} catch {
missing.push(rel);
}
}
return {
required,
files,
missing,
ready: missing.length === 0,
emscriptenAvailable: Boolean(await commandExists("emcc")),
};
}
async function commandExists(command) {
const { spawn } = await import("node:child_process");
return new Promise((resolveCommand) => {
const child = spawn("bash", ["-lc", `command -v ${command}`], { stdio: "ignore" });
child.on("exit", (code) => resolveCommand(code === 0));
});
}