main
noah Add datasheet, figures, hero card, provenance, verification log and README a043b0e 10h ago
#!/usr/bin/env python3
"""Derive the wiki page's KiCad symbol and footprint for Wurth Elektronik 61300411121 from the project
library files the board uses, changing only identity text (names, model path, generic description, order
numbers). Geometry, pads, drills, graphics and pins are copied unchanged; compare_cad() proves it.

    make_cad.py <board .kicad_sym> <board .kicad_mod> <out dir>
"""
import hashlib, json, re, sys

MPN = "61300411121"
FIELDS = [  # added after Manufacturer, in this order, hidden
    ("Mouser", "710-61300411121"),
    ("DigiKey", "732-5317-ND"),
    ("LCSC", "C5184785"),
    ("Adom Wiki", "https://wiki.adom.inc/adom/61300411121"),
]
DESC = ("Wurth Elektronik WR-PHD 61300411121 pin header, 2.54 mm pitch, 1x04, THT vertical, "
        "gold-plated 0.64 mm square pins, 3 A, 250 VAC")


def block(text, head):
    i = text.find(head)
    if i < 0:
        raise SystemExit(f"not found: {head}")
    d = 0
    for j in range(i, len(text)):
        if text[j] == "(":
            d += 1
        elif text[j] == ")":
            d -= 1
            if d == 0:
                return text[i:j + 1]
    raise SystemExit("unbalanced")


def prop(name, value, indent="\t\t"):
    return (f'{indent}(property "{name}" "{value}"\n'
            f'{indent}\t(at 0 0 0)\n{indent}\t(show_name no)\n{indent}\t(do_not_autoplace no)\n'
            f'{indent}\t(hide yes)\n{indent}\t(effects\n{indent}\t\t(font\n{indent}\t\t\t(size 1.27 1.27)\n'
            f'{indent}\t\t)\n{indent}\t)\n{indent})\n')


def make_symbol(src):
    s = open(src).read()
    sym = block(s, f'(symbol "{MPN}"')
    sym = sym.replace('(property "Footprint" "fc_local:WE_61300411121_1x04_P2.54mm_Vertical"',
                      f'(property "Footprint" "{MPN}:{MPN}"', 1)
    sym, n = re.subn(r'\(property "Description" "[^"]*"', f'(property "Description" "{DESC}"', sym, count=1)
    assert n == 1
    # insert the order fields after the Manufacturer property block
    m = re.search(r'\t\t\(property "Manufacturer" "[^"]*"\n(?:.*\n)*?\t\t\)\n', "\t" + sym)
    assert m, "Manufacturer property not found"
    ins = "".join(prop(k, v) for k, v in FIELDS)
    sym = ("\t" + sym)[:m.end()] + ins + ("\t" + sym)[m.end():]
    head = ('(kicad_symbol_lib\n\t(version 20251024)\n\t(generator "kicad_symbol_editor")\n'
            '\t(generator_version "10.0")\n')
    return head + sym + "\n)\n"


def make_footprint(src):
    f = open(src).read()
    f = f.replace('(footprint "WE_61300411121_1x04_P2.54mm_Vertical"', f'(footprint "{MPN}"', 1)
    f = f.replace('(model "${KIPRJMOD}/lib/fc_local.3dshapes/WE_61300411121.step"',
                  '(model "${KIPRJMOD}/' + MPN + '.step"', 1)
    return f


def geometry(text):
    """Everything that is not identity text: pads, graphics, pins, layers."""
    out = []
    for line in text.splitlines():
        t = line.strip()
        if re.match(r'\((footprint|symbol|property|descr|model|kicad_symbol_lib|version|generator)\b', t):
            out.append("<id>")
            continue
        out.append(t)
    return out


def main():
    sym_src, fp_src, out = sys.argv[1:4]
    sym = make_symbol(sym_src)
    fp = make_footprint(fp_src)
    open(f"{out}/{MPN}.kicad_sym", "w").write(sym)
    open(f"{out}/{MPN}.kicad_mod", "w").write(fp)
    # pads and graphics unchanged? compare the footprint line by line outside identity lines
    fsrc = open(fp_src).read()
    same_fp = [a for a in zip(fsrc.splitlines(), fp.splitlines()) if a[0] != a[1]]
    print(json.dumps({
        "footprint_lines_changed": [{"board": a, "page": b} for a, b in same_fp],
        "symbol_pins": re.findall(r'\(pin (\w+) line\s*\n\s*\(at ([^)]*)\)\s*\n\s*\(length ([^)]*)\)\s*\n\s*\(name "([^"]*)"(?:.|\n)*?\(number "([^"]*)"', sym),
        "sha256": {
            "board_symbol_lib": hashlib.sha256(open(sym_src, "rb").read()).hexdigest(),
            "board_footprint": hashlib.sha256(open(fp_src, "rb").read()).hexdigest(),
            "page_symbol": hashlib.sha256(sym.encode()).hexdigest(),
            "page_footprint": hashlib.sha256(fp.encode()).hexdigest(),
        },
    }, indent=1))


if __name__ == "__main__":
    main()