From 102614dc8fe2f5aefd0fd92c1b6e48107a9629b0 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Tue, 12 Dec 2017 12:02:29 +0100 Subject: o Adding a kicad-make-pos tool. --- src/ee/_utils.py | 6 +++++ src/ee/kicad/__init__.py | 18 ++++++++------ src/ee/kicad/pcb/__init__.py | 41 ++++++++++++++++++++++++++++--- src/ee/tools/kicad_make_pos.py | 56 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 src/ee/tools/kicad_make_pos.py (limited to 'src') diff --git a/src/ee/_utils.py b/src/ee/_utils.py index 598bca1..9df6126 100644 --- a/src/ee/_utils.py +++ b/src/ee/_utils.py @@ -10,3 +10,9 @@ def ensure_has_columns(df: pd.DataFrame, columns: List[str]): df.insert(0, column=c, value=pd.Series()) # print("df={}".format(df.columns.tolist())) return df + +def run_filters(filters, obj): + for f in filters: + if not f(obj): + return False + return True diff --git a/src/ee/kicad/__init__.py b/src/ee/kicad/__init__.py index 8d5acd1..1492fc4 100644 --- a/src/ee/kicad/__init__.py +++ b/src/ee/kicad/__init__.py @@ -3,6 +3,7 @@ from typing import Any from ee import EeException from ee.kicad.read_schematic import read_schematic, read_schematics from ee.kicad.to_bom import to_bom, to_bom_xml +from .._utils import run_filters from .model import * __all__ = [ @@ -17,17 +18,20 @@ __all__ = [ "to_pandas", ] +def parse_ref(ref): + m = parse_ref.r.match(ref) + if not m: + return + g = m.groups() +# print("groups={}".format(g)) + return (g[0], None if g[1] == "?" else int(g[1])) + +parse_ref.r = re.compile("([^0-9]+)([0-9]+|\?)$") def to_pandas(obj: Any, **kwarg): import pandas import numpy as np - def run_filter(filters, obj): - for f in filters: - if not f(obj): - return False - return True - def to_pandas_schematics(ss: Schematics): dfs = [to_pandas_schematic(schematic) for schematic in ss.schematics] @@ -65,7 +69,7 @@ def to_pandas(obj: Any, **kwarg): if not include_flg: filters.append(lambda c: c.ref_type != "#FLG") - data = [make_dict(c) for c in components if run_filter(filters, c)] + data = [make_dict(c) for c in components if run_filters(filters, c)] columns = set([key for row in data for key in list(row)]) - set(special_fields) columns = special_fields + list(columns) diff --git a/src/ee/kicad/pcb/__init__.py b/src/ee/kicad/pcb/__init__.py index 10350c3..679c5ea 100644 --- a/src/ee/kicad/pcb/__init__.py +++ b/src/ee/kicad/pcb/__init__.py @@ -1,4 +1,5 @@ from .. import sexpr +from ..._utils import run_filters def auto_str(cls): def __str__(self): @@ -25,16 +26,32 @@ class Module(object): for k, v in kwargs.items(): setattr(self, k, v) + self.fp_texts = self.fp_texts or [] + + def filter_fp_text(self, kind = None): + filters = [] + + if kind: + filters.append(lambda fp_text: fp_text.kind == kind) + + return (fp_text for fp_text in self.fp_texts if run_filters(filters, fp_text)) + @auto_str class Pad(object): def __init__(self, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) +@auto_str +class FpText(object): + def __init__(self, **kwargs): + for k, v in kwargs.items(): + setattr(self, k, v) + def parse(path): count = 0 p = sexpr.parse(path) - p = sexpr.logging_parser(p) + #p = sexpr.logging_parser(p) (event, token) = next(p) assert event == sexpr.EVENT_LPAREN @@ -111,10 +128,12 @@ def parse(path): def _parse_module(): pads = [] + fp_texts = [] args = {} args["footprint"] = _parse_text() args["pads"] = pads + args["fp_texts"] = fp_texts (event, token) = next(p) while event == sexpr.EVENT_TEXT: @@ -128,6 +147,8 @@ def parse(path): args[token] = _parse_at() elif token == "pad": pads.append(_parse_pad()) + elif token == "fp_text": + fp_texts.append(_parse_fp_text()) else: _consume() (event, token) = next(p) @@ -155,6 +176,21 @@ def parse(path): return Pad(**args) + def _parse_fp_text(): + args = { + "kind": _parse_text(), + "value": _parse_text(), + } + (event, token) = next(p) + while event == sexpr.EVENT_LPAREN: + (event, token) = next(p) + if token == "at": + args[token] = _parse_at() + if token == "layer": + args[token] = _parse_text() + else: + _consume() + return FpText(**args) def _parse_at(): x = _parse_text(to=float) @@ -163,6 +199,5 @@ def parse(path): return (x, y, rot or 0) kicad_pcb = _parse_kicad_pcb() -# (event, token) = next(p) -# assert event == sexpr.EVENT_END + assert next(p, None) == None return kicad_pcb diff --git a/src/ee/tools/kicad_make_pos.py b/src/ee/tools/kicad_make_pos.py new file mode 100644 index 0000000..cd381a5 --- /dev/null +++ b/src/ee/tools/kicad_make_pos.py @@ -0,0 +1,56 @@ +import sys +import argparse +import csv +from . import mk_parents +from ..kicad import pcb, parse_ref +from .._utils import run_filters + +parser = argparse.ArgumentParser(description="Create a pick and place file from a KiCAD schematic") + +parser.add_argument("--kicad-pcb", + required=True, + dest="pcb", + metavar="PCB", + help="The pcb to read") + +parser.add_argument("--out", + metavar="FILE", + help="The output file") + +args = parser.parse_args() + +pcb = pcb.parse(args.pcb) + +def run(stream): + rows = [] + for m in pcb.modules: + row = {} + ref_fp_text = next(m.filter_fp_text(kind = "reference"), None) + row["ref"] = ref= ref_fp_text and ref_fp_text.value + row["r"] = parse_ref(ref) + value_fp_text = next(m.filter_fp_text(kind = "value"), None) + row["value"] = value_fp_text and value_fp_text.value + (row["library"], row["footprint"]) = m.footprint.split(":") + (row["x"], row["y"], row["rot"]) = m.at + if m.layer == "F.Cu": + row["side"] = "top" + elif m.layer == "B.Cu": + row["side"] = "bottom" + else: + row["side"] = None + rows.append(row) + + rows = sorted(rows, key=lambda row: row["r"]) + + out = csv.writer(stream) + out.writerow(["ref", "value", "library", "footprint", "x", "y", "rotation", "side"]) + for row in rows: + del row["r"] + out.writerow(row.values()) + +if args.out: + mk_parents(args.out) + with open(args.out, "w") as f: + run(f) +else: + run(sys.stdout) -- cgit v1.2.3