diff options
-rw-r--r-- | src/ee/souffle/digikey.dl | 3 | ||||
-rw-r--r-- | src/ee/souffle/kicad.dl | 43 | ||||
-rw-r--r-- | src/ee/tools/__init__.py | 6 | ||||
-rw-r--r-- | src/ee/tools/ninja.py | 1 | ||||
-rw-r--r-- | src/ee/tools/part_apply_souffle_post.py | 56 | ||||
-rw-r--r-- | src/ee/tools/part_apply_souffle_pre.py | 64 | ||||
-rw-r--r-- | src/ee/tools/souffle-export.py | 51 | ||||
-rw-r--r-- | src/ee/tools/templates/build.ninja.j2 | 38 | ||||
-rw-r--r-- | test/souffle/test.souffle | 29 |
9 files changed, 289 insertions, 2 deletions
diff --git a/src/ee/souffle/digikey.dl b/src/ee/souffle/digikey.dl new file mode 100644 index 0000000..735dd2d --- /dev/null +++ b/src/ee/souffle/digikey.dl @@ -0,0 +1,3 @@ +#include "facts.dl" + +.output fact diff --git a/src/ee/souffle/kicad.dl b/src/ee/souffle/kicad.dl new file mode 100644 index 0000000..5448b05 --- /dev/null +++ b/src/ee/souffle/kicad.dl @@ -0,0 +1,43 @@ +#include "facts.dl" + +.output fact + +/* +.decl is_resistor(uri:symbol) +.decl is_capacitor(uri:symbol) +.decl is_inductor(uri:symbol) + +is_resistor(Ref) :- + resistor_lib(lib). + fact(Ref, "http://purl.org/ee/kicad-sch-fact-type#footprint-library", lib). + +is_capacitor(Ref) :- + capacitor_lib(lib). + fact(Ref, "http://purl.org/ee/kicad-sch-fact-type#footprint-library", lib). + +is_inductor(Ref) :- + inductor_lib(lib). + fact(Ref, "http://purl.org/ee/kicad-sch-fact-type#footprint-library", lib). +*/ + +.decl resistor_lib(name:symbol) +resistor_lib("Resistor_SMD"). +resistor_lib("Resistor_THT"). + +.decl capacitor_lib(name:symbol) +capacitor_lib("Capacitor_SMD"). + +.decl inductor_lib(name:symbol) +inductor_lib("Inductor_SMD"). + +fact(Ref, "http://purl.org/ee/fact-type/ee-component-type", "http://purl.org/ee/part-type#resistor") :- + resistor_lib(lib), + fact(Ref, "http://purl.org/ee/kicad-sch-fact-type#footprint-library", lib). + +fact(Ref, "http://purl.org/ee/fact-type/ee-component-type", "http://purl.org/ee/part-type#capacitor") :- + capacitor_lib(lib), + fact(Ref, "http://purl.org/ee/kicad-sch-fact-type#footprint-library", lib). + +fact(Ref, "http://purl.org/ee/fact-type/ee-component-type", "http://purl.org/ee/part-type#inductor") :- + inductor_lib(lib), + fact(Ref, "http://purl.org/ee/kicad-sch-fact-type#footprint-library", lib). diff --git a/src/ee/tools/__init__.py b/src/ee/tools/__init__.py index 46004e9..112e306 100644 --- a/src/ee/tools/__init__.py +++ b/src/ee/tools/__init__.py @@ -26,5 +26,11 @@ def mk_parents(path: Union[str, Path]): if len(dirname) == 0: return + mk_dirs(dirname) + + +def mk_dirs(path: Union[str, Path]): + dirname = str(path) + if not os.path.isdir(dirname): os.mkdir(dirname) diff --git a/src/ee/tools/ninja.py b/src/ee/tools/ninja.py index 9e919fb..c5ee959 100644 --- a/src/ee/tools/ninja.py +++ b/src/ee/tools/ninja.py @@ -58,6 +58,7 @@ def generate(project: Project): "ee": "{} -m ee".format(os.path.relpath(sys.executable, Path("."))), "project": project, "part_dbs": part_dbs, + "souffle_ee_src": Path(__file__).parent.parent / "souffle", } gerber_zip = None diff --git a/src/ee/tools/part_apply_souffle_post.py b/src/ee/tools/part_apply_souffle_post.py new file mode 100644 index 0000000..4c23128 --- /dev/null +++ b/src/ee/tools/part_apply_souffle_post.py @@ -0,0 +1,56 @@ +import argparse +import csv +from pathlib import Path + +from ee import EeException +from ee.db import ObjDb +from ee.part import Part, load_db, save_db, PartDb + + +def work(in_path: Path, out_path: Path, work_dir: Path): + in_parts: ObjDb[Part] = ObjDb[Part]() + uri_idx = in_parts.add_unique_index("uri", lambda p: p.uri) + + for entry in load_db(in_path).parts: + in_parts.add(Part(entry.part)) + + out_parts = PartDb() + + for part in in_parts: + out_parts.add_entry(part, False) + + with (work_dir / "out" / "fact.csv").open("r") as f: + reader = csv.reader(f, dialect="excel-tab") + for uri, key, value in reader: + try: + part = uri_idx.get_single(uri) + except KeyError: + raise EeException("Unknown part found in output: uri={}".format(uri)) + + old_value = part.facts.get_value(key) + + if old_value is None: + print("New fact for {}: {}={}".format(uri, key, value)) + part.facts.add(key, value) + + save_db(out_path, out_parts) + + +parser = argparse.ArgumentParser() + +parser.add_argument("--in", + dest="in_path", + required=True, + metavar="PART DB") + +parser.add_argument("--out", + required=True, + metavar="PART DB") + +parser.add_argument("--work", + required=True, + metavar="DIR") + +args = parser.parse_args() + +work(Path(args.in_path), Path(args.out), Path(args.work)) diff --git a/src/ee/tools/part_apply_souffle_pre.py b/src/ee/tools/part_apply_souffle_pre.py new file mode 100644 index 0000000..d035ef7 --- /dev/null +++ b/src/ee/tools/part_apply_souffle_pre.py @@ -0,0 +1,64 @@ +import argparse +import csv +from pathlib import Path + +from ee import tools +from ee.part import Part, load_db +from ee.tools import mk_dirs + + +def work(in_path: Path, work_dir: Path): + in_parts = load_db(in_path) + + print_header = False + + tools.mk_dirs(work_dir) + with (work_dir / "facts.dl").open("w") as f: + print(".decl part(uri:symbol, ref:symbol)", file=f) + print(".input part", file=f) + print("", file=f) + print(".decl fact(part_uri:symbol, key:symbol, value:symbol)", file=f) + print(".input fact", file=f) + + part_count = 0 + fact_count = 0 + + in_dir = work_dir / "in" + mk_dirs(in_dir) + with (in_dir / "part.facts").open("w") as part_f: + with (in_dir / "fact.facts").open("w") as fact_f: + part_csv = csv.writer(part_f, dialect="excel-tab") + if print_header: + part_csv.writerow(["uri", "ref"]) + + fact_csv = csv.writer(fact_f, dialect="excel-tab") + if print_header: + fact_csv.writerow(["part_uri", "key", "value"]) + + for xml in load_db(in_path).iterparts(): + p = Part(xml) + sch_ref = p.get_only_schematic_reference() + part_csv.writerow([p.uri, sch_ref.referenceProp if sch_ref else None]) + part_count += 1 + + for fact in p.get_facts(): + fact_csv.writerow([p.uri, fact.keyProp, fact.valueProp]) + fact_count += 1 + + print("Loaded clauses: {} parts, {} facts".format(part_count, fact_count)) + + +parser = argparse.ArgumentParser() + +parser.add_argument("--in", + dest="in_path", + required=True, + metavar="PART DB") + +parser.add_argument("--work", + required=True, + metavar="DIR") + +args = parser.parse_args() + +work(Path(args.in_path), Path(args.work)) diff --git a/src/ee/tools/souffle-export.py b/src/ee/tools/souffle-export.py new file mode 100644 index 0000000..098eb11 --- /dev/null +++ b/src/ee/tools/souffle-export.py @@ -0,0 +1,51 @@ +import argparse +import csv +import os.path +from pathlib import Path + +import ee.tools +from ee.part import load_db, Part + + +def run(args): + print_header = False + + part_db = Path(args.part_db) + out_dir = part_db.parent / "{}.{}".format(os.path.splitext(part_db.name)[0], "souffle") + + if not out_dir.is_dir(): + ee.tools.mk_dirs(out_dir) + + with open(out_dir / "facts.dl", "w") as f: + print(".decl part(uri:symbol, ref:symbol, supplier:symbol)", file=f) + print(".input part", file=f) + print("", file=f) + print(".decl fact(part_uri:symbol, key:symbol, value:symbol)", file=f) + print(".input fact", file=f) + + with open(out_dir / "part.facts", "w") as part_f: + with open(out_dir / "fact.facts", "w") as fact_f: + part_csv = csv.writer(part_f, dialect="excel-tab") + if print_header: + part_csv.writerow(["uri", "ref", "supplier"]) + + fact_csv = csv.writer(fact_f, dialect="excel-tab") + if print_header: + fact_csv.writerow(["part_uri", "key", "value"]) + + for xml in load_db(part_db).iterparts(): + p = Part(xml) + sch_ref = p.get_only_schematic_reference() + part_csv.writerow([p.uri, sch_ref.referenceProp if sch_ref else None, p.supplier]) + + for fact in p.get_facts(): + fact_csv.writerow([p.uri, fact.keyProp, fact.valueProp]) + + +parser = argparse.ArgumentParser() + +parser.add_argument("--part-db", + required=True, + metavar="PART DB") + +run(parser.parse_args()) diff --git a/src/ee/tools/templates/build.ninja.j2 b/src/ee/tools/templates/build.ninja.j2 index 6ff4e0a..894365b 100644 --- a/src/ee/tools/templates/build.ninja.j2 +++ b/src/ee/tools/templates/build.ninja.j2 @@ -30,6 +30,15 @@ rule pn-part-search-list rule part-apply-function command = $ee part-apply-function --execution $execution --in $in --out $out $functions $arguments +rule part-apply-souffle-pre + command = $ee part-apply-souffle-pre --in $in --work $work + +rule souffle + command = souffle -F $work/in -D $work/out -I $work -I "{{ souffle_ee_src }}" $in + +rule part-apply-souffle-post + command = $ee part-apply-souffle-post --in $in_sch --out $out --work $work + rule part-find-requirements description = part-find-requirements command = $ee part-find-requirements --in $in --out $out $report @@ -74,8 +83,9 @@ build {{ gerber_zip }}: kicad-gerber $pcb {% if sch is defined -%} {%- set cfg = project.cfg["kicad-project"] %} -build ee/kicad-sch.xml: kicad-make-bom $sch -build ee/sch.xml: part-apply-function ee/kicad-sch.xml +build ee/kicad/sch.xml: kicad-make-bom $sch +{# +build ee/sch.xml: part-apply-function ee/kicad/sch.xml execution = kicad {%- if cfg and "functions" in cfg and cfg["functions"] %} functions = --function {{ cfg["functions"] }} @@ -83,6 +93,17 @@ build ee/sch.xml: part-apply-function ee/kicad-sch.xml {%- if cfg and "function-arguments" in cfg and cfg["function-arguments"] %} arguments = --argument {{ cfg["function-arguments"] }} {%- endif %} +#} + +build ee/kicad/souffle/facts.dl: part-apply-souffle-pre ee/kicad/sch.xml + work = ee/kicad/souffle + +build ee/kicad/souffle/out/fact.csv: souffle {{ souffle_ee_src }}/kicad.dl | ee/kicad/souffle/facts.dl + work = ee/kicad/souffle + +build ee/sch.xml: part-apply-souffle-post ee/kicad/souffle/out/fact.csv + in_sch = ee/kicad/sch.xml + work = ee/kicad/souffle {%- endif %} build $report_dir/part-validate-parts.rst: part-validate-parts ee/sch.xml @@ -101,6 +122,7 @@ build ee/{{ s }}/pn-part-search-list.xml: pn-part-search-list ee/sch.xml build ee/{{ s }}/downloaded.xml | ee/{{ s }}/downloaded.rst: {{ s }}-search-parts ee/{{ s }}/pn-part-search-list.xml {%- set reports=reports+["$report_dir/" + s + "/downloaded.rst"] %} +{# build ee/{{ s }}/parts.xml: part-apply-function ee/{{ s }}/downloaded.xml execution = {{ s }} {%- if cfg and "functions" in cfg and cfg["functions"] %} @@ -109,6 +131,16 @@ build ee/{{ s }}/parts.xml: part-apply-function ee/{{ s }}/downloaded.xml {%- if cfg and "function-arguments" in cfg and cfg["function-arguments"] %} arguments = --argument {{ cfg["function-arguments"] }} {%- endif %} +#} +build ee/{{ s }}/souffle/facts.dl: part-apply-souffle-pre ee/{{ s }}/downloaded.xml + work = ee/{{ s }}/souffle + +build ee/{{ s }}/souffle/out/fact.csv: souffle {{ souffle_ee_src }}/{{ s }}.dl | ee/{{ s }}/souffle/facts.dl + work = ee/{{ s }}/souffle + +build ee/{{ s }}/parts.xml: part-apply-souffle-post ee/{{ s }}/souffle/out/fact.csv + in_sch = ee/{{ s }}/downloaded.xml + work = ee/{{ s }}/souffle {%- endfor %} {%- for f in parts_yaml_files %} @@ -149,3 +181,5 @@ build ee/seeed/opl/{{ opl }}.xml: seeed-download-opl # Reports build ee-reports: phony {{ " ".join(reports) }} +build part-dbs: phony {%- for p in part_dbs %} {{ p }}.xml{% endfor %} +build ee-all: phony ee-reports part-dbs diff --git a/test/souffle/test.souffle b/test/souffle/test.souffle new file mode 100644 index 0000000..1072e9a --- /dev/null +++ b/test/souffle/test.souffle @@ -0,0 +1,29 @@ +.symbol_type Ref +.symbol_type Uri +.symbol_type Value + +.decl part(uri:Uri, supplier:Uri) +.input part + +.decl fact(part:Uri, key:Uri, value:Value) +.input fact + +.decl warning(part:Uri, msg:symbol) +.output warning + +.decl part_status(part:Uri, status:Value) +.decl active(part:Uri) + +part_status(part, status) :- fact(part, "http://purl.org/ee/digikey-fact-key#1989", status). + +active(part) :- part_status(part, "Active"). + +warning(part, cat("Not active from ", supplier)) :- part(part, supplier), !active(part). + +/* +active(part) :- fact(part, "http://purl.org/ee/digikey-fact-key#1989", "Active"). + +warning(part, "Not active") :- + fact(part, "http://purl.org/ee/digikey-fact-key#1989", status), + status != "Active". +*/
\ No newline at end of file |