abstract footprint file format
This commit is contained in:
parent
f8fe77771f
commit
59db22ac7c
156
fp_generator.py
Normal file
156
fp_generator.py
Normal file
|
@ -0,0 +1,156 @@
|
|||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import List, Literal, Optional, Tuple, Union
|
||||
|
||||
INCH = 25.4
|
||||
|
||||
|
||||
class Footprint:
|
||||
text_size = {
|
||||
# layer: (size_x, size_y, stroke)
|
||||
"F.SilkS": (0.030 * INCH, 0.030 * INCH, 0.007 * INCH),
|
||||
"B.SilkS": (0.030 * INCH, 0.030 * INCH, 0.007 * INCH),
|
||||
"F.Fab": (0.030 * INCH, 0.030 * INCH, 0.005 * INCH),
|
||||
"B.Fab": (0.030 * INCH, 0.030 * INCH, 0.005 * INCH),
|
||||
None: (0.030 * INCH, 0.030 * INCH, 0.005 * INCH),
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
description: Optional[str] = None,
|
||||
keywords: Optional[List[str]] = None,
|
||||
):
|
||||
self.name = name
|
||||
self.description = "" if description is None else description
|
||||
self._pads = []
|
||||
self._models = []
|
||||
self._contents = (
|
||||
"("
|
||||
f'footprint "{self.name}"\n'
|
||||
f' (version {datetime.now().strftime("%Y%m%d")})\n'
|
||||
" (generator pcbnew)\n"
|
||||
' (layer "F.Cu")\n'
|
||||
" (attr smd)\n"
|
||||
)
|
||||
if description is not None:
|
||||
self._contents += f' (descr "{description}")\n'
|
||||
|
||||
if keywords is not None:
|
||||
self._contents += f' (tags "{" ".join(keywords)}")\n'
|
||||
|
||||
def add_pad(
|
||||
self,
|
||||
id: Union[int, str],
|
||||
x: float,
|
||||
y: float,
|
||||
size: Union[float, Tuple[float]],
|
||||
hole: Optional[float] = None,
|
||||
shape: Optional[Literal["circle", "rect", "round_rect"]] = None,
|
||||
):
|
||||
try:
|
||||
size = (float(size), float(size))
|
||||
except ValueError:
|
||||
size = tuple(size)
|
||||
|
||||
if shape not in [None, "circle", "rect", "round_rect"]:
|
||||
raise ValueError
|
||||
|
||||
if hole is not None:
|
||||
self._contents.replace("(attr smd)", "(attr through_hole)")
|
||||
|
||||
self._contents += (
|
||||
f' (pad "{id}" thru_hole {shape} '
|
||||
f"(at {x} {y}) "
|
||||
f"(size {size[0]} {size[1]}) "
|
||||
f"(drill {hole}) "
|
||||
"(layers *.Cu *.Mask)"
|
||||
")\n"
|
||||
)
|
||||
|
||||
def add_model(
|
||||
self,
|
||||
path: Union[Path, str],
|
||||
offset: Tuple[float] = (0, 0, 0),
|
||||
rotate: Tuple[float] = (0, 0, 0),
|
||||
scale: Tuple[float] = (1, 1, 1),
|
||||
):
|
||||
self._contents += (
|
||||
f' (model "{str(path)}" '
|
||||
f"(offset (xyz {offset[0]} {offset[1]} {offset[2]})) "
|
||||
f"(scale (xyz {scale[0]} {scale[1]} {scale[2]})) "
|
||||
f"(rotate (xyz {rotate[0]} {rotate[1]} {rotate[2]})) "
|
||||
")\n"
|
||||
)
|
||||
|
||||
def add_text(
|
||||
self,
|
||||
name: str,
|
||||
text: str,
|
||||
x: float,
|
||||
y: float,
|
||||
layer: str,
|
||||
rotation: float = 0,
|
||||
size: Optional[Tuple[float]] = None,
|
||||
hidden: bool = False,
|
||||
):
|
||||
if size is None:
|
||||
if layer in self.text_size:
|
||||
size = self.text_size[layer]
|
||||
else:
|
||||
size = self.text_size[None]
|
||||
self._contents += (
|
||||
f' (fp_text {name} "{text}" '
|
||||
f"(at {x} {y} {rotation}) "
|
||||
f'(layer "{layer}") '
|
||||
f"{'hide' if hidden else ''}"
|
||||
f"(effects (font (size {size[0]} {size[1]}) (thickness {size[2]}))) "
|
||||
")\n"
|
||||
)
|
||||
|
||||
def add_line(
|
||||
self,
|
||||
start: Tuple[float],
|
||||
end: Tuple[float],
|
||||
layer: str,
|
||||
stroke: Optional[float] = None,
|
||||
):
|
||||
if stroke is None:
|
||||
if layer in self.text_size:
|
||||
stroke = self.text_size[layer][2]
|
||||
else:
|
||||
stroke = self.text_size[None][2]
|
||||
stroke = float(stroke)
|
||||
|
||||
self._contents += (
|
||||
f' (fp_line (start {start[0]} {start[1]}) (end {end[0]} {end[1]}) (layer "{layer}") (width {stroke}))\n'
|
||||
)
|
||||
|
||||
def add_rect(
|
||||
self,
|
||||
start: Tuple[float],
|
||||
end: Tuple[float],
|
||||
layer: str,
|
||||
stroke: Optional[float] = None,
|
||||
fill: bool = False,
|
||||
):
|
||||
if stroke is None:
|
||||
if layer in self.text_size:
|
||||
stroke = self.text_size[layer][2]
|
||||
else:
|
||||
stroke = self.text_size[None][2]
|
||||
stroke = float(stroke)
|
||||
|
||||
self._contents += (
|
||||
" (fp_rect "
|
||||
f"(start {start[0]} {start[1]}) "
|
||||
f"(end {end[0]} {end[1]}) "
|
||||
f"(stroke (width {stroke}) (type default)) "
|
||||
f'(fill {"none" if not fill else "solidButIDontKnowWhatStringToUse"}) '
|
||||
f'(layer "{layer}") '
|
||||
")\n"
|
||||
)
|
||||
|
||||
def write(self, lib: Union[Path, str]):
|
||||
with open(Path(lib) / f"{self.name}.kicad_mod", "w") as f:
|
||||
f.write(self._contents + ")")
|
100
generate.py
100
generate.py
|
@ -13,19 +13,24 @@ from selenium.webdriver.support import expected_conditions as EC
|
|||
from selenium.webdriver.support.select import By
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
|
||||
from fp_generator import INCH, Footprint
|
||||
|
||||
dir_ = Path(__file__).parent
|
||||
rng = np.random.default_rng()
|
||||
|
||||
SILK_WIDTH = 0.007 * INCH
|
||||
|
||||
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)],
|
||||
*[f"TSW-1{i:02d}-07-L-S" for i in range(1, 5)],
|
||||
# *[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
|
||||
|
@ -66,7 +71,7 @@ def download_step_samtec(part: str) -> None:
|
|||
"prefs",
|
||||
{"download.default_directory": str(tmp)},
|
||||
)
|
||||
chrome_options.add_argument("--headless")
|
||||
# chrome_options.add_argument("--headless")
|
||||
|
||||
with webdriver.Chrome(
|
||||
options=chrome_options,
|
||||
|
@ -135,11 +140,6 @@ def download_step_molex(part: Union[str, int]) -> None:
|
|||
raise ValueError(f"Could not download .stp for part {part}")
|
||||
|
||||
|
||||
now = datetime.now()
|
||||
INCH = 25.4
|
||||
SILK_WIDTH = 0.007 * INCH
|
||||
|
||||
|
||||
def footprint_samtec(part: str, lib: Optional[Union[Path, str]] = None) -> str:
|
||||
if any([part.startswith(s) for s in ["TSW-", "HTSW-"]]):
|
||||
# pinheader
|
||||
|
@ -156,8 +156,8 @@ def footprint_samtec(part: str, lib: Optional[Union[Path, str]] = None) -> str:
|
|||
lead_style = "vertical"
|
||||
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',
|
||||
"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",
|
||||
"T": "Matte Tin",
|
||||
}[match[5]]
|
||||
cols = {"S": 1, "D": 2, "T": 3, "Q": 4}[match[6]]
|
||||
|
@ -179,46 +179,34 @@ def footprint_samtec(part: str, lib: Optional[Union[Path, str]] = None) -> str:
|
|||
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' (descr "{description}")',
|
||||
f' (tags "{" ".join(keywords)}")',
|
||||
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}) "
|
||||
f"(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}) "
|
||||
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}))',
|
||||
*[
|
||||
f' (pad "{pad + 1}" thru_hole {"rect" if pad == 0 else "circle"} '
|
||||
f"(at {strobe[0] * (pad % cols)} {strobe[1] * int(pad / cols)}) "
|
||||
f"(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")",
|
||||
|
||||
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], center[1], rotation=90, layer="F.Fab", hidden=True)
|
||||
fp.add_text("user", r"${REFERENCE}**", center[0], center[1], rotation=90, layer="F.Fab")
|
||||
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",
|
||||
)
|
||||
+ "\n"
|
||||
fp.add_model(
|
||||
f"/home/brendan/Documents/projects/kicad/samtec.pretty/3dshapes/{part}.stp",
|
||||
(center[0], center[1], 0),
|
||||
rotate=(-90, 0, 90),
|
||||
)
|
||||
fp.write(lib)
|
||||
|
||||
elif any([part.startswith(s) for s in ["SSW-"]]):
|
||||
# socket
|
||||
raise NotImplementedError()
|
||||
|
@ -231,15 +219,15 @@ if __name__ == "__main__":
|
|||
|
||||
pool = Pool(10)
|
||||
|
||||
# for part in PARTS_SAMTEC:
|
||||
for part in PARTS_SAMTEC:
|
||||
# time.sleep(rng.random() * 3)
|
||||
# download_step_samtec(part)
|
||||
# footprint_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.map(download_step_molex, PARTS_MOLEX)
|
||||
pool.close()
|
||||
|
|
Loading…
Reference in New Issue
Block a user