diff --git a/src/batch.py b/src/batch.py index aee9322..e6c3686 100644 --- a/src/batch.py +++ b/src/batch.py @@ -1,7 +1,7 @@ -import yaml2wireviz +import wireviz # TODO: make examples (progressively more complex), batch process all of them -yaml2wireviz.parse('../examples/example1.yml') -yaml2wireviz.parse('../examples/example2.yml') -yaml2wireviz.parse('../examples/ferrules.yml') -yaml2wireviz.parse('../examples/bundles.yml') +wireviz.parse('../examples/example1.yml') +wireviz.parse('../examples/example2.yml') +wireviz.parse('../examples/ferrules.yml') +wireviz.parse('../examples/bundles.yml') diff --git a/src/wireviz.py b/src/wireviz.py old mode 100644 new mode 100755 index 63d803c..f9f03e6 --- a/src/wireviz.py +++ b/src/wireviz.py @@ -1,5 +1,8 @@ +#!/usr/bin/env python3 +import os from dataclasses import dataclass, field from typing import Any, List +import yaml from graphviz import Graph COLOR_CODES = {'DIN': ['WH','BN','GN','YE','GY','PK','BU','RD','BK','VT'], # ,'GYPK','RDBU','WHGN','BNGN','WHYE','YEBN','WHGY','GYBN','WHPK','PKBN'], @@ -385,3 +388,194 @@ def awg_equiv(mm2): return awg_equiv_table[k] else: return None + +def parse(file_in, file_out=None): + + file_in = os.path.abspath(file_in) + if not file_out: + file_out = file_in + pre, ext = os.path.splitext(file_out) + file_out = pre # extension will be added by graphviz output function + file_out = os.path.abspath(file_out) + + with open(file_in, 'r') as stream: + try: + input = yaml.safe_load(stream) + except yaml.YAMLError as exc: + print(exc) + + def expand(input): + # input 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(input, list): + input = [input,] + for e in input: + 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: + x = e + output.append(x) + return output + + def check_designators(what, where): + for i, x in enumerate(what): + # print('Looking for {} in {}'.format(x,where[i])) + if x not in input[where[i]]: + return False + return True + + h = Harness() + + # add items + sections = ['nodes','wires','ferrules','connections'] + types = [dict, dict, dict, list] + for sec, ty in zip(sections, types): + if sec in input and type(input[sec]) == ty: + if len(input[sec]) > 0: + if ty == dict: + for k, o in input[sec].items(): + if sec == 'nodes': + h.add_node(name=k, **o) + elif sec == 'wires': + h.add_cable(name=k, **o) + elif sec == 'ferrules': + pass + else: + print('{} section empty'.format(sec)) + else: + print('No {} section found'.format(sec)) + if ty == dict: + input[sec] = {} + elif ty == list: + input[sec] = [] + + # add connections + ferrule_counter = 0 + for con in input['connections']: + if len(con) == 3: # format: connector -- wire -- conector + + for c in con: + if len(list(c.keys())) != 1: # check that each entry in con has only one key, which is the designator + raise Exception('Too many keys') + + from_name = list(con[0].keys())[0] + via_name = list(con[1].keys())[0] + to_name = list(con[2].keys())[0] + + if not check_designators([from_name,via_name,to_name],('nodes','wires','nodes')): + raise Exception('Bad connection definition (3)') + + from_pins = expand(con[0][from_name]) + via_pins = expand(con[1][via_name]) + to_pins = expand(con[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): + h.connect(from_name, from_pin, via_name, via_pin, to_name, to_pin) + + elif len(con) == 2: + + for c in con: + if type(c) is dict: + if len(list(c.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 wires + if type(con[0]) == str: + name = con[0] + con[0] = {} + con[0][name] = name + if type(con[1]) == str: + name = con[1] + con[1] = {} + con[1][name] = name + + from_name = list(con[0].keys())[0] + to_name = list(con[1].keys())[0] + + n_w = check_designators([from_name, to_name],('nodes','wires')) + w_n = check_designators([from_name, to_name],('wires','nodes')) + n_n = check_designators([from_name, to_name],('nodes','nodes')) + + + f_w = check_designators([from_name, to_name],('ferrules','wires')) + w_f = check_designators([from_name, to_name],('wires','ferrules')) + + if not n_w and not w_n and not n_n and not f_w and not w_f: + raise Exception('Wrong designators') + + from_pins = expand(con[0][from_name]) + to_pins = expand(con[1][to_name]) + + if n_w or w_n or n_n: + if len(from_pins) != len(to_pins): + raise Exception('List length mismatch') + + if n_w or w_n: + for (from_pin, to_pin) in zip(from_pins, to_pins): + if n_w: + h.connect(from_name, from_pin, to_name, to_pin, None, None) + else: # w_n + h.connect(None, None, from_name, from_pin, to_name, to_pin) + elif n_n: + con_name = list(con[0].keys())[0] + from_pins = expand(con[0][from_name]) + to_pins = expand(con[1][to_name]) + + for (from_pin, to_pin) in zip(from_pins, to_pins): + h.loop(con_name, from_pin, to_pin) + if f_w or w_f: + from_pins = expand(con[0][from_name]) + to_pins = expand(con[1][to_name]) + + if f_w: + ferrule_name = from_name + wire_name = to_name + wire_pins = to_pins + else: + ferrule_name = to_name + wire_name = from_name + wire_pins = from_pins + + ferrule_params = input['ferrules'][ferrule_name] + for wire_pin in wire_pins: + ferrule_counter = ferrule_counter + 1 + ferrule_id = 'F{}'.format(ferrule_counter) + h.add_node(ferrule_id, **ferrule_params) + + if f_w: + h.connect(ferrule_id, 1, wire_name, wire_pin, None, None) + else: + h.connect(None, None, wire_name, wire_pin, ferrule_id, 1) + + + else: + raise Exception('Wrong number of connection parameters') + + h.output(filename=file_out, format=('png','svg'), view=False) + +if __name__ == '__main__': + import argparse + ap = argparse.ArgumentParser() + ap.add_argument('file_input', nargs='?', default='_test/test.yml') + ap.add_argument('file_output', nargs='?', default=None) + args = ap.parse_args() + + parse(args.file_input, args.file_output) diff --git a/src/yaml2wireviz.py b/src/yaml2wireviz.py deleted file mode 100755 index 2cab55f..0000000 --- a/src/yaml2wireviz.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/env python3 -import os -import yaml -import wireviz - -def expand(input): - # input 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(input, list): - input = [input,] - for e in input: - 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: - x = e - output.append(x) - return output - -def _parse(file_in, file_out): - - def check_designators(what, where): - for i, x in enumerate(what): - # print('Looking for {} in {}'.format(x,where[i])) - if x not in input[where[i]]: - return False - return True - - with open(file_in, 'r') as stream: - try: - input = yaml.safe_load(stream) - except yaml.YAMLError as exc: - print(exc) - - h = wireviz.Harness() - - # add items - sections = ['nodes','wires','ferrules','connections'] - types = [dict, dict, dict, list] - for sec, ty in zip(sections, types): - if sec in input and type(input[sec]) == ty: - if len(input[sec]) > 0: - if ty == dict: - for k, o in input[sec].items(): - if sec == 'nodes': - h.add_node(name=k, **o) - elif sec == 'wires': - h.add_cable(name=k, **o) - elif sec == 'ferrules': - pass - else: - print('{} section empty'.format(sec)) - else: - print('No {} section found'.format(sec)) - if ty == dict: - input[sec] = {} - elif ty == list: - input[sec] = [] - - # add connections - ferrule_counter = 0 - for con in input['connections']: - if len(con) == 3: # format: connector -- wire -- conector - - for c in con: - if len(list(c.keys())) != 1: # check that each entry in con has only one key, which is the designator - raise Exception('Too many keys') - - from_name = list(con[0].keys())[0] - via_name = list(con[1].keys())[0] - to_name = list(con[2].keys())[0] - - if not check_designators([from_name,via_name,to_name],('nodes','wires','nodes')): - raise Exception('Bad connection definition (3)') - - from_pins = expand(con[0][from_name]) - via_pins = expand(con[1][via_name]) - to_pins = expand(con[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): - h.connect(from_name, from_pin, via_name, via_pin, to_name, to_pin) - - elif len(con) == 2: - - for c in con: - if type(c) is dict: - if len(list(c.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 wires - if type(con[0]) == str: - name = con[0] - con[0] = {} - con[0][name] = name - if type(con[1]) == str: - name = con[1] - con[1] = {} - con[1][name] = name - - from_name = list(con[0].keys())[0] - to_name = list(con[1].keys())[0] - - n_w = check_designators([from_name, to_name],('nodes','wires')) - w_n = check_designators([from_name, to_name],('wires','nodes')) - n_n = check_designators([from_name, to_name],('nodes','nodes')) - - - f_w = check_designators([from_name, to_name],('ferrules','wires')) - w_f = check_designators([from_name, to_name],('wires','ferrules')) - - if not n_w and not w_n and not n_n and not f_w and not w_f: - raise Exception('Wrong designators') - - from_pins = expand(con[0][from_name]) - to_pins = expand(con[1][to_name]) - - if n_w or w_n or n_n: - if len(from_pins) != len(to_pins): - raise Exception('List length mismatch') - - if n_w or w_n: - for (from_pin, to_pin) in zip(from_pins, to_pins): - if n_w: - h.connect(from_name, from_pin, to_name, to_pin, None, None) - else: # w_n - h.connect(None, None, from_name, from_pin, to_name, to_pin) - elif n_n: - con_name = list(con[0].keys())[0] - from_pins = expand(con[0][from_name]) - to_pins = expand(con[1][to_name]) - - for (from_pin, to_pin) in zip(from_pins, to_pins): - h.loop(con_name, from_pin, to_pin) - if f_w or w_f: - from_pins = expand(con[0][from_name]) - to_pins = expand(con[1][to_name]) - - if f_w: - ferrule_name = from_name - wire_name = to_name - wire_pins = to_pins - else: - ferrule_name = to_name - wire_name = from_name - wire_pins = from_pins - - ferrule_params = input['ferrules'][ferrule_name] - for wire_pin in wire_pins: - ferrule_counter = ferrule_counter + 1 - ferrule_id = 'F{}'.format(ferrule_counter) - h.add_node(ferrule_id, **ferrule_params) - - if f_w: - h.connect(ferrule_id, 1, wire_name, wire_pin, None, None) - else: - h.connect(None, None, wire_name, wire_pin, ferrule_id, 1) - - - else: - raise Exception('Wrong number of connection parameters') - - h.output(filename=file_out, format=('png','svg'), view=False) - -def parse(filename_in, filename_out=None): - fin = os.path.abspath(filename_in) - if filename_out: - fout = filename_out - else: - fout = fin - pre, ext = os.path.splitext(fout) - fout = pre # extension will be added by graphviz output function - fout = os.path.abspath(fout) - - _parse(fin, fout) - -if __name__ == '__main__': - import argparse - ap = argparse.ArgumentParser() - ap.add_argument('file_input', nargs='?', default='_test/test.yml') - ap.add_argument('file_output', nargs='?', default=None) - args = ap.parse_args() - - parse(args.file_input, args.file_output)