add rest of altium-supported ipc-d-356 statements

This commit is contained in:
Hamilton Kibbe 2015-03-06 16:47:12 -05:00
parent f6dbe87c03
commit b5ce83beae

View file

@ -27,6 +27,9 @@ _NNAME = re.compile(r'^NNAME\d+$')
# Board Edge Coordinates
_COORD = re.compile(r'X?(?P<x>[\d\s]*)?Y?(?P<y>[\d\s]*)?')
_SM_FIELD = {'0': 'none', '1': 'primary side', '2': 'secondary side', '3': 'both'}
def read(filename):
""" Read data from filename and return an IPC_D_356
@ -81,8 +84,19 @@ class IPC_D_356(CamFile):
@property
def nets(self):
return list(set([rec.net_name for rec in self.test_records
if rec.net_name is not None]))
nets = []
for net in list(set([rec.net_name for rec in self.test_records
if rec.net_name is not None])):
adjacent_nets = set()
for record in self.adjacency_records:
if record.net == net:
adjacent_nets = adjacent_nets.update(record.adjacent_nets)
elif net in record.adjacent_nets:
adjacent_nets.add(record.net)
nets.append(IPC356_Net(net, adjacent_nets))
return nets
@property
def components(self):
@ -94,14 +108,17 @@ class IPC_D_356(CamFile):
return [rec.id for rec in self.test_records if rec.id == 'VIA']
@property
def board_outline(self):
outline = [stmt for stmt in self.statements if isinstance(stmt, IPC356_BoardEdge)]
if len(outline):
return outline[0].points
else:
return None
def outlines(self):
return [stmt for stmt in self.statements
if isinstance(stmt, IPC356_Outline)]
@property
def adjacency_records(self):
return [record for record in self.statements
if isinstance(record, IPC356_Adjacency)]
def render(self, ctx, layer='both', filename=None):
for p in self.primitives:
if layer == 'both' and p.layer in ('top', 'bottom', 'both'):
@ -182,12 +199,17 @@ class IPC_D_356_Parser(object):
record.net_name = self.nnames[record.net_name]
self.statements.append(record)
elif line[0:3] == '378':
# Conductor
self.statements.append(IPC356_Conductor.from_line(line, self.settings))
elif line[0:3] == '379':
# Net Adjacency Info
pass
# Net Adjacency
self.statements.append(IPC356_Adjacency.from_line(line))
elif line[0:3] == '389':
# Altium Board Edge Info
self.statements.append(IPC356_BoardEdge.from_line(line, self.settings))
# Outline
self.statements.append(IPC356_Outline.from_line(line, self.settings))
class IPC356_Comment(object):
@ -296,7 +318,8 @@ class IPC356_TestRecord(object):
if len(line) >= 74:
end = len(line) - 1 if len(line) < 75 else 74
record['soldermask_info'] = line[73:74].strip()
sm_info = line[73:74].strip()
record['soldermask_info'] = _SM_FIELD.get(sm_info)
if len(line) >= 76:
end = len(line) - 1 if len(line < 80) else 79
@ -309,13 +332,14 @@ class IPC356_TestRecord(object):
setattr(self, key, kwargs[key])
def __repr__(self):
return '<IPC-D-356 Test Record: Net: %s Type: %s>' % (self.net_name,
return '<IPC-D-356 %s Test Record: %s>' % (self.net_name,
self.feature_type)
class IPC356_BoardEdge(object):
class IPC356_Outline(object):
@classmethod
def from_line(cls, line, settings):
type = line[3:17].strip()
scale = 0.0001 if settings.units == 'inch' else 0.001
points = []
x = 0
@ -326,27 +350,78 @@ class IPC356_BoardEdge(object):
x = int(coord_dict['x']) if coord_dict['x'] is not '' else x
y = int(coord_dict['y']) if coord_dict['y'] is not '' else y
points.append((x * scale, y * scale))
return cls(points)
return cls(type, points)
def __init__(self, points):
def __init__(self, type, points):
self.type = type
self.points = points
def __repr__(self):
return '<IPC-D-356 Board Edge Definition>'
return '<IPC-D-356 %s Outline Definition>' % self.type
class IPC356_Conductor(object):
@classmethod
def from_line(cls, line, settings):
if line[0:3] != '378':
raise ValueError('Not a valid IPC-D-356 Conductor statement')
scale = 0.0001 if settings.units == 'inch' else 0.001
net_name = line[3:17].strip()
layer = int(line[19:21])
# Parse out aperture definiting
raw_aperture = line[22:].split()[0]
aperture_dict = _COORD.match(raw_aperture).groupdict()
x = 0
y = 0
x = int(aperture_dict['x']) * scale if aperture_dict['x'] is not '' else None
y = int(aperture_dict['y']) * scale if aperture_dict['y'] is not '' else None
aperture = (x, y)
# Parse out conductor shapes
shapes = []
coord_list = ' '.join(line[22:].split()[1:])
raw_shapes = coord_list.split('*')
for rshape in raw_shapes:
x = 0
y = 0
shape = []
coords = rshape.split()
for coord in coords:
coord_dict = _COORD.match(coord).groupdict()
x = int(coord_dict['x']) if coord_dict['x'] is not '' else x
y = int(coord_dict['y']) if coord_dict['y'] is not '' else y
shape.append((x * scale, y * scale))
shapes.append(tuple(shape))
return cls(net_name, layer, aperture, tuple(shapes))
def __init__(self, net_name, layer, aperture, shapes):
self.net_name = net_name
self.layer = layer
self.aperture = aperture
self.shapes = shapes
def __repr__(self):
return '<IPC-D-356 %s Conductor Record>' % self.net_name
class IPC356_Adjacency(object):
@classmethod
def from_line(cls, line):
nets = line.strip().split()[1:]
return cls(nets)
def __init__(self, nets):
self.nets = nets
if line[0:3] != '379':
raise ValueError('Not a valid IPC-D-356 Conductor statement')
nets = line[3:].strip().split()
return cls(nets[0], nets[1:])
def __init__(self, net, adjacent_nets):
self.net = net
self.adjacent_nets = adjacent_nets
def __repr__(self):
return '<IPC-D-356 Adjacency Record>'
return '<IPC-D-356 %s Adjacency Record>' % self.net
class IPC356_EndOfFile(object):
@ -358,3 +433,12 @@ class IPC356_EndOfFile(object):
def __repr__(self):
return '<IPC-D-356 EOF>'
class IPC356_Net(object):
def __init__(self, name, adjacent_nets):
self.name = name
self.adjacent_nets = set(adjacent_nets) if adjacent_nets is not None else set()
def __repr__(self):
return '<IPC-D-356 Net %s>' % self.name