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

Reference wires by color or label #194

Merged
merged 3 commits into from
Nov 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/ex08.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ cables:
connections:
-
- Key: [S,R,T]
- W1: [1,2,3]
- W1: [WH,BN,GN]
-
- Key: S
- W1: s
17 changes: 13 additions & 4 deletions src/wireviz/DataClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,11 @@ class Cable:
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)

Expand Down Expand Up @@ -233,6 +235,10 @@ def __post_init__(self) -> None:
raise Exception('Unknown number of wires. Must specify wirecount or colors (implicit length)')
self.wirecount = len(self.colors)

if self.wirelabels:
if self.shield and 's' in self.wirelabels:
raise Exception('"s" may not be used as a wire label for a shielded cable.')

# if lists of part numbers are provided check this is a bundle and that it matches the wirecount.
for idfield in [self.manufacturer, self.mpn, self.pn]:
if isinstance(idfield, list):
Expand All @@ -243,21 +249,24 @@ def __post_init__(self) -> None:
else:
raise Exception('lists of part data are only supported for bundles')

# by default, show wire numbers for cables, hide for bundles
if not self.show_wirenumbers:
self.show_wirenumbers = self.category != 'bundle'

for i, item in enumerate(self.additional_components):
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: NoneOrMorePins, via_pin: OneOrMoreWires,
def connect(self, from_name: Optional[Designator], from_pin: NoneOrMorePins, via_wire: OneOrMoreWires,
to_name: Optional[Designator], to_pin: NoneOrMorePins) -> None:
from_pin = int2tuple(from_pin)
via_pin = int2tuple(via_pin)
via_wire = int2tuple(via_wire)
to_pin = int2tuple(to_pin)
if len(from_pin) != len(to_pin):
raise Exception('from_pin must have the same number of elements as to_pin')
for i, _ in enumerate(from_pin):
# self.connections.append((from_name, from_pin[i], via_pin[i], to_name, to_pin[i]))
self.connections.append(Connection(from_name, from_pin[i], via_pin[i], to_name, to_pin[i]))
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:
if not qty_multiplier:
Expand Down
108 changes: 77 additions & 31 deletions src/wireviz/Harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,30 +35,46 @@ def add_cable(self, name: str, *args, **kwargs) -> None:
def add_bom_item(self, item: dict) -> None:
self.additional_bom_items.append(item)

def connect(self, from_name: str, from_pin: (int, str), via_name: str, via_pin: (int, str), to_name: str, to_pin: (int, str)) -> None:
for (name, pin) in zip([from_name, to_name], [from_pin, to_pin]): # check from and to connectors
def connect(self, from_name: str, from_pin: (int, str), via_name: str, via_wire: (int, str), to_name: str, to_pin: (int, str)) -> None:
# check from and to connectors
for (name, pin) in zip([from_name, to_name], [from_pin, to_pin]):
if name is not None and name in self.connectors:
connector = self.connectors[name]
# check if provided name is ambiguous
if pin in connector.pins and pin in connector.pinlabels:
if connector.pins.index(pin) == connector.pinlabels.index(pin):
# TODO: Maybe issue a warning? It's not worthy of an exception if it's unambiguous, but maybe risky?
pass
else:
if connector.pins.index(pin) != connector.pinlabels.index(pin):
raise Exception(f'{name}:{pin} is defined both in pinlabels and pins, for different pins.')
# TODO: Maybe issue a warning if present in both lists but referencing the same pin?
if pin in connector.pinlabels:
if connector.pinlabels.count(pin) > 1:
raise Exception(f'{name}:{pin} is defined more than once.')
else:
index = connector.pinlabels.index(pin)
pin = connector.pins[index] # map pin name to pin number
if name == from_name:
from_pin = pin
if name == to_name:
to_pin = pin
index = connector.pinlabels.index(pin)
pin = connector.pins[index] # map pin name to pin number
if name == from_name:
from_pin = pin
if name == to_name:
to_pin = pin
if not pin in connector.pins:
raise Exception(f'{name}:{pin} not found.')

self.cables[via_name].connect(from_name, from_pin, via_pin, to_name, to_pin)
# check via cable
if via_name in self.cables:
cable = self.cables[via_name]
# check if provided name is ambiguous
if via_wire in cable.colors and via_wire in cable.wirelabels:
if cable.colors.index(via_wire) != cable.wirelabels.index(via_wire):
raise Exception(f'{via_name}:{via_wire} is defined both in colors and wirelabels, for different wires.')
# TODO: Maybe issue a warning if present in both lists but referencing the same wire?
if via_wire in cable.colors:
if cable.colors.count(via_wire) > 1:
raise Exception(f'{via_name}:{via_wire} is used for more than one wire.')
via_wire = cable.colors.index(via_wire) + 1 # list index starts at 0, wire IDs start at 1
elif via_wire in cable.wirelabels:
if cable.wirelabels.count(via_wire) > 1:
raise Exception(f'{via_name}:{via_wire} is used for more than one wire.')
via_wire = cable.wirelabels.index(via_wire) + 1 # list index starts at 0, wire IDs start at 1

self.cables[via_name].connect(from_name, from_pin, via_wire, to_name, to_pin)
if from_name in self.connectors:
self.connectors[from_name].activate_pin(from_pin)
if to_name in self.connectors:
Expand Down Expand Up @@ -197,10 +213,22 @@ def create_graph(self) -> Graph:
wirehtml.append('<table border="0" cellspacing="0" cellborder="0">') # conductor table
wirehtml.append(' <tr><td>&nbsp;</td></tr>')

for i, connection_color in enumerate(cable.colors, 1):
for i, (connection_color, wirelabel) in enumerate(zip_longest(cable.colors, cable.wirelabels), 1):
wirehtml.append(' <tr>')
wirehtml.append(f' <td><!-- {i}_in --></td>')
wirehtml.append(f' <td>{wv_colors.translate_color(connection_color, self.color_mode)}</td>')
wirehtml.append(f' <td>')

wireinfo = []
if cable.show_wirenumbers:
wireinfo.append(str(i))
colorstr = wv_colors.translate_color(connection_color, self.color_mode)
if colorstr:
wireinfo.append(colorstr)
if cable.wirelabels:
wireinfo.append(wirelabel if wirelabel is not None else '')
wirehtml.append(f' {":".join(wireinfo)}')

wirehtml.append(f' </td>')
wirehtml.append(f' <td><!-- {i}_out --></td>')
wirehtml.append(' </tr>')

Expand Down Expand Up @@ -254,26 +282,44 @@ def create_graph(self) -> Graph:
html = [row.replace('<!-- wire table -->', '\n'.join(wirehtml)) for row in html]

# connections
for connection_color in cable.connections:
if isinstance(connection_color.via_port, int): # check if it's an actual wire and not a shield
dot.attr('edge', color=':'.join(['#000000'] + wv_colors.get_color_hex(cable.colors[connection_color.via_port - 1], pad=pad) + ['#000000']))
for connection in cable.connections:
if isinstance(connection.via_port, int): # check if it's an actual wire and not a shield
dot.attr('edge', color=':'.join(['#000000'] + wv_colors.get_color_hex(cable.colors[connection.via_port - 1], pad=pad) + ['#000000']))
else: # it's a shield connection
# shield is shown with specified color and black borders, or as a thin black wire otherwise
dot.attr('edge', color=':'.join(['#000000', shield_color_hex, '#000000']) if isinstance(cable.shield, str) else '#000000')
if connection_color.from_port is not None: # connect to left
from_port = f':p{connection_color.from_port}r' if self.connectors[connection_color.from_name].style != 'simple' else ''
code_left_1 = f'{connection_color.from_name}{from_port}:e'
code_left_2 = f'{cable.name}:w{connection_color.via_port}:w'
if connection.from_port is not None: # connect to left
from_connector = self.connectors[connection.from_name]
from_port = f':p{connection.from_port}r' if from_connector.style != 'simple' else ''
code_left_1 = f'{connection.from_name}{from_port}:e'
code_left_2 = f'{cable.name}:w{connection.via_port}:w'
dot.edge(code_left_1, code_left_2)
from_string = f'{connection_color.from_name}:{connection_color.from_port}' if self.connectors[connection_color.from_name].show_name else ''
html = [row.replace(f'<!-- {connection_color.via_port}_in -->', from_string) for row in html]
if connection_color.to_port is not None: # connect to right
code_right_1 = f'{cable.name}:w{connection_color.via_port}:e'
to_port = f':p{connection_color.to_port}l' if self.connectors[connection_color.to_name].style != 'simple' else ''
code_right_2 = f'{connection_color.to_name}{to_port}:w'
if from_connector.show_name:
from_info = [str(connection.from_name), str(connection.from_port)]
if from_connector.pinlabels:
pinlabel = from_connector.pinlabels[from_connector.pins.index(connection.from_port)]
if pinlabel != '':
from_info.append(pinlabel)
from_string = ':'.join(from_info)
else:
from_string = ''
html = [row.replace(f'<!-- {connection.via_port}_in -->', from_string) for row in html]
if connection.to_port is not None: # connect to right
to_connector = self.connectors[connection.to_name]
code_right_1 = f'{cable.name}:w{connection.via_port}:e'
to_port = f':p{connection.to_port}l' if self.connectors[connection.to_name].style != 'simple' else ''
code_right_2 = f'{connection.to_name}{to_port}:w'
dot.edge(code_right_1, code_right_2)
to_string = f'{connection_color.to_name}:{connection_color.to_port}' if self.connectors[connection_color.to_name].show_name else ''
html = [row.replace(f'<!-- {connection_color.via_port}_out -->', to_string) for row in html]
if to_connector.show_name:
to_info = [str(connection.to_name), str(connection.to_port)]
if to_connector.pinlabels:
pinlabel = to_connector.pinlabels[to_connector.pins.index(connection.to_port)]
if pinlabel != '':
to_info.append(pinlabel)
to_string = ':'.join(to_info)
else:
to_string = ''
html = [row.replace(f'<!-- {connection.via_port}_out -->', to_string) for row in html]

html = '\n'.join(html)
dot.node(cable.name, label=f'<\n{html}\n>', shape='box',
Expand Down