diff --git a/src/wireviz/wv_bom.py b/src/wireviz/wv_bom.py index 68a29af..0c769f3 100644 --- a/src/wireviz/wv_bom.py +++ b/src/wireviz/wv_bom.py @@ -22,10 +22,10 @@ BomCategory = Enum( "BomEntry", "CONNECTOR CABLE WIRE ADDITIONAL_INSIDE ADDITIONAL_OUTSIDE" ) QtyMultiplierConnector = Enum( - "QtyMultiplierConnector", "ONE PINCOUNT POPULATED CONNECTIONS" + "QtyMultiplierConnector", "PINCOUNT POPULATED CONNECTIONS" ) QtyMultiplierCable = Enum( - "QtyMultiplierCable", "ONE WIRECOUNT TERMINATION LENGTH TOTAL_LENGTH" + "QtyMultiplierCable", "WIRECOUNT TERMINATION LENGTH TOTAL_LENGTH" ) PART_NUMBER_HEADERS = PartNumberInfo( diff --git a/src/wireviz/wv_dataclasses.py b/src/wireviz/wv_dataclasses.py index bad04fb..4e74273 100644 --- a/src/wireviz/wv_dataclasses.py +++ b/src/wireviz/wv_dataclasses.py @@ -262,7 +262,7 @@ class Component: @dataclass class AdditionalComponent(Component): qty_multiplier: Union[QtyMultiplierConnector, QtyMultiplierCable, int] = 1 - qty_multipliers_computed: Dict = field(default_factory=list) + _qty_multiplier_computed: Union[int, float] = 1 designators: Optional[str] = None # used for components definedi in the # additional_bom_items section within another component bgcolor: SingleColor = None # ^ same here @@ -288,12 +288,11 @@ class AdditionalComponent(Component): @property def bom_qty(self): - return 999 + return self.qty.number * self._qty_multiplier_computed @property def description(self) -> str: - substrs = [self.type, self.subtype if self.subtype else ""] - return ", ".join(substrs) + return f"{self.type}{', ' + self.subtype if self.subtype else ''}" @dataclass @@ -450,32 +449,26 @@ class Connector(TopLevelGraphicalComponent): self.ports_right = True def compute_qty_multipliers(self): + # do not run before all connections in harness have been made! + num_populated_pins = len( + [pin for pin in self.pin_objects.values() if pin._num_connections > 0] + ) + num_connections = sum( + [pin._num_connections for pin in self.pin_objects.values()] + ) + qty_multipliers_computed = { + "PINCOUNT": self.pincount, + "POPULATED": num_populated_pins, + "CONNECTIONS": num_connections, + } for subitem in self.additional_components: - populated_pins = [] - subitem.qty_multipliers_computed["ONE"] = 1 - subitem.qty_multipliers_computed["PINCOUNT"] = self.pincount - subitem.qty_multipliers_computed["POPULATED"] = 999 - subitem.qty_multipliers_computed["CONNECTIONS"] = 999 - - # QtyMultiplierConnector = Enum( - # "QtyMultiplierConnector", "ONE PINCOUNT POPULATED CONNECTIONS" - # ) - # QtyMultiplierCable = Enum( - # "QtyMultiplierCable", "ONE WIRECOUNT TERMINATION LENGTH TOTAL_LENGTH" - # ) - - # def get_qty_multiplier(self, qty_multiplier: Optional[ConnectorMultiplier]) -> int: - # # TODO!!! how and when to compute final qty for additional components??? - # if not qty_multiplier: - # return 1 - # elif qty_multiplier == "pincount": - # return self.pincount - # elif qty_multiplier == "populated": - # return sum(self.visible_pins.values()) - # else: - # raise ValueError( - # f"invalid qty multiplier parameter for connector {qty_multiplier}" - # ) + if isinstance(subitem.qty_multiplier, QtyMultiplierConnector): + computed_factor = qty_multipliers_computed[subitem.qty_multiplier.name] + elif isinstance(subitem.qty_multiplier, QtyMultiplierCable): + raise Exception("Used a cable multiplier in a connector!") + else: # int or float + computed_factor = subitem.qty_multiplier + subitem._qty_multiplier_computed = computed_factor @dataclass @@ -744,21 +737,37 @@ class Cable(TopLevelGraphicalComponent): via_wire_obj = self.wire_objects[via_wire_id] self._connections.append(Connection(from_pin_obj, via_wire_obj, to_pin_obj)) - # def get_qty_multiplier(self, qty_multiplier: Optional[CableMultiplier]) -> float: - # if not qty_multiplier: - # return 1 - # elif qty_multiplier == "wirecount": - # return self.wirecount - # elif qty_multiplier == "terminations": - # return len(self.connections) - # elif qty_multiplier == "length": - # return self.length - # elif qty_multiplier == "total_length": - # return self.length * self.wirecount - # else: - # raise ValueError( - # f"invalid qty multiplier parameter for cable {qty_multiplier}" - # ) + def compute_qty_multipliers(self): + # do not run before all connections in harness have been made! + total_length = sum( + [ + wire.length.number if wire.length else 0 + for wire in self.wire_objects.values() + ] + ) + qty_multipliers_computed = { + "WIRECOUNT": len(self.wire_objects), + "TERMINATIONS": 999, # TODO + "LENGTH": self.length.number if self.length else 0, + "TOTAL_LENGTH": total_length, + } + for subitem in self.additional_components: + if isinstance(subitem.qty_multiplier, QtyMultiplierCable): + computed_factor = qty_multipliers_computed[subitem.qty_multiplier.name] + # inherit component's length unit if appropriate + if subitem.qty_multiplier.name in ["LENGTH", "TOTAL_LENGTH"]: + if subitem.qty.unit is not None: + raise Exception( + f"No unit may be specified when using" + f"{subitem.qty_multiplier} as a multiplier" + ) + subitem.qty = NumberAndUnit(subitem.qty.number, self.length.unit) + + elif isinstance(subitem.qty_multiplier, QtyMultiplierConnector): + raise Exception("Used a connector multiplier in a cable!") + else: # int or float + computed_factor = subitem.qty_multiplier + subitem._qty_multiplier_computed = computed_factor @dataclass diff --git a/src/wireviz/wv_graphviz.py b/src/wireviz/wv_graphviz.py index e9b1af8..a6f191d 100644 --- a/src/wireviz/wv_graphviz.py +++ b/src/wireviz/wv_graphviz.py @@ -64,8 +64,7 @@ def gv_node_component(component: Component) -> Table: x = colorbar_cell(component.color) if component.color else None line_image, line_image_caption = image_and_caption_cells(component) - # line_additional_component_table = get_additional_component_table(self, connector) - line_additional_component_table = None + line_additional_component_table = gv_additional_component_table(component) line_notes = [html_line_breaks(component.notes)] if isinstance(component, Connector): @@ -100,6 +99,25 @@ def gv_node_component(component: Component) -> Table: return tbl +def gv_additional_component_table(component): + if not component.additional_components: + return None + + rows = [] + for subitem in component.additional_components: + rows.append( + Tr( + [ + Td(f"{subitem.bom_qty}"), + Td(f"{subitem.qty.unit if subitem.qty.unit else 'x'}"), + Td(f"{subitem.description}"), + ] + ) + ) + + return Table(rows) + + def calculate_node_bgcolor(component, harness_options): # assign component node bgcolor at the GraphViz node level # instead of at the HTML table level for better rendering of node outline diff --git a/src/wireviz/wv_harness.py b/src/wireviz/wv_harness.py index 707d8ba..2dd00be 100644 --- a/src/wireviz/wv_harness.py +++ b/src/wireviz/wv_harness.py @@ -142,6 +142,10 @@ class Harness: 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