Prevent XYZAC TP queue starvation
This commit is contained in:
@@ -1552,7 +1552,12 @@ export class SimSession {
|
||||
// planner queue. Let the motion phase consume one segment
|
||||
// before reading another source line instead of converting
|
||||
// normal planner backpressure into a program fault.
|
||||
if (this.wasmMotion.read().state.queue_depth >= this.wasmMotion.queueCapacity) return;
|
||||
// TP_DEFAULT_QUEUE_SIZE includes the active TC slot. Leave
|
||||
// one slot available for the active segment before appending
|
||||
// another lookahead TC; the upstream queue otherwise rejects
|
||||
// the boundary insertion even though its reported depth is
|
||||
// one below the configured capacity.
|
||||
if (this.wasmMotion.read().state.queue_depth >= this.wasmMotion.queueCapacity - 1) return;
|
||||
if (this.pendingXyzacMotion) {
|
||||
const pendingMode = Number(this.pendingXyzacMotion.kinematics_mode ?? 0);
|
||||
if (this.xyzacAppendDeferredOnActive) return;
|
||||
@@ -1562,7 +1567,14 @@ export class SimSession {
|
||||
: Number(queueState.active_id ?? 0) === this.xyzacDeferredBlockedActiveId;
|
||||
const deferredSwitchMotion = this.xyzacDeferredPhysicalSwitchMode !== null && pendingMode === this.xyzacDeferredPhysicalSwitchMode && this.xyzacQueuedLookaheadTargetId === 0 && deferredControlReady;
|
||||
const queuedLookaheadReady = deferredSwitchMotion || (this.xyzacAllowQueuedLookahead && this.xyzacLastServoActiveId === this.xyzacQueuedLookaheadTargetId);
|
||||
if (queueState.queue_depth !== 0 && !queuedLookaheadReady) return;
|
||||
// Same-mode segments may be queued while the current TC is
|
||||
// active. LinuxCNC TP needs that lookahead to blend G64
|
||||
// paths; withholding it until the queue drains creates a
|
||||
// real zero-velocity servo sample between long runs. A
|
||||
// kinematics-mode change remains synchronized below and is
|
||||
// deliberately deferred until its existing TC boundary.
|
||||
const pendingModeChanged = pendingMode !== this.xyzacLastEnqueuedKinematicsMode;
|
||||
if (queueState.queue_depth !== 0 && pendingModeChanged && !queuedLookaheadReady) return;
|
||||
if (queuedLookaheadReady && !deferredSwitchMotion && this.xyzacAllowQueuedLookahead) {
|
||||
// The next segment is appended after the first servo
|
||||
// sample of the now-active target, matching LinuxCNC's
|
||||
|
||||
@@ -46,6 +46,7 @@ const run = send("PROGRAM_RUN");
|
||||
assert.equal(run.kind, "COMMAND_RESULT");
|
||||
let stepped = null;
|
||||
let executionTraceEntries = 0;
|
||||
let runningQueueGaps = 0;
|
||||
// The first long Z feed is planned at the configured LinuxCNC TP limit. Give
|
||||
// the fixed-tick simulation enough servo time to complete it before checking
|
||||
// that the interpreter has advanced beyond the historical line-82 boundary.
|
||||
@@ -55,6 +56,7 @@ for (let cycle = 0; cycle < cyclesToRun; cycle += 1) {
|
||||
stepped = send("SIM_STEP", { cycles: 1, response: "motion_delta" });
|
||||
assert.equal(stepped.kind, "COMMAND_RESULT");
|
||||
executionTraceEntries += stepped.payload.execution_trace.length;
|
||||
if (cycle > 0 && stepped.payload.program.program_status === "running" && Number(stepped.payload.wasm_motion.state.queue_depth) === 0) runningQueueGaps += 1;
|
||||
}
|
||||
assert.equal(stepped.kind, "COMMAND_RESULT");
|
||||
assert.equal(stepped.payload.program.program_status, "running");
|
||||
@@ -65,6 +67,7 @@ assert.equal(stepped.payload.motion_authority, "wasm");
|
||||
assert.equal(Object.hasOwn(stepped.payload.xyzac_ledger.entries.at(-1), "response"), false);
|
||||
assert.equal(session.timeline.snapshots.snapshots.size, 0);
|
||||
assert.equal(session.ledger.audit({ maxEntries: 32 }).entries.length, 32);
|
||||
assert.equal(runningQueueGaps, 0);
|
||||
|
||||
console.log(JSON.stringify({
|
||||
schema: "xyzac-3d-chips-program.v1",
|
||||
@@ -77,6 +80,7 @@ console.log(JSON.stringify({
|
||||
cycles: cyclesToRun,
|
||||
execution_line: stepped.payload.program.current_line,
|
||||
execution_trace: executionTraceEntries,
|
||||
running_queue_gaps: runningQueueGaps,
|
||||
motion_authority: stepped.payload.motion_authority,
|
||||
motion_delta_snapshots: session.timeline.snapshots.snapshots.size,
|
||||
ledger_audit_window: session.ledger.audit({ maxEntries: 32 }).entries.length,
|
||||
|
||||
@@ -17,11 +17,11 @@ 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_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_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;
|
||||
this.queueCapacity = 2000;
|
||||
this.queueCapacity = 0;
|
||||
this.planner = 0;
|
||||
this.posePointer = 0;
|
||||
this.centerPointer = 0;
|
||||
@@ -47,6 +47,8 @@ export class WasmLinuxcncTp {
|
||||
if (![this.planner, this.posePointer, this.centerPointer, this.normalPointer, this.statePointer].every(Boolean)) throw new Error("linuxcnc_tp_allocation_failed");
|
||||
const result = call(core, "sim_linuxcnc_tp_init", "number", ["number", "number", "number", "number"], [this.planner, cycleTime, velocityLimit, accelerationLimit]);
|
||||
if (result !== 0) throw new Error(`linuxcnc_tp_init_${result}`);
|
||||
this.queueCapacity = Number(call(core, "sim_linuxcnc_tp_queue_capacity", "number", ["number"], [this.planner]));
|
||||
if (!Number.isSafeInteger(this.queueCapacity) || this.queueCapacity < 1) throw new Error("linuxcnc_tp_queue_capacity_invalid");
|
||||
this.setPosition({ x: 0, y: 0, z: 0, a: 0, b: 0, c: 0, u: 0, v: 0, w: 0 });
|
||||
this.setProfile(0);
|
||||
for (const [axis, limit] of Object.entries(limits)) {
|
||||
|
||||
Reference in New Issue
Block a user