Make connecting components together easier and more flexible
Closes #67. - Allow defining arbitrarily long lists of alternating connectors and cables in a connection set. - Start work towards removing 'ferrules' as special case, merging them with normal connectors - Stramline auto-generation of simple, one pin connectors (ferrules, wire splices, ...)
This commit is contained in:
parent
8f5b1aaf16
commit
b4791900f2
@ -24,6 +24,7 @@ class Connector:
|
||||
show_name: bool = True
|
||||
show_pincount: bool = True
|
||||
hide_disconnected_pins: bool = False
|
||||
autogenerate: bool = False
|
||||
|
||||
def __post_init__(self):
|
||||
self.ports_left = False
|
||||
|
||||
@ -13,6 +13,7 @@ if __name__ == '__main__':
|
||||
|
||||
|
||||
from wireviz.Harness import Harness
|
||||
from wireviz.wv_helper import expand
|
||||
|
||||
|
||||
def parse(yaml_input, file_out=None, generate_bom=False, return_types: (None, str, Tuple[str]) = None):
|
||||
@ -31,40 +32,6 @@ def parse(yaml_input, file_out=None, generate_bom=False, return_types: (None, st
|
||||
|
||||
yaml_data = yaml.safe_load(yaml_input)
|
||||
|
||||
def expand(yaml_data):
|
||||
# yaml_data can be:
|
||||
# - a singleton (normally str or int)
|
||||
# - a list of str or int
|
||||
# if str is of the format '#-#', it is treated as a range (inclusive) and expanded
|
||||
output = []
|
||||
if not isinstance(yaml_data, list):
|
||||
yaml_data = [yaml_data]
|
||||
for e in yaml_data:
|
||||
e = str(e)
|
||||
if '-' in e: # list of pins
|
||||
a, b = tuple(map(int, e.split('-')))
|
||||
if a < b:
|
||||
for x in range(a, b + 1):
|
||||
output.append(x)
|
||||
elif a > b:
|
||||
for x in range(a, b - 1, -1):
|
||||
output.append(x)
|
||||
elif a == b:
|
||||
output.append(a)
|
||||
else:
|
||||
try:
|
||||
x = int(e)
|
||||
except Exception:
|
||||
x = e
|
||||
output.append(x)
|
||||
return output
|
||||
|
||||
def check_designators(what, where):
|
||||
for i, x in enumerate(what):
|
||||
if x not in yaml_data[where[i]]:
|
||||
return False
|
||||
return True
|
||||
|
||||
harness = Harness()
|
||||
|
||||
# add items
|
||||
@ -74,11 +41,12 @@ def parse(yaml_input, file_out=None, generate_bom=False, return_types: (None, st
|
||||
if sec in yaml_data and type(yaml_data[sec]) == ty:
|
||||
if len(yaml_data[sec]) > 0:
|
||||
if ty == dict:
|
||||
for key, o in yaml_data[sec].items():
|
||||
for key, attribs in yaml_data[sec].items():
|
||||
if sec == 'connectors':
|
||||
harness.add_connector(name=key, **o)
|
||||
if not attribs.get('autogenerate', False):
|
||||
harness.add_connector(name=key, **attribs)
|
||||
elif sec == 'cables':
|
||||
harness.add_cable(name=key, **o)
|
||||
harness.add_cable(name=key, **attribs)
|
||||
elif sec == 'ferrules':
|
||||
pass
|
||||
else:
|
||||
@ -90,109 +58,93 @@ def parse(yaml_input, file_out=None, generate_bom=False, return_types: (None, st
|
||||
yaml_data[sec] = []
|
||||
|
||||
# add connections
|
||||
ferrule_counter = 0
|
||||
for connections in yaml_data['connections']:
|
||||
if len(connections) == 3: # format: connector -- cable -- connector
|
||||
|
||||
for connection in connections:
|
||||
if len(list(connection.keys())) != 1: # check that each entry in con has only one key, which is the designator
|
||||
raise Exception('Too many keys')
|
||||
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
|
||||
|
||||
from_name = list(connections[0].keys())[0]
|
||||
via_name = list(connections[1].keys())[0]
|
||||
to_name = list(connections[2].keys())[0]
|
||||
autogenerated_ids = {}
|
||||
for connection in yaml_data['connections']:
|
||||
# TODO: check that items are of alternating type CONNECTOR/FERRULE/FERRULE_LIST and CABLE/WIRE
|
||||
# TODO: special case: loops!
|
||||
|
||||
if not check_designators([from_name, via_name, to_name], ('connectors', 'cables', 'connectors')):
|
||||
print([from_name, via_name, to_name])
|
||||
raise Exception('Bad connection definition (3)')
|
||||
# check that all iterable items (lists and dicts) are the same length
|
||||
itemcount = None
|
||||
for item in connection:
|
||||
if isinstance(item, list):
|
||||
itemcount_new = len(item)
|
||||
elif isinstance(item, dict):
|
||||
if len(item.keys()) != 1:
|
||||
raise Exception('Dicts may contain only one item here!')
|
||||
itemcount_new = len(expand(list(item.values())[0]))
|
||||
elif isinstance(item, str):
|
||||
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!')
|
||||
|
||||
from_pins = expand(connections[0][from_name])
|
||||
via_pins = expand(connections[1][via_name])
|
||||
to_pins = expand(connections[2][to_name])
|
||||
# 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)
|
||||
elif False: # TODO: placeholer; a loop inside a connector was specified
|
||||
pass
|
||||
else:
|
||||
raise Exception('Unexpected item in connection list')
|
||||
|
||||
if len(from_pins) != len(via_pins) or len(via_pins) != len(to_pins):
|
||||
raise Exception('List length mismatch')
|
||||
|
||||
for (from_pin, via_pin, to_pin) in zip(from_pins, via_pins, to_pins):
|
||||
# actually connect things 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
|
||||
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
|
||||
else:
|
||||
to_name = connection_list[i+1][j][0]
|
||||
to_pin = connection_list[i+1][j][1]
|
||||
harness.connect(from_name, from_pin, via_name, via_pin, to_name, to_pin)
|
||||
|
||||
elif len(connections) == 2:
|
||||
|
||||
for connection in connections:
|
||||
if type(connection) is dict:
|
||||
if len(list(connection.keys())) != 1: # check that each entry in con has only one key, which is the designator
|
||||
raise Exception('Too many keys')
|
||||
|
||||
# hack to make the format for ferrules compatible with the formats for connectors and cables
|
||||
if type(connections[0]) == str:
|
||||
name = connections[0]
|
||||
connections[0] = {}
|
||||
connections[0][name] = name
|
||||
if type(connections[1]) == str:
|
||||
name = connections[1]
|
||||
connections[1] = {}
|
||||
connections[1][name] = name
|
||||
|
||||
from_name = list(connections[0].keys())[0]
|
||||
to_name = list(connections[1].keys())[0]
|
||||
|
||||
con_cbl = check_designators([from_name, to_name], ('connectors', 'cables'))
|
||||
cbl_con = check_designators([from_name, to_name], ('cables', 'connectors'))
|
||||
con_con = check_designators([from_name, to_name], ('connectors', 'connectors'))
|
||||
|
||||
fer_cbl = check_designators([from_name, to_name], ('ferrules', 'cables'))
|
||||
cbl_fer = check_designators([from_name, to_name], ('cables', 'ferrules'))
|
||||
|
||||
if not con_cbl and not cbl_con and not con_con and not fer_cbl and not cbl_fer:
|
||||
raise Exception('Wrong designators')
|
||||
|
||||
from_pins = expand(connections[0][from_name])
|
||||
to_pins = expand(connections[1][to_name])
|
||||
|
||||
if con_cbl or cbl_con or con_con:
|
||||
if len(from_pins) != len(to_pins):
|
||||
raise Exception('List length mismatch')
|
||||
|
||||
if con_cbl or cbl_con:
|
||||
for (from_pin, to_pin) in zip(from_pins, to_pins):
|
||||
if con_cbl:
|
||||
harness.connect(from_name, from_pin, to_name, to_pin, None, None)
|
||||
else: # cbl_con
|
||||
harness.connect(None, None, from_name, from_pin, to_name, to_pin)
|
||||
elif con_con:
|
||||
cocon_coname = list(connections[0].keys())[0]
|
||||
from_pins = expand(connections[0][from_name])
|
||||
to_pins = expand(connections[1][to_name])
|
||||
|
||||
for (from_pin, to_pin) in zip(from_pins, to_pins):
|
||||
harness.loop(cocon_coname, from_pin, to_pin)
|
||||
if fer_cbl or cbl_fer:
|
||||
from_pins = expand(connections[0][from_name])
|
||||
to_pins = expand(connections[1][to_name])
|
||||
|
||||
if fer_cbl:
|
||||
ferrule_name = from_name
|
||||
cable_name = to_name
|
||||
cable_pins = to_pins
|
||||
else:
|
||||
ferrule_name = to_name
|
||||
cable_name = from_name
|
||||
cable_pins = from_pins
|
||||
|
||||
ferrule_params = yaml_data['ferrules'][ferrule_name]
|
||||
for cable_pin in cable_pins:
|
||||
ferrule_counter = ferrule_counter + 1
|
||||
ferrule_id = f'_F{ferrule_counter}'
|
||||
harness.add_connector(ferrule_id, category='ferrule', **ferrule_params)
|
||||
|
||||
if fer_cbl:
|
||||
harness.connect(ferrule_id, 1, cable_name, cable_pin, None, None)
|
||||
else:
|
||||
harness.connect(None, None, cable_name, cable_pin, ferrule_id, 1)
|
||||
|
||||
else:
|
||||
raise Exception('Wrong number of connection parameters')
|
||||
|
||||
if file_out is not None:
|
||||
harness.output(filename=file_out, fmt=('png', 'svg'), gen_bom=generate_bom, view=False)
|
||||
|
||||
|
||||
@ -59,6 +59,35 @@ def nested_html_table(rows):
|
||||
return html
|
||||
|
||||
|
||||
def expand(yaml_data):
|
||||
# yaml_data can be:
|
||||
# - a singleton (normally str or int)
|
||||
# - a list of str or int
|
||||
# if str is of the format '#-#', it is treated as a range (inclusive) and expanded
|
||||
output = []
|
||||
if not isinstance(yaml_data, list):
|
||||
yaml_data = [yaml_data]
|
||||
for e in yaml_data:
|
||||
e = str(e)
|
||||
if '-' in e: # list of pins
|
||||
a, b = tuple(map(int, e.split('-')))
|
||||
if a < b:
|
||||
for x in range(a, b + 1):
|
||||
output.append(x)
|
||||
elif a > b:
|
||||
for x in range(a, b - 1, -1):
|
||||
output.append(x)
|
||||
elif a == b:
|
||||
output.append(a)
|
||||
else:
|
||||
try:
|
||||
x = int(e)
|
||||
except Exception:
|
||||
x = e
|
||||
output.append(x)
|
||||
return output
|
||||
|
||||
|
||||
def int2tuple(inp):
|
||||
if isinstance(inp, tuple):
|
||||
output = inp
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user