Show live LinuxCNC G-code execution progress

This commit is contained in:
2026-08-29 07:03:43 +08:00
parent 24d4930803
commit 74ae2538a3
5 changed files with 32 additions and 7 deletions

View File

@@ -161,9 +161,12 @@ export class SimSession {
loaded: program.loaded,
current_line: program.current_line,
line: program.line,
execution_line: program.execution_line,
execution_line_text: program.execution_line_text,
path: program.path,
source_hash: program.source_hash,
state_hash: program.state_hash,
active_gcodes: program.active_gcodes,
} : null;
const trace = Array.isArray(statePayload.motion_trace) ? statePayload.motion_trace : [];
const motionSample = motionDelta ? (trace.at(-1) ?? {

View File

@@ -15,6 +15,16 @@ function programError(errorPrefix, result) {
return { status: "error", code: `wasm_${errorPrefix}_${result}`, result, reason, message_key: reason, source: "linuxcnc-wasm-sim_program" };
}
function modalCode(prefix, value, fallback) { return `${prefix}${Number.isFinite(Number(value)) ? Number(value) : fallback}`; }
function activeGcodes(state) {
const motion = { 0: "G0", 1: "G1", 2: "G2", 3: "G3" }[state.motion_mode] ?? "G1";
const spindle = { 3: "M3", 4: "M4", 5: "M5" }[state.spindle_mode] ?? `M${state.spindle_mode ?? 5}`;
const feed = Number(state.feed ?? 0);
const speed = Number(state.speed ?? 0);
return [state.units === 20 ? "G20" : "G21", state.distance_mode === 91 ? "G91" : "G90", modalCode("G", state.plane, 17), motion, spindle, `F${Number.isFinite(feed) ? feed : 0}`, `S${Number.isFinite(speed) ? speed : 0}`].join(" ");
}
function call(core, name, returnType, argTypes, args) {
const fn = core?.[`_${name}`] ?? core?.[name];
if (typeof fn === "function") return fn(...args);
@@ -64,6 +74,7 @@ export class WasmProgramRuntime {
this.source = "";
this.path = null;
this.subroutines = [];
this.executionSource = "";
this.rapidVelocity = 35;
if (!this.available || typeof core?._malloc !== "function") return;
this.pointer = core._malloc(call(core, "sim_program_size", "number", [], []));
@@ -94,6 +105,7 @@ export class WasmProgramRuntime {
this.source = source;
this.path = path;
this.subroutines = [];
this.executionSource = source;
return { status: "verified", program: this.snapshot() };
} finally { this.core._free(pointer); }
}
@@ -154,6 +166,8 @@ export class WasmProgramRuntime {
const code = call(this.core, "sim_program_add_subroutine", "number", ["number", "number", "number"], [this.pointer, pointer, bytes.byteLength]);
if (code !== 0) return { status: "error", code: `wasm_program_add_subroutine_${code}`, result: code };
this.subroutines.push({ name: String(name ?? "subroutine"), source });
if (this.executionSource && !this.executionSource.endsWith("\n")) this.executionSource += "\n";
this.executionSource += source;
return { status: "verified", command: "sim_program_add_subroutine", name: String(name ?? "subroutine") };
} finally { this.core._free(pointer); }
}
@@ -247,9 +261,11 @@ export class WasmProgramRuntime {
const state = JSON.parse(readString(this.core, buffer, required));
const hash = call(this.core, "sim_program_state_hash", "number", ["number"], [this.pointer]);
const programStatus = { 0: "idle", 1: "loaded", 2: "running", 3: "paused", 4: "completed", 5: "aborted", 6: "fault" }[state.status] ?? "idle";
return { schema: SCHEMA, source: "linuxcnc-wasm-sim_program", ...state, loaded: state.status !== 0, line: state.current_line, currentLine: state.current_line, program_status: programStatus, path: this.path, subroutines: this.subroutines.map((subroutine) => subroutine.name), state_hash: `fnv1a64:${BigInt.asUintN(64, BigInt(hash >>> 0)).toString(16).padStart(16, "0")}`, source_hash: this.source ? fnv1a64(this.source) : null };
const executionLines = this.executionSource ? this.executionSource.split(/\r?\n/) : [];
const executionLineText = state.current_line > 0 ? (executionLines[state.current_line - 1] ?? "").trim() : "";
return { schema: SCHEMA, source: "linuxcnc-wasm-sim_program", ...state, active_gcodes: activeGcodes(state), loaded: state.status !== 0, line: state.current_line, currentLine: state.current_line, execution_line: state.current_line, execution_line_text: executionLineText, program_status: programStatus, path: this.path, subroutines: this.subroutines.map((subroutine) => subroutine.name), state_hash: `fnv1a64:${BigInt.asUintN(64, BigInt(hash >>> 0)).toString(16).padStart(16, "0")}`, source_hash: this.source ? fnv1a64(this.source) : null };
} finally { this.core._free(buffer); }
}
destroy() { for (const pointer of [this.pointer, this.motionPointer, this.samplePointer, this.samplePosePointer]) if (pointer && typeof this.core?._free === "function") this.core._free(pointer); this.pointer = this.motionPointer = this.samplePointer = this.samplePosePointer = 0; }
destroy() { for (const pointer of [this.pointer, this.motionPointer, this.samplePointer, this.samplePosePointer]) if (pointer && typeof this.core?._free === "function") this.core._free(pointer); this.pointer = this.motionPointer = this.samplePointer = this.samplePosePointer = 0; this.executionSource = ""; }
}