Remap and code execution order
- andyandy26
- Offline
- New Member
Less
More
- Posts: 7
- Thank you received: 2
31 Mar 2024 10:19 #297269
by andyandy26
Remap and code execution order was created by andyandy26
Hey All,
I'm trying to setup my CNC mill, and am configuring the "tool changer". It does not have an ATC, majority of my tooling is collet-based, and I don't have bed room for a tool setter.
Thus, my process for doing this is:
1 - Whilst the code is running with the current tool, I manually prepare the next tool in another tool holder, and measure the tool offset with some equipment
2 - The program gets to a M10 Pxx command (my stand in for a tool change to tool xx).
3 - The program does some minor stuff and raises the Z height to 0mm.
4 - The program asks for the tool offset of tool xx, and sets the tool-table entry.
5 - The program asks me to put in the new tool, I do so and acknowledge.
6 - Program loads new tool offset and continues.
I have this working through an M10 remap to a Gcode subroutine, which calls some code provided a while ago by Andypugh.
I'm getting 2 strange behaviours;
1. The code doesn't execute in the proper order of my subroutine, the Z doesn't raise until after M22 (the offset dialog) occurs, and
2. The M22 (dialog), is occuring after the M6 tool change... again?
The number entered into this second dialog box doesn't end up getting used.
I've attached my INI, python files for the dialog, and my subroutine file. Can someone please help explain to me what I'm doing wrong?
I'm trying to setup my CNC mill, and am configuring the "tool changer". It does not have an ATC, majority of my tooling is collet-based, and I don't have bed room for a tool setter.
Thus, my process for doing this is:
1 - Whilst the code is running with the current tool, I manually prepare the next tool in another tool holder, and measure the tool offset with some equipment
2 - The program gets to a M10 Pxx command (my stand in for a tool change to tool xx).
3 - The program does some minor stuff and raises the Z height to 0mm.
4 - The program asks for the tool offset of tool xx, and sets the tool-table entry.
5 - The program asks me to put in the new tool, I do so and acknowledge.
6 - Program loads new tool offset and continues.
I have this working through an M10 remap to a Gcode subroutine, which calls some code provided a while ago by Andypugh.
I'm getting 2 strange behaviours;
1. The code doesn't execute in the proper order of my subroutine, the Z doesn't raise until after M22 (the offset dialog) occurs, and
2. The M22 (dialog), is occuring after the M6 tool change... again?
The number entered into this second dialog box doesn't end up getting used.
I've attached my INI, python files for the dialog, and my subroutine file. Can someone please help explain to me what I'm doing wrong?
Please Log in or Create an account to join the conversation.
- Aciera
- Offline
- Administrator
Less
More
- Posts: 3946
- Thank you received: 1705
31 Mar 2024 10:55 - 31 Mar 2024 10:56 #297270
by Aciera
Replied by Aciera on topic Remap and code execution order
Generally you need to be aware that the Gcode (and with it your remap code) will be ingested by the read ahead some time before the machine movement actually happens. The read ahead will interpret the line of code and add the requested commands to a queue to be executed. The read-ahead can be thousands of lines ahead of actual machine movment.
In your case your python code will be executed when the read ahead ingests the remapped Mcode so you'll probably need to look at stopping the read ahead in the right places to get the machine movement to sync with your remap code.
Your remaps end withthat means the read-ahead just simply executed the python code and ingests the next line in your gcode program.
The first thing you could try is to end your remaps with:Which will make sure the read-ahead waits for programm execution to catch up before ingesting and executing your next line of gcode.
Since you are not using any self.execute() commands you can also use 'yield INTERP_EXECUTE_FINISH' in other places of your remap code if necessary.
In your case your python code will be executed when the read ahead ingests the remapped Mcode so you'll probably need to look at stopping the read ahead in the right places to get the machine movement to sync with your remap code.
Your remaps end with
return INTERP_OK
The first thing you could try is to end your remaps with:
yield INTERP_EXECUTE_FINISH
return INTERP_OK
Since you are not using any self.execute() commands you can also use 'yield INTERP_EXECUTE_FINISH' in other places of your remap code if necessary.
Last edit: 31 Mar 2024 10:56 by Aciera.
Please Log in or Create an account to join the conversation.
- andyandy26
- Offline
- New Member
Less
More
- Posts: 7
- Thank you received: 2
31 Mar 2024 12:26 #297275
by andyandy26
Replied by andyandy26 on topic Remap and code execution order
Thank you sir!
I'll give it a try tomorrow and see how I go!
I'm still learning the backend of linuxcnc, quite complex under the hood!
I'll give it a try tomorrow and see how I go!
I'm still learning the backend of linuxcnc, quite complex under the hood!
Please Log in or Create an account to join the conversation.
- andyandy26
- Offline
- New Member
Less
More
- Posts: 7
- Thank you received: 2
01 Apr 2024 00:51 - 01 Apr 2024 00:55 #297317
by andyandy26
Replied by andyandy26 on topic Remap and code execution order
This post got mangled by the edit, so I reposted below.
Last edit: 01 Apr 2024 00:55 by andyandy26.
Please Log in or Create an account to join the conversation.
- andyandy26
- Offline
- New Member
Less
More
- Posts: 7
- Thank you received: 2
01 Apr 2024 00:56 #297319
by andyandy26
Replied by andyandy26 on topic Remap and code execution order
So I've tried implementing the following at the end of my M22 command (that displays the dialog box)
However this gives the following error on opening a program file.
The code still will execute when run, however the issues persists where the G0 rapid move occurs after the M22, and the M22 occurs a second time after the M6.
After some reading of other posts: forum.linuxcnc.org/20-g-code/44150-how-t...-when-remapping-code
Indicates that I shouldn't be returning an argument after the yield (no INTERP_OK)?
Does the documentation have any examples of this usage I can read into?
yield INTERP_EXECUTE_FINISH
return INTERP_OK
However this gives the following error on opening a program file.
G-Code error in setup1.ngc
Near line 0 of
/home/cnc/linuxcnc/nc_files/setup1.ngc
pycall: remap.m22:
StopIteration: INTERP_OK
The code still will execute when run, however the issues persists where the G0 rapid move occurs after the M22, and the M22 occurs a second time after the M6.
After some reading of other posts: forum.linuxcnc.org/20-g-code/44150-how-t...-when-remapping-code
Indicates that I shouldn't be returning an argument after the yield (no INTERP_OK)?
Does the documentation have any examples of this usage I can read into?
Please Log in or Create an account to join the conversation.
- Aciera
- Offline
- Administrator
Less
More
- Posts: 3946
- Thank you received: 1705
01 Apr 2024 08:54 #297349
by Aciera
Replied by Aciera on topic Remap and code execution order
There seems to be a fair bit of confusion about how to actually do it properly and I'm afraid I cannot give you any definite answers but I have recently completed a fairly substantial remap which seems to be working fine.
Here is part of it that may give you some ideas:
def g53x_core(self):
global saved_work_offset, twp_matrix, twp_flag, pre_rot
global joint_letter_primary, joint_letter_secondary, twp_error_status
global orient_mode
if self.task == 0: # ignore the preview interpreter
yield INTERP_EXECUTE_FINISH
return INTERP_OK
if not hal.get_value(twp_is_defined):
# reset the twp parameters
reset_twp_params(self)
msg = "G53.x: No TWP defined."
log.debug(msg)
emccanon.CANON_ERROR(msg)
yield INTERP_EXECUTE_FINISH # w/o this the error message is not displayed
yield INTERP_EXIT # w/o this the error does not abort a running gcode program
return INTERP_ERROR
elif hal.get_value(twp_is_active):
# reset the twp parameters
reset_twp_params(self)
msg = "G53.x: TWP already active"
log.debug(msg)
emccanon.CANON_ERROR(msg)
yield INTERP_EXECUTE_FINISH # w/o this the error message is not displayed
yield INTERP_EXIT # w/o this the error does not abort a running gcode program
return INTERP_ERROR
# Check if any words have been passed with the respective G53.x command
c = self.blocks[self.remap_level]
p = c.p_number if c.p_flag else 0
x = c.i_number if c.i_flag else None
y = c.j_number if c.j_flag else None
z = c.k_number if c.k_flag else None
log.debug('G53.x Words passed: (P, X,Y,Z): %s', (p,x,y,z))
if p not in [0,1,2]:
# reset the twp parameters
reset_twp_params(self)
msg = "G53.x : unrecognised P-Word found."
log.debug(msg)
emccanon.CANON_ERROR(msg)
yield INTERP_EXECUTE_FINISH # w/o this the error message is not displayed
yield INTERP_EXIT # w/o this the error does not abort a running gcode program
return INTERP_ERROR
orient_mode = p
# calculate the required rotary joint positions and pre_rotation for the requested tool-orientation
try:
tool_z_requested = [twp_matrix[0,2],twp_matrix[1,2],twp_matrix[2,2]]
# calculate all possible pairs of (primary, secondary) angles so our tool-z vector matches the requested tool-z
# angles are returned in [-pi,pi]
possible_prim_sec_angle_pairs = kins_calc_jnt_angles(self, tool_z_requested)
# An excepton will occur if the requested tool orientation cannot be achieved wtih the kinematic at hand
except Exception as error:
log.error('G53.x: Calculation failed, %s', error)
possible_prim_sec_angle_pairs = []
if not possible_prim_sec_angle_pairs:
# reset the twp parameters
reset_twp_params(self)
msg = "G53.x ERROR: Requested tool orientation not reachable -> aborting G53.x"
log.debug(msg)
emccanon.CANON_ERROR(msg)
yield INTERP_EXECUTE_FINISH # w/o this the error message is not displayed
yield INTERP_EXIT # w/o this the error does not abort a running gcode program
return INTERP_ERROR
# this returns one pair of optimized angles in degrees, or (None, None) if no solution could be found
theta_1, theta_2 = calc_optimal_joint_move(self, possible_prim_sec_angle_pairs)
if theta_1 == None:
# reset the twp parameters
reset_twp_params(self)
msg = ("G53.x ERROR: Requested tool orientation not reachable -> aborting G53.x")
log.debug(msg)
emccanon.CANON_ERROR(msg)
yield INTERP_EXECUTE_FINISH # w/o this the error message is not displayed
yield INTERP_EXIT # w/o this the error does not abort a running gcode program
return INTERP_ERROR
theta_1 = radians(theta_1)
theta_2 = radians(theta_2)
# calculate the pre-rotation needed so our tool-x vector matches the requested tool-x vector
tool_x_requested = [twp_matrix[0,0],twp_matrix[1,0],twp_matrix[2,0]]
pre_rot = kins_calc_pre_rot(self,theta_1, theta_2, tool_x_requested, tool_z_requested)
log.debug("Calculated pre-rotation (pre_rot) to match requested tool-x): %s", pre_rot)
# mark twp-flag as active
twp_flag = [0, 'active']
gui_update_twp(self)
# set the pre-rotation value in the kinematic component
log.debug("G53.x: setting primary, secondary and pre_rotation angles in kinematic component: %s", (degrees(theta_1), degrees(theta_2), degrees(pre_rot)))
hal.set_p(kins_pre_rotation, str(pre_rot))
hal.set_p(kins_primary_rotation, str(degrees(theta_1)))
hal.set_p(kins_secondary_rotation, str(degrees(theta_2)))
# calculate the work offset in tool-coords
P = matrix_to_point(kins_calc_tool_transformation(self, point_to_matrix(saved_work_offset), theta_1, theta_2, pre_rot))
# get the current twp_origin
twp_offset = (twp_matrix[0,3],twp_matrix[1,3],twp_matrix[2,3])
# calculate the twp offset in tool-coords
Q = matrix_to_point(kins_calc_tool_transformation(self, point_to_matrix(twp_offset), theta_1, theta_2, pre_rot))
log.debug("G53.x: Setting transformed work-offsets for tool-kins in G59, G59.1, G59.2 and G59.3 to: %s ", P)
# set the dedicated TWP work offset values (G53, G53.1, G53.2, G53.3)
self.execute("G10 L2 P6 X%f Y%f Z%f " % (P[0]+Q[0], P[1]+Q[1], P[2]+Q[2]), lineno())
self.execute("G10 L2 P7 X%f Y%f Z%f " % (P[0]+Q[0], P[1]+Q[1], P[2]+Q[2]), lineno())
self.execute("G10 L2 P8 X%f Y%f Z%f " % (P[0]+Q[0], P[1]+Q[1], P[2]+Q[2]), lineno())
self.execute("G10 L2 P9 X%f Y%f Z%f " % (P[0]+Q[0], P[1]+Q[1], P[2]+Q[2]), lineno())
log.debug("G53.x: Moving (secondary and primary) joints to: %s", (degrees(theta_2), degrees(theta_1)))
if (x,y,z) == (None,None,None):
# Move rotary joints to align the tool with the requested twp
self.execute("G0 %s%f %s%f" % (joint_letter_secondary, degrees(theta_2), joint_letter_primary, degrees(theta_1)), lineno())
# switch to the dedicated TWP work offsets
self.execute("G59", lineno())
# activate TOOL kinematics
self.execute("M68 E3 Q2")
if (x,y,z) != (None,None,None):
log.debug('G53.3 called')
self.execute("G0 X%s Y%s Z%s %s%f %s%f" % (x, y, z, joint_letter_secondary, degrees(theta_2), joint_letter_primary, degrees(theta_1)), lineno())
# set twp-state to 'active' (2)
self.execute("M68 E2 Q2")
yield INTERP_EXECUTE_FINISH
return INTERP_OK
Here is part of it that may give you some ideas:
Warning: Spoiler!
def g53x_core(self):
global saved_work_offset, twp_matrix, twp_flag, pre_rot
global joint_letter_primary, joint_letter_secondary, twp_error_status
global orient_mode
if self.task == 0: # ignore the preview interpreter
yield INTERP_EXECUTE_FINISH
return INTERP_OK
if not hal.get_value(twp_is_defined):
# reset the twp parameters
reset_twp_params(self)
msg = "G53.x: No TWP defined."
log.debug(msg)
emccanon.CANON_ERROR(msg)
yield INTERP_EXECUTE_FINISH # w/o this the error message is not displayed
yield INTERP_EXIT # w/o this the error does not abort a running gcode program
return INTERP_ERROR
elif hal.get_value(twp_is_active):
# reset the twp parameters
reset_twp_params(self)
msg = "G53.x: TWP already active"
log.debug(msg)
emccanon.CANON_ERROR(msg)
yield INTERP_EXECUTE_FINISH # w/o this the error message is not displayed
yield INTERP_EXIT # w/o this the error does not abort a running gcode program
return INTERP_ERROR
# Check if any words have been passed with the respective G53.x command
c = self.blocks[self.remap_level]
p = c.p_number if c.p_flag else 0
x = c.i_number if c.i_flag else None
y = c.j_number if c.j_flag else None
z = c.k_number if c.k_flag else None
log.debug('G53.x Words passed: (P, X,Y,Z): %s', (p,x,y,z))
if p not in [0,1,2]:
# reset the twp parameters
reset_twp_params(self)
msg = "G53.x : unrecognised P-Word found."
log.debug(msg)
emccanon.CANON_ERROR(msg)
yield INTERP_EXECUTE_FINISH # w/o this the error message is not displayed
yield INTERP_EXIT # w/o this the error does not abort a running gcode program
return INTERP_ERROR
orient_mode = p
# calculate the required rotary joint positions and pre_rotation for the requested tool-orientation
try:
tool_z_requested = [twp_matrix[0,2],twp_matrix[1,2],twp_matrix[2,2]]
# calculate all possible pairs of (primary, secondary) angles so our tool-z vector matches the requested tool-z
# angles are returned in [-pi,pi]
possible_prim_sec_angle_pairs = kins_calc_jnt_angles(self, tool_z_requested)
# An excepton will occur if the requested tool orientation cannot be achieved wtih the kinematic at hand
except Exception as error:
log.error('G53.x: Calculation failed, %s', error)
possible_prim_sec_angle_pairs = []
if not possible_prim_sec_angle_pairs:
# reset the twp parameters
reset_twp_params(self)
msg = "G53.x ERROR: Requested tool orientation not reachable -> aborting G53.x"
log.debug(msg)
emccanon.CANON_ERROR(msg)
yield INTERP_EXECUTE_FINISH # w/o this the error message is not displayed
yield INTERP_EXIT # w/o this the error does not abort a running gcode program
return INTERP_ERROR
# this returns one pair of optimized angles in degrees, or (None, None) if no solution could be found
theta_1, theta_2 = calc_optimal_joint_move(self, possible_prim_sec_angle_pairs)
if theta_1 == None:
# reset the twp parameters
reset_twp_params(self)
msg = ("G53.x ERROR: Requested tool orientation not reachable -> aborting G53.x")
log.debug(msg)
emccanon.CANON_ERROR(msg)
yield INTERP_EXECUTE_FINISH # w/o this the error message is not displayed
yield INTERP_EXIT # w/o this the error does not abort a running gcode program
return INTERP_ERROR
theta_1 = radians(theta_1)
theta_2 = radians(theta_2)
# calculate the pre-rotation needed so our tool-x vector matches the requested tool-x vector
tool_x_requested = [twp_matrix[0,0],twp_matrix[1,0],twp_matrix[2,0]]
pre_rot = kins_calc_pre_rot(self,theta_1, theta_2, tool_x_requested, tool_z_requested)
log.debug("Calculated pre-rotation (pre_rot) to match requested tool-x): %s", pre_rot)
# mark twp-flag as active
twp_flag = [0, 'active']
gui_update_twp(self)
# set the pre-rotation value in the kinematic component
log.debug("G53.x: setting primary, secondary and pre_rotation angles in kinematic component: %s", (degrees(theta_1), degrees(theta_2), degrees(pre_rot)))
hal.set_p(kins_pre_rotation, str(pre_rot))
hal.set_p(kins_primary_rotation, str(degrees(theta_1)))
hal.set_p(kins_secondary_rotation, str(degrees(theta_2)))
# calculate the work offset in tool-coords
P = matrix_to_point(kins_calc_tool_transformation(self, point_to_matrix(saved_work_offset), theta_1, theta_2, pre_rot))
# get the current twp_origin
twp_offset = (twp_matrix[0,3],twp_matrix[1,3],twp_matrix[2,3])
# calculate the twp offset in tool-coords
Q = matrix_to_point(kins_calc_tool_transformation(self, point_to_matrix(twp_offset), theta_1, theta_2, pre_rot))
log.debug("G53.x: Setting transformed work-offsets for tool-kins in G59, G59.1, G59.2 and G59.3 to: %s ", P)
# set the dedicated TWP work offset values (G53, G53.1, G53.2, G53.3)
self.execute("G10 L2 P6 X%f Y%f Z%f " % (P[0]+Q[0], P[1]+Q[1], P[2]+Q[2]), lineno())
self.execute("G10 L2 P7 X%f Y%f Z%f " % (P[0]+Q[0], P[1]+Q[1], P[2]+Q[2]), lineno())
self.execute("G10 L2 P8 X%f Y%f Z%f " % (P[0]+Q[0], P[1]+Q[1], P[2]+Q[2]), lineno())
self.execute("G10 L2 P9 X%f Y%f Z%f " % (P[0]+Q[0], P[1]+Q[1], P[2]+Q[2]), lineno())
log.debug("G53.x: Moving (secondary and primary) joints to: %s", (degrees(theta_2), degrees(theta_1)))
if (x,y,z) == (None,None,None):
# Move rotary joints to align the tool with the requested twp
self.execute("G0 %s%f %s%f" % (joint_letter_secondary, degrees(theta_2), joint_letter_primary, degrees(theta_1)), lineno())
# switch to the dedicated TWP work offsets
self.execute("G59", lineno())
# activate TOOL kinematics
self.execute("M68 E3 Q2")
if (x,y,z) != (None,None,None):
log.debug('G53.3 called')
self.execute("G0 X%s Y%s Z%s %s%f %s%f" % (x, y, z, joint_letter_secondary, degrees(theta_2), joint_letter_primary, degrees(theta_1)), lineno())
# set twp-state to 'active' (2)
self.execute("M68 E2 Q2")
yield INTERP_EXECUTE_FINISH
return INTERP_OK
Please Log in or Create an account to join the conversation.
- Aciera
- Offline
- Administrator
Less
More
- Posts: 3946
- Thank you received: 1705
01 Apr 2024 09:02 #297350
by Aciera
Replied by Aciera on topic Remap and code execution order
What we certainly cannot do after 'yield INTERP_EXECUTE_FINISH' is use 'self.execute(...)' but I'm not having any problem using 'return INTERP_OK'.
Please Log in or Create an account to join the conversation.
- andyandy26
- Offline
- New Member
Less
More
- Posts: 7
- Thank you received: 2
04 Apr 2024 02:17 #297555
by andyandy26
Replied by andyandy26 on topic Remap and code execution order
Hi again, sorry for the delay in replying, got the flu and it's been kicking my butt...
Thank you Aciera, with your help I did work out a solution to this, mostly using trial and error, using the following python code did the trick:
With remap:
The error I was getting was seemingly because if you had a yield in one spot (the end), you need to have them before EVERY return. I could be wrong though.
I had to separate the prelog routine to first raise the head, as if I did this in the same python function, it would get thrown into the queue, and wouldn't action until after the Tk dialog box got destroyed.
Also I found that if I separate the Tx M6 G43 into two separate self.execute lines, then the G43 seems to occur before the preceeding G10?
I think the interpreter has a funny way of queueing the code, I'm sure that over time I'll get better versed with it.
For now it's working though, so I'm happy!
So when I order an M10 Pxx (my remap);
- my head raises to max height,
- asks for the new tool offset (tool Pxx), this gets loaded into the tool table,
- then asks me to place the tool into the spindle (through QTDragon's manual tool change).
- The current tool is then set with the tool offset and the program continues
Thanks again for your help!
Thank you Aciera, with your help I did work out a solution to this, mostly using trial and error, using the following python code did the trick:
#!/usr/bin/python
import sys
from interpreter import *
from tkinter import *
#from util import lineno
toolOffset = 0.0
nextTool = 0.0
def setToolOffset(self, dialog, entry):
global toolOffset
val = entry.get()
if val.isdigit:
toolOffset = float(val)
dialog.update()
dialog.destroy()
def m10_prolog(self, **words):
if not self.task:
yield INTERP_EXECUTE_FINISH
return INTERP_OK
dir(self)
sys.argv = [""]
#Stop spindle and raise head
self.execute("M5")
self.execute("G53 G0 Z0")
print("prelog")
yield INTERP_EXECUTE_FINISH
return INTERP_OK
#M10 Pxx where xx is tool number
def m10(self, **words):
if not self.task:
yield INTERP_EXECUTE_FINISH
return INTERP_OK
dir(self)
sys.argv = [""]
if not hasattr(sys, 'argv'):
sys.argv = [""]
global nextTool, toolOffset
# inspect controlling block for relevant words, get tool number
c = self.blocks[self.remap_level]
nextTool = float(c.p_number) if c.p_flag else 0
#Request new tool offset
dialog = Tk()
dialog.title("Enter tool offset...")
Label(dialog, text="OFFSET").grid(row=1)
entry = Entry(dialog)
entry.grid(column=1, row=1)
try:
entry.insert(0, self.params[_OFFSET])
except:
pass
b1 = Button(dialog, text='OK', command=lambda: setToolOffset(self, dialog, entry))
b1.grid(row=2, column=0, sticky=W, pady=4)
b1.focus()
b1.bind('<Return>', lambda x: setToolOffset(self, dialog, entry))
#Block the code from continueing whilst listening for events on button
mainloop()
self.execute("G10 L1 P%f Z%f" % (nextTool, toolOffset)) #Set tool offset in tool table
self.execute("T%f M6 G43" % (nextTool)) #Request tool change to new tool and set Z offset from tool table
yield INTERP_EXECUTE_FINISH
return INTERP_OK
With remap:
REMAP = M10 modalgroup=10 argspec=P python=m10 prolog=m10_prolog
The error I was getting was seemingly because if you had a yield in one spot (the end), you need to have them before EVERY return. I could be wrong though.
I had to separate the prelog routine to first raise the head, as if I did this in the same python function, it would get thrown into the queue, and wouldn't action until after the Tk dialog box got destroyed.
Also I found that if I separate the Tx M6 G43 into two separate self.execute lines, then the G43 seems to occur before the preceeding G10?
I think the interpreter has a funny way of queueing the code, I'm sure that over time I'll get better versed with it.
For now it's working though, so I'm happy!
So when I order an M10 Pxx (my remap);
- my head raises to max height,
- asks for the new tool offset (tool Pxx), this gets loaded into the tool table,
- then asks me to place the tool into the spindle (through QTDragon's manual tool change).
- The current tool is then set with the tool offset and the program continues
Thanks again for your help!
Please Log in or Create an account to join the conversation.
Time to create page: 0.104 seconds