Merge 9821ca31f77edfc0e0b0793a76ba27cdfd174cf3 into 523d0c659ee58412d8cf11416978eb80e424ec4c

This commit is contained in:
Daniel Rojas 2021-03-23 17:09:33 +00:00 committed by GitHub
commit edbe2b1e89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 197 additions and 97 deletions

View File

@ -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: connectors:
X1: X1:
<<: *molex_f # copying items from the template type: KK 254
pinlabels: [GND, +5V, SCL, SDA, MISO, MOSI, SCK, N/C] 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: X2:
<<: *molex_f type: 3.5 mm
<<: *con_i2c # it is possible to copy from more than one template 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: X3:
<<: *molex_f type: KK 254
<<: *con_i2c subtype: female
pinlabels: [VCC, GND, SCL, SDA]
pincolors: [RD, BK, GN, BU]
X4: X4:
<<: *molex_f type: D-Sub
pinlabels: [GND, +12V, MISO, MOSI, SCK] subtype: female
ferrule_crimp: 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 style: simple
autogenerate: true autogenerate: true
type: Crimp ferrule type: Crimp ferrule
subtype: 0.25 mm² subtype: 0.5 mm²
color: YE color: OG
cables: cables:
W1: W1:
<<: *wire_i2c wirecount: 3
length: 0.2 shield: true
show_equiv: true length: 0.5
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:
gauge: 0.25 mm2 gauge: 0.25 mm2
length: 0.3
colors: [BK, RD]
show_equiv: true 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: connections:
- -
- X1: [1-4] - X1: [Audio L, Audio R, Audio GND, Audio GND]
- W1: [1-4] - W1: [1-3,s]
- X2: [1-4] - X2: [T,R,S,S]
- -
- X1: [1-4] - X1: [I2C GND, I2C +5V, SCL, SDA]
- W2: [1-4] - 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] - W3: [1-4]
- X4: [1,3-5] - X4: [1,2,3,4]
- -
- ferrule_crimp - F
- W4: [1,2] - W4: [1,2]
- X4: [1,2] - X4: [6,7]
-
- X4: [8,9]
- W5: [1,2]
- F

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- 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 dataclasses import dataclass, field, InitVar
from pathlib import Path from pathlib import Path
@ -76,6 +76,7 @@ class AdditionalComponent:
qty: float = 1 qty: float = 1
unit: Optional[str] = None unit: Optional[str] = None
qty_multiplier: Union[ConnectorMultiplier, CableMultiplier, None] = None qty_multiplier: Union[ConnectorMultiplier, CableMultiplier, None] = None
note: Optional[str] = None
@property @property
def description(self) -> str: def description(self) -> str:
@ -93,6 +94,7 @@ class Connector:
type: Optional[MultilineHypertext] = None type: Optional[MultilineHypertext] = None
subtype: Optional[MultilineHypertext] = None subtype: Optional[MultilineHypertext] = None
pincount: Optional[int] = None pincount: Optional[int] = None
additional_parameters: Optional[Dict] = None
image: Optional[Image] = None image: Optional[Image] = None
notes: Optional[MultilineHypertext] = None notes: Optional[MultilineHypertext] = None
pinlabels: List[Pin] = field(default_factory=list) pinlabels: List[Pin] = field(default_factory=list)
@ -104,6 +106,7 @@ class Connector:
hide_disconnected_pins: bool = False hide_disconnected_pins: bool = False
autogenerate: bool = False autogenerate: bool = False
loops: List[List[Pin]] = field(default_factory=list) loops: List[List[Pin]] = field(default_factory=list)
bom_item_number: Optional[int] = None
ignore_in_bom: bool = False ignore_in_bom: bool = False
additional_components: List[AdditionalComponent] = field(default_factory=list) additional_components: List[AdditionalComponent] = field(default_factory=list)
@ -180,6 +183,7 @@ class Cable:
color: Optional[Color] = None color: Optional[Color] = None
wirecount: Optional[int] = None wirecount: Optional[int] = None
shield: Union[bool, Color] = False shield: Union[bool, Color] = False
additional_parameters: Optional[Dict] = None
image: Optional[Image] = None image: Optional[Image] = None
notes: Optional[MultilineHypertext] = None notes: Optional[MultilineHypertext] = None
colors: List[Colors] = field(default_factory=list) colors: List[Colors] = field(default_factory=list)
@ -188,6 +192,7 @@ class Cable:
show_name: bool = True show_name: bool = True
show_wirecount: bool = True show_wirecount: bool = True
show_wirenumbers: Optional[bool] = None show_wirenumbers: Optional[bool] = None
bom_item_number: Optional[int] = None
ignore_in_bom: bool = False ignore_in_bom: bool = False
additional_components: List[AdditionalComponent] = field(default_factory=list) additional_components: List[AdditionalComponent] = field(default_factory=list)

View File

@ -12,8 +12,8 @@ from wireviz import wv_colors, __version__, APP_NAME, APP_URL
from wireviz.DataClasses import Connector, Cable from wireviz.DataClasses import Connector, Cable
from wireviz.wv_colors import get_color_hex from wireviz.wv_colors import get_color_hex
from wireviz.wv_gv_html import nested_html_table, html_colorbar, html_image, \ from wireviz.wv_gv_html import nested_html_table, html_colorbar, html_image, \
html_caption, remove_links, html_line_breaks html_caption, remove_links, html_line_breaks, bom_bubble, nested_html_table_dict
from wireviz.wv_bom import manufacturer_info_field, component_table_entry, \ from wireviz.wv_bom import manufacturer_info_field, \
get_additional_component_table, bom_list, generate_bom get_additional_component_table, bom_list, generate_bom
from wireviz.wv_html import generate_html_output from wireviz.wv_html import generate_html_output
from wireviz.wv_helper import awg_equiv, mm2_equiv, tuplelist2tsv, flatten2d, \ from wireviz.wv_helper import awg_equiv, mm2_equiv, tuplelist2tsv, flatten2d, \
@ -24,7 +24,8 @@ class Harness:
def __init__(self): def __init__(self):
self.color_mode = 'SHORT' 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.connectors = {}
self.cables = {} self.cables = {}
self._bom = [] # Internal Cache for generated bom self._bom = [] # Internal Cache for generated bom
@ -114,17 +115,20 @@ class Harness:
html = [] html = []
rows = [[remove_links(connector.name) if connector.show_name else None], rows = [[remove_links(connector.name) if connector.show_name else None],
[f'P/N: {remove_links(connector.pn)}' if connector.pn else None, [bom_bubble(connector.bom_item_number) if self.show_bom_item_numbers else None, # TODO: Show actual BOM number
html_line_breaks(manufacturer_info_field(connector.manufacturer, connector.mpn))], html_line_breaks(connector.type),
[html_line_breaks(connector.type),
html_line_breaks(connector.subtype), html_line_breaks(connector.subtype),
f'{connector.pincount}-pin' if connector.show_pincount else None, f'{connector.pincount}-pin' if connector.show_pincount else None,
connector.color, html_colorbar(connector.color)], 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),
'<!-- connector table -->' if connector.style != 'simple' else None, '<!-- connector table -->' if connector.style != 'simple' else None,
[html_image(connector.image)], [html_image(connector.image)],
[html_caption(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)]) rows.append([html_line_breaks(connector.notes)])
html.extend(nested_html_table(rows)) html.extend(nested_html_table(rows))
if connector.style != 'simple': if connector.style != 'simple':
@ -195,21 +199,23 @@ class Harness:
awg_fmt = f' ({mm2_equiv(cable.gauge)} mm\u00B2)' awg_fmt = f' ({mm2_equiv(cable.gauge)} mm\u00B2)'
rows = [[remove_links(cable.name) if cable.show_name else None], 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, [bom_bubble(cable.bom_item_number) if self.show_bom_item_numbers else None, # TODO: Show actual BOM number
html_line_breaks(manufacturer_info_field( html_line_breaks(cable.type),
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),
f'{cable.wirecount}x' if cable.show_wirecount else None, f'{cable.wirecount}x' if cable.show_wirecount else None,
f'{cable.gauge} {cable.gauge_unit}{awg_fmt}' if cable.gauge else None, f'{cable.gauge} {cable.gauge_unit}{awg_fmt}' if cable.gauge else None,
'+ S' if cable.shield else None, '+ S' if cable.shield else None,
f'{cable.length} {cable.length_unit}' if cable.length > 0 else None, f'{cable.length} {cable.length_unit}' if cable.length > 0 else None,
cable.color, html_colorbar(cable.color)], 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),
'<!-- wire table -->', '<!-- wire table -->',
[html_image(cable.image)], [html_image(cable.image)],
[html_caption(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)]) rows.append([html_line_breaks(cable.notes)])
html.extend(nested_html_table(rows)) html.extend(nested_html_table(rows))

View File

@ -6,7 +6,7 @@ from itertools import groupby
from typing import Any, Dict, List, Optional, Tuple, Union from typing import Any, Dict, List, Optional, Tuple, Union
from wireviz.DataClasses import AdditionalComponent, Connector, Cable 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 from wireviz.wv_helper import clean_whitespace
BOMColumn = str # = Literal['id', 'description', 'qty', 'unit', 'designators', 'pn', 'manufacturer', 'mpn'] 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.""" """Return a list of diagram node table row strings with additional components."""
rows = [] rows = []
if component.additional_components: if component.additional_components:
rows.append(["Additional components"]) parts = []
for part in component.additional_components: for part in component.additional_components:
common_args = { # if True:
'qty': part.qty * component.get_qty_multiplier(part.qty_multiplier), # id = get_bom_index(harness.bom(), part)
'unit': part.unit, # rows.append(component_table_entry(f'#{id} ({part.type.rstrip()})', **common_args))
} # else:
if harness.mini_bom_mode: # rows.append(component_table_entry(part.description, **common_args, **optional_fields(part)))
id = get_bom_index(harness.bom(), part) id = get_bom_index(harness.bom(), part)
rows.append(component_table_entry(f'#{id} ({part.type.rstrip()})', **common_args)) manufacturer_str = manufacturer_info_field(part.manufacturer, part.mpn)
else: columns = []
rows.append(component_table_entry(part.description, **common_args, **optional_fields(part))) if harness.show_bom_item_numbers:
return rows 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 <tr>\n'
for index, column in enumerate(part):
sides = "tbl" if index == 0 else "tbr" if index == len(part) -1 else "tb"
rowstr = rowstr + f' <td align="left" balign="left" sides="{sides}">{html_line_breaks(column)}</td>\n'
rowstr = rowstr + ' </tr>'
rows.append(rowstr)
pre = '<table border="0" cellspacing="0" cellpadding="3" cellborder="1">'
post = '\n </table>'
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]: def get_additional_component_bom(component: Union[Connector, Cable]) -> List[BOMEntry]:
"""Return a list of BOM entries with additional components.""" """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 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 [[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}'
+ ('<br/>' 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'''<table border="0" cellspacing="0" cellpadding="3" cellborder="1"><tr>
<td align="left" balign="left">{html_line_breaks(output)}</td>
</tr></table>'''
def manufacturer_info_field(manufacturer: Optional[str], mpn: Optional[str]) -> Optional[str]: 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.""" """Return the manufacturer and/or the mpn in one single string or None otherwise."""
if manufacturer or mpn: if manufacturer or mpn:

View File

@ -1,12 +1,25 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from typing import List, Union from typing import List, Dict, Union
import re import re
from wireviz.wv_colors import translate_color from wireviz.wv_colors import translate_color
from wireviz.wv_helper import remove_links from wireviz.wv_helper import remove_links
def nested_html_table_dict(rows):
if isinstance(rows, Dict):
html = []
html.append('<table border="0" cellspacing="0" cellpadding="3" cellborder="1">')
for (key, value) in rows.items():
html.append(f' <tr><td align="left" balign="left">{key}</td>')
html.append(f' <td align="left" balign="left">{html_line_breaks(value)}</td></tr>')
html.append(' </table>')
out = '\n'.join(html)
else:
out = None
return out
def nested_html_table(rows): def nested_html_table(rows):
# input: list, each item may be scalar or list # 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 # 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): def html_line_breaks(inp):
return remove_links(inp).replace('\n', '<br />') if isinstance(inp, str) else inp return remove_links(inp).replace('\n', '<br />') if isinstance(inp, str) else inp
def bom_bubble(inp):
return(f'<table border="0"><tr><td border="1" style="rounded">{inp}</td></tr></table>')

9
test/bomnumbertest.bom.tsv generated Normal file
View File

@ -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
1 Id Item Qty Unit Designators P/N Manufacturer MPN
2 1 Cable, 4 x 0.25 mm² 99 m W1 qwerty uiop
3 2 Connector, Plug, 4 pins 1 X1 123 ACME ABC
4 3 Connector, Receptacle, 4 pins 1 X2 234 ACME DEF
5 4 Crimp 1 X2 876 ACME WVU
6 5 Crimp 4 X1 987 ACME ZYX
7 6 Housing 1 X1 345 OTHER
8 7 Label 1 X1
9 8 Sleeving 5 m W1