Preserve motion queue ABI for feed override

This commit is contained in:
2026-08-31 06:36:53 +08:00
parent 51ba923cb5
commit 6033dcd47a
4 changed files with 10 additions and 9 deletions

View File

@@ -30,10 +30,11 @@ typedef struct sim_motion_command {
sim_posemath_segment segment; sim_posemath_segment segment;
uint32_t id; uint32_t id;
uint32_t tag; uint32_t tag;
uint32_t flags;
} sim_motion_command; } sim_motion_command;
#define SIM_MOTION_COMMAND_FLAG_RAPID 1u /* The high tag bit is reserved for Rapid blocks so the command ABI remains
compatible with existing checkpoints and WASM clients. */
#define SIM_MOTION_COMMAND_TAG_RAPID (UINT32_C(1) << 31)
typedef struct sim_motion_state { typedef struct sim_motion_state {
sim_posemath_pose position; sim_posemath_pose position;

View File

@@ -184,11 +184,11 @@ static double planned_velocity(const sim_motion_queue *queue, uint32_t offset) {
uint32_t index; uint32_t index;
if (queue == NULL || offset >= queue->count) return 0.0; if (queue == NULL || offset >= queue->count) return 0.0;
index = (queue->head + offset) % SIM_MOTION_QUEUE_CAPACITY; index = (queue->head + offset) % SIM_MOTION_QUEUE_CAPACITY;
velocity = queue->commands[index].segment.velocity * (meta == NULL || (queue->commands[index].flags & SIM_MOTION_COMMAND_FLAG_RAPID) != 0u ? 1.0 : meta->feed_override); velocity = queue->commands[index].segment.velocity * (meta == NULL || (queue->commands[index].tag & SIM_MOTION_COMMAND_TAG_RAPID) != 0u ? 1.0 : meta->feed_override);
if (meta == NULL || meta->profile == SIM_MOTION_PROFILE_EXACT_STOP) return velocity; if (meta == NULL || meta->profile == SIM_MOTION_PROFILE_EXACT_STOP) return velocity;
for (index = 1; index <= meta->lookahead && offset + index < queue->count; ++index) { for (index = 1; index <= meta->lookahead && offset + index < queue->count; ++index) {
uint32_t next = (queue->head + offset + index) % SIM_MOTION_QUEUE_CAPACITY; uint32_t next = (queue->head + offset + index) % SIM_MOTION_QUEUE_CAPACITY;
const double next_velocity = queue->commands[next].segment.velocity * ((queue->commands[next].flags & SIM_MOTION_COMMAND_FLAG_RAPID) != 0u ? 1.0 : meta->feed_override); const double next_velocity = queue->commands[next].segment.velocity * ((queue->commands[next].tag & SIM_MOTION_COMMAND_TAG_RAPID) != 0u ? 1.0 : meta->feed_override);
if (next_velocity < velocity) velocity = next_velocity; if (next_velocity < velocity) velocity = next_velocity;
} }
return velocity; return velocity;
@@ -232,7 +232,7 @@ int sim_motion_step(sim_motion_queue *queue) {
command = &queue->commands[queue->head]; command = &queue->commands[queue->head];
length = segment_length(&command->segment); length = segment_length(&command->segment);
if (length <= 0.0) { queue->state.status = SIM_MOTION_ERR_GEOMETRY; return queue->state.status; } if (length <= 0.0) { queue->state.status = SIM_MOTION_ERR_GEOMETRY; return queue->state.status; }
queue->state.active_id = command->id; queue->state.active_tag = command->tag; queue->state.motion_type = command->segment.motion_type; queue->state.active_id = command->id; queue->state.active_tag = command->tag & ~SIM_MOTION_COMMAND_TAG_RAPID; queue->state.motion_type = command->segment.motion_type;
desired_velocity = planned_velocity(queue, 0); desired_velocity = planned_velocity(queue, 0);
acceleration = (desired_velocity - meta->current_velocity) / queue->cycle_time; acceleration = (desired_velocity - meta->current_velocity) / queue->cycle_time;
if (acceleration > meta->current_acceleration + meta->jerk_limit * queue->cycle_time) acceleration = meta->current_acceleration + meta->jerk_limit * queue->cycle_time; if (acceleration > meta->current_acceleration + meta->jerk_limit * queue->cycle_time) acceleration = meta->current_acceleration + meta->jerk_limit * queue->cycle_time;

View File

@@ -60,7 +60,7 @@ int main(void) {
assert(override_state.position.tran.x > 0.049 && override_state.position.tran.x < 0.051); assert(override_state.position.tran.x > 0.049 && override_state.position.tran.x < 0.051);
assert(override_state.velocity > 0.49 && override_state.velocity < 0.51); assert(override_state.velocity > 0.49 && override_state.velocity < 0.51);
sim_motion_reset(&override_queue); sim_motion_reset(&override_queue);
override_command.flags = SIM_MOTION_COMMAND_FLAG_RAPID; override_command.tag = SIM_MOTION_COMMAND_TAG_RAPID;
assert(sim_motion_enqueue(&override_queue, &override_command, 0) == SIM_MOTION_OK); assert(sim_motion_enqueue(&override_queue, &override_command, 0) == SIM_MOTION_OK);
assert(sim_motion_step(&override_queue) == SIM_MOTION_OK); assert(sim_motion_step(&override_queue) == SIM_MOTION_OK);
assert(sim_motion_read_state(&override_queue, &override_state) == SIM_MOTION_OK); assert(sim_motion_read_state(&override_queue, &override_state) == SIM_MOTION_OK);

View File

@@ -102,10 +102,10 @@ export class WasmMotionQueue {
[...poseToArray(segment.start), ...poseToArray(segment.end), Number(segment.center?.x ?? 0), Number(segment.center?.y ?? 0), Number(segment.center?.z ?? 0), Number(segment.normal?.x ?? 0), Number(segment.normal?.y ?? 0), Number(segment.normal?.z ?? 0), Number(segment.velocity ?? 0), Number(segment.acceleration ?? 0)].forEach((value, index) => { doubles[base + index] = value; }); [...poseToArray(segment.start), ...poseToArray(segment.end), Number(segment.center?.x ?? 0), Number(segment.center?.y ?? 0), Number(segment.center?.z ?? 0), Number(segment.normal?.x ?? 0), Number(segment.normal?.y ?? 0), Number(segment.normal?.z ?? 0), Number(segment.velocity ?? 0), Number(segment.acceleration ?? 0)].forEach((value, index) => { doubles[base + index] = value; });
ints[this.command / 4 + SEGMENT_INT_OFFSET] = Number(segment.motion_type ?? 1); ints[this.command / 4 + SEGMENT_INT_OFFSET] = Number(segment.motion_type ?? 1);
ints[this.command / 4 + SEGMENT_INT_OFFSET + 1] = Number(segment.turn ?? 0); ints[this.command / 4 + SEGMENT_INT_OFFSET + 1] = Number(segment.turn ?? 0);
// sim_motion_command layout is segment (216 bytes), id (4), tag (4), flags (4). // sim_motion_command layout is segment (216 bytes), id (4), tag (4).
// Keep the WASM switchkins mode in the tag field; the C queue assigns id. // Keep the WASM switchkins mode in the tag field; the C queue assigns id.
ints[this.command / 4 + SEGMENT_INT_OFFSET + 3] = Number(segment.kinematics_mode ?? segment.tag ?? 0); const tag = Number(segment.kinematics_mode ?? segment.tag ?? 0) & 0x7fffffff;
ints[this.command / 4 + SEGMENT_INT_OFFSET + 4] = segment.kind === "rapid" ? 1 : 0; ints[this.command / 4 + SEGMENT_INT_OFFSET + 3] = tag | (segment.kind === "rapid" ? 0x80000000 : 0);
const result = call(this.core, "sim_motion_enqueue", "number", ["number", "number", "number"], [this.queue, this.command, 0]); const result = call(this.core, "sim_motion_enqueue", "number", ["number", "number", "number"], [this.queue, this.command, 0]);
return result === 0 ? { status: "verified", source_line: segment.source_line ?? null } : { status: "error", code: `wasm_motion_enqueue_${result}`, reason: MOTION_ERROR_REASONS[result] ?? `wasm_motion_enqueue_${result}`, message_key: MOTION_ERROR_REASONS[result] ?? `wasm_motion_enqueue_${result}`, source: "linuxcnc-wasm-motion" }; return result === 0 ? { status: "verified", source_line: segment.source_line ?? null } : { status: "error", code: `wasm_motion_enqueue_${result}`, reason: MOTION_ERROR_REASONS[result] ?? `wasm_motion_enqueue_${result}`, message_key: MOTION_ERROR_REASONS[result] ?? `wasm_motion_enqueue_${result}`, source: "linuxcnc-wasm-motion" };
} }