Skip to content
Merged
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
10 changes: 7 additions & 3 deletions src/emc/iotask/ioControl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -864,9 +864,6 @@ int main(int argc, char *argv[])
int t = ((EMC_TOOL_PREPARE*)emcioCommand)->tool;
rtapi_print_msg(RTAPI_MSG_DBG, "EMC_TOOL_PREPARE tool=%d pocket=%d\n", t, p);

// it doesn't make sense to prep the spindle pocket
if(random_toolchanger && p == 0) break;

// Set HAL pins/params for tool number, pocket, and index.
iocontrol_data->tool_prep_index = p;
*(iocontrol_data->tool_prep_pocket) = random_toolchanger? p: fms[p];
Expand All @@ -875,6 +872,13 @@ int main(int argc, char *argv[])
} else {
*(iocontrol_data->tool_prep_number) = emcioStatus.tool.toolTable[p].toolno;
}

// it doesn't make sense to prep the spindle pocket
if (random_toolchanger && p == 0) {
emcioStatus.tool.pocketPrepped = 0;
break;
}

/* then set the prepare pin to tell external logic to get started */
*(iocontrol_data->tool_prepare) = 1;
// the feedback logic is done inside read_hal_inputs()
Expand Down
3 changes: 3 additions & 0 deletions tests/toolchanger/reload-tool/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This test reproduces https://github.com/LinuxCNC/linuxcnc/issues/330.

It tests the behavior for both random and non-random toolchanger machines.
2 changes: 2 additions & 0 deletions tests/toolchanger/reload-tool/checkresult
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/sh
exit 0 # test failure is indicated by test.sh exit value
60 changes: 60 additions & 0 deletions tests/toolchanger/reload-tool/core_sim.hal
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# core HAL config file for simulation

# first load all the RT modules that will be needed
# kinematics
loadrt trivkins
# motion controller, get name and thread periods from ini file
loadrt [EMCMOT]EMCMOT base_period_nsec=[EMCMOT]BASE_PERIOD servo_period_nsec=[EMCMOT]SERVO_PERIOD num_joints=[TRAJ]AXES
# load 6 differentiators (for velocity and accel signals
loadrt ddt count=6
# load additional blocks
loadrt hypot count=2
loadrt comp count=3
loadrt or2 count=1

# add motion controller functions to servo thread
addf motion-command-handler servo-thread
addf motion-controller servo-thread
# link the differentiator functions into the code
addf ddt.0 servo-thread
addf ddt.1 servo-thread
addf ddt.2 servo-thread
addf ddt.3 servo-thread
addf ddt.4 servo-thread
addf ddt.5 servo-thread
addf hypot.0 servo-thread
addf hypot.1 servo-thread

# create HAL signals for position commands from motion module
# loop position commands back to motion module feedback
net Xpos axis.0.motor-pos-cmd => axis.0.motor-pos-fb ddt.0.in
net Ypos axis.1.motor-pos-cmd => axis.1.motor-pos-fb ddt.2.in
net Zpos axis.2.motor-pos-cmd => axis.2.motor-pos-fb ddt.4.in

# send the position commands thru differentiators to
# generate velocity and accel signals
net Xvel ddt.0.out => ddt.1.in hypot.0.in0
net Xacc <= ddt.1.out
net Yvel ddt.2.out => ddt.3.in hypot.0.in1
net Yacc <= ddt.3.out
net Zvel ddt.4.out => ddt.5.in hypot.1.in0
net Zacc <= ddt.5.out

# Cartesian 2- and 3-axis velocities
net XYvel hypot.0.out => hypot.1.in1
net XYZvel <= hypot.1.out

# estop loopback
net estop-loop iocontrol.0.user-enable-out iocontrol.0.emc-enable-in

# create signals for tool loading loopback
net tool-prepare <= iocontrol.0.tool-prepare
net tool-prepared => iocontrol.0.tool-prepared

net tool-change <= iocontrol.0.tool-change
net tool-changed => iocontrol.0.tool-changed

net tool-number <= iocontrol.0.tool-number
net tool-prep-number <= iocontrol.0.tool-prep-number
net tool-prep-pocket <= iocontrol.0.tool-prep-pocket

1 change: 1 addition & 0 deletions tests/toolchanger/reload-tool/non-random/checkresult
1 change: 1 addition & 0 deletions tests/toolchanger/reload-tool/non-random/core_sim.hal
1 change: 1 addition & 0 deletions tests/toolchanger/reload-tool/non-random/postgui.hal
273 changes: 273 additions & 0 deletions tests/toolchanger/reload-tool/non-random/test-ui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
#!/usr/bin/env python

import linuxcnc
import hal

import math
import time
import sys
import subprocess
import os
import signal
import glob
import re


def wait_for_linuxcnc_startup(status, timeout=10.0):

"""Poll the Status buffer waiting for it to look initialized,
rather than just allocated (all-zero). Returns on success, throws
RuntimeError on failure."""

start_time = time.time()
while time.time() - start_time < timeout:
status.poll()
if (status.angular_units == 0.0) \
or (status.axes == 0) \
or (status.axis_mask == 0) \
or (status.cycle_time == 0.0) \
or (status.exec_state != linuxcnc.EXEC_DONE) \
or (status.interp_state != linuxcnc.INTERP_IDLE) \
or (status.inpos == False) \
or (status.linear_units == 0.0) \
or (status.max_acceleration == 0.0) \
or (status.max_velocity == 0.0) \
or (status.program_units == 0.0) \
or (status.rapidrate == 0.0) \
or (status.state != linuxcnc.STATE_ESTOP) \
or (status.task_state != linuxcnc.STATE_ESTOP):
time.sleep(0.1)
else:
# looks good
return

# timeout, throw an exception
raise RuntimeError


def wait_for_hal_pin(name, value, timeout=10):
start_time = time.time()
while (time.time() - start_time) < timeout:
if h[name] == value:
return
time.sleep(0.1)
raise RuntimeError("hal pin %s didn't get to %s after %.3f seconds" % (name, value, timeout))


# After doing something that should change the stat buffer, wait this
# long before polling to let the change propagate through.
# FIXME: this is bogus
stat_poll_wait = 0.100


c = linuxcnc.command()
s = linuxcnc.stat()
e = linuxcnc.error_channel()

h = hal.component("test-ui")

h.newpin("tool-change", hal.HAL_BIT, hal.HAL_IN)
h.newpin("tool-changed", hal.HAL_BIT, hal.HAL_OUT)
h["tool-changed"] = False

h.newpin("tool-prepare", hal.HAL_BIT, hal.HAL_IN)
h.newpin("tool-prepared", hal.HAL_BIT, hal.HAL_OUT)
h["tool-prepared"] = False

h.newpin("tool-number", hal.HAL_S32, hal.HAL_IN)
h.newpin("tool-prep-number", hal.HAL_S32, hal.HAL_IN)
h.newpin("tool-prep-pocket", hal.HAL_S32, hal.HAL_IN)

h.ready()

os.system("halcmd source ./postgui.hal")


# Wait for LinuxCNC to initialize itself so the Status buffer stabilizes.
wait_for_linuxcnc_startup(s)

c.state(linuxcnc.STATE_ESTOP_RESET)
c.state(linuxcnc.STATE_ON)
c.home(-1)
c.wait_complete()

c.mode(linuxcnc.MODE_MDI)


#
# At startup there's no tool in the spindle and no tool prep or change
# is being requested.
#

assert(h['tool-change'] == False)
assert(h['tool-prepare'] == False)
assert(h['tool-number'] == 0)
assert(h['tool-prep-number'] == 0)
assert(h['tool-prep-pocket'] == 0)

s.poll()
assert(s.tool_in_spindle == 0)
assert(s.pocket_prepped == -1)


#
# Prepare T2
#

c.mdi('t2')
wait_for_hal_pin('tool-prepare', True)

assert(h['tool-change'] == False)
assert(h['tool-prepare'] == True)
assert(h['tool-number'] == 0)
assert(h['tool-prep-number'] == 2)
assert(h['tool-prep-pocket'] == 46)

s.poll()
assert(s.tool_in_spindle == 0)
assert(s.pocket_prepped == -1)

h['tool-prepared'] = True
wait_for_hal_pin('tool-prepare', False)
h['tool-prepared'] = False

assert(h['tool-change'] == False)
assert(h['tool-prepare'] == False)
assert(h['tool-number'] == 0)
assert(h['tool-prep-number'] == 2)
assert(h['tool-prep-pocket'] == 46)

time.sleep(stat_poll_wait)
s.poll()
assert(s.tool_in_spindle == 0)
assert(s.pocket_prepped == 6) # ugh, non-random tc gives you tool-table-array index, not pocket


#
# Change to T2
#

c.mdi('m6')
wait_for_hal_pin('tool-change', True)

assert(h['tool-change'] == True)
assert(h['tool-prepare'] == False)
assert(h['tool-number'] == 0)
assert(h['tool-prep-number'] == 2)
assert(h['tool-prep-pocket'] == 46)

time.sleep(stat_poll_wait)
s.poll()
assert(s.tool_in_spindle == 0)
assert(s.pocket_prepped == 6)

h['tool-changed'] = True
wait_for_hal_pin('tool-change', False)
h['tool-changed'] = False

assert(h['tool-change'] == False)
assert(h['tool-prepare'] == False)
assert(h['tool-number'] == 2)
assert(h['tool-prep-number'] == 0)
assert(h['tool-prep-pocket'] == 0)

time.sleep(stat_poll_wait)
s.poll()
assert(s.tool_in_spindle == 2)
assert(s.pocket_prepped == -1)


#
# Prepare T12
#

c.mdi('t12')
wait_for_hal_pin('tool-prepare', True)

assert(h['tool-change'] == False)
assert(h['tool-prepare'] == True)
assert(h['tool-number'] == 2)
assert(h['tool-prep-number'] == 12)
assert(h['tool-prep-pocket'] == 9)

time.sleep(stat_poll_wait)
s.poll()
assert(s.tool_in_spindle == 2)
assert(s.pocket_prepped == -1)

h['tool-prepared'] = True
wait_for_hal_pin('tool-prepare', False)
h['tool-prepared'] = False

assert(h['tool-change'] == False)
assert(h['tool-prepare'] == False)
assert(h['tool-number'] == 2)
assert(h['tool-prep-number'] == 12)
assert(h['tool-prep-pocket'] == 9)

time.sleep(stat_poll_wait)
s.poll()
assert(s.tool_in_spindle == 2)
assert(s.pocket_prepped == 4)


#
# Prepare T2
# This tool is already in the spindle.
#

c.mdi('t2')
wait_for_hal_pin('tool-prepare', True)

assert(h['tool-change'] == False)
assert(h['tool-prepare'] == True)
assert(h['tool-number'] == 2)
assert(h['tool-prep-number'] == 2)
assert(h['tool-prep-pocket'] == 46)

time.sleep(stat_poll_wait)
s.poll()
assert(s.tool_in_spindle == 2)
assert(s.pocket_prepped == 4)

h['tool-prepared'] = True
wait_for_hal_pin('tool-prepare', False)
h['tool-prepared'] = False

assert(h['tool-change'] == False)
assert(h['tool-prepare'] == False)
assert(h['tool-number'] == 2)
assert(h['tool-prep-number'] == 2)
assert(h['tool-prep-pocket'] == 46)

time.sleep(stat_poll_wait)
s.poll()
assert(s.tool_in_spindle == 2)
assert(s.pocket_prepped == 6)


#
# Change to prepared tool (T2 again)
#

c.mdi('m6')
try:
wait_for_hal_pin('tool-change', True, timeout=5)
except RuntimeError:
# It *should* time out, no tool change should be performed since
# the prepared tool is already in the spindle.
pass

assert(h['tool-change'] == False)
assert(h['tool-prepare'] == False)
assert(h['tool-number'] == 2)
assert(h['tool-prep-number'] == 2)
assert(h['tool-prep-pocket'] == 46)

time.sleep(stat_poll_wait)
s.poll()
assert(s.tool_in_spindle == 2)
assert(s.pocket_prepped == 6)


sys.exit(0)
Loading