214 lines
6.7 KiB
JavaScript
214 lines
6.7 KiB
JavaScript
import { readFileSync } from "node:fs";
|
|
import { fileURLToPath } from "node:url";
|
|
import { dirname, resolve } from "node:path";
|
|
import assert from "node:assert/strict";
|
|
|
|
import createLinuxCncTaskHalModule from "../../../build/wasm/task-hal/linuxcnc_task_hal.js";
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
const rootDir = resolve(__dirname, "../../..");
|
|
const wasmPath = resolve(rootDir, "build/wasm/task-hal/linuxcnc_task_hal.wasm");
|
|
|
|
const hal = await createLinuxCncTaskHalModule({
|
|
wasmBinary: readFileSync(wasmPath),
|
|
print() {},
|
|
printErr(message) {
|
|
console.error(message);
|
|
},
|
|
});
|
|
|
|
function allocCString(value) {
|
|
const bytes = hal.lengthBytesUTF8(value) + 1;
|
|
const ptr = hal._malloc(bytes);
|
|
hal.stringToUTF8(value, ptr, bytes);
|
|
return ptr;
|
|
}
|
|
|
|
function withCString(value, fn) {
|
|
const ptr = allocCString(value);
|
|
try {
|
|
return fn(ptr);
|
|
} finally {
|
|
hal._free(ptr);
|
|
}
|
|
}
|
|
|
|
function readJsonWithBuffer(call) {
|
|
const bytes = 32768;
|
|
const ptr = hal._malloc(bytes);
|
|
try {
|
|
const rc = call(ptr, bytes);
|
|
assert.equal(rc, 0);
|
|
return JSON.parse(hal.UTF8ToString(ptr));
|
|
} finally {
|
|
hal._free(ptr);
|
|
}
|
|
}
|
|
|
|
function createPin(name, type, dir = 32) {
|
|
const ptrAddr = hal._malloc(4);
|
|
try {
|
|
const rc = withCString(name, (namePtr) => {
|
|
switch (type) {
|
|
case "bit":
|
|
return hal._hal_pin_bit_new(namePtr, dir, ptrAddr, 1);
|
|
case "float":
|
|
return hal._hal_pin_float_new(namePtr, dir, ptrAddr, 1);
|
|
case "s32":
|
|
return hal._hal_pin_s32_new(namePtr, dir, ptrAddr, 1);
|
|
case "u32":
|
|
return hal._hal_pin_u32_new(namePtr, dir, ptrAddr, 1);
|
|
default:
|
|
throw new Error(`unsupported pin type: ${type}`);
|
|
}
|
|
});
|
|
assert.equal(rc, 0, `${name} create`);
|
|
return hal.getValue(ptrAddr, "i32");
|
|
} finally {
|
|
hal._free(ptrAddr);
|
|
}
|
|
}
|
|
|
|
function setPinFloat(name, value) {
|
|
withCString(name, (namePtr) => assert.equal(hal._lchal_set_pin_float(namePtr, value), 0));
|
|
}
|
|
|
|
function setPinBit(name, value) {
|
|
withCString(name, (namePtr) => assert.equal(hal._lchal_set_pin_bit(namePtr, value ? 1 : 0), 0));
|
|
}
|
|
|
|
function getPin(name) {
|
|
return withCString(name, (namePtr) =>
|
|
readJsonWithBuffer((outPtr, outLen) => hal._lchal_get_pin_json(namePtr, outPtr, outLen)),
|
|
);
|
|
}
|
|
|
|
function getP(name) {
|
|
return withCString(name, (namePtr) => {
|
|
const bytes = 128;
|
|
const outPtr = hal._malloc(bytes);
|
|
try {
|
|
assert.equal(hal._hal_get_p(namePtr, outPtr, bytes), 0);
|
|
return hal.UTF8ToString(outPtr);
|
|
} finally {
|
|
hal._free(outPtr);
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadHalFile(path, text) {
|
|
return withCString(path, (pathPtr) =>
|
|
withCString(text, (textPtr) => hal._lchal_load_hal_file(pathPtr, textPtr)),
|
|
);
|
|
}
|
|
|
|
assert.equal(hal._lchal_init_runtime(), 0);
|
|
assert.notEqual(createPin("motion.analog-out-03", "float"), 0);
|
|
assert.notEqual(createPin("probe.out", "bit"), 0);
|
|
assert.notEqual(createPin("probe.in", "bit", 16), 0);
|
|
assert.notEqual(createPin("motion.program-line", "s32"), 0);
|
|
assert.notEqual(hal._hal_malloc(16), 0);
|
|
|
|
setPinFloat("motion.analog-out-03", 2.5);
|
|
assert.equal(getPin("motion.analog-out-03").value, 2.5);
|
|
assert.equal(getP("motion.analog-out-03"), "2.5");
|
|
|
|
const paramFloatAddr = hal._malloc(8);
|
|
const paramS32Addr = hal._malloc(4);
|
|
try {
|
|
hal.setValue(paramFloatAddr, 7.25, "double");
|
|
hal.setValue(paramS32Addr, -12, "i32");
|
|
withCString("standalone.param-float", (namePtr) =>
|
|
assert.equal(hal._hal_param_float_new(namePtr, 192, paramFloatAddr, 1), 0),
|
|
);
|
|
withCString("standalone.param-s32", (namePtr) =>
|
|
assert.equal(hal._hal_param_s32_new(namePtr, 192, paramS32Addr, 1), 0),
|
|
);
|
|
assert.equal(getP("standalone.param-float"), "7.25");
|
|
assert.equal(getP("standalone.param-s32"), "-12");
|
|
} finally {
|
|
hal._free(paramFloatAddr);
|
|
hal._free(paramS32Addr);
|
|
}
|
|
|
|
withCString("motion.program-line", (namePtr) => {
|
|
const typePtr = hal._malloc(4);
|
|
const valuePtrPtr = hal._malloc(4);
|
|
const connectedPtr = hal._malloc(1);
|
|
try {
|
|
assert.equal(hal._hal_get_pin_value_by_name(namePtr, typePtr, valuePtrPtr, connectedPtr), 0);
|
|
assert.equal(hal.getValue(typePtr, "i32"), 3);
|
|
assert.notEqual(hal.getValue(valuePtrPtr, "i32"), 0);
|
|
} finally {
|
|
hal._free(typePtr);
|
|
hal._free(valuePtrPtr);
|
|
hal._free(connectedPtr);
|
|
}
|
|
});
|
|
|
|
assert.equal(loadHalFile("probe.hal", "net probe-signal probe.out probe.in\n"), 0);
|
|
setPinBit("probe.out", true);
|
|
assert.equal(getPin("probe.in").value, true);
|
|
withCString("probe-signal", (namePtr) => {
|
|
const typePtr = hal._malloc(4);
|
|
const valuePtrPtr = hal._malloc(4);
|
|
const connectedPtr = hal._malloc(1);
|
|
try {
|
|
assert.equal(hal._hal_get_signal_value_by_name(namePtr, typePtr, valuePtrPtr, connectedPtr), 0);
|
|
assert.equal(hal.getValue(typePtr, "i32"), 1);
|
|
assert.notEqual(hal.getValue(valuePtrPtr, "i32"), 0);
|
|
} finally {
|
|
hal._free(typePtr);
|
|
hal._free(valuePtrPtr);
|
|
hal._free(connectedPtr);
|
|
}
|
|
});
|
|
|
|
assert.equal(
|
|
loadHalFile(
|
|
"threads.hal",
|
|
[
|
|
"loadrt trivkins",
|
|
"addf motion-command-handler servo-thread",
|
|
"addf motion-controller servo-thread",
|
|
"",
|
|
].join("\n"),
|
|
),
|
|
0,
|
|
);
|
|
assert.equal(hal._lchal_step_threads(1000000, 2), 0);
|
|
withCString("motion-command-handler", (functPtr) =>
|
|
withCString("servo-thread", (threadPtr) =>
|
|
assert.equal(hal._hal_del_funct_from_thread(functPtr, threadPtr), 0),
|
|
),
|
|
);
|
|
assert.equal(hal._hal_stop_threads(), 0);
|
|
assert.equal(hal._hal_start_threads(), 0);
|
|
assert.equal(loadHalFile("blocked.hal", "loadusr external-user-m\n"), 2);
|
|
|
|
const snapshot = readJsonWithBuffer((outPtr, outLen) => hal._lchal_get_snapshot_json(outPtr, outLen));
|
|
assert.equal(snapshot.semanticBoundary, "linuxcnc_hal_runtime_phase2_minimal");
|
|
assert.equal(snapshot.halRuntimeReady, true);
|
|
assert.equal(snapshot.halSyncReady, false);
|
|
assert.equal(snapshot.nativeHalSyncReady, false);
|
|
assert.equal(snapshot.cycle, 2);
|
|
assert.equal(snapshot.pins["probe.in"].value, true);
|
|
assert.equal(snapshot.signals["probe-signal"].value, true);
|
|
assert.equal(snapshot.params["standalone.param-float"].value, 7.25);
|
|
assert.equal(snapshot.params["standalone.param-s32"].value, -12);
|
|
assert.equal(snapshot.threads["servo-thread"].functions.length >= 1, true);
|
|
assert.equal(snapshot.events.includes("blocked:loadusr"), true);
|
|
assert.equal(
|
|
snapshot.events.includes("call:servo-thread:motion-controller"),
|
|
true,
|
|
);
|
|
assert.equal(snapshot.events.includes("delf:motion-command-handler:servo-thread"), true);
|
|
assert.equal(snapshot.events.includes("threads_stop"), true);
|
|
|
|
console.log("hal_runtime_registry=ok");
|
|
console.log("hal_net_signal_propagation=ok");
|
|
console.log("hal_thread_scheduler=ok");
|
|
console.log("loadusr_blocked_evidence=ok");
|
|
console.log("hal_runtime_wasm_node_smoke=ok");
|