From 85eab22b13249c3c719cc355ccaedc330b89262e Mon Sep 17 00:00:00 2001 From: Daniel Rojas Date: Sun, 24 May 2020 10:51:38 +0200 Subject: [PATCH] Re-implement rendering using graphviz module --- src/example1.py | 15 ++-- src/example2.py | 36 +++++----- src/example3.py | 43 ++++++------ src/output/.gitignore | 3 + src/wireviz.py | 158 ++++++++++++++++++++++++++++++++++++------ 5 files changed, 186 insertions(+), 69 deletions(-) diff --git a/src/example1.py b/src/example1.py index 59463d3..d1aaf49 100644 --- a/src/example1.py +++ b/src/example1.py @@ -1,14 +1,15 @@ -from wireviz import Harness, Node, Cable +import wireviz -Harness = Harness() +h = wireviz.Harness() -Harness.add(Cable('W1', mm2=0.25, length=0.2, show_name=True, show_pinout=True, num_wires=3, color_code='DIN', shield=True)) -Harness.add(Node('X1', type='D-Sub', gender='female', pinout=('DCD','RX','TX','DTR','GND','DSR','RTS','CTS','RI'), ports_right=True)) -Harness.add(Node('X2', type='Molex KK 254', gender='female', pinout=('GND','RX','TX','NC','OUT','IN'), ports_left=True)) +h.add_cable('W1', mm2=0.25, length=0.2, show_name=True, show_pinout=True, num_wires=3, color_code='DIN', shield=True) +h.add_node('X1', type='D-Sub', gender='female', pinout=('DCD','RX','TX','DTR','GND','DSR','RTS','CTS','RI'), ports_right=True) +h.add_node('X2', type='Molex KK 254', gender='female', pinout=('GND','RX','TX','NC','OUT','IN'), ports_left=True) # Option 1: define wires and shield in one line -Harness.objects['W1'].connect('X1',(5,2,3,5),(1,2,3,'s'),'X2',(1,3,2,None)) +h.connect('W1','X1',(5,2,3,5),(1,2,3,'s'),'X2',(1,3,2,None)) +h.loop('X2', 5, 6) # Option 2: define wires and shield separately # Harness.objects['W1'].connect('X1',(5,2,3),'auto','X2',(1,3,2)) # wires # Harness.objects['W1'].connect('X1',(5,),('s',),'X2',(None,)) # shield -Harness.graphviz() +h.output('output/output', format='png', view=False) diff --git a/src/example2.py b/src/example2.py index e972fdb..45e0b79 100644 --- a/src/example2.py +++ b/src/example2.py @@ -1,31 +1,31 @@ -from wireviz import Harness, Node, Cable +import wireviz + +h = wireviz.Harness() # shortcuts for use during harness creation PINOUT_I2C = ('GND','+5V','SCL','SDA') COLORS_I2C = ('BK', 'RD', 'YE', 'GN') PINOUT_SPI_DATAONLY = ('MISO','MOSI','SCK') -Harness = Harness() - -Harness.add(Node('X1',type='Molex KK 254', gender='female', pinout=('GND', +h.add_node('X1',type='Molex KK 254', gender='female', pinout=('GND', '+5V', 'SCL', 'SDA', 'MISO', 'MOSI', 'SCK', - 'N/C'), ports_right=True)) -Harness.add(Node('X2', type='Molex KK 254', gender='female', pinout=PINOUT_I2C, ports_left=True)) -Harness.add(Node('X3', type='Molex KK 254', gender='female', pinout=PINOUT_I2C, ports_left=True)) -Harness.add(Node('X4', type='Molex KK 254', gender='female', pinout=('GND','+12V')+PINOUT_SPI_DATAONLY, ports_left=True)) -Harness.add(Node('X5', type='Molex Micro-Fit', gender='male', pinout=('GND','+12V'), ports_right=True)) -Harness.add(Cable('W1', mm2=0.14, show_equiv=True, length=0.2, colors=COLORS_I2C)) -Harness.add(Cable('W2', mm2=0.14, show_equiv=True, length=0.2, colors=COLORS_I2C)) -Harness.add(Cable('W3', mm2=0.14, show_equiv=True, length=0.2, colors=('BK','BU','OG','VT'))) -Harness.add(Cable('W4', mm2=0.5, show_equiv=True, length=0.35, colors=('BK','RD'))) -Harness.objects['W1'].connect('X1',(1,2,3,4),'auto','X2','auto') -Harness.objects['W2'].connect('X1',(1,2,3,4),'auto','X3','auto') -Harness.objects['W3'].connect('X1',(1,5,6,7),'auto','X4',(1,3,4,5)) -Harness.objects['W4'].connect_all_straight('X5','X4') + 'N/C'), ports_right=True) +h.add_node('X2', type='Molex KK 254', gender='female', pinout=PINOUT_I2C, ports_left=True) +h.add_node('X3', type='Molex KK 254', gender='female', pinout=PINOUT_I2C, ports_left=True) +h.add_node('X4', type='Molex KK 254', gender='female', pinout=('GND','+12V')+PINOUT_SPI_DATAONLY, ports_left=True) +h.add_node('X5', type='Molex Micro-Fit', gender='male', pinout=('GND','+12V'), ports_right=True) +h.add_cable('W1', mm2=0.14, show_equiv=True, length=0.2, colors=COLORS_I2C, show_name=False) +h.add_cable('W2', mm2=0.14, show_equiv=True, length=0.2, colors=COLORS_I2C, show_name=False) +h.add_cable('W3', mm2=0.14, show_equiv=True, length=0.2, colors=('BK','BU','OG','VT'), show_name=False) +h.add_cable('W4', mm2=0.5, show_equiv=True, length=0.35, colors=('BK','RD'), show_name=False) +h.connect('W1','X1',(1,2,3,4),'auto','X2','auto') +h.connect('W2','X1',(1,2,3,4),'auto','X3','auto') +h.connect('W3','X1',(1,5,6,7),'auto','X4',(1,3,4,5)) +h.connect_all_straight('W4','X5','X4') -Harness.graphviz() +h.output('output/output', format='png', view=False) diff --git a/src/example3.py b/src/example3.py index 25af45e..f0bc41e 100644 --- a/src/example3.py +++ b/src/example3.py @@ -1,29 +1,26 @@ -from wireviz import Harness, Node, Cable +import wireviz -Harness = Harness() -Harness.color_mode = 'full' +h = wireviz.Harness() +h.color_mode = 'full' -Harness.add(Node('X1', num_pins=10, ports_right=True)) -Harness.add(Node('X2', num_pins=10, ports_left=True)) -Harness.add(Cable('W1', num_wires=10, color_code='IEC')) -Harness.objects['W1'].connect_all_straight('X1','X2') +h.add_node('X1', num_pins=10, ports_right=True) +h.add_node('X2', num_pins=10, ports_left=True) +h.add_cable('W1', num_wires=10, color_code='IEC') +h.connect_all_straight('W1','X1','X2') -Harness.add(Node('X3', num_pins=20, ports_right=True)) -Harness.add(Node('X4', num_pins=20, ports_left=True)) -Harness.add(Cable('W2', num_wires=20, color_code='DIN')) -Harness.objects['W2'].connect_all_straight('X3','X4') +h.add_node('X3', num_pins=20, ports_right=True) +h.add_node('X4', num_pins=20, ports_left=True) +h.add_cable('W2', num_wires=20, color_code='DIN') +h.connect_all_straight('W2','X3','X4') -Harness.add(Node('X5', num_pins=20, ports_right=True)) -Harness.add(Node('X6', num_pins=20, ports_left=True)) -Harness.add(Cable('W3', num_wires=20, colors=('RD','YE','BU'))) -Harness.objects['W3'].connect_all_straight('X5','X6') +h.add_node('X5', num_pins=20, ports_right=True) +h.add_node('X6', num_pins=20, ports_left=True) +h.add_cable('W3', num_wires=20, colors=('RD','YE','BU')) +h.connect_all_straight('W3','X5','X6') -Harness.add(Node('X7', num_pins=6, ports_right=True)) -Harness.add(Node('X8', num_pins=6, ports_left=True)) -Harness.add(Cable('W4', num_wires=6, length=1, mm2=1)) -Harness.objects['W4'].connect_all_straight('X7','X8') +h.add_node('X7', num_pins=6, ports_right=True) +h.add_node('X8', num_pins=6, ports_left=True) +h.add_cable('W4', num_wires=6, length=1, mm2=1) +h.connect_all_straight('W4','X7','X8') - - - -Harness.graphviz() +h.output('output/output', format='png', view=False) diff --git a/src/output/.gitignore b/src/output/.gitignore index 95d5a41..e1a5a94 100644 --- a/src/output/.gitignore +++ b/src/output/.gitignore @@ -1 +1,4 @@ +output output.dot +output.pdf +output.png diff --git a/src/wireviz.py b/src/wireviz.py index 7da11d8..7d93ee0 100644 --- a/src/wireviz.py +++ b/src/wireviz.py @@ -1,5 +1,6 @@ +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'], +COLOR_CODES = {'DIN': ['WH','BN','GN','YE','GY','PK','BU','RD','BK','VT'], # ,'GYPK','RDBU','WHGN','BNGN','WHYE','YEBN','WHGY','GYBN','WHPK','PKBN'], 'IEC': ['BN','RD','OG','YE','GN','BU','VT','GY','WH','BK'], 'BW': ['BK','WH']} @@ -53,31 +54,107 @@ class Harness: def __init__(self): self.color_mode = 'SHORT' - self.objects = {} + self.objects = {} # DELETE + self.nodes = {} + self.cables = {} - def add(self, object): - self.objects[object.name] = object - self.objects[object.name].color_mode = self.color_mode + # def add(self, object): + # self.objects[object.name] = object + # self.objects[object.name].color_mode = self.color_mode - def graphviz(self, print_to_screen=False): - with open('output/output.dot','w') as f: - with open('input/header.dot','r') as infile: - for line in infile: - f.write(line) - f.write('\n\n') + def add_node(self, name, type=None, gender=None, show_name=True, num_pins=None, pinout=None, ports_left=False, ports_right=False): + self.nodes[name] = Node(name, type, gender, show_name, num_pins, pinout, ports_left, ports_right) - for o in self.objects: - f.write(self.objects[o].graphviz() + '\n') + def add_cable(self, name, mm2=None, awg=None, show_equiv=False, length=0, show_name=False, show_pinout=False, num_wires=None, colors=None, color_code=None, shield=False): + self.cables[name] = Cable(name, mm2, awg, show_equiv, length, show_name, show_pinout, num_wires, colors, color_code, shield) - f.write('\n\n') - with open('input/footer.dot','r') as infile: - for line in infile: - f.write(line) + def loop(self, node_name, from_pin, to_pin, side=None): + self.nodes[node_name].loop(from_pin, to_pin, side) - if print_to_screen == True: - with open('output/output.dot','r') as f: - for line in f: - print(line) + def connect(self, cable_name, from_name, from_pin, via, to_name, to_pin): + self.cables[cable_name].connect(from_name, from_pin, via, to_name, to_pin) + + def connect_all_straight(self, cable_name, from_name, to_name): + self.cables[cable_name].connect_all_straight(from_name, to_name) + + def create_graph(self): + dot = Graph() + font = 'arial' + dot.attr('graph', rankdir='LR', ranksep='2', bgcolor='transparent', fontname=font) + dot.attr('node', shape='record', style='rounded,filled', fillcolor='white', fontname=font) + dot.attr('edge', style='bold', fontname=font) + + for k in self.nodes: + n = self.nodes[k] + # a = attributes + a = [n.type, n.gender, '{}-pin'.format(len(n.pinout))] + # p = pinout + p = [[],[],[]] + p[1] = list(n.pinout) + for i,x in enumerate(n.pinout, 1): + if n.ports_left == True: + p[0].append('{portno}'.format(portno=i)) + if n.ports_right == True: + p[2].append('{portno}'.format(portno=i)) + # l = label + l = [n.name if n.show_name == True else '', a, p] + dot.node(k, label=nested(l)) + + for x in n.loops: + dot.edge('{name}:p{port_from}:{loop_side}'.format(name=n.name, port_from=x[0], port_to=x[1], loop_side=x[2]), + '{name}:p{port_to}:{loop_side}'.format(name=n.name, port_from=x[0], port_to=x[1], loop_side=x[2])) + + for k in self.cables: + c = self.cables[k] + # a = attributes + a = ['{}x'.format(len(c.colors)), + '{} mm\u00B2{}'.format(c.mm2, ' ({} AWG)'.format(awg_equiv(c.mm2)) if c.show_equiv == True else ''), + c.awg, + '+ S' if c.shield == True else '', + '{} m'.format(c.length)] + # p = pinout + p = [[],[],[]] + for i,x in enumerate(c.colors,1): + if c.show_pinout: + p[0].append('{wireno}'.format(wireno=i)) + p[1].append('{wirecolor}'.format(wirecolor=translate_color(x, self.color_mode))) + p[2].append('{wireno}'.format(wireno=i)) + else: + p[1].append('{wirecolor}'.format(wireno=i,wirecolor=translate_color(x, self.color_mode))) + if c.shield == True: + if c.show_pinout: + p[0].append('') + p[1].append('Shield') + p[2].append('') + else: + p[1].append('Shield') + # l = label + l = [c.name if c.show_name == True else '', a, p] + dot.node(k, label=nested(l)) + + # connections + for x in c.connections: + if isinstance(x[2], int): # check if it's an actual wire and not a shield + search_color = c.colors[x[2]-1] + if search_color in color_hex: + dot.attr('edge',color='#000000:{wire_color}:#000000'.format(wire_color=color_hex[search_color])) + else: # color name not found + dot.attr('edge',color='#000000') + else: # it's a shield connection + dot.attr('edge',color='#000000') + if x[1] is not None: # connect to left + dot.edge('{from_name}:p{from_port}'.format(from_name=x[0],from_port=x[1]), + '{via_name}:w{via_wire}{via_subport}'.format(via_name=c.name, via_wire=x[2], via_subport='i' if c.show_pinout == True else '')) + if x[4] is not None: # connect to right + dot.edge('{via_name}:w{via_wire}{via_subport}'.format(via_name=c.name, via_wire=x[2], via_subport='o' if c.show_pinout == True else ''), + '{to_name}:p{to_port}'.format(to_name=x[3], to_port=x[4])) + + return dot + + def output(self, filename, format='pdf', view=True): + d = self.create_graph() + d.format = format + d.render(filename, view=view) class Node: @@ -112,6 +189,7 @@ class Node: loop_side = side self.loops.append((from_pin, to_pin, loop_side)) + # TODO: remove this function def graphviz(self): s = '' # print header @@ -224,6 +302,7 @@ class Cable: def connect_all_straight(self, from_name, to_name): self.connect(from_name, 'auto', 'auto', to_name, 'auto') + # TODO: remove this function def graphviz(self): s = '' # print header @@ -329,6 +408,43 @@ class Cable: return s +def nested(input): + l = [] + for x in input: + if isinstance(x, list): + if len(x) > 0: + l.append('{' + nested(x) + '}') + else: + if x is not None: + if x != '': + l.append(str(x)) + s = '|'.join(l) + return s + +def translate_color(input, color_mode): + if input == '': + output = '' + else: + if color_mode == 'full': + output = color_full[input].lower() + elif color_mode == 'FULL': + output = color_hex[input].upper() + elif color_mode == 'hex': + output = color_hex[input].lower() + elif color_mode == 'HEX': + output = color_hex[input].upper() + elif color_mode == 'ger': + output = color_ger[input].lower() + elif color_mode == 'GER': + output = color_ger[input].upper() + elif color_mode == 'short': + output = input.lower() + elif color_mode == 'SHORT': + output = input.upper() + else: + raise Exception('Unknown color mode') + return output + def awg_equiv(mm2): awg_equiv_table = { '0.09': 28,