From a7467e17cad39a8df3f0e9767ebbb34a837fa04b Mon Sep 17 00:00:00 2001 From: Andrei Date: Sat, 13 Mar 2021 19:45:30 +0200 Subject: [PATCH] A script to convert Kicad modules to AD --- bin/convert_kicad_module_footprint_to_pcad.py | 213 ++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 bin/convert_kicad_module_footprint_to_pcad.py diff --git a/bin/convert_kicad_module_footprint_to_pcad.py b/bin/convert_kicad_module_footprint_to_pcad.py new file mode 100644 index 0000000..f664d04 --- /dev/null +++ b/bin/convert_kicad_module_footprint_to_pcad.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python +############################################################################################ +# Hellen-One: A script to convert a Kicad module footprint (*.kicad_mod) to PCAD .LIA. +# Needed by AD projects to use Kicad-created modules. +# (c) andreika +############################################################################################ + +import os, sys, errno +import csv, re +import glob, shutil + +#if len(sys.argv) < 4: +# print "Error! Please specify the module project folder, module name and rev." +# sys.exit(1) +#project_base = sys.argv[1] +name = "wbo" # sys.argv[2] +#rev = sys.argv[3] + +#base_path = project_base +#src_path = base_path +#dst_path = "modules" + +mod_name = "MOD_HELLEN_" + name.upper() + +pat_pad = re.compile(r'^\s*\(pad \"([^\"]+)\"\s+(thru_hole|smd)\s+(circle|oval|rect|roundrect)\s+\(at\s+([0-9\.\-]+)\s+([0-9\.\-]+)(\s+([0-9\.\-]+))?\)(\s+\(locked\))?\s+\(size\s+([0-9\.\-]+)\s+([0-9\.\-]+)\)(\s+\(drill\s+([0-9\.\-]+)\))?\s+\(layers\s+\"?(.*?)\.Cu') +pat_line_rect = re.compile(r'^\s*\(fp_(line|rect)\s+\(start\s+([0-9\.\-]+)\s+([0-9\.\-]+)\)\s+\(end\s+([0-9\.\-]+)\s+([0-9\.\-]+)\)\s+\(layer\s+\"([^\"]+)\"\)\s+\(width\s+([0-9\.\-]+)\)(\s+\(fill\s+([a-z]+)\))?') +pat_zone = re.compile(r'^\s*\(zone\s+\(net 0\)\s+\(net_name\s+\"\"\)\s+\(layers\s+\"?(.*?)\.Cu\)') +pat_xy = re.compile(r'^\s*\(xy\s+([0-9\.\-]+)\s+([0-9\.\-]+)\)') + +shapeLut = {"circle": "Oval", "roundrect": "RndRect", "oval": "Oval", "rect": "Rect" } + +# used by add_zone_line() +class Zone: + in_zone = False + zone_first = None + zone_prev = None + layer = "" + lines = None + + def add_line(self, zone_cur): + if self.zone_prev: + print ("* adding line (" + self.zone_prev[0] + "," + self.zone_prev[1] + " - " + zone_cur[0] + "," + zone_cur[1] + ")") + if (self.layer == 'F' or self.layer == '*'): + self.lines.append([ self.zone_prev[0], self.zone_prev[1], zone_cur[0], zone_cur[1], "F.Zone", "0.1" ]) + if (self.layer == 'B' or self.layer == '*'): + self.lines.append([ self.zone_prev[0], self.zone_prev[1], zone_cur[0], zone_cur[1], "B.Zone", "0.1" ]) + else: + self.zone_first = zone_cur + self.zone_prev = zone_cur + + def open(self, layer, lines): + self.in_zone = True + self.layer = layer + self.lines = lines + + def close(self): + if self.zone_first: + self.add_line(self.zone_first) + +def process_pcb(src_name, dst_name): + with open(src_name, 'rt') as src_f, open(dst_name, 'w') as dst_f: + dst_f.write("ACCEL_ASCII \"" + name + ".LIA\"\n\n") + dst_f.write("(asciiHeader\n (asciiVersion 3 0)\n)\n\n") + + pad_lib = [] + pads = [] + lines = [] + pad_names = {} + + zone = Zone() + + for line in src_f: + zon = pat_zone.match(line) + if zon: + zone.open(zon.group(1), lines) + print ("* Zone detected! Layer=" + zone.layer) + + pad = pat_pad.match(line) + if pad: + pad_name = pad.group(1) + pad_type = pad.group(2) + pad_shape = pad.group(3) + pad_x = pad.group(4) + pad_y = str(-float(pad.group(5))) # Kicad->AD: reverse Y-coord + pad_rot = pad.group(7) or "0" + pad_w = pad.group(9) + pad_h = pad.group(10) + pad_hole = pad.group(12) or "0" + pad_layers = pad.group(13) + print ("* pad detected!") + print (" name=" + pad_name) + print (" type=" + pad_type) + print (" shape=" + pad_shape) + print (" x=" + pad_x) + print (" y=" + pad_y) + print (" rot=" + pad_rot) + print (" w=" + pad_w) + print (" h=" + pad_h) + print (" hole=" + pad_hole) + print (" layers=" + pad_layers) + pad_lib_entry = [ pad_type, pad_shape, pad_w, pad_h, pad_hole, pad_layers ] + if pad_lib_entry not in pad_lib: + pad_entry_idx = len(pad_lib) + pad_lib.append(pad_lib_entry) + print ("* adding pad type #" + str(pad_entry_idx)) + else: + pad_entry_idx = pad_lib.index(pad_lib_entry) + if (pad_name in pad_names): + pad_unique_name = pad_name + "_" + str(pad_names[pad_name]) + pad_names[pad_name] += 1 + else: + pad_unique_name = pad_name + pad_names[pad_name] = 1 + pad_entry = [ pad_entry_idx, pad_name, pad_x, pad_y, pad_rot, pad_unique_name ] + pads.append(pad_entry) + lin = pat_line_rect.match(line) + if lin: + line_type = lin.group(1) + line_x1 = lin.group(2) + line_y1 = str(-float(lin.group(3))) # Kicad->AD: reverse Y-coord + line_x2 = lin.group(4) + line_y2 = str(-float(lin.group(5))) # Kicad->AD: reverse Y-coord + line_layer = lin.group(6) + line_width = lin.group(7) + line_fill = lin.group(9) + print ("* " + line_type + " detected!") + print (" (" + line_x1 + "," + line_y1 + " - " + line_x2 + "," + line_y2 + ")") + print (" * layer=" + line_layer) + print (" * width=" + line_width) + print (" * fill=" + (line_fill or "?")) + if (line_type == "rect"): + if (line_fill != "none"): + print ("Error! Filled rects currently not supported!") + sys.exit(1) + lines.append([ line_x1, line_y1, line_x2, line_y1, line_layer, line_width ]) + lines.append([ line_x2, line_y1, line_x2, line_y2, line_layer, line_width ]) + lines.append([ line_x2, line_y2, line_x1, line_y2, line_layer, line_width ]) + lines.append([ line_x1, line_y2, line_x1, line_y1, line_layer, line_width ]) + else: + lines.append([ line_x1, line_y1, line_x2, line_y2, line_layer, line_width ]) + # parse zones and add them as lines on special layers (P-CAD doesn't support zones natively) + if zone.in_zone: + xy = pat_xy.match(line) + if xy: + zone_point_x = xy.group(1) + zone_point_y = str(-float(xy.group(2))) # Kicad->AD: reverse Y-coord + zone.add_line([zone_point_x, zone_point_y]) + + # todo: add correct exit-zone conditions + zone.close() + + # add pad entries + dst_f.write("(library \"Library_1\"\n") + pi = 0 + for pe in pad_lib: + dst_f.write(" (padStyleDef \"PAD" + str(pi) + "\"\n") + dst_f.write(" (holeDiam " + pe[4] + "mm)\n") + dst_f.write(" (startRange 1)\n (endRange 2)\n") + if pe[0] == "smd": + if pe[5] == "B": + layers = ["layerNumRef 2"] + else: + layers = ["layerNumRef 1"] + else: + layers = ["layerNumRef 1", "layerNumRef 2", "layerType Signal"] + for l in layers: + dst_f.write(" (padShape (" + l + ") (padShapeType " + shapeLut[pe[1]] + ") (shapeWidth " + pe[2] + "mm) (shapeHeight " + pe[3] + "mm) )\n") + dst_f.write(" )\n") + pi += 1 + + # add pads + dst_f.write(" (patternDefExtended \"" + mod_name + "_1\"\n") + dst_f.write(" (originalName \"" + mod_name + "\")\n") + dst_f.write(" (patternGraphicsNameRef \"Primary\")\n\n (patternGraphicsDef\n") + dst_f.write(" (patternGraphicsNameDef \"Primary\")\n") + dst_f.write(" (multiLayer\n") + pi = 1 + for p in pads: + dst_f.write(" (pad (padNum " + str(pi) + ") (padStyleRef \"PAD" + str(p[0]) + "\") (pt " + p[2] + "mm " + p[3] + "mm) (rotation " + p[4] + ") (defaultPinDes \"" + p[1] + "\"))\n") + pi += 1 + dst_f.write(" )\n") + line_layers = [ [ "F.SilkS", 6 ], [ "B.SilkS", 7 ], [ "F.Zone", 12 ], [ "B.Zone", 13 ] ] + # output lines + for ll in line_layers: + dst_f.write(" (layerContents (layerNumRef " + str(ll[1]) + ")\n") + for l in lines: + if (l[4] == ll[0]): + dst_f.write(" (line (pt " + l[0] + "mm " + l[1] + "mm) (pt " + l[2] + "mm " + l[3] + "mm) (width " + l[5] + "mm) )\n") + dst_f.write(" )\n") + dst_f.write(" )\n )\n") + dst_f.write(" (compDef \"" + mod_name + "_1\"\n (originalName \"" + mod_name + "\")\n") + dst_f.write(" (compHeader\n (sourceLibrary \"\")\n (numPins " + str(len(pads)) + ")\n (numParts 1)\n (refDesPrefix \"\")\n") + dst_f.write(" )\n") + for p in pads: + dst_f.write(" (compPin \"" + p[5] + "\" (partNum 1) (symPinNum 1) (gateEq 0) (pinEq 0) )\n") + dst_f.write(" (attachedPattern (patternNum 1) (patternName \"" + mod_name + "\")\n") + dst_f.write(" (numPads " + str(len(pads)) + ")\n") + dst_f.write(" (padPinMap\n") + pi = 1 + for p in pads: + dst_f.write(" (padNum " + str(pi) + ") (compPinRef \"" + p[5] + "\")\n") + pi += 1 + dst_f.write(" )\n )\n )\n)\n") + + +################################################# + +print ("Processing the pcb file...") + +#process_pcb(src_path + "/hellen1-" + name + ".kicad_pcb", dst_path + "/" + name + "/" + rev + "/" + name + ".kicad_mod") +process_pcb("wbo.kicad_mod", "wbo.LIA") + +print ("Done!")