接入 task HAL Web 仿真运行时
This commit is contained in:
213
wasm-port/tests/wasm/node/verify_hal_runtime.mjs
Normal file
213
wasm-port/tests/wasm/node/verify_hal_runtime.mjs
Normal file
@@ -0,0 +1,213 @@
|
||||
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");
|
||||
Reference in New Issue
Block a user