Files
cnc_wams/wasm-port/tests/wasm/node/verify_hal_runtime.mjs

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");