diff --git a/configs/sim/gmoccapy/simulated_home.hal b/configs/sim/gmoccapy/simulated_home.hal index 3422569833e..dfa0cf700cf 100644 --- a/configs/sim/gmoccapy/simulated_home.hal +++ b/configs/sim/gmoccapy/simulated_home.hal @@ -1,4 +1,3 @@ -loadrt or2 names=or2_0 loadrt comp names=comp_x,comp_y,comp_z net Xhomeswpos => comp_x.in0 @@ -17,16 +16,10 @@ setp comp_x.hyst .02 setp comp_y.hyst .02 setp comp_z.hyst .02 -net Xhomesw <= comp_x.out +net Xhomesw <= comp_x.out => joint.0.home-sw-in net Yhomesw <= comp_y.out => joint.1.home-sw-in -net Zhomesw <= comp_z.out - -net Xhomesw => or2_0.in0 -net Zhomesw => or2_0.in1 -net XZhomesw or2_0.out => joint.0.home-sw-in joint.2.home-sw-in +net Zhomesw <= comp_z.out => joint.2.home-sw-in addf comp_x servo-thread addf comp_y servo-thread addf comp_z servo-thread - -addf or2_0 servo-thread diff --git a/configs/sim/touchy/touchy.ini b/configs/sim/touchy/touchy.ini index 369d601b336..6ab65406719 100644 --- a/configs/sim/touchy/touchy.ini +++ b/configs/sim/touchy/touchy.ini @@ -50,7 +50,7 @@ SERVO_PERIOD = 1000000 HALFILE = core_sim.hal # add a test panel so people can actually test the screen -HALCMD = loadusr gladevcp --always_above -c touchy_test touchy_test_panel.glade +HALCMD = loadusr -Wn touchy_test gladevcp --always_above -c touchy_test touchy_test_panel.glade POSTGUI_HALFILE = touchy_postgui.hal [TRAJ] diff --git a/lib/python/linuxcnc_util.py b/lib/python/linuxcnc_util.py index 3b215bf78a3..6ae8ff9e6f5 100644 --- a/lib/python/linuxcnc_util.py +++ b/lib/python/linuxcnc_util.py @@ -102,6 +102,34 @@ def wait_for_home(self, joints, timeout=10.0): raise LinuxCNC_Exception("timeout waiting for homing to complete:\nstatus.homed:" + str(self.status.homed) + "\nstatus.position:" + str(self.status.position)) + def home_joints(self, joints, timeout=10.0): + """Arguments: + + 'joints' is a list of unhomed joint numbers. + + 'timeout' is a float, the number of seconds to wait for + each joint to home before giving up. + + Homes the joints one after the other: motion refuses to home a + joint while another one is homing. A joint counts as done once + it is homed and no joint is homing any more; motion keeps the + homing flag set until it accepts the next home command. + + Returns once all the joints are homed, raises + LinuxCNC_Exception if one did not home before the timeout.""" + + for joint in joints: + self.command.home(joint) + start_time = time.time() + while True: + self.status.poll() + if self.status.homed[joint] and not any(j["homing"] for j in self.status.joint): + break + if (time.time() - start_time) > timeout: + raise LinuxCNC_Exception("timeout waiting for joint %d to home:\nstatus.homed:" % joint + str(self.status.homed) + "\nstatus.position:" + str(self.status.position)) + time.sleep(0.01) + + def wait_for_axis_to_stop(self, axis_letter, timeout=10.0): """Arguments: 'axis_letter' is a single character from the set [xyzabcuvw] diff --git a/src/emc/task/taskintf.cc b/src/emc/task/taskintf.cc index 049ae4d26d4..84a41c9e038 100644 --- a/src/emc/task/taskintf.cc +++ b/src/emc/task/taskintf.cc @@ -1704,7 +1704,9 @@ int emcTrajUpdate(EMC_TRAJ_STAT * stat) if (emcmotStatus.motionFlag & EMCMOT_MOTION_ERROR_BIT) { stat->status = RCS_STATUS::ERROR; - } else if (stat->inpos && (stat->queue == 0)) { + } else if (stat->inpos && (stat->queue == 0) && !emcmotStatus.probing) { + // a probe move that ends without contact is reported a servo + // cycle after the planner goes idle, so wait for the verdict stat->status = RCS_STATUS::DONE; } else { stat->status = RCS_STATUS::EXEC; diff --git a/tests/abort/on_abort_command-crazy-move/test-ui.py b/tests/abort/on_abort_command-crazy-move/test-ui.py index 0fd8c1dc559..1de81bc80b9 100755 --- a/tests/abort/on_abort_command-crazy-move/test-ui.py +++ b/tests/abort/on_abort_command-crazy-move/test-ui.py @@ -38,10 +38,7 @@ c.state(linuxcnc.STATE_ESTOP_RESET) c.state(linuxcnc.STATE_ON) -c.home(0) -c.home(1) -c.home(2) -l.wait_for_home([1, 1, 1, 0, 0, 0, 0, 0, 0]) +l.home_joints([0, 1, 2]) # # Print status function diff --git a/tests/abort/stop-button-crazy-move/test-ui.py b/tests/abort/stop-button-crazy-move/test-ui.py index 839940e1e26..e20888a9489 100755 --- a/tests/abort/stop-button-crazy-move/test-ui.py +++ b/tests/abort/stop-button-crazy-move/test-ui.py @@ -10,7 +10,7 @@ # Time increment and timeout, seconds TIME_INCR = 0.1 -TIMEOUT = 10.0 +TIMEOUT = 60.0 # # connect to LinuxCNC @@ -37,10 +37,7 @@ c.state(linuxcnc.STATE_ESTOP_RESET) c.state(linuxcnc.STATE_ON) -c.home(0) -c.home(1) -c.home(2) -l.wait_for_home([1, 1, 1, 0, 0, 0, 0, 0, 0]) +l.home_joints([0, 1, 2]) # # Print status function diff --git a/tests/hal-show/checkresult b/tests/hal-show/checkresult new file mode 100755 index 00000000000..11774b2335e --- /dev/null +++ b/tests/hal-show/checkresult @@ -0,0 +1,12 @@ +#!/bin/bash +set -e + +TESTDIR=$(dirname "$1") +cd "${TESTDIR}" + +# The owner column is the component ID, which depends on the order in which +# halcmd and rtapi_app take IDs during each loadrt. That order changes with +# CPU load, so mask it. +sed --in-place --regexp-extended --expression='s/^ *[0-9]+( +[a-z0-9]+ +(IN|OUT|I\/O) .*)$/(ignored comp id)\1/' result + +diff -u expected result diff --git a/tests/hal-show/expected b/tests/hal-show/expected index dd41b0cf1e4..27325b418c1 100644 --- a/tests/hal-show/expected +++ b/tests/hal-show/expected @@ -1,11 +1,11 @@ Component Pins: Owner Type Dir Value Name - 10 bool IN FALSE conv-bool-uint.0.in <== net-conv-bool-uint.0.in - 16 real IN 0 conv-real-sint.0.in <== net-conv-real-sint.0.in - 19 sint IN 0 conv-sint-real.0.in <== net-conv-sint-real.0.in - 22 sint IN 0 conv-sint-uint.0.in <== net-conv-sint-uint.0.in - 13 uint IN 0x0000000000000000 conv-uint-bool.0.in <== net-conv-uint-bool.0.in - 25 uint IN 0x0000000000000000 conv-uint-sint.0.in <== net-conv-uint-sint.0.in +(ignored comp id) bool IN FALSE conv-bool-uint.0.in <== net-conv-bool-uint.0.in +(ignored comp id) real IN 0 conv-real-sint.0.in <== net-conv-real-sint.0.in +(ignored comp id) sint IN 0 conv-sint-real.0.in <== net-conv-sint-real.0.in +(ignored comp id) sint IN 0 conv-sint-uint.0.in <== net-conv-sint-uint.0.in +(ignored comp id) uint IN 0x0000000000000000 conv-uint-bool.0.in <== net-conv-uint-bool.0.in +(ignored comp id) uint IN 0x0000000000000000 conv-uint-sint.0.in <== net-conv-uint-sint.0.in Signals: Type Value Name (linked to) @@ -36,12 +36,12 @@ sint 0 net-conv-uint-sint.0.out Component Pins: Owner Type Dir Value Name - 10 bool IN TRUE conv-bool-uint.0.in <== net-conv-bool-uint.0.in - 16 real IN 2.147484e+09 conv-real-sint.0.in <== net-conv-real-sint.0.in - 19 sint IN -2147483648 conv-sint-real.0.in <== net-conv-sint-real.0.in - 22 sint IN -9223372036854775808 conv-sint-uint.0.in <== net-conv-sint-uint.0.in - 13 uint IN 0x00000000FFFFFFFF conv-uint-bool.0.in <== net-conv-uint-bool.0.in - 25 uint IN 0xFFFFFFFFFFFFFFFF conv-uint-sint.0.in <== net-conv-uint-sint.0.in +(ignored comp id) bool IN TRUE conv-bool-uint.0.in <== net-conv-bool-uint.0.in +(ignored comp id) real IN 2.147484e+09 conv-real-sint.0.in <== net-conv-real-sint.0.in +(ignored comp id) sint IN -2147483648 conv-sint-real.0.in <== net-conv-sint-real.0.in +(ignored comp id) sint IN -9223372036854775808 conv-sint-uint.0.in <== net-conv-sint-uint.0.in +(ignored comp id) uint IN 0x00000000FFFFFFFF conv-uint-bool.0.in <== net-conv-uint-bool.0.in +(ignored comp id) uint IN 0xFFFFFFFFFFFFFFFF conv-uint-sint.0.in <== net-conv-uint-sint.0.in Signals: Type Value Name (linked to) diff --git a/tests/halmodule/stream/stream_reader.py b/tests/halmodule/stream/stream_reader.py index 0aaa20965e8..bcb98c89be2 100755 --- a/tests/halmodule/stream/stream_reader.py +++ b/tests/halmodule/stream/stream_reader.py @@ -10,8 +10,9 @@ assert reader.sampleno == i+1 assert reader.read() is None assert reader.num_underruns == 1 +# report before ready(): halrun ends the run once it sees ready +print("pass", flush=True) c.ready() -print("pass") try: while 1: time.sleep(1) diff --git a/tests/interp/g28.2/immediate-mode-guard/test-ui.py b/tests/interp/g28.2/immediate-mode-guard/test-ui.py index 6a45e0b7c86..008b8bc6e28 100755 --- a/tests/interp/g28.2/immediate-mode-guard/test-ui.py +++ b/tests/interp/g28.2/immediate-mode-guard/test-ui.py @@ -30,6 +30,7 @@ """ import linuxcnc +import linuxcnc_util import hal import sys @@ -74,11 +75,7 @@ def drain_errors(): c.state(linuxcnc.STATE_ESTOP_RESET) c.state(linuxcnc.STATE_ON) c.mode(linuxcnc.MODE_MANUAL) -c.home(0) -c.home(1) -c.home(2) -if not wait_homed([1, 1, 1]): - fail("initial home-all did not home all joints: {}".format(list(s.homed[:3]))) +linuxcnc_util.LinuxCNC(command=c, status=s, error=e).home_joints([0, 1, 2]) # Get into a coordinated (teleop) mode -- what a stray FREE dip would destroy. c.teleop_enable(1) diff --git a/tests/interp/g28.2/invalid-pword/test-ui.py b/tests/interp/g28.2/invalid-pword/test-ui.py index 4237a215b8c..19effdd827e 100755 --- a/tests/interp/g28.2/invalid-pword/test-ui.py +++ b/tests/interp/g28.2/invalid-pword/test-ui.py @@ -31,6 +31,7 @@ """ import linuxcnc +import linuxcnc_util import hal import os @@ -68,16 +69,6 @@ def wait_idle(timeout=10.0): return False -def wait_homed(expected, timeout=10.0): - t0 = time.time() - while time.time() - t0 < timeout: - poll() - if list(s.homed[:3]) == expected: - return True - time.sleep(0.01) - return False - - def drain_errors(): msgs = [] while True: @@ -95,11 +86,7 @@ def mode_name(m): c.state(linuxcnc.STATE_ESTOP_RESET) c.state(linuxcnc.STATE_ON) -c.home(0) -c.home(1) -c.home(2) -if not wait_homed([1, 1, 1]): - fail("initial home-all did not home all joints: {}".format(list(s.homed[:3]))) +linuxcnc_util.LinuxCNC(command=c, status=s, error=e).home_joints([0, 1, 2]) # Establish a coordinated mode worth preserving. c.mode(linuxcnc.MODE_MDI) diff --git a/tests/interp/g28.2/position-model/test-ui.py b/tests/interp/g28.2/position-model/test-ui.py index dd1e78295e0..108f8b9e009 100755 --- a/tests/interp/g28.2/position-model/test-ui.py +++ b/tests/interp/g28.2/position-model/test-ui.py @@ -20,6 +20,7 @@ """ import linuxcnc +import linuxcnc_util import hal import sys @@ -46,16 +47,6 @@ def wait_idle(timeout=10.0): return False -def wait_homed(expected, timeout=10.0): - t0 = time.time() - while time.time() - t0 < timeout: - poll() - if list(s.homed[:len(expected)]) == expected: - return True - time.sleep(0.01) - return False - - def fail(msg): print("FAIL: " + msg) sys.exit(1) @@ -76,10 +67,7 @@ def mdi(cmd): c.state(linuxcnc.STATE_ESTOP_RESET) c.state(linuxcnc.STATE_ON) -for j in range(4): - c.home(j) -if not wait_homed([1, 1, 1, 1]): - fail("initial home-all did not home all joints: {}".format(list(s.homed[:4]))) +linuxcnc_util.LinuxCNC(command=c, status=s).home_joints([0, 1, 2, 3]) c.mode(linuxcnc.MODE_MDI) time.sleep(0.2) diff --git a/tests/interp/g28.2/sequencing/test-ui.py b/tests/interp/g28.2/sequencing/test-ui.py index 87c68638802..49cd512bed1 100755 --- a/tests/interp/g28.2/sequencing/test-ui.py +++ b/tests/interp/g28.2/sequencing/test-ui.py @@ -13,6 +13,7 @@ """ import linuxcnc +import linuxcnc_util import hal import sys @@ -53,16 +54,6 @@ def wait_x(expected, timeout=5.0): return False -def wait_homed(expected, timeout=5.0): - t0 = time.time() - while time.time() - t0 < timeout: - poll() - if list(s.homed[:3]) == expected: - return True - time.sleep(0.01) - return False - - def fail(msg): print("FAIL: " + msg) sys.exit(1) @@ -74,11 +65,7 @@ def near(a, b, tol=0.001): c.state(linuxcnc.STATE_ESTOP_RESET) c.state(linuxcnc.STATE_ON) -c.home(0) -c.home(1) -c.home(2) -if not wait_homed([1, 1, 1]): - fail("initial home-all did not home all joints: {}".format(list(s.homed[:3]))) +linuxcnc_util.LinuxCNC(command=c, status=s).home_joints([0, 1, 2]) c.mode(linuxcnc.MODE_MDI) time.sleep(0.2) diff --git a/tests/interp/m98m99/12-M99-endless-main-program/expected.motion-logger b/tests/interp/m98m99/12-M99-endless-main-program/expected.motion-logger index aebc5e1e183..3f1d81c8228 100644 --- a/tests/interp/m98m99/12-M99-endless-main-program/expected.motion-logger +++ b/tests/interp/m98m99/12-M99-endless-main-program/expected.motion-logger @@ -176,6 +176,7 @@ SPINDLE_OFF DISABLE JOINT_UNHOME joint=-2 FREE +SET_TERM_COND termCond=2, tolerance=0.001 JOG_ABORT joint=0 JOG_ABORT joint=1 JOG_ABORT joint=2 diff --git a/tests/interp/m98m99/12-M99-endless-main-program/test-ui.py b/tests/interp/m98m99/12-M99-endless-main-program/test-ui.py index 0813006d4eb..108274aea10 100755 --- a/tests/interp/m98m99/12-M99-endless-main-program/test-ui.py +++ b/tests/interp/m98m99/12-M99-endless-main-program/test-ui.py @@ -16,6 +16,22 @@ e = linuxcnc.error_channel() +# +# Task queues the canon commands of interpreter init before it reads its +# first command. Let it issue them, or an early ESTOP_RESET wipes them +# and the log depends on how fast this script starts. +# + +deadline = time.monotonic() + 60 +while True: + s.poll() + if s.state == linuxcnc.RCS_DONE: break + if time.monotonic() > deadline: + print("task did not finish startup") + sys.exit(1) + time.sleep(0.01) + + # # Come out of E-stop, turn the machine on, home, and switch to Auto mode. # diff --git a/tests/interp/mdi-oword-m66/test-ui.py b/tests/interp/mdi-oword-m66/test-ui.py index f923af3d7a9..f14aa9cdff6 100755 --- a/tests/interp/mdi-oword-m66/test-ui.py +++ b/tests/interp/mdi-oword-m66/test-ui.py @@ -34,11 +34,7 @@ c.state(linuxcnc.STATE_ESTOP_RESET) c.state(linuxcnc.STATE_ON) -c.home(0) -c.home(1) -c.home(2) - -l.wait_for_home([1, 1, 1, 0, 0, 0, 0, 0, 0]) +l.home_joints([0, 1, 2]) c.mode(linuxcnc.MODE_MDI) diff --git a/tests/interp/subroutine-return/test-ui.py b/tests/interp/subroutine-return/test-ui.py index df8769d3b9a..98bd7f968ff 100755 --- a/tests/interp/subroutine-return/test-ui.py +++ b/tests/interp/subroutine-return/test-ui.py @@ -34,10 +34,7 @@ c.state(linuxcnc.STATE_ESTOP_RESET) c.state(linuxcnc.STATE_ON) -c.home(0) -c.home(1) -c.home(2) -l.wait_for_home([1, 1, 1, 0, 0, 0, 0, 0, 0]) +l.home_joints([0, 1, 2]) c.mode(linuxcnc.MODE_AUTO) diff --git a/tests/maxkins/test-ui.py b/tests/maxkins/test-ui.py index d65e561e516..de629c82fc0 100755 --- a/tests/maxkins/test-ui.py +++ b/tests/maxkins/test-ui.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import linuxcnc +import linuxcnc_util import hal import time @@ -49,9 +50,7 @@ def wait_for_linuxcnc_startup(status, timeout=10.0): # HOME_ABSOLUTE_ENCODER = 1 is used in the ini file. c.state(linuxcnc.STATE_ESTOP_RESET) c.state(linuxcnc.STATE_ON) -for joint in range(9): - c.home(joint) -c.wait_complete() +linuxcnc_util.LinuxCNC(command=c, status=s, error=e).home_joints(range(9)) LETTERS = 'XYZABCUVW' diff --git a/tests/motion-logger/basic/expected.builtin-startup.in b/tests/motion-logger/basic/expected.builtin-startup.in index 2296018b31c..fce76f310a5 100644 --- a/tests/motion-logger/basic/expected.builtin-startup.in +++ b/tests/motion-logger/basic/expected.builtin-startup.in @@ -176,6 +176,7 @@ SPINDLE_OFF DISABLE JOINT_UNHOME joint=-2 FREE +SET_TERM_COND termCond=2, tolerance=0.001 JOG_ABORT joint=0 JOG_ABORT joint=1 JOG_ABORT joint=2 diff --git a/tests/motion-logger/basic/test-ui.py b/tests/motion-logger/basic/test-ui.py index 21da4c2bd47..edbefe1a447 100755 --- a/tests/motion-logger/basic/test-ui.py +++ b/tests/motion-logger/basic/test-ui.py @@ -46,6 +46,22 @@ def end_log(logfile_name): e = linuxcnc.error_channel() +# +# Task queues the canon commands of interpreter init before it reads its +# first command. Let it issue them, or an early ESTOP_RESET wipes them +# and the log depends on how fast this script starts. +# + +deadline = time.monotonic() + 60 +while True: + s.poll() + if s.state == linuxcnc.RCS_DONE: break + if time.monotonic() > deadline: + print("task did not finish startup") + sys.exit(1) + time.sleep(0.01) + + # # Come out of E-stop, turn the machine on, and switch to Auto mode. # diff --git a/tests/motion-logger/mountaindew/expected.motion-logger b/tests/motion-logger/mountaindew/expected.motion-logger index c52ea9598ed..928320a5a4e 100644 --- a/tests/motion-logger/mountaindew/expected.motion-logger +++ b/tests/motion-logger/mountaindew/expected.motion-logger @@ -80,6 +80,7 @@ SPINDLE_OFF DISABLE JOINT_UNHOME joint=-2 FREE +SET_TERM_COND termCond=2, tolerance=0.001 JOG_ABORT joint=0 JOG_ABORT joint=1 JOG_ABORT joint=2 diff --git a/tests/motion-logger/mountaindew/test-ui.py b/tests/motion-logger/mountaindew/test-ui.py index e4310ff6a86..89bc7f93df6 100755 --- a/tests/motion-logger/mountaindew/test-ui.py +++ b/tests/motion-logger/mountaindew/test-ui.py @@ -16,6 +16,22 @@ e = linuxcnc.error_channel() +# +# Task queues the canon commands of interpreter init before it reads its +# first command. Let it issue them, or an early ESTOP_RESET wipes them +# and the log depends on how fast this script starts. +# + +deadline = time.monotonic() + 60 +while True: + s.poll() + if s.state == linuxcnc.RCS_DONE: break + if time.monotonic() > deadline: + print("task did not finish startup") + sys.exit(1) + time.sleep(0.01) + + # # Come out of E-stop, turn the machine on, home, and switch to Auto mode. # diff --git a/tests/motion-logger/startup-gcode-abort/expected.motion-logger.in b/tests/motion-logger/startup-gcode-abort/expected.motion-logger.in index 8bef6b8b1a0..8bc77b66e94 100644 --- a/tests/motion-logger/startup-gcode-abort/expected.motion-logger.in +++ b/tests/motion-logger/startup-gcode-abort/expected.motion-logger.in @@ -114,12 +114,15 @@ JOINT_UNHOME joint=-2 FREE # -# Task is now got the 'motion.in-position' pin visible, and is -# accepting abort calls from the UI. +# Task now issues what interp_list holds: the interpreter's init and the +# first move of the startup code, then waits in its dwell. # +SET_TERM_COND termCond=2, tolerance=0.001 +SET_LINE x=-1, y=-2, z=-3, a=0, b=0, c=0, u=0, v=0, w=0, id=2, motion_type=1, vel=4.98888, ini_maxvel=4.98888, acc=1247.22, turn=-1 + # -# The test UI immediately sent an Abort. +# The test UI sent an Abort during the dwell. # JOG_ABORT joint=0 diff --git a/tests/motion-logger/startup-gcode-abort/test-ui.py b/tests/motion-logger/startup-gcode-abort/test-ui.py index 601664c4261..0735b97326d 100755 --- a/tests/motion-logger/startup-gcode-abort/test-ui.py +++ b/tests/motion-logger/startup-gcode-abort/test-ui.py @@ -31,6 +31,15 @@ time.sleep(0.1) waitlimit -= 1 +# Abort in the dwell of the startup code, after its first move went out. +waitlimit = 600 +while 0 < waitlimit: + s.poll() + if s.exec_state == linuxcnc.EXEC_WAITING_FOR_DELAY: + break + time.sleep(0.1) + waitlimit -= 1 + print("UI abort") sys.stdout.flush() diff --git a/tests/mqtt/display.py b/tests/mqtt/display.py index 3132c836434..c2e9a3faf95 100755 --- a/tests/mqtt/display.py +++ b/tests/mqtt/display.py @@ -20,12 +20,8 @@ c.state(linuxcnc.STATE_ON) c.wait_complete() -c.home(0) -c.home(1) -c.home(2) - -print("info: Waiting for homing to complete") -l.wait_for_home([1, 1, 1, 0, 0, 0, 0, 0, 0]) +print("info: Homing") +l.home_joints([0, 1, 2]) c.mode(linuxcnc.MODE_MDI) diff --git a/tests/multiclick/test.hal b/tests/multiclick/test.hal index 9e4d8558fc9..035f6eee00a 100644 --- a/tests/multiclick/test.hal +++ b/tests/multiclick/test.hal @@ -2,8 +2,8 @@ loadrt threads name1=millisecond period1=1000000 loadrt multiclick count=2 -loadrt sampler depth=100 cfg=bbbbbbbbbsbbbbbbbbbs -loadrt streamer depth=100 cfg=bb +loadrt sampler depth=128 cfg=bbbbbbbbbsbbbbbbbbbs +loadrt streamer depth=128 cfg=bb net in0 streamer.0.pin.0 => multiclick.0.in sampler.0.pin.0 @@ -50,9 +50,9 @@ setp multiclick.1.output-hold-ns 1500000 setp multiclick.1.invert-input True -# run halstreamer and let it pre-load the fifo -loadusr halstreamer input-signals -loadusr -w sleep 0.5 +# pre-load the whole input before the thread starts; halstreamer exits +# once the last line is in the fifo +loadusr -w halstreamer input-signals start loadusr -w halsampler -n 105 diff --git a/tests/mux/test.hal b/tests/mux/test.hal index 5cd72d1d369..f74fcc819d3 100644 --- a/tests/mux/test.hal +++ b/tests/mux/test.hal @@ -1,8 +1,8 @@ setexact_for_test_suite_only loadrt threads name1=slow period1=1000000 loadrt mux_generic config="b2b,b2f,b2s,b2u,f2b,f2f,f2s,f2u,s2b,s2f,s2s,s2u,u2b,u2f,u2s,u2u,b2b,f5f" -loadrt streamer depth=100 cfg=bu -loadrt sampler depth=100 cfg=bfsubfsubfsubfsubfbu +loadrt streamer depth=128 cfg=bu +loadrt sampler depth=128 cfg=bfsubfsubfsubfsubfbu addf streamer.0 slow addf mux-gen.00 slow @@ -80,8 +80,9 @@ net o15 mux-gen.15.out-uint sampler.0.pin.15 net o16 mux-gen.16.out-bool sampler.0.pin.16 net o17 mux-gen.17.out-real sampler.0.pin.17 -loadusr halstreamer input-signals -loadusr -w sleep 0.5 +# pre-load the whole input before the thread starts; halstreamer exits +# once the last line is in the fifo +loadusr -w halstreamer input-signals start loadusr -w halsampler -n 105 diff --git a/tests/remap/remap-io/test-ui.py b/tests/remap/remap-io/test-ui.py index c96923b1124..6e68dccd600 100755 --- a/tests/remap/remap-io/test-ui.py +++ b/tests/remap/remap-io/test-ui.py @@ -76,6 +76,10 @@ def do_in(inp, d_in=True, wait_mode=None, timeout=None): timeout = (' Q%d' % timeout if timeout is not None else ''), ) + # Read the pre-test values before setting the input pin; motion copies + # the pin into the status within a servo cycle + s.poll() # Update status channel + # Set input pin if d_in: h['d_in'] = inp @@ -83,7 +87,6 @@ def do_in(inp, d_in=True, wait_mode=None, timeout=None): h['a_in'] = inp # Print pre-test values - s.poll() # Update status channel c.mdi('(print,cmd: "%s"; input: %.4f)' % (cmd, inp)) c.mdi('(print, M66 remapping pre: 5399=#5399; 100=#100; ' 'ain0=%.2f; din0=%d)' % (s.ain[0], s.din[0])) diff --git a/tests/ui-smoke/_lib/drive.py b/tests/ui-smoke/_lib/drive.py index 796d681e0ca..3de99ebf67f 100755 --- a/tests/ui-smoke/_lib/drive.py +++ b/tests/ui-smoke/_lib/drive.py @@ -158,9 +158,15 @@ def home_all(cmd, stat, timeout): Ensures ESTOP_RESET + ON + MANUAL and retries the whole sequence (see HOME_RETRY_BUDGET).""" attempt_timeout = min(timeout, HOME_ATTEMPT_TIMEOUT_S) + deadline = time.monotonic() + timeout for attempt in range(1, HOME_RETRY_BUDGET + 1): - if not ensure_state(cmd, stat, linuxcnc.STATE_ESTOP_RESET, - "STATE_ESTOP_RESET"): + # Task reports ESTOP_RESET only while the machine is off, and an + # ESTOP_RESET command does not turn it off, so requesting it on a + # retry after the machine came on would never be met. + stat.poll() + if (stat.task_state != linuxcnc.STATE_ON and + not ensure_state(cmd, stat, linuxcnc.STATE_ESTOP_RESET, + "STATE_ESTOP_RESET")): return False if not ensure_state(cmd, stat, linuxcnc.STATE_ON, "STATE_ON"): return False @@ -171,10 +177,19 @@ def home_all(cmd, stat, timeout): stat.poll() njoints = stat.joints cmd.home(-1) - if wait_until_quiet( + all_homed = lambda s: all(s.homed[i] for i in range(njoints)) + done = wait_until_quiet(stat, all_homed, attempt_timeout) + # On a slow machine homing can still be running here. Homing + # again would restart joints whose home switch another, already + # homed joint now holds active (the gmoccapy sim shares one switch + # between X and Z), so let a running sequence finish instead. + if not done and any(stat.joint[i]["homing"] for i in range(njoints)): + done = wait_until_quiet( stat, - lambda s: all(s.homed[i] for i in range(njoints)), - attempt_timeout): + lambda s: all_homed(s) or not any( + s.joint[i]["homing"] for i in range(njoints)), + max(0.0, deadline - time.monotonic())) and all_homed(stat) + if done: cmd.teleop_enable(1) cmd.wait_complete() return True diff --git a/tests/ui-smoke/_lib/qtdragon-prepare.sh b/tests/ui-smoke/_lib/qtdragon-prepare.sh index 19404a998db..5227966b99c 100644 --- a/tests/ui-smoke/_lib/qtdragon-prepare.sh +++ b/tests/ui-smoke/_lib/qtdragon-prepare.sh @@ -128,9 +128,16 @@ def _ui_smoke_grab(): pass def _ui_smoke_on_sigusr1(signum, frame): + # The launcher signals from interpreter startup on, so this can run + # in the middle of an import, qtvcp's first PyQt5 import included. + # An import from here then loads PyQt5.QtCore a second time and sip + # refuses it, killing qtvcp. Do nothing until qtpy.QtCore is in; the + # launcher asks again. + timer = getattr(sys.modules.get('qtpy.QtCore'), 'QTimer', None) + if timer is None: + return try: - from qtpy.QtCore import QTimer - QTimer.singleShot(0, _ui_smoke_grab) + timer.singleShot(0, _ui_smoke_grab) except Exception: pass diff --git a/tests/ui-smoke/_lib/window-fit.sh b/tests/ui-smoke/_lib/window-fit.sh index 46717c762c6..f332e06b903 100644 --- a/tests/ui-smoke/_lib/window-fit.sh +++ b/tests/ui-smoke/_lib/window-fit.sh @@ -10,8 +10,9 @@ # # window_fit_check # is matched literally against the xwininfo tree -# line, e.g. '("touchy" "Touchy")'. The first matching window is checked -# against the root window (the screen). Needs xwininfo (x11-utils). +# line, e.g. '("touchy" "Touchy")'. The first viewable matching window +# is checked against the root window (the screen). Needs xwininfo +# (x11-utils). window_fit_check() { pattern="$1" @@ -22,11 +23,30 @@ window_fit_check() { root=$(xwininfo -root 2>/dev/null) screen_w=$(echo "$root" | awk '/Width:/{print $2; exit}') screen_h=$(echo "$root" | awk '/Height:/{print $2; exit}') - geom=$(xwininfo -root -tree 2>/dev/null | grep -F "$pattern" \ - | grep -oE '[0-9]+x[0-9]+\+[0-9]+\+[0-9]+' | head -1) - win_w=${geom%%x*} - rest=${geom#*x} - win_h=${rest%%+*} + # drive.py returns once task is up, which under load can be before + # the GUI maps its window. The tree also lists unmapped windows, at + # the size they were created with, so measure only a viewable one, + # and poll until its size reads the same twice in a row. + win_w= + win_h= + prev= + for _ in $(seq "${UI_SMOKE_FIT_TIMEOUT:-60}"); do + size= + for w in $(xwininfo -root -tree 2>/dev/null | grep -F "$pattern" \ + | grep -oE '^ +0x[0-9a-f]+'); do + info=$(xwininfo -id "$w" 2>/dev/null) + echo "$info" | grep -q "Map State: IsViewable" || continue + size=$(echo "$info" | awk '/^ *Width:/{w=$2} /^ *Height:/{h=$2} END{print w "x" h}') + break + done + if [ -n "$size" ] && [ "$size" = "$prev" ]; then + win_w=${size%x*} + win_h=${size#*x} + break + fi + prev=$size + sleep 1 + done if [ -z "$win_w" ] || [ -z "$win_h" ]; then echo "UI_SMOKE_FAIL: window-fit: no window matching $pattern found" return 1