Initial wasm simulator checkpoint
This commit is contained in:
148
web/src/wasm-core.js
Normal file
148
web/src/wasm-core.js
Normal file
@@ -0,0 +1,148 @@
|
||||
const DIALECT = {
|
||||
linuxcnc: 0,
|
||||
fanuc: 1,
|
||||
siemens: 2,
|
||||
};
|
||||
|
||||
const EVENT_TYPE = {
|
||||
1: "error",
|
||||
2: "comment",
|
||||
3: "set-units",
|
||||
4: "set-plane",
|
||||
5: "set-feed",
|
||||
6: "set-spindle",
|
||||
7: "tool-change",
|
||||
8: "dwell",
|
||||
9: "rapid",
|
||||
10: "linear-feed",
|
||||
11: "arc-feed",
|
||||
12: "probe",
|
||||
13: "program-end",
|
||||
14: "rtcp-pivot",
|
||||
15: "kinematics-switch",
|
||||
16: "rtcp-state",
|
||||
17: "set-g5x-offset",
|
||||
18: "set-g92-offset",
|
||||
19: "set-xy-rotation",
|
||||
};
|
||||
|
||||
const EVENT_OFFSETS = {
|
||||
type: 4,
|
||||
line: 8,
|
||||
plane: 12,
|
||||
tool: 16,
|
||||
feed: 24,
|
||||
spindle: 32,
|
||||
dwellSeconds: 40,
|
||||
start: 48,
|
||||
end: 120,
|
||||
center: 192,
|
||||
arcTurns: 264,
|
||||
reserved: 268,
|
||||
};
|
||||
|
||||
function readPose(view, offset) {
|
||||
return {
|
||||
x: view.getFloat64(offset + 0, true),
|
||||
y: view.getFloat64(offset + 8, true),
|
||||
z: view.getFloat64(offset + 16, true),
|
||||
a: view.getFloat64(offset + 24, true),
|
||||
b: view.getFloat64(offset + 32, true),
|
||||
c: view.getFloat64(offset + 40, true),
|
||||
u: view.getFloat64(offset + 48, true),
|
||||
v: view.getFloat64(offset + 56, true),
|
||||
w: view.getFloat64(offset + 64, true),
|
||||
};
|
||||
}
|
||||
|
||||
function readEvent(module, ptr) {
|
||||
const view = new DataView(module.HEAPU8.buffer, ptr, 272);
|
||||
return {
|
||||
type: EVENT_TYPE[view.getInt32(EVENT_OFFSETS.type, true)] ?? "unknown",
|
||||
line: view.getInt32(EVENT_OFFSETS.line, true),
|
||||
plane: view.getInt32(EVENT_OFFSETS.plane, true),
|
||||
tool: view.getInt32(EVENT_OFFSETS.tool, true),
|
||||
feed: view.getFloat64(EVENT_OFFSETS.feed, true),
|
||||
spindle: view.getFloat64(EVENT_OFFSETS.spindle, true),
|
||||
dwellSeconds: view.getFloat64(EVENT_OFFSETS.dwellSeconds, true),
|
||||
start: readPose(view, EVENT_OFFSETS.start),
|
||||
end: readPose(view, EVENT_OFFSETS.end),
|
||||
center: readPose(view, EVENT_OFFSETS.center),
|
||||
arcTurns: view.getInt32(EVENT_OFFSETS.arcTurns, true),
|
||||
reserved: view.getInt32(EVENT_OFFSETS.reserved, true),
|
||||
};
|
||||
}
|
||||
|
||||
async function loadModuleFactory() {
|
||||
const module = await import("/cnc_sim.js");
|
||||
return module.default ?? module.createCncSimModule ?? globalThis.createCncSimModule;
|
||||
}
|
||||
|
||||
export async function createWasmSimulator() {
|
||||
const createModule = await loadModuleFactory();
|
||||
if (!createModule) {
|
||||
throw new Error("cnc_sim.js did not export createCncSimModule");
|
||||
}
|
||||
|
||||
const module = await createModule();
|
||||
const create = module.cwrap("cnc_sim_create", "number", []);
|
||||
const destroy = module.cwrap("cnc_sim_destroy", null, ["number"]);
|
||||
const reset = module.cwrap("cnc_sim_reset", null, ["number"]);
|
||||
const setDialect = module.cwrap("cnc_sim_set_dialect", "number", ["number", "number"]);
|
||||
const setCallback = module.cwrap("cnc_sim_set_event_callback", "number", ["number", "number", "number"]);
|
||||
const loadConfig = module.cwrap("cnc_sim_load_config_json", "number", ["number", "number", "number"]);
|
||||
const parseProgram = module.cwrap("cnc_sim_parse_program", "number", ["number", "number", "number"]);
|
||||
const lastError = module.cwrap("cnc_sim_last_error", "number", ["number"]);
|
||||
|
||||
const handle = create();
|
||||
let callbackPtr = 0;
|
||||
|
||||
return {
|
||||
parse(program, dialect = "linuxcnc", options = {}) {
|
||||
const events = [];
|
||||
reset(handle);
|
||||
setDialect(handle, DIALECT[dialect] ?? DIALECT.linuxcnc);
|
||||
|
||||
const config = JSON.stringify({
|
||||
backend: options.backend ?? "smoke",
|
||||
...(options.rtcp ? { rtcp: options.rtcp } : {}),
|
||||
});
|
||||
const configBytes = module.lengthBytesUTF8(config) + 1;
|
||||
const configPtr = module._malloc(configBytes);
|
||||
module.stringToUTF8(config, configPtr, configBytes);
|
||||
const configRc = loadConfig(handle, configPtr, configBytes - 1);
|
||||
module._free(configPtr);
|
||||
if (configRc !== 0) {
|
||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
||||
}
|
||||
|
||||
if (callbackPtr) {
|
||||
module.removeFunction(callbackPtr);
|
||||
}
|
||||
callbackPtr = module.addFunction((eventPtr) => {
|
||||
events.push(readEvent(module, eventPtr));
|
||||
return 0;
|
||||
}, "iii");
|
||||
setCallback(handle, callbackPtr, 0);
|
||||
|
||||
const bytes = module.lengthBytesUTF8(program) + 1;
|
||||
const ptr = module._malloc(bytes);
|
||||
module.stringToUTF8(program, ptr, bytes);
|
||||
const rc = parseProgram(handle, ptr, bytes - 1);
|
||||
module._free(ptr);
|
||||
|
||||
if (rc !== 0) {
|
||||
throw new Error(module.UTF8ToString(lastError(handle)));
|
||||
}
|
||||
return events;
|
||||
},
|
||||
|
||||
dispose() {
|
||||
if (callbackPtr) {
|
||||
module.removeFunction(callbackPtr);
|
||||
callbackPtr = 0;
|
||||
}
|
||||
destroy(handle);
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user