Align Web XYZAC command gates with LinuxCNC

This commit is contained in:
2026-08-28 21:33:11 +08:00
parent 78d884bc67
commit cbd0bc1003
30 changed files with 337 additions and 150 deletions

File diff suppressed because one or more lines are too long

View File

@@ -53,6 +53,47 @@ enum sim_task_program_value {
SIM_TASK_PROGRAM_FAULT = 6,
};
/* LinuxCNC emctaskmain command classes used by the Web XYZAC surface. */
enum sim_task_gate_command {
SIM_TASK_GATE_ESTOP = 1,
SIM_TASK_GATE_RESET = 2,
SIM_TASK_GATE_POWER = 3,
SIM_TASK_GATE_HOME = 4,
SIM_TASK_GATE_JOG = 5,
SIM_TASK_GATE_MDI = 6,
SIM_TASK_GATE_RUN = 7,
SIM_TASK_GATE_PAUSE = 8,
SIM_TASK_GATE_RESUME = 9,
SIM_TASK_GATE_ABORT = 10,
SIM_TASK_GATE_RESTART = 11,
SIM_TASK_GATE_KINEMATICS = 12,
SIM_TASK_GATE_SET_MODE = 13,
SIM_TASK_GATE_PROGRAM_LOAD = 14,
SIM_TASK_GATE_STEP = 15,
};
enum sim_task_gate_result {
SIM_TASK_GATE_ALLOWED = 0,
SIM_TASK_GATE_ESTOP_ACTIVE = 1,
SIM_TASK_GATE_MACHINE_OFF = 2,
SIM_TASK_GATE_MACHINE_ON = 3,
SIM_TASK_GATE_INTERP_ACTIVE = 4,
SIM_TASK_GATE_TASKFILE_MISSING = 5,
SIM_TASK_GATE_ESTOPOFF_REQUIRED = 6,
SIM_TASK_GATE_PAUSE_STATE = 7,
SIM_TASK_GATE_RESUME_STATE = 8,
SIM_TASK_GATE_ABORT_STATE = 9,
SIM_TASK_GATE_RESTART_STATE = 10,
SIM_TASK_GATE_KINEMATICS_INVALID = 11,
SIM_TASK_GATE_MODE_STATE = 12,
SIM_TASK_GATE_MODE_AUTO_ACTIVE = 13,
SIM_TASK_GATE_JOG_STATE = 14,
SIM_TASK_GATE_MDI_STATE = 15,
SIM_TASK_GATE_RESET_STATE = 16,
SIM_TASK_GATE_UNKNOWN_COMMAND = 17,
SIM_TASK_GATE_UNHOMED = 18,
};
typedef struct sim_task_state {
char context_id[SIM_TASK_CONTEXT_CAPACITY];
char session_id[SIM_TASK_SESSION_CAPACITY];
@@ -81,6 +122,11 @@ int32_t sim_task_transition(sim_task *task, uint32_t state);
int32_t sim_task_fault(sim_task *task, int32_t error_code);
int32_t sim_task_estop(sim_task *task, int32_t error_code);
int32_t sim_task_recover(sim_task *task);
int32_t sim_task_command_gate(const sim_task *task, uint32_t command,
uint32_t requested, uint32_t io_ready,
uint32_t io_estop, uint32_t homed,
uint32_t queue_depth,
uint32_t queue_limit);
int32_t sim_task_restore(sim_task *task, uint32_t state, uint32_t mode,
uint32_t program_status, uint32_t seq,
uint32_t simulation_tick, uint32_t current_line,

View File

@@ -456,7 +456,6 @@ int sim_io_set_estop(sim_io *io, uint32_t estop) {
int sim_io_set_power(sim_io *io, uint32_t active) {
if (!valid_io(io) || active > 1U) return SIM_IO_ERR_ARGUMENT;
if (io->state.estop) { set_last(io, SIM_IO_ERR_ESTOP); return SIM_IO_ERR_ESTOP; }
if (!active) {
io->state.ready = 0;
io->state.tool_prepare = 0;
@@ -470,6 +469,7 @@ int sim_io_set_power(sim_io *io, uint32_t active) {
io->state.spindles[index].brake = 1;
}
} else {
if (io->state.estop) { set_last(io, SIM_IO_ERR_ESTOP); return SIM_IO_ERR_ESTOP; }
if (io->state.fault || io->state.toolchanger_fault) { set_last(io, SIM_IO_ERR_FAULT); return SIM_IO_ERR_FAULT; }
if (!spindle_stopped(io)) { set_last(io, SIM_IO_ERR_BUSY); return SIM_IO_ERR_BUSY; }
io->state.ready = 1;

View File

@@ -165,6 +165,90 @@ int32_t sim_task_recover(sim_task *task) {
return result;
}
int32_t sim_task_command_gate(const sim_task *task, uint32_t command,
uint32_t requested, uint32_t io_ready,
uint32_t io_estop, uint32_t homed,
uint32_t queue_depth,
uint32_t queue_limit) {
int machine_on;
int active;
int idle;
if (!valid_task(task) || io_ready > 1U || io_estop > 1U || homed > 1U)
return SIM_TASK_GATE_UNKNOWN_COMMAND;
machine_on = io_ready != 0U && io_estop == 0U;
active = task->state.state == SIM_TASK_RUNNING ||
task->state.state == SIM_TASK_PAUSED ||
task->state.program_status == SIM_TASK_PROGRAM_RUNNING ||
task->state.program_status == SIM_TASK_PROGRAM_PAUSED;
idle = !active;
switch (command) {
case SIM_TASK_GATE_ESTOP:
/* LinuxCNC accepts ESTOP from every task state; reset is only the
inverse edge while the auxiliary estop input is asserted. */
return requested == 1U || io_estop ? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_ESTOPOFF_REQUIRED;
case SIM_TASK_GATE_POWER:
if (requested == 1U) return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : machine_on ? SIM_TASK_GATE_MACHINE_ON : SIM_TASK_GATE_ALLOWED;
/* emcTaskSetState(OFF) aborts motion and is accepted while running. */
return machine_on ? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_MACHINE_OFF;
case SIM_TASK_GATE_PROGRAM_LOAD:
/* PLAN_OPEN is an immediate task command and is accepted in the
OFF/ESTOP/ESTOP_RESET branches when the interpreter is idle. */
return idle ? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_INTERP_ACTIVE;
case SIM_TASK_GATE_RUN:
if (io_estop || !machine_on) return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
if (!idle) return SIM_TASK_GATE_INTERP_ACTIVE;
if (!homed) return SIM_TASK_GATE_UNHOMED;
return task->state.program_status == SIM_TASK_PROGRAM_IDLE ? SIM_TASK_GATE_TASKFILE_MISSING : SIM_TASK_GATE_ALLOWED;
case SIM_TASK_GATE_STEP:
if (io_estop || !machine_on) return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
if (!homed) return SIM_TASK_GATE_UNHOMED;
return task->state.program_status != SIM_TASK_PROGRAM_IDLE &&
(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
? 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
? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_RESUME_STATE;
case SIM_TASK_GATE_ABORT:
/* EMC_TASK_ABORT_TYPE is an immediate command in every emctaskmain
state branch, including OFF/ESTOP and an idle interpreter. */
return SIM_TASK_GATE_ALLOWED;
case SIM_TASK_GATE_RESTART:
if (!idle) return SIM_TASK_GATE_INTERP_ACTIVE;
return task->state.program_status == SIM_TASK_PROGRAM_IDLE ? SIM_TASK_GATE_TASKFILE_MISSING : SIM_TASK_GATE_ALLOWED;
case SIM_TASK_GATE_HOME:
return machine_on && idle ? SIM_TASK_GATE_ALLOWED : machine_on ? SIM_TASK_GATE_INTERP_ACTIVE : io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
case SIM_TASK_GATE_JOG:
/* AXIS allows jog commands while idle in all three task modes; the
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_MDI:
if (io_estop || !machine_on) return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
if (!idle) return SIM_TASK_GATE_INTERP_ACTIVE;
if (!homed) return SIM_TASK_GATE_UNHOMED;
if (queue_limit != 0U && queue_depth >= queue_limit) return SIM_TASK_GATE_MDI_STATE;
return SIM_TASK_GATE_ALLOWED;
case SIM_TASK_GATE_KINEMATICS:
if (requested > 2U) return SIM_TASK_GATE_KINEMATICS_INVALID;
if (io_estop || !machine_on) return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : SIM_TASK_GATE_MACHINE_OFF;
if (!homed) return SIM_TASK_GATE_UNHOMED;
return idle ? SIM_TASK_GATE_ALLOWED : SIM_TASK_GATE_INTERP_ACTIVE;
case SIM_TASK_GATE_SET_MODE:
if (requested > SIM_TASK_MODE_AUTO || io_estop || !machine_on) return io_estop ? SIM_TASK_GATE_ESTOP_ACTIVE : io_ready ? SIM_TASK_GATE_MODE_STATE : SIM_TASK_GATE_MACHINE_OFF;
/* emcTaskSetMode explicitly refuses AUTO -> MANUAL/MDI while the
AUTO interpreter is non-idle. */
if (active && task->state.mode == SIM_TASK_MODE_AUTO && requested != SIM_TASK_MODE_AUTO)
return SIM_TASK_GATE_MODE_AUTO_ACTIVE;
return SIM_TASK_GATE_ALLOWED;
case SIM_TASK_GATE_RESET:
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_RESET_STATE;
default:
return SIM_TASK_GATE_UNKNOWN_COMMAND;
}
}
int32_t sim_task_restore(sim_task *task, uint32_t state, uint32_t mode,
uint32_t program_status, uint32_t seq,
uint32_t simulation_tick, uint32_t current_line,

View File

@@ -17,9 +17,20 @@ int main(void) {
assert(sim_task_attach(&task) == SIM_TASK_OK);
assert(sim_task_set_command(&task, 3, 0) == SIM_TASK_ERR_SEQUENCE);
assert(sim_task_set_command(&task, 2, 0) == SIM_TASK_OK);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_PROGRAM_LOAD, 0, 0, 0, 0, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_RUN, 0, 0, 0, 0, 0, 10) == SIM_TASK_GATE_MACHINE_OFF);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_POWER, 1, 0, 0, 0, 0, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_POWER, 0, 0, 0, 0, 0, 10) == SIM_TASK_GATE_MACHINE_OFF);
assert(sim_task_set_program(&task, SIM_TASK_PROGRAM_LOADED, 0) == SIM_TASK_OK);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_RUN, 0, 1, 0, 1, 1, 10) == SIM_TASK_GATE_ALLOWED);
assert(sim_task_transition(&task, SIM_TASK_RUNNING) == SIM_TASK_OK);
assert(sim_task_set_program(&task, SIM_TASK_PROGRAM_RUNNING, 1) == SIM_TASK_OK);
assert(sim_task_command_gate(&task, SIM_TASK_GATE_PAUSE, 0, 1, 0, 1, 0, 10) == SIM_TASK_GATE_PAUSE_STATE);
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_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);
assert(sim_task_set_command(&task, 3, 4) == SIM_TASK_OK);
assert(sim_task_transition(&task, SIM_TASK_PAUSED) == SIM_TASK_OK);
assert(sim_task_set_command(&task, 4, 3) == SIM_TASK_ERR_TICK);