ecadlib 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ecadlib/__init__.py +3 -0
- ecadlib/__main__.py +5 -0
- ecadlib/a365_sync.py +637 -0
- ecadlib/agent.py +1407 -0
- ecadlib/agent_queue.py +780 -0
- ecadlib/agent_watch.py +732 -0
- ecadlib/altium365.py +385 -0
- ecadlib/altium_dblib_sync.py +530 -0
- ecadlib/altium_export.py +209 -0
- ecadlib/altium_forms.py +768 -0
- ecadlib/altium_lib.py +1089 -0
- ecadlib/altium_menu.py +1751 -0
- ecadlib/altium_pkg.py +396 -0
- ecadlib/altium_project.py +1095 -0
- ecadlib/app.py +1458 -0
- ecadlib/approvals.py +395 -0
- ecadlib/assets/demo_bom.csv +8 -0
- ecadlib/assets/kicad_action_plugin.py +607 -0
- ecadlib/assets/kicad_reread.py +60 -0
- ecadlib/assets/pcblib_template.json.gz +0 -0
- ecadlib/bom_inputs.py +339 -0
- ecadlib/bom_triage.py +519 -0
- ecadlib/cad_model.py +528 -0
- ecadlib/cfb.py +171 -0
- ecadlib/cli.py +185 -0
- ecadlib/clihelp.py +149 -0
- ecadlib/cliutil.py +119 -0
- ecadlib/completion.py +173 -0
- ecadlib/costs.py +276 -0
- ecadlib/crosscheck.py +412 -0
- ecadlib/datasheet.py +2168 -0
- ecadlib/dedupe.py +572 -0
- ecadlib/detect.py +170 -0
- ecadlib/doctor.py +450 -0
- ecadlib/ds_route.py +541 -0
- ecadlib/eagle_export.py +1409 -0
- ecadlib/easy_setup.py +338 -0
- ecadlib/easyeda_convert.py +386 -0
- ecadlib/easyeda_pro_export.py +972 -0
- ecadlib/eval/__init__.py +0 -0
- ecadlib/eval/baselines/kicad-v1.json +404 -0
- ecadlib/eval/build_set.py +299 -0
- ecadlib/eval/cli.py +119 -0
- ecadlib/eval/gate.py +358 -0
- ecadlib/eval/golden/demo.txt +379 -0
- ecadlib/eval/golden/specs.txt +3120 -0
- ecadlib/eval/golden/testpack.txt +463 -0
- ecadlib/eval/golden.py +446 -0
- ecadlib/eval/replay/kicad-v1.json.gz +0 -0
- ecadlib/eval/runner.py +456 -0
- ecadlib/eval/score.py +179 -0
- ecadlib/eval/sets/kicad-v1.json +630 -0
- ecadlib/firstrun.py +400 -0
- ecadlib/footprint_adv.py +1006 -0
- ecadlib/footprint_bench.py +218 -0
- ecadlib/footprint_check.py +562 -0
- ecadlib/footprint_gen.py +582 -0
- ecadlib/footprint_rules.py +1092 -0
- ecadlib/forge.py +267 -0
- ecadlib/fp_share.py +95 -0
- ecadlib/fp_tidy.py +389 -0
- ecadlib/fsutil.py +62 -0
- ecadlib/house_style.py +1051 -0
- ecadlib/httpcache.py +124 -0
- ecadlib/hub.py +1490 -0
- ecadlib/hub_copy.py +161 -0
- ecadlib/hub_tools.py +79 -0
- ecadlib/kicad_dblib.py +257 -0
- ecadlib/kicad_export.py +837 -0
- ecadlib/kicad_import.py +913 -0
- ecadlib/kicad_index.py +160 -0
- ecadlib/kicad_menu.py +193 -0
- ecadlib/kicad_pcm.py +475 -0
- ecadlib/launcher.py +156 -0
- ecadlib/librarian.py +963 -0
- ecadlib/lifecycle.py +691 -0
- ecadlib/llm/__init__.py +276 -0
- ecadlib/llm/cli.py +70 -0
- ecadlib/llm/openai_compat.py +214 -0
- ecadlib/llm/profiles.py +245 -0
- ecadlib/llm/rails.py +570 -0
- ecadlib/llm/run.py +126 -0
- ecadlib/llm/sampling.py +49 -0
- ecadlib/mcp_server.py +723 -0
- ecadlib/messages.py +419 -0
- ecadlib/migrate.py +407 -0
- ecadlib/notify.py +329 -0
- ecadlib/package_schema.json +1238 -0
- ecadlib/package_specs/CP_EIA-3216-18_Kemet-A.json +27 -0
- ecadlib/package_specs/C_0402_1005Metric.json +22 -0
- ecadlib/package_specs/C_0805_2012Metric.json +22 -0
- ecadlib/package_specs/DFN-8-1EP_3x3mm_P0.5mm_EP1.66x2.38mm.json +40 -0
- ecadlib/package_specs/D_SMA.json +27 -0
- ecadlib/package_specs/D_SMB.json +27 -0
- ecadlib/package_specs/D_SOD-123.json +43 -0
- ecadlib/package_specs/D_SOD-323.json +43 -0
- ecadlib/package_specs/LED_0603_1608Metric.json +23 -0
- ecadlib/package_specs/LED_0805_2012Metric.json +23 -0
- ecadlib/package_specs/LQFP-48_7x7mm_P0.5mm.json +31 -0
- ecadlib/package_specs/L_0805_2012Metric.json +22 -0
- ecadlib/package_specs/MSOP-8_3x3mm_P0.65mm.json +31 -0
- ecadlib/package_specs/QFN-16-1EP_3x3mm_P0.5mm_EP1.75x1.75mm.json +39 -0
- ecadlib/package_specs/QFN-32-1EP_5x5mm_P0.5mm_EP3.45x3.45mm.json +37 -0
- ecadlib/package_specs/R_0402_1005Metric.json +22 -0
- ecadlib/package_specs/R_0603_1608Metric.json +22 -0
- ecadlib/package_specs/R_0805_2012Metric.json +22 -0
- ecadlib/package_specs/R_1206_3216Metric.json +22 -0
- ecadlib/package_specs/SOIC-16_3.9x9.9mm_P1.27mm.json +32 -0
- ecadlib/package_specs/SOIC-8_3.9x4.9mm_P1.27mm.json +32 -0
- ecadlib/package_specs/SOT-223-3_TabPin2.json +56 -0
- ecadlib/package_specs/SOT-23-5.json +57 -0
- ecadlib/package_specs/SOT-23-6.json +62 -0
- ecadlib/package_specs/SOT-23.json +48 -0
- ecadlib/package_specs/TSSOP-16_4.4x5mm_P0.65mm.json +31 -0
- ecadlib/package_specs/VQFN-16-1EP_3x3mm_P0.5mm_EP1.68x1.68mm.json +39 -0
- ecadlib/package_specs/advanced/BGA-100_11.0x11.0mm_Layout10x10_P1.0mm_Ball0.5mm_Pad0.4mm_NSMD.json +26 -0
- ecadlib/package_specs/advanced/BGA-121_9.0x9.0mm_Layout11x11_P0.8mm_Ball0.4mm_Pad0.35mm_NSMD.json +26 -0
- ecadlib/package_specs/advanced/BGA-144_7.0x7.0mm_Layout13x13_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD.json +29 -0
- ecadlib/package_specs/advanced/BGA-169_11.0x11.0mm_Layout13x13_P0.8mm_Ball0.5mm_Pad0.4mm_NSMD.json +23 -0
- ecadlib/package_specs/advanced/BGA-256_11.0x11.0mm_Layout20x20_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD.json +26 -0
- ecadlib/package_specs/advanced/BGA-256_17.0x17.0mm_Layout16x16_P1.0mm_Ball0.5mm_Pad0.4mm_NSMD.json +26 -0
- ecadlib/package_specs/advanced/BGA-324_15.0x15.0mm_Layout18x18_P0.8mm_Ball0.5mm_Pad0.4mm_NSMD.json +26 -0
- ecadlib/package_specs/advanced/BGA-36_3.396x3.466mm_Layout6x6_P0.4mm_Ball0.25mm_Pad0.2mm_NSMD.json +24 -0
- ecadlib/package_specs/advanced/BGA-672_27.0x27.0mm_Layout26x26_P1.0mm_Ball0.6mm_Pad0.5mm_NSMD.json +32 -0
- ecadlib/package_specs/advanced/BGA-68_5.0x5.0mm_Layout9x9_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD.json +30 -0
- ecadlib/package_specs/advanced/BGA-90_8.0x13.0mm_Layout2x3x15_P0.8mm.json +26 -0
- ecadlib/package_specs/advanced/DFN-8-1EP_3x3mm_P0.5mm_EP1.65x2.38mm_ThermalVias.json +60 -0
- ecadlib/package_specs/advanced/Example_Custom_LPad_Slot.json +24 -0
- ecadlib/package_specs/advanced/JST_GH_SM04B-GHS-TB_1x04-1MP_P1.25mm_Horizontal.json +49 -0
- ecadlib/package_specs/advanced/JST_PH_B4B-PH-K_1x04_P2.00mm_Vertical.json +33 -0
- ecadlib/package_specs/advanced/JST_PH_S4B-PH-SM4-TB_1x04-1MP_P2.00mm_Horizontal.json +55 -0
- ecadlib/package_specs/advanced/JST_SH_SM04B-SRSS-TB_1x04-1MP_P1.00mm_Horizontal.json +49 -0
- ecadlib/package_specs/advanced/JST_XH_B2B-XH-A_1x02_P2.50mm_Vertical.json +34 -0
- ecadlib/package_specs/advanced/JST_XH_B4B-XH-A_1x04_P2.50mm_Vertical.json +34 -0
- ecadlib/package_specs/advanced/JST_XH_B6B-XH-A_1x06_P2.50mm_Vertical.json +33 -0
- ecadlib/package_specs/advanced/LFBGA-144_10x10mm_Layout12x12_P0.8mm.json +23 -0
- ecadlib/package_specs/advanced/Molex_KK-254_AE-6410-04A_1x04_P2.54mm_Vertical.json +34 -0
- ecadlib/package_specs/advanced/Molex_PicoBlade_53398-0471_1x04-1MP_P1.25mm_Vertical.json +54 -0
- ecadlib/package_specs/advanced/PinHeader_1x05_P1.00mm_Vertical_SMD_Pin1Left.json +14 -0
- ecadlib/package_specs/advanced/PinHeader_1x05_P1.27mm_Vertical_SMD_Pin1Left.json +14 -0
- ecadlib/package_specs/advanced/PinHeader_1x05_P2.00mm_Vertical.json +12 -0
- ecadlib/package_specs/advanced/PinHeader_1x05_P2.00mm_Vertical_SMD_Pin1Left.json +14 -0
- ecadlib/package_specs/advanced/PinHeader_1x05_P2.54mm_Vertical.json +12 -0
- ecadlib/package_specs/advanced/PinHeader_1x05_P2.54mm_Vertical_SMD_Pin1Left.json +25 -0
- ecadlib/package_specs/advanced/PinHeader_1x05_P2.54mm_Vertical_SMD_Pin1Right.json +14 -0
- ecadlib/package_specs/advanced/PinHeader_1x10_P1.00mm_Vertical.json +15 -0
- ecadlib/package_specs/advanced/PinHeader_2x05_P1.00mm_Vertical_SMD.json +13 -0
- ecadlib/package_specs/advanced/PinHeader_2x05_P1.27mm_Vertical.json +18 -0
- ecadlib/package_specs/advanced/PinHeader_2x05_P1.27mm_Vertical_SMD.json +24 -0
- ecadlib/package_specs/advanced/PinHeader_2x05_P2.00mm_Vertical_SMD.json +13 -0
- ecadlib/package_specs/advanced/PinHeader_2x05_P2.54mm_Vertical.json +12 -0
- ecadlib/package_specs/advanced/PinHeader_2x05_P2.54mm_Vertical_SMD.json +24 -0
- ecadlib/package_specs/advanced/PinHeader_2x20_P2.54mm_Vertical.json +12 -0
- ecadlib/package_specs/advanced/QFN-16-1EP_3x3mm_P0.5mm_EP1.75x1.75mm_ThermalVias.json +87 -0
- ecadlib/package_specs/advanced/UFBGA-132_7x7mm_Layout12x12_P0.5mm.json +43 -0
- ecadlib/package_specs/advanced/USB_C_Receptacle_HRO_TYPE-C-31-M-12.json +218 -0
- ecadlib/package_specs/standard.json.gz +0 -0
- ecadlib/parity.py +1204 -0
- ecadlib/parts_export.py +154 -0
- ecadlib/partview.py +215 -0
- ecadlib/plugins.py +231 -0
- ecadlib/providers.py +226 -0
- ecadlib/rebuild.py +471 -0
- ecadlib/release_check.py +437 -0
- ecadlib/reports.py +274 -0
- ecadlib/resolve.py +556 -0
- ecadlib/review_cli.py +292 -0
- ecadlib/rules.py +979 -0
- ecadlib/rules_infer.py +1205 -0
- ecadlib/sexpr.py +149 -0
- ecadlib/sheetsafe.py +65 -0
- ecadlib/sim_models.py +1087 -0
- ecadlib/sourcing.py +1161 -0
- ecadlib/stdparts.py +755 -0
- ecadlib/step_gen.py +929 -0
- ecadlib/symbol_layout.py +798 -0
- ecadlib/team.py +558 -0
- ecadlib/team_setup.py +288 -0
- ecadlib/term.py +420 -0
- ecadlib/term_views.py +64 -0
- ecadlib/testpack.py +334 -0
- ecadlib/ui/__init__.py +1 -0
- ecadlib/ui/__main__.py +3 -0
- ecadlib/ui/server.py +1029 -0
- ecadlib/ui/static/hairline.js +2 -0
- ecadlib/ui/static/index.html +2098 -0
- ecadlib/ui/static/setup.html +294 -0
- ecadlib/upkeep.py +717 -0
- ecadlib/upstream.py +573 -0
- ecadlib/userconfig.py +257 -0
- ecadlib/visual_check.py +1603 -0
- ecadlib/ws_status.py +203 -0
- ecadlib-0.3.0.dist-info/METADATA +2245 -0
- ecadlib-0.3.0.dist-info/RECORD +199 -0
- ecadlib-0.3.0.dist-info/WHEEL +5 -0
- ecadlib-0.3.0.dist-info/entry_points.txt +2 -0
- ecadlib-0.3.0.dist-info/licenses/LICENSE +21 -0
- ecadlib-0.3.0.dist-info/licenses/NOTICE +53 -0
- ecadlib-0.3.0.dist-info/top_level.txt +1 -0
ecadlib/testpack.py
ADDED
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
"""A small Altium + KiCad + Fusion + EasyEDA test pack for a person to open in the real tools (no network, no keys).
|
|
2
|
+
|
|
3
|
+
ecadlib testpack --out DIR [--name LIB] [--json]
|
|
4
|
+
|
|
5
|
+
Six representative parts go through the same pipeline a BOM does (librarian -> release check -> exports):
|
|
6
|
+
|
|
7
|
+
R1 RC0603FR-0710KL 0603 chip resistor footprint + symbol from ecadlib's standard parts (stdparts)
|
|
8
|
+
Q1 MMBT3904LT1G SOT-23 NPN transistor footprint + symbol (1=B 2=E 3=C) from stdparts
|
|
9
|
+
U1 LM358DR SOIC-8 dual op amp footprint from stdparts, symbol from the pack's own library
|
|
10
|
+
U2 TPS62133RGTR VQFN-16 with exposed pad footprint from package_specs (pad 17 = EP), symbol from the pack library
|
|
11
|
+
J1 TYPE-C-31-M-12 USB-C receptacle (HRO) footprint from package_specs/advanced (stacked pads, slots, NPTH)
|
|
12
|
+
J2 61300511121 1x05 2.54 mm THT header footprint from package_specs/advanced (drilled pads, square pin 1)
|
|
13
|
+
|
|
14
|
+
DIR gets:
|
|
15
|
+
CHECKLIST.md what to open in Altium Designer, Altium 365, KiCad, Fusion and EasyEDA Pro, and what each part
|
|
16
|
+
must look like
|
|
17
|
+
bom.csv the BOM the pack was built from
|
|
18
|
+
source/ the pack's own "existing library" (.kicad_sym + .pretty), used like a user's --lib
|
|
19
|
+
library/ the librarian's output: plan.json, review.md, release_check.md, models/ (SchLib, PcbLib, KiCad)
|
|
20
|
+
altium/ a Database Library: testpack.DbLib + .xlsx/.csv + SchLib/ + PcbLib/ (ecadlib dblib-sync)
|
|
21
|
+
kicad/ testpack.kicad_sym, testpack.pretty, testpack.3dshapes, fp-lib-table, sym-lib-table, and the
|
|
22
|
+
database library testpack.kicad_dbl + testpack.sqlite (ecadlib kicad-export / kicad-dblib)
|
|
23
|
+
fusion/ testpack.lbr for Autodesk Fusion Electronics / EAGLE, its STEP models, INSTALL.md and one zip
|
|
24
|
+
(ecadlib eagle-export)
|
|
25
|
+
easyeda/ testpack.elibz for EasyEDA Pro, its STEP models, INSTALL.md and one zip (ecadlib easyeda-export)
|
|
26
|
+
testpack.json what was built, for scripts
|
|
27
|
+
|
|
28
|
+
The pin maps of U1, U2 and J1 are written from the manufacturers' datasheets (and match KiCad's own symbols for the
|
|
29
|
+
same parts); the checklist still asks for a look against the datasheet, as for any library part."""
|
|
30
|
+
import argparse, json, sys
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
from . import footprint_gen as F, sexpr
|
|
34
|
+
|
|
35
|
+
NAME = "testpack"
|
|
36
|
+
DS = {"R1": "https://www.yageo.com/upload/media/product/productsearch/datasheet/rchip/PYu-RC_Group_51_RoHS_L_12.pdf",
|
|
37
|
+
"Q1": "https://www.onsemi.com/pdf/datasheet/mmbt3904lt1-d.pdf",
|
|
38
|
+
"U1": "https://www.ti.com/lit/ds/symlink/lm358.pdf",
|
|
39
|
+
"U2": "https://www.ti.com/lit/ds/symlink/tps62130.pdf",
|
|
40
|
+
"J1": "https://www.lcsc.com/datasheet/C165948.pdf",
|
|
41
|
+
"J2": "https://www.we-online.com/components/products/datasheet/61300511121.pdf"}
|
|
42
|
+
# designator, value, BOM footprint, manufacturer, MPN, description
|
|
43
|
+
PARTS = [
|
|
44
|
+
("R1", "10k", "R_0603_1608Metric", "Yageo", "RC0603FR-0710KL", "Resistor 10k 1% 0.1W 0603"),
|
|
45
|
+
("Q1", "MMBT3904", "SOT-23", "onsemi", "MMBT3904LT1G", "NPN transistor 40V 200mA SOT-23"),
|
|
46
|
+
("U1", "LM358DR", "SOIC-8_3.9x4.9mm_P1.27mm", "Texas Instruments", "LM358DR", "Dual operational amplifier SOIC-8"),
|
|
47
|
+
("U2", "TPS62133RGTR", "VQFN-16-1EP_3x3mm_P0.5mm_EP1.68x1.68mm", "Texas Instruments", "TPS62133RGTR",
|
|
48
|
+
"3-17V 3A step-down converter 3.3V VQFN-16 3x3mm with exposed pad"),
|
|
49
|
+
("J1", "USB-C", "USB_C_Receptacle_HRO_TYPE-C-31-M-12", "Korean Hroparts Elec", "TYPE-C-31-M-12",
|
|
50
|
+
"USB 2.0 Type-C receptacle, 16 pins, SMD with THT shell pegs"),
|
|
51
|
+
("J2", "Conn_01x05", "PinHeader_1x05_P2.54mm_Vertical", "Wurth Elektronik", "61300511121",
|
|
52
|
+
"Pin header 1x05 2.54mm vertical, through hole"),
|
|
53
|
+
]
|
|
54
|
+
# the pack's own symbols (name -> pins: num, name, electrical type, side); footprints of J1 / J2 go in source/ too
|
|
55
|
+
SYMBOLS = {
|
|
56
|
+
"LM358DR": ("U", "SOIC-8_3.9x4.9mm_P1.27mm", [
|
|
57
|
+
("3", "+IN_A", "input", "L"), ("2", "-IN_A", "input", "L"), ("5", "+IN_B", "input", "L"), ("6", "-IN_B", "input", "L"),
|
|
58
|
+
("8", "V+", "power_in", "L"), ("1", "OUT_A", "output", "R"), ("7", "OUT_B", "output", "R"), ("4", "V-", "power_in", "R")]),
|
|
59
|
+
"TPS62133RGTR": ("U", "VQFN-16-1EP_3x3mm_P0.5mm_EP1.68x1.68mm", [
|
|
60
|
+
("11", "PVIN", "power_in", "L"), ("12", "PVIN", "power_in", "L"), ("10", "AVIN", "power_in", "L"),
|
|
61
|
+
("13", "EN", "input", "L"), ("9", "SS/TR", "input", "L"), ("7", "FSW", "input", "L"), ("8", "DEF", "input", "L"),
|
|
62
|
+
("6", "AGND", "power_in", "L"), ("1", "SW", "output", "R"), ("2", "SW", "output", "R"), ("3", "SW", "output", "R"),
|
|
63
|
+
("14", "VOS", "input", "R"), ("5", "FB", "input", "R"), ("4", "PG", "open_collector", "R"),
|
|
64
|
+
("15", "PGND", "power_in", "R"), ("16", "PGND", "power_in", "R"), ("17", "EP", "power_in", "R")]),
|
|
65
|
+
"TYPE-C-31-M-12": ("J", "USB_C_Receptacle_HRO_TYPE-C-31-M-12", [
|
|
66
|
+
("A4", "VBUS", "power_in", "L"), ("A9", "VBUS", "power_in", "L"), ("B4", "VBUS", "power_in", "L"),
|
|
67
|
+
("B9", "VBUS", "power_in", "L"), ("A5", "CC1", "bidirectional", "L"), ("B5", "CC2", "bidirectional", "L"),
|
|
68
|
+
("A6", "D+", "bidirectional", "L"), ("B6", "D+", "bidirectional", "L"), ("A7", "D-", "bidirectional", "L"),
|
|
69
|
+
("B7", "D-", "bidirectional", "L"), ("A8", "SBU1", "bidirectional", "R"), ("B8", "SBU2", "bidirectional", "R"),
|
|
70
|
+
("A1", "GND", "power_in", "R"), ("A12", "GND", "power_in", "R"), ("B1", "GND", "power_in", "R"),
|
|
71
|
+
("B12", "GND", "power_in", "R"), ("SH", "SHIELD", "passive", "R")]),
|
|
72
|
+
"61300511121": ("J", "PinHeader_1x05_P2.54mm_Vertical", [(str(i), f"Pin_{i}", "passive", "L") for i in range(1, 6)]),
|
|
73
|
+
}
|
|
74
|
+
WIN_DIR = r"C:\ecadlib\testpack" # where the checklist asks for the altium folder (the .DbLib's Excel path)
|
|
75
|
+
LOCAL_FP = ("USB_C_Receptacle_HRO_TYPE-C-31-M-12", "PinHeader_1x05_P2.54mm_Vertical", "VQFN-16-1EP_3x3mm_P0.5mm_EP1.68x1.68mm")
|
|
76
|
+
# what a person must see, per part (CHECKLIST.md)
|
|
77
|
+
EXPECT = { # checked against the generated .kicad_mod by tests/test_testpack.py
|
|
78
|
+
"RES-0603-10k": "2 pads 0.8 x 0.95 mm, centres 1.65 mm apart; symbol: 2-pin resistor",
|
|
79
|
+
"MMBT3904LT1G": "3 pads 1.475 x 0.6 mm: 1 and 2 on the left 1.9 mm apart, 3 alone on the right, rows 1.875 mm apart; "
|
|
80
|
+
"symbol pin 1 = B, 2 = E, 3 = C",
|
|
81
|
+
"LM358DR": "8 pads 1.95 x 0.6 mm in two rows of 4, 1.27 mm pitch, rows 4.95 mm apart (centres), pin 1 top-left; "
|
|
82
|
+
"symbol: 8 pins, 1 = OUT_A, 4 = V-, 8 = V+",
|
|
83
|
+
"TPS62133RGTR": "16 pads 0.835 x 0.25 mm, 0.5 mm pitch, 4 per side, pin 1 top of the left side; pad 17 = exposed pad "
|
|
84
|
+
"1.68 x 1.68 mm in the centre with 4 paste windows; symbol pin 17 = EP",
|
|
85
|
+
"TYPE-C-31-M-12": "16 numbered SMD pads on 12 positions (A1/B12, A4/B9, A9/B4, A12/B1 stacked), 4 shell pads SH with "
|
|
86
|
+
"slotted drills, 2 unplated 0.65 mm holes; symbol has every A/B pin and SHIELD",
|
|
87
|
+
"61300511121": "5 through-hole pads 1.7 mm, 1.0 mm drill, 2.54 mm pitch in a line; pad 1 square; symbol pins 1-5",
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# ---------------------------------------------------------------- the pack's own KiCad library (source/)
|
|
92
|
+
def kicad_symbol(name, ref, footprint, pins, desc=""):
|
|
93
|
+
"""One KiCad symbol: rectangle body, pins left (L) and right (R) on a 100 mil grid."""
|
|
94
|
+
q = sexpr.quote
|
|
95
|
+
left, right = [p for p in pins if p[3] == "L"], [p for p in pins if p[3] == "R"]
|
|
96
|
+
rows = max(len(left), len(right), 1)
|
|
97
|
+
top, bot = 2.54 * ((rows + 1) // 2), 2.54 * ((rows + 1) // 2 - rows - 1)
|
|
98
|
+
w = 7.62 if max([len(p[1]) for p in pins] or [1]) < 6 else 10.16
|
|
99
|
+
font = "(effects (font (size 1.27 1.27)))"
|
|
100
|
+
prop = lambda k, v, y, hide=False: (f' (property {q(k)} {q(v)} (at 0 {y:g} 0) (effects (font (size 1.27 1.27))'
|
|
101
|
+
f'{" hide" if hide else ""}))')
|
|
102
|
+
out = [f" (symbol {q(name)} (in_bom yes) (on_board yes)",
|
|
103
|
+
prop("Reference", ref, top + 1.27), prop("Value", name, bot - 1.27), prop("Footprint", f"{NAME}:{footprint}", 0, True),
|
|
104
|
+
prop("Datasheet", "~", 0, True), prop("Description", desc, 0, True),
|
|
105
|
+
f' (symbol "{name}_0_1" (rectangle (start {-w:g} {top:g}) (end {w:g} {bot:g}) (stroke (width 0.254) (type default)) '
|
|
106
|
+
f'(fill (type background))))', f' (symbol "{name}_1_1"']
|
|
107
|
+
for side, ps, x, rot in (("L", left, -w - 2.54, 0), ("R", right, w + 2.54, 180)):
|
|
108
|
+
for i, (num, pname, typ, _) in enumerate(ps):
|
|
109
|
+
y = round(top - 2.54 * (i + 1), 2)
|
|
110
|
+
out.append(f" (pin {typ} line (at {x:g} {y:g} {rot}) (length 2.54) (name {q(pname)} {font}) "
|
|
111
|
+
f"(number {q(num)} {font}))")
|
|
112
|
+
out.append(" ))")
|
|
113
|
+
return "\n".join(out)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def write_source(d):
|
|
117
|
+
"""source/testpack.kicad_sym and source/testpack.pretty: the pack's 'existing library'."""
|
|
118
|
+
from .footprint_bench import spec_path
|
|
119
|
+
d = Path(d)
|
|
120
|
+
pretty = d / f"{NAME}.pretty"
|
|
121
|
+
pretty.mkdir(parents=True, exist_ok=True)
|
|
122
|
+
for n in LOCAL_FP:
|
|
123
|
+
text, _ = F.build(json.loads(spec_path(n).read_text(encoding="utf-8")))
|
|
124
|
+
(pretty / f"{n}.kicad_mod").write_text(text, encoding="utf-8", newline="\n")
|
|
125
|
+
syms = [kicad_symbol(n, ref, fp, pins, next((p[5] for p in PARTS if p[4] == n), "")) for n, (ref, fp, pins) in SYMBOLS.items()]
|
|
126
|
+
text = '(kicad_symbol_lib (version 20220914) (generator "ecadlib_testpack")\n' + "\n".join(syms) + "\n)\n"
|
|
127
|
+
sexpr.parse(text)
|
|
128
|
+
(d / f"{NAME}.kicad_sym").write_text(text, encoding="utf-8", newline="\n")
|
|
129
|
+
return d
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def write_bom(path):
|
|
133
|
+
import csv
|
|
134
|
+
with open(path, "w", newline="", encoding="utf-8") as f:
|
|
135
|
+
w = csv.writer(f)
|
|
136
|
+
w.writerow(["Designator", "Qty", "Value", "Footprint", "LibRef", "Manufacturer", "Mfr. Part #", "Description", "Datasheet"])
|
|
137
|
+
for des, val, fp, mfr, mpn, desc in PARTS:
|
|
138
|
+
w.writerow([des, 1, val, fp, mpn if mpn in SYMBOLS else "", mfr, mpn, desc, DS[des]])
|
|
139
|
+
return Path(path)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# ---------------------------------------------------------------- build
|
|
143
|
+
def build(out, name=NAME, log=print):
|
|
144
|
+
"""Write the whole pack into `out`. Returns the summary dict (also testpack.json)."""
|
|
145
|
+
from . import altium_dblib_sync as DS_, kicad_dblib as KD, kicad_export as X, librarian as L, parts_export as PE
|
|
146
|
+
out = Path(out)
|
|
147
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
148
|
+
src = write_source(out / "source")
|
|
149
|
+
bom = write_bom(out / "bom.csv")
|
|
150
|
+
quiet = lambda *a, **k: None
|
|
151
|
+
items = L.run(str(bom), [str(src)], out / "library", ["standard"], None, False, [], True, out / "library" / "cache",
|
|
152
|
+
0.0, [], log=quiet, gen_step=True)
|
|
153
|
+
plan = out / "library" / "plan.json"
|
|
154
|
+
held = {i["name"]: i.get("hold_reasons") for i in items if i["status"] != "ready"}
|
|
155
|
+
kx = X.export(plan, out / "kicad", name, libs=[str(src)], log=quiet)
|
|
156
|
+
kd = KD.build(plan, out / "kicad", name, log=quiet)
|
|
157
|
+
ad = DS_.sync(plan, out / "altium", name=name, win_dir=WIN_DIR)
|
|
158
|
+
PE.write(PE.rows(plan, name), out / "library" / "parts.csv", "csv")
|
|
159
|
+
from . import parity as PY # both libraries read back and compared, part by part and row by row
|
|
160
|
+
py = PY.check_plan(plan, out / "kicad", name, out / "altium")
|
|
161
|
+
PY.write_report(py, out / "parity")
|
|
162
|
+
from . import eagle_export as EG, easyeda_pro_export as EZ # from the KiCad files above, each read back pad by pad
|
|
163
|
+
other = {}
|
|
164
|
+
for key, mod in (("fusion", EG), ("easyeda", EZ)):
|
|
165
|
+
r = mod.export(plan, out / key, name, kicad_dir=out / "kicad", log=quiet, second_reader=False)
|
|
166
|
+
other[key] = {"file": f"{key}/{r['file']}", "bundle": f"{key}/{r['bundle']}", "counts": r["counts"],
|
|
167
|
+
"errors": r["errors"], "approximations": len(r["warnings"]), "models": len(r["models"])}
|
|
168
|
+
parts = []
|
|
169
|
+
for it in items:
|
|
170
|
+
parts.append({"part": it["name"], "status": it["status"], "symbol": (it.get("symbol") or {}).get("ref"),
|
|
171
|
+
"footprint": (it.get("footprint") or {}).get("ref"), "source": it.get("source"),
|
|
172
|
+
"expect": EXPECT.get(it["name"], ""), "hold_reasons": it.get("hold_reasons") or []})
|
|
173
|
+
tables = sorted({str(i.get("folder") or "").rstrip("/").split("/")[-1] for i in items if i["status"] == "ready"})
|
|
174
|
+
summary = {"name": name, "tables": tables, "parts": parts, "held": held, "kicad": {"symbols": kx.get("symbols"),
|
|
175
|
+
"footprints": sorted(kx.get("footprints") or {})}, "kicad_dblib": kd.get("dblib") if isinstance(kd, dict) else None,
|
|
176
|
+
"altium": {"dblib": str(Path(ad["dblib"]).relative_to(out).as_posix()) if ad.get("dblib") else None},
|
|
177
|
+
"parity": {**py["counts"], "database_rows": py.get("database_rows"), "report": "parity.md"}, **other}
|
|
178
|
+
(out / "testpack.json").write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8")
|
|
179
|
+
(out / "CHECKLIST.md").write_text(checklist(summary), encoding="utf-8")
|
|
180
|
+
return summary
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _other(s, key):
|
|
184
|
+
o = s.get(key) or {}
|
|
185
|
+
if not o:
|
|
186
|
+
return f"{key} not written"
|
|
187
|
+
return (f"{o['file']} {'read back the same' if not o['errors'] else 'read back with ' + str(len(o['errors'])) + ' difference(s)'}"
|
|
188
|
+
f" ({o['approximations']} approximation(s) listed in its report)")
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def checklist(s):
|
|
192
|
+
n, win, tables = s["name"], WIN_DIR, ", ".join(s.get("tables") or [])
|
|
193
|
+
rows = "\n".join(f"| {p['part']} | {p['footprint']} | {p['symbol']} | {p['expect']} |" for p in s["parts"])
|
|
194
|
+
from . import messages as M
|
|
195
|
+
held = "".join(f"\n* **{k}** is held, so it is left out of the exports (tell us): "
|
|
196
|
+
+ " ".join(e["text"] for e in M.explain_all(v or [])) for k, v in s["held"].items())
|
|
197
|
+
sts = [p["status"] for p in s["parts"]]
|
|
198
|
+
py = s.get("parity") or {}
|
|
199
|
+
return f"""# ecadlib test pack: what to open and what you should see
|
|
200
|
+
|
|
201
|
+
**{M.plural(len(sts), 'part')}: {M.counts(sts)}.** KiCad and Altium match for {py.get('match', '?')} of them.
|
|
202
|
+
Next: go through steps 1-5 below and report anything that does not match (a screenshot and the step number are enough).
|
|
203
|
+
|
|
204
|
+
This folder was written by `ecadlib testpack` with no network and no API keys. Nothing in it has been opened in
|
|
205
|
+
Altium Designer, Altium 365, KiCad, Fusion or EasyEDA Pro by ecadlib's own tests (the end of this file says what was checked).
|
|
206
|
+
|
|
207
|
+
## The six parts
|
|
208
|
+
|
|
209
|
+
| Part | Footprint | Symbol | What it must look like |
|
|
210
|
+
|---|---|---|---|
|
|
211
|
+
{rows}
|
|
212
|
+
{held}
|
|
213
|
+
|
|
214
|
+
Check every part against its datasheet link (the Datasheet parameter) as you would any new library part.
|
|
215
|
+
|
|
216
|
+
## 1. Altium Designer: the Database Library (`altium/`)
|
|
217
|
+
|
|
218
|
+
1. Copy the contents of the `altium` folder to `{win}` on the Windows PC (the .DbLib's Excel connection points there;
|
|
219
|
+
elsewhere, Altium asks for the data source: point it at `{n}.xlsx` next to the .DbLib, Excel / Microsoft.ACE OLE DB
|
|
220
|
+
provider, and click **Connect**).
|
|
221
|
+
2. Open `{win}\\{n}.DbLib`.
|
|
222
|
+
* You should see one table per category ({tables}), one row per part.
|
|
223
|
+
* Library Path / Footprint Path are relative: the `SchLib\\` and `PcbLib\\` folders next to the .DbLib.
|
|
224
|
+
3. In the **Components** panel pick the library, then each part:
|
|
225
|
+
* the symbol preview draws, with the pin numbers listed in the table above;
|
|
226
|
+
* the footprint preview draws (2D and 3D), and the 3D body sits on the pads at a sensible height;
|
|
227
|
+
* the parameters Manufacturer, Manufacturer Part Number and the Datasheet link (ComponentLink2URL) are filled.
|
|
228
|
+
4. Place every part on a new schematic, **Design > Update PCB Document** into a new PCB: every footprint arrives with
|
|
229
|
+
no "footprint not found" message. Run **Tools > Design Rule Check**: no violations between pads of one part,
|
|
230
|
+
except on the USB-C receptacle if its stacked pads (A1/B12 GND, A4/B9 VBUS ...) are left on different nets.
|
|
231
|
+
5. Open each `.PcbLib` in `altium/PcbLib/` directly: one footprint each, pads as in the table; the courtyard is on
|
|
232
|
+
Mechanical 15 and the assembly outline on Mechanical 13 (ecadlib's default layers).
|
|
233
|
+
6. Open each `.SchLib` in `altium/SchLib/` directly: one symbol each; pin names as in the table.
|
|
234
|
+
|
|
235
|
+
## 2. Altium 365
|
|
236
|
+
|
|
237
|
+
Nothing was sent to Altium 365. To try it: in Altium Designer, with the workspace signed in, open each `.SchLib` /
|
|
238
|
+
`.PcbLib`, then **Right-click > Release** (or **File > Save to Server**) into a test folder, then make a component
|
|
239
|
+
from them in the Component Editor. Or, with a Nexar token, from a shell:
|
|
240
|
+
`ecadlib altium365 push library/plan.json --workspace <your workspace URL> --dry-run` (shows the exact requests;
|
|
241
|
+
remove --dry-run to release into the workspace for real, into Components/<category>).
|
|
242
|
+
|
|
243
|
+
## 3. KiCad 8 or later (`kicad/`)
|
|
244
|
+
|
|
245
|
+
The KiCad files are in KiCad 8 format: KiCad 7 refuses to open them (it will say the library is from a newer version).
|
|
246
|
+
|
|
247
|
+
1. Create an empty KiCad project, then copy everything in `kicad/` into the project folder. The project now has
|
|
248
|
+
`{n}` in both library tables (**Preferences > Manage Symbol / Footprint Libraries > Project Specific Libraries**),
|
|
249
|
+
with paths `${{KIPRJMOD}}/{n}.kicad_sym` and `${{KIPRJMOD}}/{n}.pretty`.
|
|
250
|
+
2. Symbol editor: library `{n}` lists the six parts; each opens and draws; **Inspect > Symbol Checker**: no errors.
|
|
251
|
+
3. Footprint editor: library `{n}` lists the footprints; open each: pads as in the table above, courtyard on F.Courtyard,
|
|
252
|
+
**Inspect > Footprint Checker**: note anything it lists. Expected for the USB-C receptacle only: its stacked pads
|
|
253
|
+
(A1/B12, A4/B9, A9/B4, A12/B1) overlap and its four shell pads share the number SH, as in KiCad's own footprint.
|
|
254
|
+
4. **3D viewer** (footprint editor or the board): every footprint has a 3D model from `{n}.3dshapes/`, the same one the
|
|
255
|
+
Altium footprint carries (ecadlib's generated STEP; for the USB-C receptacle its Altium extruded body as a STEP).
|
|
256
|
+
5. In the schematic, place all six parts, assign footprints (already set), **Update PCB from Schematic**: nothing
|
|
257
|
+
missing. In the PCB, **Inspect > Design Rules Checker** with *Test for parity between PCB and schematic*: no
|
|
258
|
+
library errors ("footprint not found in library" or "doesn't match library").
|
|
259
|
+
6. Database library (optional; needs an SQLite ODBC driver, see `ecadlib kicad-dblib -h`): the `{n}-db` row in the
|
|
260
|
+
symbol table opens `{n}.kicad_dbl`; the parts appear grouped by category with Value, Manufacturer and MPN columns.
|
|
261
|
+
|
|
262
|
+
## 4. Autodesk Fusion Electronics or EAGLE 9 (`fusion/`)
|
|
263
|
+
|
|
264
|
+
`fusion/INSTALL.md` has the import steps; `fusion/{n}_fusion.zip` is the same folder as one file.
|
|
265
|
+
|
|
266
|
+
1. Fusion: upload `fusion/{n}.lbr` to a project (Data Panel > Upload) and open it, or import it in an electronics
|
|
267
|
+
design's Library Manager. EAGLE 9: **Library > Open** `fusion/{n}.lbr`.
|
|
268
|
+
2. The library lists six device sets named after the parts (column 1 of the table above) and six packages named after
|
|
269
|
+
the footprints. Each device set has one device with its package, and **Connect** shows every pin on a pad.
|
|
270
|
+
3. Open each package: the pads as in the table above, the silkscreen on tPlace, the courtyard on tKeepout, the
|
|
271
|
+
assembly outline on tDocu. USB-C: four shell pads with slots (the slot outline is on the Milling layer, 46) and
|
|
272
|
+
two non-plated holes; stacked pads A1/B12 GND and A4/B9 VBUS are separate pads, as in the KiCad footprint.
|
|
273
|
+
4. Open each device set's attributes: MPN, MANUFACTURER, DATASHEET, DESCRIPTION, PACKAGE and CATEGORY_L1 are filled
|
|
274
|
+
(R1's value is typed when it is placed, as in Eagle's own resistors).
|
|
275
|
+
5. Place all six in a schematic, switch to the board: every package arrives; run DRC: no errors between pads of one
|
|
276
|
+
part (the USB-C stacked pads aside, as in Altium).
|
|
277
|
+
6. Optional: attach the STEP files in `fusion/{n}.3dshapes/` as 3D packages (INSTALL.md's table says which file goes
|
|
278
|
+
on which package) and check the 3D view: each body sits on its pads.
|
|
279
|
+
|
|
280
|
+
## 5. EasyEDA Pro (`easyeda/`)
|
|
281
|
+
|
|
282
|
+
`easyeda/INSTALL.md` has the import steps; `easyeda/{n}_easyeda.zip` is the same folder as one file.
|
|
283
|
+
|
|
284
|
+
1. In EasyEDA Pro (the web version is enough): **File > Import > EasyEDA Pro**, choose `easyeda/{n}.elibz` and a
|
|
285
|
+
personal library to import into.
|
|
286
|
+
2. The library lists six devices named after the parts, each with its symbol and footprint linked.
|
|
287
|
+
3. Open each footprint: the pads as in the table above, top silkscreen, the courtyard; the exposed pad of U2 (pad 17)
|
|
288
|
+
has its paste split into windows as in the KiCad footprint. Open each symbol: the pin numbers and names in the table
|
|
289
|
+
(overbars are written with a trailing #).
|
|
290
|
+
4. Device attributes: Manufacturer, Manufacturer Part, Datasheet; Designator R?, Q?, U?, J?.
|
|
291
|
+
5. Place all six in a schematic and update the PCB from it: every footprint arrives; run DRC.
|
|
292
|
+
6. Optional: import the STEP files in `easyeda/{n}.3dshapes/` as 3D models and attach each to its footprint (INSTALL.md
|
|
293
|
+
says which), then check the 3D view.
|
|
294
|
+
|
|
295
|
+
## What ecadlib itself checked before writing this pack
|
|
296
|
+
|
|
297
|
+
* the release check passed for every part not listed as held above (pins match pads, footprint checks, 3D height,
|
|
298
|
+
fields, files read back);
|
|
299
|
+
* KiCad and Altium match (`ecadlib parity`, report `parity.md`): {s.get('parity', {}).get('match', '?')} of {len(s['parts'])} parts
|
|
300
|
+
read back the same from the KiCad and the Altium files (pads, drills, paste, courtyard, 3D model, pins, fields), and
|
|
301
|
+
the KiCad database rows match the DbLib rows;
|
|
302
|
+
* the Fusion / EAGLE and EasyEDA Pro libraries were read back by ecadlib's own readers and compared with the KiCad
|
|
303
|
+
footprints pad by pad and the symbols pin by pin: {_other(s, 'fusion')}; {_other(s, 'easyeda')}. In ecadlib's
|
|
304
|
+
CI, KiCad 8's own Eagle and EasyEDA Pro importers read libraries like these as a second check. Neither was opened in
|
|
305
|
+
Fusion or EasyEDA Pro;
|
|
306
|
+
* `tests/test_e2e.py` (run in ecadlib's own test suite) loads a pack like this one with KiCad 7's kicad-cli and pcbnew
|
|
307
|
+
(after converting to KiCad 7 syntax), places every footprint through the library table and runs DRC, and reads every
|
|
308
|
+
.PcbLib / .SchLib back with pyaltiumlib and ecadlib's own reader. That is all file-level: the real Altium checks are
|
|
309
|
+
the steps above.
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def main(argv=None):
|
|
314
|
+
ap = argparse.ArgumentParser(prog="ecadlib testpack",
|
|
315
|
+
description="Write a small Altium + KiCad test pack (6 representative parts) and a CHECKLIST.md "
|
|
316
|
+
"saying what to open in Altium Designer / Altium 365 / KiCad and what to see.",
|
|
317
|
+
epilog="example: ecadlib testpack --out testpack (then open testpack/CHECKLIST.md)")
|
|
318
|
+
ap.add_argument("--out", "-o", default="ecadlib_testpack", help="folder to write (default %(default)s)")
|
|
319
|
+
ap.add_argument("--name", default=NAME, help="library name in the pack (default %(default)s)")
|
|
320
|
+
ap.add_argument("--json", action="store_true", help="print the summary as JSON")
|
|
321
|
+
a = ap.parse_args(argv)
|
|
322
|
+
s = build(a.out, a.name)
|
|
323
|
+
if a.json:
|
|
324
|
+
print(json.dumps(s, indent=2))
|
|
325
|
+
return 0 if not s["held"] else 2
|
|
326
|
+
for p in s["parts"]:
|
|
327
|
+
print(f" {p['status']:<6} {p['part']:<18} fp={p['footprint']:<40} sym={p['symbol']}")
|
|
328
|
+
print(f"{len(s['parts'])} parts, {len(s['held'])} held -> {Path(a.out) / 'CHECKLIST.md'} "
|
|
329
|
+
f"(Altium: {Path(a.out) / 'altium'}, KiCad: {Path(a.out) / 'kicad'})")
|
|
330
|
+
return 0 if not s["held"] else 2
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
if __name__ == "__main__":
|
|
334
|
+
sys.exit(main())
|
ecadlib/ui/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Local web UI for ecadlib."""
|
ecadlib/ui/__main__.py
ADDED