接入 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

@@ -1,5 +1,6 @@
export { createLinuxCncIniSdk } from "./linuxcnc-ini.js";
export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js";
export { createLinuxCncTaskHalSdk } from "./linuxcnc-task-hal.js";
export {
createLinuxCncKinematicsSdk,
linuxCncKinematicsWasmFile,

View File

@@ -0,0 +1,128 @@
import createLinuxCncTaskHalModule from "../../../build/wasm/task-hal/linuxcnc_task_hal.js";
const SEMANTIC_BOUNDARY = "linuxcnc_task_motion_hal_wasm_phase4_minimal";
function allocCString(mod, value) {
const text = String(value ?? "");
const bytes = mod.lengthBytesUTF8(text) + 1;
const ptr = mod._malloc(bytes);
mod.stringToUTF8(text, ptr, bytes);
return ptr;
}
function withCString(mod, value, fn) {
const ptr = allocCString(mod, value);
try {
return fn(ptr);
} finally {
mod._free(ptr);
}
}
function requireWasmFunction(mod, functionName) {
const fn = mod[`_${functionName}`];
if (typeof fn !== "function") {
throw new Error(`linuxcnc task/HAL WASM missing ${functionName}; rebuild wasm-port/tools/build_task_hal_wasm.sh`);
}
return fn;
}
function readJson(mod, functionName) {
const fn = requireWasmFunction(mod, functionName);
let bytes = 131072;
for (let attempt = 0; attempt < 2; attempt += 1) {
const ptr = mod._malloc(bytes);
try {
const rc = fn(ptr, bytes);
if (rc === 0) {
return JSON.parse(mod.UTF8ToString(ptr));
}
if (rc > bytes) {
bytes = rc;
continue;
}
throw new Error(`${functionName} failed with rc=${rc}`);
} finally {
mod._free(ptr);
}
}
throw new Error(`${functionName} output exceeded buffer`);
}
function callWithJson(mod, functionName, payload) {
const fn = requireWasmFunction(mod, functionName);
return withCString(mod, JSON.stringify(payload ?? {}), (ptr) => fn(ptr));
}
export async function createLinuxCncTaskHalSdk(moduleOptions = {}) {
const mod = await createLinuxCncTaskHalModule(moduleOptions);
return {
apiName: "linuxcnc-task-hal-wasm-sdk",
semanticBoundary: SEMANTIC_BOUNDARY,
module: mod,
readiness() {
return {
apiName: "linuxcnc-task-hal-wasm-sdk-readiness",
loaded: true,
semanticBoundary: SEMANTIC_BOUNDARY,
taskRuntimeReady: typeof mod._lctask_init_session === "function",
motionRuntimeReady: typeof mod._lcmot_step_servo === "function",
halRuntimeReady: typeof mod._lchal_get_snapshot_json === "function",
nativeTaskReady: false,
nativeHalSyncReady: false,
};
},
initSession(session = {}) {
const rc = callWithJson(mod, "lctask_init_session", session);
if (rc !== 0) {
throw new Error(`lctask_init_session failed with rc=${rc}`);
}
return rc;
},
stageFile(path, text) {
return withCString(mod, path, (pathPtr) =>
withCString(mod, text, (textPtr) =>
requireWasmFunction(mod, "lctask_stage_file")(pathPtr, textPtr),
),
);
},
openProgram(path) {
return withCString(mod, path, (pathPtr) =>
requireWasmFunction(mod, "lctask_open_program")(pathPtr),
);
},
sendCommand(command) {
return callWithJson(mod, "lctask_send_command_json", command);
},
runCycles({
taskPeriodNs = 10000000,
servoPeriodNs = 1000000,
taskCycles = 1,
} = {}) {
return requireWasmFunction(mod, "lctask_run_cycles")(
Number(taskPeriodNs) || 10000000,
Number(servoPeriodNs) || 1000000,
Number(taskCycles) || 0,
);
},
readStatus() {
return readJson(mod, "lctask_read_status_json");
},
readEvents() {
return readJson(mod, "lctask_read_events_json");
},
resetSession() {
return requireWasmFunction(mod, "lctask_reset_session")();
},
};
}