Skip to content

Commit 5ca6e30

Browse files
committed
qtvcp -improve touchoff subprogram error handling
now we get messages with context and return data to qtdragon/hd
1 parent ea3ad6f commit 5ca6e30

4 files changed

Lines changed: 171 additions & 42 deletions

File tree

lib/python/qtvcp/lib/touchoff_subprogram.py

Lines changed: 138 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,25 @@
1818

1919
import sys
2020
import json
21+
import time
22+
import linuxcnc
2123

2224
from PyQt5.QtCore import QObject
23-
from qtvcp.core import Status, Action
25+
from qtvcp.core import Status, Action, Info
26+
from qtvcp import logger
27+
LOG = logger.getLogger(__name__)
28+
29+
# Force the log level for this module
30+
#LOG.setLevel(logger.ERROR) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL
2431

2532
STATUS = Status()
2633
ACTION = Action()
34+
INFO = Info()
2735

2836
class TouchOffSubprog(QObject):
2937
def __init__(self):
3038
QObject.__init__(self)
39+
self.timeout = 30
3140
# input parameters
3241
self.search_vel = 10.0
3342
self.probe_vel = 10.0
@@ -61,20 +70,33 @@ def __init__(self):
6170

6271
self.process()
6372

73+
# mdi timeout setting
74+
def set_timeout(self, time):
75+
self.timeout = time
76+
6477
def process(self):
6578
while 1:
6679
try:
6780
cmd = sys.stdin.readline()
6881
if cmd:
6982
error = self.process_command(cmd)
83+
84+
# block polling here. main program should start polling in their end
85+
STATUS.block_error_polling()
86+
7087
# error = 1 means success, error = None means ignore, anything else is an error
7188
if error is not None:
7289
if error != 1:
73-
sys.stdout.write("ERROR Touchoff returned with error with:{}\n".format(cmd))
90+
if type(error) == str:
91+
sys.stdout.write("ERROR Touchoff routine: {}\n".format(error))
92+
else:
93+
sys.stdout.write("ERROR Touchoff returned with error from cmd:{}\n".format(cmd))
7494
else:
7595
self.collect_status()
76-
sys.stdout.write("COMPLETE$" + self.string_to_send + "\n")
77-
sys.stdout.flush()
96+
sys.stdout.write("{} COMPLETE {}\n".format(cmd.rstrip().split('$')[0], self.string_to_send))
97+
else:
98+
sys.stdout.write("COMPLETE returned FROM COMMAND:{}\n".format(cmd))
99+
sys.stdout.flush()
78100
except KeyboardInterrupt:
79101
break
80102
except Exception as e:
@@ -85,7 +107,14 @@ def process(self):
85107
def process_command(self, cmd):
86108
cmd = cmd.rstrip().split('$')
87109
if not STATUS.is_on_and_idle(): return None
110+
pre = self.prechecks()
111+
if pre is not None: return pre
112+
113+
# start polling errors here - parent program should have blocked their polling
114+
STATUS.unblock_error_polling()
115+
88116
ACTION.CALL_MDI("G49")
117+
LOG.debug('COMMAND= {}'.format(cmd))
89118
if cmd[0] == "touchoff":
90119
self.search_vel = float(cmd[1])
91120
self.probe_vel = float(cmd[2])
@@ -94,60 +123,141 @@ def process_command(self, cmd):
94123
self.z_safe_travel = float(cmd[5])
95124
self.z_offset = float(cmd[6])
96125
error = self.touchoff()
97-
return error
98126
elif cmd[0] == "probe_z":
99127
parms = json.loads(cmd[1])
100128
self.update_data(parms)
101129
error = self.probe_z()
102-
return error
103130
else:
104-
return 0
131+
return 'No such touchoff routine'
132+
self.postreset()
133+
return error
134+
135+
def CALL_MDI_WAIT(self, code, timeout = 5):
136+
LOG.debug('MDI_WAIT_COMMAND= {}, maxt = {}'.format(code, timeout))
137+
for l in code:
138+
try:
139+
ACTION.CALL_MDI( l )
140+
result = ACTION.cmd.wait_complete(timeout)
141+
# give a chance for the error message to get to stdin
142+
time.sleep(.1)
143+
error = STATUS.ERROR.poll()
144+
if not error is None:
145+
ACTION.ABORT()
146+
LOG.debug('MDI error= {}'.format( error[1]))
147+
return error[1]
148+
except Exception as e:
149+
ACTION.ABORT()
150+
return e
151+
152+
if result == -1:
153+
ACTION.ABORT()
154+
return 'Command timed out: ({} second)'.format(timeout)
155+
elif result == linuxcnc.RCS_ERROR:
156+
ACTION.ABORT()
157+
return 'MDI_COMMAND_WAIT RCS error'
158+
159+
return 1
160+
161+
# need to be in the right mode - entries are in machine units
162+
def prechecks(self):
163+
ACTION.CALL_MDI('M70')
164+
if INFO.MACHINE_IS_METRIC and STATUS.is_metric_mode():
165+
return None
166+
if not INFO.MACHINE_IS_METRIC and not STATUS.is_metric_mode():
167+
return None
168+
# record motion modes
169+
if INFO.MACHINE_IS_METRIC:
170+
ACTION.CALL_MDI('g21')
171+
else:
172+
ACTION.CALL_MDI('g20')
173+
return None
174+
175+
# return to previous motion modes
176+
def postreset(self):
177+
ACTION.CALL_MDI('M72')
105178

106179
def touchoff(self):
180+
name = 'Touchoff'
107181
ACTION.CALL_MDI("G10 L20 P0 Z0")
108-
error = self.probe_down()
109-
if error == 0:
110-
ACTION.CALL_MDI("G90")
111-
return 0
112-
ACTION.CALL_MDI("G10 L20 P0 Z{}".format(self.z_offset))
113-
if ACTION.CALL_MDI_WAIT("G1 Z{} F{}".format(self.retract_distance, self.search_vel)) == -1:
182+
rtn = self.probe_down()
183+
if rtn != 1:
114184
ACTION.CALL_MDI("G90")
115-
return 0
116-
ACTION.CALL_MDI("G90")
185+
return '{} failed: {}'.format(name, rtn)
186+
cmdList = []
187+
cmdList.append("G10 L20 P0 Z{}".format(self.z_offset))
188+
cmdList.append("G1 Z{} F{}".format(self.retract_distance, self.search_vel))
189+
cmdList.append("G90")
190+
rtn = self.CALL_MDI_WAIT(cmdList, self.timeout)
191+
if rtn != 1:
192+
return '{} failed: {}'.format(name, rtn)
193+
117194
return 1
118195

119196
def probe_z(self):
197+
name = 'Probe Z 1st positioning'
120198
# rapid jog to first probe position
121-
if ACTION.CALL_MDI_WAIT("G0 Z{}".format(self.z_safe_travel), 30) == -1: return 0
122-
if ACTION.CALL_MDI_WAIT("G0 X{} Y{}".format(self.pos_x1, self.pos_y1), 30) == -1: return 0
123-
if ACTION.CALL_MDI_WAIT("G0 Z{}".format(self.pos_z1), 30) == -1: return 0
199+
cmdList = []
200+
cmdList.append( "G0 Z{}".format(self.z_safe_travel))
201+
cmdList.append( "G0 X{} Y{}".format(self.pos_x1, self.pos_y1))
202+
cmdList.append( "G0 Z{}".format(self.pos_z1))
203+
rtn = self.CALL_MDI_WAIT(cmdList, self.timeout)
204+
if rtn != 1:
205+
return '{} failed: {}'.format(name, rtn)
206+
124207
error = self.probe_down()
125-
print('probeDown:{}\n'.format(error))
126208
ACTION.CALL_MDI("G90")
127-
if error == 0: return 0
209+
if error != 0: return error
210+
128211
pos = STATUS.get_probed_position_with_offsets()
129212
self.status_z1 = float(pos[2])
213+
214+
name = 'Probe Z 2nd positioning'
130215
# rapid jog to second probe position
131-
if ACTION.CALL_MDI_WAIT("G0 Z{}".format(self.z_safe_travel), 30) == -1: return 0
132-
if ACTION.CALL_MDI_WAIT("G0 X{} Y{}".format(self.pos_x2, self.pos_y2), 30) == -1: return 0
133-
if ACTION.CALL_MDI_WAIT("G0 Z{}".format(self.pos_z2), 30) == -1: return 0
216+
cmdList=[]
217+
cmdList.append("G0 Z{}".format(self.z_safe_travel))
218+
cmdList.append("G0 X{} Y{}".format(self.pos_x2, self.pos_y2))
219+
cmdList.append("G0 Z{}".format(self.pos_z2))
220+
rtn = self.CALL_MDI_WAIT(cmdList, self.timeout)
221+
if rtn != 1:
222+
return '{} failed: {}'.format(name, rtn)
223+
134224
error = self.probe_down()
135225
ACTION.CALL_MDI("G90")
136-
if error == 0: return 0
226+
if error != 1: return error
227+
137228
pos = STATUS.get_probed_position_with_offsets()
138229
self.status_z2 = float(pos[2])
139-
if ACTION.CALL_MDI_WAIT("G0 Z{}".format(self.z_safe_travel), 10) == -1: return 0
230+
231+
s = "G0 Z{}".format(self.z_safe_travel)
232+
rtn = self.CALL_MDI_WAIT([s], self.timeout)
233+
if rtn != 1:
234+
return 'Probe {} failed: {}'.format(name, rtn)
235+
140236
return 1
141237

142238
def probe_down(self):
239+
name = '1st Probe down'
143240
ACTION.CALL_MDI("G91")
144241
cmd = "G38.2 Z-{} F{}".format(self.max_probe, self.search_vel)
145-
if ACTION.CALL_MDI_WAIT(cmd, 30) == -1: return 0
242+
rtn = self.CALL_MDI_WAIT([cmd], self.timeout)
243+
if rtn != 1:
244+
return '{} failed: {}'.format(name, rtn)
245+
246+
name = 'Probe retract'
146247
cmd = "G1 Z{} F{}".format(self.retract_distance, self.search_vel)
147-
if ACTION.CALL_MDI_WAIT(cmd, 30) == -1: return 0
248+
rtn = self.CALL_MDI_WAIT([cmd], self.timeout)
249+
if rtn != 1:
250+
return '{} failed: {}'.format(name, rtn)
251+
252+
name = '2nd Probe down'
148253
ACTION.CALL_MDI("G4 P0.5")
149254
cmd = "G38.2 Z-{} F{}".format(1.1 * self.retract_distance, self.probe_vel)
150-
if ACTION.CALL_MDI_WAIT(cmd, 30) == -1: return 0
255+
rtn = self.CALL_MDI_WAIT([cmd], self.timeout)
256+
if rtn != 1:
257+
return '{} failed: {}'.format(name, rtn)
258+
259+
# success
260+
return 1
151261

152262
def update_data(self, parms):
153263
for key in parms:

lib/python/qtvcp/qt_action.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,10 @@ def SET_TEMPARARY_MESSAGE(self, msg):
754754
STATUS.emit('error', STATUS.TEMPARARY_MESSAGE, msg)
755755

756756
def TOUCHPLATE_TOUCHOFF(self, search_vel, probe_vel, max_probe,
757-
z_offset, retract_distance, z_safe_travel):
757+
z_offset, retract_distance, z_safe_travel, rtn_method=None):
758+
# if not none will be called with returned data
759+
self._touchoff_return = rtn_method
760+
758761
if self.proc is not None:
759762
return 0
760763
self.proc = QProcess()
@@ -772,6 +775,7 @@ def TOUCHPLATE_TOUCHOFF(self, search_vel, probe_vel, max_probe,
772775
str(z_safe_travel),
773776
str(z_offset))
774777
#print(string_to_send)
778+
# block polling here, the sub program will poll now
775779
STATUS.block_error_polling()
776780
self.proc.writeData(bytes(string_to_send, 'utf-8'))
777781
return 1
@@ -973,28 +977,32 @@ def read_stderror(self):
973977
def parse_line(self, line):
974978
line = line.decode("utf-8")
975979
if "COMPLETE" in line:
976-
STATUS.unblock_error_polling()
977-
self.SET_DISPLAY_MESSAGE("Touchplate touchoff routine returned successfully")
978-
elif "DEBUG" in line: # must set DEBUG level on LOG in top of this file
979-
LOG.debug(line[line.find('DEBUG')+6:])
980+
# did we get a return method to send return data to?
981+
if self._touchoff_return is None:
982+
self.SET_DISPLAY_MESSAGE("Touchplate touchoff routine returned successfully")
983+
else:
984+
# strip ugly text
985+
s = line[line.find('COMPLETE ')+9:]
986+
self._touchoff_return(s)
987+
980988
# This also gets error text sent from logging of ACTION library in the subprogram
981989
elif "ERROR" in line:
982-
STATUS.unblock_error_polling()
983-
# remove preceding text
990+
# remove preceding text 'ERROR'
984991
s = line[line.find('ERROR')+6:]
985992
s = s[s.find(']')+1:]
986-
# remove (possible)trailing debug info
987-
d = s.find('(')
988-
if not d == -1:
989-
s = s[:d]
990993
self.SET_ERROR_MESSAGE(s)
994+
elif "DEBUG" in line: # must set DEBUG level on LOG in top of this file
995+
LOG.debug(line[line.find('DEBUG')+6:])
991996

992997
def touchoff_started(self):
993998
LOG.debug("Touchplate touchOff subprogram started with PID {}\n".format(self.proc.processId()))
994999

9951000
def touchoff_finished(self, exitCode, exitStatus):
9961001
LOG.debug("Touchplate touchoff Process finished - exitCode {} exitStatus {}".format(exitCode, exitStatus))
9971002
self.proc = None
1003+
STATUS.unblock_error_polling()
1004+
# clean up return method variable
1005+
self._touchoff_return = None
9981006

9991007
#------- boiler code
10001008

share/qtvcp/screens/qtdragon/qtdragon_handler.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,17 +1043,24 @@ def touchoff(self, selector):
10431043
else:
10441044
self.add_status("Unknown touchoff routine specified", CRITICAL)
10451045
return
1046-
self.add_status("Touchoff to {} started".format(selector))
1046+
10471047
max_probe = self.w.lineEdit_max_probe.text()
10481048
search_vel = self.w.lineEdit_search_vel.text()
10491049
probe_vel = self.w.lineEdit_probe_vel.text()
10501050
retract = self.w.lineEdit_retract_distance.text()
10511051
safe_z = self.w.lineEdit_z_safe_travel.text()
1052+
self.add_status("Touchoff to {} started with {} {} {} {} {} {}".format(selector,
1053+
search_vel, probe_vel, max_probe,
1054+
z_offset, retract, safe_z))
10521055
rtn = ACTION.TOUCHPLATE_TOUCHOFF(search_vel, probe_vel, max_probe,
1053-
z_offset, retract, safe_z)
1056+
z_offset, retract, safe_z, self.touchoff_return)
10541057
if rtn == 0:
10551058
self.add_status("Touchoff routine is already running", CRITICAL)
10561059

1060+
def touchoff_return(self, data):
1061+
self.add_status("Touchplate touchoff routine returned successfully")
1062+
self.add_status("Touchplate returned: "+data, CRITICAL)
1063+
10571064
def kb_jog(self, state, joint, direction, fast = False, linear = True):
10581065
ACTION.SET_MANUAL_MODE()
10591066
if not STATUS.is_man_mode() or not STATUS.machine_is_on():

share/qtvcp/screens/qtdragon_hd/qtdragon_hd_handler.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1118,10 +1118,14 @@ def touchoff(self, selector):
11181118
retract = self.w.lineEdit_retract_distance.text()
11191119
safe_z = self.w.lineEdit_z_safe_travel.text()
11201120
rtn = ACTION.TOUCHPLATE_TOUCHOFF(search_vel, probe_vel, max_probe,
1121-
z_offset, retract, safe_z)
1121+
z_offset, retract, safe_z, self.touchoff_return)
11221122
if rtn == 0:
11231123
self.add_status("Touchoff routine is already running", WARNING)
11241124

1125+
def touchoff_return(self, data):
1126+
self.add_status("Touchplate touchoff routine returned successfully")
1127+
self.add_status("Touchplate returned:"+data, CRITICAL)
1128+
11251129
def kb_jog(self, state, joint, direction, fast = False, linear = True):
11261130
ACTION.SET_MANUAL_MODE()
11271131
if not STATUS.is_man_mode() or not STATUS.machine_is_on():

0 commit comments

Comments
 (0)