WireViz/src/wireviz/wireviz.py
Gabe R 82b173f2ce Refactor and clean up code (#39)
* Format all files using autopep8 to add basic PEP8 conformity.
* Add Exception types to bare excepts to prevent catching `ctrl+c`
* Remove some unnecessary assignment to dummy variables before returning
* Add `Optional` to type hints that can be `NoneType`
* Change a number of single-letter variables to more descriptive names
* Replace string.format() use with Python's f-strings, as they tends to be cleaner, and provide a performance boost.
  * One multiline string was left as string.format() as I do not believe f-strings support multiline
  * Some of the string.format() instances had unused/ignored arguments. I left them out of the f-strings, but I marked those cases with a comments that begins `# FIXME:`
* Rename variables that were shadowding python standard functions (specifically `format->fmt`, `input->inp`, `type->maintype`)
  * Some instances of `type` were not changed, as it breaks the yaml parsing. Needs to be looked into.
* Move classes in `wireviz.py` to two new files `DataClasses.py` and `Harness.py`.

Co-authored-by: Daniel Rojas <github@danielrojas.net>
2020-06-29 11:57:03 +02:00

246 lines
8.9 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import os
import sys
import yaml
if __name__ == '__main__':
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from wireviz.Harness import Harness
def parse(yaml_input, file_out=None, generate_bom=False):
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
sections = ['connectors', 'cables', 'ferrules', 'connections']
types = [dict, 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 ty == dict:
for key, o in yaml_data[sec].items():
if sec == 'connectors':
harness.add_connector(name=key, **o)
elif sec == 'cables':
harness.add_cable(name=key, **o)
elif sec == 'ferrules':
pass
else:
pass # section exists but is empty
else: # section does not exist, create empty section
if ty == dict:
yaml_data[sec] = {}
elif ty == list:
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')
harness.output(filename=file_out, fmt=('png', 'svg'), gen_bom=generate_bom, view=False)
def parse_file(yaml_file, file_out=None, generate_bom=False):
with open(yaml_file, 'r') as file:
yaml_input = file.read()
if not file_out:
fn, fext = os.path.splitext(yaml_file)
file_out = fn
file_out = os.path.abspath(file_out)
parse(yaml_input, file_out=file_out, generate_bom=generate_bom)
def parse_cmdline():
parser = argparse.ArgumentParser(
description='Generate cable and wiring harness documentation from YAML descriptions',
)
parser.add_argument('input_file', action='store', type=str, metavar='YAML_FILE')
parser.add_argument('-o', '--output_file', action='store', type=str, metavar='OUTPUT')
parser.add_argument('--generate-bom', action='store_true', default=True)
parser.add_argument('--prepend-file', action='store', type=str, metavar='YAML_FILE')
return parser.parse_args()
def main():
args = parse_cmdline()
if not os.path.exists(args.input_file):
print(f'Error: input file {args.input_file} inaccessible or does not exist, check path')
sys.exit(1)
with open(args.input_file) as fh:
yaml_input = fh.read()
if args.prepend_file:
if not os.path.exists(args.prepend_file):
print(f'Error: prepend input file {args.prepend_file} inaccessible or does not exist, check path')
sys.exit(1)
with open(args.prepend_file) as fh:
prepend = fh.read()
yaml_input = prepend + yaml_input
if not args.output_file:
file_out = args.input_file
pre, _ = os.path.splitext(file_out)
file_out = pre # extension will be added by graphviz output function
else:
file_out = args.output_file
file_out = os.path.abspath(file_out)
parse(yaml_input, file_out=file_out, generate_bom=args.generate_bom)
if __name__ == '__main__':
main()