From 5f3623b8dd26b37b9ea6011bf71467b2a608b5ff Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Mon, 20 May 2019 22:46:45 +0200 Subject: common_fact_types: Adding key for footprint. functions: o Changing the structure of the functions, they're now factories that will be given kwargs and must return a function that processes the parts. o Adding new function to default set; 'map_footprint' that maps the KiCAD footprints to common footprints. part_validate_parts: Using only common keys. --- src/ee/kicad/functions.py | 128 ++++++++++++++++++++++++---------- src/ee/part/__init__.py | 3 + src/ee/part/common_fact_types.py | 4 +- src/ee/part/fact_keys.py | 1 + src/ee/tools/init.py | 3 + src/ee/tools/part_apply_function.py | 29 ++++++-- src/ee/tools/part_validate_parts.py | 104 +++++++++++++++++++++++++++ src/ee/tools/templates/build.ninja.j2 | 12 +++- 8 files changed, 240 insertions(+), 44 deletions(-) create mode 100644 src/ee/tools/part_validate_parts.py (limited to 'src') diff --git a/src/ee/kicad/functions.py b/src/ee/kicad/functions.py index 39614b6..2d8106a 100644 --- a/src/ee/kicad/functions.py +++ b/src/ee/kicad/functions.py @@ -1,7 +1,10 @@ import re +import yaml + import ee.kicad.model import ee.kicad.sch_fact_types as kicad_ft +from ee import EeException from ee.kicad import sch_fact_types from ee.part import Part from ee.part import common_fact_types @@ -36,70 +39,121 @@ def part_type_from_ref_type(ref_type): return uris.TRANSISTOR -def part_type_strategy(part: Part) -> Part: - pt = None +def part_type_strategy(**kwargs): + def on_part(part: Part) -> Part: + pt = None + + fp_lib = part.facts.get_value(kicad_ft.footprint_library) + if fp_lib is not None: + pt = part_type_from_footprint(fp_lib) + + ref = part.get_only_schematic_reference() + if ref: + ref_type, ref_num = ee.kicad.model.split_ref(ref.referenceProp) - fp_lib = part.facts.get_value(kicad_ft.footprint_library) - if fp_lib is not None: - pt = part_type_from_footprint(fp_lib) + if ref_type: + pt = part_type_from_ref_type(ref_type) + + if pt is not None: + part.facts.add(common_fact_types.ee_component_type, pt) + + return part + + return on_part + + +def fix_value_strategy(**kwargs): + def on_part(part: Part) -> Part: + ref = part.get_only_schematic_reference() + + if not ref: + return part - ref = part.get_only_schematic_reference() - if ref: ref_type, ref_num = ee.kicad.model.split_ref(ref.referenceProp) - if ref_type: - pt = part_type_from_ref_type(ref_type) + if not ref_num: + return part - if pt is not None: - part.facts.add(common_fact_types.ee_component_type, pt) + v = part.facts.get_value(sch_fact_types.value) + if not v: + return part - return part + symbol_name = part.facts.get_value(sch_fact_types.symbol_name) + if ref_type in ("D", "R", "L", "C") and v == symbol_name: + part.remove_fact(uris.make_fact_key("value")) -def fix_value_strategy(part: Part) -> Part: - ref = part.get_only_schematic_reference() + if ref_type == "Q" and v == symbol_name and re.match("^Q_[NP]MOS_[DSG]{3}$", symbol_name): + part.remove_fact(uris.make_fact_key("value")) - if not ref: return part - ref_type, ref_num = ee.kicad.model.split_ref(ref.referenceProp) + return on_part - if not ref_num: - return part - v = part.facts.get_value(sch_fact_types.value) - if not v: +def mpn_strategy(**kwargs): + def on_part(part: Part) -> Part: + for field in part.facts.all(sch_fact_types.field): + + k, v = re.split(":", field.value, 1) + if k == "mpn": + part.add_mpn(v) + return part - symbol_name = part.facts.get_value(sch_fact_types.symbol_name) + return on_part + - if ref_type in ("D", "R", "L", "C") and v == symbol_name: - part.remove_fact(uris.make_fact_key("value")) +def map_footprint(footprint_mappings=None, **kwargs): + if footprint_mappings is None: + return None - if ref_type == "Q" and v == symbol_name and re.match("^Q_[NP]MOS_[DSG]{3}$", symbol_name): - part.remove_fact(uris.make_fact_key("value")) + mappings = {} + with open(footprint_mappings, "r") as f: + doc = yaml.load(f, Loader=yaml.SafeLoader) + if not isinstance(doc, dict): + raise EeException("The footprint mappings document must be a dict.") - return part + if "kicad-to-common" not in doc: + raise EeException("The footprint mappings document must contain the key 'kicad-to-common'.") + for k, v in doc["kicad-to-common"].items(): + if not isinstance(v, str): + raise EeException("Bad value for key {}, must be a string".format(k)) -def mpn_strategy(part: Part) -> Part: - for field in part.facts.all(sch_fact_types.field): + mappings[k] = v - k, v = re.split(":", field.value, 1) - if k == "mpn": - part.add_mpn(v) + def on_part(part: Part) -> Part: + kicad_footprint = part.facts.get_value(kicad_ft.footprint) - return part + if not kicad_footprint: + return part + footprint = mappings.get(kicad_footprint, None) + + if footprint: + part.facts.add(common_fact_types.footprint, footprint) + + return part -def default(part: Part) -> Part: - functions = [ + return on_part + + +def default(**kwargs): + function_factories = [ fix_value_strategy, mpn_strategy, part_type_strategy, + map_footprint, ] - for f in functions: - part = f(part) + functions = [factory(**kwargs) for factory in function_factories] + functions = [f for f in functions if f is not None] + + def on_part(part: Part) -> Part: + for f in functions: + part = f(part) + + return part - return part + return on_part diff --git a/src/ee/part/__init__.py b/src/ee/part/__init__.py index a2face8..3ce255e 100644 --- a/src/ee/part/__init__.py +++ b/src/ee/part/__init__.py @@ -375,6 +375,9 @@ class Facts(object): k = self._get_key(key) return next((f.valueProp for f in self.part.get_facts() if f.keyProp == k), None) + def get_values(self, *keys: Union[str, FactType]) -> List[Optional[str]]: + return [self.get_value(key) for key in keys] + class Entry(object): def __init__(self, new: bool, part: types.Part): diff --git a/src/ee/part/common_fact_types.py b/src/ee/part/common_fact_types.py index 12c099e..10ba1eb 100644 --- a/src/ee/part/common_fact_types.py +++ b/src/ee/part/common_fact_types.py @@ -1,5 +1,7 @@ import ee -from ee.part import EeValueFactType, fact_keys +from ee.part import EeValueFactType, fact_keys, FactType + +footprint = FactType(fact_keys.footprint, "Footprint") resistance = EeValueFactType(fact_keys.resistance, "Resistance", ee.resistance_type) capacitance = EeValueFactType(fact_keys.capacitance, "Capacitance", ee.capacitance_type) diff --git a/src/ee/part/fact_keys.py b/src/ee/part/fact_keys.py index 65dc699..9617b9c 100644 --- a/src/ee/part/fact_keys.py +++ b/src/ee/part/fact_keys.py @@ -14,3 +14,4 @@ place_part = "http://purl.org/ee/fact-type/place-part" imperial_footprint_size = "http://purl.org/ee/fact-type/imperial-footprint-size" part_class = "http://purl.org/ee/fact-type/part-class" +footprint = "http://purl.org/ee/fact-type/footprint" diff --git a/src/ee/tools/init.py b/src/ee/tools/init.py index 156d1b7..6d7908e 100644 --- a/src/ee/tools/init.py +++ b/src/ee/tools/init.py @@ -25,6 +25,9 @@ def init_kicad_project(basedir: Path, cfg, args): if pcb_file.is_file(): cfg["kicad-project"]["pcb"] = str(pcb_file) + + cfg["kicad-project"]["functions"] = "ee.kicad.functions.default" + cfg["kicad-project"]["function-arguments"] = "" else: print("Found more than one kicad project file.") diff --git a/src/ee/tools/part_apply_function.py b/src/ee/tools/part_apply_function.py index bece364..af51b5a 100644 --- a/src/ee/tools/part_apply_function.py +++ b/src/ee/tools/part_apply_function.py @@ -4,7 +4,7 @@ from pathlib import Path from typing import List from ee import tools, EeException -from ee.part import Part, PartDb, load_db, save_db +from ee.part import Part, load_db, save_db from ee.project import Project @@ -23,15 +23,29 @@ def load_functions(function_names): return functions -def work(in_path: Path, out_path: Path, report_path: Path, function_names: List[str]): - functions = load_functions(function_names) +def work(in_path: Path, out_path: Path, report_path: Path, function_names: List[str], arguments: List[str]): + factories = load_functions(function_names) in_parts = load_db(in_path) - parts = PartDb() + kwargs = {} + for a in arguments: + import re + k, v = re.split(":", a, maxsplit=1) + kwargs[k] = v + + functions = [[factory[0], factory[1](**kwargs)] for factory in factories] tools.mk_parents(report_path) with report_path.open("w") as rpt: + + if len(kwargs): + print("Arguments:", file=rpt) + print("", file=rpt) + for key in sorted(kwargs): + print(" * {}={}".format(key, kwargs[key]), file=rpt) + print("", file=rpt) + for xml in in_parts.iterparts(): part = Part(xml) @@ -57,6 +71,11 @@ parser.add_argument("--function", nargs="*", metavar="FUNCTION") +parser.add_argument("--argument", + required=True, + nargs="*", + metavar="ARG") + parser.add_argument("--execution", default="default") @@ -64,4 +83,4 @@ args = parser.parse_args() project = Project.load() report = project.report_dir / "apply-function" / (args.execution + ".rst") -work(Path(args.in_path), Path(args.out), report, args.function) +work(Path(args.in_path), Path(args.out), report, args.function, args.argument) diff --git a/src/ee/tools/part_validate_parts.py b/src/ee/tools/part_validate_parts.py new file mode 100644 index 0000000..897b0bf --- /dev/null +++ b/src/ee/tools/part_validate_parts.py @@ -0,0 +1,104 @@ +import argparse +from pathlib import Path + +from ee.part import Part, load_db, common_fact_types + + +class Messages(object): + INFO = 1 + WARNING = 2 + ERROR = 3 + + def __init__(self): + self.messages = [] + + def error(self, msg): + self.messages.append((Messages.ERROR, msg)) + return self + + def warning(self, msg): + self.messages.append((Messages.WARNING, msg)) + return self + + def info(self, msg): + self.messages.append((Messages.INFO, msg)) + return self + + @property + def errors(self): + return [m[1] for m in self.messages if m[0] == Messages.ERROR] + + @property + def warnings(self): + return [m[1] for m in self.messages if m[0] == Messages.WARNING] + + @property + def infos(self): + return [m[1] for m in self.messages if m[0] == Messages.INFO] + + def __len__(self): + return self.messages.__len__() + + def append(self, messages: "Messages"): + self.messages.extend(messages.messages) + + +def check_has_footprint(part: Part): + fp = part.facts.get_value(common_fact_types.footprint) + if fp is not None: + return + + return Messages().warning("No footprint set") + + +def validate(f, part: Part): + validators = [ + check_has_footprint + ] + + messages = Messages() + for validator in validators: + m = validator(part) + if m: + messages.append(m) + + print("{}".format(part.printable_reference), file=f) + print("{}".format("=" * len(part.printable_reference)), file=f) + print("", file=f) + + for msg in messages.errors: + print("* ERROR: {}".format(msg), file=f) + for msg in messages.warnings: + print("* WARNING: {}".format(msg), file=f) + for msg in messages.infos: + print("* INFO: {}".format(msg), file=f) + + if len(messages) == 0: + print("No issues found", file=f) + print("", file=f) + + +def work(in_path: Path, out_path: Path): + in_parts = load_db(in_path) + + with out_path.open("w") as f: + for xml in in_parts.iterparts(): + part = Part(xml) + + validate(f, part) + + +parser = argparse.ArgumentParser() + +parser.add_argument("--in", + dest="in_path", + required=True, + metavar="PART DB") + +parser.add_argument("--out", + required=True, + metavar="REQUIREMENTS") + +args = parser.parse_args() + +work(Path(args.in_path), Path(args.out)) diff --git a/src/ee/tools/templates/build.ninja.j2 b/src/ee/tools/templates/build.ninja.j2 index 1f9ba35..0aaa76f 100644 --- a/src/ee/tools/templates/build.ninja.j2 +++ b/src/ee/tools/templates/build.ninja.j2 @@ -26,12 +26,15 @@ rule pn-part-search-list command = $ee pn-part-search-list --in $in --out $out --supplier $supplier rule part-apply-function - command = $ee part-apply-function --in $in --out $out $functions + command = $ee part-apply-function --in $in --out $out $functions $arguments rule part-find-requirements description = part-find-requirements command = $ee part-find-requirements --in $in --out $out +rule part-validate-parts + command = $ee part-validate-parts --in $in --out $out + rule digikey-search-parts description = digikey-search-parts command = $ee digikey-search-parts --in $in --out $out @@ -83,8 +86,15 @@ build ee/sch.xml: part-apply-function ee/kicad-sch.xml {%- else %} functions = --function ee.kicad.functions.default {%- endif %} +{%- if project.cfg["kicad-project"]["function-arguments"] %} + arguments = --argument {{ project.cfg["kicad-project"]["function-arguments"] }} +{%- else %} + arguments = --argument "" +{%- endif %} {%- endif %} +build $report_dir/part-validate-parts.rst: part-validate-parts ee/sch.xml + build ee/requirements.xml: part-find-requirements ee/sch.xml {% for s in distributors %} -- cgit v1.2.3