Support XYZAC and XYZBC in shared web machine UI

This commit is contained in:
2026-09-11 06:25:13 +08:00
parent aac1ceb250
commit 02dcc573b0
20 changed files with 318 additions and 146 deletions

View File

@@ -13,11 +13,10 @@ const CONFIG = Object.freeze({
limit_enabled: 1448, joint_min: 1464, joint_max: 1536,
});
const SWITCH = Object.freeze({ switching: 16, active_mode: 20, pending_mode: 24, generation: 28, switch_count: 32, abort_count: 36 });
const TYPE = Object.freeze({ identity: 1, xyzac: 2, userk: 1 });
const TYPE = Object.freeze({ identity: 1, xyzac: 2, xyzbc: 3, userk: 1 });
const MODE = Object.freeze({ identity: 0, xyzac: 1, userk: 2 });
const AXES = Object.freeze(["x", "y", "z", "a", "b", "c", "u", "v", "w"]);
const COORDINATES = Object.freeze(["x", "y", "z", "a", "c"]);
const DEFAULT_LIMITS = Object.freeze({ x: [-200, 200], y: [-100, 100], z: [-120, 120], a: [-100, 50], c: [-36000, 36000] });
const DEFAULT_LIMITS = Object.freeze({ x: [-200, 200], y: [-100, 100], z: [-120, 120], a: [-100, 50], b: [-100, 50], c: [-36000, 36000] });
function call(core, name, returnType, argTypes, args) {
const direct = core?.[`_${name}`] ?? core?.[name];
@@ -31,11 +30,11 @@ function pose(core, pointer) {
return Object.fromEntries(AXES.map((axis, index) => [axis, doubles[index]]));
}
function setCoordinates(core, pointer) {
for (const [index, value] of [..."XYZAC"].entries()) core.HEAPU8[pointer + CONFIG.coordinates + index] = value.charCodeAt(0);
function setCoordinates(core, pointer, coordinates) {
for (const [index, value] of [...coordinates].entries()) core.HEAPU8[pointer + CONFIG.coordinates + index] = value.charCodeAt(0);
}
function configFor(core, pointer, type, options) {
function configFor(core, pointer, type, options, coordinates) {
core.HEAPU8.fill(0, pointer, pointer + CONFIG_SIZE);
const view = new DataView(core.HEAPU8.buffer, pointer, CONFIG_SIZE);
view.setInt32(CONFIG.type, type, true);
@@ -43,14 +42,22 @@ function configFor(core, pointer, type, options) {
view.setUint32(CONFIG.joint_count, 5, true);
view.setUint32(CONFIG.kinematics_joint_count, 5, true);
view.setUint32(CONFIG.extra_joint_count, 0, true);
setCoordinates(core, pointer);
for (const [index, axis] of COORDINATES.entries()) {
setCoordinates(core, pointer, coordinates);
for (const [index, axis] of coordinates.toLowerCase().split("").entries()) {
const limit = options.limits?.[axis] ?? DEFAULT_LIMITS[axis];
core.HEAPU8[pointer + CONFIG.limit_enabled + index] = 1;
view.setFloat64(CONFIG.joint_min + index * 8, Number(limit[0]), true);
view.setFloat64(CONFIG.joint_max + index * 8, Number(limit[1]), true);
}
for (const [key, offset] of Object.entries({ x_rot_point: CONFIG.x_rot_point, y_offset: CONFIG.y_offset, z_offset: CONFIG.z_offset, tool_offset: CONFIG.tool_offset })) {
for (const [key, offset] of Object.entries({
x_rot_point: CONFIG.x_rot_point,
y_rot_point: CONFIG.y_rot_point,
z_rot_point: CONFIG.z_rot_point,
x_offset: CONFIG.x_offset,
y_offset: CONFIG.y_offset,
z_offset: CONFIG.z_offset,
tool_offset: CONFIG.tool_offset,
})) {
view.setFloat64(offset, Number(options[key] ?? 0), true);
}
}
@@ -62,17 +69,21 @@ function resultCode(operation, result) {
export class WasmXyzacKinematics {
constructor(core, options = {}) {
this.core = core;
this.options = { x_rot_point: 0, y_offset: 20, z_offset: 10, tool_offset: 0, limits: DEFAULT_LIMITS, ...options };
this.coordinates = String(options.coordinates ?? "XYZAC").toUpperCase();
if (!["XYZAC", "XYZBC"].includes(this.coordinates)) throw new Error("wasm_trt_coordinates_invalid");
this.kinematicsType = this.coordinates === "XYZBC" ? "xyzbc-trt-kins" : "xyzac-trt-kins";
this.options = { x_rot_point: 0, y_rot_point: 0, z_rot_point: 0, x_offset: 0, y_offset: 20, z_offset: 10, tool_offset: 0, limits: DEFAULT_LIMITS, ...options };
if (this.coordinates === "XYZBC" && options.limits === undefined) this.options.limits = { x: [-200, 200], y: [-100, 100], z: [-120, 120], b: [-100, 50], c: [-36000, 36000] };
this.available = ["sim_kinematics_init", "sim_kinematics_switch_init", "sim_kinematics_switch_register", "sim_kinematics_switch_select", "sim_kinematics_switch_select_physical", "sim_kinematics_switch_forward", "sim_kinematics_switch_inverse", "sim_kinematics_switch_inverse_mode", "sim_kinematics_switch_active", "sim_kinematics_switch_state_hash"].every((name) => typeof core?.[`_${name}`] === "function" || typeof core?.[name] === "function");
this.lastCommand = null;
this.modules = new Map();
this.pointers = [];
if (!this.available || typeof core?._malloc !== "function") return;
for (const [mode, type] of [[MODE.identity, TYPE.identity], [MODE.xyzac, TYPE.xyzac], [MODE.userk, TYPE.userk]]) {
for (const [mode, type] of [[MODE.identity, TYPE.identity], [MODE.xyzac, this.coordinates === "XYZBC" ? TYPE.xyzbc : TYPE.xyzac], [MODE.userk, TYPE.userk]]) {
const config = core._malloc(CONFIG_SIZE);
const kinematics = core._malloc(KINEMATICS_SIZE);
if (!config || !kinematics) throw new Error("wasm_xyzac_allocation_failed");
configFor(core, config, type, this.options);
configFor(core, config, type, this.options, this.coordinates);
const result = call(core, "sim_kinematics_init", "number", ["number", "number"], [kinematics, config]);
if (result !== 0) throw new Error(`wasm_xyzac_init_${result}`);
this.modules.set(mode, { config, kinematics });
@@ -185,10 +196,10 @@ export class WasmXyzacKinematics {
snapshot(currentPose = pose(this.core, this.posePointer), joints = [...this.core.HEAPF64.subarray(this.jointPointer / 8, this.jointPointer / 8 + 5)]) {
const activeMode = this.activeMode;
const nativeType = activeMode === MODE.xyzac ? "xyzac-trt-kins" : activeMode === MODE.userk ? "userk-identity-template" : "identity";
const nativeType = activeMode === MODE.xyzac ? this.kinematicsType : activeMode === MODE.userk ? "userk-identity-template" : "identity";
const view = new DataView(this.core.HEAPU8.buffer, this.switchPointer, SWITCH_SIZE);
const limits = Object.fromEntries(COORDINATES.map((axis) => [axis, [...(this.options.limits?.[axis] ?? DEFAULT_LIMITS[axis])]]));
const unsigned = { schema: "wasm-xyzac-kinematics.v1", active_type: activeMode, native_type: nativeType, coordinates: "XYZAC", joint_count: 5, linear_units: "mm", angular_units: "deg", pose: currentPose, joints: joints.slice(0, 5), offsets: { x_rot_point: this.options.x_rot_point, y_offset: this.options.y_offset, z_offset: this.options.z_offset, tool_offset: this.options.tool_offset }, limits, generation: view.getUint32(SWITCH.generation, true), switch_count: view.getUint32(SWITCH.switch_count, true), abort_count: view.getUint32(SWITCH.abort_count, true), last_command: this.lastCommand, capability: { userk: "linked_identity_template", dynamic_modules: "denied" }, source: "linuxcnc-wasm-sim_kinematics_switch" };
const limits = Object.fromEntries(this.coordinates.toLowerCase().split("").map((axis) => [axis, [...(this.options.limits?.[axis] ?? DEFAULT_LIMITS[axis])]]));
const unsigned = { schema: "wasm-xyzac-kinematics.v1", active_type: activeMode, native_type: nativeType, coordinates: this.coordinates, joint_count: 5, linear_units: "mm", angular_units: "deg", pose: currentPose, joints: joints.slice(0, 5), offsets: { x_rot_point: this.options.x_rot_point, y_rot_point: this.options.y_rot_point, z_rot_point: this.options.z_rot_point, x_offset: this.options.x_offset, y_offset: this.options.y_offset, z_offset: this.options.z_offset, tool_offset: this.options.tool_offset }, limits, generation: view.getUint32(SWITCH.generation, true), switch_count: view.getUint32(SWITCH.switch_count, true), abort_count: view.getUint32(SWITCH.abort_count, true), last_command: this.lastCommand, capability: { userk: "linked_identity_template", dynamic_modules: "denied" }, source: "linuxcnc-wasm-sim_kinematics_switch" };
const hash = call(this.core, "sim_kinematics_switch_state_hash", "number", ["number", "number", "number", "number"], [this.switchPointer, this.jointPointer, 5, this.posePointer]);
return { status: "verified", ...unsigned, wasm_state_hash: `fnv1a64:${BigInt.asUintN(64, BigInt(hash >>> 0)).toString(16).padStart(16, "0")}`, state_hash: fnv1a64(unsigned) };
}