kicad/generate_samtec.py

179 lines
6.1 KiB
Python
Raw Normal View History

2022-09-20 22:55:56 -06:00
import time
2022-09-22 20:05:31 -06:00
import zipfile
2022-09-20 22:55:56 -06:00
from pathlib import Path
from tempfile import TemporaryDirectory
2022-09-21 01:07:42 -06:00
from typing import Optional, Union
2022-09-20 22:55:56 -06:00
2022-09-21 01:07:42 -06:00
import numpy as np
import regex as re
2022-09-20 22:55:56 -06:00
from selenium import webdriver
from selenium.webdriver.support import expected_conditions as EC
2022-09-21 01:07:42 -06:00
from selenium.webdriver.support.select import By
2022-09-20 22:55:56 -06:00
from selenium.webdriver.support.wait import WebDriverWait
2022-09-23 01:14:27 -06:00
from fp_generator import INCH, Footprint
2022-09-20 22:55:56 -06:00
dir_ = Path(__file__).parent
2022-09-21 01:07:42 -06:00
rng = np.random.default_rng()
2022-09-20 22:55:56 -06:00
2022-09-23 01:14:27 -06:00
PARTS = [
2022-09-22 20:05:31 -06:00
# Pin Headers
*[f"TSW-1{i:02d}-07-L-S" for i in range(1, 5)], # quick for testing
2022-09-23 01:14:27 -06:00
# *[f"TSW-1{i:02d}-07-L-S" for i in range(1, 51)],
# *[f"TSW-1{i:02d}-07-L-D" for i in range(1, 51)],
# # *[f"TSW-1{i:02d}-07-L-T" for i in range(1, 51)],
# # *[f"TSW-1{i:02d}-07-L-Q" for i in range(1, 51)],
# *[f"TSW-2{i:02d}-07-L-S" for i in range(2, 26)],
# *[f"TSW-2{i:02d}-07-L-D" for i in range(2, 26)],
# # *[f"TSW-2{i:02d}-07-L-T" for i in range(2, 26)],
# # *[f"TSW-2{i:02d}-07-L-Q" for i in range(2, 26)],
2022-09-22 20:05:31 -06:00
# Sockets
# SSW
# SMH
# Discrete Wire
# TFM
# SFM
# High Speed Board to Board
# *np.flatten(
# [[f"LSHM-1{i:02d}-{h:02.1f}-L-DV-A-S-TR" for i in [5, 10, 20, 30, 40, 50]] for h in [2.5, 3.0, 4.0, 6.0]]
# ),
]
2022-09-20 22:55:56 -06:00
def download_step(part: str, headless: bool = True) -> None:
2022-09-20 22:55:56 -06:00
print(part)
2022-09-21 01:07:42 -06:00
with TemporaryDirectory(prefix=str(dir_ / "tmp") + "/") as tmp:
2022-09-20 22:55:56 -06:00
tmp = Path(tmp)
chrome_options = webdriver.ChromeOptions()
chrome_options.add_experimental_option(
"prefs",
{"download.default_directory": str(tmp)},
)
if headless:
chrome_options.add_argument("--headless")
2022-09-20 22:55:56 -06:00
with webdriver.Chrome(
options=chrome_options,
) as browser:
2022-09-22 20:05:31 -06:00
browser.get(f"https://www.snapeda.com/parts/{part.upper()}/Samtec/embed/?ref=samtec")
2022-09-21 01:07:42 -06:00
wait = WebDriverWait(browser, 30)
2022-09-20 22:55:56 -06:00
wait.until(
2022-09-21 01:07:42 -06:00
EC.element_to_be_clickable(browser.find_element(by=By.ID, value="download_traceparts_3d_model"))
2022-09-20 22:55:56 -06:00
).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)
2022-09-21 01:07:42 -06:00
# print(list((Path(tmp).glob("*.zip"))))
2022-09-20 22:55:56 -06:00
# unzip
2022-09-22 20:05:31 -06:00
with zipfile.ZipFile(list((Path(tmp).glob("*.zip")))[0]) as zip:
2022-09-20 22:55:56 -06:00
zip.extractall(path=str(tmp))
# move
filename = f"{part.upper()}.stp"
2022-09-21 01:07:42 -06:00
# 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)
def footprint(part: str, lib: Optional[Union[Path, str]] = None) -> str:
2022-09-23 19:17:09 -06:00
if lib is None:
lib = dir_ / "samtec.pretty"
lib = Path(lib)
2022-09-22 20:05:31 -06:00
if any([part.startswith(s) for s in ["TSW-", "HTSW-"]]):
2022-09-21 01:07:42 -06:00
# pinheader
match = re.match(
2022-09-22 20:05:31 -06:00
r"(H?)TSW-([12])(\d\d)-(\d\d)-([FLGT])-([SDTQ])(-R[AE])?(-NA)?(-LL)?(-LC)?(-LA)?(-(\d\d\d))?",
2022-09-21 01:07:42 -06:00
part.upper(),
)
high_temp = match[1] != ""
spacing = int(match[2])
rows = int(match[3])
# lead_style = {
# 5:
# }[int(match[4])]
2022-09-21 01:19:35 -06:00
lead_style = "vertical"
2022-09-21 01:07:42 -06:00
plating = {
"F": "Gold flash on post, Matte Tin on tail",
2022-09-23 01:14:27 -06:00
"L": "10 µin (0.25 µm) Gold on post, Matte Tin on tail",
"G": "10 µin (0.25 µm) Gold on post, Gold flash on balance",
2022-09-21 01:07:42 -06:00
"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}"
2022-09-21 01:19:35 -06:00
description = (
f"Header, Male, {cols}x{rows:02d}, {lead_style}, {plating}{', high temperature' if high_temp else ''}"
)
keywords = ["header", "pin"]
2022-09-21 01:07:42 -06:00
hole_diam = 0.040 * INCH
pad_size = 0.075 * INCH
center = [strobe[0] * (cols - 1) / 2, strobe[1] * (rows - 1) / 2]
2022-09-23 01:14:27 -06:00
fp = Footprint(fp_name, description=description, keywords=keywords)
fp.add_text("reference", "REF**", center[0], -2.032, layer="F.SilkS")
fp.add_text("value", "VAL**", center[0] - 0.025 * INCH, center[1], rotation=90, layer="F.Fab")
fp.add_text("user", r"${REFERENCE}**", center[0] + 0.025 * INCH, center[1], rotation=90, layer="F.Fab")
2022-09-23 01:14:27 -06:00
fp.add_rect(
[-0.05 * INCH] * 2,
(0.05 * INCH + strobe[0] * (cols - 1), 0.05 * INCH + strobe[1] * (rows - 1)),
layer="F.SilkS",
)
fp.add_line((-0.05 * INCH, 0.05 * INCH), (0.05 * INCH, 0.05 * INCH), layer="F.SilkS")
fp.add_line((0.05 * INCH, -0.05 * INCH), (0.05 * INCH, 0.05 * INCH), layer="F.SilkS")
for pad in range(rows * cols):
fp.add_pad(
pad + 1,
strobe[0] * (pad % cols),
strobe[1] * int(pad / cols),
size=pad_size,
hole=hole_diam,
shape="rect" if pad == 0 else "circle",
)
fp.add_model(
2022-09-23 19:17:09 -06:00
lib / "3dshapes" / f"{part}.stp",
2022-09-23 01:14:27 -06:00
(center[0], center[1], 0),
rotate=(-90, 0, 90),
2022-09-21 01:07:42 -06:00
)
2022-09-23 01:14:27 -06:00
fp.write(lib)
2022-09-22 20:05:31 -06:00
elif any([part.startswith(s) for s in ["SSW-"]]):
# socket
raise NotImplementedError()
2022-09-21 01:07:42 -06:00
else:
raise ValueError(f"Unknown part family for {part}")
2022-09-20 22:55:56 -06:00
2022-09-21 01:07:42 -06:00
if __name__ == "__main__":
2022-09-22 20:05:31 -06:00
from multiprocessing import Pool
pool = Pool(10)
# for part in PARTS:
# # time.sleep(rng.random() * 3)
# # download_step(part)
# footprint(part)
2022-09-22 20:05:31 -06:00
# pool.map(download_step, PARTS)
pool.map(footprint, PARTS)
2022-09-22 20:05:31 -06:00
pool.close()