Compare commits

...

18 Commits

Author SHA1 Message Date
Jonas Zeunert
c6113acb92 WIP 2024-05-13 11:51:11 +02:00
Jonas Zeunert
b4b13ec175 Test + Impl: empty footprint 2024-04-27 22:10:24 +02:00
Jonas Zeunert
ebcd74cd5c Impl: replace_template 2024-04-27 16:30:39 +02:00
Jonas Zeunert
3ebc8d47f5 Test: convert_template 2024-04-27 16:26:38 +02:00
Jonas Zeunert
100dc8125b Add a bit of output 2024-04-27 15:51:15 +02:00
Jonas Zeunert
7b3293a8f3 Create output file 2024-04-27 15:43:27 +02:00
Jonas Zeunert
5cb869a9d8 Restructure: remove separate parser 2024-04-27 15:39:23 +02:00
Jonas Zeunert
e03a70cc05 Restructure: remove separate parser 2024-04-27 15:38:36 +02:00
Jonas Zeunert
8e5cf17c0b parse, convert, write 2024-04-27 15:19:30 +02:00
Jonas Zeunert
4561e0399b Rewrite tests 2024-04-27 14:58:50 +02:00
Jonas Zeunert
8c3d678ffb Add: Test for filenotfounderror 2024-04-27 14:35:19 +02:00
Jonas Zeunert
20a6b12642 Add: Parser + test 2024-04-27 14:21:55 +02:00
Jonas Zeunert
de6753ce3a Add: JS template 2024-04-27 13:57:53 +02:00
Jonas Zeunert
28a38ad317 Integration test 2024-04-27 13:41:41 +02:00
Jonas Zeunert
254c714c8b First integration test 2024-04-26 20:29:43 +02:00
Jonas Zeunert
041aaa5d88 Move to pdm as build env 2024-04-26 17:11:02 +02:00
Jonas Zeunert
9de4b13aca Move to pdm as build env 2024-04-26 17:08:24 +02:00
Jonas Zeunert
17f54506b6 First structure 2024-04-25 13:18:00 +02:00
14 changed files with 741 additions and 3 deletions

6
.gitignore vendored
View File

@@ -1,4 +1,3 @@
# ---> Python
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
@@ -107,8 +106,10 @@ ipython_config.py
#pdm.lock #pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control. # in version control.
# https://pdm.fming.dev/#use-with-ide # https://pdm-project.org/#use-with-ide
.pdm.toml .pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/ __pypackages__/
@@ -159,4 +160,3 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear # and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/ #.idea/

236
pdm.lock generated Normal file
View File

@@ -0,0 +1,236 @@
# This file is @generated by PDM.
# It is not intended for manual editing.
[metadata]
groups = ["default", "test", "dev"]
strategy = ["cross_platform", "inherit_metadata"]
lock_version = "4.4.1"
content_hash = "sha256:3fa91e80a653abfaf653ee5ca1604ecb90d017144568af9ce50b04d47871092f"
[[package]]
name = "argparse"
version = "1.4.0"
summary = "Python command-line parsing library"
groups = ["default"]
files = [
{file = "argparse-1.4.0-py2.py3-none-any.whl", hash = "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"},
{file = "argparse-1.4.0.tar.gz", hash = "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4"},
]
[[package]]
name = "astroid"
version = "3.1.0"
requires_python = ">=3.8.0"
summary = "An abstract syntax tree for Python with inference support."
groups = ["dev"]
files = [
{file = "astroid-3.1.0-py3-none-any.whl", hash = "sha256:951798f922990137ac090c53af473db7ab4e70c770e6d7fae0cec59f74411819"},
{file = "astroid-3.1.0.tar.gz", hash = "sha256:ac248253bfa4bd924a0de213707e7ebeeb3138abeb48d798784ead1e56d419d4"},
]
[[package]]
name = "colorama"
version = "0.4.6"
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
summary = "Cross-platform colored terminal text."
groups = ["default", "dev", "test"]
marker = "sys_platform == \"win32\""
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "dill"
version = "0.3.8"
requires_python = ">=3.8"
summary = "serialize all of Python"
groups = ["dev"]
marker = "python_version >= \"3.11\""
files = [
{file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"},
{file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"},
]
[[package]]
name = "iniconfig"
version = "2.0.0"
requires_python = ">=3.7"
summary = "brain-dead simple config-ini parsing"
groups = ["default", "dev", "test"]
files = [
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
]
[[package]]
name = "isort"
version = "5.13.2"
requires_python = ">=3.8.0"
summary = "A Python utility / library to sort Python imports."
groups = ["dev"]
files = [
{file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"},
{file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"},
]
[[package]]
name = "kiutils"
version = "1.4.8"
requires_python = ">=3.7"
summary = "Simple and SCM-friendly KiCad file parser for KiCad 6.0 and up"
groups = ["default"]
files = [
{file = "kiutils-1.4.8-py3-none-any.whl", hash = "sha256:ca7c5c7eafb7bc33daa3d6cfe42cc704be2bcd2206103b28ad2682d88a6072ae"},
{file = "kiutils-1.4.8.tar.gz", hash = "sha256:18c5803283e579cfe8772955e7702548d7139e0fc630daa579ed6b2b767db846"},
]
[[package]]
name = "mccabe"
version = "0.7.0"
requires_python = ">=3.6"
summary = "McCabe checker, plugin for flake8"
groups = ["dev"]
files = [
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
]
[[package]]
name = "mypy"
version = "1.10.0"
requires_python = ">=3.8"
summary = "Optional static typing for Python"
groups = ["default"]
dependencies = [
"mypy-extensions>=1.0.0",
"typing-extensions>=4.1.0",
]
files = [
{file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"},
{file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"},
{file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"},
{file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"},
{file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"},
{file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"},
{file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"},
{file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"},
{file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"},
{file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"},
{file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"},
{file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"},
]
[[package]]
name = "mypy-extensions"
version = "1.0.0"
requires_python = ">=3.5"
summary = "Type system extensions for programs checked with the mypy type checker."
groups = ["default"]
files = [
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
]
[[package]]
name = "packaging"
version = "24.0"
requires_python = ">=3.7"
summary = "Core utilities for Python packages"
groups = ["default", "dev", "test"]
files = [
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
]
[[package]]
name = "pathlib"
version = "1.0.1"
summary = "Object-oriented filesystem paths"
groups = ["default"]
files = [
{file = "pathlib-1.0.1-py3-none-any.whl", hash = "sha256:f35f95ab8b0f59e6d354090350b44a80a80635d22efdedfa84c7ad1cf0a74147"},
{file = "pathlib-1.0.1.tar.gz", hash = "sha256:6940718dfc3eff4258203ad5021090933e5c04707d5ca8cc9e73c94a7894ea9f"},
]
[[package]]
name = "platformdirs"
version = "4.2.1"
requires_python = ">=3.8"
summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
groups = ["dev"]
files = [
{file = "platformdirs-4.2.1-py3-none-any.whl", hash = "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1"},
{file = "platformdirs-4.2.1.tar.gz", hash = "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf"},
]
[[package]]
name = "pluggy"
version = "1.5.0"
requires_python = ">=3.8"
summary = "plugin and hook calling mechanisms for python"
groups = ["default", "dev", "test"]
files = [
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[[package]]
name = "pylint"
version = "3.1.0"
requires_python = ">=3.8.0"
summary = "python code static checker"
groups = ["dev"]
dependencies = [
"astroid<=3.2.0-dev0,>=3.1.0",
"colorama>=0.4.5; sys_platform == \"win32\"",
"dill>=0.3.6; python_version >= \"3.11\"",
"dill>=0.3.7; python_version >= \"3.12\"",
"isort!=5.13.0,<6,>=4.2.5",
"mccabe<0.8,>=0.6",
"platformdirs>=2.2.0",
"tomlkit>=0.10.1",
]
files = [
{file = "pylint-3.1.0-py3-none-any.whl", hash = "sha256:507a5b60953874766d8a366e8e8c7af63e058b26345cfcb5f91f89d987fd6b74"},
{file = "pylint-3.1.0.tar.gz", hash = "sha256:6a69beb4a6f63debebaab0a3477ecd0f559aa726af4954fc948c51f7a2549e23"},
]
[[package]]
name = "pytest"
version = "8.1.2"
requires_python = ">=3.8"
summary = "pytest: simple powerful testing with Python"
groups = ["default", "dev", "test"]
dependencies = [
"colorama; sys_platform == \"win32\"",
"iniconfig",
"packaging",
"pluggy<2.0,>=1.4",
]
files = [
{file = "pytest-8.1.2-py3-none-any.whl", hash = "sha256:6c06dc309ff46a05721e6fd48e492a775ed8165d2ecdf57f156a80c7e95bb142"},
{file = "pytest-8.1.2.tar.gz", hash = "sha256:f3c45d1d5eed96b01a2aea70dee6a4a366d51d38f9957768083e4fecfc77f3ef"},
]
[[package]]
name = "tomlkit"
version = "0.12.4"
requires_python = ">=3.7"
summary = "Style preserving TOML library"
groups = ["dev"]
files = [
{file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"},
{file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"},
]
[[package]]
name = "typing-extensions"
version = "4.11.0"
requires_python = ">=3.8"
summary = "Backported and Experimental Type Hints for Python 3.8+"
groups = ["default"]
files = [
{file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"},
{file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"},
]

63
pyproject.toml Normal file
View File

@@ -0,0 +1,63 @@
[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"
[project]
name = "ergogen-footprint-converter"
version = "0.1.0"
authors = [
{ name="rhetenor", email="rhetenor@rhetenor.de" },
]
maintainers = [
{ name="rhetenor", email="rhetenor@rhetenor.de" },
]
description = "Converts kicad footprints to ergogen footprint templates."
readme = "README.md"
licence = {file = "LICENCE"}
keywords = ["ergogen", "kicad", "footprints", "kicad_mod", "converter"]
requires-python = ">=3.11"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: GPL3 License",
"Operating System :: OS Independent",
]
dependencies = [
"argparse",
"pathlib",
"pytest>=8.1.2",
"mypy>=1.10.0",
"kiutils>=1.4.8",
]
license = {text = "GPL"}
[project.optional-dependencies]
dev = [
"pylint",
"pytest>=8.1.1",
]
test = ["pytest"]
[project.urls]
Homepage = "https://github.com/pypa/sampleproject"
Issues = "https://github.com/pypa/sampleproject/issues"
[tool.pdm.scripts]
test = "pytest"
[tool.pdm.scripts.main]
call = "ergogen_footprint_converter.main:run"
[tool.pytest.ini_options]
minversion = "6.0"
addopts = "-ra -q"
testpaths = [
"tests",
"tests/integration",
"tests/unit",
]
pythonpath = './src'
[tool.pdm]
distribution = true

28
res/template.js Normal file
View File

@@ -0,0 +1,28 @@
module.exports = {
params: {
{% params %}
},
body: params => {
const main = (...body) => {
return `
{% main %}
${params.at /* parametric position given by ergogen */}
${...body.join('\n')}
`
}
const captions = () => {
return `
{% captions %}
`
}
const pins = () => {
return `
{% pins %}
`
}
return main(captions, pins)
}
}

View File

@@ -0,0 +1,35 @@
from kiutils.footprint import Footprint
from kiutils.items.fpitems import FpText
class Converter:
footprint: Footprint
template: str
def __init__(self, footprint: Footprint):
self.footprint = footprint
self.template = open("res/template.js").read()
def convert(self) -> str:
captions = self.footprint.graphicItems
captions_str = ''.join(list(map(lambda x: x.to_sexpr().join('\n'), captions)))
self.footprint.graphicItems = []
main = self.footprint.to_sexpr()
# replace new footprint name with module to be consistent
main = main.replace("footprint", "module")
return self.replace_template(self.template, "", main, "", "")
@staticmethod
def replace_template(template: str, params: str, main: str, captions: str, pins: str) -> str:
replacements = {
"{% params %}": params,
"{% main %}": main,
"{% captions %}": captions,
"{% pins %}": pins
}
for key in replacements:
template = template.replace(key, replacements[key])
return template

View File

@@ -0,0 +1,67 @@
#!/bin/python
from argparse import ArgumentParser
from pathlib import Path
from .converter import Converter
from kiutils.footprint import Footprint
def main (args) -> None:
check_args(args)
print("> Input: " + args["in_file"].absolute().__str__())
converted = convert(args["in_file"])
print("> Succesfully converted!")
write_output(converted, args["in_file"], args["out"])
print("> Output written! Exiting...")
def convert(in_file: Path) -> str:
print("> Parsing input...")
parsed = Footprint.from_file(in_file.absolute().__str__())
print("> Input parsed!")
converter = Converter(parsed)
print("> Converting...")
return converter.convert()
def write_output (output: str, in_file: Path, out_dir: Path) -> None:
filename = in_file.name.split('.')[0] + ".js"
out_file = out_dir / filename
print("> Writing output to " + out_file.absolute().__str__())
with open(out_file, 'w') as file:
file.writelines(output)
def check_args(args) -> None:
in_file: Path = args["in_file"]
out_dir: Path = args["out"]
if not in_file.exists():
raise FileNotFoundError("Input file not found!")
out_dir.mkdir(exist_ok=True)
def parse_args():
parser = ArgumentParser()
parser.add_argument("in_file", type=Path)
parser.add_argument("--out", type=Path, required=False, default=Path('.'))
return parser.parse_args()
def run () -> None:
args = parse_args()
main(args)
if __name__ == "__main__":
run()

0
tests/__init__.py Normal file
View File

View File

View File

@@ -0,0 +1,123 @@
module.exports = {
params: {
designator: 'MCU',
orientation: 'down',
RAW: {type: 'net', value: 'RAW'},
GND: {type: 'net', value: 'GND'},
RST: {type: 'net', value: 'RST'},
VCC: {type: 'net', value: 'VCC'},
P21: {type: 'net', value: 'P21'},
P20: {type: 'net', value: 'P20'},
P19: {type: 'net', value: 'P19'},
P18: {type: 'net', value: 'P18'},
P15: {type: 'net', value: 'P15'},
P14: {type: 'net', value: 'P14'},
P16: {type: 'net', value: 'P16'},
P10: {type: 'net', value: 'P10'},
P1: {type: 'net', value: 'P1'},
P0: {type: 'net', value: 'P0'},
P2: {type: 'net', value: 'P2'},
P3: {type: 'net', value: 'P3'},
P4: {type: 'net', value: 'P4'},
P5: {type: 'net', value: 'P5'},
P6: {type: 'net', value: 'P6'},
P7: {type: 'net', value: 'P7'},
P8: {type: 'net', value: 'P8'},
P9: {type: 'net', value: 'P9'}
},
body: p => {
const standard = `
(module ProMicro (layer F.Cu) (tedit 5B307E4C)
${p.at /* parametric position */}
${'' /* footprint reference */}
(fp_text reference "${p.ref}" (at 0 0) (layer F.SilkS) ${p.ref_hide} (effects (font (size 1.27 1.27) (thickness 0.15))))
(fp_text value "" (at 0 0) (layer F.SilkS) hide (effects (font (size 1.27 1.27) (thickness 0.15))))
${''/* illustration of the (possible) USB port overhang */}
(fp_line (start -19.304 -3.81) (end -14.224 -3.81) (layer Dwgs.User) (width 0.15))
(fp_line (start -19.304 3.81) (end -19.304 -3.81) (layer Dwgs.User) (width 0.15))
(fp_line (start -14.224 3.81) (end -19.304 3.81) (layer Dwgs.User) (width 0.15))
(fp_line (start -14.224 -3.81) (end -14.224 3.81) (layer Dwgs.User) (width 0.15))
${''/* component outline */}
(fp_line (start -17.78 8.89) (end 15.24 8.89) (layer F.SilkS) (width 0.15))
(fp_line (start 15.24 8.89) (end 15.24 -8.89) (layer F.SilkS) (width 0.15))
(fp_line (start 15.24 -8.89) (end -17.78 -8.89) (layer F.SilkS) (width 0.15))
(fp_line (start -17.78 -8.89) (end -17.78 8.89) (layer F.SilkS) (width 0.15))
`
function pins(def_neg, def_pos) {
return `
${''/* extra border around "RAW", in case the rectangular shape is not distinctive enough */}
(fp_line (start -15.24 ${def_pos}6.35) (end -12.7 ${def_pos}6.35) (layer F.SilkS) (width 0.15))
(fp_line (start -15.24 ${def_pos}6.35) (end -15.24 ${def_pos}8.89) (layer F.SilkS) (width 0.15))
(fp_line (start -12.7 ${def_pos}6.35) (end -12.7 ${def_pos}8.89) (layer F.SilkS) (width 0.15))
${''/* pin names */}
(fp_text user RAW (at -13.97 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user GND (at -11.43 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user RST (at -8.89 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user VCC (at -6.35 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P21 (at -3.81 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P20 (at -1.27 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P19 (at 1.27 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P18 (at 3.81 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P15 (at 6.35 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P14 (at 8.89 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P16 (at 11.43 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P10 (at 13.97 ${def_pos}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P01 (at -13.97 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P00 (at -11.43 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user GND (at -8.89 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user GND (at -6.35 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P02 (at -3.81 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P03 (at -1.27 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P04 (at 1.27 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P05 (at 3.81 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P06 (at 6.35 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P07 (at 8.89 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P08 (at 11.43 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P09 (at 13.97 ${def_neg}4.8 ${p.r + 90}) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
${''/* and now the actual pins */}
(pad 1 thru_hole rect (at -13.97 ${def_pos}7.62 ${p.r}) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.RAW})
(pad 2 thru_hole circle (at -11.43 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.GND})
(pad 3 thru_hole circle (at -8.89 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.RST})
(pad 4 thru_hole circle (at -6.35 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.VCC})
(pad 5 thru_hole circle (at -3.81 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P21})
(pad 6 thru_hole circle (at -1.27 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P20})
(pad 7 thru_hole circle (at 1.27 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P19})
(pad 8 thru_hole circle (at 3.81 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P18})
(pad 9 thru_hole circle (at 6.35 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P15})
(pad 10 thru_hole circle (at 8.89 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P14})
(pad 11 thru_hole circle (at 11.43 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P16})
(pad 12 thru_hole circle (at 13.97 ${def_pos}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P10})
(pad 13 thru_hole circle (at -13.97 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P1})
(pad 14 thru_hole circle (at -11.43 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P0})
(pad 15 thru_hole circle (at -8.89 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.GND})
(pad 16 thru_hole circle (at -6.35 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.GND})
(pad 17 thru_hole circle (at -3.81 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P2})
(pad 18 thru_hole circle (at -1.27 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P3})
(pad 19 thru_hole circle (at 1.27 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P4})
(pad 20 thru_hole circle (at 3.81 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P5})
(pad 21 thru_hole circle (at 6.35 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P6})
(pad 22 thru_hole circle (at 8.89 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P7})
(pad 23 thru_hole circle (at 11.43 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P8})
(pad 24 thru_hole circle (at 13.97 ${def_neg}7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask) ${p.P9})
`
}
if(p.orientation == 'down') {
return `
${standard}
${pins('-', '')})
`
} else {
return `
${standard}
${pins('', '-')})
`
}
}
}

View File

@@ -0,0 +1,66 @@
(module ProMicro (layer F.Cu) (tedit 5B307E4C)
(fp_text value "" (at 0 0) (layer F.SilkS) hide (effects (font (size 1.27 1.27) (thickness 0.15))))
(fp_line (start -19.304 -3.81) (end -14.224 -3.81) (layer Dwgs.User) (width 0.15))
(fp_line (start -19.304 3.81) (end -19.304 -3.81) (layer Dwgs.User) (width 0.15))
(fp_line (start -14.224 3.81) (end -19.304 3.81) (layer Dwgs.User) (width 0.15))
(fp_line (start -14.224 -3.81) (end -14.224 3.81) (layer Dwgs.User) (width 0.15))
(fp_line (start -17.78 8.89) (end 15.24 8.89) (layer F.SilkS) (width 0.15))
(fp_line (start 15.24 8.89) (end 15.24 -8.89) (layer F.SilkS) (width 0.15))
(fp_line (start 15.24 -8.89) (end -17.78 -8.89) (layer F.SilkS) (width 0.15))
(fp_line (start -17.78 -8.89) (end -17.78 8.89) (layer F.SilkS) (width 0.15))
(fp_line (start -15.24 6.35) (end -12.7 6.35) (layer F.SilkS) (width 0.15))
(fp_line (start -15.24 6.35) (end -15.24 8.89) (layer F.SilkS) (width 0.15))
(fp_line (start -12.7 6.35) (end -12.7 8.89) (layer F.SilkS) (width 0.15))
(fp_text user RAW (at -13.97 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user GND (at -11.43 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user RST (at -8.89 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user VCC (at -6.35 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P21 (at -3.81 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P20 (at -1.27 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P19 (at 1.27 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P18 (at 3.81 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P15 (at 6.35 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P14 (at 8.89 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P16 (at 11.43 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P10 (at 13.97 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P01 (at -13.97 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P00 (at -11.43 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user GND (at -8.89 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user GND (at -6.35 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P02 (at -3.81 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P03 (at -1.27 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P04 (at 1.27 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P05 (at 3.81 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P06 (at 6.35 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P07 (at 8.89 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P08 (at 11.43 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(fp_text user P09 (at 13.97 4.8 0) (layer F.SilkS) (effects (font (size 0.8 0.8) (thickness 0.15))))
(pad 1 thru_hole rect (at -13.97 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 2 thru_hole circle (at -11.43 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 3 thru_hole circle (at -8.89 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 4 thru_hole circle (at -6.35 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 5 thru_hole circle (at -3.81 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 6 thru_hole circle (at -1.27 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 7 thru_hole circle (at 1.27 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 8 thru_hole circle (at 3.81 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 9 thru_hole circle (at 6.35 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 10 thru_hole circle (at 8.89 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 11 thru_hole circle (at 11.43 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 12 thru_hole circle (at 13.97 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 13 thru_hole circle (at -13.97 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 14 thru_hole circle (at -11.43 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 15 thru_hole circle (at -8.89 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 16 thru_hole circle (at -6.35 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 17 thru_hole circle (at -3.81 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 18 thru_hole circle (at -1.27 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 19 thru_hole circle (at 1.27 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 20 thru_hole circle (at 3.81 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 21 thru_hole circle (at 6.35 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 22 thru_hole circle (at 8.89 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 23 thru_hole circle (at 11.43 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
(pad 24 thru_hole circle (at 13.97 7.62 0) (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.SilkS *.Mask))
)

View File

@@ -0,0 +1,45 @@
from pytest import fixture
from pathlib import Path
import pytest
import ergogen_footprint_converter.main as main
res_path = "tests/integration/data/simple/"
in_name = "promicro.kicad_mod"
@fixture
def out_dir (tmp_path):
out_dir = tmp_path / "out"
out_dir.mkdir()
return out_dir
def call_main (in_file, out_dir):
args = {
"in_file": Path(res_path + in_file),
"out": Path(out_dir)
}
main.main(args)
return out_dir / (in_name.split('.')[0] + ".js")
def test_not_exists(out_dir):
with pytest.raises(FileNotFoundError):
call_main("nop", out_dir)
def test_exists(out_dir):
out = call_main(in_name, out_dir)
assert out.exists()
def test_simple(out_dir):
out = call_main(in_name, out_dir)
expected = open("tests/integration/data/simple/promicro.js").readlines()
assert out.read_text() == expected

0
tests/unit/__init__.py Normal file
View File

View File

@@ -0,0 +1,75 @@
from ergogen_footprint_converter.converter import Converter
import pytest
from kiutils.footprint import Footprint
from kiutils.items.fpitems import FpText
@pytest.fixture
def main_rendered ():
return """
(module "test" (version 20211014) (generator kiutils)
(layer "F.Cu")
(tedit 64de79c2)
)
"""
@pytest.fixture
def footprint():
footprint = Footprint.create_new("test", "test")
footprint.tedit = "64de79c2"
footprint.graphicItems = []
return footprint
@pytest.fixture
def template() -> str:
return open("res/template.js").read()
strip = lambda s: ''.join([x.strip() for x in s.split()])
def test_empty_footprint(footprint, template, main_rendered):
converter = Converter(footprint)
res = strip(converter.convert().strip('\n').strip())
expected = strip(Converter.replace_template(template, "", main_rendered, "", ""))
assert res == expected
def test_contains_caption(footprint: Footprint, template: str, main_rendered):
first_item = FpText("Test 1")
second_item =FpText("Test 2")
captions = [first_item, second_item]
captions_str = ''.join(list(map(lambda x: x.to_sexpr().join('\n'), captions)))
print(captions_str)
footprint.graphicItems = captions
result = strip(Converter(footprint).convert())
expected = strip(Converter.replace_template(template, "", main_rendered, captions_str, ""))
assert result == expected
def test_replace_template(template: str):
replacements = {
"{% params %}": "one",
"{% main %}": "two",
"{% captions %}": "three",
"{% pins %}": "four"
}
expected = template
for key in replacements:
expected = expected.replace(key, replacements[key])
res = Converter.replace_template(template, "one", "two", "three", "four")
assert res == expected