Align AXIS control flow and fix pause and program rendering

Document source UI gates, explicit native live-plot clearing, and remaining parity boundaries. Fix pause/resume transport continuity, repeated stepping, coordinate previews, and spindle override state. Optimize large program snapshots and rendering, and cover the control flows with native Tcl, WASM, and browser checks.

Validation: 22 native tests and source/browser checks pass. Worker/App regression: 113 of 123 pass; 10 pre-existing failures remain documented with no new failures.
This commit is contained in:
2026-09-08 10:46:27 +08:00
parent 845075f4a3
commit bade560372
55 changed files with 2495 additions and 286 deletions

View File

@@ -72,6 +72,7 @@ if(NOT EMSCRIPTEN)
add_test(NAME sim_api_test COMMAND sim_api_test)
add_executable(sim_protocol_test tests/sim_protocol_test.c)
target_compile_options(sim_protocol_test PRIVATE -UNDEBUG)
target_link_libraries(sim_protocol_test PRIVATE sim_core)
target_include_directories(sim_protocol_test PRIVATE include)
add_test(NAME sim_protocol_test COMMAND sim_protocol_test)
@@ -308,7 +309,7 @@ if(EMSCRIPTEN)
string(REPLACE "_sim_device_init" "_sim_device_io_size','_sim_device_init" _sim_module_link_flags "${_sim_module_link_flags}")
string(REPLACE "_sim_motion_step','_sim_motion_read_state" "_sim_motion_step','_sim_motion_set_profile','_sim_motion_set_lookahead','_sim_motion_set_feed_override','_sim_motion_peek','_sim_motion_read_state" _sim_module_link_flags "${_sim_module_link_flags}")
string(REPLACE "_sim_motion_state_hash','_sim_motion_canonical" "_sim_motion_state_hash','_sim_motion_plan_hash','_sim_motion_canonical" _sim_module_link_flags "${_sim_module_link_flags}")
string(REPLACE "_sim_motion_init" "_sim_motion_init','_sim_linuxcnc_tp_size','_sim_linuxcnc_tp_queue_capacity','_sim_linuxcnc_tp_init','_sim_linuxcnc_tp_reset','_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_position','_sim_linuxcnc_tp_set_goal_position','_sim_linuxcnc_tp_add_line','_sim_linuxcnc_tp_add_circle','_sim_linuxcnc_tp_step','_sim_linuxcnc_tp_idle','_sim_linuxcnc_tp_read_state','_sim_linuxcnc_tp_queue_depth','_sim_linuxcnc_tp_queue_segment','_sim_linuxcnc_tp_restore_progress','_sim_linuxcnc_tp_state_hash','_sim_linuxcnc_tp_canonical" _sim_module_link_flags "${_sim_module_link_flags}")
string(REPLACE "_sim_motion_init" "_sim_motion_init','_sim_linuxcnc_tp_size','_sim_linuxcnc_tp_queue_capacity','_sim_linuxcnc_tp_init','_sim_linuxcnc_tp_reset','_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_axis_limit','_sim_linuxcnc_tp_step_line','_sim_linuxcnc_tp_is_stepping','_sim_linuxcnc_tp_set_paused','_sim_linuxcnc_tp_abort','_sim_linuxcnc_tp_set_position','_sim_linuxcnc_tp_set_goal_position','_sim_linuxcnc_tp_add_line','_sim_linuxcnc_tp_add_circle','_sim_linuxcnc_tp_step','_sim_linuxcnc_tp_idle','_sim_linuxcnc_tp_read_state','_sim_linuxcnc_tp_queue_depth','_sim_linuxcnc_tp_queue_segment','_sim_linuxcnc_tp_restore_progress','_sim_linuxcnc_tp_state_hash','_sim_linuxcnc_tp_canonical" _sim_module_link_flags "${_sim_module_link_flags}")
string(REPLACE "_sim_linuxcnc_tp_velocity_limit" "_sim_linuxcnc_tp_velocity_limit','_sim_linuxcnc_tp_velocity_mm_min" _sim_module_link_flags "${_sim_module_link_flags}")
set_target_properties(sim_core_module PROPERTIES LINK_FLAGS "${_sim_module_link_flags}")
endif()

View File

@@ -64,6 +64,15 @@ typedef struct sim_interpreter {
char canonical[SIM_INTERPRETER_CANONICAL_CAPACITY];
} sim_interpreter;
typedef struct sim_coordinate_context {
double offsets[9][9];
int system;
} sim_coordinate_context;
int sim_interpreter_get_coordinates(const sim_interpreter *interpreter, sim_coordinate_context *context);
int sim_interpreter_set_coordinates(sim_interpreter *interpreter, const sim_coordinate_context *context);
int sim_interpreter_sync_position(sim_interpreter *interpreter, const sim_posemath_pose *position);
int sim_interpreter_init(sim_interpreter *interpreter);
int sim_interpreter_reset(sim_interpreter *interpreter);
int sim_interpreter_execute(sim_interpreter *interpreter, const char *block, uint32_t length);

View File

@@ -71,6 +71,8 @@ typedef struct sim_io_spindle_state {
double orient_angle;
double revs;
uint32_t tick;
double requested_rpm;
int32_t direction;
} sim_io_spindle_state;
typedef struct sim_io_probe_state {

View File

@@ -36,6 +36,9 @@ double sim_linuxcnc_tp_jog_velocity(const sim_linuxcnc_tp *planner, int axis);
double sim_linuxcnc_tp_feed_override(const sim_linuxcnc_tp *planner);
int32_t sim_linuxcnc_tp_set_dynamics(sim_linuxcnc_tp *planner, double acceleration_limit);
int32_t sim_linuxcnc_tp_set_paused(sim_linuxcnc_tp *planner, int paused);
int32_t sim_linuxcnc_tp_step_line(sim_linuxcnc_tp *planner);
int32_t sim_linuxcnc_tp_is_stepping(const sim_linuxcnc_tp *planner);
double sim_linuxcnc_tp_axis_limit(const sim_linuxcnc_tp *planner, int axis, int maximum);
int32_t sim_linuxcnc_tp_abort(sim_linuxcnc_tp *planner);
int32_t sim_linuxcnc_tp_set_position(sim_linuxcnc_tp *planner, const sim_posemath_pose *position);
int32_t sim_linuxcnc_tp_set_goal_position(sim_linuxcnc_tp *planner, const sim_posemath_pose *position);

View File

@@ -79,6 +79,7 @@ typedef struct sim_program {
int motion_control_mode;
double path_tolerance;
double naive_cam_tolerance;
uint32_t pause_from;
} sim_program;
uint32_t sim_program_size(void);

View File

@@ -101,17 +101,12 @@ static int axis_index(char letter) {
static double *axis_pointer(sim_interpreter_state *state, int index) { return &state->x + index; }
static void machine_pose_for_state(const interpreter_meta *meta, const sim_interpreter_state *state, int machine_coordinates, sim_posemath_pose *pose) {
int coordinate = meta->coordinate_system > 0 ? meta->coordinate_system - 1 : 0;
const double *offset = meta->coordinate_offsets[coordinate];
pose->tran.x = state->x + (machine_coordinates ? 0.0 : offset[0]);
pose->tran.y = state->y + (machine_coordinates ? 0.0 : offset[1]);
pose->tran.z = state->z + (machine_coordinates ? 0.0 : offset[2]);
pose->a = state->a + (machine_coordinates ? 0.0 : offset[3]);
pose->b = state->b + (machine_coordinates ? 0.0 : offset[4]);
pose->c = state->c + (machine_coordinates ? 0.0 : offset[5]);
pose->u = state->u + (machine_coordinates ? 0.0 : offset[6]);
pose->v = state->v + (machine_coordinates ? 0.0 : offset[7]);
pose->w = state->w + (machine_coordinates ? 0.0 : offset[8]);
// The interpreter state already stores machine coordinates. Offsets are
// applied exactly once when accepting an absolute coordinate word.
(void)meta; (void)machine_coordinates;
pose->tran.x = state->x; pose->tran.y = state->y; pose->tran.z = state->z;
pose->a = state->a; pose->b = state->b; pose->c = state->c;
pose->u = state->u; pose->v = state->v; pose->w = state->w;
}
static void set_error(sim_interpreter *interpreter, interpreter_meta *meta, int code, uint32_t column) {
@@ -352,6 +347,7 @@ static int apply_g(sim_interpreter_state *state, interpreter_meta *meta, double
if (code_is(code, 80) || (code >= 81.0 && code <= 89.0 && floor(code) == code)) { if (meta) meta->cycle_mode = (int)code; return SIM_INTERP_OK; }
if (code_is(code, 61) || code_is_value(code, 61.1) || code_is(code, 64)) { if (++groups[7] > 1) return SIM_INTERP_ERR_STATE; return SIM_INTERP_OK; }
if (code_is(code, 54) || code_is(code, 55) || code_is(code, 56) || code_is(code, 57) || code_is(code, 58) || code_is(code, 59)) { if (meta) meta->coordinate_system = (int)code - 53; return SIM_INTERP_OK; }
if (code_is_value(code, 59.1) || code_is_value(code, 59.2) || code_is_value(code, 59.3)) { if (meta) meta->coordinate_system = 6 + (int)lround((code - 59.0) * 10.0); return SIM_INTERP_OK; }
if (code_is(code, 53)) return SIM_INTERP_OK;
if (code_is(code, 10)) return SIM_INTERP_OK;
return SIM_INTERP_ERR_UNKNOWN_CODE;
@@ -524,16 +520,14 @@ int sim_interpreter_execute(sim_interpreter *interpreter, const char *block, uin
coordinate = p_value == 0.0 ? (meta->coordinate_system > 0 ? meta->coordinate_system - 1 : 0) : (int)p_value - 1;
if (code_is(l_value, 20)) {
meta->coordinate_offsets[coordinate][axis] = *axis_pointer(&previous, (int)axis) - axis_value;
if (p_value == 0.0) meta->coordinate_l20 = 1;
*destination = axis_value;
} else {
meta->coordinate_offsets[coordinate][axis] = axis_value;
}
} else if (next.distance_mode == SIM_INTERP_INCREMENTAL) *destination += axis_value;
else *destination = axis_value + (seen_g53 || meta->coordinate_l20 ? 0.0 : meta->coordinate_offsets[meta->coordinate_system > 0 ? meta->coordinate_system - 1 : 0][axis]);
else *destination = axis_value + (seen_g53 ? 0.0 : meta->coordinate_offsets[meta->coordinate_system > 0 ? meta->coordinate_system - 1 : 0][axis]);
}
}
if (seen_g10 && !seen_axis[0] && !seen_axis[1] && !seen_axis[2]) { set_error(interpreter, meta, SIM_INTERP_ERR_PARAMETER, 1); return SIM_INTERP_ERR_PARAMETER; }
if (seen_g10 && !seen_axis[0] && !seen_axis[1] && !seen_axis[2] && !seen_axis[3] && !seen_axis[4] && !seen_axis[5] && !seen_axis[6] && !seen_axis[7] && !seen_axis[8]) { set_error(interpreter, meta, SIM_INTERP_ERR_PARAMETER, 1); return SIM_INTERP_ERR_PARAMETER; }
if (meta->cycle_mode >= 81 && meta->cycle_mode <= 89 && seen_r && seen_axis[2]) {
double return_plane = r_value;
if (next.units == SIM_INTERP_UNITS_INCH) return_plane *= 25.4;
@@ -635,3 +629,30 @@ int sim_interpreter_parameter_set(sim_interpreter *interpreter, uint32_t index,
int sim_interpreter_parameter_get(const sim_interpreter *interpreter, uint32_t index, double *value) {
interpreter_meta *meta = meta_for(interpreter, 0); if (meta == NULL || value == NULL || index < SIM_INTERPRETER_PARAMETER_MIN || index > SIM_INTERPRETER_PARAMETER_MAX) return SIM_INTERP_ERR_PARAMETER; *value = meta->parameters[index]; return SIM_INTERP_OK;
}
int sim_interpreter_get_coordinates(const sim_interpreter *interpreter, sim_coordinate_context *context) {
interpreter_meta *meta = meta_for(interpreter, 0);
if (meta == NULL || context == NULL) return SIM_INTERP_ERR_ARGUMENT;
memcpy(context->offsets, meta->coordinate_offsets, sizeof(context->offsets));
context->system = meta->coordinate_system > 0 ? meta->coordinate_system : 1;
return SIM_INTERP_OK;
}
int sim_interpreter_set_coordinates(sim_interpreter *interpreter, const sim_coordinate_context *context) {
interpreter_meta *meta = meta_for(interpreter, 0);
if (meta == NULL || context == NULL || context->system < 1 || context->system > 9) return SIM_INTERP_ERR_ARGUMENT;
memcpy(meta->coordinate_offsets, context->offsets, sizeof(meta->coordinate_offsets));
meta->coordinate_system = context->system;
return SIM_INTERP_OK;
}
int sim_interpreter_sync_position(sim_interpreter *interpreter, const sim_posemath_pose *position) {
interpreter_meta *meta = meta_for(interpreter, 0);
if (meta == NULL || position == NULL) return SIM_INTERP_ERR_ARGUMENT;
memcpy(&interpreter->state.x, &position->tran.x, 9 * sizeof(double));
meta->last_output_pose = *position;
meta->has_output_pose = 1;
meta->has_motion = 0;
interpreter->state.status = SIM_INTERP_OK;
return SIM_INTERP_OK;
}

View File

@@ -257,6 +257,8 @@ int sim_io_spindle_command(sim_io *io, uint32_t spindle, double rpm, uint32_t en
if (io->state.estop) { set_last(io, SIM_IO_ERR_ESTOP); return SIM_IO_ERR_ESTOP; }
if (!io->state.ready) { set_last(io, SIM_IO_ERR_NOT_READY); return SIM_IO_ERR_NOT_READY; }
state = &io->state.spindles[spindle];
state->requested_rpm = enabled ? rpm : 0.0;
state->direction = enabled ? (rpm > 0 ? 1 : rpm < 0 ? -1 : 0) : 0;
state->command_rpm = enabled && !state->inhibit && !state->amp_fault ? rpm * state->override_scale : 0.0;
state->enabled = enabled ? 1U : 0U;
set_last(io, SIM_IO_OK);
@@ -274,11 +276,12 @@ int sim_io_spindle_adjust(sim_io *io, uint32_t spindle, double delta) {
state = &io->state.spindles[spindle];
// Match LinuxCNC EMCMOT_SPINDLE_INCREASE/DECREASE: change the signed
// speed only while the spindle is running and keep its direction.
if (state->command_rpm > 0.0) target = fmax(0.0, state->command_rpm + delta);
else if (state->command_rpm < 0.0) target = fmin(0.0, state->command_rpm - delta);
if (state->requested_rpm > 0.0) target = fmax(0.0, state->requested_rpm + delta);
else if (state->requested_rpm < 0.0) target = fmin(0.0, state->requested_rpm - delta);
else target = 0.0;
target = fmax(-state->max_rpm, fmin(state->max_rpm, target));
state->command_rpm = state->enabled && !state->inhibit && !state->amp_fault ? target : 0.0;
state->requested_rpm = target;
state->command_rpm = state->enabled && !state->inhibit && !state->amp_fault ? target * state->override_scale : 0.0;
set_last(io, SIM_IO_OK);
mirror_pins(io);
return SIM_IO_OK;
@@ -300,12 +303,11 @@ int sim_io_spindle_set_override(sim_io *io, uint32_t spindle, double scale) {
sim_io_spindle_state *state;
double base;
if (!valid_spindle(io, spindle) || !isfinite(scale) || scale < 0.0 || scale > 2.0) return SIM_IO_ERR_RANGE;
if (io->state.estop) { set_last(io, SIM_IO_ERR_ESTOP); return SIM_IO_ERR_ESTOP; }
if (!io->state.ready) { set_last(io, SIM_IO_ERR_NOT_READY); return SIM_IO_ERR_NOT_READY; }
/* EMC_TRAJ_SET_SPINDLE_SCALE remains accepted while OFF/ESTOP. */
state = &io->state.spindles[spindle];
base = state->override_scale > 0.0 ? state->command_rpm / state->override_scale : 0.0;
base = state->requested_rpm;
state->override_scale = scale;
state->command_rpm = state->enabled && !state->inhibit && !state->amp_fault ? base * scale : 0.0;
state->command_rpm = scale > 0 && state->enabled && !state->inhibit && !state->amp_fault ? base * scale : 0.0;
set_last(io, SIM_IO_OK);
mirror_pins(io);
return SIM_IO_OK;
@@ -623,9 +625,9 @@ uint32_t sim_io_canonical(const sim_io *io, char *output, uint32_t capacity) {
const sim_io_spindle_state *spindle = &io->state.spindles[index];
int part = snprintf(destination + (output && (uint32_t)written < capacity ? written : 0),
output && (uint32_t)written < capacity ? capacity - (uint32_t)written : 0,
"%s{\"at_speed\":%u,\"brake\":%u,\"command_rpm\":%.9g,\"enabled\":%u,\"forward\":%u,\"inhibit\":%u,\"override\":%.9g,\"reverse\":%u,\"speed_rpm\":%.9g,\"tick\":%u}",
"%s{\"at_speed\":%u,\"brake\":%u,\"command_rpm\":%.9g,\"enabled\":%u,\"forward\":%u,\"inhibit\":%u,\"override\":%.9g,\"reverse\":%u,\"speed_rpm\":%.9g,\"tick\":%u,\"requested_rpm\":%.9g,\"direction\":%d}",
index ? "," : "", spindle->at_speed, spindle->brake, spindle->command_rpm,
spindle->enabled, spindle->forward, spindle->inhibit, spindle->override_scale, spindle->reverse, spindle->speed_rpm, spindle->tick);
spindle->enabled, spindle->forward, spindle->inhibit, spindle->override_scale, spindle->reverse, spindle->speed_rpm, spindle->tick, spindle->requested_rpm, spindle->enabled ? spindle->direction : 0);
if (part < 0) return 0; written += part;
}
{ int part = snprintf(destination + (output && (uint32_t)written < capacity ? written : 0),

View File

@@ -26,6 +26,7 @@ struct sim_linuxcnc_tp {
uint32_t last_active_id;
int just_completed;
int initialized;
int step_id;
};
static sim_linuxcnc_tp *active_planner;
@@ -211,11 +212,19 @@ int32_t sim_linuxcnc_tp_jog(sim_linuxcnc_tp *planner, int axis, int direction, d
double *value;
double velocity;
struct state_tag_t state_tag = {0};
if (planner == NULL || !planner->initialized || axis < 0 || axis >= 9 || (direction != -1 && direction != 1) || !isfinite(increment) || increment <= 0.0) return -1215;
if (planner == NULL || !planner->initialized || axis < 0 || axis >= 9 || (direction != -1 && direction != 1) || !isfinite(increment) || increment < 0.0) return -1215;
active_planner = planner;
end = planner->tp.goalPos;
value = axis == 0 ? &end.tran.x : axis == 1 ? &end.tran.y : axis == 2 ? &end.tran.z : axis == 3 ? &end.a : axis == 4 ? &end.b : axis == 5 ? &end.c : axis == 6 ? &end.u : axis == 7 ? &end.v : &end.w;
*value += (double)direction * increment;
// command.c::EMCMOT_JOG_CONT targets the directional jog limit until abort.
// Zero is the continuous-mode encoding; positive increments retain their ABI.
if (increment == 0.0) {
double target = direction > 0 ? planner->axis_max[axis] : planner->axis_min[axis];
if ((target - *value) * direction <= 0.0) return -1217;
*value = target;
} else {
*value += (double)direction * increment;
}
if (!isfinite(*value) || *value < planner->axis_min[axis] || *value > planner->axis_max[axis]) return -1217;
velocity = planner->axis_velocity[axis] * planner->jog_scale;
state_tag.fields_float[GM_FIELD_FLOAT_LINE_NUMBER] = (float)tag;
@@ -257,15 +266,36 @@ int32_t sim_linuxcnc_tp_set_dynamics(sim_linuxcnc_tp *planner, double accelerati
int32_t sim_linuxcnc_tp_set_paused(sim_linuxcnc_tp *planner, int paused) {
if (planner == NULL || !planner->initialized || (paused != 0 && paused != 1)) return -1219;
active_planner = planner;
planner->status.stepping = 0;
if (paused) return tpPause(&planner->tp) == TP_ERR_OK ? 0 : -1220;
return tpResume(&planner->tp) == TP_ERR_OK ? 0 : -1221;
}
int32_t sim_linuxcnc_tp_step_line(sim_linuxcnc_tp *planner) {
if (planner == NULL || !planner->initialized) return -1219;
TC_STRUCT *active = tcqItem(&planner->tp.queue, 0);
if (active == NULL) return -1219;
active_planner = planner;
planner->step_id = active->id;
planner->status.stepping = 1;
return tpResume(&planner->tp) == TP_ERR_OK ? 0 : -1221;
}
int32_t sim_linuxcnc_tp_is_stepping(const sim_linuxcnc_tp *planner) {
return planner != NULL && planner->initialized && planner->status.stepping;
}
double sim_linuxcnc_tp_axis_limit(const sim_linuxcnc_tp *planner, int axis, int maximum) {
if (planner == NULL || !planner->initialized || axis < 0 || axis > 8) return NAN;
return maximum ? planner->axis_max[axis] : planner->axis_min[axis];
}
int32_t sim_linuxcnc_tp_abort(sim_linuxcnc_tp *planner) {
if (planner == NULL || !planner->initialized) return -1222;
active_planner = planner;
planner->last_active_id = 0;
planner->just_completed = 0;
planner->status.stepping = 0;
if (tpAbort(&planner->tp) != TP_ERR_OK) return -1223;
// A browser PROGRAM_ABORT has no background emcmot servo thread after
// the command response. Complete the source TP abort transition here so
@@ -316,9 +346,17 @@ int32_t sim_linuxcnc_tp_step(sim_linuxcnc_tp *planner) {
planner->just_completed = 0;
const int prior_id = tpGetExecId(&planner->tp);
const int prior_depth = tcqLen(&planner->tp.queue);
const int was_stepping = planner->status.stepping;
const int result = tpRunCycle(&planner->tp, (long)llround(planner->tp.cycleTime * 1e9));
// motion/control.c stops a step when the execution ID changes. Compare
// the next queued ID too, before the following TC can start moving.
TC_STRUCT *next = tcqItem(&planner->tp.queue, 0);
if (planner->status.stepping && (next == NULL || next->id != planner->step_id)) {
tpPause(&planner->tp);
planner->status.stepping = 0;
}
planner->tick += 1u;
if (prior_id > 0 && prior_depth > 0 && tcqLen(&planner->tp.queue) == 0) {
if (!was_stepping && prior_id > 0 && prior_depth > 0 && tcqLen(&planner->tp.queue) == 0) {
planner->last_active_id = (uint32_t)prior_id;
planner->just_completed = 1;
}

View File

@@ -310,6 +310,7 @@ static void reset_runtime(sim_program *program) {
program->motion_control_mode = SIM_PROGRAM_MOTION_CONTINUOUS;
program->path_tolerance = 0.0;
program->naive_cam_tolerance = 0.0;
program->pause_from = SIM_PROGRAM_RUNNING;
memset(&program->motion, 0, sizeof(program->motion));
memset(program->subroutines, 0, sizeof(program->subroutines));
memset(program->call_stack, 0, sizeof(program->call_stack));
@@ -344,9 +345,21 @@ void sim_program_destroy(sim_program *program) {
}
int32_t sim_program_load(sim_program *program, const char *source, uint32_t length) {
sim_coordinate_context coordinates;
sim_interpreter_state modal_state;
if (program == NULL || source == NULL) return SIM_PROGRAM_ERR_ARGUMENT;
if (length == 0 || length > SIM_PROGRAM_MAX_SOURCE_BYTES) return SIM_PROGRAM_ERR_RANGE;
modal_state = program->interpreter.state;
if (sim_interpreter_get_coordinates(&program->interpreter, &coordinates) != SIM_INTERP_OK) return SIM_PROGRAM_ERR_INTERPRETER;
if (sim_program_reset(program) != SIM_PROGRAM_OK) return SIM_PROGRAM_ERR_INTERPRETER;
if (sim_interpreter_set_coordinates(&program->interpreter, &coordinates) != SIM_INTERP_OK) return SIM_PROGRAM_ERR_INTERPRETER;
program->interpreter.state.units = modal_state.units;
program->interpreter.state.distance_mode = modal_state.distance_mode;
program->interpreter.state.plane = modal_state.plane;
program->interpreter.state.motion_mode = modal_state.motion_mode;
program->interpreter.state.feed = modal_state.feed;
program->interpreter.state.speed = modal_state.speed;
program->interpreter.state.spindle_mode = modal_state.spindle_mode;
if (append_source(program, source, length) != SIM_PROGRAM_OK) return SIM_PROGRAM_ERR_RANGE;
program->main_line_count = program->line_count;
program->status = SIM_PROGRAM_LOADED;
@@ -370,20 +383,10 @@ int32_t sim_program_set_rapid_velocity(sim_program *program, double velocity) {
int32_t sim_program_set_position(sim_program *program, const sim_posemath_pose *position) {
const double *values;
size_t index;
if (program == NULL || position == NULL || program->status == SIM_PROGRAM_IDLE) return SIM_PROGRAM_ERR_ARGUMENT;
if (program == NULL || position == NULL) return SIM_PROGRAM_ERR_ARGUMENT;
values = &position->tran.x;
for (index = 0; index < 9; ++index) if (!isfinite(values[index])) return SIM_PROGRAM_ERR_RANGE;
program->interpreter.state.x = position->tran.x;
program->interpreter.state.y = position->tran.y;
program->interpreter.state.z = position->tran.z;
program->interpreter.state.a = position->a;
program->interpreter.state.b = position->b;
program->interpreter.state.c = position->c;
program->interpreter.state.u = position->u;
program->interpreter.state.v = position->v;
program->interpreter.state.w = position->w;
program->interpreter.state.status = SIM_INTERP_OK;
return SIM_PROGRAM_OK;
return sim_interpreter_sync_position(&program->interpreter, position) == SIM_INTERP_OK ? SIM_PROGRAM_OK : SIM_PROGRAM_ERR_INTERPRETER;
}
int32_t sim_program_start(sim_program *program) {
@@ -412,14 +415,15 @@ int32_t sim_program_start(sim_program *program) {
}
int32_t sim_program_pause(sim_program *program) {
if (program == NULL || program->status != SIM_PROGRAM_RUNNING) return SIM_PROGRAM_ERR_STATE;
if (program == NULL || (program->status != SIM_PROGRAM_RUNNING && program->status != SIM_PROGRAM_COMPLETED && program->status != SIM_PROGRAM_LOADED)) return SIM_PROGRAM_ERR_STATE;
program->pause_from = program->status;
program->status = SIM_PROGRAM_PAUSED;
return SIM_PROGRAM_OK;
}
int32_t sim_program_resume(sim_program *program) {
if (program == NULL || program->status != SIM_PROGRAM_PAUSED) return SIM_PROGRAM_ERR_STATE;
program->status = SIM_PROGRAM_RUNNING;
program->status = program->pause_from;
return SIM_PROGRAM_OK;
}
@@ -494,8 +498,11 @@ int32_t sim_program_step(sim_program *program) {
}
int32_t sim_program_seek(sim_program *program, uint32_t line) {
sim_coordinate_context coordinates;
if (program == NULL || line > program->line_count) return SIM_PROGRAM_ERR_RANGE;
if (sim_interpreter_get_coordinates(&program->interpreter, &coordinates) != SIM_INTERP_OK) return SIM_PROGRAM_ERR_INTERPRETER;
if (sim_interpreter_reset(&program->interpreter) != SIM_INTERP_OK) return SIM_PROGRAM_ERR_INTERPRETER;
if (sim_interpreter_set_coordinates(&program->interpreter, &coordinates) != SIM_INTERP_OK) return SIM_PROGRAM_ERR_INTERPRETER;
program->current_line = 0; program->status = SIM_PROGRAM_PAUSED; program->has_motion = 0;
while (program->current_line < line) {
int result = sim_program_step(program);
@@ -528,11 +535,22 @@ uint32_t sim_program_canonical(const sim_program *program, char *output, uint32_
char *destination = output == NULL ? discard : output;
uint32_t destination_capacity = output == NULL ? 1 : capacity;
int written;
sim_coordinate_context coordinates;
char coordinate_json[3072];
size_t used = 0;
if (program == NULL) return 0;
if (sim_interpreter_get_coordinates(&program->interpreter, &coordinates) != SIM_INTERP_OK) return 0;
used += (size_t)snprintf(coordinate_json + used, sizeof(coordinate_json) - used, "{\"system\":%d,\"offsets\":[", coordinates.system);
for (int system = 0; system < 9; ++system) {
used += (size_t)snprintf(coordinate_json + used, sizeof(coordinate_json) - used, "%s[", system ? "," : "");
for (int axis = 0; axis < 9; ++axis) used += (size_t)snprintf(coordinate_json + used, sizeof(coordinate_json) - used, "%s%.17g", axis ? "," : "", coordinates.offsets[system][axis]);
used += (size_t)snprintf(coordinate_json + used, sizeof(coordinate_json) - used, "]");
}
(void)snprintf(coordinate_json + used, sizeof(coordinate_json) - used, "]}");
written = snprintf(destination, destination_capacity,
"{\"call_depth\":%u,\"control_code\":%d,\"current_line\":%u,\"error_line\":%u,\"feed\":%.17g,\"has_control\":%u,\"has_motion\":%u,\"last_error\":%d,\"line_count\":%u,\"main_line_count\":%u,\"motion_control_mode\":%d,\"path_tolerance\":%.17g,\"naive_cam_tolerance\":%.17g,\"motion_mode\":%d,\"plane\":%d,\"rapid_velocity\":%.17g,\"schema\":\"sim_program.v1\",\"speed\":%.17g,\"spindle_mode\":%d,\"status\":%u,\"subroutine_count\":%u,\"source_length\":%u,\"units\":%d,\"distance_mode\":%d,\"interpreter_state_hash\":\"%llu\"}",
"{\"call_depth\":%u,\"control_code\":%d,\"current_line\":%u,\"error_line\":%u,\"feed\":%.17g,\"has_control\":%u,\"has_motion\":%u,\"last_error\":%d,\"line_count\":%u,\"main_line_count\":%u,\"motion_control_mode\":%d,\"path_tolerance\":%.17g,\"naive_cam_tolerance\":%.17g,\"motion_mode\":%d,\"plane\":%d,\"rapid_velocity\":%.17g,\"schema\":\"sim_program.v1\",\"speed\":%.17g,\"spindle_mode\":%d,\"status\":%u,\"subroutine_count\":%u,\"source_length\":%u,\"units\":%d,\"distance_mode\":%d,\"interpreter_state_hash\":\"%llu\",\"coordinate_context\":%s,\"pause_from\":%u}",
program->call_depth, program->control_code, program->current_line, program->error_line, program->interpreter.state.feed, program->has_control, program->has_motion, program->last_error, program->line_count, program->main_line_count, program->motion_control_mode, program->path_tolerance, program->naive_cam_tolerance, program->interpreter.state.motion_mode, program->interpreter.state.plane, program->rapid_velocity, program->interpreter.state.speed, program->interpreter.state.spindle_mode, program->status, program->subroutine_count, program->source_length, program->interpreter.state.units, program->interpreter.state.distance_mode,
(unsigned long long)sim_interpreter_state_hash(&program->interpreter));
(unsigned long long)sim_interpreter_state_hash(&program->interpreter), coordinate_json, program->pause_from);
return written < 0 ? 0 : (uint32_t)written + 1u;
}

View File

@@ -44,15 +44,10 @@ static int state_accepts_kind(uint32_t current, const char *kind) {
if (current == SIM_PROTOCOL_NEW) return is_kind(kind, "HELLO");
if (current == SIM_PROTOCOL_HELLO) return is_kind(kind, "SESSION_ATTACH") || is_kind(kind, "CAPABILITIES");
if (current == SIM_PROTOCOL_ATTACHED) return is_kind(kind, "SESSION_READY");
if (current == SIM_PROTOCOL_READY) return 1;
if (current == SIM_PROTOCOL_PAUSED) {
return is_kind(kind, "PROGRAM_RESUME") || is_kind(kind, "RESUME") ||
is_kind(kind, "PROGRAM_ABORT") || is_kind(kind, "ESTOP") ||
is_kind(kind, "POWER") || is_kind(kind, "SET_OVERRIDE") ||
is_kind(kind, "SIM_STEP") ||
is_kind(kind, "PING") || is_kind(kind, "SNAPSHOT_REQUEST") ||
is_kind(kind, "DESTROY");
}
/* Task pause does not disconnect the command channel. The task gate
decides whether a valid frame may execute, and records refusals at
that sequence so Resume/Abort cannot be stranded behind a gap. */
if (current == SIM_PROTOCOL_READY || current == SIM_PROTOCOL_PAUSED) return 1;
return is_kind(kind, "RESET");
}

View File

@@ -206,7 +206,7 @@ int32_t sim_task_command_gate(const sim_task *task, uint32_t command,
(idle || task->state.mode == SIM_TASK_MODE_AUTO)
? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_RESET_STATE;
case SIM_TASK_GATE_PAUSE:
return machine_on && task->state.mode == SIM_TASK_MODE_AUTO && task->state.program_status == SIM_TASK_PROGRAM_RUNNING
return machine_on && (task->state.mode == SIM_TASK_MODE_AUTO || task->state.mode == SIM_TASK_MODE_MDI) && task->state.state == SIM_TASK_RUNNING
? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_PAUSE_STATE;
case SIM_TASK_GATE_RESUME:
return machine_on && (task->state.mode == SIM_TASK_MODE_AUTO || task->state.mode == SIM_TASK_MODE_MDI) && task->state.program_status == SIM_TASK_PROGRAM_PAUSED
@@ -225,18 +225,17 @@ int32_t sim_task_command_gate(const sim_task *task, uint32_t command,
manual tab changes mode before sending the command when required. */
return machine_on && idle ? SIM_TASK_GATE_ALLOWED : io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : machine_on ? SIM_TASK_GATE_INTERP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
case SIM_TASK_GATE_OVERRIDE:
/* Feed override is an online trajectory control. LinuxCNC accepts it
while AUTO is running or paused, provided the machine is powered. */
return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : machine_on ? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_MACHINE_OFF;
/* AXIS setters are available independently of machine power. */
return SIM_TASK_GATE_ALLOWED;
case SIM_TASK_GATE_SPINDLE:
return machine_on && idle && task->state.mode == SIM_TASK_MODE_MANUAL ? SIM_TASK_GATE_ALLOWED : io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : !machine_on ? SIM_TASK_GATE_MACHINE_OFF : SIM_TASK_GATE_MODE_STATE;
return machine_on && idle ? SIM_TASK_GATE_ALLOWED : io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : !machine_on ? SIM_TASK_GATE_MACHINE_OFF : SIM_TASK_GATE_INTERP_ACTIVE;
case SIM_TASK_GATE_COOLANT:
return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : machine_on ? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_MACHINE_OFF;
case SIM_TASK_GATE_TOOL:
case SIM_TASK_GATE_PROBE:
return machine_on && idle && task->state.mode == SIM_TASK_MODE_MANUAL ? SIM_TASK_GATE_ALLOWED : io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : !machine_on ? SIM_TASK_GATE_MACHINE_OFF : SIM_TASK_GATE_MODE_STATE;
case SIM_TASK_GATE_TOUCH_OFF:
return machine_on && idle && homed && task->state.mode == SIM_TASK_MODE_MANUAL ? SIM_TASK_GATE_ALLOWED : io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : !machine_on ? SIM_TASK_GATE_MACHINE_OFF : !homed ? SIM_TASK_GATE_UNHOMED : SIM_TASK_GATE_MODE_STATE;
return machine_on && idle && homed ? SIM_TASK_GATE_ALLOWED : io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : !machine_on ? SIM_TASK_GATE_MACHINE_OFF : !homed ? SIM_TASK_GATE_UNHOMED : SIM_TASK_GATE_INTERP_ACTIVE;
case SIM_TASK_GATE_TOOL_TOUCH_OFF:
return machine_on && idle && homed && requested == 1U && task->state.mode == SIM_TASK_MODE_MANUAL ? SIM_TASK_GATE_ALLOWED : io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : !machine_on ? SIM_TASK_GATE_MACHINE_OFF : !homed ? SIM_TASK_GATE_UNHOMED : SIM_TASK_GATE_MODE_STATE;
case SIM_TASK_GATE_MDI:

View File

@@ -64,6 +64,9 @@ int main(void) {
assert(fabs(interpreter.state.x - 11.0) < 1e-12);
assert(fabs(interpreter.state.y - 22.0) < 1e-12);
assert(fabs(interpreter.state.z - 33.0) < 1e-12);
assert(sim_interpreter_read_motion(&interpreter, &motion) == SIM_INTERP_OK);
assert(fabs(motion.end.tran.x - 11.0) < 1e-12);
assert(fabs(motion.end.tran.y - 22.0) < 1e-12);
assert(sim_interpreter_execute(&interpreter, "G53 G0 X0 Y0 Z10", (uint32_t)(sizeof("G53 G0 X0 Y0 Z10") - 1)) == SIM_INTERP_OK);
assert(fabs(interpreter.state.x) < 1e-12 && fabs(interpreter.state.y) < 1e-12 && fabs(interpreter.state.z - 10.0) < 1e-12);
assert(sim_interpreter_execute(&interpreter, "G10 L20 P0 X0 Y0 Z10", (uint32_t)(sizeof("G10 L20 P0 X0 Y0 Z10") - 1)) == SIM_INTERP_OK);
@@ -78,5 +81,17 @@ int main(void) {
assert(sim_interpreter_execute(&interpreter, "G80 M7", (uint32_t)(sizeof("G80 M7") - 1)) == SIM_INTERP_OK);
assert(sim_interpreter_execute(&interpreter, "M8", 2) == SIM_INTERP_OK);
assert(sim_interpreter_execute(&interpreter, "M9", 2) == SIM_INTERP_OK);
assert(sim_interpreter_reset(&interpreter) == SIM_INTERP_OK);
assert(sim_interpreter_execute(&interpreter, "G0 X30 A20 C40", sizeof("G0 X30 A20 C40") - 1) == SIM_INTERP_OK);
assert(sim_interpreter_execute(&interpreter, "G10 L20 P1 X5", 13) == SIM_INTERP_OK);
assert(interpreter.state.x == 30.0);
assert(sim_interpreter_execute(&interpreter, "G0 X6", 5) == SIM_INTERP_OK);
assert(sim_interpreter_read_motion(&interpreter, &motion) == SIM_INTERP_OK);
assert(motion.start.tran.x == 30.0 && motion.end.tran.x == 31.0);
assert(sim_interpreter_execute(&interpreter, "G10 L20 P9 A5 C10", 17) == SIM_INTERP_OK);
assert(interpreter.state.a == 20.0 && interpreter.state.c == 40.0);
assert(sim_interpreter_execute(&interpreter, "G59.3 G0 A6 C11", 15) == SIM_INTERP_OK);
assert(sim_interpreter_read_motion(&interpreter, &motion) == SIM_INTERP_OK);
assert(motion.end.a == 21.0 && motion.end.c == 41.0);
return 0;
}

View File

@@ -1,6 +1,7 @@
#include "sim_linuxcnc_tp.h"
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
@@ -43,6 +44,44 @@ int main(void) {
assert(sim_linuxcnc_tp_step(planner) == 0);
assert(sim_linuxcnc_tp_read_state(planner, &state) == 0);
assert(state.position.tran.x > 0.0 && sim_linuxcnc_tp_jog(planner, 0, 1, 100.0, 22) != 0);
assert(sim_linuxcnc_tp_abort(planner) == 0);
assert(sim_linuxcnc_tp_set_position(planner, &origin) == 0);
assert(sim_linuxcnc_tp_jog(planner, 0, 1, -1.0, 23) != 0);
assert(sim_linuxcnc_tp_jog(planner, 0, 1, NAN, 23) != 0);
assert(sim_linuxcnc_tp_jog(planner, 0, 1, 0.0, 23) == 0);
for (int index = 0; index < 3000; ++index) {
assert(sim_linuxcnc_tp_step(planner) == 0);
assert(sim_linuxcnc_tp_read_state(planner, &state) == 0);
assert(state.position.tran.x <= 5.0);
if (state.queue_depth == 0) break;
}
assert(state.queue_depth == 0 && fabs(state.position.tran.x - 5.0) < 1e-9);
assert(sim_linuxcnc_tp_jog(planner, 0, 1, 0.0, 24) != 0);
assert(sim_linuxcnc_tp_jog(planner, 0, -1, 0.0, 25) == 0);
for (int index = 0; index < 50; ++index) assert(sim_linuxcnc_tp_step(planner) == 0);
assert(sim_linuxcnc_tp_read_state(planner, &state) == 0);
assert(state.position.tran.x < 5.0 && state.position.tran.x > -5.0);
assert(sim_linuxcnc_tp_abort(planner) == 0);
assert(sim_linuxcnc_tp_read_state(planner, &state) == 0 && state.queue_depth == 0);
assert(sim_linuxcnc_tp_set_position(planner, &origin) == 0);
first = pose(1, 0, 0); second = pose(2, 0, 0);
assert(sim_linuxcnc_tp_add_line(planner, &first, 10, 300, 101, 1) == 0);
assert(sim_linuxcnc_tp_add_line(planner, &second, 10, 300, 102, 1) == 0);
assert(sim_linuxcnc_tp_set_paused(planner, 1) == 0);
assert(sim_linuxcnc_tp_step_line(planner) == 0);
for (int tick = 0; tick < 3000 && sim_linuxcnc_tp_is_stepping(planner); ++tick) assert(sim_linuxcnc_tp_step(planner) == 0);
assert(!sim_linuxcnc_tp_is_stepping(planner));
assert(sim_linuxcnc_tp_read_state(planner, &state) == 0);
/* motion/control.c detects an ID transition after the servo sample;
a transition may advance less than one cycle into the following TC. */
assert(state.position.tran.x >= 1.0 && state.position.tran.x < 1.0 + 10.0 * 0.001);
assert(sim_linuxcnc_tp_queue_depth(planner) == 1);
assert(sim_linuxcnc_tp_step_line(planner) == 0);
for (int tick = 0; tick < 3000 && sim_linuxcnc_tp_is_stepping(planner); ++tick) assert(sim_linuxcnc_tp_step(planner) == 0);
assert(sim_linuxcnc_tp_read_state(planner, &state) == 0);
assert(fabs(state.position.tran.x - 2.0) < 1e-9);
assert(sim_linuxcnc_tp_axis_limit(planner, 0, 0) == -5.0);
assert(sim_linuxcnc_tp_axis_limit(planner, 0, 1) == 5.0);
puts(canonical);
free(planner);
return 0;

View File

@@ -1,6 +1,7 @@
#include "sim_protocol.h"
#include <assert.h>
#include <stddef.h>
int main(void) {
sim_protocol_reset();
@@ -15,17 +16,31 @@ int main(void) {
assert(sim_protocol_last_seq() == 4);
assert(sim_protocol_accept(1, 5, "PROGRAM_PAUSE", "op-4", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, 6, "SIM_STEP", "op-5-step", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, 7, "MDI_EXECUTE", "op-5", 2) == SIM_PROTOCOL_ERR_STATE);
assert(sim_protocol_accept(1, 7, "PROGRAM_ABORT", "op-5", 2) == SIM_PROTOCOL_OK);
/* These are connected-channel frames. MDI can still be rejected by
the task; the transport must not poison subsequent command IDs. */
const char *paused_commands[] = {"VISUAL_CLEAR", "SET_SPINDLE_OVERRIDE", "SET_MAX_VELOCITY", "PROGRAM_STEP", "MDI_EXECUTE", "PROGRAM_PAUSE"};
uint32_t seq = 7;
for (unsigned int i = 0; i < sizeof(paused_commands) / sizeof(paused_commands[0]); ++i) {
assert(sim_protocol_accept(1, seq, paused_commands[i], "paused-op", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_last_seq() == seq++);
assert(sim_protocol_state_value() == SIM_PROTOCOL_PAUSED);
}
assert(sim_protocol_accept(1, seq + 1, "PROGRAM_RESUME", "resume-gap", 2) == SIM_PROTOCOL_ERR_SEQUENCE);
assert(sim_protocol_last_seq() == seq - 1);
assert(sim_protocol_accept(1, seq, "MDI_EXECUTE", NULL, 2) == SIM_PROTOCOL_ERR_OPERATION_ID);
assert(sim_protocol_accept(1, seq++, "PROGRAM_RESUME", "resume", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_state_value() == SIM_PROTOCOL_READY);
assert(sim_protocol_accept(1, 8, "PROGRAM_PAUSE", "op-6", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, 9, "PROGRAM_RESUME", "op-7", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(2, 10, "PING", NULL, 2) == SIM_PROTOCOL_ERR_SCHEMA);
assert(sim_protocol_accept(1, 10, "PING", NULL, 1) == SIM_PROTOCOL_ERR_TICK);
assert(sim_protocol_accept(1, 10, "DESTROY", NULL, 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, 11, "PING", NULL, 2) == SIM_PROTOCOL_ERR_STATE);
assert(sim_protocol_accept(1, 12, "RESET", "reset-1", 0) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, seq++, "PROGRAM_PAUSE", "pause-again", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, seq++, "PROGRAM_ABORT", "abort", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_state_value() == SIM_PROTOCOL_READY);
assert(sim_protocol_accept(2, seq, "PING", NULL, 2) == SIM_PROTOCOL_ERR_SCHEMA);
assert(sim_protocol_accept(1, seq, "PING", NULL, 1) == SIM_PROTOCOL_ERR_TICK);
assert(sim_protocol_accept(1, seq++, "DESTROY", NULL, 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, seq, "PING", NULL, 2) == SIM_PROTOCOL_ERR_STATE);
assert(sim_protocol_accept(1, seq, "RESET", "reset-1", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_state_value() == SIM_PROTOCOL_NEW);
assert(sim_protocol_set_tick(4) == SIM_PROTOCOL_ERR_TICK);
assert(sim_protocol_last_seq() == 0);
assert(sim_protocol_set_tick(4) == SIM_PROTOCOL_OK);
assert(sim_protocol_set_tick(3) == SIM_PROTOCOL_ERR_TICK);
return 0;
}

View File

@@ -29,8 +29,9 @@ int main(void) {
assert(sim_task_set_mode(&task, SIM_TASK_MODE_AUTO) == SIM_TASK_OK);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_PAUSE, 0, 1, 0, 1, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_OVERRIDE, 0, 1, 0, 1, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_OVERRIDE, 0, 0, 0, 1, 0, 10) == SIM_TASK_GATE_MACHINE_OFF);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_OVERRIDE, 0, 1, 1, 1, 0, 10) == SIM_TASK_GATE_ESTOP_ACTIVE);
/* axis.tcl sliders are available in OFF and ESTOP as well as AUTO. */
assert(sim_task_command_gate(&task, SIM_TASK_GATE_OVERRIDE, 0, 0, 0, 1, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_OVERRIDE, 0, 1, 1, 1, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_POWER, 0, 1, 0, 1, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_SET_MODE, SIM_TASK_MODE_MANUAL, 1, 0, 1, 0, 10) == SIM_TASK_GATE_MODE_AUTO_ACTIVE);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_ESTOP, 1, 1, 0, 1, 0, 10) == SIM_TASK_GATE_ALLOWED);