Complete 3axis tutorial native and Web/WASM parity tools

This commit is contained in:
2026-08-24 14:01:30 +08:00
parent 938654b19d
commit 7e90142b63
147 changed files with 17639 additions and 557 deletions

View File

@@ -41,6 +41,39 @@ function readInterpreterState(core, pointer) {
};
}
function readInterpreterMotion(core, interpreter, sourceLine = null) {
if (typeof core._sim_interpreter_read_motion !== "function" || typeof core._sim_interpreter_motion_size !== "function") return null;
const size = core._sim_interpreter_motion_size();
if (!Number.isSafeInteger(size) || size < 216 || size > 4096) throw new Error("wasm_motion_size_invalid");
const pointer = core._malloc(size);
if (!pointer) throw new Error("wasm_motion_allocation_failed");
try {
const result = call(core, "sim_interpreter_read_motion", "number", ["number", "number"], [interpreter, pointer]);
if (result !== 0) return null;
const doubles = core.HEAPF64.subarray(pointer / 8, pointer / 8 + 26);
const ints = core.HEAP32.subarray(pointer / 4 + 52, pointer / 4 + 54);
const pose = (offset) => ({
x: doubles[offset], y: doubles[offset + 1], z: doubles[offset + 2],
a: doubles[offset + 3], b: doubles[offset + 4], c: doubles[offset + 5],
u: doubles[offset + 6], v: doubles[offset + 7], w: doubles[offset + 8],
});
return {
source_line: sourceLine,
start: pose(0),
end: pose(9),
center: { x: doubles[18], y: doubles[19], z: doubles[20] },
normal: { x: doubles[21], y: doubles[22], z: doubles[23] },
velocity: doubles[24],
acceleration: doubles[25],
motion_type: ints[0],
turn: ints[1],
source: "wasm_interpreter_canonical_motion",
};
} finally {
core._free(pointer);
}
}
function readCanonical(core, interpreter) {
if (typeof core._sim_interpreter_canonical !== "function") return null;
const required = call(core, "sim_interpreter_canonical", "number", ["number", "number", "number"], [interpreter, 0, 0]);
@@ -328,6 +361,32 @@ export class ProgramRunner {
return this.snapshot();
}
preview() {
if (!this.loaded) return { status: "error", code: "program_not_loaded" };
const source = this.source;
const path = this.path;
const segments = [];
let result = { status: "verified" };
try {
for (let steps = 0; steps < MAX_EXPANDED_LINES && this.programCounter < this.lines.length; steps += 1) {
result = this.executeNext(true);
if (result.status !== "verified") break;
if (result.motion) {
const motion = result.motion;
const linearNoop = motion.motion_type === 1
&& ["x", "y", "z", "a", "b", "c", "u", "v", "w"].every((axis) => motion.start[axis] === motion.end[axis]);
if (!linearNoop) segments.push(motion);
}
}
if (result.status !== "verified") return { ...result, preview: { status: "error", segments } };
return { status: "verified", preview: { status: "verified", segments } };
} catch (error) {
return { status: "error", code: error.message, preview: { status: "error", segments } };
} finally {
this.load(source, path);
}
}
pause() {
if (this.status !== "running") return { status: "error", code: "program_not_running" };
this.status = "paused";
@@ -398,7 +457,7 @@ export class ProgramRunner {
if (stateResult !== 0) return { status: "error", code: "mdi_state_read", result: stateResult };
this.state = readInterpreterState(this.core, this.stateBuffer);
const interpreterState = cloneState(this.state);
return { status: "verified", block, state: interpreterState, interpreter_state: interpreterState, runner: this.snapshot() };
return { status: "verified", block, state: interpreterState, interpreter_state: interpreterState, motion: readInterpreterMotion(this.core, this.interpreter, this.state.line), runner: this.snapshot() };
} catch (error) {
return { status: "error", code: error.message };
}
@@ -588,7 +647,7 @@ export class ProgramRunner {
}
if (this.state.program_end) this.status = "completed";
else if (this.programCounter >= this.lines.length && !seeking) this.status = "completed";
return { status: "verified", line: index + 1, text: sourceLine, state: cloneState(this.state), program_status: this.status };
return { status: "verified", line: index + 1, text: sourceLine, state: cloneState(this.state), program_status: this.status, motion: readInterpreterMotion(this.core, this.interpreter, index + 1) };
}
this.status = "completed";
return { status: "verified", line: this.currentLine, text: null, state: cloneState(this.state), program_status: this.status };
@@ -618,6 +677,11 @@ export class ProgramRunner {
this.lastError = this.lastError ?? { code: error.message };
}
}
const unitsCode = this.state.units === 20 ? "G20" : "G21";
const distanceCode = this.state.distance_mode === 91 ? "G91" : "G90";
const planeCode = this.state.plane === 18 ? "G18" : this.state.plane === 19 ? "G19" : "G17";
const motionCode = `G${this.state.motion_mode}`;
const spindleCode = this.state.spindle_mode === 5 ? "M5" : `M${this.state.spindle_mode}`;
return {
status: "verified",
program_status: this.status,
@@ -632,6 +696,7 @@ export class ProgramRunner {
modal_hash,
motion_control: { ...this.motionControl },
motion_control_hash: fnv1a64(this.motionControl),
active_gcodes: `${unitsCode} ${distanceCode} ${planeCode} ${motionCode} ${spindleCode}`,
error_column,
error_code,
error: this.lastError,