2022-09-20 22:55:56 -06:00
|
|
|
import time
|
2022-09-22 20:05:31 -06:00
|
|
|
import zipfile
|
2022-09-21 01:07:42 -06:00
|
|
|
from datetime import datetime
|
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-22 20:05:31 -06:00
|
|
|
import requests
|
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
|
|
|
|
|
|
|
|
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-22 20:05:31 -06:00
|
|
|
PARTS_SAMTEC = [
|
|
|
|
# Pin Headers
|
|
|
|
*[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)],
|
|
|
|
# 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]]
|
|
|
|
# ),
|
|
|
|
]
|
|
|
|
|
|
|
|
PARTS_MOLEX = [
|
|
|
|
# Micro-Fit 3.0 Single Row Headers
|
|
|
|
*[f"43650-{i:02d}10" for i in range(2, 13)], # 1 row, RA, press-fit retention clip
|
|
|
|
*[f"43650-{i:02d}13" for i in range(2, 13)], # 1 row, RA, solder tab
|
|
|
|
*[f"43650-{i:02d}22" for i in range(2, 13)], # 1 row, Vertical, press-fit retention clip
|
|
|
|
*[f"43650-{i:02d}25" for i in range(2, 13)], # 1 row, RA, solder tab
|
|
|
|
# Micro-Fit 3.0 Dual Row Headers
|
|
|
|
*[f"43045-{i:02d}07" for i in range(2, 25, 2)], # 2 row, RA, press-fit retention clip
|
|
|
|
*[f"43045-{i:02d}10" for i in range(2, 25, 2)], # 2 row, RA, solder tab
|
|
|
|
*[f"43045-{i:02d}16" for i in range(2, 25, 2)], # 2 row, Vertical, press-fit retention clip
|
|
|
|
*[f"43045-{i:02d}19" for i in range(2, 25, 2)], # 2 row, RA, solder tab
|
|
|
|
# Eurostyle 5.08mm Headers
|
|
|
|
# "39531-0002",
|
|
|
|
# Eurostyle 5.08mm Plugs
|
|
|
|
# "39530-0002",
|
2022-09-20 22:55:56 -06:00
|
|
|
]
|
|
|
|
|
|
|
|
|
2022-09-22 20:05:31 -06:00
|
|
|
def download_step_samtec(part: str) -> 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)},
|
|
|
|
)
|
|
|
|
chrome_options.add_argument("--headless")
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
2022-09-22 20:05:31 -06:00
|
|
|
def download_step_molex(part: Union[str, int]) -> None:
|
|
|
|
# remove dash
|
|
|
|
part = f'{int(str(part).replace("-", "")):09d}'
|
|
|
|
|
|
|
|
print(part)
|
|
|
|
|
|
|
|
with TemporaryDirectory(prefix=str(dir_ / "tmp") + "/") as tmp:
|
|
|
|
tmp = Path(tmp)
|
|
|
|
|
|
|
|
for p in [part, part[:5] + "-" + part[5:]]:
|
|
|
|
try:
|
|
|
|
zipname = f"{part}_stp.zip"
|
|
|
|
|
|
|
|
# download
|
|
|
|
with open(tmp / zipname, "wb") as f:
|
|
|
|
f.write(requests.get(f"https://www.molex.com/pdm_docs/stp/{p}_stp.zip").content)
|
|
|
|
|
|
|
|
# unzip
|
|
|
|
with zipfile.ZipFile(tmp / zipname) as zip:
|
|
|
|
zip.extractall(path=str(tmp))
|
|
|
|
|
|
|
|
# move
|
|
|
|
filename = f"{part}.stp"
|
|
|
|
(Path(tmp) / filename).rename(dir_ / "molex.pretty" / "3dshapes" / filename)
|
|
|
|
|
|
|
|
return
|
|
|
|
except zipfile.BadZipFile:
|
|
|
|
print(f"zip does not exist for part {p}")
|
|
|
|
|
|
|
|
raise ValueError(f"Could not download .stp for part {part}")
|
|
|
|
|
|
|
|
|
2022-09-21 01:07:42 -06:00
|
|
|
now = datetime.now()
|
|
|
|
INCH = 25.4
|
|
|
|
SILK_WIDTH = 0.007 * INCH
|
|
|
|
|
|
|
|
|
|
|
|
def footprint_samtec(part: str, lib: Optional[Union[Path, str]] = None) -> str:
|
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",
|
|
|
|
"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}"
|
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]
|
|
|
|
|
|
|
|
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)",
|
2022-09-21 01:19:35 -06:00
|
|
|
f' (descr "{description}")',
|
|
|
|
f' (tags "{" ".join(keywords)}")',
|
2022-09-21 01:07:42 -06:00
|
|
|
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" )",
|
2022-09-21 01:19:35 -06:00
|
|
|
f" (fp_rect (start {-0.05 * INCH} {-0.05 * INCH}) "
|
|
|
|
f"(end {0.05 * INCH + strobe[0] * (cols - 1)} {0.05 * INCH + strobe[1] * (rows - 1)})",
|
2022-09-21 01:07:42 -06:00
|
|
|
f' (stroke (width {SILK_WIDTH}) (type default)) (fill none) (layer "F.SilkS")' r" )",
|
2022-09-21 01:19:35 -06:00
|
|
|
f" (fp_line (start {-0.05 * INCH} {0.05 * INCH}) (end {0.05 * INCH} {0.05 * INCH}) "
|
|
|
|
f'(layer "F.SilkS") (width {SILK_WIDTH}))',
|
|
|
|
f" (fp_line (start {0.05 * INCH} {-0.05 * INCH}) (end {0.05 * INCH} {0.05 * INCH}) "
|
|
|
|
f'(layer "F.SilkS") (width {SILK_WIDTH}))',
|
2022-09-21 01:07:42 -06:00
|
|
|
*[
|
|
|
|
f' (pad "{pad + 1}" thru_hole {"rect" if pad == 0 else "circle"} '
|
2022-09-21 01:19:35 -06:00
|
|
|
f"(at {strobe[0] * (pad % cols)} {strobe[1] * int(pad / cols)}) "
|
|
|
|
f"(size {pad_size} {pad_size}) (drill {hole_diam}) (layers *.Cu *.Mask))"
|
2022-09-21 01:07:42 -06:00
|
|
|
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"
|
|
|
|
)
|
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_SAMTEC:
|
|
|
|
# time.sleep(rng.random() * 3)
|
|
|
|
# download_step_samtec(part)
|
|
|
|
# footprint_samtec(part)
|
|
|
|
|
|
|
|
# pool.map(download_step_samtec, PARTS_SAMTEC)
|
|
|
|
# pool.map(footprint_samtec, PARTS_SAMTEC)
|
|
|
|
|
|
|
|
# for part in PARTS_MOLEX:
|
|
|
|
# download_step_molex(part)
|
|
|
|
pool.map(download_step_molex, PARTS_MOLEX)
|
|
|
|
pool.close()
|