Jelajahi Sumber

Added some error handling to the Excellon parser.

jpcaram 11 tahun lalu
induk
melakukan
207842f98f
2 mengubah file dengan 106 tambahan dan 84 penghapusan
  1. 7 1
      FlatCAMApp.py
  2. 99 83
      camlib.py

+ 7 - 1
FlatCAMApp.py

@@ -1610,7 +1610,13 @@ class App(QtCore.QObject):
                 self.progress.emit(0)  # TODO: self and app_bjj mixed
                 self.progress.emit(0)  # TODO: self and app_bjj mixed
                 raise IOError("Cannot open file: " + filename)
                 raise IOError("Cannot open file: " + filename)
 
 
-            excellon_obj.create_geometry()
+            try:
+                excellon_obj.create_geometry()
+            except Exception as e:
+                app_obj.inform.emit("[error] Failed to create geometry after parsing: " + filename)
+                self.progress.emit(0)
+                raise e
+
             self.progress.emit(70)
             self.progress.emit(70)
 
 
         # Object name
         # Object name

+ 99 - 83
camlib.py

@@ -51,6 +51,10 @@ handler.setFormatter(formatter)
 log.addHandler(handler)
 log.addHandler(handler)
 
 
 
 
+class ParseError(Exception):
+    pass
+
+
 class Geometry(object):
 class Geometry(object):
     """
     """
     Base geometry class.
     Base geometry class.
@@ -1781,7 +1785,8 @@ class Excellon(Geometry):
         self.hend_re = re.compile(r'^(?:M95|%)$')
         self.hend_re = re.compile(r'^(?:M95|%)$')
 
 
         # FMAT Excellon format
         # FMAT Excellon format
-        self.fmat_re = re.compile(r'^FMAT,([12])$')
+        # Ignored in the parser
+        #self.fmat_re = re.compile(r'^FMAT,([12])$')
 
 
         # Number format and units
         # Number format and units
         # INCH uses 6 digits
         # INCH uses 6 digits
@@ -1866,104 +1871,115 @@ class Excellon(Geometry):
 
 
         #### Parsing starts here ####
         #### Parsing starts here ####
         line_num = 0  # Line number
         line_num = 0  # Line number
-        for eline in elines:
-            line_num += 1
+        eline = ""
+        try:
+            for eline in elines:
+                line_num += 1
+                log.debug("%3d %s" % (line_num, str(eline)))
 
 
-            ### Cleanup lines
-            eline = eline.strip(' \r\n')
+                ### Cleanup lines
+                eline = eline.strip(' \r\n')
 
 
-            ## Header Begin/End ##
-            if self.hbegin_re.search(eline):
-                in_header = True
-                continue
+                ## Header Begin (M48) / End (M95) ##
+                if self.hbegin_re.search(eline):
+                    in_header = True
+                    continue
 
 
-            if self.hend_re.search(eline):
-                in_header = False
-                continue
+                if self.hend_re.search(eline):
+                    in_header = False
+                    continue
 
 
-            #### Body ####
-            if not in_header:
+                #### Body ####
+                if not in_header:
 
 
-                ## Tool change ##
-                match = self.toolsel_re.search(eline)
-                if match:
-                    current_tool = str(int(match.group(1)))
-                    continue
+                    ## Tool change ##
+                    match = self.toolsel_re.search(eline)
+                    if match:
+                        current_tool = str(int(match.group(1)))
+                        log.debug("Tool change: %s" % current_tool)
+                        continue
 
 
-                ## Coordinates without period ##
-                match = self.coordsnoperiod_re.search(eline)
-                if match:
-                    try:
-                        #x = float(match.group(1))/10000
-                        x = self.parse_number(match.group(1))
-                        current_x = x
-                    except TypeError:
-                        x = current_x
+                    ## Coordinates without period ##
+                    match = self.coordsnoperiod_re.search(eline)
+                    if match:
+                        try:
+                            #x = float(match.group(1))/10000
+                            x = self.parse_number(match.group(1))
+                            current_x = x
+                        except TypeError:
+                            x = current_x
 
 
-                    try:
-                        #y = float(match.group(2))/10000
-                        y = self.parse_number(match.group(2))
-                        current_y = y
-                    except TypeError:
-                        y = current_y
+                        try:
+                            #y = float(match.group(2))/10000
+                            y = self.parse_number(match.group(2))
+                            current_y = y
+                        except TypeError:
+                            y = current_y
+
+                        if x is None or y is None:
+                            log.error("Missing coordinates")
+                            continue
 
 
-                    if x is None or y is None:
-                        log.error("Missing coordinates")
+                        self.drills.append({'point': Point((x, y)), 'tool': current_tool})
                         continue
                         continue
 
 
-                    self.drills.append({'point': Point((x, y)), 'tool': current_tool})
-                    continue
+                    ## Coordinates with period: Use literally. ##
+                    match = self.coordsperiod_re.search(eline)
+                    if match:
+                        try:
+                            x = float(match.group(1))
+                            current_x = x
+                        except TypeError:
+                            x = current_x
 
 
-                ## Coordinates with period: Use literally. ##
-                match = self.coordsperiod_re.search(eline)
-                if match:
-                    try:
-                        x = float(match.group(1))
-                        current_x = x
-                    except TypeError:
-                        x = current_x
+                        try:
+                            y = float(match.group(2))
+                            current_y = y
+                        except TypeError:
+                            y = current_y
 
 
-                    try:
-                        y = float(match.group(2))
-                        current_y = y
-                    except TypeError:
-                        y = current_y
+                        if x is None or y is None:
+                            log.error("Missing coordinates")
+                            continue
 
 
-                    if x is None or y is None:
-                        log.error("Missing coordinates")
+                        self.drills.append({'point': Point((x, y)), 'tool': current_tool})
                         continue
                         continue
 
 
-                    self.drills.append({'point': Point((x, y)), 'tool': current_tool})
-                    continue
+                #### Header ####
+                if in_header:
 
 
-            #### Header ####
-            if in_header:
+                    ## Tool definitions ##
+                    match = self.toolset_re.search(eline)
+                    if match:
 
 
-                ## Tool definitions ##
-                match = self.toolset_re.search(eline)
-                if match:
-                    name = str(int(match.group(1)))
-                    spec = {
-                        "C": float(match.group(2)),
-                        # "F": float(match.group(3)),
-                        # "S": float(match.group(4)),
-                        # "B": float(match.group(5)),
-                        # "H": float(match.group(6)),
-                        # "Z": float(match.group(7))
-                    }
-                    self.tools[name] = spec
-                    continue
+                        name = str(int(match.group(1)))
+                        spec = {
+                            "C": float(match.group(2)),
+                            # "F": float(match.group(3)),
+                            # "S": float(match.group(4)),
+                            # "B": float(match.group(5)),
+                            # "H": float(match.group(6)),
+                            # "Z": float(match.group(7))
+                        }
+                        self.tools[name] = spec
+                        log.debug("  Tool definition: %s %s" % (name, spec))
+                        continue
 
 
-                ## Units and number format ##
-                match = self.units_re.match(eline)
-                if match:
-                    self.zeros = match.group(2) or self.zeros  # "T" or "L". Might be empty
-                    self.units = {"INCH": "IN", "METRIC": "MM"}[match.group(1)]
-                    continue
+                    ## Units and number format ##
+                    match = self.units_re.match(eline)
+                    if match:
+                        self.zeros = match.group(2) or self.zeros  # "T" or "L". Might be empty
+                        self.units = {"INCH": "IN", "METRIC": "MM"}[match.group(1)]
+                        log.debug("  Units/Format: %s %s" % (self.units, self.zeros))
+                        continue
 
 
-            log.warning("Line ignored: %s" % eline)
+                log.warning("Line ignored: %s" % eline)
 
 
-        log.info("Zeros: %s, Units %s." % (self.zeros, self.units))
+            log.info("Zeros: %s, Units %s." % (self.zeros, self.units))
+
+        except Exception as e:
+            log.error("PARSING FAILED. Line %d: %s" % (line_num, eline))
+            raise
         
         
     def parse_number(self, number_str):
     def parse_number(self, number_str):
         """
         """
@@ -1983,16 +1999,16 @@ class Excellon(Geometry):
             # If less than size digits, they are automatically added,
             # If less than size digits, they are automatically added,
             # 5 digits then are divided by 10^3 and so on.
             # 5 digits then are divided by 10^3 and so on.
             match = self.leadingzeros_re.search(number_str)
             match = self.leadingzeros_re.search(number_str)
-            return float(number_str)/(10**(len(match.group(1)) + len(match.group(2)) - 2))
+            return float(number_str) / (10 ** (len(match.group(1)) + len(match.group(2)) - 2))
 
 
         else:  # Trailing
         else:  # Trailing
             # You must show all zeros to the right of the number and can omit
             # You must show all zeros to the right of the number and can omit
             # all zeros to the left of the number. The CNC-7 will count the number
             # all zeros to the left of the number. The CNC-7 will count the number
             # of digits you typed and automatically fill in the missing zeros.
             # of digits you typed and automatically fill in the missing zeros.
             if self.units.lower() == "in":  # Inches is 00.0000
             if self.units.lower() == "in":  # Inches is 00.0000
-                return float(number_str)/10000
+                return float(number_str) / 10000
 
 
-            return float(number_str)/1000  # Metric is 000.000
+            return float(number_str) / 1000  # Metric is 000.000
 
 
     def create_geometry(self):
     def create_geometry(self):
         """
         """
@@ -2006,7 +2022,7 @@ class Excellon(Geometry):
         for drill in self.drills:
         for drill in self.drills:
             #poly = drill['point'].buffer(self.tools[drill['tool']]["C"]/2.0)
             #poly = drill['point'].buffer(self.tools[drill['tool']]["C"]/2.0)
             tooldia = self.tools[drill['tool']]['C']
             tooldia = self.tools[drill['tool']]['C']
-            poly = drill['point'].buffer(tooldia/2.0)
+            poly = drill['point'].buffer(tooldia / 2.0)
             self.solid_geometry.append(poly)
             self.solid_geometry.append(poly)
 
 
     def scale(self, factor):
     def scale(self, factor):