Refactor functions for harness building
- Use pin names instead of pin indices, until the last moment when generating the ports for the GraphViz nodes - `Harness.add_mate_pin()` now uses pin names - Remove unused `if is_arrow()` check from `Harness.connect()` - Consolidate calling of `Connector.activate_pin()` to prevent subtle bugs - Call it from `connect()` and `add_mate_pin()` - No longer call it from `create_graph()` - Misc. other tuning
This commit is contained in:
parent
f0b63de3c7
commit
6b1e274d57
@ -1,5 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from enum import Enum, auto
|
||||||
from typing import Dict, List, Optional, Tuple, Union
|
from typing import Dict, List, Optional, Tuple, Union
|
||||||
from dataclasses import dataclass, field, InitVar
|
from dataclasses import dataclass, field, InitVar
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -23,11 +24,17 @@ ImageScale = PlainText # = Literal['false', 'true', 'width', 'height', 'both']
|
|||||||
Pin = Union[int, PlainText] # Pin identifier
|
Pin = Union[int, PlainText] # Pin identifier
|
||||||
PinIndex = int # Zero-based pin index
|
PinIndex = int # Zero-based pin index
|
||||||
Wire = Union[int, PlainText] # Wire number or Literal['s'] for shield
|
Wire = Union[int, PlainText] # Wire number or Literal['s'] for shield
|
||||||
|
NoneOrMorePins = Union[Pin, Tuple[Pin, ...], None] # None, one, or a tuple of pin identifiers
|
||||||
NoneOrMorePinIndices = Union[PinIndex, Tuple[PinIndex, ...], None] # None, one, or a tuple of zero-based pin indices
|
NoneOrMorePinIndices = Union[PinIndex, Tuple[PinIndex, ...], None] # None, one, or a tuple of zero-based pin indices
|
||||||
OneOrMoreWires = Union[Wire, Tuple[Wire, ...]] # One or a tuple of wires
|
OneOrMoreWires = Union[Wire, Tuple[Wire, ...]] # One or a tuple of wires
|
||||||
|
|
||||||
# Metadata can contain whatever is needed by the HTML generation/template.
|
# Metadata can contain whatever is needed by the HTML generation/template.
|
||||||
MetadataKeys = PlainText # Literal['title', 'description', 'notes', ...]
|
MetadataKeys = PlainText # Literal['title', 'description', 'notes', ...]
|
||||||
|
|
||||||
|
class Side(Enum):
|
||||||
|
LEFT = auto()
|
||||||
|
RIGHT = auto()
|
||||||
|
|
||||||
class Metadata(dict):
|
class Metadata(dict):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -188,8 +195,12 @@ class Connector:
|
|||||||
if isinstance(item, dict):
|
if isinstance(item, dict):
|
||||||
self.additional_components[i] = AdditionalComponent(**item)
|
self.additional_components[i] = AdditionalComponent(**item)
|
||||||
|
|
||||||
def activate_pin(self, pin: Pin) -> None:
|
def activate_pin(self, pin: Pin, side: Side) -> None:
|
||||||
self.visible_pins[pin] = True
|
self.visible_pins[pin] = True
|
||||||
|
if side == Side.LEFT:
|
||||||
|
self.ports_left = True
|
||||||
|
elif side == Side.RIGHT:
|
||||||
|
self.ports_right = True
|
||||||
|
|
||||||
def get_qty_multiplier(self, qty_multiplier: Optional[ConnectorMultiplier]) -> int:
|
def get_qty_multiplier(self, qty_multiplier: Optional[ConnectorMultiplier]) -> int:
|
||||||
if not qty_multiplier:
|
if not qty_multiplier:
|
||||||
@ -349,17 +360,17 @@ class Cable:
|
|||||||
@dataclass
|
@dataclass
|
||||||
class Connection:
|
class Connection:
|
||||||
from_name: Optional[Designator]
|
from_name: Optional[Designator]
|
||||||
from_port: Optional[PinIndex]
|
from_pin: Optional[Pin]
|
||||||
via_port: Wire
|
via_port: Wire
|
||||||
to_name: Optional[Designator]
|
to_name: Optional[Designator]
|
||||||
to_port: Optional[PinIndex]
|
to_pin: Optional[Pin]
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MatePin:
|
class MatePin:
|
||||||
from_name: Designator
|
from_name: Designator
|
||||||
from_port: PinIndex
|
from_pin: Pin
|
||||||
to_name: Designator
|
to_name: Designator
|
||||||
to_port: PinIndex
|
to_pin: Pin
|
||||||
shape: str
|
shape: str
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|||||||
@ -9,7 +9,7 @@ from itertools import zip_longest
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from wireviz import wv_colors, __version__, APP_NAME, APP_URL
|
from wireviz import wv_colors, __version__, APP_NAME, APP_URL
|
||||||
from wireviz.DataClasses import Cable, Connector, MatePin, MateComponent, Metadata, Options, Tweak
|
from wireviz.DataClasses import Cable, Connector, MatePin, MateComponent, Metadata, Options, Tweak, Side
|
||||||
from wireviz.wv_colors import get_color_hex, translate_color
|
from wireviz.wv_colors import get_color_hex, translate_color
|
||||||
from wireviz.wv_gv_html import nested_html_table, \
|
from wireviz.wv_gv_html import nested_html_table, \
|
||||||
html_bgcolor_attr, html_bgcolor, html_colorbar, \
|
html_bgcolor_attr, html_bgcolor, html_colorbar, \
|
||||||
@ -41,9 +41,9 @@ class Harness:
|
|||||||
self.cables[name] = Cable(name, *args, **kwargs)
|
self.cables[name] = Cable(name, *args, **kwargs)
|
||||||
|
|
||||||
def add_mate_pin(self, from_name, from_pin, to_name, to_pin, arrow_type) -> None:
|
def add_mate_pin(self, from_name, from_pin, to_name, to_pin, arrow_type) -> None:
|
||||||
from_pin_id = self.connectors[from_name].pins.index(from_pin)
|
self.mates.append(MatePin(from_name, from_pin, to_name, to_pin, arrow_type))
|
||||||
to_pin_id = self.connectors[to_name].pins.index(to_pin)
|
self.connectors[from_name].activate_pin(from_pin, Side.RIGHT)
|
||||||
self.mates.append(MatePin(from_name, from_pin_id, to_name, to_pin_id, arrow_type))
|
self.connectors[to_name].activate_pin(to_pin, Side.LEFT)
|
||||||
|
|
||||||
def add_mate_component(self, from_name, to_name, arrow_type) -> None:
|
def add_mate_component(self, from_name, to_name, arrow_type) -> None:
|
||||||
self.mates.append(MateComponent(from_name, to_name, arrow_type))
|
self.mates.append(MateComponent(from_name, to_name, arrow_type))
|
||||||
@ -74,12 +74,7 @@ class Harness:
|
|||||||
raise Exception(f'{name}:{pin} not found.')
|
raise Exception(f'{name}:{pin} not found.')
|
||||||
|
|
||||||
# check via cable
|
# check via cable
|
||||||
if is_arrow(via_name):
|
if via_name in self.cables:
|
||||||
if '-' in via_name:
|
|
||||||
self.mates[(from_name, from_pin, to_name, to_pin)] = via_name
|
|
||||||
elif '=' in via_name:
|
|
||||||
self.mates[(from_name, to_name)] = via_name
|
|
||||||
elif via_name in self.cables:
|
|
||||||
cable = self.cables[via_name]
|
cable = self.cables[via_name]
|
||||||
# check if provided name is ambiguous
|
# check if provided name is ambiguous
|
||||||
if via_wire in cable.colors and via_wire in cable.wirelabels:
|
if via_wire in cable.colors and via_wire in cable.wirelabels:
|
||||||
@ -95,14 +90,12 @@ class Harness:
|
|||||||
raise Exception(f'{via_name}:{via_wire} is used for more than one wire.')
|
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
|
via_wire = cable.wirelabels.index(via_wire) + 1 # list index starts at 0, wire IDs start at 1
|
||||||
|
|
||||||
from_pin_id = self.connectors[from_name].pins.index(from_pin) if from_pin is not None else None
|
# perform the actual connection
|
||||||
to_pin_id = self.connectors[to_name].pins.index(to_pin) if to_pin is not None else None
|
self.cables[via_name].connect(from_name, from_pin, via_wire, to_name, to_pin)
|
||||||
|
|
||||||
self.cables[via_name].connect(from_name, from_pin_id, via_wire, to_name, to_pin_id)
|
|
||||||
if from_name in self.connectors:
|
if from_name in self.connectors:
|
||||||
self.connectors[from_name].activate_pin(from_pin)
|
self.connectors[from_name].activate_pin(from_pin, Side.RIGHT)
|
||||||
if to_name in self.connectors:
|
if to_name in self.connectors:
|
||||||
self.connectors[to_name].activate_pin(to_pin)
|
self.connectors[to_name].activate_pin(to_pin, Side.LEFT)
|
||||||
|
|
||||||
def create_graph(self) -> Graph:
|
def create_graph(self) -> Graph:
|
||||||
dot = Graph()
|
dot = Graph()
|
||||||
@ -122,23 +115,6 @@ class Harness:
|
|||||||
dot.attr('edge', style='bold',
|
dot.attr('edge', style='bold',
|
||||||
fontname=self.options.fontname)
|
fontname=self.options.fontname)
|
||||||
|
|
||||||
# prepare ports on connectors depending on which side they will connect
|
|
||||||
for _, cable in self.cables.items():
|
|
||||||
for connection_color in cable.connections:
|
|
||||||
if connection_color.from_port is not None: # connect to left
|
|
||||||
self.connectors[connection_color.from_name].ports_right = True
|
|
||||||
self.connectors[connection_color.from_name].activate_pin(connection_color.from_port)
|
|
||||||
if connection_color.to_port is not None: # connect to right
|
|
||||||
self.connectors[connection_color.to_name].ports_left = True
|
|
||||||
self.connectors[connection_color.to_name].activate_pin(connection_color.to_port)
|
|
||||||
|
|
||||||
for mate in self.mates:
|
|
||||||
if isinstance(mate, MatePin):
|
|
||||||
self.connectors[mate.from_name].ports_right = True
|
|
||||||
self.connectors[mate.from_name].activate_pin(mate.from_port)
|
|
||||||
self.connectors[mate.to_name].ports_left = True
|
|
||||||
self.connectors[mate.to_name].activate_pin(mate.to_port)
|
|
||||||
|
|
||||||
for connector in self.connectors.values():
|
for connector in self.connectors.values():
|
||||||
|
|
||||||
# If no wires connected (except maybe loop wires)?
|
# If no wires connected (except maybe loop wires)?
|
||||||
@ -341,32 +317,34 @@ class Harness:
|
|||||||
else: # it's a shield connection
|
else: # it's a shield connection
|
||||||
# shield is shown with specified color and black borders, or as a thin black wire otherwise
|
# 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')
|
dot.attr('edge', color=':'.join(['#000000', shield_color_hex, '#000000']) if isinstance(cable.shield, str) else '#000000')
|
||||||
if connection.from_port is not None: # connect to left
|
if connection.from_pin is not None: # connect to left
|
||||||
from_connector = self.connectors[connection.from_name]
|
from_connector = self.connectors[connection.from_name]
|
||||||
from_port = f':p{connection.from_port+1}r' if from_connector.style != 'simple' else ''
|
from_pin_index = from_connector.pins.index(connection.from_pin)
|
||||||
code_left_1 = f'{connection.from_name}{from_port}:e'
|
from_port_str = f':p{from_pin_index+1}r' if from_connector.style != 'simple' else ''
|
||||||
|
code_left_1 = f'{connection.from_name}{from_port_str}:e'
|
||||||
code_left_2 = f'{cable.name}:w{connection.via_port}:w'
|
code_left_2 = f'{cable.name}:w{connection.via_port}:w'
|
||||||
dot.edge(code_left_1, code_left_2)
|
dot.edge(code_left_1, code_left_2)
|
||||||
if from_connector.show_name:
|
if from_connector.show_name:
|
||||||
from_info = [str(connection.from_name), str(self.connectors[connection.from_name].pins[connection.from_port])]
|
from_info = [str(connection.from_name), str(connection.from_pin)]
|
||||||
if from_connector.pinlabels:
|
if from_connector.pinlabels:
|
||||||
pinlabel = from_connector.pinlabels[connection.from_port]
|
pinlabel = from_connector.pinlabels[from_pin_index]
|
||||||
if pinlabel != '':
|
if pinlabel != '':
|
||||||
from_info.append(pinlabel)
|
from_info.append(pinlabel)
|
||||||
from_string = ':'.join(from_info)
|
from_string = ':'.join(from_info)
|
||||||
else:
|
else:
|
||||||
from_string = ''
|
from_string = ''
|
||||||
html = [row.replace(f'<!-- {connection.via_port}_in -->', from_string) for row in html]
|
html = [row.replace(f'<!-- {connection.via_port}_in -->', from_string) for row in html]
|
||||||
if connection.to_port is not None: # connect to right
|
if connection.to_pin is not None: # connect to right
|
||||||
to_connector = self.connectors[connection.to_name]
|
to_connector = self.connectors[connection.to_name]
|
||||||
|
to_pin_index = to_connector.pins.index(connection.to_pin)
|
||||||
|
to_port_str = f':p{to_pin_index+1}l' if to_connector.style != 'simple' else ''
|
||||||
code_right_1 = f'{cable.name}:w{connection.via_port}:e'
|
code_right_1 = f'{cable.name}:w{connection.via_port}:e'
|
||||||
to_port = f':p{connection.to_port+1}l' if self.connectors[connection.to_name].style != 'simple' else ''
|
code_right_2 = f'{connection.to_name}{to_port_str}:w'
|
||||||
code_right_2 = f'{connection.to_name}{to_port}:w'
|
|
||||||
dot.edge(code_right_1, code_right_2)
|
dot.edge(code_right_1, code_right_2)
|
||||||
if to_connector.show_name:
|
if to_connector.show_name:
|
||||||
to_info = [str(connection.to_name), str(self.connectors[connection.to_name].pins[connection.to_port])]
|
to_info = [str(connection.to_name), str(connection.to_pin)]
|
||||||
if to_connector.pinlabels:
|
if to_connector.pinlabels:
|
||||||
pinlabel = to_connector.pinlabels[connection.to_port]
|
pinlabel = to_connector.pinlabels[to_pin_index]
|
||||||
if pinlabel != '':
|
if pinlabel != '':
|
||||||
to_info.append(pinlabel)
|
to_info.append(pinlabel)
|
||||||
to_string = ':'.join(to_info)
|
to_string = ':'.join(to_info)
|
||||||
@ -448,11 +426,22 @@ class Harness:
|
|||||||
else:
|
else:
|
||||||
raise Exception(f'{mate} is an unknown mate')
|
raise Exception(f'{mate} is an unknown mate')
|
||||||
|
|
||||||
|
from_connector = self.connectors[mate.from_name]
|
||||||
|
if isinstance(mate, MatePin) and self.connectors[mate.from_name].style != 'simple':
|
||||||
|
from_pin_index = from_connector.pins.index(mate.from_pin)
|
||||||
|
from_port_str = f':p{from_pin_index+1}r'
|
||||||
|
else: # MateComponent or style == 'simple'
|
||||||
|
from_port_str = ''
|
||||||
|
if isinstance(mate, MatePin) and self.connectors[mate.to_name].style != 'simple':
|
||||||
|
to_pin_index = to_connector.pins.index(mate.to_pin)
|
||||||
|
to_port_str = f':p{to_pin_index+1}l' if isinstance(mate, MatePin) and self.connectors[mate.to_name].style != 'simple' else ''
|
||||||
|
else: # MateComponent or style == 'simple'
|
||||||
|
to_port_str = ''
|
||||||
|
code_from = f'{mate.from_name}{from_port_str}:e'
|
||||||
|
to_connector = self.connectors[mate.to_name]
|
||||||
|
code_to = f'{mate.to_name}{to_port_str}:w'
|
||||||
|
|
||||||
dot.attr('edge', color=color, style='dashed', dir=dir)
|
dot.attr('edge', color=color, style='dashed', dir=dir)
|
||||||
from_port = f':p{mate.from_port+1}r' if isinstance(mate, MatePin) and self.connectors[mate.from_name].style != 'simple' else ''
|
|
||||||
code_from = f'{mate.from_name}{from_port}:e'
|
|
||||||
to_port = f':p{mate.to_port+1}l' if isinstance(mate, MatePin) and self.connectors[mate.to_name].style != 'simple' else ''
|
|
||||||
code_to = f'{mate.to_name}{to_port}:w'
|
|
||||||
dot.edge(code_from, code_to)
|
dot.edge(code_from, code_to)
|
||||||
|
|
||||||
return dot
|
return dot
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user