diff --git a/examples/demo02.yml b/examples/demo02.yml
index 5002e7d..ca5cadf 100644
--- a/examples/demo02.yml
+++ b/examples/demo02.yml
@@ -1,69 +1,126 @@
-templates: # defining templates to be used later on
- - &molex_f
- type: Molex KK 254
- subtype: female
- - &con_i2c
- pinlabels: [GND, +5V, SCL, SDA]
- - &wire_i2c
- category: bundle
- gauge: 0.14 mm2
- colors: [BK, RD, YE, GN]
-
connectors:
X1:
- <<: *molex_f # copying items from the template
- pinlabels: [GND, +5V, SCL, SDA, MISO, MOSI, SCK, N/C]
+ type: KK 254
+ pn: CON-245-8
+ manufacturer: Molex
+ mpn: '0022013087'
+ subtype: female
+ pincount: 8
+ pinlabels: [Audio L, Audio R, Audio GND, N/C, I2C GND, I2C +5V, SCL, SDA]
+ additional_parameters:
+ Sleeving removal: 20 mm
+ Insulation removal: 2 mm
+ additional_components:
+ -
+ type: Crimp
+ pn: CRI-254
+ manufacturer: Molex
+ mpn: '008500032'
+ qty_multiplier: populated
+ -
+ type: Label
+ pn: LAB-444
+ note: '"C745-X1"'
+ notes: |
+ - Attach to main PCB
+ - Ensure proper contact
+ - Clamp down cables after attaching
X2:
- <<: *molex_f
- <<: *con_i2c # it is possible to copy from more than one template
+ type: 3.5 mm
+ subtype: jack
+ color: BK
+ pins: [T,R,S]
+ show_pincount: false
+ pinlabels: [L, R, GND]
+ image:
+ src: resources/stereo-phone-plug-TRS.png
+ caption: Tip, Ring, and Sleeve
X3:
- <<: *molex_f
- <<: *con_i2c
+ type: KK 254
+ subtype: female
+ pinlabels: [VCC, GND, SCL, SDA]
+ pincolors: [RD, BK, GN, BU]
X4:
- <<: *molex_f
- pinlabels: [GND, +12V, MISO, MOSI, SCK]
- ferrule_crimp:
+ type: D-Sub
+ subtype: female
+ pincount: 9
+ pn: CON-D9-F
+ pinlabels: [GND, +5V, SCL, SDA, N/C, +12V IN, GND, +12V OUT, GND]
+ additional_components:
+ -
+ type: Casing, plastic
+ pn: CAS-D9
+ -
+ type: Mounting screws, M3 x 8
+ qty: 2
+ F:
style: simple
autogenerate: true
type: Crimp ferrule
- subtype: 0.25 mm²
- color: YE
+ subtype: 0.5 mm²
+ color: OG
cables:
W1:
- <<: *wire_i2c
- length: 0.2
- show_equiv: true
- W2:
- <<: *wire_i2c
- length: 0.4
- show_equiv: true
- W3:
- category: bundle
- gauge: 0.14 mm2
- length: 0.3
- colors: [BK, BU, OG, VT]
- show_equiv: true
- W4:
+ wirecount: 3
+ shield: true
+ length: 0.5
gauge: 0.25 mm2
- length: 0.3
- colors: [BK, RD]
show_equiv: true
+ colors: [WH, RD, BK]
+ color: GY
+ additional_components:
+ -
+ type: Heatshrink D=5mm
+ qty: 15
+ unit: mm
+ note: left
+ -
+ type: Heatshrink D=5mm
+ qty: 25
+ unit: mm
+ note: right
+ W2: &wire_i2c
+ wirecount: 4
+ length: 0.2
+ gauge: 0.25 mm2
+ show_equiv: true
+ color_code: IEC
+ W3:
+ <<: *wire_i2c
+ color_code: DIN
+ W4: &wire_power
+ category: bundle
+ show_name: false
+ wirecount: 2
+ colors: [RD, BK]
+ gauge: 0.5 mm2
+ show_equiv: true
+ length: 1.0
+ additional_parameters:
+ Twist rate: 10/m
+ Twist direction: CCW
+ W5:
+ <<: *wire_power
connections:
-
- - X1: [1-4]
- - W1: [1-4]
- - X2: [1-4]
+ - X1: [Audio L, Audio R, Audio GND, Audio GND]
+ - W1: [1-3,s]
+ - X2: [T,R,S,S]
-
- - X1: [1-4]
+ - X1: [I2C GND, I2C +5V, SCL, SDA]
- W2: [1-4]
- - X3: [1-4]
+ - X3: [GND, VCC, SCL, SDA]
-
- - X1: [1,5-7]
+ - X1: [I2C GND, I2C +5V, SCL, SDA]
- W3: [1-4]
- - X4: [1,3-5]
+ - X4: [1,2,3,4]
-
- - ferrule_crimp
+ - F
- W4: [1,2]
- - X4: [1,2]
+ - X4: [6,7]
+ -
+ - X4: [8,9]
+ - W5: [1,2]
+ - F
diff --git a/src/wireviz/DataClasses.py b/src/wireviz/DataClasses.py
index fac6ab5..6aa9e66 100644
--- a/src/wireviz/DataClasses.py
+++ b/src/wireviz/DataClasses.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-from typing import Optional, List, Tuple, Union
+from typing import Optional, List, Dict, Tuple, Union
from dataclasses import dataclass, field, InitVar
from pathlib import Path
@@ -76,6 +76,7 @@ class AdditionalComponent:
qty: float = 1
unit: Optional[str] = None
qty_multiplier: Union[ConnectorMultiplier, CableMultiplier, None] = None
+ note: Optional[str] = None
@property
def description(self) -> str:
@@ -93,6 +94,7 @@ class Connector:
type: Optional[MultilineHypertext] = None
subtype: Optional[MultilineHypertext] = None
pincount: Optional[int] = None
+ additional_parameters: Optional[Dict] = None
image: Optional[Image] = None
notes: Optional[MultilineHypertext] = None
pinlabels: List[Pin] = field(default_factory=list)
@@ -104,6 +106,7 @@ class Connector:
hide_disconnected_pins: bool = False
autogenerate: bool = False
loops: List[List[Pin]] = field(default_factory=list)
+ bom_item_number: Optional[int] = None
ignore_in_bom: bool = False
additional_components: List[AdditionalComponent] = field(default_factory=list)
@@ -180,6 +183,7 @@ class Cable:
color: Optional[Color] = None
wirecount: Optional[int] = None
shield: Union[bool, Color] = False
+ additional_parameters: Optional[Dict] = None
image: Optional[Image] = None
notes: Optional[MultilineHypertext] = None
colors: List[Colors] = field(default_factory=list)
@@ -188,6 +192,7 @@ class Cable:
show_name: bool = True
show_wirecount: bool = True
show_wirenumbers: Optional[bool] = None
+ bom_item_number: Optional[int] = None
ignore_in_bom: bool = False
additional_components: List[AdditionalComponent] = field(default_factory=list)
diff --git a/src/wireviz/Harness.py b/src/wireviz/Harness.py
index 9d83a3d..486dfdd 100644
--- a/src/wireviz/Harness.py
+++ b/src/wireviz/Harness.py
@@ -12,8 +12,8 @@ from wireviz import wv_colors, __version__, APP_NAME, APP_URL
from wireviz.DataClasses import Connector, Cable
from wireviz.wv_colors import get_color_hex
from wireviz.wv_gv_html import nested_html_table, html_colorbar, html_image, \
- html_caption, remove_links, html_line_breaks
-from wireviz.wv_bom import manufacturer_info_field, component_table_entry, \
+ html_caption, remove_links, html_line_breaks, bom_bubble, nested_html_table_dict
+from wireviz.wv_bom import manufacturer_info_field, \
get_additional_component_table, bom_list, generate_bom
from wireviz.wv_html import generate_html_output
from wireviz.wv_helper import awg_equiv, mm2_equiv, tuplelist2tsv, flatten2d, \
@@ -24,7 +24,8 @@ class Harness:
def __init__(self):
self.color_mode = 'SHORT'
- self.mini_bom_mode = True
+ self.show_part_numbers = True # TODO: Make configurable via YAML
+ self.show_bom_item_numbers = True # TODO: Make configurable via YAML
self.connectors = {}
self.cables = {}
self._bom = [] # Internal Cache for generated bom
@@ -114,17 +115,20 @@ class Harness:
html = []
rows = [[remove_links(connector.name) if connector.show_name else None],
- [f'P/N: {remove_links(connector.pn)}' if connector.pn else None,
- html_line_breaks(manufacturer_info_field(connector.manufacturer, connector.mpn))],
- [html_line_breaks(connector.type),
+ [bom_bubble(connector.bom_item_number) if self.show_bom_item_numbers else None, # TODO: Show actual BOM number
+ html_line_breaks(connector.type),
html_line_breaks(connector.subtype),
f'{connector.pincount}-pin' if connector.show_pincount else None,
connector.color, html_colorbar(connector.color)],
+ [f'P/N: {remove_links(connector.pn)}' if connector.pn else None,
+ html_line_breaks(manufacturer_info_field(connector.manufacturer, connector.mpn))] if self.show_part_numbers else None,
+ nested_html_table_dict(connector.additional_parameters),
'' if connector.style != 'simple' else None,
[html_image(connector.image)],
[html_caption(connector.image)]]
- rows.extend(get_additional_component_table(self, connector))
+ rows.append(get_additional_component_table(self, connector))
rows.append([html_line_breaks(connector.notes)])
+
html.extend(nested_html_table(rows))
if connector.style != 'simple':
@@ -195,21 +199,23 @@ class Harness:
awg_fmt = f' ({mm2_equiv(cable.gauge)} mm\u00B2)'
rows = [[remove_links(cable.name) if cable.show_name else None],
- [f'P/N: {remove_links(cable.pn)}' if (cable.pn and not isinstance(cable.pn, list)) else None,
- html_line_breaks(manufacturer_info_field(
- cable.manufacturer if not isinstance(cable.manufacturer, list) else None,
- cable.mpn if not isinstance(cable.mpn, list) else None))],
- [html_line_breaks(cable.type),
+ [bom_bubble(cable.bom_item_number) if self.show_bom_item_numbers else None, # TODO: Show actual BOM number
+ html_line_breaks(cable.type),
f'{cable.wirecount}x' if cable.show_wirecount else None,
f'{cable.gauge} {cable.gauge_unit}{awg_fmt}' if cable.gauge else None,
'+ S' if cable.shield else None,
f'{cable.length} {cable.length_unit}' if cable.length > 0 else None,
cable.color, html_colorbar(cable.color)],
+ [f'P/N: {remove_links(cable.pn)}' if (cable.pn and not isinstance(cable.pn, list)) else None,
+ html_line_breaks(manufacturer_info_field(
+ cable.manufacturer if not isinstance(cable.manufacturer, list) else None,
+ cable.mpn if not isinstance(cable.mpn, list) else None))],
+ nested_html_table_dict(cable.additional_parameters),
'',
[html_image(cable.image)],
[html_caption(cable.image)]]
- rows.extend(get_additional_component_table(self, cable))
+ rows.append(get_additional_component_table(self, cable)) # TODO: reimplement
rows.append([html_line_breaks(cable.notes)])
html.extend(nested_html_table(rows))
diff --git a/src/wireviz/wv_bom.py b/src/wireviz/wv_bom.py
index f71abc1..b0ac666 100644
--- a/src/wireviz/wv_bom.py
+++ b/src/wireviz/wv_bom.py
@@ -6,7 +6,7 @@ from itertools import groupby
from typing import Any, Dict, List, Optional, Tuple, Union
from wireviz.DataClasses import AdditionalComponent, Connector, Cable
-from wireviz.wv_gv_html import html_line_breaks
+from wireviz.wv_gv_html import html_line_breaks, bom_bubble
from wireviz.wv_helper import clean_whitespace
BOMColumn = str # = Literal['id', 'description', 'qty', 'unit', 'designators', 'pn', 'manufacturer', 'mpn']
@@ -20,18 +20,48 @@ def get_additional_component_table(harness: "Harness", component: Union[Connecto
"""Return a list of diagram node table row strings with additional components."""
rows = []
if component.additional_components:
- rows.append(["Additional components"])
+ parts = []
for part in component.additional_components:
- common_args = {
- 'qty': part.qty * component.get_qty_multiplier(part.qty_multiplier),
- 'unit': part.unit,
- }
- if harness.mini_bom_mode:
- id = get_bom_index(harness.bom(), part)
- rows.append(component_table_entry(f'#{id} ({part.type.rstrip()})', **common_args))
- else:
- rows.append(component_table_entry(part.description, **common_args, **optional_fields(part)))
- return rows
+ # if True:
+ # id = get_bom_index(harness.bom(), part)
+ # rows.append(component_table_entry(f'#{id} ({part.type.rstrip()})', **common_args))
+ # else:
+ # rows.append(component_table_entry(part.description, **common_args, **optional_fields(part)))
+ id = get_bom_index(harness.bom(), part)
+ manufacturer_str = manufacturer_info_field(part.manufacturer, part.mpn)
+ columns = []
+ if harness.show_bom_item_numbers:
+ columns.append(bom_bubble(id))
+ columns.append(f'{part.qty * component.get_qty_multiplier(part.qty_multiplier)}' + (f' {part.unit}' if part.unit else 'x'))
+ columns.append(f'{part.type}')
+ if harness.show_part_numbers:
+ columns.append(f'P/N: {part.pn}' if part.pn else '')
+ columns.append(f'{manufacturer_str}' if manufacturer_str else '')
+ columns.append(f'{part.note}' if part.note else '')
+
+ parts.append(columns)
+
+ # remove unused columns
+ transp = list(map(list, zip(*parts))) # transpose list
+ transp = [item for item in transp if any(item)] # remove empty rows (easier)
+ parts = list(map(list, zip(*transp))) # transpose back
+
+ # generate HTML output
+ for part in parts:
+ rowstr = '\n
\n'
+ for index, column in enumerate(part):
+ sides = "tbl" if index == 0 else "tbr" if index == len(part) -1 else "tb"
+ rowstr = rowstr + f' | {html_line_breaks(column)} | \n'
+ rowstr = rowstr + '
'
+ rows.append(rowstr)
+
+ pre = ''
+ if len(rows) > 0:
+ tbl = pre + ''.join(rows) + post
+ else:
+ tbl = None
+ return tbl
def get_additional_component_bom(component: Union[Connector, Cable]) -> List[BOMEntry]:
"""Return a list of BOM entries with additional components."""
@@ -138,29 +168,6 @@ def bom_list(bom: List[BOMEntry]) -> List[List[str]]:
return ([[bom_headings.get(k, k.capitalize()) for k in keys]] + # Create header row with key names
[[make_str(entry.get(k)) for k in keys] for entry in bom]) # Create string list for each entry row
-def component_table_entry(
- type: str,
- qty: Union[int, float],
- unit: Optional[str] = None,
- pn: Optional[str] = None,
- manufacturer: Optional[str] = None,
- mpn: Optional[str] = None,
- ) -> str:
- """Return a diagram node table row string with an additional component."""
- manufacturer_str = manufacturer_info_field(manufacturer, mpn)
- output = (f'{qty}'
- + (f' {unit}' if unit else '')
- + f' x {type}'
- + ('
' if pn or manufacturer_str else '')
- + (f'P/N: {pn}' if pn else '')
- + (', ' if pn and manufacturer_str else '')
- + (manufacturer_str or ''))
- # format the above output as left aligned text in a single visible cell
- # indent is set to two to match the indent in the generated html table
- return f'''
- | {html_line_breaks(output)} |
-
'''
-
def manufacturer_info_field(manufacturer: Optional[str], mpn: Optional[str]) -> Optional[str]:
"""Return the manufacturer and/or the mpn in one single string or None otherwise."""
if manufacturer or mpn:
diff --git a/src/wireviz/wv_gv_html.py b/src/wireviz/wv_gv_html.py
index ee5b2e2..ea80ada 100644
--- a/src/wireviz/wv_gv_html.py
+++ b/src/wireviz/wv_gv_html.py
@@ -1,12 +1,25 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from typing import List, Union
+from typing import List, Dict, Union
import re
from wireviz.wv_colors import translate_color
from wireviz.wv_helper import remove_links
+def nested_html_table_dict(rows):
+ if isinstance(rows, Dict):
+ html = []
+ html.append('')
+ for (key, value) in rows.items():
+ html.append(f' | {key} | ')
+ html.append(f' {html_line_breaks(value)} |
')
+ html.append('
')
+ out = '\n'.join(html)
+ else:
+ out = None
+ return out
+
def nested_html_table(rows):
# input: list, each item may be scalar or list
# output: a parent table with one child table per parent item that is list, and one cell per parent item that is scalar
@@ -64,3 +77,6 @@ def html_size_attr(image):
def html_line_breaks(inp):
return remove_links(inp).replace('\n', '
') if isinstance(inp, str) else inp
+
+def bom_bubble(inp):
+ return(f'')
diff --git a/test/bomnumbertest.bom.tsv b/test/bomnumbertest.bom.tsv
new file mode 100644
index 0000000..fb6b2da
--- /dev/null
+++ b/test/bomnumbertest.bom.tsv
@@ -0,0 +1,9 @@
+Id Item Qty Unit Designators P/N Manufacturer MPN
+1 Cable, 4 x 0.25 mm² 99 m W1 qwerty uiop
+2 Connector, Plug, 4 pins 1 X1 123 ACME ABC
+3 Connector, Receptacle, 4 pins 1 X2 234 ACME DEF
+4 Crimp 1 X2 876 ACME WVU
+5 Crimp 4 X1 987 ACME ZYX
+6 Housing 1 X1 345 OTHER
+7 Label 1 X1
+8 Sleeving 5 m W1