diff --git a/FlatCAMApp.py b/FlatCAMApp.py index eb14051e..b39f20e7 100644 --- a/FlatCAMApp.py +++ b/FlatCAMApp.py @@ -2102,10 +2102,15 @@ class App(QtCore.QObject): try: return_value = initialize(obj, self) except Exception as e: - if str(e) == "Empty Geometry": - self.inform.emit("[error_notcl] Object (%s) failed because: %s" % (kind, str(e))) - else: - self.inform.emit("[error] Object (%s) failed because: %s" % (kind, str(e))) + msg = "[error_notcl] An internal error has ocurred. See shell.\n" + msg += "Object (%s) failed because: %s \n\n" % (kind, str(e)) + msg += traceback.format_exc() + self.inform.emit(msg) + + # if str(e) == "Empty Geometry": + # self.inform.emit("[error_notcl] ) + # else: + # self.inform.emit("[error] Object (%s) failed because: %s" % (kind, str(e))) return "fail" t2 = time.time() diff --git a/FlatCAMObj.py b/FlatCAMObj.py index 8431132e..5d1049ca 100644 --- a/FlatCAMObj.py +++ b/FlatCAMObj.py @@ -1665,7 +1665,7 @@ class FlatCAMExcellon(FlatCAMObj, Excellon): job_obj.dwell = self.options["dwell"] job_obj.dwelltime = self.options["dwelltime"] job_obj.pp_excellon_name = pp_excellon_name - job_obj.toolchange_xy = "excellon" + job_obj.toolchange_xy_type = "excellon" job_obj.coords_decimals = int(self.app.defaults["cncjob_coords_decimals"]) job_obj.fr_decimals = int(self.app.defaults["cncjob_fr_decimals"]) @@ -3116,7 +3116,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): app_obj.progress.emit(40) - dia_cnc_dict['gcode'] = job_obj.generate_from_geometry_2( + res = job_obj.generate_from_geometry_2( self, tooldia=tooldia_val, offset=tool_offset, tolerance=0.0005, z_cut=z_cut, z_move=z_move, feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid, @@ -3127,10 +3127,16 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): pp_geometry_name=pp_geometry_name, tool_no=tool_cnt) + if res == 'fail': + log.debug("FlatCAMGeometry.mtool_gen_cncjob() --> generate_from_geometry2() failed") + return 'fail' + else: + dia_cnc_dict['gcode'] = res + app_obj.progress.emit(50) # tell gcode_parse from which point to start drawing the lines depending on what kind of # object is the source of gcode - job_obj.toolchange_xy = "geometry" + job_obj.toolchange_xy_type = "geometry" dia_cnc_dict['gcode_parsed'] = job_obj.gcode_parse() @@ -3283,7 +3289,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): app_obj.progress.emit(40) tool_solid_geometry = self.tools[current_uid]['solid_geometry'] - dia_cnc_dict['gcode'] = job_obj.generate_from_multitool_geometry( + res = job_obj.generate_from_multitool_geometry( tool_solid_geometry, tooldia=tooldia_val, offset=tool_offset, tolerance=0.0005, z_cut=z_cut, z_move=z_move, feedrate=feedrate, feedrate_z=feedrate_z, feedrate_rapid=feedrate_rapid, @@ -3294,6 +3300,12 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): pp_geometry_name=pp_geometry_name, tool_no=tool_cnt) + if res == 'fail': + log.debug("FlatCAMGeometry.mtool_gen_cncjob() --> generate_from_geometry2() failed") + return 'fail' + else: + dia_cnc_dict['gcode'] = res + dia_cnc_dict['gcode_parsed'] = job_obj.gcode_parse() # TODO this serve for bounding box creation only; should be optimized @@ -3301,7 +3313,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): # tell gcode_parse from which point to start drawing the lines depending on what kind of # object is the source of gcode - job_obj.toolchange_xy = "geometry" + job_obj.toolchange_xy_type = "geometry" app_obj.progress.emit(80) @@ -3317,14 +3329,14 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): def job_thread(app_obj): if self.solid_geometry: with self.app.proc_container.new("Generating CNC Code"): - app_obj.new_object("cncjob", outname, job_init_single_geometry) - app_obj.inform.emit("[success]CNCjob created: %s" % outname) - app_obj.progress.emit(100) + if app_obj.new_object("cncjob", outname, job_init_single_geometry) != 'fail': + app_obj.inform.emit("[success]CNCjob created: %s" % outname) + app_obj.progress.emit(100) else: with self.app.proc_container.new("Generating CNC Code"): - app_obj.new_object("cncjob", outname, job_init_multi_geometry) - app_obj.inform.emit("[success]CNCjob created: %s" % outname) - app_obj.progress.emit(100) + if app_obj.new_object("cncjob", outname, job_init_multi_geometry) != 'fail': + app_obj.inform.emit("[success]CNCjob created: %s" % outname) + app_obj.progress.emit(100) # Create a promise with the name self.app.collection.promise(outname) @@ -3433,7 +3445,7 @@ class FlatCAMGeometry(FlatCAMObj, Geometry): app_obj.progress.emit(50) # tell gcode_parse from which point to start drawing the lines depending on what kind of object is the # source of gcode - job_obj.toolchange_xy = "geometry" + job_obj.toolchange_xy_type = "geometry" job_obj.gcode_parse() app_obj.progress.emit(80) diff --git a/README.md b/README.md index 9e79ece0..d6562e8c 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ CAD program, and create G-Code for Isolation routing. - added a space before Y coordinate in end_code() function in some of the postprocessor files - added in Calculators Tool an Electroplating Calculator. - remade the App Menu for Editors: now they will be showed only when the respective Editor is active and hidden when the Editor is closed. +- added a traceback report in the TCL Shell for the errors that don't allow creation of an object; useful to trace exceptions/errors +- in case that the Toolchange X,Y parameter in Selected (or in Preferences) are deleted then the app will still do the job using the current coordinates for toolchange +- fixed an issue in camlib.CNCJob where tha variable self.toolchange_xy was used for 2 different purposes which created loss of information. 29.01.2019 diff --git a/camlib.py b/camlib.py index f544e487..f9deb1ae 100644 --- a/camlib.py +++ b/camlib.py @@ -4393,6 +4393,7 @@ class CNCjob(Geometry): self.tooldia = tooldia self.toolchangez = toolchangez self.toolchange_xy = None + self.toolchange_xy_type = None self.endz = endz self.depthpercut = depthpercut @@ -4534,7 +4535,19 @@ class CNCjob(Geometry): self.z_cut = drillz self.toolchangez = toolchangez - self.toolchange_xy = [float(eval(a)) for a in toolchangexy.split(",")] + + try: + if toolchangexy == '': + self.toolchange_xy = None + else: + self.toolchange_xy = [float(eval(a)) for a in toolchangexy.split(",")] + if len(self.toolchange_xy) < 2: + self.app.inform.emit("[error]The Toolchange X,Y field in Edit -> Preferences has to be " + "in the format (x, y) \nbut now there is only one value, not two. ") + return 'fail' + except Exception as e: + log.debug("camlib.CNCJob.generate_from_excellon_by_tool() --> %s" % str(e)) + pass self.startz = startz self.endz = endz @@ -4583,8 +4596,8 @@ class CNCjob(Geometry): # Initialization gcode = self.doformat(p.start_code) gcode += self.doformat(p.feedrate_code) - gcode += self.doformat(p.lift_code, x=0, y=0) - gcode += self.doformat(p.startz_code, x=0, y=0) + gcode += self.doformat(p.lift_code, x=self.toolchange_xy[0], y=self.toolchange_xy[1]) + gcode += self.doformat(p.startz_code, x=self.toolchange_xy[0], y=self.toolchange_xy[1]) # Distance callback class CreateDistanceCallback(object): @@ -4618,8 +4631,8 @@ class CNCjob(Geometry): locations.append((point.coords.xy[0][0], point.coords.xy[1][0])) return locations - oldx = 0 - oldy = 0 + oldx = self.toolchange_xy[0] + oldy = self.toolchange_xy[1] measured_distance = 0 current_platform = platform.architecture()[0] @@ -5067,7 +5080,19 @@ class CNCjob(Geometry): self.multidepth = multidepth self.toolchangez = toolchangez - self.toolchange_xy = [float(eval(a)) for a in toolchangexy.split(",")] + + try: + if toolchangexy == '': + self.toolchange_xy = None + else: + self.toolchange_xy = [float(eval(a)) for a in toolchangexy.split(",")] + if len(self.toolchange_xy) < 2: + self.app.inform.emit("[error]The Toolchange X,Y field in Edit -> Preferences has to be " + "in the format (x, y) \nbut now there is only one value, not two. ") + return 'fail' + except Exception as e: + log.debug("camlib.CNCJob.generate_from_geometry_2() --> %s" % str(e)) + pass self.pp_geometry_name = pp_geometry_name if pp_geometry_name else 'default' @@ -5328,10 +5353,17 @@ class CNCjob(Geometry): # Current path: temporary storage until tool is # lifted or lowered. - if self.toolchange_xy == "excellon": - pos_xy = [float(eval(a)) for a in self.app.defaults["excellon_toolchangexy"].split(",")] + if self.toolchange_xy_type == "excellon": + if self.app.defaults["excellon_toolchangexy"] == '': + pos_xy = [0, 0] + else: + pos_xy = [float(eval(a)) for a in self.app.defaults["excellon_toolchangexy"].split(",")] else: - pos_xy = [float(eval(a)) for a in self.app.defaults["geometry_toolchangexy"].split(",")] + if self.app.defaults["geometry_toolchangexy"] == '': + pos_xy = [0, 0] + else: + pos_xy = [float(eval(a)) for a in self.app.defaults["geometry_toolchangexy"].split(",")] + path = [pos_xy] # path = [(0, 0)] diff --git a/postprocessors/default.py b/postprocessors/default.py index 7beae193..fe42e3b9 100644 --- a/postprocessors/default.py +++ b/postprocessors/default.py @@ -29,7 +29,12 @@ class default(FlatCAMPostProc): gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n' gcode += '(Z Toolchange: ' + str(p['toolchangez']) + units + ')\n' - gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n' + + if coords_xy is not None: + gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n' + else: + gcode += '(X,Y Toolchange: ' + "None" + units + ')\n' + gcode += '(Z Start: ' + str(p['startz']) + units + ')\n' gcode += '(Z End: ' + str(p['endz']) + units + ')\n' gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n' @@ -62,8 +67,11 @@ class default(FlatCAMPostProc): def toolchange_code(self, p): toolchangez = p.toolchangez toolchangexy = p.toolchange_xy - toolchangex = toolchangexy[0] - toolchangey = toolchangexy[1] + gcode = '' + + if toolchangexy is not None: + toolchangex = toolchangexy[0] + toolchangey = toolchangexy[1] no_drills = 1 @@ -79,17 +87,23 @@ class default(FlatCAMPostProc): for i in p['options']['Tools_in_use']: if i[0] == p.tool: no_drills = i[2] - return """G00 Z{toolchangez} + + gcode = """G00 Z{toolchangez} T{tool} M5 M6 -(MSG, Change to Tool Dia = {toolC}, Total drills for current tool = {t_drills}) +(MSG, Change to Tool Dia = {toolC}, Total drills for tool T{tool} = {t_drills}) M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez), tool=int(p.tool), t_drills=no_drills, toolC=toolC_formatted) + + if toolchangexy is not None: + gcode += ('\n' + 'G00 X{toolchangex} Y{toolchangey}'.format(toolchangex=toolchangex, + toolchangey=toolchangey)) + return gcode else: - return """G00 Z{toolchangez} + gcode = """G00 Z{toolchangez} T{tool} M5 M6 @@ -97,6 +111,10 @@ M6 M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez), tool=int(p.tool), toolC=toolC_formatted) + if toolchangexy is not None: + gcode += ('\n' + 'G00 X{toolchangex} Y{toolchangey}'.format(toolchangex=toolchangex, + toolchangey=toolchangey)) + return gcode def up_to_zero_code(self, p): return 'G01 Z0' @@ -114,7 +132,9 @@ M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez) def end_code(self, p): coords_xy = p['toolchange_xy'] gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n") - gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n" + + if coords_xy is not None: + gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n" return gcode def feedrate_code(self, p): diff --git a/postprocessors/grbl_11.py b/postprocessors/grbl_11.py index 3b98d044..cf81fef0 100644 --- a/postprocessors/grbl_11.py +++ b/postprocessors/grbl_11.py @@ -29,7 +29,10 @@ class grbl_11(FlatCAMPostProc): gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n' gcode += '(Z Toolchange: ' + str(p['toolchangez']) + units + ')\n' - gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n' + if coords_xy is not None: + gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n' + else: + gcode += '(X,Y Toolchange: ' + "None" + units + ')\n' gcode += '(Z Start: ' + str(p['startz']) + units + ')\n' gcode += '(Z End: ' + str(p['endz']) + units + ')\n' gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n' @@ -62,6 +65,12 @@ class grbl_11(FlatCAMPostProc): def toolchange_code(self, p): toolchangez = p.toolchangez + toolchangexy = p.toolchange_xy + gcode = '' + + if toolchangexy is not None: + toolchangex = toolchangexy[0] + toolchangey = toolchangexy[1] if int(p.tool) == 1 and p.startz is not None: toolchangez = p.startz @@ -71,28 +80,40 @@ class grbl_11(FlatCAMPostProc): else: toolC_formatted = format(p.toolC, '.4f') + no_drills = 1 + if str(p['options']['type']) == 'Excellon': for i in p['options']['Tools_in_use']: if i[0] == p.tool: no_drills = i[2] - return """G00 Z{toolchangez} -T{tool} -M5 -M6 -(MSG, Change to Tool Dia = {toolC}, Total drills for current tool = {t_drills}) -M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez), - tool=int(p.tool), - t_drills=no_drills, - toolC=toolC_formatted) + + gcode = """G00 Z{toolchangez} + T{tool} + M5 + M6 + (MSG, Change to Tool Dia = {toolC}, Total drills for tool T{tool} = {t_drills}) + M0""".format(toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez), + tool=int(p.tool), + t_drills=no_drills, + toolC=toolC_formatted) + + if toolchangexy is not None: + gcode += ('\n' + 'G00 X{toolchangex} Y{toolchangey}'.format(toolchangex=toolchangex, + toolchangey=toolchangey)) + return gcode else: - return """G00 Z{toolchangez} -T{tool} -M5 -M6 -(MSG, Change to Tool Dia = {toolC}) -M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez), - tool=int(p.tool), - toolC=toolC_formatted) + gcode = """G00 Z{toolchangez} + T{tool} + M5 + M6 + (MSG, Change to Tool Dia = {toolC}) + M0""".format(toolchangez=self.coordinate_format % (p.coords_decimals, toolchangez), + tool=int(p.tool), + toolC=toolC_formatted) + if toolchangexy is not None: + gcode += ('\n' + 'G00 X{toolchangex} Y{toolchangey}'.format(toolchangex=toolchangex, + toolchangey=toolchangey)) + return gcode def up_to_zero_code(self, p): return 'G01 Z0' @@ -110,8 +131,10 @@ M0""".format(toolchangez=self.coordinate_format%(p.coords_decimals, toolchangez) def end_code(self, p): coords_xy = p['toolchange_xy'] - gcode = ('G00 Z' + self.feedrate_format % (p.fr_decimals, p.endz) + "\n") - gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n" + gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n") + + if coords_xy is not None: + gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n" return gcode def feedrate_code(self, p): diff --git a/postprocessors/grbl_laser.py b/postprocessors/grbl_laser.py index 72611f00..9a7dd274 100644 --- a/postprocessors/grbl_laser.py +++ b/postprocessors/grbl_laser.py @@ -59,8 +59,11 @@ class grbl_laser(FlatCAMPostProc): ' F' + str(self.feedrate_format %(p.fr_decimals, p.feedrate)) def end_code(self, p): + coords_xy = p['toolchange_xy'] gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n") - gcode += 'G00 X0Y0' + + if coords_xy is not None: + gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n" return gcode def feedrate_code(self, p): diff --git a/postprocessors/line_xyz.py b/postprocessors/line_xyz.py index 2f01e492..78160b2f 100644 --- a/postprocessors/line_xyz.py +++ b/postprocessors/line_xyz.py @@ -29,7 +29,10 @@ class line_xyz(FlatCAMPostProc): gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n' gcode += '(Z Toolchange: ' + str(p['toolchangez']) + units + ')\n' - gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n' + if coords_xy is not None: + gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n' + else: + gcode += '(X,Y Toolchange: ' + "None" + units + ')\n' gcode += '(Z Start: ' + str(p['startz']) + units + ')\n' gcode += '(Z End: ' + str(p['endz']) + units + ')\n' gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n' @@ -71,8 +74,14 @@ class line_xyz(FlatCAMPostProc): def toolchange_code(self, p): toolchangez = p.toolchangez toolchangexy = p.toolchange_xy - toolchangex = toolchangexy[0] - toolchangey = toolchangexy[1] + gcode = '' + + if toolchangexy is not None: + toolchangex = toolchangexy[0] + toolchangey = toolchangexy[1] + else: + toolchangex = p.x + toolchangey = p.y no_drills = 1 @@ -132,7 +141,11 @@ M0""".format(toolchangex=self.coordinate_format%(p.coords_decimals, toolchangex) return g def end_code(self, p): - g = ('G00 ' + self.position_code(p)).format(**p) + coords_xy = p['toolchange_xy'] + if coords_xy is not None: + g = 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n" + else: + g = ('G00 ' + self.position_code(p)).format(**p) g += ' Z' + self.coordinate_format % (p.coords_decimals, p.endz) return g diff --git a/postprocessors/manual_toolchange.py b/postprocessors/manual_toolchange.py index 64e65e77..37faf366 100644 --- a/postprocessors/manual_toolchange.py +++ b/postprocessors/manual_toolchange.py @@ -29,7 +29,10 @@ class manual_toolchange(FlatCAMPostProc): gcode += '(Z_Move: ' + str(p['z_move']) + units + ')\n' gcode += '(Z Toolchange: ' + str(p['toolchangez']) + units + ')\n' - gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n' + if coords_xy is not None: + gcode += '(X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + ')\n' + else: + gcode += '(X,Y Toolchange: ' + "0.0, 0.0" + units + ')\n' gcode += '(Z Start: ' + str(p['startz']) + units + ')\n' gcode += '(Z End: ' + str(p['endz']) + units + ')\n' gcode += '(Steps per circle: ' + str(p['steps_per_circle']) + ')\n' @@ -62,8 +65,13 @@ class manual_toolchange(FlatCAMPostProc): def toolchange_code(self, p): toolchangez = p.toolchangez toolchangexy = p.toolchange_xy - toolchangex = toolchangexy[0] - toolchangey = toolchangexy[1] + + if toolchangexy is not None: + toolchangex = toolchangexy[0] + toolchangey = toolchangexy[1] + else: + toolchangex = 0.0 + toolchangey = 0.0 no_drills = 1 @@ -128,7 +136,10 @@ M0 def end_code(self, p): coords_xy = p['toolchange_xy'] gcode = ('G00 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + "\n") - gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n" + if coords_xy is not None: + gcode += 'G00 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + "\n" + else: + gcode += 'G00 X0 Y0' + "\n" return gcode def feedrate_code(self, p): diff --git a/postprocessors/marlin.py b/postprocessors/marlin.py index 57cb5be9..d282f525 100644 --- a/postprocessors/marlin.py +++ b/postprocessors/marlin.py @@ -9,6 +9,7 @@ class marlin(FlatCAMPostProc): def start_code(self, p): units = ' ' + str(p['units']).lower() + coords_xy = p['toolchange_xy'] gcode = '' if str(p['options']['type']) == 'Geometry': @@ -29,6 +30,12 @@ class marlin(FlatCAMPostProc): gcode += ';Z_Move: ' + str(p['z_move']) + units + '\n' gcode += ';Z Toolchange: ' + str(p['toolchangez']) + units + '\n' + + if coords_xy is not None: + gcode += ';X,Y Toolchange: ' + "%.4f, %.4f" % (coords_xy[0], coords_xy[1]) + units + '\n' + else: + gcode += ';X,Y Toolchange: ' + "None" + units + '\n' + gcode += ';Z Start: ' + str(p['startz']) + units + '\n' gcode += ';Z End: ' + str(p['endz']) + units + '\n' gcode += ';Steps per circle: ' + str(p['steps_per_circle']) + '\n' @@ -104,7 +111,9 @@ M0 Change to Tool Dia = {toolC} def end_code(self, p): coords_xy = p['toolchange_xy'] gcode = ('G0 Z' + self.feedrate_format %(p.fr_decimals, p.endz) + " " + self.feedrate_rapid_code(p) + "\n") - gcode += 'G0 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + " " + self.feedrate_rapid_code(p) + "\n" + + if coords_xy is not None: + gcode += 'G0 X{x} Y{y}'.format(x=coords_xy[0], y=coords_xy[1]) + " " + self.feedrate_rapid_code(p) + "\n" return gcode