Compare commits
42 Commits
master
...
backup/fea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d1e3d715d | ||
|
|
45a45dc69e | ||
|
|
347f7dbd56 | ||
|
|
7eccbe4809 | ||
|
|
4c74a5cb01 | ||
|
|
3ef376ecdf | ||
|
|
ec966cc278 | ||
|
|
39698a3d61 | ||
|
|
81940469ac | ||
|
|
87cde3b21f | ||
|
|
ecbc7d5b4e | ||
|
|
259044ac84 | ||
|
|
00df29aa57 | ||
|
|
d913bea14c | ||
|
|
b589b68b8a | ||
|
|
19d44c32f9 | ||
|
|
29b38364b3 | ||
|
|
9ddb3afe6e | ||
|
|
fc58108de0 | ||
|
|
addb180292 | ||
|
|
ea9a4e4c74 | ||
|
|
b89412aabe | ||
|
|
2e5d58b103 | ||
|
|
caaaa38feb | ||
|
|
9d0582e10c | ||
|
|
2c78e46c77 | ||
|
|
37cedb960e | ||
|
|
82fcfbe71e | ||
|
|
88176dbd98 | ||
|
|
f985a7917c | ||
|
|
e62e08ac0e | ||
|
|
faa72df625 | ||
|
|
6686122e2a | ||
|
|
e94ff2c957 | ||
|
|
b007c5a659 | ||
|
|
57cc55a0b4 | ||
|
|
ea05519a24 | ||
|
|
ef9f36f1b0 | ||
|
|
a50c7794c5 | ||
|
|
c6b2375e5c | ||
|
|
4e74aa2a9c | ||
|
|
ef76d4e962 |
@ -22,9 +22,8 @@ connectors:
|
|||||||
X4:
|
X4:
|
||||||
<<: *molex_f
|
<<: *molex_f
|
||||||
pinlabels: [GND, +12V, MISO, MOSI, SCK]
|
pinlabels: [GND, +12V, MISO, MOSI, SCK]
|
||||||
ferrule_crimp:
|
F:
|
||||||
style: simple
|
style: simple
|
||||||
autogenerate: true
|
|
||||||
type: Crimp ferrule
|
type: Crimp ferrule
|
||||||
subtype: 0.25 mm²
|
subtype: 0.25 mm²
|
||||||
color: YE
|
color: YE
|
||||||
@ -64,6 +63,6 @@ connections:
|
|||||||
- W3: [1-4]
|
- W3: [1-4]
|
||||||
- X4: [1,3-5]
|
- X4: [1,3-5]
|
||||||
-
|
-
|
||||||
- ferrule_crimp
|
- F.
|
||||||
- W4: [1,2]
|
- W4: [1,2]
|
||||||
- X4: [1,2]
|
- X4: [1,2]
|
||||||
|
|||||||
@ -8,13 +8,12 @@ cables:
|
|||||||
category: bundle
|
category: bundle
|
||||||
|
|
||||||
connectors:
|
connectors:
|
||||||
ferrule_crimp:
|
F:
|
||||||
style: simple
|
style: simple
|
||||||
autogenerate: true
|
|
||||||
type: Crimp ferrule
|
type: Crimp ferrule
|
||||||
|
|
||||||
connections:
|
connections:
|
||||||
-
|
-
|
||||||
- ferrule_crimp
|
- F.
|
||||||
- W1: [1-6]
|
- W1: [1-6]
|
||||||
- ferrule_crimp
|
- F.
|
||||||
|
|||||||
@ -102,7 +102,6 @@ class Connector:
|
|||||||
show_name: Optional[bool] = None
|
show_name: Optional[bool] = None
|
||||||
show_pincount: Optional[bool] = None
|
show_pincount: Optional[bool] = None
|
||||||
hide_disconnected_pins: bool = False
|
hide_disconnected_pins: bool = False
|
||||||
autogenerate: bool = False
|
|
||||||
loops: List[List[Pin]] = field(default_factory=list)
|
loops: List[List[Pin]] = field(default_factory=list)
|
||||||
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)
|
||||||
@ -134,7 +133,8 @@ class Connector:
|
|||||||
raise Exception('Pins are not unique')
|
raise Exception('Pins are not unique')
|
||||||
|
|
||||||
if self.show_name is None:
|
if self.show_name is None:
|
||||||
self.show_name = not self.autogenerate # hide auto-generated designators by default
|
# hide designators for simple and for auto-generated connectors by default
|
||||||
|
self.show_name = (self.style != 'simple' and self.name[0:2] != '__')
|
||||||
|
|
||||||
if self.show_pincount is None:
|
if self.show_pincount is None:
|
||||||
self.show_pincount = self.style != 'simple' # hide pincount for simple (1 pin) connectors by default
|
self.show_pincount = self.style != 'simple' # hide pincount for simple (1 pin) connectors by default
|
||||||
@ -184,7 +184,7 @@ class Cable:
|
|||||||
colors: List[Colors] = field(default_factory=list)
|
colors: List[Colors] = field(default_factory=list)
|
||||||
wirelabels: List[Wire] = field(default_factory=list)
|
wirelabels: List[Wire] = field(default_factory=list)
|
||||||
color_code: Optional[ColorScheme] = None
|
color_code: Optional[ColorScheme] = None
|
||||||
show_name: bool = True
|
show_name: Optional[bool] = None
|
||||||
show_wirecount: bool = True
|
show_wirecount: bool = True
|
||||||
show_wirenumbers: Optional[bool] = None
|
show_wirenumbers: Optional[bool] = None
|
||||||
ignore_in_bom: bool = False
|
ignore_in_bom: bool = False
|
||||||
@ -250,9 +250,11 @@ class Cable:
|
|||||||
else:
|
else:
|
||||||
raise Exception('lists of part data are only supported for bundles')
|
raise Exception('lists of part data are only supported for bundles')
|
||||||
|
|
||||||
# by default, show wire numbers for cables, hide for bundles
|
if self.show_name is None:
|
||||||
|
self.show_name = self.name[0:2] != '__' # hide designators for auto-generated cables by default
|
||||||
|
|
||||||
if not self.show_wirenumbers:
|
if not self.show_wirenumbers:
|
||||||
self.show_wirenumbers = self.category != 'bundle'
|
self.show_wirenumbers = self.category != 'bundle' # by default, show wire numbers for cables, hide for bundles
|
||||||
|
|
||||||
for i, item in enumerate(self.additional_components):
|
for i, item in enumerate(self.additional_components):
|
||||||
if isinstance(item, dict):
|
if isinstance(item, dict):
|
||||||
@ -291,3 +293,17 @@ class Connection:
|
|||||||
via_port: Wire
|
via_port: Wire
|
||||||
to_name: Optional[Designator]
|
to_name: Optional[Designator]
|
||||||
to_port: Optional[Pin]
|
to_port: Optional[Pin]
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MatePin:
|
||||||
|
from_name: Designator
|
||||||
|
from_port: Pin
|
||||||
|
to_name: Designator
|
||||||
|
to_port: Pin
|
||||||
|
shape: str
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class MateComponent:
|
||||||
|
from_name: Designator
|
||||||
|
to_name: Designator
|
||||||
|
shape: str
|
||||||
|
|||||||
@ -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 Connector, Cable
|
from wireviz.DataClasses import Connector, Cable, MatePin, MateComponent
|
||||||
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
|
||||||
@ -17,8 +17,7 @@ from wireviz.wv_bom import manufacturer_info_field, component_table_entry, \
|
|||||||
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, \
|
||||||
open_file_read, open_file_write
|
open_file_read, open_file_write, is_arrow
|
||||||
|
|
||||||
|
|
||||||
class Harness:
|
class Harness:
|
||||||
|
|
||||||
@ -27,6 +26,7 @@ class Harness:
|
|||||||
self.mini_bom_mode = True
|
self.mini_bom_mode = True
|
||||||
self.connectors = {}
|
self.connectors = {}
|
||||||
self.cables = {}
|
self.cables = {}
|
||||||
|
self.mates = []
|
||||||
self._bom = [] # Internal Cache for generated bom
|
self._bom = [] # Internal Cache for generated bom
|
||||||
self.additional_bom_items = []
|
self.additional_bom_items = []
|
||||||
|
|
||||||
@ -36,6 +36,12 @@ class Harness:
|
|||||||
def add_cable(self, name: str, *args, **kwargs) -> None:
|
def add_cable(self, name: str, *args, **kwargs) -> None:
|
||||||
self.cables[name] = Cable(name, *args, **kwargs)
|
self.cables[name] = Cable(name, *args, **kwargs)
|
||||||
|
|
||||||
|
def add_mate_pin(self, *args, **kwargs) -> None:
|
||||||
|
self.mates.append(MatePin(*args, **kwargs))
|
||||||
|
|
||||||
|
def add_mate_component(self, *args, **kwargs) -> None:
|
||||||
|
self.mates.append(MateComponent(*args, **kwargs))
|
||||||
|
|
||||||
def add_bom_item(self, item: dict) -> None:
|
def add_bom_item(self, item: dict) -> None:
|
||||||
self.additional_bom_items.append(item)
|
self.additional_bom_items.append(item)
|
||||||
|
|
||||||
@ -62,7 +68,12 @@ class Harness:
|
|||||||
raise Exception(f'{name}:{pin} not found.')
|
raise Exception(f'{name}:{pin} not found.')
|
||||||
|
|
||||||
# check via cable
|
# check via cable
|
||||||
if via_name in self.cables:
|
if is_arrow(via_name):
|
||||||
|
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:
|
||||||
@ -106,8 +117,17 @@ class Harness:
|
|||||||
for connection_color in cable.connections:
|
for connection_color in cable.connections:
|
||||||
if connection_color.from_port is not None: # connect to left
|
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].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
|
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].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():
|
||||||
|
|
||||||
@ -329,6 +349,30 @@ class Harness:
|
|||||||
dot.node(cable.name, label=f'<\n{html}\n>', shape='box',
|
dot.node(cable.name, label=f'<\n{html}\n>', shape='box',
|
||||||
style='filled,dashed' if cable.category == 'bundle' else '', margin='0', fillcolor='white')
|
style='filled,dashed' if cable.category == 'bundle' else '', margin='0', fillcolor='white')
|
||||||
|
|
||||||
|
for mate in self.mates:
|
||||||
|
if mate.shape[0] == '<' and mate.shape[-1] == '>':
|
||||||
|
dir = 'both'
|
||||||
|
elif mate.shape[0] == '<':
|
||||||
|
dir = 'back'
|
||||||
|
elif mate.shape[-1] == '>':
|
||||||
|
dir = 'forward'
|
||||||
|
else:
|
||||||
|
dir = 'none'
|
||||||
|
|
||||||
|
if isinstance(mate, MatePin):
|
||||||
|
color = '#000000'
|
||||||
|
elif isinstance(mate, MateComponent):
|
||||||
|
color = '#000000:#ffffff:#000000' # GraphViz bug? 'back' and 'both' do not work with multicolor edges
|
||||||
|
else:
|
||||||
|
raise Exception(f'{mate} is an unknown mate')
|
||||||
|
|
||||||
|
dot.attr('edge', color=color, style='dashed', dir=dir)
|
||||||
|
from_port = f':p{mate.from_port}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}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)
|
||||||
|
|
||||||
return dot
|
return dot
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@ -14,7 +14,7 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
from wireviz import __version__
|
from wireviz import __version__
|
||||||
from wireviz.Harness import Harness
|
from wireviz.Harness import Harness
|
||||||
from wireviz.wv_helper import expand, open_file_read
|
from wireviz.wv_helper import expand, open_file_read, is_arrow, get_single_key_and_value
|
||||||
|
|
||||||
|
|
||||||
def parse(yaml_input: str, file_out: (str, Path) = None, return_types: (None, str, Tuple[str]) = None) -> Any:
|
def parse(yaml_input: str, file_out: (str, Path) = None, return_types: (None, str, Tuple[str]) = None) -> Any:
|
||||||
@ -32,16 +32,25 @@ def parse(yaml_input: str, file_out: (str, Path) = None, return_types: (None, st
|
|||||||
- "harness" - will return the `Harness` instance
|
- "harness" - will return the `Harness` instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
yaml_data = yaml.safe_load(yaml_input)
|
# define variables =========================================================
|
||||||
|
# containers for parsed component data and connection sets
|
||||||
|
template_connectors = {}
|
||||||
|
template_cables = {}
|
||||||
|
connection_sets = []
|
||||||
|
# actual harness
|
||||||
harness = Harness()
|
harness = Harness()
|
||||||
|
# others
|
||||||
|
designators_and_templates = {} # store mapping of components to their respective template
|
||||||
|
autogenerated_designators = {} # keep track of auto-generated designators to avoid duplicates
|
||||||
|
|
||||||
# add items
|
# parse YAML input file ====================================================
|
||||||
|
|
||||||
|
yaml_data = yaml.safe_load(yaml_input)
|
||||||
sections = ['connectors', 'cables', 'connections']
|
sections = ['connectors', 'cables', 'connections']
|
||||||
types = [dict, dict, list]
|
types = [dict, dict, list]
|
||||||
for sec, ty in zip(sections, types):
|
for sec, ty in zip(sections, types):
|
||||||
if sec in yaml_data and type(yaml_data[sec]) == ty:
|
if sec in yaml_data and type(yaml_data[sec]) == ty: # section exists
|
||||||
if len(yaml_data[sec]) > 0:
|
if len(yaml_data[sec]) > 0: # section has contents
|
||||||
if ty == dict:
|
if ty == dict:
|
||||||
for key, attribs in yaml_data[sec].items():
|
for key, attribs in yaml_data[sec].items():
|
||||||
# The Image dataclass might need to open an image file with a relative path.
|
# The Image dataclass might need to open an image file with a relative path.
|
||||||
@ -49,132 +58,191 @@ def parse(yaml_input: str, file_out: (str, Path) = None, return_types: (None, st
|
|||||||
if isinstance(image, dict):
|
if isinstance(image, dict):
|
||||||
image['gv_dir'] = Path(file_out if file_out else '').parent # Inject context
|
image['gv_dir'] = Path(file_out if file_out else '').parent # Inject context
|
||||||
|
|
||||||
|
# store component templates only; do not generate instances yet
|
||||||
if sec == 'connectors':
|
if sec == 'connectors':
|
||||||
if not attribs.get('autogenerate', False):
|
template_connectors[key] = attribs
|
||||||
harness.add_connector(name=key, **attribs)
|
|
||||||
elif sec == 'cables':
|
elif sec == 'cables':
|
||||||
harness.add_cable(name=key, **attribs)
|
template_cables[key] = attribs
|
||||||
else:
|
else: # section exists but is empty
|
||||||
pass # section exists but is empty
|
pass
|
||||||
else: # section does not exist, create empty section
|
else: # section does not exist, create empty section
|
||||||
if ty == dict:
|
if ty == dict:
|
||||||
yaml_data[sec] = {}
|
yaml_data[sec] = {}
|
||||||
elif ty == list:
|
elif ty == list:
|
||||||
yaml_data[sec] = []
|
yaml_data[sec] = []
|
||||||
|
|
||||||
# add connections
|
connection_sets = yaml_data['connections']
|
||||||
|
|
||||||
def check_designators(what, where): # helper function
|
# go through connection sets, generate and connect components ==============
|
||||||
for i, x in enumerate(what):
|
|
||||||
if x not in yaml_data[where[i]]:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
autogenerated_ids = {}
|
template_separator_char = '.' # TODO: make user-configurable (in case user wants to use `.` as part of their template/component names)
|
||||||
for connection in yaml_data['connections']:
|
|
||||||
# find first component (potentially nested inside list or dict)
|
|
||||||
first_item = connection[0]
|
|
||||||
if isinstance(first_item, list):
|
|
||||||
first_item = first_item[0]
|
|
||||||
elif isinstance(first_item, dict):
|
|
||||||
first_item = list(first_item.keys())[0]
|
|
||||||
elif isinstance(first_item, str):
|
|
||||||
pass
|
|
||||||
|
|
||||||
# check which section the first item belongs to
|
def resolve_designator(inp, separator):
|
||||||
alternating_sections = ['connectors','cables']
|
if separator in inp: # generate a new instance of an item
|
||||||
for index, section in enumerate(alternating_sections):
|
if inp.count(separator) > 1:
|
||||||
if first_item in yaml_data[section]:
|
raise Exception(f'{inp} - Found more than one separator ({separator})')
|
||||||
expected_index = index
|
template, designator = inp.split(separator)
|
||||||
break
|
if designator == '':
|
||||||
else:
|
autogenerated_designators[template] = autogenerated_designators.get(template, 0) + 1
|
||||||
raise Exception('First item not found anywhere.')
|
designator = f'__{template}_{autogenerated_designators[template]}'
|
||||||
expected_index = 1 - expected_index # flip once since it is flipped back at the *beginning* of every loop
|
# check if redefining existing component to different template
|
||||||
|
if designator in designators_and_templates:
|
||||||
# check that all iterable items (lists and dicts) are the same length
|
if designators_and_templates[designator] != template:
|
||||||
# and that they are alternating between connectors and cables/bundles, starting with either
|
raise Exception(f'Trying to redefine {designator} from {designators_and_templates[designator]} to {template}')
|
||||||
itemcount = None
|
|
||||||
for item in connection:
|
|
||||||
expected_index = 1 - expected_index # make sure items alternate between connectors and cables
|
|
||||||
expected_section = alternating_sections[expected_index]
|
|
||||||
if isinstance(item, list):
|
|
||||||
itemcount_new = len(item)
|
|
||||||
for subitem in item:
|
|
||||||
if not subitem in yaml_data[expected_section]:
|
|
||||||
raise Exception(f'{subitem} is not in {expected_section}')
|
|
||||||
elif isinstance(item, dict):
|
|
||||||
if len(item.keys()) != 1:
|
|
||||||
raise Exception('Dicts may contain only one key here!')
|
|
||||||
itemcount_new = len(expand(list(item.values())[0]))
|
|
||||||
subitem = list(item.keys())[0]
|
|
||||||
if not subitem in yaml_data[expected_section]:
|
|
||||||
raise Exception(f'{subitem} is not in {expected_section}')
|
|
||||||
elif isinstance(item, str):
|
|
||||||
if not item in yaml_data[expected_section]:
|
|
||||||
raise Exception(f'{item} is not in {expected_section}')
|
|
||||||
continue
|
|
||||||
if itemcount is not None and itemcount_new != itemcount:
|
|
||||||
raise Exception('All lists and dict lists must be the same length!')
|
|
||||||
itemcount = itemcount_new
|
|
||||||
if itemcount is None:
|
|
||||||
raise Exception('No item revealed the number of connections to make!')
|
|
||||||
|
|
||||||
# populate connection list
|
|
||||||
connection_list = []
|
|
||||||
for i, item in enumerate(connection):
|
|
||||||
if isinstance(item, str): # one single-pin component was specified
|
|
||||||
sublist = []
|
|
||||||
for i in range(1, itemcount + 1):
|
|
||||||
if yaml_data['connectors'][item].get('autogenerate'):
|
|
||||||
autogenerated_ids[item] = autogenerated_ids.get(item, 0) + 1
|
|
||||||
new_id = f'_{item}_{autogenerated_ids[item]}'
|
|
||||||
harness.add_connector(new_id, **yaml_data['connectors'][item])
|
|
||||||
sublist.append([new_id, 1])
|
|
||||||
else:
|
|
||||||
sublist.append([item, 1])
|
|
||||||
connection_list.append(sublist)
|
|
||||||
elif isinstance(item, list): # a list of single-pin components were specified
|
|
||||||
sublist = []
|
|
||||||
for subitem in item:
|
|
||||||
if yaml_data['connectors'][subitem].get('autogenerate'):
|
|
||||||
autogenerated_ids[subitem] = autogenerated_ids.get(subitem, 0) + 1
|
|
||||||
new_id = f'_{subitem}_{autogenerated_ids[subitem]}'
|
|
||||||
harness.add_connector(new_id, **yaml_data['connectors'][subitem])
|
|
||||||
sublist.append([new_id, 1])
|
|
||||||
else:
|
|
||||||
sublist.append([subitem, 1])
|
|
||||||
connection_list.append(sublist)
|
|
||||||
elif isinstance(item, dict): # a component with multiple pins was specified
|
|
||||||
sublist = []
|
|
||||||
id = list(item.keys())[0]
|
|
||||||
pins = expand(list(item.values())[0])
|
|
||||||
for pin in pins:
|
|
||||||
sublist.append([id, pin])
|
|
||||||
connection_list.append(sublist)
|
|
||||||
else:
|
else:
|
||||||
raise Exception('Unexpected item in connection list')
|
designators_and_templates[designator] = template
|
||||||
|
else:
|
||||||
|
template, designator = (inp, inp)
|
||||||
|
if designator in designators_and_templates:
|
||||||
|
pass # referencing an exiting connector, no need to add again
|
||||||
|
else:
|
||||||
|
designators_and_templates[designator] = template
|
||||||
|
return (template, designator)
|
||||||
|
|
||||||
# actually connect components using connection list
|
# utilities to check for alternating connectors and cables/arrows ==========
|
||||||
for i, item in enumerate(connection_list):
|
|
||||||
id = item[0][0] # TODO: make more elegant/robust/pythonic
|
alternating_types = ['connector','cable/arrow']
|
||||||
if id in harness.cables:
|
expected_type = None
|
||||||
for j, con in enumerate(item):
|
|
||||||
if i == 0: # list started with a cable, no connector to join on left side
|
def check_type(designator, template, actual_type):
|
||||||
from_name = None
|
nonlocal expected_type
|
||||||
from_pin = None
|
if not expected_type: # each connection set may start with either section
|
||||||
|
expected_type = actual_type
|
||||||
|
|
||||||
|
if actual_type != expected_type: # did not alternate
|
||||||
|
raise Exception(f'Expected {expected_type}, but "{designator}" ("{template}") is {actual_type}')
|
||||||
|
|
||||||
|
def alternate_type(): # flip between connector and cable/arrow
|
||||||
|
nonlocal expected_type
|
||||||
|
expected_type = alternating_types[1 - alternating_types.index(expected_type)]
|
||||||
|
|
||||||
|
for connection_set in connection_sets:
|
||||||
|
|
||||||
|
# figure out number of parallel connections within this set
|
||||||
|
connectioncount = []
|
||||||
|
for entry in connection_set:
|
||||||
|
if isinstance(entry, list):
|
||||||
|
connectioncount.append(len(entry))
|
||||||
|
elif isinstance(entry, dict):
|
||||||
|
connectioncount.append(len(expand(list(entry.values())[0]))) # - X1: [1-4,6] yields 5
|
||||||
|
else:
|
||||||
|
pass # strings do not reveal connectioncount
|
||||||
|
if not any(connectioncount):
|
||||||
|
raise Exception('No item in connection set revealed number of connections')
|
||||||
|
# TODO: The following should be a valid connection set,
|
||||||
|
# even though no item reveals the connection count;
|
||||||
|
# the count is not needed because only a component-level mate happens.
|
||||||
|
# -
|
||||||
|
# - CONNECTOR
|
||||||
|
# - ==>
|
||||||
|
# - CONNECTOR
|
||||||
|
|
||||||
|
# check that all entries are the same length
|
||||||
|
if len(set(connectioncount)) > 1:
|
||||||
|
raise Exception('All items in connection set must reference the same number of connections')
|
||||||
|
# all entries are the same length, connection count is set
|
||||||
|
connectioncount = connectioncount[0]
|
||||||
|
|
||||||
|
# expand string entries to list entries of correct length
|
||||||
|
for index, entry in enumerate(connection_set):
|
||||||
|
if isinstance(entry, str):
|
||||||
|
connection_set[index] = [entry] * connectioncount
|
||||||
|
|
||||||
|
# resolve all designators
|
||||||
|
for index, entry in enumerate(connection_set):
|
||||||
|
if isinstance(entry, list):
|
||||||
|
for subindex, item in enumerate(entry):
|
||||||
|
template, designator = resolve_designator(item, template_separator_char)
|
||||||
|
connection_set[index][subindex] = designator
|
||||||
|
elif isinstance(entry, dict):
|
||||||
|
key = list(entry.keys())[0]
|
||||||
|
template, designator = resolve_designator(key, template_separator_char)
|
||||||
|
value = entry[key]
|
||||||
|
connection_set[index] = {designator: value}
|
||||||
|
else:
|
||||||
|
pass # string entries have been expanded in previous step
|
||||||
|
|
||||||
|
# expand all pin lists
|
||||||
|
for index, entry in enumerate(connection_set):
|
||||||
|
if isinstance(entry, list):
|
||||||
|
connection_set[index] = [{designator: 1} for designator in entry]
|
||||||
|
elif isinstance(entry, dict):
|
||||||
|
designator = list(entry.keys())[0]
|
||||||
|
pinlist = expand(entry[designator])
|
||||||
|
connection_set[index] = [{designator: pin} for pin in pinlist]
|
||||||
|
else:
|
||||||
|
pass # string entries have been expanded in previous step
|
||||||
|
|
||||||
|
# Populate wiring harness ==============================================
|
||||||
|
|
||||||
|
expected_type = None # reset check for alternating types
|
||||||
|
# at the beginning of every connection set
|
||||||
|
# since each set may begin with either type
|
||||||
|
|
||||||
|
# generate components
|
||||||
|
for entry in connection_set:
|
||||||
|
for item in entry:
|
||||||
|
designator = list(item.keys())[0]
|
||||||
|
template = designators_and_templates[designator]
|
||||||
|
|
||||||
|
if designator in harness.connectors: # existing connector instance
|
||||||
|
check_type(designator, template, 'connector')
|
||||||
|
elif template in template_connectors.keys(): # generate new connector instance from template
|
||||||
|
check_type(designator, template, 'connector')
|
||||||
|
harness.add_connector(name = designator, **template_connectors[template])
|
||||||
|
|
||||||
|
elif designator in harness.cables: # existing cable instance
|
||||||
|
check_type(designator, template, 'cable/arrow')
|
||||||
|
elif template in template_cables.keys(): # generate new cable instance from template
|
||||||
|
check_type(designator, template, 'cable/arrow')
|
||||||
|
harness.add_cable(name = designator, **template_cables[template])
|
||||||
|
|
||||||
|
elif is_arrow(designator):
|
||||||
|
check_type(designator, template, 'cable/arrow')
|
||||||
|
# arrows do not need to be generated here
|
||||||
|
else:
|
||||||
|
raise Exception(f'{template} is an unknown template/designator/arrow.')
|
||||||
|
|
||||||
|
alternate_type() # entries in connection set must alternate between connectors and cables/arrows
|
||||||
|
|
||||||
|
# transpose connection set list
|
||||||
|
# before: one item per component, one subitem per connection in set
|
||||||
|
# after: one item per connection in set, one subitem per component
|
||||||
|
connection_set = list(map(list, zip(*connection_set)))
|
||||||
|
|
||||||
|
# connect components
|
||||||
|
for index_entry, entry in enumerate(connection_set):
|
||||||
|
for index_item, item in enumerate(entry):
|
||||||
|
designator = list(item.keys())[0]
|
||||||
|
|
||||||
|
if designator in harness.cables:
|
||||||
|
if index_item == 0: # list started with a cable, no connector to join on left side
|
||||||
|
from_name, from_pin = (None, None)
|
||||||
else:
|
else:
|
||||||
from_name = connection_list[i-1][j][0]
|
from_name, from_pin = get_single_key_and_value(connection_set[index_entry][index_item-1])
|
||||||
from_pin = connection_list[i-1][j][1]
|
via_name, via_pin = (designator, item[designator])
|
||||||
via_name = item[j][0]
|
if index_item == len(entry) - 1: # list ends with a cable, no connector to join on right side
|
||||||
via_pin = item[j][1]
|
to_name, to_pin = (None, None)
|
||||||
if i == len(connection_list) - 1: # list ends with a cable, no connector to join on right side
|
|
||||||
to_name = None
|
|
||||||
to_pin = None
|
|
||||||
else:
|
else:
|
||||||
to_name = connection_list[i+1][j][0]
|
to_name, to_pin = get_single_key_and_value(connection_set[index_entry][index_item+1])
|
||||||
to_pin = connection_list[i+1][j][1]
|
|
||||||
harness.connect(from_name, from_pin, via_name, via_pin, to_name, to_pin)
|
harness.connect(from_name, from_pin, via_name, via_pin, to_name, to_pin)
|
||||||
|
|
||||||
|
elif is_arrow(designator):
|
||||||
|
if index_item == 0: # list starts with an arrow
|
||||||
|
raise Exception('An arrow cannot be at the start of a connection set')
|
||||||
|
elif index_item == len(entry) - 1: # list ends with an arrow
|
||||||
|
raise Exception('An arrow cannot be at the end of a connection set')
|
||||||
|
|
||||||
|
from_name, from_pin = get_single_key_and_value(connection_set[index_entry][index_item-1])
|
||||||
|
via_name, via_pin = (designator, None)
|
||||||
|
to_name, to_pin = get_single_key_and_value(connection_set[index_entry][index_item+1])
|
||||||
|
if '-' in designator: # mate pin by pin
|
||||||
|
harness.add_mate_pin(from_name, from_pin, to_name, to_pin, designator)
|
||||||
|
elif '=' in designator and index_entry == 0: # mate two connectors as a whole
|
||||||
|
harness.add_mate_component(from_name, to_name, designator)
|
||||||
|
|
||||||
|
# harness population completed =============================================
|
||||||
|
|
||||||
if "additional_bom_items" in yaml_data:
|
if "additional_bom_items" in yaml_data:
|
||||||
for line in yaml_data["additional_bom_items"]:
|
for line in yaml_data["additional_bom_items"]:
|
||||||
harness.add_bom_item(line)
|
harness.add_bom_item(line)
|
||||||
|
|||||||
@ -66,6 +66,12 @@ def expand(yaml_data):
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def get_single_key_and_value(d: dict):
|
||||||
|
k = list(d.keys())[0]
|
||||||
|
v = d[k]
|
||||||
|
return (k, v)
|
||||||
|
|
||||||
|
|
||||||
def int2tuple(inp):
|
def int2tuple(inp):
|
||||||
if isinstance(inp, tuple):
|
if isinstance(inp, tuple):
|
||||||
output = inp
|
output = inp
|
||||||
@ -106,6 +112,18 @@ def open_file_write(filename):
|
|||||||
def open_file_append(filename):
|
def open_file_append(filename):
|
||||||
return open(filename, 'a', encoding='UTF-8')
|
return open(filename, 'a', encoding='UTF-8')
|
||||||
|
|
||||||
|
def is_arrow(inp):
|
||||||
|
"""
|
||||||
|
Matches strings of one or multiple `-` or `=` (but not mixed)
|
||||||
|
optionally starting with `<` and/or ending with `>`.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
<-, --, ->, <->
|
||||||
|
<==, ==, ==>, <=>
|
||||||
|
"""
|
||||||
|
# regex by @shiraneyo
|
||||||
|
return bool(re.match(r"^\s*(?P<leftHead><?)(?P<body>-+|=+)(?P<rightHead>>?)\s*$", inp))
|
||||||
|
|
||||||
def aspect_ratio(image_src):
|
def aspect_ratio(image_src):
|
||||||
try:
|
try:
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|||||||
5
test/.gitignore
vendored
Normal file
5
test/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
*.bom.tsv
|
||||||
|
*.gv
|
||||||
|
*.html
|
||||||
|
*.png
|
||||||
|
*.svg
|
||||||
25
test/test1.yml
Normal file
25
test/test1.yml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# based on @stmaxed's example in #134
|
||||||
|
|
||||||
|
connectors:
|
||||||
|
X1: &X
|
||||||
|
type: Screw connector
|
||||||
|
subtype: male
|
||||||
|
color: GN
|
||||||
|
pincount: 4
|
||||||
|
pinlabels: [A, B, C, D]
|
||||||
|
F:
|
||||||
|
style: simple
|
||||||
|
type: Ferrule
|
||||||
|
color: GY
|
||||||
|
|
||||||
|
cables:
|
||||||
|
W:
|
||||||
|
color: BK
|
||||||
|
colors: [BK, WH, BU, BN]
|
||||||
|
|
||||||
|
connections:
|
||||||
|
- # ferrules + connector X1
|
||||||
|
- W.W1: [1-4]
|
||||||
|
- F.
|
||||||
|
- -->
|
||||||
|
- X1: [1-4]
|
||||||
25
test/test2.yml
Normal file
25
test/test2.yml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# based on @MSBGit's example in #134
|
||||||
|
|
||||||
|
connectors:
|
||||||
|
X1: &dupont
|
||||||
|
type: Dupont 2.54mm
|
||||||
|
subtype: male
|
||||||
|
pincount: 5
|
||||||
|
color: BK
|
||||||
|
X2:
|
||||||
|
<<: *dupont
|
||||||
|
subtype: female
|
||||||
|
|
||||||
|
cables:
|
||||||
|
W:
|
||||||
|
category: bundle
|
||||||
|
colors: [RD, BK, BU, GN]
|
||||||
|
length: 0.2
|
||||||
|
|
||||||
|
connections:
|
||||||
|
-
|
||||||
|
- W.W1: [1-4]
|
||||||
|
- X1: [1-4]
|
||||||
|
- ==>
|
||||||
|
- X2: [1-4]
|
||||||
|
- W.W2: [1-4]
|
||||||
33
test/test3.yml
Normal file
33
test/test3.yml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# expanding upon @stmaxed's example in #134
|
||||||
|
|
||||||
|
connectors:
|
||||||
|
X1: &X
|
||||||
|
type: Screw connector
|
||||||
|
subtype: male
|
||||||
|
color: GN
|
||||||
|
pincount: 4
|
||||||
|
pinlabels: [A, B, C, D]
|
||||||
|
X2:
|
||||||
|
<<: *X
|
||||||
|
subtype: female
|
||||||
|
F:
|
||||||
|
style: simple
|
||||||
|
type: Ferrule
|
||||||
|
color: GY
|
||||||
|
|
||||||
|
cables:
|
||||||
|
W:
|
||||||
|
color: BK
|
||||||
|
colors: [BK, WH, BU, BN]
|
||||||
|
|
||||||
|
connections:
|
||||||
|
- # ferrules + connector X1
|
||||||
|
- W.W1: [1-4]
|
||||||
|
- F.
|
||||||
|
- -->
|
||||||
|
- X1: [1-4]
|
||||||
|
- ==>
|
||||||
|
- X2: [1-4]
|
||||||
|
- <--
|
||||||
|
- F.
|
||||||
|
- W.W2: [1-4]
|
||||||
26
test/test4.yml
Normal file
26
test/test4.yml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# based on @formatc1702's example in #184
|
||||||
|
|
||||||
|
connectors:
|
||||||
|
X:
|
||||||
|
pincount: 4
|
||||||
|
pinlabels: [A, B, C, D]
|
||||||
|
F:
|
||||||
|
style: simple
|
||||||
|
type: ferrule
|
||||||
|
|
||||||
|
cables:
|
||||||
|
C:
|
||||||
|
wirecount: 4
|
||||||
|
color_code: DIN
|
||||||
|
|
||||||
|
connections:
|
||||||
|
-
|
||||||
|
- X.X1: [1-4]
|
||||||
|
- C.C1: [1-4]
|
||||||
|
- [F.F1, F.F2, F.F3, F.F4] # generate new instances of F and assign designators
|
||||||
|
- C.C2: [1-4]
|
||||||
|
- X.X2: [1-4]
|
||||||
|
-
|
||||||
|
- [F1, F2, F3, F4] # use previously assigned designators
|
||||||
|
- C.C3: [1-4]
|
||||||
|
- X.X3: [1-4]
|
||||||
64
test/test5.yml
Normal file
64
test/test5.yml
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
connectors:
|
||||||
|
XS:
|
||||||
|
type: Screw terminal connector
|
||||||
|
subtype: male
|
||||||
|
color: GN
|
||||||
|
pincount: 3
|
||||||
|
XM:
|
||||||
|
type: Molex KK 254
|
||||||
|
subtype: female
|
||||||
|
pincount: 3
|
||||||
|
F:
|
||||||
|
style: simple
|
||||||
|
type: Ferrule
|
||||||
|
subtype: 0.25 mm2
|
||||||
|
color: LB
|
||||||
|
LED_RD: &LED
|
||||||
|
type: LED
|
||||||
|
subtype: 5mm
|
||||||
|
show_pincount: false
|
||||||
|
color: RD
|
||||||
|
pins: [+, -]
|
||||||
|
pinlabels: [Anode, Cathode]
|
||||||
|
LED_GN:
|
||||||
|
<<: *LED
|
||||||
|
color: GN
|
||||||
|
LED_YE:
|
||||||
|
<<: *LED
|
||||||
|
color: YE
|
||||||
|
|
||||||
|
cables:
|
||||||
|
C:
|
||||||
|
category: bundle
|
||||||
|
# show_name: false
|
||||||
|
colors: [RD, BK]
|
||||||
|
gauge: 0.25 mm2
|
||||||
|
W:
|
||||||
|
category: bundle
|
||||||
|
# show_name: false
|
||||||
|
colors: [BN]
|
||||||
|
gauge: 0.25 mm2
|
||||||
|
|
||||||
|
connections:
|
||||||
|
-
|
||||||
|
- [F.F1, F.F2]
|
||||||
|
- C.W1: [1,2]
|
||||||
|
- LED_RD: [+,-]
|
||||||
|
-
|
||||||
|
- F.F3
|
||||||
|
- W.W2: [1]
|
||||||
|
- LED_GN: [+]
|
||||||
|
-
|
||||||
|
- LED_GN: [-]
|
||||||
|
- W.W3: [1]
|
||||||
|
-
|
||||||
|
- XS.X1: [1-3]
|
||||||
|
- <--
|
||||||
|
- [F1, F2, F3]
|
||||||
|
-
|
||||||
|
- LED_YE: [+,-]
|
||||||
|
- C.W4: [1,2]
|
||||||
|
- XM.X2: [1,2]
|
||||||
|
-
|
||||||
|
- W3: [1]
|
||||||
|
- X2: [3]
|
||||||
55
test/test9.yml
Normal file
55
test/test9.yml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
connectors:
|
||||||
|
JSTMALE: &JST_SM # use generic names here, assign designators at generation time
|
||||||
|
type: JST SM
|
||||||
|
subtype: male
|
||||||
|
pincount: 4
|
||||||
|
pinlabels: [A, B, C, D]
|
||||||
|
JSTFEMALE:
|
||||||
|
<<: *JST_SM # easily create JSTMALE's matching connector
|
||||||
|
subtype: female
|
||||||
|
X4: # this connector is only used once, use fixed designator here already
|
||||||
|
type: Screw terminal connector
|
||||||
|
pincount: 4
|
||||||
|
color: GN
|
||||||
|
pinlabels: [W, X, Y, Z]
|
||||||
|
S:
|
||||||
|
style: simple
|
||||||
|
type: Splice
|
||||||
|
color: CU
|
||||||
|
F:
|
||||||
|
style: simple
|
||||||
|
type: Ferrule
|
||||||
|
color: GY
|
||||||
|
|
||||||
|
|
||||||
|
cables:
|
||||||
|
CABLE:
|
||||||
|
wirecount: 4
|
||||||
|
color_code: DIN
|
||||||
|
length: 0.1
|
||||||
|
WIRE:
|
||||||
|
wirecount: 1
|
||||||
|
colors: [BK]
|
||||||
|
length: 0.1
|
||||||
|
|
||||||
|
connections:
|
||||||
|
-
|
||||||
|
- JSTMALE.X1: [4-1] # use `.` syntax to generate a new instance of JSTMALE, named X1
|
||||||
|
- CABLE.W1: [1-4] # same syntax for cables
|
||||||
|
- [S., S., S.S1, S.] # splice W1 and W2 together; only wire #3 needs a user-defined designator
|
||||||
|
- CABLE.W2: [1-4]
|
||||||
|
- S. # test shorthand, auto-get required number of ferrules from context
|
||||||
|
- CABLE.W21: [1-4]
|
||||||
|
- JSTFEMALE.X2: [1-4]
|
||||||
|
- <=> # mate X2 and X3
|
||||||
|
- JSTMALE.X3: [1-4]
|
||||||
|
- CABLE.W3: [1-4]
|
||||||
|
- [F., F., F., F.]
|
||||||
|
- --> # insert ferrules into screw terminal connector
|
||||||
|
- X4: [2,1,4,3] # X4 does not require auto-generation, thus no `.` syntax here
|
||||||
|
-
|
||||||
|
- S1: [1] # reuse previously generated splice
|
||||||
|
# TODO: Make it work with `- F1` only, making pin 1 is implied
|
||||||
|
- WIRE.: [1] # We don't care about a simple wire's designator, auto-generate please!
|
||||||
|
# TODO: Make it work with `- W.W4: 1`, dropping the need for `[]`
|
||||||
|
- X2: [4]
|
||||||
@ -5,7 +5,6 @@ connectors:
|
|||||||
subtype: female
|
subtype: female
|
||||||
F1:
|
F1:
|
||||||
style: simple
|
style: simple
|
||||||
autogenerate: true
|
|
||||||
type: Crimp ferrule
|
type: Crimp ferrule
|
||||||
subtype: 0.5 mm²
|
subtype: 0.5 mm²
|
||||||
color: OG # optional color
|
color: OG # optional color
|
||||||
@ -19,6 +18,6 @@ cables:
|
|||||||
|
|
||||||
connections:
|
connections:
|
||||||
-
|
-
|
||||||
- F1 # a new ferrule is auto-generated for each of the four wires
|
- F1. # a new ferrule is auto-generated for each of the four wires
|
||||||
- W1: [1-4]
|
- W1: [1-4]
|
||||||
- X1: [1-4]
|
- X1: [1-4]
|
||||||
|
|||||||
@ -5,13 +5,11 @@ connectors:
|
|||||||
subtype: female
|
subtype: female
|
||||||
F_10: # this is a unique ferrule
|
F_10: # this is a unique ferrule
|
||||||
style: simple
|
style: simple
|
||||||
show_name: false # non-autogenerated connectors show their name by default; override
|
|
||||||
type: Crimp ferrule
|
type: Crimp ferrule
|
||||||
subtype: 1.0 mm²
|
subtype: 1.0 mm²
|
||||||
color: YE # optional color
|
color: YE # optional color
|
||||||
F_05: # this is a ferrule that will be auto-generated on demand
|
F_05: # this is a ferrule that will be auto-generated on demand
|
||||||
style: simple
|
style: simple
|
||||||
autogenerate: true
|
|
||||||
type: Crimp ferrule
|
type: Crimp ferrule
|
||||||
subtype: 0.5 mm²
|
subtype: 0.5 mm²
|
||||||
color: OG
|
color: OG
|
||||||
@ -25,6 +23,6 @@ cables:
|
|||||||
|
|
||||||
connections:
|
connections:
|
||||||
-
|
-
|
||||||
- [F_05, F_10, F_10, F_05]
|
- [F_05., F_10.F1, F_10.F1, F_05.]
|
||||||
- W1: [1-4]
|
- W1: [1-4]
|
||||||
- X1: [1-4]
|
- X1: [1-4]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user