import os.path import shlex from ee.kicad.model import * # Reads all .sch files referenced from the given .sch file. def read_schematics(path) -> Schematics: def read(schematic_path): schematic = read_schematic(schematic_path) schematics = [schematic] for sheet in schematic.sheets: p = os.path.join(os.path.dirname(schematic_path), sheet.path) children = read(p) schematics.extend(children) return schematics return Schematics(read(path)) # Reads a single .sch file. All references to other sheets are read and available as schematic.sheets def read_schematic(path: str) -> Schematic: path_basename = os.path.basename(path) schematic = Schematic(path) def descr_section(lines): # print("descr_section: len={}".format(len(lines))) pass def sheet_section(lines): name = None sheet_path = None for line in lines: parts = shlex.split(line) if len(parts) < 2: continue if parts[0] == "F0": name = parts[1] elif parts[0] == "F1": sheet_path = parts[1] schematic.add_sheet(Sheet(name, sheet_path)) def comp_section(lines): # print("comp_section: len={}".format(len(lines))) timestamp = None position = None library = None symbol = None unit = None ref = 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(path_basename, line)) if parts[0] == "L" and len(parts) == 3: symbol = Symbol(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] == "AR": pass elif parts[0] == "F" and len(parts) >= 10: # orientation = 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 # text_attrs = 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(path_basename, line)) schematic.add_component(Component(position, timestamp, library, symbol, unit, ref, fields)) def load(f): header = f.readline() line_number = 1 if "EESchema Schematic File Version" not in header: raise EeException("Not a KiCAD schematic file.") section_name = None section = [] seen_end = False while not seen_end: line = f.readline() line_number = line_number + 1 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) elif section_name == "$Sheet": sheet_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 parts[0:3] == ["Entry", "Wire", "Line"] or \ parts[0:2] == ["Text", "Label"] or \ parts[0:2] == ["Text", "GLabel"] or \ parts[0:2] == ["Text", "Notes"] or \ parts[0:2] == ["Text", "HLabel"] or \ parts[0:3] == ["Wire", "Notes", "Line"] or \ parts[0:3] == ["Wire", "Wire", "Line"] or \ parts[0:3] == ["Wire", "Bus", "Line"]: f.readline() # ignore the next line for now line_number = line_number + 1 elif line.startswith("NoConn "): pass elif parts[0:2] == ["Connection", "~"]: pass else: # print("line={}, len={}, wat={}".format(line, len(line), parts[0:3])) raise EeException("{}:{}: Bad line: {}".format(path, line_number, line)) return schematic with open(path) as file: return load(file)