229 lines
6.3 KiB
JavaScript
229 lines
6.3 KiB
JavaScript
import createLinuxCncInterpModule from "../../../build/wasm/core/linuxcnc_interp.js";
|
|
import { createVirtualHalWasmBridgeSnapshot } from "./linuxcnc-hal.js";
|
|
|
|
function allocCString(mod, value) {
|
|
const bytes = mod.lengthBytesUTF8(value) + 1;
|
|
const ptr = mod._malloc(bytes);
|
|
mod.stringToUTF8(value, ptr, bytes);
|
|
return ptr;
|
|
}
|
|
|
|
function ensureParentPath(mod, path) {
|
|
const parts = path.split("/").filter(Boolean);
|
|
let current = "";
|
|
for (const part of parts.slice(0, -1)) {
|
|
current += `/${part}`;
|
|
try {
|
|
mod.FS.mkdir(current);
|
|
} catch {
|
|
// Directory already exists.
|
|
}
|
|
}
|
|
}
|
|
|
|
function copyResultString(mod, resultPtr, functionName) {
|
|
if (!resultPtr) {
|
|
throw new Error(`${functionName} returned null`);
|
|
}
|
|
return mod.UTF8ToString(resultPtr);
|
|
}
|
|
|
|
function callStringResult(mod, functionName, ...values) {
|
|
const ptrs = values.map((value) => allocCString(mod, value));
|
|
let resultPtr = 0;
|
|
try {
|
|
resultPtr = mod[`_${functionName}`](...ptrs);
|
|
return copyResultString(mod, resultPtr, functionName);
|
|
} finally {
|
|
if (resultPtr) {
|
|
mod._lcinterp_free_string(resultPtr);
|
|
}
|
|
for (const ptr of ptrs) {
|
|
mod._free(ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
function requireWasmFunction(mod, functionName) {
|
|
const fn = mod[`_${functionName}`];
|
|
if (typeof fn !== "function") {
|
|
throw new Error(`linuxcnc interpreter WASM missing ${functionName}; rebuild wasm-port/tools/build_wasm_core.sh`);
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
function callVoidWithStringsAndNumbers(mod, functionName, strings, numbers = []) {
|
|
const fn = requireWasmFunction(mod, functionName);
|
|
const ptrs = strings.map((value) => allocCString(mod, value));
|
|
try {
|
|
return fn(...ptrs, ...numbers);
|
|
} finally {
|
|
for (const ptr of ptrs) {
|
|
mod._free(ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function createLinuxCncInterpSdk(moduleOptions = {}) {
|
|
const mod = await createLinuxCncInterpModule(moduleOptions);
|
|
|
|
return {
|
|
module: mod,
|
|
|
|
hasWasmFunction(functionName) {
|
|
return typeof mod[`_${functionName}`] === "function";
|
|
},
|
|
|
|
writeTextFile(path, text) {
|
|
ensureParentPath(mod, path);
|
|
mod.FS.writeFile(path, text, { encoding: "utf8" });
|
|
},
|
|
|
|
readTextFile(path) {
|
|
return mod.FS.readFile(path, { encoding: "utf8" });
|
|
},
|
|
|
|
runSimConfigProgram({
|
|
iniPath,
|
|
programPath,
|
|
files = [],
|
|
executionMode = "fileWithIni",
|
|
}) {
|
|
for (const file of files) {
|
|
ensureParentPath(mod, file.path);
|
|
mod.FS.writeFile(file.path, file.text, { encoding: "utf8" });
|
|
if (file.executable) {
|
|
mod.FS.chmod(file.path, 0o755);
|
|
}
|
|
}
|
|
|
|
if (executionMode === "fiveAxisRemap") {
|
|
return callStringResult(
|
|
mod,
|
|
"lcinterp_run_fiveaxis_remap_file",
|
|
programPath,
|
|
iniPath,
|
|
);
|
|
}
|
|
if (executionMode === "fileWithIni") {
|
|
return callStringResult(mod, "lcinterp_run_file_with_ini", programPath, iniPath);
|
|
}
|
|
throw new Error(`unsupported sim config execution mode: ${executionMode}`);
|
|
},
|
|
|
|
runProgram(programText) {
|
|
return callStringResult(mod, "lcinterp_run_program", programText);
|
|
},
|
|
|
|
runProgramWithIni(programText, iniPath) {
|
|
return callStringResult(mod, "lcinterp_run_program_with_ini", programText, iniPath);
|
|
},
|
|
|
|
runFile(path) {
|
|
return callStringResult(mod, "lcinterp_run_file", path);
|
|
},
|
|
|
|
runFileWithIni(path, iniPath) {
|
|
return callStringResult(mod, "lcinterp_run_file_with_ini", path, iniPath);
|
|
},
|
|
|
|
runFileWithIniContinueOnError(path, iniPath) {
|
|
return callStringResult(
|
|
mod,
|
|
"lcinterp_run_file_with_ini_continue_on_error",
|
|
path,
|
|
iniPath,
|
|
);
|
|
},
|
|
|
|
runFiveAxisRemapFile(path, iniPath) {
|
|
return callStringResult(mod, "lcinterp_run_fiveaxis_remap_file", path, iniPath);
|
|
},
|
|
|
|
runRemapFile(path, iniPath) {
|
|
return callStringResult(mod, "lcinterp_run_remap_file", path, iniPath);
|
|
},
|
|
|
|
runRemapFileContinueOnError(path, iniPath) {
|
|
return callStringResult(mod, "lcinterp_run_remap_file_continue_on_error", path, iniPath);
|
|
},
|
|
|
|
runRemapIoMdiSequence(iniPath) {
|
|
return callStringResult(mod, "lcinterp_run_remap_io_mdi_sequence", iniPath);
|
|
},
|
|
|
|
restoreParameters(path) {
|
|
return callStringResult(mod, "lcinterp_restore_parameters", path);
|
|
},
|
|
|
|
saveParameters(path, values) {
|
|
const assignments = Object.entries(values)
|
|
.map(([parameter, value]) => `${parameter} ${value}`)
|
|
.join("\n");
|
|
return callStringResult(mod, "lcinterp_save_parameters", path, assignments);
|
|
},
|
|
|
|
loadToolTable(path, options = {}) {
|
|
const functionName = options.randomToolChanger
|
|
? "lcinterp_load_tool_table_random"
|
|
: "lcinterp_load_tool_table";
|
|
return callStringResult(mod, functionName, path);
|
|
},
|
|
|
|
saveToolTable(path) {
|
|
return callStringResult(mod, "lcinterp_save_tool_table", path);
|
|
},
|
|
|
|
probeInitAndSynch() {
|
|
return callStringResult(mod, "lcinterp_probe_init_and_synch");
|
|
},
|
|
|
|
probeIndexer() {
|
|
return callStringResult(mod, "lcinterp_probe_indexer");
|
|
},
|
|
|
|
probeNamedParameters(iniPath) {
|
|
return callStringResult(mod, "lcinterp_probe_named_parameters", iniPath);
|
|
},
|
|
|
|
resetHal() {
|
|
requireWasmFunction(mod, "lcinterp_hal_reset")();
|
|
},
|
|
|
|
setHalValue({ kind = "pin", name, type = "HAL_FLOAT", value = 0, connected = true }) {
|
|
if (!name) {
|
|
throw new Error("setHalValue requires a HAL name");
|
|
}
|
|
return callVoidWithStringsAndNumbers(
|
|
mod,
|
|
"lcinterp_hal_set_value",
|
|
[kind, name, type],
|
|
[Number(value) || 0, connected === false ? 0 : 1],
|
|
);
|
|
},
|
|
|
|
applyVirtualHalSnapshot(snapshot, options = {}) {
|
|
if (options.reset !== false) {
|
|
this.resetHal();
|
|
}
|
|
for (const value of snapshot?.values ?? []) {
|
|
this.setHalValue(value);
|
|
}
|
|
return {
|
|
apiName: "linuxcnc-wasm-hal-apply-result",
|
|
applied: snapshot?.values?.length ?? 0,
|
|
reset: options.reset !== false,
|
|
source: snapshot?.source ?? "unknown",
|
|
};
|
|
},
|
|
|
|
applyVirtualHalState(halState, options = {}) {
|
|
return this.applyVirtualHalSnapshot(createVirtualHalWasmBridgeSnapshot(halState), options);
|
|
},
|
|
|
|
probeHalNamed(name) {
|
|
return callStringResult(mod, "lcinterp_probe_hal_named", name);
|
|
},
|
|
};
|
|
}
|