Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BOM generation refactoring #249

Closed
wants to merge 9 commits into from
179 changes: 133 additions & 46 deletions src/wireviz/DataClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

from wireviz.wv_helper import int2tuple, aspect_ratio
from wireviz.wv_colors import Color, Colors, ColorMode, ColorScheme, COLOR_CODES

from wireviz.wv_bom_new import Bom_hash, Bom_hash_list
from wireviz.wv_gv_html import nested_html_table, bom_bubble

# Each type alias have their legal values described in comments - validation might be implemented in the future
PlainText = str # Text not containing HTML tags nor newlines
Expand Down Expand Up @@ -41,6 +42,7 @@ class Options:
bgcolor_cable: Optional[Color] = None
bgcolor_bundle: Optional[Color] = None
color_mode: ColorMode = 'SHORT'
show_bom_ids: bool = False
mini_bom_mode: bool = True

def __post_init__(self):
Expand Down Expand Up @@ -98,52 +100,109 @@ def __post_init__(self, gv_dir):


@dataclass
class AdditionalComponent:
type: MultilineHypertext
subtype: Optional[MultilineHypertext] = None
manufacturer: Optional[MultilineHypertext] = None
mpn: Optional[MultilineHypertext] = None
supplier: Optional[MultilineHypertext] = None
spn: Optional[MultilineHypertext] = None
pn: Optional[Hypertext] = None
class Component:
type: Union[MultilineHypertext, List[MultilineHypertext]] = None
subtype: Union[MultilineHypertext, List[MultilineHypertext]] = None
category: Optional[str] = None # currently only used by cables, to define bundles

pn: Union[Hypertext, List[Hypertext], None] = None
manufacturer: Union[MultilineHypertext, List[MultilineHypertext], None] = None
mpn: Union[MultilineHypertext, List[MultilineHypertext], None] = None
supplier: Union[MultilineHypertext, List[MultilineHypertext], None] = None
spn: Union[MultilineHypertext, List[MultilineHypertext], None] = None

ignore_in_bom: bool = False
bom_id: Optional[str] = None # to be filled after harness is built

@property
def bom_hash(self) -> Bom_hash:

def force_list(inp):
if isinstance(inp, list):
return inp
else:
return [inp for i in range(len(self.colors))]

if self.category == 'bundle':
# create a single item that includes the necessary fields,
# which may or may not be lists
_hash_list = Bom_hash_list(
self.description,
self.unit,
self.pn,
self.manufacturer,
self.mpn,
self.supplier,
self.spn,
)
# convert elements that are not lists, into lists
_hash_matrix = list(map(force_list, [elem for elem in _hash_list]))
# transpose list of lists, convert to tuple for next step
_hash_matrix = list(map(tuple, zip(*_hash_matrix)))
# generate list of Bom_hashes
hash_list = [Bom_hash(*item) for item in _hash_matrix]
return hash_list
else:
return Bom_hash(
self.description,
self.unit,
self.pn,
self.manufacturer,
self.mpn,
self.supplier,
self.spn,
)


@dataclass
class GraphicalComponent(Component):
bgcolor: Optional[Color] = None

@dataclass
class AdditionalComponent(Component):
qty: float = 1
unit: Optional[str] = None
qty_multiplier: Union[ConnectorMultiplier, CableMultiplier, None] = None
bgcolor: Optional[Color] = None
designators: Optional[str] = None # used for components define in the `additional_bom_items` YAML section

@property
def description(self) -> str:
return self.type.rstrip() + (f', {self.subtype.rstrip()}' if self.subtype else '')


@dataclass
class Connector:
name: Designator
bgcolor: Optional[Color] = None
class TopLevelGraphicalComponent(GraphicalComponent):
name: Designator = None
bgcolor_title: Optional[Color] = None
manufacturer: Optional[MultilineHypertext] = None
mpn: Optional[MultilineHypertext] = None
supplier: Optional[MultilineHypertext] = None
spn: Optional[MultilineHypertext] = None
pn: Optional[Hypertext] = None
style: Optional[str] = None
category: Optional[str] = None
type: Optional[MultilineHypertext] = None
subtype: Optional[MultilineHypertext] = None
pincount: Optional[int] = None
color: Optional[Color] = None
image: Optional[Image] = None
notes: Optional[MultilineHypertext] = None
additional_components: List[AdditionalComponent] = field(default_factory=list)

show_name: bool = True

def gen_add_bom_table(self):
if self.additional_components:
rows = []
for comp in self.additional_components:
rows.append([bom_bubble(comp.bom_id), comp.qty, comp.description, comp.pn])
return rows
else:
return None


@dataclass
class Connector(TopLevelGraphicalComponent):
style: Optional[str] = None
pincount: Optional[int] = None
pins: List[Pin] = field(default_factory=list)
pinlabels: List[Pin] = field(default_factory=list)
pincolors: List[Color] = field(default_factory=list)
color: Optional[Color] = None
show_name: Optional[bool] = None
show_pincount: Optional[bool] = None
hide_disconnected_pins: bool = False
autogenerate: bool = False
loops: List[List[Pin]] = field(default_factory=list)
ignore_in_bom: bool = False
additional_components: List[AdditionalComponent] = field(default_factory=list)
unit = None

def __post_init__(self) -> None:

Expand Down Expand Up @@ -188,10 +247,11 @@ def __post_init__(self) -> None:
if isinstance(item, dict):
self.additional_components[i] = AdditionalComponent(**item)


def activate_pin(self, pin: Pin) -> None:
self.visible_pins[pin] = True

def get_qty_multiplier(self, qty_multiplier: Optional[ConnectorMultiplier]) -> int:
def qty_factor(self, qty_multiplier: Optional[ConnectorMultiplier]) -> int:
if not qty_multiplier:
return 1
elif qty_multiplier == 'pincount':
Expand All @@ -201,37 +261,32 @@ def get_qty_multiplier(self, qty_multiplier: Optional[ConnectorMultiplier]) -> i
else:
raise ValueError(f'invalid qty multiplier parameter for connector {qty_multiplier}')

@property
def description(self) -> str:
substrs = [
'Connector',
self.type,
self.subtype,
self.pincount if self.show_pincount else None,
str(self.color) + ' (reimplement color translation!)' if self.color else None, # translate_color(self.color, harness.options.color_mode)] <- get harness.color_mode!
]
return ', '.join([str(s) for s in substrs if s is not None and s != ''])


@dataclass
class Cable:
name: Designator
bgcolor: Optional[Color] = None
bgcolor_title: Optional[Color] = None
manufacturer: Union[MultilineHypertext, List[MultilineHypertext], None] = None
mpn: Union[MultilineHypertext, List[MultilineHypertext], None] = None
supplier: Union[MultilineHypertext, List[MultilineHypertext], None] = None
spn: Union[MultilineHypertext, List[MultilineHypertext], None] = None
pn: Union[Hypertext, List[Hypertext], None] = None
category: Optional[str] = None
type: Optional[MultilineHypertext] = None
class Cable(TopLevelGraphicalComponent):
gauge: Optional[float] = None
gauge_unit: Optional[str] = None
show_equiv: bool = False
length: float = 0
length_unit: Optional[str] = None
color: Optional[Color] = None
wirecount: Optional[int] = None
shield: Union[bool, Color] = False
image: Optional[Image] = None
notes: Optional[MultilineHypertext] = None
colors: List[Colors] = field(default_factory=list)
wirelabels: List[Wire] = field(default_factory=list)
color_code: Optional[ColorScheme] = None
show_name: bool = True
show_wirecount: bool = True
show_wirenumbers: Optional[bool] = None
ignore_in_bom: bool = False
additional_components: List[AdditionalComponent] = field(default_factory=list)

def __post_init__(self) -> None:

Expand Down Expand Up @@ -318,6 +373,7 @@ def __post_init__(self) -> None:
if isinstance(item, dict):
self.additional_components[i] = AdditionalComponent(**item)


# The *_pin arguments accept a tuple, but it seems not in use with the current code.
def connect(self, from_name: Optional[Designator], from_pin: NoneOrMorePinIndices, via_wire: OneOrMoreWires,
to_name: Optional[Designator], to_pin: NoneOrMorePinIndices) -> None:
Expand All @@ -329,7 +385,7 @@ def connect(self, from_name: Optional[Designator], from_pin: NoneOrMorePinIndice
for i, _ in enumerate(from_pin):
self.connections.append(Connection(from_name, from_pin[i], via_wire[i], to_name, to_pin[i]))

def get_qty_multiplier(self, qty_multiplier: Optional[CableMultiplier]) -> float:
def qty_factor(self, qty_multiplier: Optional[CableMultiplier]) -> float:
if not qty_multiplier:
return 1
elif qty_multiplier == 'wirecount':
Expand All @@ -343,6 +399,37 @@ def get_qty_multiplier(self, qty_multiplier: Optional[CableMultiplier]) -> float
else:
raise ValueError(f'invalid qty multiplier parameter for cable {qty_multiplier}')

@property
def unit(self): # for compatibility with parent class
return self.length_unit

@property
def description(self) -> str:
if self.category == 'bundle':
desc_list = []
for index, color in enumerate(self.colors):
substrs = [
'Wire',
self.type,
self.subtype,
f'{self.gauge} {self.gauge_unit}' if self.gauge else None,
str(self.color) + ' (reimplement color translation!)' if self.color else None, # translate_color(self.color, harness.options.color_mode)] <- get harness.color_mode!
]
desc_list.append(', '.join([s for s in substrs if s is not None and s != '']))
return desc_list
else:
substrs = [
('', 'Cable'),
(', ', self.type),
(', ', self.subtype),
(', ', self.wirecount),
(' ', f'x {self.gauge} {self.gauge_unit}' if self.gauge else ' wires'),
(' ', 'shielded' if self.shield else None),
(', ', str(self.color) + ' (reimplement color translation!)' if self.color else None)
]
desc = ''.join([f'{s[0]}{s[1]}' for s in substrs if s[1] is not None and s[1] != ''])
return desc


@dataclass
class Connection:
Expand Down
Loading