diff options
Diffstat (limited to 'src/ee/kicad')
-rw-r--r-- | src/ee/kicad/__init__.py | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/src/ee/kicad/__init__.py b/src/ee/kicad/__init__.py index e69de29..9f6394d 100644 --- a/src/ee/kicad/__init__.py +++ b/src/ee/kicad/__init__.py @@ -0,0 +1,238 @@ +import re +from ee import EeException +import shlex +from typing import List + +class Position(object): + def __init__(self, x, y): + self._x = x + self._y = y + + @property + def x(self): + return _x + + @property + def y(self): + return _y + +class ComponentField(object): + names = ["Name", "Reference", "Value", "Footprint", "Datasheet"] + def __init__(self, index, name, value, position): + self._index = index + self._name = name if index >= len(ComponentField.names) else ComponentField.names[index] + self._value = value + self._position = position + + @property + def name(self): + return self._name + + @property + def value(self): + return self._value + + @property + def position(self): + return self._position + +class Component(object): + def __init__(self, position, timestamp, library, name, unit, ref, fields): + self._position = position + self._timestamp = timestamp + self._library = library + self._name = name + self._unit = unit + self._ref = ref + self._fields = fields # type List[ComponentField] + + @property + def unit(self): + return self._unit + + @property + def ref(self): + return self._ref + + @property + def fields(self) -> List[ComponentField]: + return list(self._fields) + + @property + def named_fields(self): + return [f for f in self._fields if f.name] + +class Sheet(object): + def __init__(self): + self._components = [] + pass + + @property + def components(self): + return frozenset(self._components) + + def add_component(self, component): + self._components.append(component) + +class Library(object): + def __init__(self, name): + self.name = name + +class Schematic(object): + def __init__(self): + self._libraries = set() + self._sheets = [Sheet()] + pass + + @property + def first_sheet(self): + return self._sheets[0] + + @property + def libraries(self): + return frozenset(self._libraries) + + def add_library(self, library): + self._libraries.add(Library(library)) + + # Getters + @property + def components(self): + a = [] + for s in self._sheets: + for c in s.components: + a.append(c) + return a + + def get_component(self, ref, unit = 1): + for c in self.components: + if c.ref == ref and unit == unit: + return c + + raise KeyError("No such component: {}".format(ref)) + +def read_schematic(path): + schematic = Schematic() + sheet = None + + def descr_section(lines): + print("descr_section: len={}".format(len(lines))) + pass + + def comp_section(lines): + print("comp_section: len={}".format(len(lines))) + timestamp = None + position = None + library = None + name = None + unit = None + fields = [] + + extra_lines = 0 + for line in lines: + if line.startswith((' ', '\t')): + parts = line.strip().split(" ") + if extra_lines == 0: + pass + else: + # rotation/mirroring, x1, y1, x2, y2 + pass + extra_lines += 1 + else: + parts = shlex.split(line) + if len(parts) < 3: + raise EeException("Bad component line: {}".format(line)) + + if parts[0] == "L" and len(parts) == 3: + name = parts[1] + ref = parts[2] + elif parts[0] == "U" and len(parts) == 4: + unit = int(parts[1]) + # parts[2] == body style + timestamp = parts[3] + elif parts[0] == "P" and len(parts) == 3: + position = Position(int(parts[1]), int(parts[2])) + elif parts[0] == "F" and len(parts) >= 10: + oritentation = parts[3] + pos = Position(int(parts[4]), int(parts[5])) + value = parts[2] + size = parts[6] + attributes = parts[7] + justify = parts[8] # One of L, R, C + textAttrs = parts[9] # tree characters, + # 0: T=top justify, B=bottom justify, C=center + # 1: I=italics, N=normal + # 2: B=bold, N=normal + field_name = parts[10] if len(parts) > 10 else None + idx = int(parts[1]) + if len(fields) != idx: + raise EeException("Bad index: {}, expected={}. component={}, line={}".format(idx, len(fields), ref, line)) + fields.append(ComponentField(idx, field_name, value, pos)) + else: + raise EeException("Bad component field: '{}'".format(line)) + + sheet.add_component(Component(position, timestamp, library, name, unit, ref, fields)) + + with open(path) as f: + header = f.readline() + if not "EESchema Schematic File Version" in header: + raise EeException("Not a KiCAD schematic file.") + + sheet = schematic.first_sheet + + section_name = None + section = [] + + seen_end = False + while not seen_end: + line = f.readline() + if not line: + break + + line = line.rstrip() + + if section_name: + if line.startswith("$End"): + print("END SECTION: {}".format(section_name)) + if section_name == "$Comp": + comp_section(section) + elif section_name == "$Descr": + descr_section(section) + else: + if line == "$EndSCHEMATIC": + seen_end = True + break + section_name = None + else: + section.append(line) + else: + parts = line.split(" ") + if line.startswith("EELAYER "): + pass # Legacy, ignored + elif line.startswith("LIBS:"): + schematic.add_library(line[5:]) + elif line.startswith("$"): + section_name = parts[0] + print("SECTION: {}".format(section_name)) + section = [] + elif line == "Entry Wire Line": + f.readline() # ignore the next line for now + elif line.startswith("Text Label "): + f.readline() # ignore the next line for now + elif line.startswith("Text Notes "): + f.readline() # ignore the next line for now + elif line.startswith("NoConn "): + pass + elif parts[0:3] == ["Wire", "Notes", "Line"]: + f.readline() # ignore the next line for now + elif parts[0:3] == ["Wire", "Wire", "Line"]: + f.readline() # ignore the next line for now + elif parts[0:3] == ["Wire", "Bus", "Line"]: + f.readline() # ignore the next line for now + elif parts[0:2] == ["Connection", "~"]: + pass + else: + print("line={}, len={}, wat={}".format(line, len(line), parts[0:3])) + raise EeException("Bad line: {}".format(line)) + + return schematic |