aboutsummaryrefslogtreecommitdiff
path: root/src/ee
diff options
context:
space:
mode:
Diffstat (limited to 'src/ee')
-rw-r--r--src/ee/bom.py14
-rw-r--r--src/ee/part/__init__.py3
-rw-r--r--src/ee/tools/part_validate_parts.py99
-rw-r--r--src/ee/tools/templates/build.ninja.j211
4 files changed, 99 insertions, 28 deletions
diff --git a/src/ee/bom.py b/src/ee/bom.py
index 0a0225d..dd1c016 100644
--- a/src/ee/bom.py
+++ b/src/ee/bom.py
@@ -139,13 +139,19 @@ def create_bom(project: Project, schematic_path: Path, out_path: Path, part_dbs:
for bom_part in bom_parts:
if not bom_part.selected_part:
log.info("No part selected for {}".format(bom_part.part.printable_reference))
- continue
+ supplier_part = None
+ else:
+ supplier_part = bom_part.selected_part
- supplier_part = bom_part.selected_part
+ uri = None # TODO: generate
- part = Part(types.Part(supplier=supplier_part.supplier))
+ part = Part(types.Part(uri=uri))
+ # TODO: this should use the part's uri instead of schematic reference. However, right now there is no way to
+ # differentiate between two part-reference objects.
part.add_schematic_reference(bom_part.part.get_exactly_one_schematic_reference().referenceProp)
- part.add_part_reference(supplier_part.uri)
+
+ if supplier_part:
+ part.add_part_reference(supplier_part.uri)
out_parts.add_entry(part, True)
diff --git a/src/ee/part/__init__.py b/src/ee/part/__init__.py
index 3ce255e..c18dd02 100644
--- a/src/ee/part/__init__.py
+++ b/src/ee/part/__init__.py
@@ -237,6 +237,9 @@ class Part(object):
def get_part_references(self) -> List[types.PartReference]:
return self.xml.referencesProp.part_referenceProp
+ def get_only_part_reference(self) -> Optional[types.PartReference]:
+ return next(iter(self.get_part_references()), None)
+
def get_exactly_one_part_reference(self) -> types.PartReference:
refs = self.get_part_references()
if len(refs) == 0:
diff --git a/src/ee/tools/part_validate_parts.py b/src/ee/tools/part_validate_parts.py
index bf45ace..a9c5ba7 100644
--- a/src/ee/tools/part_validate_parts.py
+++ b/src/ee/tools/part_validate_parts.py
@@ -1,8 +1,10 @@
import argparse
from itertools import groupby
from pathlib import Path
+from typing import List
import ee.tools
+from ee.db import ObjDb
from ee.part import Part, load_db, common_fact_types
@@ -46,27 +48,55 @@ class Messages(object):
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
+class Context(object):
+ def __init__(self, supplier_parts: ObjDb[Part]):
+ self.supplier_parts = supplier_parts
+ self.uri_idx = supplier_parts.index("uri")
- return Messages(part).warning("No footprint")
+ def get_supplier_part(self, part_uri):
+ return self.uri_idx.get_single(part_uri)
-def validate(f, part: Part):
+def check_has_footprint(ctx: Context, bom: Part, sch: Part):
+ ms = Messages(sch)
+
+ fp = sch.facts.get_value(common_fact_types.footprint)
+ if fp is None:
+ return ms.warning("No footprint in schematic part")
+
+ part_ref = bom.get_only_part_reference()
+
+ if part_ref is None:
+ return ms.warning("Part has footprint in schematic but no BOM part selected.")
+
+ supplier_part = ctx.get_supplier_part(part_ref.part_uriProp)
+
+ supplier_fp = supplier_part.facts.get_value(common_fact_types.footprint)
+
+ if supplier_fp is None:
+ return ms.warning(f"Part has footprint in schematic and a BOM part but the BOM part doesn't have a footprint. "
+ f"Part's footprint: {fp}.")
+ if fp != supplier_fp:
+ return ms.warning("Part has footprint in schematic and a BOM part but their footprints do not match. "
+ f"Footprints: part: {fp}, BOM: {supplier_fp}.")
+
+ ms.info("Part has footprint and BOM part. Their footprints matches.")
+
+
+def validate(f, ctx: Context, bom_part: Part, sch_part: Part):
validators = [
check_has_footprint
]
- messages = Messages(part)
+ messages = Messages(sch_part)
for validator in validators:
- m = validator(part)
+ m = validator(ctx, bom_part, sch_part)
if m:
messages.append(m)
- print("{}".format(part.printable_reference), file=f)
- print("{}".format("=" * len(part.printable_reference)), file=f)
+ title = "Checking {}".format(sch_part.printable_reference)
+ print(title, file=f)
+ print("{}".format("=" * len(title)), file=f)
print("", file=f)
for msg in messages.errors:
@@ -83,16 +113,31 @@ def validate(f, part: Part):
return messages
-def work(in_path: Path, out_path: Path):
- in_parts = load_db(in_path)
+def work(bom_path: Path, sch_path: Path, report_path: Path, part_dbs: List[Path]):
+ bom_parts: ObjDb[Part] = ObjDb[Part]()
+ bom_parts.add_unique_index("uri", lambda p: p.uri)
+ [bom_parts.add(Part(xml)) for xml in load_db(bom_path).iterparts()]
- with out_path.open("w") as f:
- messages = Messages(None)
+ sch_parts: ObjDb[Part] = ObjDb[Part]()
+ sch_parts.add_unique_index("uri", lambda p: p.uri)
+ ref_idx = sch_parts.add_unique_index("ref", lambda p: p.get_exactly_one_schematic_reference().referenceProp)
+ [sch_parts.add(Part(xml)) for xml in load_db(sch_path).iterparts()]
- for xml in in_parts.iterparts():
- part = Part(xml)
+ supplier_parts = ObjDb[Part]()
+ supplier_parts.add_unique_index("uri", lambda p: p.uri)
- ms = validate(f, part)
+ for path in part_dbs:
+ for xml in load_db(path).iterparts():
+ supplier_parts.add(Part(xml))
+
+ ctx = Context(supplier_parts)
+
+ with report_path.open("w") as f:
+ messages = Messages(None)
+
+ for bom_part in bom_parts:
+ sch_part = ref_idx.get_single(bom_part.get_exactly_one_schematic_reference().referenceProp)
+ ms = validate(f, ctx, bom_part, sch_part)
messages.append(ms)
print("", file=f)
@@ -111,16 +156,26 @@ def work(in_path: Path, out_path: Path):
parser = argparse.ArgumentParser()
ee.tools.add_default_argparse_group(parser)
-parser.add_argument("--in",
- dest="in_path",
+parser.add_argument("--bom",
+ required=True,
+ metavar="PART DB")
+
+parser.add_argument("--sch",
required=True,
metavar="PART DB")
-parser.add_argument("--out",
+parser.add_argument("--report",
required=True,
- metavar="REQUIREMENTS")
+ metavar="FILE")
+
+parser.add_argument("--part-db",
+ dest="part_dbs",
+ nargs="*",
+ required=True,
+ metavar="PART DB")
args = parser.parse_args()
ee.tools.process_default_argparse_group(args)
-work(Path(args.in_path), Path(args.out))
+part_dbs_paths = [Path(path) for path in args.part_dbs]
+work(Path(args.bom), Path(args.sch), Path(args.report), part_dbs_paths)
diff --git a/src/ee/tools/templates/build.ninja.j2 b/src/ee/tools/templates/build.ninja.j2
index dbfe8f0..206a487 100644
--- a/src/ee/tools/templates/build.ninja.j2
+++ b/src/ee/tools/templates/build.ninja.j2
@@ -43,7 +43,7 @@ rule part-find-requirements
command = $ee part-find-requirements {{ log }} --in $in --out $out $report
rule part-validate-parts
- command = $ee part-validate-parts {{ log }} --in $in --out $out
+ command = $ee part-validate-parts {{ log }} --bom $bom --sch $sch --report $out --part-db $part_dbs
rule digikey-search-parts
command = $ee digikey-search-parts {{ log }} --in $in --out $out
@@ -102,12 +102,17 @@ build ee/sch.xml: part-apply-souffle-post ee/kicad/souffle/out/fact.csv
work = ee/kicad/souffle
{%- endif %}
-build $report_dir/part-validate-parts.rst: part-validate-parts ee/sch.xml
+build $report_dir/part-validate-parts.rst: part-validate-parts ee/bom.xml ee/sch.xml
+ sch = ee/sch.xml
+ bom = ee/bom.xml
+ part_dbs ={%- for p in part_dbs %} {{ p }}.xml{% endfor %}
{%- set reports=reports+["$report_dir/part-validate-parts.rst"] %}
+{#- TODO: complete
build ee/requirements.xml | $report_dir/requirements.rst: part-find-requirements ee/sch.xml
report = --report $report_dir/requirements.rst
{%- set reports=reports+["$report_dir/requirements.rst"] %}
+#}
{% for s in distributors %}
{%- set cfg = project.cfg["supplier:" + s] if "supplier:" + s in project.cfg else None %}
@@ -155,12 +160,14 @@ build ee/bom.xml | $report_dir/bom.rst: create-bom ee/sch.xml {%- for p in part_
{%- endif %}
{%- set reports=reports+["$report_dir/bom.rst"] %}
+{#- TODO: complete
build ee/orders/index.xml: split-parts-by-supplier ee/bom.xml {%- for p in part_dbs %} {{ p }}.xml{% endfor %}
order = ee/bom.xml
part_dbs ={%- for p in part_dbs %} --part-db {{ p }}.xml{% endfor %}
out_dir = ee/orders
default ee/orders/index.xml
+#}
rule seeed-download-opl
description = seeed-download-opl $opl