diff --git a/.gitignore b/.gitignore index a3a21b8..552ccc3 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,5 @@ Temporary Items *.OutJob *.SchDoc.Zip *.OutJob.Zip +*.NET +*.PcbDoc.htm diff --git a/parse.py b/parse.py index 2d44875..056833a 100644 --- a/parse.py +++ b/parse.py @@ -3,6 +3,9 @@ import olefile import re import json import copy +import math +import logging as lg +lg.basicConfig(level=lg.DEBUG) def parse(input, format, **kwargs): fullPath = input @@ -45,6 +48,13 @@ def parse(input, format, **kwargs): return schematic def determine_hierarchy(schematic): + """Convert a dict containing a flat list of records + into a dict of records in a hierarchy + + :param schematic: dict with 'header' and 'records' populated + :return: the input dict with 'records' assembled into parent/child hierarchy + """ + # prep a scratchpad copy of records to build hierarchy from records_copy = copy.deepcopy(schematic["records"]) schematic["hierarchy"] = [] @@ -75,8 +85,143 @@ def determine_parts_list(schematic): return parts_list def determine_net_list(schematic): - print('NOT IMPLEMENTED') + _, wires = find_record(schematic, key="RECORD", value="27") + _, pins = find_record(schematic, key="RECORD", value="2") + _, labels = find_record(schematic, key="RECORD", value="25") + _, power_ports = find_record(schematic, key="RECORD", value="17") + devices = wires + pins + labels + power_ports + + p = re.compile('^(?PX)(?P\d+)$') + for device in devices: + # if a Pin, do some fancy geometry math + if device["RECORD"] == "2": + rotation = (int(device["PINCONGLOMERATE"]) & 0x03) * 90 + device['coords'] = [[ + int(int(device['LOCATION.X']) + math.cos(rotation / 180 * math.pi) * int(device['PINLENGTH'])), + int(int(device['LOCATION.Y']) + math.sin(rotation / 180 * math.pi) * int(device['PINLENGTH'])) + ]] + # if a Wire, follow inconsistent location key names (X1 vs LOCATION.X, etc..) + elif device["RECORD"] == "27": + coord_name_matches = [x for x in [p.match(key) for key in device.keys()] if x] + device['coords'] = [ ( int(device['X' + match.group('index')]) , int(device['Y' + match.group('index')]) ) + for match in coord_name_matches ] + # everything else, just convert the location values to ints + else: + device['coords'] = [(int(device['LOCATION.X']), int(device['LOCATION.Y']))] + + nets = [] + for device in devices: + if device["index"] not in [d['index'] for net in nets for d in net['devices']]: + net = {'name': None, + 'devices': find_connected_wires(device, devices, [], schematic)} + nets.append(net) + + for net in nets: + net['devices'].sort(key=lambda k: k['index']) + if not net['name']: + net['name'] = next(iter(d['TEXT'] for d in net['devices'] if ((d['RECORD'] == '17') or (d['RECORD'] == '25'))), None) + + if not net['name']: + naming_pin = next(iter(d for d in net['devices'] if d['RECORD'] == '2'), None) + parent = next(iter(find_record(schematic, key="index", value=int(naming_pin['OWNERINDEX']))[1]), None) if naming_pin else None + net['name'] = next(iter('Net' + r['TEXT'] for r in parent['children'] if (r['RECORD'] == '34')), None) if parent else None + + schematic["nets"] = nets + return schematic + +def find_record(schematic, key, value, record=None, visited=None, found=None): + lg.debug("finding records where: {0} = {1}".format(key, value)) + + if visited == None: + visited = [] + if found == None: + found = [] + if record == None: + for record in schematic['records']: + visited, found = find_record(schematic, key, value, record=record, visited=visited, found=found) + else: + if record['index'] not in [r['index'] for r in visited]: + visited.append(record) + + if key in record.keys(): + if record[key] == value: + found.append(record) + + if "children" in record.keys(): + for child_record in record["children"]: + visited, found = find_record(schematic, key, value, record=child_record, visited=visited, found=found) + + return visited, found + +def find_connected_wires(wire, devices, visited, schematic): + neighbors = find_neighbors(wire, devices, schematic) + lg.debug('entering: {0}'.format(wire['index'])) + + if wire['index'] not in [w['index'] for w in visited]: + lg.debug('adding: {0} to {1}'.format(wire['index'], [w['index'] for w in visited])) + visited.append(wire) + + for neighbor in neighbors: + lg.debug('trying: {0} of {1}'.format(neighbor['index'], [x['index'] for x in neighbors])) + visited = find_connected_wires(neighbor, devices, visited, schematic) + lg.debug('visited = {0}'.format([w['index'] for w in visited])) + else: + lg.debug('skipping: {0} already in list {1}'.format(wire['index'], [w['index'] for w in visited])) + + lg.debug('returning: {0}'.format(wire['index'])) + return visited + +def find_neighbors(wire, devices, schematic): + all_wires = devices + other_wires = [record for record in all_wires if record != wire] + + neighbors = [] + for other_wire in other_wires: + if is_connected(wire, other_wire): + neighbors.append(other_wire) + + return neighbors + +def is_connected(wire_a, wire_b): + + if wire_a["RECORD"] == "27": + a_line_segments = [(wire_a['coords'][i], wire_a['coords'][i + 1]) for i in + range(len(wire_a['coords']) - 1)] + else: + a_line_segments = [(wire_a['coords'][0], wire_a['coords'][0])] + + if wire_b["RECORD"] == "27": + b_line_segments = [(wire_b['coords'][i], wire_b['coords'][i + 1]) for i in + range(len(wire_b['coords']) - 1)] + else: + b_line_segments = [(wire_b['coords'][0], wire_b['coords'][0])] + + # check if any vertices in wire_a lie on wire_b + for vertex in [vx for line in a_line_segments for vx in line]: + for b_line in b_line_segments: + b_xs = sorted(list(zip(*b_line))[0]) + b_ys = sorted(list(zip(*b_line))[1]) + + if ((min(b_xs) <= vertex[0] <= max(b_xs)) + and (min(b_ys) <= vertex[1] <= max(b_ys))): + return True + + # check if any vertices in wire_b lie on wire_a + for vertex in [vx for line in b_line_segments for vx in line]: + for a_line in a_line_segments: + a_xs = sorted(list(zip(*a_line))[0]) + a_ys = sorted(list(zip(*a_line))[1]) + + if ((min(a_xs) <= vertex[0] <= max(a_xs)) + and (min(a_ys) <= vertex[1] <= max(a_ys))): + return True + + # check if both items are Power Ports with the same TEXT value + if ( wire_a["RECORD"] == "17" ) and ( wire_b["RECORD"] == "17" ) and ( wire_a["TEXT"] == wire_b["TEXT"] ): + return True + + return False def main(args): schematic = parse(**vars(args))