Align AXIS Web control lifecycle with LinuxCNC

This commit is contained in:
2026-09-07 13:47:10 +08:00
parent 35adac6471
commit 0a679b5eeb
34 changed files with 2598 additions and 177 deletions

View File

@@ -78,6 +78,7 @@ export class WasmProgramRuntime {
this.executionLines = [];
this.executionLineMetadata = [];
this.rapidVelocity = 35;
this.synchronizedPosition = Object.fromEntries(AXES.map((axis) => [axis, 0]));
if (!this.available || typeof core?._malloc !== "function") return;
this.pointer = core._malloc(call(core, "sim_program_size", "number", [], []));
this.motionPointer = core._malloc(SEGMENT_BYTES);
@@ -137,6 +138,8 @@ export class WasmProgramRuntime {
try {
const loaded = temporary.load(this.source, this.path);
if (loaded.status !== "verified") return loaded;
const synchronized = temporary.synchronizePosition(this.synchronizedPosition);
if (synchronized.status !== "verified") return synchronized;
temporary.setRapidVelocity(this.rapidVelocity);
for (const subroutine of this.subroutines) {
const added = temporary.addSubroutine(subroutine.name, subroutine.source, subroutine.path);
@@ -158,6 +161,15 @@ export class WasmProgramRuntime {
}
start() { return this.invoke("sim_program_start", [], "program_start"); }
synchronizePosition(value) {
const position = Object.fromEntries(AXES.map((axis) => [axis, Number(value?.[axis] ?? 0)]));
if (Object.values(position).some((coordinate) => !Number.isFinite(coordinate))) return { status: "error", code: "program_position_nonfinite" };
this.core.HEAPF64.set(AXES.map((axis) => position[axis]), this.samplePosePointer / 8);
const result = call(this.core, "sim_program_set_position", "number", ["number", "number"], [this.pointer, this.samplePosePointer]);
if (result !== 0) return programError("program_set_position", result);
this.synchronizedPosition = position;
return { status: "verified", command: "sim_program_set_position", position: { ...position } };
}
setRapidVelocity(velocity) {
if (!Number.isFinite(Number(velocity)) || Number(velocity) <= 0) return { status: "error", code: "program_rapid_velocity_invalid" };
this.rapidVelocity = Number(velocity);
@@ -200,6 +212,8 @@ export class WasmProgramRuntime {
const subroutines = [...this.subroutines];
const loaded = this.load(this.source, this.path);
if (loaded.status !== "verified") throw new Error(loaded.code);
const synchronized = this.synchronizePosition(Object.fromEntries(AXES.map((axis) => [axis, Number(value[axis] ?? 0)])));
if (synchronized.status !== "verified") throw new Error(synchronized.code);
for (const subroutine of subroutines) {
const added = this.addSubroutine(subroutine.name, subroutine.source);
if (added.status !== "verified") throw new Error(added.code);
@@ -243,6 +257,15 @@ export class WasmProgramRuntime {
const result = call(this.core, "sim_program_read_motion", "number", ["number", "number"], [this.pointer, this.motionPointer]);
if (result !== 0) return null;
const value = segment(this.core, this.motionPointer);
// LinuxCNC does not enqueue a trajectory segment when a linear/rapid
// block resolves to the current synchronized pose. This occurs naturally
// when AUTO_RUN reopens a completed/aborted file and its first positioning
// block names the position where the previous run stopped.
const linearNoop = value.motion_type === 1 && AXES.every((axis) => Math.abs(Number(value.start?.[axis] ?? 0) - Number(value.end?.[axis] ?? 0)) <= 1e-12);
if (linearNoop) {
call(this.core, "sim_program_clear_motion", "number", ["number"], [this.pointer]);
return null;
}
// LinuxCNC rapid blocks do not carry an F word. The trajectory queue
// requires a positive velocity, so use the simulation's configured rapid
// rate for that WASM-produced linear segment.