kicad/generate.py

168 lines
6.6 KiB
Python

import time
from datetime import datetime
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import Optional, Union
from zipfile import ZipFile
import numpy as np
import regex as re
from retry import retry
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import By
from selenium.webdriver.support.wait import WebDriverWait
dir_ = Path(__file__).parent
rng = np.random.default_rng()
PARTS = [
*[f"TSW-1{i:02d}-07-L-S" for i in range(1, 50)],
*[f"TSW-1{i:02d}-07-L-D" for i in range(1, 50)],
*[f"TSW-1{i:02d}-07-L-T" for i in range(1, 50)],
*[f"TSW-1{i:02d}-07-L-Q" for i in range(1, 50)],
*[f"TSW-2{i:02d}-07-L-S" for i in range(2, 25)],
*[f"TSW-2{i:02d}-07-L-D" for i in range(2, 25)],
*[f"TSW-2{i:02d}-07-L-T" for i in range(2, 25)],
*[f"TSW-2{i:02d}-07-L-Q" for i in range(2, 25)],
]
URL_STEP = "https://www.snapeda.com/parts/{0}/Samtec/embed/?ref=samtec"
# @retry(tries=10, delay=1)
def download_step_samtec(part: str):
print(part)
with TemporaryDirectory(prefix=str(dir_ / "tmp") + "/") as tmp:
tmp = Path(tmp)
chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option(
"prefs",
{"download.default_directory": str(tmp)},
)
chrome_options.add_argument("--headless")
with webdriver.Chrome(
options=chrome_options,
) as browser:
browser.get(URL_STEP.format(part.upper()))
wait = WebDriverWait(browser, 30)
wait.until(
EC.element_to_be_clickable(browser.find_element(by=By.ID, value="download_traceparts_3d_model"))
).click()
wait.until(
EC.element_to_be_clickable(
browser.find_element(
by=By.ID,
value="samtec-checkbox-3d-modal-download-individual-btn",
)
)
).click()
# wait for download to finish
while len(list((Path(tmp).glob("*.zip")))) == 0:
time.sleep(0.1)
# print(list((Path(tmp).glob("*.zip"))))
# unzip
with ZipFile(list((Path(tmp).glob("*.zip")))[0]) as zip:
zip.extractall(path=str(tmp))
# move
filename = f"{part.upper()}.stp"
# if filename not in list(Path(tmp).glob("*.stp")):
# raise ValueError(
# f"Name mismatch: {filename} does not exist. Found {[f.name for f in list(Path(tmp).glob('*.stp'))]}"
# )
(Path(tmp) / filename).rename(dir_ / "samtec.pretty" / "3dshapes" / filename)
now = datetime.now()
INCH = 25.4
SILK_WIDTH = 0.007 * INCH
def footprint_samtec(part: str, lib: Optional[Union[Path, str]] = None) -> str:
if part[:4].upper() == "TSW-":
# pinheader
match = re.match(
r"(H?)TSW-([12])(\d\d)-(\d\d)-([FLGT])-([SDTQ])(-RA)?(-RE)?(-NA)?(-LL)?(-LC)?(-LA)?(-(\d\d\d))?",
part.upper(),
)
high_temp = match[1] != ""
spacing = int(match[2])
rows = int(match[3])
# lead_style = {
# 5:
# }[int(match[4])]
plating = {
"F": "Gold flash on post, Matte Tin on tail",
"L": '10 µ" (0.25 µm) Gold on post, Matte Tin on tail',
"G": '10 µ" (0.25 µm) Gold on post, Gold flash on balance',
"T": "Matte Tin",
}[match[5]]
cols = {"S": 1, "D": 2, "T": 3, "Q": 4}[match[6]]
strobe = [(2 if cols == 4 else 1) * 0.1 * INCH, spacing * 0.1 * INCH]
if cols == 4:
# only outer columns filled
cols = 2
fp_name = f"PinHeader_{cols}x{rows:02d}_0.1in_{part}"
hole_diam = 0.040 * INCH
pad_size = 0.075 * INCH
center = [strobe[0] * (cols - 1) / 2, strobe[1] * (rows - 1) / 2]
if lib is None:
lib = dir_ / "samtec.pretty"
lib = Path(lib)
with open(lib / f"{fp_name}.kicad_mod", "w") as f:
f.write(
"\n".join(
(
f'(footprint "{fp_name}" (version {now.strftime("%Y%m%d")}) (generator pcbnew) (layer "F.Cu")',
r" (attr through_hole)",
f' (fp_text reference "REF**" (at {center[0]} {-2.032}) (layer "F.SilkS")',
r" (effects (font (size 0.762 0.762) (thickness 0.127)))",
r" )",
f' (fp_text value "VAL**" (at {center[0]} {center[1]} 90) (layer "F.Fab") hide',
r" (effects (font (size 0.762 0.762) (thickness 0.127)))",
r" )",
r' (fp_text user "${REFERENCE}" ' f'(at {center[0]} {center[1]} 90) (layer "F.Fab")',
r" (effects (font (size 1 1) (thickness 0.15)))",
r" )",
f" (fp_rect (start {-0.05 * INCH} {-0.05 * INCH}) (end {0.05 * INCH + strobe[0] * (cols - 1)} {0.05 * INCH + strobe[1] * (rows - 1)})",
f' (stroke (width {SILK_WIDTH}) (type default)) (fill none) (layer "F.SilkS")' r" )",
f' (fp_line (start {-0.05 * INCH} {0.05 * INCH}) (end {0.05 * INCH} {0.05 * INCH}) (layer "F.SilkS") (width {SILK_WIDTH}))',
f' (fp_line (start {0.05 * INCH} {-0.05 * INCH}) (end {0.05 * INCH} {0.05 * INCH}) (layer "F.SilkS") (width {SILK_WIDTH}))',
*[
f' (pad "{pad + 1}" thru_hole {"rect" if pad == 0 else "circle"} '
f"(at {strobe[0] * (pad % cols)} {strobe[1] * int(pad / cols)}) (size {pad_size} {pad_size}) (drill {hole_diam}) (layers *.Cu *.Mask))"
for pad in range(rows * cols)
],
f' (model "/home/brendan/Documents/projects/kicad/samtec.pretty/3dshapes/{part}.stp"',
f" (offset (xyz {center[0]} {-center[1]} 0))",
r" (scale (xyz 1 1 1))",
r" (rotate (xyz -90 0 90))",
r" )",
r")",
)
)
+ "\n"
)
else:
raise ValueError(f"Unknown part family for {part}")
if __name__ == "__main__":
for part in PARTS:
time.sleep(rng.random() * 3)
download_step_samtec(part)
footprint_samtec(part)
# from multiprocessing import Pool
# pool = Pool(10)
# pool.map(download_step_samtec, PARTS)
# pool.map(footprint_samtec, PARTS)
# pool.close()