From f103cc0a7058b5312df8102beaadc8c2b7c0fda2 Mon Sep 17 00:00:00 2001 From: Marius Stanciu Date: Fri, 5 Mar 2021 05:36:40 +0200 Subject: [PATCH] - added detection of Gerber files in Gerber X2 format - added a new feature: if a Gerber file in format Gerber X2 having Drill information's is detected then after loading, it will be converted to an Excellon object --- CHANGELOG.md | 5 +++++ appObjects/AppObject.py | 15 +++++++++----- appParsers/ParseGerber.py | 43 +++++++++++++++++++++++++++++++++++---- app_Main.py | 39 +++++++++++++++++++++++++++-------- 4 files changed, 84 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f42e775f..e5c12749 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ CHANGELOG for FlatCAM beta ================================================= +5.03.2021 + +- added detection of Gerber files in Gerber X2 format +- added a new feature: if a Gerber file in format Gerber X2 having Drill information's is detected then after loading, it will be converted to an Excellon object + 4.03.2021 - fixed Punch Gerber Tool to work in new conditions diff --git a/appObjects/AppObject.py b/appObjects/AppObject.py index ab02a8d6..cea96f18 100644 --- a/appObjects/AppObject.py +++ b/appObjects/AppObject.py @@ -55,7 +55,7 @@ class AppObject(QtCore.QObject): self.object_plotted.connect(self.on_object_plotted) self.plots_updated.connect(self.app.on_plots_updated) - def new_object(self, kind, name, initialize: object, plot=True, autoselected=True, callback=None, + def new_object(self, kind, name, initialize, plot=True, autoselected=True, callback=None, callback_params=None): """ Creates a new specialized FlatCAMObj and attaches it to the application, @@ -74,7 +74,7 @@ class AppObject(QtCore.QObject): :param initialize: Function to run after creation of the object but before it is attached to the application. The function is called with 2 parameters: the new object and the App instance. - :type initialize: object + :type initialize: function :param plot: If to plot the resulting object :param autoselected: if the resulting object is autoselected in the Project tab and therefore in the self.collection @@ -214,9 +214,14 @@ class AppObject(QtCore.QObject): # ############################################################################################################ obj.moveToThread(self.app.main_thread) - if callback_params is None: - callback_params = [] - self.object_created.emit(obj, obj_plot, obj_autoselected, callback, callback_params) + if return_value == 'drill_gx2': + self.object_created.emit(obj, obj_plot, obj_autoselected, self.app.convert_any2excellon, [name]) + self.app.log.warning("Gerber X2 drill file detected. Converted to Excellon object.") + self.app.inform.emit('[WARNING] %s' % _("Gerber X2 drill file detected. Converted to Excellon object.")) + else: + if callback_params is None: + callback_params = [] + self.object_created.emit(obj, obj_plot, obj_autoselected, callback, callback_params) return obj diff --git a/appParsers/ParseGerber.py b/appParsers/ParseGerber.py index 97ccd0ae..c02b17fb 100644 --- a/appParsers/ParseGerber.py +++ b/appParsers/ParseGerber.py @@ -140,7 +140,13 @@ class Gerber(Geometry): self.source_file = '' - # ### Parser patterns ## ## + # ############################################################################################################# + # ################################# Parser patterns ########################################################### + # ############################################################################################################# + + # Detect Gerber x2 format + self.gx2_re = re.compile(r'%TF\.FileFunction.*') + # FS - Format Specification # The format of X and Y must be the same! # L-omit leading zeros, T-omit trailing zeros, D-no zero supression @@ -355,7 +361,14 @@ class Gerber(Geometry): break processed_lines = list(line_generator()) - self.parse_lines(processed_lines) + ret_val = self.parse_lines(processed_lines) + + if ret_val == 'fail': + return 'fail' + elif ret_val == 'drill': + return 'drill_gx2' + else: + return # @profile def parse_lines(self, glines): @@ -366,10 +379,12 @@ class Gerber(Geometry): :param glines: Gerber code as list of strings, each element being one line of the source file. :type glines: list - :return: None - :rtype: None + :return: only errors/warnings + :rtype: str """ + is_excellon_gx2 = False + # Coordinates of the current path, each is [x, y] path = [] @@ -451,6 +466,22 @@ class Gerber(Geometry): if match: continue + # ###################################################################################################### + # ######## Detect GERBER X2 format ##################################################################### + # ###################################################################################################### + match = self.gx2_re.search(gline) + if match: + self.app.log.warning('Gerber X2 format detected !!!') + self.app.inform.emit( + '[WARNING] %s' % _('Gerber X2 format detected. Parsing may not be done correctly.')) + if 'Drill' in gline: + self.app.log.warning('Drill file in Gerber X2 format detected !!!') + self.app.inform.emit( + '[WARNING] %s' % _('Drill file Gerber X2 format detected. ' + 'Parsing may not be done correctly.')) + is_excellon_gx2 = True + continue + # ############################################################### # ################ Polarity change ############################# # ######## Example: %LPD*% or %LPC*% ################### @@ -1703,6 +1734,10 @@ class Gerber(Geometry): loc = '%s #%d %s: %s\n' % (_("Gerber Line"), line_num, _("Gerber Line Content"), gline) + repr(err) self.app.inform.emit('[ERROR] %s\n%s:' % (_("Gerber Parser ERROR"), loc)) + return 'fail' + + if is_excellon_gx2 is True: + return 'drill' @staticmethod def create_flash_geometry(location, aperture, steps_per_circle=None): diff --git a/app_Main.py b/app_Main.py index a2b4e6f7..d22eb547 100644 --- a/app_Main.py +++ b/app_Main.py @@ -5941,10 +5941,11 @@ class App(QtCore.QObject): except Exception as e: return "Operation failed: %s" % str(e) - def convert_any2excellon(self): + def convert_any2excellon(self, conv_obj_name=None): """ Will convert any object out of Gerber, Excellon, Geometry to an Excellon object. + :param conv_obj_name: a FlatCAM object :return: """ @@ -6093,15 +6094,30 @@ class App(QtCore.QObject): obj_init.source_file = app_obj.f_handlers.export_excellon(obj_name=outname, local_use=obj_init, filename=None, use_thread=False) - if not self.collection.get_selected(): - self.log.warning("App.convert_any2excellon--> No object selected") - self.inform.emit('[WARNING_NOTCL] %s' % _("No object is selected.")) - return + if conv_obj_name is None: + if not self.collection.get_selected(): + self.log.warning("App.convert_any2excellon--> No object selected") + self.inform.emit('[WARNING_NOTCL] %s' % _("No object is selected.")) + return - for obj in self.collection.get_selected(): + for obj in self.collection.get_selected(): + + obj_name = obj.options["name"] + outname = "%s_conv" % str(obj_name) + try: + if obj.kind == 'gerber': + self.app_obj.new_object("excellon", outname, initialize_from_gerber) + elif obj.kind == 'geometry': + self.app_obj.new_object("excellon", outname, initialize_from_geometry) + else: + self.log.warning("App.convert_any2excellon --> This is no valid object for conversion.") + + except Exception as e: + return "Operation failed: %s" % str(e) + else: + outname = conv_obj_name + obj = self.collection.get_by_name(outname) - obj_name = obj.options["name"] - outname = "%s_conv" % str(obj_name) try: if obj.kind == 'gerber': self.app_obj.new_object("excellon", outname, initialize_from_gerber) @@ -10663,7 +10679,7 @@ class MenuFileHandlers(QtCore.QObject): # Opening the file happens here try: - gerber_obj.parse_file(filename) + parse_ret_val = gerber_obj.parse_file(filename) except IOError: app_obj.inform.emit('[ERROR_NOTCL] %s: %s' % (_("Failed to open file"), filename)) return "fail" @@ -10683,11 +10699,16 @@ class MenuFileHandlers(QtCore.QObject): _("Object is not Gerber file or empty. Aborting object creation.")) return "fail" + if parse_ret_val: + return parse_ret_val + self.log.debug("open_gerber()") if not os.path.exists(filename): self.inform.emit('[ERROR_NOTCL] %s' % _("File no longer available.")) return + ret_val = None + with self.app.proc_container.new('%s...' % _("Opening")): # Object name name = outname or filename.split('/')[-1].split('\\')[-1]