接入 task HAL Web 仿真运行时

This commit is contained in:
2026-06-22 06:11:55 +08:00
parent 3771b9eafe
commit bd11a5f8d6
42 changed files with 5574 additions and 50 deletions

View File

@@ -8,7 +8,7 @@ assert.equal(blocked.phase, "blocked");
assert.equal(blocked.promotionAllowed, false);
assert.equal(blocked.fullLinuxCncProgramExecutionReady, false);
assert.equal(blocked.missing.includes("linuxcnc kinematics WASM frame"), true);
assert.equal(blocked.blockers.some((blocker) => blocker.includes("native LinuxCNC task")), true);
assert.equal(blocked.blockers.some((blocker) => blocker.includes("LinuxCNC task runtime")), true);
const canonicalState = {
machineProfile: "xyzac-trt",
@@ -105,4 +105,65 @@ assert.equal(remap.semanticBoundary, "linuxcnc_machine_file_remap_ready_planner_
assert.equal(remap.satisfied.includes("fiveaxis-remap-machine-file-run"), true);
assert.equal(remap.evidence.machineFileFlags.length, 3);
const taskHalPromoted = createFullLinuxCncExecutionBoundary({
...canonicalState,
programExecution: remap.programExecution || {
...canonicalState.programExecution,
plannerTiming: {
plannerRuntimeReady: true,
semanticBoundary: "linuxcnc_tp_queue_runtime_timing_from_canonical_motion",
},
summary: {
...canonicalState.programExecution.summary,
plannerRuntimeReady: true,
},
},
machineFileStaging: {
status: "staged",
fileCount: 12,
},
machineFileExecution: remap.evidence ? {
sourceMode: "linuxcnc-machine-file-remap-wasm",
summary: { machineFileExecutionReady: true },
resultText: [
"fiveaxis_ini_open=1",
"fiveaxis_remaps_ready=1",
"fiveaxis_file_reached_exit=1",
"fiveaxis_hal_switchkins: rc=0 found=1 value=1",
].join("\n"),
} : null,
taskHalRuntimeReadiness: {
taskRuntimeReady: true,
motionRuntimeReady: true,
halRuntimeReady: true,
halSyncReady: true,
},
taskHalStatus: {
summary: {
taskRuntimeReady: true,
motionRuntimeReady: true,
halRuntimeReady: true,
halSyncReady: true,
taskHalComparisonReady: true,
},
ui: {
taskCycle: 2,
servoCycle: 20,
motionQueueDepth: 0,
halChangedPinCount: 4,
},
},
});
assert.equal(taskHalPromoted.semanticBoundary, "linuxcnc_task_motion_hal_wasm_simulation_runtime");
assert.equal(taskHalPromoted.nativeTaskReady, true);
assert.equal(taskHalPromoted.nativeHalSyncReady, true);
assert.equal(taskHalPromoted.fullLinuxCncProgramExecutionReady, true);
assert.equal(taskHalPromoted.promotionAllowed, true);
assert.equal(taskHalPromoted.hardwareDrive, false);
assert.equal(taskHalPromoted.hostRealtimeKernel, false);
assert.equal(taskHalPromoted.externalUserMProcessReady, false);
assert.equal(taskHalPromoted.toolDbProcessReady, false);
assert.equal(taskHalPromoted.evidence.taskCycle, 2);
console.log("full_execution_boundary_smoke=ok");

View File

@@ -0,0 +1,96 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, resolve } from "node:path";
import { createLinuxCncTaskHalSdk } from "../../../wasm-port/runtime/sdk/src/linuxcnc-task-hal.js";
import { wrapTaskHalSdk } from "../../app/src/runtime/linuxcnc-task-hal-runtime.js";
import { createSimulationStore } from "../../app/src/state/store.js";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const rootDir = resolve(__dirname, "../../..");
const wasmPath = resolve(rootDir, "wasm-port/build/wasm/task-hal/linuxcnc_task_hal.wasm");
const sdk = await createLinuxCncTaskHalSdk({
wasmBinary: readFileSync(wasmPath),
print() {},
printErr(message) {
console.error(message);
},
});
const runtime = wrapTaskHalSdk(sdk);
const store = createSimulationStore({
programLines: [
"G0 X1 Y2 Z-3 A10 C20",
"G1 X2 Y3 Z-4 A11 C21",
"M428",
"G1 X3 Y4 Z-5 A12 C22",
],
activeProgram: "task-hal-store-smoke.ngc",
});
store.dispatch({ type: "ATTACH_TASK_HAL_RUNTIME", runtime });
assert.equal(store.getState().taskHalRuntimeReadiness.taskRuntimeReady, true);
assert.equal(store.getState().taskHalRuntimeReadiness.motionRuntimeReady, true);
assert.equal(store.getState().taskHalRuntimeReadiness.halRuntimeReady, true);
store.dispatch({ type: "TOGGLE_POWER" });
await waitForTaskHal(store);
store.dispatch({ type: "HOME" });
store.dispatch({ type: "SET_MODE", mode: "auto" });
await waitForTaskHal(store);
store.dispatch({ type: "RUN" });
await waitForTaskHal(store);
let state = store.getState();
assert.equal(state.taskHalStatus.summary.taskRuntimeReady, true);
assert.equal(state.taskHalStatus.summary.halSyncReady, true);
assert.equal(state.taskHalStatus.ui.activeLine >= 1, true);
assert.equal(state.axisPose.x >= 1, true);
assert.equal(state.programExecutionSourceMode, "linuxcnc-task-motion-hal-wasm");
assert.equal(state.fullExecutionBoundary.nativeTaskReady, true);
assert.equal(state.fullExecutionBoundary.nativeHalSyncReady, true);
store.dispatch({ type: "SET_MODE", mode: "mdi" });
await waitForTaskHal(store);
store.dispatch({ type: "RUN_MDI", command: "M428" });
await waitForTaskHal(store);
state = store.getState();
assert.equal(state.taskHalStatus.ui.switchkinsType, 1);
assert.equal(state.kinsType, "tcp-xyzac");
assert.equal(state.rtcpState, "on");
store.dispatch({ type: "SET_MODE", mode: "manual" });
await waitForTaskHal(store);
store.dispatch({ type: "JOG", axis: "x", direction: 1, increment: 0.5 });
await waitForTaskHal(store);
state = store.getState();
assert.equal(state.taskHalStatus.motionStatus.motion.teleopMode, 1);
assert.equal(state.programRuntimeFeedback.sourceMode, "linuxcnc-task-motion-hal-wasm");
store.dispatch({ type: "SET_MODE", mode: "auto" });
await waitForTaskHal(store);
store.dispatch({ type: "PAUSE" });
await waitForTaskHal(store);
assert.equal(store.getState().machine.interpState, "paused");
store.dispatch({ type: "RESUME" });
await waitForTaskHal(store);
assert.equal(store.getState().machine.interpState, "reading");
console.log("linuxcnc_task_hal_runtime_smoke=ok");
console.log("task_hal_machine_file_smoke=ok");
console.log("switchkins_remap_hal_sync_smoke=ok");
console.log("browser_task_hal_worker_smoke=ok");
async function waitForTaskHal(store) {
for (let attempt = 0; attempt < 30; attempt += 1) {
if (!store.getState().taskHalExecutionPending) {
await new Promise((resolve) => setTimeout(resolve, 0));
if (!store.getState().taskHalExecutionPending) return store.getState();
}
await new Promise((resolve) => setTimeout(resolve, 0));
}
throw new Error("task/HAL store command did not settle");
}