Update excellon statements/ExcellonFile
This commit is contained in:
parent
e565624b81
commit
08253b40f6
4 changed files with 173 additions and 51 deletions
|
|
@ -92,7 +92,7 @@ class CncFile(object):
|
|||
decimal digits)
|
||||
"""
|
||||
|
||||
def __init__(self, settings=None, filename=None):
|
||||
def __init__(self, statements=None, settings=None, filename=None):
|
||||
if settings is not None:
|
||||
self.notation = settings['notation']
|
||||
self.units = settings['units']
|
||||
|
|
@ -103,6 +103,7 @@ class CncFile(object):
|
|||
self.units = 'inch'
|
||||
self.zero_suppression = 'trailing'
|
||||
self.format = (2, 5)
|
||||
self.statements = statements if statements is not None else []
|
||||
self.filename = filename
|
||||
|
||||
@property
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@ class ExcellonFile(CncFile):
|
|||
either 'inch' or 'metric'.
|
||||
|
||||
"""
|
||||
def __init__(self, tools, hits, settings, filename=None):
|
||||
super(ExcellonFile, self).__init__(settings, filename)
|
||||
def __init__(self, statements, tools, hits, settings, filename=None):
|
||||
super(ExcellonFile, self).__init__(statements, settings, filename)
|
||||
self.tools = tools
|
||||
self.hits = hits
|
||||
|
||||
|
|
@ -98,14 +98,20 @@ class ExcellonParser(object):
|
|||
with open(filename, 'r') as f:
|
||||
for line in f:
|
||||
self._parse(line)
|
||||
return ExcellonFile(self.tools, self.hits, self._settings(), filename)
|
||||
return ExcellonFile(self.statements, self.tools, self.hits, self._settings(), filename)
|
||||
|
||||
def dump(self, filename):
|
||||
if self.ctx is not None:
|
||||
self.ctx.dump(filename)
|
||||
|
||||
def _parse(self, line):
|
||||
if 'M48' in line:
|
||||
zs = self._settings()['zero_suppression']
|
||||
fmt = self._settings()['format']
|
||||
|
||||
if line[0] == ';':
|
||||
self.statements.append(CommentStmt.from_excellon(line))
|
||||
|
||||
elif line[:3] == 'M48':
|
||||
self.statements.append(HeaderBeginStmt())
|
||||
self.state = 'HEADER'
|
||||
|
||||
|
|
@ -114,56 +120,59 @@ class ExcellonParser(object):
|
|||
if self.state == 'HEADER':
|
||||
self.state = 'DRILL'
|
||||
|
||||
elif 'M95' in line:
|
||||
elif line[:3] == 'M95':
|
||||
self.statements.append(HeaderEndStmt())
|
||||
if self.state == 'HEADER':
|
||||
self.state = 'DRILL'
|
||||
|
||||
elif 'G00' in line:
|
||||
elif line[:3] == 'G00':
|
||||
self.state = 'ROUT'
|
||||
|
||||
elif 'G05' in line:
|
||||
elif line[:3] == 'G05':
|
||||
self.state = 'DRILL'
|
||||
|
||||
if 'INCH' in line or line.strip() == 'M72':
|
||||
self.units = 'inch'
|
||||
|
||||
elif 'METRIC' in line or line.strip() == 'M71':
|
||||
self.units = 'metric'
|
||||
|
||||
if 'LZ' in line:
|
||||
self.zeros = 'L'
|
||||
|
||||
elif 'TZ' in line:
|
||||
self.zeros = 'T'
|
||||
|
||||
if 'ICI' in line and 'ON' in line or line.strip() == 'G91':
|
||||
self.notation = 'incremental'
|
||||
|
||||
if 'ICI' in line and 'OFF' in line or line.strip() == 'G90':
|
||||
self.notation = 'incremental'
|
||||
|
||||
zs = self._settings()['zero_suppression']
|
||||
fmt = self._settings()['format']
|
||||
|
||||
elif ('INCH' in line or 'METRIC' in line) and ('LZ' in line or 'TZ' in line):
|
||||
stmt = UnitStmt.from_excellon(line)
|
||||
self.units = stmt.units
|
||||
self.zero_suppression = stmt.zero_suppression
|
||||
self.statements.append(stmt)
|
||||
|
||||
elif line[:3] == 'M71' or line [:3] == 'M72':
|
||||
stmt = MeasuringModeStmt.from_excellon(line)
|
||||
self.units = stmt.units
|
||||
self.statements.append(stmt)
|
||||
|
||||
elif line[:3] == 'ICI':
|
||||
stmt = IncrementalModeStmt.from_excellon(line)
|
||||
self.notation = 'incremental' if stmt.mode == 'on' else 'absolute'
|
||||
self.statements.append(stmt)
|
||||
|
||||
# tool definition
|
||||
if line[0] == 'T' and self.state == 'HEADER':
|
||||
tool = ExcellonTool.from_line(line, self._settings())
|
||||
elif line[0] == 'T' and self.state == 'HEADER':
|
||||
tool = ExcellonTool.from_excellon(line, self._settings())
|
||||
self.tools[tool.number] = tool
|
||||
self.statements.append(tool)
|
||||
|
||||
elif line[0] == 'T' and self.state != 'HEADER':
|
||||
self.active_tool = self.tools[int(line.strip().split('T')[1])]
|
||||
|
||||
if line[0] in ['X', 'Y']:
|
||||
x = None
|
||||
y = None
|
||||
if line[0] == 'X':
|
||||
splitline = line.strip('X').split('Y')
|
||||
x = parse_gerber_value(splitline[0].strip(), fmt, zs)
|
||||
if len(splitline) == 2:
|
||||
y = parse_gerber_value(splitline[1].strip(), fmt, zs)
|
||||
else:
|
||||
y = parse_gerber_value(line.strip(' Y'), fmt, zs)
|
||||
stmt = ToolSelectionStmt.from_excellon(line)
|
||||
self.active_tool self.tools[stmt.tool]
|
||||
#self.active_tool = self.tools[int(line.strip().split('T')[1])]
|
||||
self.statements.append(statement)
|
||||
|
||||
elif line[0] in ['X', 'Y']:
|
||||
stmt = CoordinateStmt.from_excellon(line, fmt, zs)
|
||||
x = stmt.x
|
||||
y = stmt.y
|
||||
self.statements.append(stmt)
|
||||
#x = None
|
||||
#y = None
|
||||
#if line[0] == 'X':
|
||||
# splitline = line.strip('X').split('Y')
|
||||
# x = parse_gerber_value(splitline[0].strip(), fmt, zs)
|
||||
# if len(splitline) == 2:
|
||||
# y = parse_gerber_value(splitline[1].strip(), fmt, zs)
|
||||
#else:
|
||||
# y = parse_gerber_value(line.strip(' Y'), fmt, zs)
|
||||
if self.notation == 'absolute':
|
||||
if x is not None:
|
||||
self.pos[0] = x
|
||||
|
|
@ -180,6 +189,9 @@ class ExcellonParser(object):
|
|||
if self.ctx is not None:
|
||||
self.ctx.drill(self.pos[0], self.pos[1],
|
||||
self.active_tool.diameter)
|
||||
|
||||
else:
|
||||
self.statements.append(UnknownStmt.from_excellon(line))
|
||||
|
||||
def _settings(self):
|
||||
return FileSettings(units=self.units, format=self.format,
|
||||
|
|
|
|||
|
|
@ -18,13 +18,21 @@
|
|||
from .utils import write_gerber_value
|
||||
|
||||
|
||||
__all__ = ['ExcellonTool', 'CommentStmt', 'HeaderBeginStmt', 'HeaderEndStmt',
|
||||
__all__ = ['ExcellonTool', 'ToolSelectionStatment', 'CoordinateStmt',
|
||||
'CommentStmt', 'HeaderBeginStmt', 'HeaderEndStmt',
|
||||
'RewindStopStmt', 'EndOfProgramStmt', 'UnitStmt',
|
||||
'IncrementalModeStmt', 'VersionStmt', 'FormatStmt', 'LinkToolStmt',
|
||||
'MeasuringModeStmt', 'UnknownStmt',
|
||||
]
|
||||
|
||||
|
||||
class ExcellonStatement(object):
|
||||
""" Excellon Statement abstract base class
|
||||
"""
|
||||
@classmethod
|
||||
def from_excellon(cls, line):
|
||||
pass
|
||||
|
||||
def to_excellon(self):
|
||||
pass
|
||||
|
||||
|
|
@ -74,7 +82,7 @@ class ExcellonTool(ExcellonStatement):
|
|||
"""
|
||||
|
||||
@classmethod
|
||||
def from_line(cls, line, settings):
|
||||
def from_excellon(cls, line, settings):
|
||||
""" Create a Tool from an excellon file tool definition line.
|
||||
|
||||
Parameters
|
||||
|
|
@ -155,7 +163,62 @@ class ExcellonTool(ExcellonStatement):
|
|||
return '<ExcellonTool %d: %0.3f%s dia.>' % (self.number, self.diameter, unit)
|
||||
|
||||
|
||||
class ToolSelectionStatment(ExcellonStatement):
|
||||
|
||||
@classmethod
|
||||
def from_excellon(cls, line):
|
||||
line = line.strip()[1:]
|
||||
compensation_index = None
|
||||
tool = int(line[:2])
|
||||
if len(line) > 2:
|
||||
compensation_index = int(line[2:])
|
||||
return cls(tool, compensation_index)
|
||||
|
||||
def __init__(self, tool, compensation_index=None):
|
||||
tool = int(tool)
|
||||
compensation_index = int(compensation_index) if compensation_index else None
|
||||
self.tool = tool
|
||||
self.compensation_index = compensation_index
|
||||
|
||||
def to_excellon(self):
|
||||
stmt = 'T%02d' % self.tool
|
||||
if self.compensation_index is not None:
|
||||
stmt += '%02d' % self.compensation_index
|
||||
return stmt
|
||||
|
||||
|
||||
class CoordinateStmt(ExcellonStatement):
|
||||
|
||||
def from_excellon(cls, line, format=(2, 5), zero_suppression='trailing'):
|
||||
x = None
|
||||
y = None
|
||||
if line[0] == 'X':
|
||||
splitline = line.strip('X').split('Y')
|
||||
x = parse_gerber_value(splitline[0].strip(), format, zero_suppression)
|
||||
if len(splitline) == 2:
|
||||
y = parse_gerber_value(splitline[1].strip(), format, zero_suppression)
|
||||
else:
|
||||
y = parse_gerber_value(line.strip(' Y'), format, zero_suppression)
|
||||
return cls(x, y)
|
||||
|
||||
def __init__(self, x=None, y=None):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
def to_excellon(self):
|
||||
stmt = ''
|
||||
if self.x is not None:
|
||||
stmt.append('X%s' % write_gerber_value(self.x))
|
||||
if self.y is not None:
|
||||
stmt.append('Y%s' % write_gerber_value(self.y))
|
||||
return stmt
|
||||
|
||||
|
||||
class CommentStmt(ExcellonStatement):
|
||||
|
||||
def from_excellon(self, line):
|
||||
return cls(line.strip().lstrip(';'))
|
||||
|
||||
def __init__(self, comment):
|
||||
self.comment = comment
|
||||
|
||||
|
|
@ -206,6 +269,12 @@ class EndOfProgramStmt(ExcellonStatement):
|
|||
|
||||
class UnitStmt(ExcellonStatement):
|
||||
|
||||
@classmethod
|
||||
def from_excellon(cls, line):
|
||||
units = 'inch' if 'INCH' in line else 'metric'
|
||||
zero_suppression = 'trailing' if 'LZ' in line else 'leading'
|
||||
return cls(units, zero_suppression)
|
||||
|
||||
def __init__(self, units='inch', zero_suppression='trailing'):
|
||||
self.units = units.lower()
|
||||
self.zero_suppression = zero_suppression
|
||||
|
|
@ -217,6 +286,10 @@ class UnitStmt(ExcellonStatement):
|
|||
|
||||
class IncrementalModeStmt(ExcellonStatement):
|
||||
|
||||
@classmethod
|
||||
def from_excellon(cls, line):
|
||||
return cls('off') if 'OFF' in line else cls('on')
|
||||
|
||||
def __init__(self, mode='off'):
|
||||
if mode.lower() not in ['on', 'off']:
|
||||
raise ValueError('Mode may be "on" or "off")
|
||||
|
|
@ -228,16 +301,33 @@ class IncrementalModeStmt(ExcellonStatement):
|
|||
|
||||
class VersionStmt(ExcellonStatement):
|
||||
|
||||
@classmethod
|
||||
def from_excellon(cls, line):
|
||||
version = int(line.split(',')[1])
|
||||
return cls(version)
|
||||
|
||||
def __init__(self, version=1):
|
||||
self.version = int(version)
|
||||
version = int(version)
|
||||
if version not in [1, 2]:
|
||||
raise ValueError('Valid versions are 1 or 2'
|
||||
self.version = version
|
||||
|
||||
def to_excellon(self):
|
||||
return 'VER,%d' % self.version
|
||||
|
||||
|
||||
class FormatStmt(ExcellonStatement):
|
||||
|
||||
@classmethod
|
||||
def from_excellon(cls, line):
|
||||
fmt = int(line.split(',')[1])
|
||||
return cls(fmt)
|
||||
|
||||
def __init__(self, format=1):
|
||||
self.format = int(format)
|
||||
format = int(format)
|
||||
if format not in [1, 2]:
|
||||
raise ValueError('Valid formats are 1 or 2')
|
||||
self.format = format
|
||||
|
||||
def to_excellon(self):
|
||||
return 'FMAT,%d' % self.format
|
||||
|
|
@ -245,6 +335,11 @@ class FormatStmt(ExcellonStatement):
|
|||
|
||||
class LinkToolStmt(ExcellonStatement):
|
||||
|
||||
@classmethod
|
||||
def from_excellon(cls, line):
|
||||
linked = [int(tool) for tool in line.strip().split('/')]
|
||||
return cls(linked)
|
||||
|
||||
def __init__(self, linked_tools):
|
||||
self.linked_tools = [int(x) for x in linked_tools]
|
||||
|
||||
|
|
@ -253,14 +348,29 @@ class LinkToolStmt(ExcellonStatement):
|
|||
|
||||
|
||||
class MeasuringModeStmt(ExcellonStatement):
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_excellon(cls, line):
|
||||
return cls('inch') if 'M72' in line else cls('metric')
|
||||
|
||||
def __init__(self, units='inch'):
|
||||
units = units.lower()
|
||||
if units not in ['inch', 'metric']:
|
||||
raise ValueError('units must be "inch" or "metric"')
|
||||
self.units = units
|
||||
|
||||
|
||||
def to_excellon(self):
|
||||
return 'M72' if self.units == 'inch' else 'M71'
|
||||
|
||||
|
||||
class UnknownStmt(ExcellonStatement):
|
||||
|
||||
@classmethod
|
||||
def from_excellon(cls, line):
|
||||
return cls(line)
|
||||
|
||||
def __init__(self, stmt):
|
||||
self.stmt = stmt
|
||||
|
||||
def to_excellon(self):
|
||||
return self.stmt
|
||||
|
|
|
|||
|
|
@ -68,8 +68,7 @@ class GerberFile(CncFile):
|
|||
|
||||
"""
|
||||
def __init__(self, statements, settings, filename=None):
|
||||
super(GerberFile, self).__init__(settings, filename)
|
||||
self.statements = statements
|
||||
super(GerberFile, self).__init__(statements, settings, filename)
|
||||
|
||||
@property
|
||||
def comments(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue