diff --git a/src/wireviz/DataClasses.py b/src/wireviz/DataClasses.py
index a27ae0ea..3e01ae1d 100644
--- a/src/wireviz/DataClasses.py
+++ b/src/wireviz/DataClasses.py
@@ -88,6 +88,7 @@ class Cable:
show_name: bool = True
show_wirecount: bool = True
ignore_in_bom: bool = False
+ additional_components: List[Any] = None
def __post_init__(self):
diff --git a/src/wireviz/Harness.py b/src/wireviz/Harness.py
index 449aa87e..f4077217 100644
--- a/src/wireviz/Harness.py
+++ b/src/wireviz/Harness.py
@@ -113,25 +113,25 @@ def create_graph(self) -> Graph:
'']
if connector.additional_components is not None:
rows.append(["Additional components"])
- for extra in connector.additional_components:
- if 'qty' in extra:
- if isinstance(extra['qty'], int) or isinstance(extra['qty'], float):
- qty = extra['qty']
- else: # check for special quantities
- if extra['qty'] == 'pincount':
- qty = connector.pincount
- elif extra['qty'] == 'connectioncount':
- qty = sum(1 for value in connector.visible_pins.values() if value is True)
- else:
- raise ValueError('invalid aty parameter')
- else:
- qty = 1
- rows.append([extra["type"], qty])
- rows.append([extra["manufacturer"],
- f'MPN: {extra["manufacturer_part_number"]}' if "manufacturer_part_number" in extra else None,
- f'IPN: {extra["internal_part_number"]}' if "internal_part_number" in extra else None],)
- rows.append([html_line_breaks(connector.notes)])
- html = nested_html_table(rows)
+ for extra in connector.additional_components:
+ if 'qty' in extra:
+ if isinstance(extra['qty'], int) or isinstance(extra['qty'], float):
+ qty = extra['qty']
+ else: # check for special quantities
+ if extra['qty'] == 'pincount':
+ qty = connector.pincount
+ elif extra['qty'] == 'connectioncount':
+ qty = sum(1 for value in connector.visible_pins.values() if value is True)
+ else:
+ raise ValueError('invalid aty parameter')
+ else:
+ qty = 1
+ rows.append([extra["type"], qty])
+ rows.append([extra["manufacturer"],
+ f'MPN: {extra["manufacturer_part_number"]}' if "manufacturer_part_number" in extra else None,
+ f'IPN: {extra["internal_part_number"]}' if "internal_part_number" in extra else None],)
+ rows.append([html_line_breaks(connector.notes)])
+ html = nested_html_table(rows)
pinouts = []
for pinnumber, pinname in zip(connector.pinnumbers, connector.pinout):
@@ -263,6 +263,44 @@ def create_graph(self) -> Graph:
html = f'{html}' # conductor table
html = f'{html}' # main table
+
+ if cable.additional_components is not None:
+ html = f'{html}
Additional components |
' # notes table
+ for extra in cable.additional_components:
+ if 'qty' in extra:
+ if isinstance(extra['qty'], int) or isinstance(extra['qty'], float):
+ qty = extra['qty']
+ else: # check for special quantities
+ if extra['qty'] == 'wirecount':
+ qty = cable.wirecount
+ elif extra['qty'] == 'terminations':
+ qty = len(cable.connections)
+ elif extra['qty'] == 'length':
+ qty = cable.length
+ elif extra['qty'] == 'total_length':
+ qty = cable.length * cable.wirecount
+ else:
+ raise ValueError('invalid aty parameter {}'.format(extra["qty"]))
+ else:
+ qty = 1
+
+ html = f'{html}'
+ html = f'{html}{extra["type"]} | '
+ html = f'{html}{qty} | '
+ html = f'{html}
|
'
+
+ identification = [extra.get("manufacturer", None),
+ f'MPN: {extra["manufacturer_part_number"]}' if "manufacturer_part_number" in extra else None,
+ f'IPN: {extra["internal_part_number"]}' if "internal_part_number" in extra else None]
+ identification = list(filter(None, identification))
+ if(len(identification) > 0): # print an identification row if values specified
+ html = f'{html}'
+ for attrib in identification[0:-1]:
+ html = f'{html}{attrib} | ' # all columns except last have a border on the right (sides="R")
+ if len(identification) > 0:
+ html = f'{html}{identification[-1]} | ' # last column has no border on the right because the enclosing table borders it
+ html = f'{html}
|
' # end identification row
+
if cable.notes:
html = f'{html}{html_line_breaks(cable.notes)} |
' # notes table
html = f'{html} |
' # spacer at the end
@@ -363,6 +401,7 @@ def bom(self):
bom_connectors = []
bom_connectors_extra = []
bom_cables = []
+ bom_cables_extra = []
bom_extra = []
# connectors
connector_group = lambda c: (c.type, c.subtype, c.pincount, c.manufacturer, c.manufacturer_part_number, c.internal_part_number)
@@ -471,6 +510,52 @@ def bom(self):
bom_cables = sorted(bom_cables, key=lambda k: k['item']) # sort list of dicts by their values (https://stackoverflow.com/a/73050)
bom.extend(bom_cables)
+ cables_extra = []
+ for cable in self.cables.values():
+ if cable.additional_components:
+ for part in cable.additional_components:
+ if 'qty' in part:
+ if isinstance(part['qty'], int) or isinstance(part['qty'], float):
+ qty = part['qty']
+ else: # check for special quantities
+ if part['qty'] == 'wirecount':
+ qty = cable.wirecount
+ elif part['qty'] == 'terminations':
+ qty = len(cable.connections)
+ elif part['qty'] == 'length':
+ qty = cable.length
+ elif part['qty'] == 'total_length':
+ qty = cable.length * cable.wirecount
+ else:
+ raise ValueError('invalid aty parameter')
+ else:
+ qty = 1
+ cables_extra.append(
+ {
+ 'type': part.get('type', None),
+ 'qty': qty,
+ 'unit': part.get('unit', None),
+ 'manufacturer': part.get('manufacturer', None),
+ 'manufacturer part number': part.get('manufacturer_part_number', None),
+ 'internal part number': part.get('internal_part_number', None),
+ 'designator': connector.name
+ }
+ )
+ cables_extra_group = lambda ce: (ce['type'], ce['qty'], ce['unit'], ce['manufacturer'], ce['manufacturer part number'], ce['internal part number'])
+ for group in Counter([connector_extra_group(v) for v in cables_extra]):
+ items = [v for v in cables_extra if connector_extra_group(v) == group]
+ shared = items[0]
+ designators = [i['designator'] for i in items]
+ designators = list(dict.fromkeys(designators)) # remove duplicates
+ designators.sort()
+ total_qty = sum(i['qty'] for i in items)
+
+ item = {'item': shared['type'], 'qty': round(total_qty, 3), 'unit': shared['unit'], 'designators': designators,
+ 'manufacturer': shared['manufacturer'], 'manufacturer part number': shared['manufacturer part number'], 'internal part number': shared['internal part number']}
+ bom_cables_extra.append(item)
+ bom_cables_extra = sorted(bom_cables_extra, key=lambda k: k['item']) # sort list of dicts by their values (https://stackoverflow.com/a/73050)
+ bom.extend(bom_cables_extra)
+
for item in self.additional_bom_items:
name = item['description'] if item.get('description', None) else ''
if isinstance(item.get('designators', None), List):