Display live WASM machining parameters
This commit is contained in:
@@ -196,6 +196,13 @@ export class SimSession {
|
||||
source_hash: program.source_hash,
|
||||
state_hash: program.state_hash,
|
||||
active_gcodes: program.active_gcodes,
|
||||
feed: program.feed,
|
||||
speed: program.speed,
|
||||
units: program.units,
|
||||
distance_mode: program.distance_mode,
|
||||
motion_control: program.motion_control,
|
||||
motion_control_mode: program.motion_control_mode,
|
||||
path_tolerance: program.path_tolerance,
|
||||
} : null;
|
||||
const trace = Array.isArray(statePayload.motion_trace) ? statePayload.motion_trace : [];
|
||||
const motionSample = motionDelta ? (trace.at(-1) ?? {
|
||||
@@ -239,12 +246,14 @@ export class SimSession {
|
||||
};
|
||||
}
|
||||
|
||||
wasmMotionProjection() {
|
||||
wasmMotionProjection(program = null) {
|
||||
const wasmMotion = this.wasmMotion.read();
|
||||
const position = wasmMotion.status === "verified" ? wasmMotion.state.position : { x: 0, y: 0, z: 0, a: 0, b: 0, c: 0, u: 0, v: 0, w: 0 };
|
||||
const home = this.wasmHomeLimit.read();
|
||||
const io = this.xyzacIo?.canonical?.();
|
||||
const ioState = io && typeof io === "object" && io.status !== "blocked" ? io : null;
|
||||
const sourceProgram = program ?? this.xyzacProgram?.snapshot();
|
||||
const spindle = ioState?.spindles?.[0] ?? null;
|
||||
const kinematics = this.xyzacKinematics?.snapshot?.();
|
||||
const plannerJoints = wasmMotion.status === "verified" && Array.isArray(wasmMotion.state.joint_commanded) && wasmMotion.state.joint_commanded.length === 5 ? [...wasmMotion.state.joint_commanded] : null;
|
||||
const kinematicsJoints = plannerJoints ?? (kinematics?.joints?.length === 5 ? [...kinematics.joints] : null);
|
||||
@@ -255,8 +264,14 @@ export class SimSession {
|
||||
axes: { x: position.x, y: position.y, z: position.z },
|
||||
joints: kinematicsJoints ?? (home.status === "verified" && Array.isArray(home.state.joints) ? [...home.state.joints] : [position.x, position.y, position.z, position.a ?? 0, position.c ?? 0]),
|
||||
velocity: wasmMotion.status === "verified" ? wasmMotion.state.velocity : 0,
|
||||
velocity_mm_min: Number(wasmMotion.state?.velocity_mm_min ?? 0),
|
||||
feed_command_mm_min: Number(sourceProgram?.feed ?? 0),
|
||||
feed_override: Number(wasmMotion.state?.feed_override ?? 1),
|
||||
spindle_override: Number(ioState?.spindles?.[0]?.override ?? 1),
|
||||
spindle_speed_rpm: Number(spindle?.speed_rpm ?? 0),
|
||||
spindle_command_rpm: Number(spindle?.command_rpm ?? 0),
|
||||
spindle_enabled: Boolean(spindle?.enabled),
|
||||
spindle_at_speed: Boolean(spindle?.at_speed),
|
||||
velocity_scale: Number(wasmMotion.state?.velocity_scale ?? 1),
|
||||
jog_scale: Number(wasmMotion.state?.jog_scale ?? 1),
|
||||
jog_velocity_linear: Number(wasmMotion.state?.jog_velocity_linear ?? 0),
|
||||
@@ -290,6 +305,17 @@ export class SimSession {
|
||||
return { ...unsigned, state_hash: fnv1a64(unsigned) };
|
||||
}
|
||||
|
||||
syncXyzacProgramSpindle(program) {
|
||||
if (!this.xyzacIo || !program || typeof program !== "object") return { status: "skipped" };
|
||||
const mode = Number(program.spindle_mode ?? 5);
|
||||
const rpm = Number(program.speed ?? 0);
|
||||
const enabled = (mode === 3 || mode === 4) && Number.isFinite(rpm) && rpm > 0;
|
||||
const signedRpm = mode === 4 ? -rpm : rpm;
|
||||
const current = this.xyzacIo.canonical()?.spindles?.[0];
|
||||
if (current && Boolean(current.enabled) === enabled && Math.abs(Number(current.command_rpm ?? 0) - (enabled ? signedRpm * Number(current.override ?? 1) : 0)) < 1e-9) return { status: "skipped" };
|
||||
return this.xyzacIo.command("SPINDLE_COMMAND", { spindle: 0, rpm: signedRpm, enabled });
|
||||
}
|
||||
|
||||
loadXyzacSubroutines(programPath) {
|
||||
if (!this.xyzacProgram) return { status: "verified", count: 0 };
|
||||
const requirement = this.xyzacProgram.requiresSubroutines();
|
||||
@@ -323,7 +349,7 @@ export class SimSession {
|
||||
}
|
||||
|
||||
panelSnapshot({ task = this.task.export(), program = this.xyzacProgram ? this.xyzacProgram.snapshot() : this.program.loaded ? this.program.snapshot() : null, input = {}, tick = this.tick, motionTrace = null, includeToolpath = input.kind !== "SIM_STEP" || !this.xyzacProgram, includeConfiguration = input.kind !== "SIM_STEP" || !this.xyzacProgram, includeStableRuntime = input.kind !== "SIM_STEP" || !this.xyzacProgram } = {}) {
|
||||
const motion = this.xyzacProgram ? this.wasmMotionProjection() : this.motion.export();
|
||||
const motion = this.xyzacProgram ? this.wasmMotionProjection(program) : this.motion.export();
|
||||
const wasmMotion = this.wasmMotion.read();
|
||||
const homeLimit = this.wasmHomeLimit.read();
|
||||
const ioState = this.xyzacIo?.canonical?.() ?? {};
|
||||
@@ -1681,6 +1707,8 @@ export class SimSession {
|
||||
}
|
||||
const step = this.xyzacProgram.step();
|
||||
if (step.status !== "verified") throw new Error(step.code);
|
||||
const spindleSync = this.syncXyzacProgramSpindle(step.program);
|
||||
if (spindleSync.status === "error") throw new Error(spindleSync.code ?? "wasm_program_spindle_sync_failed");
|
||||
// Path control is produced by the WASM interpreter and
|
||||
// applied to the WASM trajectory queue before enqueue.
|
||||
const motionControlMode = Number(step.program?.motion_control_mode ?? 0);
|
||||
|
||||
@@ -45,6 +45,8 @@ assert.equal(halfStep.payload.status, "verified");
|
||||
assert.equal(session.wasmMotion.controls.feed_override, 0.5);
|
||||
assert.equal(halfStep.payload.panel_snapshot.overrides.feed, 0.5);
|
||||
assert.ok(halfStep.payload.wasm_motion.state.velocity > 0, JSON.stringify(halfStep.payload.wasm_motion.state));
|
||||
assert.equal(halfStep.payload.motion.velocity_mm_min, halfStep.payload.wasm_motion.state.velocity_mm_min);
|
||||
assert.ok(halfStep.payload.motion.velocity_mm_min > 0);
|
||||
|
||||
const fullOverride = send("SET_OVERRIDE", { feed: 2 });
|
||||
assert.equal(fullOverride.payload.status, "verified");
|
||||
|
||||
@@ -72,7 +72,7 @@ const contractSource = await readFile(new URL("../react/src/xyzac-panel-contract
|
||||
const sessionSource = await readFile(new URL("./sim-session.mjs", import.meta.url), "utf8");
|
||||
const tpSource = await readFile(new URL("./wasm-linuxcnc-tp.mjs", import.meta.url), "utf8");
|
||||
const combined = `${appSource}\n${panelSource}\n${contractSource}`;
|
||||
for (const symbol of ["XyzacTrtPanel", "XyzacProgramPane", "XyzacKinematicsControl", "XyzacManualPanel", "XyzacDiagnostics", "KINEMATICS_SELECT", "VISUAL_CLEAR", "POWER", "switchkinsXyzac", "Operation trace", "Alarms / recovery"]) assert.ok(combined.includes(symbol), symbol);
|
||||
for (const symbol of ["XyzacTrtPanel", "XyzacProgramPane", "XyzacKinematicsControl", "XyzacManualPanel", "XyzacLiveParameters", "XyzacDiagnostics", "Spindle", "Feed actual", "TP velocity", "Live parameters", "KINEMATICS_SELECT", "VISUAL_CLEAR", "POWER", "switchkinsXyzac", "Operation trace", "Alarms / recovery"]) assert.ok(combined.includes(symbol), symbol);
|
||||
for (const symbol of ["wasmSnapshotReady", "manualReady", "sourceJogScale", "sourceVelocityScale", "spindleOverrideSource", "SET_JOG_SCALE", "SET_MAX_VELOCITY", "SET_SPINDLE_OVERRIDE", "WASM PENDING", "LinuxCNC WASM override-limits command unavailable"]) assert.ok(appSource.includes(symbol), symbol);
|
||||
for (const forbidden of [/defaultValue=\"50\"/, /defaultValue=\"100\"/, /setBlockDelete/, /setOptionalStop/, /state\.status === \"running\" \? \"READING\"/]) assert.equal(forbidden.test(appSource), false, String(forbidden));
|
||||
for (const forbidden of [/_sim_/, /_malloc/, /\bHEAP(?:U|F|I)?\d*\b/, /navigator\.storage/, /\bOPFS\b/i, /postMessage\s*\(/]) assert.equal(forbidden.test(combined), false, String(forbidden));
|
||||
|
||||
@@ -17,7 +17,7 @@ function toPose(values) { return Object.fromEntries(["x", "y", "z", "a", "b", "c
|
||||
export class WasmLinuxcncTp {
|
||||
constructor(core, { cycleTime = 0.001, velocityLimit = 35, accelerationLimit = 300, limits = {} } = {}) {
|
||||
this.core = core;
|
||||
this.available = ["sim_linuxcnc_tp_size", "sim_linuxcnc_tp_queue_capacity", "sim_linuxcnc_tp_init", "sim_linuxcnc_tp_read_state", "sim_linuxcnc_tp_step", "sim_linuxcnc_tp_idle", "sim_linuxcnc_tp_add_line", "sim_linuxcnc_tp_set_profile", "sim_linuxcnc_tp_set_feed_override", "sim_linuxcnc_tp_set_velocity_scale", "sim_linuxcnc_tp_set_jog_scale", "sim_linuxcnc_tp_set_axis_limit", "sim_linuxcnc_tp_jog", "sim_linuxcnc_tp_velocity_scale", "sim_linuxcnc_tp_velocity_limit", "sim_linuxcnc_tp_jog_scale", "sim_linuxcnc_tp_jog_velocity", "sim_linuxcnc_tp_feed_override", "sim_linuxcnc_tp_set_dynamics", "sim_linuxcnc_tp_set_paused", "sim_linuxcnc_tp_abort", "sim_linuxcnc_tp_set_goal_position", "sim_linuxcnc_tp_queue_depth", "sim_linuxcnc_tp_queue_segment", "sim_linuxcnc_tp_restore_progress"].every((name) => typeof core?.[`_${name}`] === "function" || typeof core?.[name] === "function");
|
||||
this.available = ["sim_linuxcnc_tp_size", "sim_linuxcnc_tp_queue_capacity", "sim_linuxcnc_tp_init", "sim_linuxcnc_tp_read_state", "sim_linuxcnc_tp_step", "sim_linuxcnc_tp_idle", "sim_linuxcnc_tp_add_line", "sim_linuxcnc_tp_set_profile", "sim_linuxcnc_tp_set_feed_override", "sim_linuxcnc_tp_set_velocity_scale", "sim_linuxcnc_tp_set_jog_scale", "sim_linuxcnc_tp_set_axis_limit", "sim_linuxcnc_tp_jog", "sim_linuxcnc_tp_velocity_scale", "sim_linuxcnc_tp_velocity_limit", "sim_linuxcnc_tp_velocity_mm_min", "sim_linuxcnc_tp_jog_scale", "sim_linuxcnc_tp_jog_velocity", "sim_linuxcnc_tp_feed_override", "sim_linuxcnc_tp_set_dynamics", "sim_linuxcnc_tp_set_paused", "sim_linuxcnc_tp_abort", "sim_linuxcnc_tp_set_goal_position", "sim_linuxcnc_tp_queue_depth", "sim_linuxcnc_tp_queue_segment", "sim_linuxcnc_tp_restore_progress"].every((name) => typeof core?.[`_${name}`] === "function" || typeof core?.[name] === "function");
|
||||
this.cycleTime = cycleTime;
|
||||
this.velocityLimit = velocityLimit;
|
||||
this.accelerationLimit = accelerationLimit;
|
||||
@@ -192,7 +192,8 @@ export class WasmLinuxcncTp {
|
||||
this.controls.feed_override = feedOverride;
|
||||
const position = toPose(doubles);
|
||||
const joints = this.commandedJoints(position, this.activeTag);
|
||||
const state = { position, velocity: doubles[9], acceleration: this.accelerationLimit, progress: doubles[10], tick: ints[0], queue_depth: this.queueDepthOverride === null ? ints[1] : this.queueDepthOverride, active_queue: activeId ? 1 : 0, active_id: activeId, active_tag: this.activeTag, joint_commanded: joints, joint_actual: joints.slice(), motion_type: this.core.HEAP32[this.statePointer / 4 + 25], status: this.core.HEAP32[this.statePointer / 4 + 26], exec_state: this.executionState, velocity_limit: velocityLimit, velocity_scale: velocityScale, jog_velocity_linear: this.jogVelocity("x"), jog_velocity_angular: this.jogVelocity("a"), feed_override: feedOverride, jog_scale: jogScale, source: "linuxcnc-source-tp" };
|
||||
const velocityMmMin = Number(call(this.core, "sim_linuxcnc_tp_velocity_mm_min", "number", ["number"], [this.planner]));
|
||||
const state = { position, velocity: doubles[9], velocity_mm_min: velocityMmMin, acceleration: this.accelerationLimit, progress: doubles[10], tick: ints[0], queue_depth: this.queueDepthOverride === null ? ints[1] : this.queueDepthOverride, active_queue: activeId ? 1 : 0, active_id: activeId, active_tag: this.activeTag, joint_commanded: joints, joint_actual: joints.slice(), motion_type: this.core.HEAP32[this.statePointer / 4 + 25], status: this.core.HEAP32[this.statePointer / 4 + 26], exec_state: this.executionState, velocity_limit: velocityLimit, velocity_scale: velocityScale, jog_velocity_linear: this.jogVelocity("x"), jog_velocity_angular: this.jogVelocity("a"), feed_override: feedOverride, jog_scale: jogScale, source: "linuxcnc-source-tp" };
|
||||
this.lastState = state;
|
||||
return { status: "verified", state };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user