Squash feature/mate+autogenerate branch
This commit is contained in:
parent
a6efd28124
commit
8ea8248721
@ -22,9 +22,8 @@ connectors:
|
||||
X4:
|
||||
<<: *molex_f
|
||||
pinlabels: [GND, +12V, MISO, MOSI, SCK]
|
||||
ferrule_crimp:
|
||||
F:
|
||||
style: simple
|
||||
autogenerate: true
|
||||
type: Crimp ferrule
|
||||
subtype: 0.25 mm²
|
||||
color: YE
|
||||
@ -64,6 +63,6 @@ connections:
|
||||
- W3: [1-4]
|
||||
- X4: [1,3-5]
|
||||
-
|
||||
- ferrule_crimp
|
||||
- F.
|
||||
- W4: [1,2]
|
||||
- X4: [1,2]
|
||||
|
||||
@ -8,13 +8,12 @@ cables:
|
||||
category: bundle
|
||||
|
||||
connectors:
|
||||
ferrule_crimp:
|
||||
F:
|
||||
style: simple
|
||||
autogenerate: true
|
||||
type: Crimp ferrule
|
||||
|
||||
connections:
|
||||
-
|
||||
- ferrule_crimp
|
||||
- F.
|
||||
- W1: [1-6]
|
||||
- ferrule_crimp
|
||||
- F.
|
||||
|
||||
@ -140,7 +140,6 @@ class Connector:
|
||||
show_name: Optional[bool] = None
|
||||
show_pincount: Optional[bool] = None
|
||||
hide_disconnected_pins: bool = False
|
||||
autogenerate: bool = False
|
||||
loops: List[List[Pin]] = field(default_factory=list)
|
||||
ignore_in_bom: bool = False
|
||||
additional_components: List[AdditionalComponent] = field(default_factory=list)
|
||||
@ -172,7 +171,8 @@ class Connector:
|
||||
raise Exception('Pins are not unique')
|
||||
|
||||
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:
|
||||
self.show_pincount = self.style != 'simple' # hide pincount for simple (1 pin) connectors by default
|
||||
@ -227,7 +227,7 @@ class Cable:
|
||||
colors: List[Colors] = field(default_factory=list)
|
||||
wirelabels: List[Wire] = field(default_factory=list)
|
||||
color_code: Optional[ColorScheme] = None
|
||||
show_name: bool = True
|
||||
show_name: Optional[bool] = None
|
||||
show_wirecount: bool = True
|
||||
show_wirenumbers: Optional[bool] = None
|
||||
ignore_in_bom: bool = False
|
||||
@ -310,9 +310,11 @@ class Cable:
|
||||
else:
|
||||
raise Exception('lists of part data are only supported for bundles')
|
||||
|
||||
# by default, show wire numbers for cables, hide for bundles
|
||||
if self.show_wirenumbers is None:
|
||||
self.show_wirenumbers = self.category != 'bundle'
|
||||
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:
|
||||
self.show_wirenumbers = self.category != 'bundle' # by default, show wire numbers for cables, hide for bundles
|
||||
|
||||
for i, item in enumerate(self.additional_components):
|
||||
if isinstance(item, dict):
|
||||
@ -351,3 +353,17 @@ class Connection:
|
||||
via_port: Wire
|
||||
to_name: Optional[Designator]
|
||||
to_port: Optional[PinIndex]
|
||||
|
||||
@dataclass
|
||||
class MatePin:
|
||||
from_name: Designator
|
||||
from_port: PinIndex
|
||||
to_name: Designator
|
||||
to_port: PinIndex
|
||||
shape: str
|
||||
|
||||
@dataclass
|
||||
class MateComponent:
|
||||
from_name: Designator
|
||||
to_name: Designator
|
||||
shape: str
|
||||
|
||||
@ -9,7 +9,7 @@ from itertools import zip_longest
|
||||
import re
|
||||
|
||||
from wireviz import wv_colors, __version__, APP_NAME, APP_URL
|
||||
from wireviz.DataClasses import Metadata, Options, Tweak, Connector, Cable
|
||||
from wireviz.DataClasses import Cable, Connector, MatePin, MateComponent, Metadata, Options, Tweak
|
||||
from wireviz.wv_colors import get_color_hex, translate_color
|
||||
from wireviz.wv_gv_html import nested_html_table, \
|
||||
html_bgcolor_attr, html_bgcolor, html_colorbar, \
|
||||
@ -19,8 +19,7 @@ from wireviz.wv_bom import pn_info_string, component_table_entry, \
|
||||
HEADER_PN, HEADER_MPN, HEADER_SPN
|
||||
from wireviz.wv_html import generate_html_output
|
||||
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
|
||||
|
||||
@dataclass
|
||||
class Harness:
|
||||
@ -31,6 +30,7 @@ class Harness:
|
||||
def __post_init__(self):
|
||||
self.connectors = {}
|
||||
self.cables = {}
|
||||
self.mates = []
|
||||
self._bom = [] # Internal Cache for generated bom
|
||||
self.additional_bom_items = []
|
||||
|
||||
@ -40,6 +40,12 @@ class Harness:
|
||||
def add_cable(self, name: str, *args, **kwargs) -> None:
|
||||
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:
|
||||
self.additional_bom_items.append(item)
|
||||
|
||||
@ -66,7 +72,12 @@ class Harness:
|
||||
raise Exception(f'{name}:{pin} not found.')
|
||||
|
||||
# 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]
|
||||
# check if provided name is ambiguous
|
||||
if via_wire in cable.colors and via_wire in cable.wirelabels:
|
||||
@ -114,8 +125,17 @@ class Harness:
|
||||
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():
|
||||
|
||||
@ -409,6 +429,30 @@ class Harness:
|
||||
typecheck('tweak.append', self.tweak.append, str)
|
||||
dot.body.append(self.tweak.append)
|
||||
|
||||
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:#000000'
|
||||
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
|
||||
|
||||
@property
|
||||
|
||||
@ -15,7 +15,7 @@ if __name__ == '__main__':
|
||||
from wireviz import __version__
|
||||
from wireviz.DataClasses import Metadata, Options, Tweak
|
||||
from wireviz.Harness import Harness
|
||||
from wireviz.wv_helper import expand, open_file_read
|
||||
from wireviz.wv_helper import expand, get_single_key_and_value, is_arrow, open_file_read
|
||||
|
||||
|
||||
def parse(yaml_input: str, file_out: (str, Path) = None, return_types: (None, str, Tuple[str]) = None) -> Any:
|
||||
@ -35,20 +35,33 @@ def parse(yaml_input: str, file_out: (str, Path) = None, return_types: (None, st
|
||||
|
||||
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(
|
||||
metadata = Metadata(**yaml_data.get('metadata', {})),
|
||||
options = Options(**yaml_data.get('options', {})),
|
||||
tweak = Tweak(**yaml_data.get('tweak', {})),
|
||||
)
|
||||
# others
|
||||
designators_and_templates = {} # store mapping of components to their respective template
|
||||
autogenerated_designators = {} # keep track of auto-generated designators to avoid duplicates
|
||||
|
||||
if 'title' not in harness.metadata:
|
||||
harness.metadata['title'] = Path(file_out).stem
|
||||
|
||||
# add items
|
||||
# parse YAML input file ====================================================
|
||||
|
||||
sections = ['connectors', 'cables', 'connections']
|
||||
types = [dict, dict, list]
|
||||
for sec, ty in zip(sections, types):
|
||||
if sec in yaml_data and type(yaml_data[sec]) == ty:
|
||||
if len(yaml_data[sec]) > 0:
|
||||
if sec in yaml_data and type(yaml_data[sec]) == ty: # section exists
|
||||
if len(yaml_data[sec]) > 0: # section has contents
|
||||
if ty == dict:
|
||||
for key, attribs in yaml_data[sec].items():
|
||||
# The Image dataclass might need to open an image file with a relative path.
|
||||
@ -56,132 +69,191 @@ def parse(yaml_input: str, file_out: (str, Path) = None, return_types: (None, st
|
||||
if isinstance(image, dict):
|
||||
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 not attribs.get('autogenerate', False):
|
||||
harness.add_connector(name=key, **attribs)
|
||||
template_connectors[key] = attribs
|
||||
elif sec == 'cables':
|
||||
harness.add_cable(name=key, **attribs)
|
||||
else:
|
||||
pass # section exists but is empty
|
||||
template_cables[key] = attribs
|
||||
else: # section exists but is empty
|
||||
pass
|
||||
else: # section does not exist, create empty section
|
||||
if ty == dict:
|
||||
yaml_data[sec] = {}
|
||||
elif ty == list:
|
||||
yaml_data[sec] = []
|
||||
|
||||
# add connections
|
||||
connection_sets = yaml_data['connections']
|
||||
|
||||
def check_designators(what, where): # helper function
|
||||
for i, x in enumerate(what):
|
||||
if x not in yaml_data[where[i]]:
|
||||
return False
|
||||
return True
|
||||
# go through connection sets, generate and connect components ==============
|
||||
|
||||
autogenerated_ids = {}
|
||||
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
|
||||
template_separator_char = '.' # TODO: make user-configurable (in case user wants to use `.` as part of their template/component names)
|
||||
|
||||
# check which section the first item belongs to
|
||||
alternating_sections = ['connectors','cables']
|
||||
for index, section in enumerate(alternating_sections):
|
||||
if first_item in yaml_data[section]:
|
||||
expected_index = index
|
||||
break
|
||||
def resolve_designator(inp, separator):
|
||||
if separator in inp: # generate a new instance of an item
|
||||
if inp.count(separator) > 1:
|
||||
raise Exception(f'{inp} - Found more than one separator ({separator})')
|
||||
template, designator = inp.split(separator)
|
||||
if designator == '':
|
||||
autogenerated_designators[template] = autogenerated_designators.get(template, 0) + 1
|
||||
designator = f'__{template}_{autogenerated_designators[template]}'
|
||||
# check if redefining existing component to different template
|
||||
if designator in designators_and_templates:
|
||||
if designators_and_templates[designator] != template:
|
||||
raise Exception(f'Trying to redefine {designator} from {designators_and_templates[designator]} to {template}')
|
||||
else:
|
||||
raise Exception('First item not found anywhere.')
|
||||
expected_index = 1 - expected_index # flip once since it is flipped back at the *beginning* of every loop
|
||||
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)
|
||||
|
||||
# check that all iterable items (lists and dicts) are the same length
|
||||
# and that they are alternating between connectors and cables/bundles, starting with either
|
||||
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!')
|
||||
# utilities to check for alternating connectors and cables/arrows ==========
|
||||
|
||||
# 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:
|
||||
raise Exception('Unexpected item in connection list')
|
||||
alternating_types = ['connector','cable/arrow']
|
||||
expected_type = None
|
||||
|
||||
# actually connect components using connection list
|
||||
for i, item in enumerate(connection_list):
|
||||
id = item[0][0] # TODO: make more elegant/robust/pythonic
|
||||
if id in harness.cables:
|
||||
for j, con in enumerate(item):
|
||||
if i == 0: # list started with a cable, no connector to join on left side
|
||||
from_name = None
|
||||
from_pin = None
|
||||
def check_type(designator, template, actual_type):
|
||||
nonlocal expected_type
|
||||
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:
|
||||
from_name = connection_list[i-1][j][0]
|
||||
from_pin = connection_list[i-1][j][1]
|
||||
via_name = item[j][0]
|
||||
via_pin = item[j][1]
|
||||
if i == len(connection_list) - 1: # list ends with a cable, no connector to join on right side
|
||||
to_name = None
|
||||
to_pin = None
|
||||
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:
|
||||
to_name = connection_list[i+1][j][0]
|
||||
to_pin = connection_list[i+1][j][1]
|
||||
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:
|
||||
from_name, from_pin = get_single_key_and_value(connection_set[index_entry][index_item-1])
|
||||
via_name, via_pin = (designator, item[designator])
|
||||
if index_item == len(entry) - 1: # list ends with a cable, no connector to join on right side
|
||||
to_name, to_pin = (None, None)
|
||||
else:
|
||||
to_name, to_pin = get_single_key_and_value(connection_set[index_entry][index_item+1])
|
||||
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:
|
||||
for line in yaml_data["additional_bom_items"]:
|
||||
harness.add_bom_item(line)
|
||||
|
||||
@ -65,6 +65,12 @@ def expand(yaml_data):
|
||||
return output
|
||||
|
||||
|
||||
def get_single_key_and_value(d: dict):
|
||||
k = list(d.keys())[0]
|
||||
v = d[k]
|
||||
return (k, v)
|
||||
|
||||
|
||||
def int2tuple(inp):
|
||||
if isinstance(inp, tuple):
|
||||
output = inp
|
||||
@ -105,6 +111,18 @@ def open_file_write(filename):
|
||||
def open_file_append(filename):
|
||||
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):
|
||||
try:
|
||||
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
|
||||
F1:
|
||||
style: simple
|
||||
autogenerate: true
|
||||
type: Crimp ferrule
|
||||
subtype: 0.5 mm²
|
||||
color: OG # optional color
|
||||
@ -19,6 +18,6 @@ cables:
|
||||
|
||||
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]
|
||||
- X1: [1-4]
|
||||
|
||||
@ -5,13 +5,11 @@ connectors:
|
||||
subtype: female
|
||||
F_10: # this is a unique ferrule
|
||||
style: simple
|
||||
show_name: false # non-autogenerated connectors show their name by default; override
|
||||
type: Crimp ferrule
|
||||
subtype: 1.0 mm²
|
||||
color: YE # optional color
|
||||
F_05: # this is a ferrule that will be auto-generated on demand
|
||||
style: simple
|
||||
autogenerate: true
|
||||
type: Crimp ferrule
|
||||
subtype: 0.5 mm²
|
||||
color: OG
|
||||
@ -25,6 +23,6 @@ cables:
|
||||
|
||||
connections:
|
||||
-
|
||||
- [F_05, F_10, F_10, F_05]
|
||||
- [F_05., F_10.F1, F_10.F1, F_05.]
|
||||
- W1: [1-4]
|
||||
- X1: [1-4]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user