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.
Files changed (199) hide show
  1. ecadlib/__init__.py +3 -0
  2. ecadlib/__main__.py +5 -0
  3. ecadlib/a365_sync.py +637 -0
  4. ecadlib/agent.py +1407 -0
  5. ecadlib/agent_queue.py +780 -0
  6. ecadlib/agent_watch.py +732 -0
  7. ecadlib/altium365.py +385 -0
  8. ecadlib/altium_dblib_sync.py +530 -0
  9. ecadlib/altium_export.py +209 -0
  10. ecadlib/altium_forms.py +768 -0
  11. ecadlib/altium_lib.py +1089 -0
  12. ecadlib/altium_menu.py +1751 -0
  13. ecadlib/altium_pkg.py +396 -0
  14. ecadlib/altium_project.py +1095 -0
  15. ecadlib/app.py +1458 -0
  16. ecadlib/approvals.py +395 -0
  17. ecadlib/assets/demo_bom.csv +8 -0
  18. ecadlib/assets/kicad_action_plugin.py +607 -0
  19. ecadlib/assets/kicad_reread.py +60 -0
  20. ecadlib/assets/pcblib_template.json.gz +0 -0
  21. ecadlib/bom_inputs.py +339 -0
  22. ecadlib/bom_triage.py +519 -0
  23. ecadlib/cad_model.py +528 -0
  24. ecadlib/cfb.py +171 -0
  25. ecadlib/cli.py +185 -0
  26. ecadlib/clihelp.py +149 -0
  27. ecadlib/cliutil.py +119 -0
  28. ecadlib/completion.py +173 -0
  29. ecadlib/costs.py +276 -0
  30. ecadlib/crosscheck.py +412 -0
  31. ecadlib/datasheet.py +2168 -0
  32. ecadlib/dedupe.py +572 -0
  33. ecadlib/detect.py +170 -0
  34. ecadlib/doctor.py +450 -0
  35. ecadlib/ds_route.py +541 -0
  36. ecadlib/eagle_export.py +1409 -0
  37. ecadlib/easy_setup.py +338 -0
  38. ecadlib/easyeda_convert.py +386 -0
  39. ecadlib/easyeda_pro_export.py +972 -0
  40. ecadlib/eval/__init__.py +0 -0
  41. ecadlib/eval/baselines/kicad-v1.json +404 -0
  42. ecadlib/eval/build_set.py +299 -0
  43. ecadlib/eval/cli.py +119 -0
  44. ecadlib/eval/gate.py +358 -0
  45. ecadlib/eval/golden/demo.txt +379 -0
  46. ecadlib/eval/golden/specs.txt +3120 -0
  47. ecadlib/eval/golden/testpack.txt +463 -0
  48. ecadlib/eval/golden.py +446 -0
  49. ecadlib/eval/replay/kicad-v1.json.gz +0 -0
  50. ecadlib/eval/runner.py +456 -0
  51. ecadlib/eval/score.py +179 -0
  52. ecadlib/eval/sets/kicad-v1.json +630 -0
  53. ecadlib/firstrun.py +400 -0
  54. ecadlib/footprint_adv.py +1006 -0
  55. ecadlib/footprint_bench.py +218 -0
  56. ecadlib/footprint_check.py +562 -0
  57. ecadlib/footprint_gen.py +582 -0
  58. ecadlib/footprint_rules.py +1092 -0
  59. ecadlib/forge.py +267 -0
  60. ecadlib/fp_share.py +95 -0
  61. ecadlib/fp_tidy.py +389 -0
  62. ecadlib/fsutil.py +62 -0
  63. ecadlib/house_style.py +1051 -0
  64. ecadlib/httpcache.py +124 -0
  65. ecadlib/hub.py +1490 -0
  66. ecadlib/hub_copy.py +161 -0
  67. ecadlib/hub_tools.py +79 -0
  68. ecadlib/kicad_dblib.py +257 -0
  69. ecadlib/kicad_export.py +837 -0
  70. ecadlib/kicad_import.py +913 -0
  71. ecadlib/kicad_index.py +160 -0
  72. ecadlib/kicad_menu.py +193 -0
  73. ecadlib/kicad_pcm.py +475 -0
  74. ecadlib/launcher.py +156 -0
  75. ecadlib/librarian.py +963 -0
  76. ecadlib/lifecycle.py +691 -0
  77. ecadlib/llm/__init__.py +276 -0
  78. ecadlib/llm/cli.py +70 -0
  79. ecadlib/llm/openai_compat.py +214 -0
  80. ecadlib/llm/profiles.py +245 -0
  81. ecadlib/llm/rails.py +570 -0
  82. ecadlib/llm/run.py +126 -0
  83. ecadlib/llm/sampling.py +49 -0
  84. ecadlib/mcp_server.py +723 -0
  85. ecadlib/messages.py +419 -0
  86. ecadlib/migrate.py +407 -0
  87. ecadlib/notify.py +329 -0
  88. ecadlib/package_schema.json +1238 -0
  89. ecadlib/package_specs/CP_EIA-3216-18_Kemet-A.json +27 -0
  90. ecadlib/package_specs/C_0402_1005Metric.json +22 -0
  91. ecadlib/package_specs/C_0805_2012Metric.json +22 -0
  92. ecadlib/package_specs/DFN-8-1EP_3x3mm_P0.5mm_EP1.66x2.38mm.json +40 -0
  93. ecadlib/package_specs/D_SMA.json +27 -0
  94. ecadlib/package_specs/D_SMB.json +27 -0
  95. ecadlib/package_specs/D_SOD-123.json +43 -0
  96. ecadlib/package_specs/D_SOD-323.json +43 -0
  97. ecadlib/package_specs/LED_0603_1608Metric.json +23 -0
  98. ecadlib/package_specs/LED_0805_2012Metric.json +23 -0
  99. ecadlib/package_specs/LQFP-48_7x7mm_P0.5mm.json +31 -0
  100. ecadlib/package_specs/L_0805_2012Metric.json +22 -0
  101. ecadlib/package_specs/MSOP-8_3x3mm_P0.65mm.json +31 -0
  102. ecadlib/package_specs/QFN-16-1EP_3x3mm_P0.5mm_EP1.75x1.75mm.json +39 -0
  103. ecadlib/package_specs/QFN-32-1EP_5x5mm_P0.5mm_EP3.45x3.45mm.json +37 -0
  104. ecadlib/package_specs/R_0402_1005Metric.json +22 -0
  105. ecadlib/package_specs/R_0603_1608Metric.json +22 -0
  106. ecadlib/package_specs/R_0805_2012Metric.json +22 -0
  107. ecadlib/package_specs/R_1206_3216Metric.json +22 -0
  108. ecadlib/package_specs/SOIC-16_3.9x9.9mm_P1.27mm.json +32 -0
  109. ecadlib/package_specs/SOIC-8_3.9x4.9mm_P1.27mm.json +32 -0
  110. ecadlib/package_specs/SOT-223-3_TabPin2.json +56 -0
  111. ecadlib/package_specs/SOT-23-5.json +57 -0
  112. ecadlib/package_specs/SOT-23-6.json +62 -0
  113. ecadlib/package_specs/SOT-23.json +48 -0
  114. ecadlib/package_specs/TSSOP-16_4.4x5mm_P0.65mm.json +31 -0
  115. ecadlib/package_specs/VQFN-16-1EP_3x3mm_P0.5mm_EP1.68x1.68mm.json +39 -0
  116. ecadlib/package_specs/advanced/BGA-100_11.0x11.0mm_Layout10x10_P1.0mm_Ball0.5mm_Pad0.4mm_NSMD.json +26 -0
  117. ecadlib/package_specs/advanced/BGA-121_9.0x9.0mm_Layout11x11_P0.8mm_Ball0.4mm_Pad0.35mm_NSMD.json +26 -0
  118. ecadlib/package_specs/advanced/BGA-144_7.0x7.0mm_Layout13x13_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD.json +29 -0
  119. ecadlib/package_specs/advanced/BGA-169_11.0x11.0mm_Layout13x13_P0.8mm_Ball0.5mm_Pad0.4mm_NSMD.json +23 -0
  120. ecadlib/package_specs/advanced/BGA-256_11.0x11.0mm_Layout20x20_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD.json +26 -0
  121. ecadlib/package_specs/advanced/BGA-256_17.0x17.0mm_Layout16x16_P1.0mm_Ball0.5mm_Pad0.4mm_NSMD.json +26 -0
  122. ecadlib/package_specs/advanced/BGA-324_15.0x15.0mm_Layout18x18_P0.8mm_Ball0.5mm_Pad0.4mm_NSMD.json +26 -0
  123. ecadlib/package_specs/advanced/BGA-36_3.396x3.466mm_Layout6x6_P0.4mm_Ball0.25mm_Pad0.2mm_NSMD.json +24 -0
  124. ecadlib/package_specs/advanced/BGA-672_27.0x27.0mm_Layout26x26_P1.0mm_Ball0.6mm_Pad0.5mm_NSMD.json +32 -0
  125. ecadlib/package_specs/advanced/BGA-68_5.0x5.0mm_Layout9x9_P0.5mm_Ball0.3mm_Pad0.25mm_NSMD.json +30 -0
  126. ecadlib/package_specs/advanced/BGA-90_8.0x13.0mm_Layout2x3x15_P0.8mm.json +26 -0
  127. ecadlib/package_specs/advanced/DFN-8-1EP_3x3mm_P0.5mm_EP1.65x2.38mm_ThermalVias.json +60 -0
  128. ecadlib/package_specs/advanced/Example_Custom_LPad_Slot.json +24 -0
  129. ecadlib/package_specs/advanced/JST_GH_SM04B-GHS-TB_1x04-1MP_P1.25mm_Horizontal.json +49 -0
  130. ecadlib/package_specs/advanced/JST_PH_B4B-PH-K_1x04_P2.00mm_Vertical.json +33 -0
  131. ecadlib/package_specs/advanced/JST_PH_S4B-PH-SM4-TB_1x04-1MP_P2.00mm_Horizontal.json +55 -0
  132. ecadlib/package_specs/advanced/JST_SH_SM04B-SRSS-TB_1x04-1MP_P1.00mm_Horizontal.json +49 -0
  133. ecadlib/package_specs/advanced/JST_XH_B2B-XH-A_1x02_P2.50mm_Vertical.json +34 -0
  134. ecadlib/package_specs/advanced/JST_XH_B4B-XH-A_1x04_P2.50mm_Vertical.json +34 -0
  135. ecadlib/package_specs/advanced/JST_XH_B6B-XH-A_1x06_P2.50mm_Vertical.json +33 -0
  136. ecadlib/package_specs/advanced/LFBGA-144_10x10mm_Layout12x12_P0.8mm.json +23 -0
  137. ecadlib/package_specs/advanced/Molex_KK-254_AE-6410-04A_1x04_P2.54mm_Vertical.json +34 -0
  138. ecadlib/package_specs/advanced/Molex_PicoBlade_53398-0471_1x04-1MP_P1.25mm_Vertical.json +54 -0
  139. ecadlib/package_specs/advanced/PinHeader_1x05_P1.00mm_Vertical_SMD_Pin1Left.json +14 -0
  140. ecadlib/package_specs/advanced/PinHeader_1x05_P1.27mm_Vertical_SMD_Pin1Left.json +14 -0
  141. ecadlib/package_specs/advanced/PinHeader_1x05_P2.00mm_Vertical.json +12 -0
  142. ecadlib/package_specs/advanced/PinHeader_1x05_P2.00mm_Vertical_SMD_Pin1Left.json +14 -0
  143. ecadlib/package_specs/advanced/PinHeader_1x05_P2.54mm_Vertical.json +12 -0
  144. ecadlib/package_specs/advanced/PinHeader_1x05_P2.54mm_Vertical_SMD_Pin1Left.json +25 -0
  145. ecadlib/package_specs/advanced/PinHeader_1x05_P2.54mm_Vertical_SMD_Pin1Right.json +14 -0
  146. ecadlib/package_specs/advanced/PinHeader_1x10_P1.00mm_Vertical.json +15 -0
  147. ecadlib/package_specs/advanced/PinHeader_2x05_P1.00mm_Vertical_SMD.json +13 -0
  148. ecadlib/package_specs/advanced/PinHeader_2x05_P1.27mm_Vertical.json +18 -0
  149. ecadlib/package_specs/advanced/PinHeader_2x05_P1.27mm_Vertical_SMD.json +24 -0
  150. ecadlib/package_specs/advanced/PinHeader_2x05_P2.00mm_Vertical_SMD.json +13 -0
  151. ecadlib/package_specs/advanced/PinHeader_2x05_P2.54mm_Vertical.json +12 -0
  152. ecadlib/package_specs/advanced/PinHeader_2x05_P2.54mm_Vertical_SMD.json +24 -0
  153. ecadlib/package_specs/advanced/PinHeader_2x20_P2.54mm_Vertical.json +12 -0
  154. ecadlib/package_specs/advanced/QFN-16-1EP_3x3mm_P0.5mm_EP1.75x1.75mm_ThermalVias.json +87 -0
  155. ecadlib/package_specs/advanced/UFBGA-132_7x7mm_Layout12x12_P0.5mm.json +43 -0
  156. ecadlib/package_specs/advanced/USB_C_Receptacle_HRO_TYPE-C-31-M-12.json +218 -0
  157. ecadlib/package_specs/standard.json.gz +0 -0
  158. ecadlib/parity.py +1204 -0
  159. ecadlib/parts_export.py +154 -0
  160. ecadlib/partview.py +215 -0
  161. ecadlib/plugins.py +231 -0
  162. ecadlib/providers.py +226 -0
  163. ecadlib/rebuild.py +471 -0
  164. ecadlib/release_check.py +437 -0
  165. ecadlib/reports.py +274 -0
  166. ecadlib/resolve.py +556 -0
  167. ecadlib/review_cli.py +292 -0
  168. ecadlib/rules.py +979 -0
  169. ecadlib/rules_infer.py +1205 -0
  170. ecadlib/sexpr.py +149 -0
  171. ecadlib/sheetsafe.py +65 -0
  172. ecadlib/sim_models.py +1087 -0
  173. ecadlib/sourcing.py +1161 -0
  174. ecadlib/stdparts.py +755 -0
  175. ecadlib/step_gen.py +929 -0
  176. ecadlib/symbol_layout.py +798 -0
  177. ecadlib/team.py +558 -0
  178. ecadlib/team_setup.py +288 -0
  179. ecadlib/term.py +420 -0
  180. ecadlib/term_views.py +64 -0
  181. ecadlib/testpack.py +334 -0
  182. ecadlib/ui/__init__.py +1 -0
  183. ecadlib/ui/__main__.py +3 -0
  184. ecadlib/ui/server.py +1029 -0
  185. ecadlib/ui/static/hairline.js +2 -0
  186. ecadlib/ui/static/index.html +2098 -0
  187. ecadlib/ui/static/setup.html +294 -0
  188. ecadlib/upkeep.py +717 -0
  189. ecadlib/upstream.py +573 -0
  190. ecadlib/userconfig.py +257 -0
  191. ecadlib/visual_check.py +1603 -0
  192. ecadlib/ws_status.py +203 -0
  193. ecadlib-0.3.0.dist-info/METADATA +2245 -0
  194. ecadlib-0.3.0.dist-info/RECORD +199 -0
  195. ecadlib-0.3.0.dist-info/WHEEL +5 -0
  196. ecadlib-0.3.0.dist-info/entry_points.txt +2 -0
  197. ecadlib-0.3.0.dist-info/licenses/LICENSE +21 -0
  198. ecadlib-0.3.0.dist-info/licenses/NOTICE +53 -0
  199. 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
@@ -0,0 +1,3 @@
1
+ from .server import main
2
+
3
+ main()