- fixed an KeyError exception in the GCode Editor regarding the key: 'offset' not existing

- updated the Tcl command `drillcncjob` to store the tool gcode and the parsed tool gcode in each tool data structure
- updated the Tcl command `drillcncjob` description and examples
This commit is contained in:
Marius Stanciu
2022-01-28 15:54:41 +02:00
committed by Marius
parent c470023719
commit eb6ea7e623
5 changed files with 100 additions and 52 deletions

View File

@@ -13,6 +13,9 @@ CHANGELOG for FlatCAM beta
- in camlib.CNCjob.gcode_parse() fixed an assumption that a certain object is present: tool_data["tools_drill_toolchange"]
- fixed an error in Drilling Plugin when selecting only a few tools and not all for drilling
- fixed an error when building the UI for a CNCJob object created from drilling an Excellon object with a limited selection of tools
- fixed an KeyError exception in the GCode Editor regarding the key: 'offset' not existing
- updated the Tcl command `drillcncjob` to store the tool gcode and the parsed tool gcode in each tool data structure
- updated the Tcl command `drillcncjob` description and examples
27.01.2022

View File

@@ -288,8 +288,13 @@ class AppGCodeEditor(QtCore.QObject):
dia_item = QtWidgets.QTableWidgetItem('%.*f' % (self.decimals, float(tooldia)))
nr_drills_item = QtWidgets.QTableWidgetItem('%d' % int(t_value['nr_drills']))
nr_slots_item = QtWidgets.QTableWidgetItem('%d' % int(t_value['nr_slots']))
cutz_item = QtWidgets.QTableWidgetItem('%.*f' % (
self.decimals, float(t_value['offset']) + float(t_value['data']['tools_drill_cutz'])))
try:
cutz_item = QtWidgets.QTableWidgetItem('%.*f' % (
self.decimals, float(t_value['offset']) + float(t_value['data']['tools_drill_cutz'])))
except KeyError:
cutz_item = QtWidgets.QTableWidgetItem('%.*f' % (
self.decimals, float(t_value['offset_z']) + float(t_value['data']['tools_drill_cutz'])))
t_id.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled)
dia_item.setFlags(QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled)

View File

@@ -1836,11 +1836,14 @@ class ToolDrilling(AppTool, Excellon):
selected_uid.add(uid)
return list(selected_uid)
def create_drill_points(self, selected_tools, selected_sorted_tools):
def create_drill_points(self, selected_tools, selected_sorted_tools, excellon_tools=None):
points = {}
if excellon_tools is None:
excellon_tools = self.excellon_tools
# create drill points out of the drills locations
for tool_key, tl_dict in self.excellon_tools.items():
for tool_key, tl_dict in excellon_tools.items():
if tool_key in selected_tools:
if 'drills' in tl_dict and tl_dict['drills']:
for drill_pt in tl_dict['drills']:
@@ -1858,7 +1861,7 @@ class ToolDrilling(AppTool, Excellon):
# convert slots to a sequence of drills and add them to drill points
should_add_last_pt = self.ui.last_drill_cb.get_value()
for tool_key, tl_dict in self.excellon_tools.items():
for tool_key, tl_dict in excellon_tools.items():
convert_slots = tl_dict['data']['tools_drill_drill_slots']
if convert_slots:
if tool_key in selected_tools:
@@ -1884,11 +1887,13 @@ class ToolDrilling(AppTool, Excellon):
return points
def check_intersection(self, points):
def check_intersection(self, points, excellon_tools=None):
if excellon_tools is None:
excellon_tools = self.excellon_tools
for tool_key in points:
for pt in points[tool_key]:
for area in self.app.exc_areas.exclusion_areas_storage:
pt_buf = pt.buffer(self.excellon_tools[tool_key]['tooldia'] / 2.0)
pt_buf = pt.buffer(excellon_tools[tool_key]['tooldia'] / 2.0)
if pt_buf.within(area['shape']) or pt_buf.intersects(area['shape']):
return True
return False
@@ -1968,7 +1973,7 @@ class ToolDrilling(AppTool, Excellon):
# Create a sorted list of selected sel_tools from the sorted_tools list
sel_tools = [i for i, j in sorted_tools for k in selected_tools_ids if i == k]
log.debug("Tools sorted are: %s" % str(sel_tools))
self.app.log.debug("Tools sorted are: %s" % str(sel_tools))
# #############################################################################################################
# #############################################################################################################
@@ -1982,7 +1987,7 @@ class ToolDrilling(AppTool, Excellon):
# check if there are drill points in the exclusion areas (if any areas)
if self.app.exc_areas.exclusion_areas_storage and self.check_intersection(points) is True:
self.app.inform.emit("[ERROR_NOTCL] %s" % _("Failed. Drill points inside the exclusion zones."))
self.app.inform.emit("[ERROR_NOTCL] %s %s" % (_("Failed."), _("Drill points inside the exclusion zones.")))
return 'fail'
# #############################################################################################################

View File

@@ -3013,7 +3013,7 @@ class CNCjob(Geometry):
if assignment:
# Solution cost.
log.info("OR-tools metaheuristics - Total distance: " + str(assignment.ObjectiveValue()))
self.app.log.info("OR-tools metaheuristics - Total distance: " + str(assignment.ObjectiveValue()))
# Inspect solution.
# Only one route here; otherwise iterate from 0 to routing.vehicles() - 1.
@@ -3064,6 +3064,10 @@ class CNCjob(Geometry):
transit_callback_index = routing.RegisterTransitCallback(dist_callback)
routing.SetArcCostEvaluatorOfAllVehicles(transit_callback_index)
# Setting first solution heuristic.
search_parameters.first_solution_strategy = (
routing_enums_pb2.FirstSolutionStrategy.PATH_CHEAPEST_ARC)
# Solve, returns a solution if any.
assignment = routing.SolveWithParameters(search_parameters)
@@ -3227,10 +3231,11 @@ class CNCjob(Geometry):
else:
return zcut
# used in Tool Drilling
def excellon_tool_gcode_gen(self, tool, points, tools, first_pt, is_first=False, is_last=False, opt_type='T',
toolchange=False):
"""
Used in Tool Drilling
Creates Gcode for this object from an Excellon object
for the specified tools.
@@ -3344,7 +3349,7 @@ class CNCjob(Geometry):
self.app.inform.emit('[ERROR] %s' % _("The Toolchange X,Y format has to be (x, y)."))
return 'fail'
except Exception as e:
log.error("camlib.CNCJob.generate_from_excellon_by_tool() xy_toolchange --> %s" % str(e))
self.app.log.error("camlib.CNCJob.generate_from_excellon_by_tool() xy_toolchange --> %s" % str(e))
self.xy_toolchange = [0, 0]
# End position parameters
@@ -3421,7 +3426,7 @@ class CNCjob(Geometry):
# Only if there are locations to drill
if not optimized_path:
log.debug("CNCJob.excellon_tool_gcode_gen() -> Optimized path is empty.")
self.app.log.error("CNCJob.excellon_tool_gcode_gen() -> Optimized path is empty.")
return 'fail'
if self.app.abort_flag:
@@ -4014,9 +4019,12 @@ class CNCjob(Geometry):
self.gcode = t_gcode
return self.gcode, start_gcode
# used by the Tcl command Drillcncjob
def generate_from_excellon_by_tool(self, exobj, tools="all", order='fwd', is_first=False, use_ui=False):
"""
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Used by the Tcl command Drillcncjob
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Creates Gcode for this object from an Excellon object
for the specified tools.
@@ -4068,7 +4076,7 @@ class CNCjob(Geometry):
"in the format (x, y) \nbut now there is only one value, not two. "))
return 'fail'
except Exception as e:
log.error("camlib.CNCJob.generate_from_excellon_by_tool() --> %s" % str(e))
self.app.log.error("camlib.CNCJob.generate_from_excellon_by_tool() --> %s" % str(e))
pass
# XY_end parameter
@@ -4084,7 +4092,7 @@ class CNCjob(Geometry):
self.pp_excellon = self.app.preprocessors[self.pp_excellon_name]
p = self.pp_excellon
log.debug("Creating CNC Job from Excellon...")
self.app.log.debug("Creating CNC Job from Excellon...")
# #############################################################################################################
# #############################################################################################################
@@ -4112,7 +4120,7 @@ class CNCjob(Geometry):
# Create a sorted list of selected tools from the sorted_tools list
tools = [i for i, j in sorted_tools for k in selected_tools if i == k]
log.debug("Tools sorted are: %s" % str(tools))
self.app.log.debug("Tools sorted are: %s" % str(tools))
# #############################################################################################################
# #############################################################################################################
@@ -4153,13 +4161,17 @@ class CNCjob(Geometry):
default_data[k] = deepcopy(v)
# it[1] is the tool diameter
self.tools[to_ol] = {}
self.tools[to_ol]['tooldia'] = it[1]
self.tools[to_ol]['nr_drills'] = drill_no
self.tools[to_ol]['nr_slots'] = slot_no
self.tools[to_ol]['offset_z'] = z_off
self.tools[to_ol]['data'] = default_data
self.tools[to_ol]['solid_geometry'] = deepcopy(sol_geo)
self.tools[to_ol] = {
'tooldia': it[1],
'nr_drills': drill_no,
'nr_slots': slot_no,
'offset_z': z_off,
'data': default_data,
'gcode': '',
'gcode_parsed': [],
'last_point': (0, 0),
'solid_geometry': deepcopy(sol_geo)
}
self.app.inform.emit(_("Creating a list of points to drill..."))
@@ -4181,7 +4193,7 @@ class CNCjob(Geometry):
points[tool].append(drill_pt)
except KeyError:
points[tool] = [drill_pt]
log.debug("Found %d TOOLS with drills." % len(points))
self.app.log.debug("Found %d TOOLS with drills." % len(points))
# check if there are drill points in the exclusion areas.
# If we find any within the exclusion areas return 'fail'
@@ -4271,6 +4283,8 @@ class CNCjob(Geometry):
if self.toolchange is True:
for tool in tools:
tool_gcode = ''
# check if it has drills
if not self.exc_tools[tool]['drills']:
continue
@@ -4287,7 +4301,7 @@ class CNCjob(Geometry):
self.z_feedrate = self.exc_tools[tool]['data']['tools_drill_feedrate_z']
self.feedrate = self.exc_tools[tool]['data']['tools_drill_feedrate_z']
self.z_cut = self.exc_tools[tool]['data']['tools_drill_cutz']
gcode += self.doformat(p.z_feedrate_code)
tool_gcode += self.doformat(p.z_feedrate_code)
if self.z_cut > 0:
self.app.inform.emit('[WARNING] %s' %
@@ -4365,12 +4379,12 @@ class CNCjob(Geometry):
# Tool change sequence (optional)
if self.toolchange:
gcode += self.doformat(p.toolchange_code, toolchangexy=(self.oldx, self.oldy))
tool_gcode += self.doformat(p.toolchange_code, toolchangexy=(self.oldx, self.oldy))
# Spindle start
gcode += self.doformat(p.spindle_code)
tool_gcode += self.doformat(p.spindle_code)
# Dwell time
if self.dwell is True:
gcode += self.doformat(p.dwell_code)
tool_gcode += self.doformat(p.dwell_code)
current_tooldia = float('%.*f' % (self.decimals, float(self.exc_tools[tool]["tooldia"])))
@@ -4397,7 +4411,7 @@ class CNCjob(Geometry):
geo_len = len(optimized_path)
old_disp_number = 0
log.warning("Number of drills for which to generate GCode: %s" % str(geo_len))
self.app.log.warning("Number of drills for which to generate GCode: %s" % str(geo_len))
loc_nr = 0
for point in optimized_path:
@@ -4422,26 +4436,26 @@ class CNCjob(Geometry):
if travel[0] is not None:
# move to next point
gcode += self.doformat(p.rapid_code, x=locx, y=locy)
tool_gcode += self.doformat(p.rapid_code, x=locx, y=locy)
# raise to safe Z (travel[0]) each time because safe Z may be different
self.z_move = travel[0]
gcode += self.doformat(p.lift_code, x=locx, y=locy)
tool_gcode += self.doformat(p.lift_code, x=locx, y=locy)
# restore z_move
self.z_move = self.exc_tools[tool]['data']['tools_drill_travelz']
else:
if prev_z is not None:
# move to next point
gcode += self.doformat(p.rapid_code, x=locx, y=locy)
tool_gcode += self.doformat(p.rapid_code, x=locx, y=locy)
# we assume that previously the z_move was altered therefore raise to
# the travel_z (z_move)
self.z_move = self.exc_tools[tool]['data']['tools_drill_travelz']
gcode += self.doformat(p.lift_code, x=locx, y=locy)
tool_gcode += self.doformat(p.lift_code, x=locx, y=locy)
else:
# move to next point
gcode += self.doformat(p.rapid_code, x=locx, y=locy)
tool_gcode += self.doformat(p.rapid_code, x=locx, y=locy)
# store prev_z
prev_z = travel[0]
@@ -4457,32 +4471,32 @@ class CNCjob(Geometry):
self.z_cut -= self.z_depthpercut
if abs(doc) < abs(self.z_cut) < (abs(doc) + self.z_depthpercut):
self.z_cut = doc
gcode += self.doformat(p.down_code, x=locx, y=locy)
tool_gcode += self.doformat(p.down_code, x=locx, y=locy)
measured_down_distance += abs(self.z_cut) + abs(self.z_move)
if self.f_retract is False:
gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
tool_gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
measured_up_to_zero_distance += abs(self.z_cut)
measured_lift_distance += abs(self.z_move)
else:
measured_lift_distance += abs(self.z_cut) + abs(self.z_move)
gcode += self.doformat(p.lift_code, x=locx, y=locy)
tool_gcode += self.doformat(p.lift_code, x=locx, y=locy)
else:
gcode += self.doformat(p.down_code, x=locx, y=locy)
tool_gcode += self.doformat(p.down_code, x=locx, y=locy)
measured_down_distance += abs(self.z_cut) + abs(self.z_move)
if self.f_retract is False:
gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
tool_gcode += self.doformat(p.up_to_zero_code, x=locx, y=locy)
measured_up_to_zero_distance += abs(self.z_cut)
measured_lift_distance += abs(self.z_move)
else:
measured_lift_distance += abs(self.z_cut) + abs(self.z_move)
gcode += self.doformat(p.lift_code, x=locx, y=locy)
tool_gcode += self.doformat(p.lift_code, x=locx, y=locy)
measured_distance += abs(distance_euclidian(locx, locy, self.oldx, self.oldy))
self.oldx = locx
@@ -4495,6 +4509,9 @@ class CNCjob(Geometry):
self.app.proc_container.update_view_text(' %d%%' % disp_number)
old_disp_number = disp_number
self.tools[tool]['last_point'] = (locx, locy)
self.tools[tool]['gcode'] = tool_gcode
gcode += tool_gcode
else:
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('G91 coordinates not implemented'))
return 'fail'
@@ -4731,6 +4748,7 @@ class CNCjob(Geometry):
self.app.inform.emit('[ERROR_NOTCL] %s...' % _('G91 coordinates not implemented'))
return 'fail'
self.z_cut = deepcopy(old_zcut)
self.tools[one_tool]['gcode'] = gcode
if used_excellon_optimization_type == 'M':
log.debug("The total travel distance with OR-TOOLS Metaheuristics is: %s" % str(measured_distance))

View File

@@ -59,7 +59,7 @@ class TclCommandDrillcncjob(TclCommandSignaled):
('name', 'Name of the source object.'),
('drilled_dias',
'Comma separated tool diameters of the drills to be drilled (example: 0.6,1.0 or 3.125). '
'WARNING: No space allowed'),
'WARNING: No space allowed. Can also take the value "all" which will drill the holes for all tools.'),
('drillz', 'Drill depth into material (example: -2.0). Negative value.'),
('dpp', 'Progressive drilling into material with a specified step (example: 0.7). Positive value.'),
('travelz', 'Travel distance above material (example: 2.0).'),
@@ -89,9 +89,12 @@ class TclCommandDrillcncjob(TclCommandSignaled):
('outname', 'Name of the resulting Geometry object.')
]),
'examples': ['drillcncjob test.TXT -drillz -1.5 -travelz 14 -feedrate_z 222 -feedrate_rapid 456 '
'-spindlespeed 777 -toolchangez 33 -endz 22 -pp default\n'
'Usage of -feedrate_rapid matter only when the preprocessor is using it, like -marlin-.',
'drillcncjob test.DRL -drillz -1.7 -dpp 0.5 -travelz 2 -feedrate_z 800 -endxy 3,3']
'-spindlespeed 777 -toolchangez 33 -endz 22 -pp Marlin\n'
'Usage of -feedrate_rapid matter only when the preprocessor is using it, like -Marlin-.\n',
'drillcncjob test.DRL -drillz -1.7 -dpp 0.5 -travelz 2 -feedrate_z 800 -endxy 3,3\n',
'drillcncjob test.DRL -drilled_dias "all" -drillz -1.7 -dpp 0.5 -travelz 2 -feedrate_z 800 '
'-endxy 3,3'
]
}
def execute(self, args, unnamed_args):
@@ -233,7 +236,7 @@ class TclCommandDrillcncjob(TclCommandSignaled):
toolchange = self.app.defaults["tools_drill_toolchange"]
toolchangez = float(self.app.defaults["tools_drill_toolchangez"])
if "toolchangexy" in args and args["tools_drill_toolchangexy"]:
if "toolchangexy" in args and args["toolchangexy"]:
xy_toolchange = args["toolchangexy"]
else:
if self.app.defaults["tools_drill_toolchangexy"]:
@@ -269,6 +272,8 @@ class TclCommandDrillcncjob(TclCommandSignaled):
# ################# Set parameters #########################################################
# ##########################################################################################
job_obj.options['type'] = 'Excellon'
job_obj.multigeo = True
job_obj.multitool = True
pp_excellon_name = args["pp"] if "pp" in args and args["pp"] else self.app.defaults["tools_drill_ppname_e"]
job_obj.pp_excellon_name = pp_excellon_name
@@ -277,9 +282,9 @@ class TclCommandDrillcncjob(TclCommandSignaled):
if 'dpp' in args:
job_obj.multidepth = True
if args['dpp'] is not None:
job_obj.z_depthpercut = float(args['dpp'])
job_obj.z_depthpercut = abs(float(args['dpp']))
else:
job_obj.z_depthpercut = float(obj.options["dpp"])
job_obj.z_depthpercut = abs(float(obj.options["dpp"]))
else:
job_obj.multidepth = self.app.defaults["tools_drill_multidepth"]
job_obj.z_depthpercut = self.app.defaults["tools_drill_depthperpass"]
@@ -334,20 +339,32 @@ class TclCommandDrillcncjob(TclCommandSignaled):
job_obj.excellon_optimization_type = opt_type
job_obj.spindledir = self.app.defaults["tools_drill_spindledir"]
ret_val = job_obj.generate_from_excellon_by_tool(obj, tools, use_ui=False)
job_obj.source_file = ret_val
ret_val = job_obj.generate_from_excellon_by_tool(obj, tools, is_first=True, use_ui=False)
if ret_val == 'fail':
return 'fail'
job_obj.source_file = ret_val
job_obj.gc_start = ret_val[1]
total_gcode_parsed = []
# from Excellon attribute self.tools
for t_item in job_obj.tools:
job_obj.tools[t_item]['data']['tools_drill_offset'] = \
float(job_obj.tools[t_item]['offset_z']) + float(drillz)
job_obj.tools[t_item]['data']['tools_drill_ppname_e'] = job_obj.options['ppname_e']
job_obj.gcode_parse()
used_tooldia = obj.tools[t_item]['tooldia']
job_obj.tools[t_item]['tooldia'] = used_tooldia
tool_gcode = job_obj.tools[t_item]['gcode']
first_drill_point = job_obj.tools[t_item]['last_point']
gcode_parsed = job_obj.excellon_tool_gcode_parse(used_tooldia, gcode=tool_gcode,
start_pt=first_drill_point)
total_gcode_parsed += gcode_parsed
job_obj.tools[t_item]['gcode_parsed'] = gcode_parsed
job_obj.gcode_parsed = total_gcode_parsed
# job_obj.gcode_parse()
job_obj.create_geometry()
self.app.app_obj.new_object("cncjob", args['outname'], job_init, plot=False)