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:
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user