wv_harness: refactor bom management

This commit is contained in:
Laurier Loiselle 2023-01-30 16:14:57 -05:00
parent b5ca003c82
commit 8a21201c86
No known key found for this signature in database
GPG Key ID: 345920CC72089A3F

View File

@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
from collections import defaultdict
from dataclasses import dataclass, field
from pathlib import Path
from typing import Dict, List
@ -8,12 +7,11 @@ from typing import Dict, List
from graphviz import Graph
import wireviz.wv_colors
from wireviz.wv_bom import BomCategory, bom_list
from wireviz.wv_bom import bom_list
from wireviz.wv_dataclasses import (
AUTOGENERATED_PREFIX,
AdditionalComponent,
Arrow,
ArrowWeight,
BomCategory,
Cable,
Component,
Connector,
@ -22,7 +20,6 @@ from wireviz.wv_dataclasses import (
Metadata,
Options,
Side,
TopLevelGraphicalComponent,
Tweak,
)
from wireviz.wv_graphviz import (
@ -44,16 +41,22 @@ class Harness:
metadata: Metadata
options: Options
tweak: Tweak
additional_bom_items: List[AdditionalComponent] = field(default_factory=list)
additional_bom_items: List[Component] = field(default_factory=list)
shared_bom: Dict = field(default_factory=dict)
def __post_init__(self):
self.connectors = {}
self.cables = {}
self.mates = []
self.bom = defaultdict(dict)
self.bom = {}
self.additional_bom_items = []
@property
def name(self) -> str:
pn = self.metadata.get("pn", "")
output_name = self.metadata["output_name"]
return pn + output_name
def add_connector(self, designator: str, *args, **kwargs) -> None:
conn = Connector(designator=designator, *args, **kwargs)
self.connectors[designator] = conn
@ -63,7 +66,7 @@ class Harness:
self.cables[designator] = cbl
def add_additional_bom_item(self, item: dict) -> None:
new_item = AdditionalComponent(**item)
new_item = Component(**item, category=BomCategory.ADDITIONAL)
self.additional_bom_items.append(new_item)
def add_mate_pin(self, from_name, from_pin, to_name, to_pin, arrow_str) -> None:
@ -107,31 +110,61 @@ class Harness:
+ all_subitems
)
def add_to_bom(entry):
if isinstance(entry, list):
for e in entry:
add_to_bom(e)
return
if hash(entry) in self.bom:
self.bom[hash(entry)] += entry
else:
self.bom[hash(entry)] = entry
try:
self.bom[hash(entry)]
except KeyError:
raise RuntimeError(
f"BomEntry's hash is not persitent: h1:{hash(entry)} h2:{hash(entry)}\n\tentry: {entry}\n\titem:{item}"
)
# add items to BOM
for item in all_toplevel_items:
self._add_to_internal_bom(item) # nested subitems are also handled
if item.ignore_in_bom:
continue
add_to_bom(item.bom_entry)
# sort BOM by category first, then alphabetically by description within category
self.bom = dict(
sorted(
self.bom.items(),
key=lambda x: (
x[1]["category"],
x[0].description,
x[1].category,
x[1].description,
), # x[0] = key, x[1] = value
)
)
next_id = len(self.shared_bom) + 1
# TODO: for each harness, track a (harness_name, qty) pair
def get_per_harness(v):
d = {
"qty": v["qty"],
}
return (self.name, d)
for key, values in self.bom.items():
if key in self.shared_bom:
self.shared_bom[key]["qty"] += values["qty"]
values["id"] = self.shared_bom[key]["id"]
continue
self.shared_bom[key] = values
self.shared_bom[key]["id"] = next_id
values["id"] = next_id
next_id += 1
else:
self.shared_bom[key] = values
self.shared_bom[key]["id"] = next_id
values["id"] = next_id
next_id += 1
k, v = get_per_harness(values)
self.shared_bom[key].per_harness[k] = v
# print(f'bom length: {len(self.bom)}, shared_bom length: {len(self.shared_bom)}') # for debugging
@ -139,92 +172,17 @@ class Harness:
for item in all_bom_relevant_items:
if item.ignore_in_bom:
continue
if not item.bom_hash in self.bom:
print(f"{item}'s hash' not found in BOM dict.")
if hash(item) not in self.bom:
continue
item.bom_id = self.bom[item.bom_hash]["id"]
item.id = self.bom[hash(item)].id
self.bom = dict(
sorted(
self.bom.items(),
key=lambda x: (x[1]["id"],),
key=lambda x: (x[1].id,),
)
)
# print_bom_table(self.bom) # for debugging
def _add_to_internal_bom(self, item: Component):
if item.ignore_in_bom:
return
def _add(hash, qty, designator=None, category=None):
bom_entry = self.bom[hash]
# initialize missing fields
if not "qty" in bom_entry:
bom_entry["qty"] = 0
if not "designators" in bom_entry:
bom_entry["designators"] = set()
# update fields
bom_entry["qty"] += qty
if designator is None:
designator_list = []
elif isinstance(designator, list):
designator_list = designator
else:
designator_list = [designator]
for des in designator_list:
if des and not des.startswith(AUTOGENERATED_PREFIX):
bom_entry["designators"].add(des)
bom_entry["category"] = category
if isinstance(item, TopLevelGraphicalComponent):
if isinstance(item, Connector):
cat = BomCategory.CONNECTOR
elif isinstance(item, Cable):
if item.category == "bundle":
cat = BomCategory.WIRE
else:
cat = BomCategory.CABLE
else:
cat = ""
if item.category == "bundle":
for subitem in item.wire_objects.values():
_add(
hash=subitem.bom_hash,
qty=item.bom_qty, # should be 1
designator=item.designator, # inherit from parent item
category=cat,
)
else:
_add(
hash=item.bom_hash,
qty=item.bom_qty,
designator=item.designator,
category=cat,
)
if item.additional_components:
if item.category == "bundle":
pass # TODO
item.compute_qty_multipliers()
for comp in item.additional_components:
if comp.ignore_in_bom:
continue
_add(
hash=comp.bom_hash,
designator=item.designator,
qty=comp.bom_qty,
category=BomCategory.ADDITIONAL_INSIDE,
)
elif isinstance(item, AdditionalComponent):
cat = BomCategory.ADDITIONAL_OUTSIDE
_add(
hash=item.bom_hash,
qty=item.bom_qty,
designator=None,
category=cat,
)
else:
raise Exception(f"Unknown type of item:\n{item}")
# from wireviz.wv_bom import print_bom_table ; print_bom_table(self.bom) # for debugging
def connect(
self,