diff options
Diffstat (limited to 'gerber')
-rwxr-xr-x | gerber/excellon.py | 61 | ||||
-rw-r--r-- | gerber/excellon_report/excellon_drr.py | 25 | ||||
-rw-r--r-- | gerber/excellon_statements.py | 14 | ||||
-rw-r--r-- | gerber/excellon_tool.py | 85 |
4 files changed, 162 insertions, 23 deletions
diff --git a/gerber/excellon.py b/gerber/excellon.py index 4456329..0637b23 100755 --- a/gerber/excellon.py +++ b/gerber/excellon.py @@ -175,21 +175,12 @@ class ExcellonFile(CamFile): def write(self, filename=None):
filename = filename if filename is not None else self.filename
with open(filename, 'w') as f:
-
- # Copy the header verbatim
- for statement in self.statements:
- if not isinstance(statement, ToolSelectionStmt):
- f.write(statement.to_excellon(self.settings) + '\n')
- else:
- break
-
- # Write out coordinates for drill hits by tool
- for tool in iter(self.tools.values()):
- f.write(ToolSelectionStmt(tool.number).to_excellon(self.settings) + '\n')
- for hit in self.hits:
- if hit.tool.number == tool.number:
- f.write(CoordinateStmt(*hit.position).to_excellon(self.settings) + '\n')
- f.write(EndOfProgramStmt().to_excellon() + '\n')
+ self.writes(f)
+
+ def writes(self, f):
+ # Copy the header verbatim
+ for statement in self.statements:
+ f.write(statement.to_excellon(self.settings) + '\n')
def to_inch(self):
"""
@@ -300,6 +291,8 @@ class ExcellonParser(object): self.hits = []
self.active_tool = None
self.pos = [0., 0.]
+ # Default for lated is None, which means we don't know
+ self.plated = ExcellonTool.PLATED_UNKNOWN
if settings is not None:
self.units = settings.units
self.zeros = settings.zeros
@@ -360,6 +353,12 @@ class ExcellonParser(object): if detected_format:
self.format = detected_format
+ if "TYPE=PLATED" in comment_stmt.comment:
+ self.plated = ExcellonTool.PLATED_YES
+
+ if "TYPE=NON_PLATED" in comment_stmt.comment:
+ self.plated = ExcellonTool.PLATED_NO
+
if "HEADER:" in comment_stmt.comment:
self.state = "HEADER"
@@ -370,7 +369,7 @@ class ExcellonParser(object): tools = ExcellonToolDefinitionParser(self._settings()).parse_raw(comment_stmt.comment)
if len(tools) == 1:
tool = tools[tools.keys()[0]]
- self.comment_tools[tool.number] = tool
+ self._add_comment_tool(tool)
elif line[:3] == 'M48':
self.statements.append(HeaderBeginStmt())
@@ -503,7 +502,8 @@ class ExcellonParser(object): elif line[0] == 'T' and self.state == 'HEADER':
if not ',OFF' in line and not ',ON' in line:
- tool = ExcellonTool.from_excellon(line, self._settings())
+ tool = ExcellonTool.from_excellon(line, self._settings(), None, self.plated)
+ self._merge_properties(tool)
self.tools[tool.number] = tool
self.statements.append(tool)
else:
@@ -572,6 +572,33 @@ class ExcellonParser(object): return FileSettings(units=self.units, format=self.format,
zeros=self.zeros, notation=self.notation)
+ def _add_comment_tool(self, tool):
+ """
+ Add a tool that was defined in the comments to this file.
+
+ If we have already found this tool, then we will merge this comment tool definition into
+ the information for the tool
+ """
+
+ existing = self.tools.get(tool.number)
+ if existing and existing.plated == None:
+ existing.plated = tool.plated
+
+ self.comment_tools[tool.number] = tool
+
+ def _merge_properties(self, tool):
+ """
+ When we have externally defined tools, merge the properties of that tool into this one
+
+ For now, this is only plated
+ """
+
+ if tool.plated == ExcellonTool.PLATED_UNKNOWN:
+ ext_tool = self.ext_tools.get(tool.number)
+
+ if ext_tool:
+ tool.plated = ext_tool.plated
+
def _get_tool(self, toolid):
tool = self.tools.get(toolid)
diff --git a/gerber/excellon_report/excellon_drr.py b/gerber/excellon_report/excellon_drr.py new file mode 100644 index 0000000..ab9e857 --- /dev/null +++ b/gerber/excellon_report/excellon_drr.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright 2015 Garret Fick <garret@ficksworkshop.com> + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Excellon DRR File module +==================== +**Excellon file classes** + +Extra parsers for allegro misc files that can be useful when the Excellon file doesn't contain parameter information +""" + diff --git a/gerber/excellon_statements.py b/gerber/excellon_statements.py index 66641a1..18eaea1 100644 --- a/gerber/excellon_statements.py +++ b/gerber/excellon_statements.py @@ -111,9 +111,14 @@ class ExcellonTool(ExcellonStatement): hit_count : integer Number of tool hits in excellon file. """ + + PLATED_UNKNOWN = None + PLATED_YES = 'plated' + PLATED_NO = 'nonplated' + PLATED_OPTIONAL = 'optional' @classmethod - def from_excellon(cls, line, settings, id=None): + def from_excellon(cls, line, settings, id=None, plated=None): """ Create a Tool from an excellon file tool definition line. Parameters @@ -150,6 +155,10 @@ class ExcellonTool(ExcellonStatement): args['number'] = int(val) elif cmd == 'Z': args['depth_offset'] = parse_gerber_value(val, nformat, zero_suppression) + + if plated != ExcellonTool.PLATED_UNKNOWN: + # Sometimees we can can parse the + args['plated'] = plated return cls(settings, **args) @classmethod @@ -182,6 +191,8 @@ class ExcellonTool(ExcellonStatement): self.diameter = kwargs.get('diameter') self.max_hit_count = kwargs.get('max_hit_count') self.depth_offset = kwargs.get('depth_offset') + self.plated = kwargs.get('plated') + self.hit_count = 0 def to_excellon(self, settings=None): @@ -235,6 +246,7 @@ class ExcellonTool(ExcellonStatement): and self.rpm == other.rpm and self.depth_offset == other.depth_offset and self.max_hit_count == other.max_hit_count + and self.plated == other.plated and self.settings.units == other.settings.units) def __repr__(self): diff --git a/gerber/excellon_tool.py b/gerber/excellon_tool.py index 31d72d5..bd76e54 100644 --- a/gerber/excellon_tool.py +++ b/gerber/excellon_tool.py @@ -54,13 +54,17 @@ class ExcellonToolDefinitionParser(object): """ allegro_tool = re.compile(r'(?P<size>[0-9/.]+)\s+(?P<plated>P|N)\s+T(?P<toolid>[0-9]{2})\s+(?P<xtol>[0-9/.]+)\s+(?P<ytol>[0-9/.]+)') - allegro_comment_mils = re.compile('Holesize (?P<toolid>[0-9]{1,2})\. = (?P<size>[0-9/.]+) Tolerance = \+(?P<xtol>[0-9/.]+)/-(?P<ytol>[0-9/.]+) (?P<plated>(PLATED)|(NON_PLATED)) MILS Quantity = [0-9]+') - allegro_comment_mm = re.compile('Holesize (?P<toolid>[0-9]{1,2})\. = (?P<size>[0-9/.]+) Tolerance = \+(?P<xtol>[0-9/.]+)/-(?P<ytol>[0-9/.]+) (?P<plated>(PLATED)|(NON_PLATED)) MM Quantity = [0-9]+') + allegro_comment_mils = re.compile('Holesize (?P<toolid>[0-9]{1,2})\. = (?P<size>[0-9/.]+) Tolerance = \+(?P<xtol>[0-9/.]+)/-(?P<ytol>[0-9/.]+) (?P<plated>(PLATED)|(NON_PLATED)|(OPTIONAL)) MILS Quantity = [0-9]+') + allegro2_comment_mils = re.compile('T(?P<toolid>[0-9]{1,2}) Holesize (?P<toolid2>[0-9]{1,2})\. = (?P<size>[0-9/.]+) Tolerance = \+(?P<xtol>[0-9/.]+)/-(?P<ytol>[0-9/.]+) (?P<plated>(PLATED)|(NON_PLATED)|(OPTIONAL)) MILS Quantity = [0-9]+') + allegro_comment_mm = re.compile('Holesize (?P<toolid>[0-9]{1,2})\. = (?P<size>[0-9/.]+) Tolerance = \+(?P<xtol>[0-9/.]+)/-(?P<ytol>[0-9/.]+) (?P<plated>(PLATED)|(NON_PLATED)|(OPTIONAL)) MM Quantity = [0-9]+') + allegro2_comment_mm = re.compile('T(?P<toolid>[0-9]{1,2}) Holesize (?P<toolid2>[0-9]{1,2})\. = (?P<size>[0-9/.]+) Tolerance = \+(?P<xtol>[0-9/.]+)/-(?P<ytol>[0-9/.]+) (?P<plated>(PLATED)|(NON_PLATED)|(OPTIONAL)) MM Quantity = [0-9]+') matchers = [ (allegro_tool, 'mils'), (allegro_comment_mils, 'mils'), + (allegro2_comment_mils, 'mils'), (allegro_comment_mm, 'mm'), + (allegro2_comment_mm, 'mm'), ] def __init__(self, settings=None): @@ -81,7 +85,7 @@ class ExcellonToolDefinitionParser(object): unit = matcher[1] size = float(m.group('size')) - plated = m.group('plated') + platedstr = m.group('plated') toolid = int(m.group('toolid')) xtol = float(m.group('xtol')) ytol = float(m.group('ytol')) @@ -90,7 +94,16 @@ class ExcellonToolDefinitionParser(object): xtol = self._convert_length(xtol, unit) ytol = self._convert_length(ytol, unit) - tool = ExcellonTool(None, number=toolid, diameter=size) + if platedstr == 'PLATED': + plated = ExcellonTool.PLATED_YES + elif platedstr == 'NON_PLATED': + plated = ExcellonTool.PLATED_NO + elif platedstr == 'OPTIONAL': + plated = ExcellonTool.PLATED_OPTIONAL + else: + plated = ExcellonTool.PLATED_UNKNOWN + + tool = ExcellonTool(None, number=toolid, diameter=size, plated=plated) self.tools[tool.number] = tool @@ -108,4 +121,66 @@ class ExcellonToolDefinitionParser(object): else: # Already in mm return value -
\ No newline at end of file + +def loads_rep(data, settings=None): + """ Read tool report information generated by PADS and return a map of tools + Parameters + ---------- + data : string + string containing Excellon Report file contents + + Returns + ------- + dict tool name: ExcellonTool + + """ + return ExcellonReportParser(settings).parse_raw(data) + +class ExcellonReportParser(object): + + # We sometimes get files with different encoding, so we can't actually + # match the text - the best we can do it detect the table header + header = re.compile(r'====\s+====\s+====\s+====\s+=====\s+===') + + def __init__(self, settings=None): + self.tools = {} + self.settings = settings + + self.found_header = False + + def parse_raw(self, data): + for line in StringIO(data): + self._parse(line.strip()) + + return self.tools + + def _parse(self, line): + + # skip empty lines and "comments" + if not line.strip(): + return + + if not self.found_header: + # Try to find the heaader, since we need that to be sure we understand the contents correctly. + if ExcellonReportParser.header.match(line): + self.found_header = True + + elif line[0] != '=': + # Already found the header, so we know to to map the contents + parts = line.split() + if len(parts) == 6: + toolid = int(parts[0]) + size = float(parts[1]) + if parts[2] == 'x': + plated = ExcellonTool.PLATED_YES + elif parts[2] == '-': + plated = ExcellonTool.PLATED_NO + else: + plated = ExcellonTool.PLATED_UNKNOWN + feedrate = int(parts[3]) + speed = int(parts[4]) + qty = int(parts[5]) + + tool = ExcellonTool(None, number=toolid, diameter=size, plated=plated, feed_rate=feedrate, rpm=speed) + + self.tools[tool.number] = tool
\ No newline at end of file |