Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/emc/task/taskintf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2098,10 +2098,13 @@ int emcSpindleUpdate(EMC_SPINDLE_STAT stat[], int num_spindles){

// A failed read leaves stat as it was. If that copy is older than the
// last command sent, motion may already be running it, so do not report
// it done: a probe just queued would look finished.
// it done: a probe just queued would look finished. Nor in error: an
// abort sent since has cleared it, and task would abort again, along
// with whatever command came next. An error that stands shows on the
// next read, as only an abort clears it.
static int emcMotionUpdateFailed(EMC_MOTION_STAT * stat)
{
if (stat->status == RCS_STATUS::DONE &&
if (stat->status != RCS_STATUS::EXEC &&
stat->echo_serial_number != emcmotCommand.commandNum) {
stat->status = RCS_STATUS::EXEC;
}
Expand Down
2 changes: 2 additions & 0 deletions tests/stale-motion-status/README
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ slow-controller uses kinematics that busy wait, so motion-controller
takes most of the servo period and status reads by task often fail
while it runs. The wait starts once task is up: its startup reads the
status only once a second and gives up after ten tries.

After the program, slow-controller runs five G38.2 in MDI that end without contact. A little after each probe move ends, slowkins busy waits nearly all of each servo period for a while, so that status reads by task fail but the command echoes it waits for get through. Task keeps its copy from before its abort, still in error; it must not abort again on it, which on_abort.ngc counts, nor drop the move sent as soon as wait_complete() returns.
5 changes: 5 additions & 0 deletions tests/stale-motion-status/slow-controller/on_abort.ngc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
o<on_abort> sub
; count the aborts, for test-ui.py
#4000 = [#4000 + 1]
o<on_abort> endsub
m2
1 change: 1 addition & 0 deletions tests/stale-motion-status/slow-controller/probe.hal
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ net probe probe-comp.out => motion.probe-input
# by task often fail while it runs. test-ui.py sets slowkins.wait-ns once
# task is up: at startup task reads the status only once a second.
addf slowkins.arm servo-thread 1
net motion-type motion.motion-type => slowkins.motion-type
38 changes: 31 additions & 7 deletions tests/stale-motion-status/slow-controller/slowkins.comp
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
component slowkins "Identity kinematics for X Y Z that busy wait in kinematicsForward once per servo period";
pin out bool dummy = 1; // halcompile requires at least one pin
pin in sint motion_type "Wire to motion.motion-type: the end of a probe move starts a burst";
param rw uint burst_delay = 0 "Servo periods from the end of a probe move to its burst";
param rw uint burst_periods = 0 "Servo periods of a burst; 0 for none";
param rw uint burst_ns = 0 "Busy wait in each servo period of a burst";
variable rtapi_sint was_probing = 0;
variable rtapi_uint delay_left = 0;
variable rtapi_uint burst_left = 0;
function arm "Let the next kinematicsForward call busy wait";
option singleton yes;
option extra_setup;
Expand All @@ -8,10 +15,11 @@ license "GPL";
author "Luca Toniolo";
;;
#include <kinematics.h>
#include <motion_types.h>

static struct haldata {
hal_uint_t wait_ns;
int armed;
rtapi_uint armed_ns;
} *haldata;

EXTRA_SETUP() {
Expand All @@ -29,10 +37,26 @@ EXTRA_SETUP() {
}

/* motion calls kinematicsForward twice per period in free mode; one wait
per period keeps the controller inside the period */
per period keeps the controller inside the period. A burst, a little
after a probe move ends, makes nearly every status read by task fail
while the command echoes it waits for still get through. */
FUNCTION(arm) {
(void)__comp_inst;
haldata->armed = 1;
rtapi_sint probing = motion_type == EMC_MOTION_TYPE_PROBING;

if (was_probing && !probing && burst_periods) {
delay_left = burst_delay;
burst_left = burst_periods;
}
was_probing = probing;
if (delay_left) {
delay_left--;
haldata->armed_ns = hal_get_uint(haldata->wait_ns);
} else if (burst_left) {
burst_left--;
haldata->armed_ns = burst_ns;
} else {
haldata->armed_ns = hal_get_uint(haldata->wait_ns);
}
}

KINS_NOT_SWITCHABLE
Expand All @@ -52,9 +76,9 @@ int kinematicsForward(const double *j, EmcPose *pos,
{
(void)fflags;
(void)iflags;
if (haldata->armed) {
rtapi_sint end = rtapi_get_time() + (rtapi_sint)hal_get_uint(haldata->wait_ns);
haldata->armed = 0;
if (haldata->armed_ns) {
rtapi_sint end = rtapi_get_time() + (rtapi_sint)haldata->armed_ns;
haldata->armed_ns = 0;
while (rtapi_get_time() < end)
;
}
Expand Down
2 changes: 2 additions & 0 deletions tests/stale-motion-status/slow-controller/test.ini
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ CYCLE_TIME = 0.001

[RS274NGC]
PARAMETER_FILE = sim.var
SUBROUTINE_PATH = .
ON_ABORT_COMMAND = O <on_abort> call

[EMCMOT]
EMCMOT = motmod
Expand Down
50 changes: 50 additions & 0 deletions tests/stale-motion-status/test-ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,56 @@ def error(err):
error(err)
err = e.poll()

# slow-controller: a probe that ends without contact makes task abort, once.
# Once task has seen the probe fail and aborted, slowkins makes nearly every
# status read by task fail for a while, so its copy from before the abort,
# still in error, is all it has; it must not abort again on it, nor abort
# the move sent as soon as wait_complete() returns. on_abort.ngc counts the
# aborts in #4000.
def aborts():
c.mdi("(debug,aborts #4000)")
c.wait_complete(60)
start = time.time()
while True:
err = e.poll()
if err and err[1].startswith("aborts "):
return int(float(err[1].split()[1]))
if time.time() - start > 60:
sys.stderr.write("timeout reading the abort count\n")
sys.exit(1)
time.sleep(0.01)

if hal.component_exists("slowkins"):
c.mode(linuxcnc.MODE_MDI)
c.wait_complete(60)
settle("MDI mode was on", lambda: s.task_mode == linuxcnc.MODE_MDI)
before = aborts()
hal.set_p("slowkins.burst-ns", "995000")
hal.set_p("slowkins.burst-delay", "20")
hal.set_p("slowkins.burst-periods", "200")
for n in range(5):
c.mdi("G38.2 Z-0.01 F6")
c.wait_complete(60)
c.mdi("G0 Z0")
c.wait_complete(60)
start = time.time()
while True:
s.poll()
if s.state == linuxcnc.RCS_DONE and s.interp_state == linuxcnc.INTERP_IDLE:
break
if time.time() - start > 60:
sys.stderr.write("timeout waiting for the move after probe %d\n" % n)
sys.exit(1)
time.sleep(0.05)
if abs(s.position[2]) > 1e-6:
failures.append("the move after probe %d was lost, Z%.4f" % (n, s.position[2]))
break
count = aborts() - before
if count != n + 1:
failures.append("%d aborts after probe %d, want %d" % (count, n, n + 1))
break
hal.set_p("slowkins.burst-periods", "0")

for msg in failures + errors:
sys.stderr.write("error: %s\n" % msg)
if h["finished"] != 1:
Expand Down