Skip to content

Commit

Permalink
Make connecting components together easier and more flexible
Browse files Browse the repository at this point in the history
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, ...)
  • Loading branch information
formatc1702 committed Jul 10, 2020
1 parent 8f5b1aa commit b479190
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 138 deletions.
1 change: 1 addition & 0 deletions src/wireviz/DataClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
228 changes: 90 additions & 138 deletions src/wireviz/wireviz.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@


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):
Expand All @@ -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
Expand All @@ -74,11 +41,12 @@ def check_designators(what, where):
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:
Expand All @@ -90,108 +58,92 @@ def check_designators(what, where):
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')

from_name = list(connections[0].keys())[0]
via_name = list(connections[1].keys())[0]
to_name = list(connections[2].keys())[0]

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)')

from_pins = expand(connections[0][from_name])
via_pins = expand(connections[1][via_name])
to_pins = expand(connections[2][to_name])

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):
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')
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

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!

# 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!')

# 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')

# 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)

if file_out is not None:
harness.output(filename=file_out, fmt=('png', 'svg'), gen_bom=generate_bom, view=False)
Expand Down
29 changes: 29 additions & 0 deletions src/wireviz/wv_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit b479190

Please sign in to comment.