diff options
Diffstat (limited to 'src/ee/kicad')
-rw-r--r-- | src/ee/kicad/__init__.py | 246 | ||||
-rw-r--r-- | src/ee/kicad/model.py | 109 | ||||
-rw-r--r-- | src/ee/kicad/read_schematic.py | 129 |
3 files changed, 248 insertions, 236 deletions
diff --git a/src/ee/kicad/__init__.py b/src/ee/kicad/__init__.py index 9f6394d..ab0dede 100644 --- a/src/ee/kicad/__init__.py +++ b/src/ee/kicad/__init__.py @@ -1,238 +1,12 @@ 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 +from ee.kicad.read_schematic import read_schematic +from .model import * + +__all__ = [ + "Position", + "ComponentField", + "Component", + "Schematic", + "read_schematic", +] diff --git a/src/ee/kicad/model.py b/src/ee/kicad/model.py new file mode 100644 index 0000000..3c6001c --- /dev/null +++ b/src/ee/kicad/model.py @@ -0,0 +1,109 @@ +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)) diff --git a/src/ee/kicad/read_schematic.py b/src/ee/kicad/read_schematic.py new file mode 100644 index 0000000..6fa9686 --- /dev/null +++ b/src/ee/kicad/read_schematic.py @@ -0,0 +1,129 @@ +import shlex +from ee.kicad.model import * + +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 + |