wv_harness: refactor bom management
This commit is contained in:
parent
b5ca003c82
commit
8a21201c86
@ -1,6 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from collections import defaultdict
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
@ -8,12 +7,11 @@ from typing import Dict, List
|
|||||||
from graphviz import Graph
|
from graphviz import Graph
|
||||||
|
|
||||||
import wireviz.wv_colors
|
import wireviz.wv_colors
|
||||||
from wireviz.wv_bom import BomCategory, bom_list
|
from wireviz.wv_bom import bom_list
|
||||||
from wireviz.wv_dataclasses import (
|
from wireviz.wv_dataclasses import (
|
||||||
AUTOGENERATED_PREFIX,
|
|
||||||
AdditionalComponent,
|
|
||||||
Arrow,
|
Arrow,
|
||||||
ArrowWeight,
|
ArrowWeight,
|
||||||
|
BomCategory,
|
||||||
Cable,
|
Cable,
|
||||||
Component,
|
Component,
|
||||||
Connector,
|
Connector,
|
||||||
@ -22,7 +20,6 @@ from wireviz.wv_dataclasses import (
|
|||||||
Metadata,
|
Metadata,
|
||||||
Options,
|
Options,
|
||||||
Side,
|
Side,
|
||||||
TopLevelGraphicalComponent,
|
|
||||||
Tweak,
|
Tweak,
|
||||||
)
|
)
|
||||||
from wireviz.wv_graphviz import (
|
from wireviz.wv_graphviz import (
|
||||||
@ -44,16 +41,22 @@ class Harness:
|
|||||||
metadata: Metadata
|
metadata: Metadata
|
||||||
options: Options
|
options: Options
|
||||||
tweak: Tweak
|
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)
|
shared_bom: Dict = field(default_factory=dict)
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
self.connectors = {}
|
self.connectors = {}
|
||||||
self.cables = {}
|
self.cables = {}
|
||||||
self.mates = []
|
self.mates = []
|
||||||
self.bom = defaultdict(dict)
|
self.bom = {}
|
||||||
self.additional_bom_items = []
|
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:
|
def add_connector(self, designator: str, *args, **kwargs) -> None:
|
||||||
conn = Connector(designator=designator, *args, **kwargs)
|
conn = Connector(designator=designator, *args, **kwargs)
|
||||||
self.connectors[designator] = conn
|
self.connectors[designator] = conn
|
||||||
@ -63,7 +66,7 @@ class Harness:
|
|||||||
self.cables[designator] = cbl
|
self.cables[designator] = cbl
|
||||||
|
|
||||||
def add_additional_bom_item(self, item: dict) -> None:
|
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)
|
self.additional_bom_items.append(new_item)
|
||||||
|
|
||||||
def add_mate_pin(self, from_name, from_pin, to_name, to_pin, arrow_str) -> None:
|
def add_mate_pin(self, from_name, from_pin, to_name, to_pin, arrow_str) -> None:
|
||||||
@ -107,31 +110,61 @@ class Harness:
|
|||||||
+ all_subitems
|
+ 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
|
# add items to BOM
|
||||||
for item in all_toplevel_items:
|
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
|
# sort BOM by category first, then alphabetically by description within category
|
||||||
self.bom = dict(
|
self.bom = dict(
|
||||||
sorted(
|
sorted(
|
||||||
self.bom.items(),
|
self.bom.items(),
|
||||||
key=lambda x: (
|
key=lambda x: (
|
||||||
x[1]["category"],
|
x[1].category,
|
||||||
x[0].description,
|
x[1].description,
|
||||||
), # x[0] = key, x[1] = value
|
), # x[0] = key, x[1] = value
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
next_id = len(self.shared_bom) + 1
|
next_id = len(self.shared_bom) + 1
|
||||||
# TODO: for each harness, track a (harness_name, qty) pair
|
# 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():
|
for key, values in self.bom.items():
|
||||||
if key in self.shared_bom:
|
if key in self.shared_bom:
|
||||||
self.shared_bom[key]["qty"] += values["qty"]
|
self.shared_bom[key]["qty"] += values["qty"]
|
||||||
values["id"] = self.shared_bom[key]["id"]
|
values["id"] = self.shared_bom[key]["id"]
|
||||||
continue
|
else:
|
||||||
self.shared_bom[key] = values
|
self.shared_bom[key] = values
|
||||||
self.shared_bom[key]["id"] = next_id
|
self.shared_bom[key]["id"] = next_id
|
||||||
values["id"] = next_id
|
values["id"] = next_id
|
||||||
next_id += 1
|
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
|
# 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:
|
for item in all_bom_relevant_items:
|
||||||
if item.ignore_in_bom:
|
if item.ignore_in_bom:
|
||||||
continue
|
continue
|
||||||
if not item.bom_hash in self.bom:
|
if hash(item) not in self.bom:
|
||||||
print(f"{item}'s hash' not found in BOM dict.")
|
|
||||||
continue
|
continue
|
||||||
item.bom_id = self.bom[item.bom_hash]["id"]
|
item.id = self.bom[hash(item)].id
|
||||||
|
|
||||||
self.bom = dict(
|
self.bom = dict(
|
||||||
sorted(
|
sorted(
|
||||||
self.bom.items(),
|
self.bom.items(),
|
||||||
key=lambda x: (x[1]["id"],),
|
key=lambda x: (x[1].id,),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# print_bom_table(self.bom) # for debugging
|
# from wireviz.wv_bom import print_bom_table ; 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}")
|
|
||||||
|
|
||||||
def connect(
|
def connect(
|
||||||
self,
|
self,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user