Compare commits
9 Commits
0.3.2
...
feature/upload
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c08e18ef9 | |||
| a39b65c94c | |||
| a6d1999839 | |||
| c7cc47718d | |||
| b801028d63 | |||
| 959c2110c9 | |||
| 54cdea9c33 | |||
| 08b6895ea3 | |||
| 647d8aaec2 |
Generated
Generated
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<option name="ignoredErrors">
|
||||
<list>
|
||||
<option value="N801" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredIdentifiers">
|
||||
<list>
|
||||
<option value="bpy" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/KiCadJLCplugin.iml" filepath="$PROJECT_DIR$/.idea/KiCadJLCplugin.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+40
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="81624dc4-41ca-48ac-93b8-92da00936c57" name="Changes" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/plugins/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/plugins/__init__.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/plugins/config.py" beforeDir="false" afterPath="$PROJECT_DIR$/plugins/config.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/plugins/plugin.py" beforeDir="false" afterPath="$PROJECT_DIR$/plugins/plugin.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/plugins/result_event.py" beforeDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/plugins/thread.py" beforeDir="false" afterPath="$PROJECT_DIR$/plugins/thread.py" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectId" id="2C7tSUh2zYdb8tYB6kgwqDOifTc" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
||||
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="81624dc4-41ca-48ac-93b8-92da00936c57" name="Changes" comment="" />
|
||||
<created>1658165450946</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1658165450946</updated>
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
</project>
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"Drilland",
|
||||
"EXCELLON",
|
||||
"FPID",
|
||||
"LCSC",
|
||||
"netlist",
|
||||
"pcbnew",
|
||||
"Plotfile",
|
||||
"Protel",
|
||||
"Soldermask"
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,12 @@
|
||||
# JLC PCB Plug-in for KiCad (Fabrication Toolkit)
|
||||
<img src="https://github.com/bennymeg/JLC-Plugin-for-KiCad/blob/master/assets/logo.svg?raw=true"
|
||||
style="display:block margin-left: auto; margin-right: auto;" alt="JLC PCB Plug-in for KiCad">
|
||||
|
||||
<div align="center">
|
||||
|
||||
| **JLC PCB Plug-in for KiCad** |
|
||||
|:-----:|
|
||||
|
||||
</div>
|
||||
|
||||
## Features
|
||||
1. Generates gerber files in correct format for production
|
||||
@@ -9,7 +17,7 @@
|
||||
## Installation
|
||||
|
||||
### Official Installation
|
||||
Open the "Plugin and Content Manager" from the KiCad main menu an install the "Fabrication Toolkit" plugin from the selection list.
|
||||
Open the "Plugin and Content Manager" from the KiCad main menu an install the "<ins>Fabrication Toolkit</ins>" plugin from the selection list.
|
||||
|
||||
### Manual installation
|
||||
Download the latest ZIP file from https://github.com/bennymeg/JLC-Plugin-for-KiCad. Open the "Plugin and Content Manager" from the KiCads main window and install the ZIP file via "Install from File".
|
||||
@@ -17,14 +25,18 @@ Download the latest ZIP file from https://github.com/bennymeg/JLC-Plugin-for-KiC
|
||||
## Options
|
||||
|
||||
### Include Component Part Number in Production Files
|
||||
Add an 'MPN'* field with the LCSC component part number to the footprint component options.
|
||||
Add an 'MPN'* field with the LCSC component part number to the symbol's fields property.
|
||||
|
||||
<img src="https://github.com/bennymeg/JLC-Plugin-for-KiCad/blob/master/assets/mpn.png?raw=true" height=420>
|
||||
|
||||
#### Similar Fields*:
|
||||
|'mpn' | 'Mpn' | 'MPN' | 'JLC_MPN' | 'LCSC_MPN' | 'LCSC Part #' | 'JLC' | 'LCSC'|
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
|
||||
### Ignore Footprint in Production Files
|
||||
Select 'Exclude from position files' or 'Exclude from BOM' in the footprint component options in order to ignore the footprint from the relevant file.
|
||||
Select 'Exclude from position files' or 'Exclude from BOM' in the symbol's attributes property in order to ignore the footprint from the relevant file.
|
||||
|
||||
<img src="https://github.com/bennymeg/JLC-Plugin-for-KiCad/blob/master/assets/attributes.png?raw=true" height=420>
|
||||
|
||||
## Author
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
+183
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="1300"
|
||||
height="136.763"
|
||||
id="svg3509"
|
||||
version="1.1"
|
||||
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
|
||||
sodipodi:docname="Fabrication Toolkit Logo.svg"
|
||||
inkscape:export-filename="/home/fabrizio/Desktop/kicad_logo2.png"
|
||||
inkscape:export-xdpi="130.52866"
|
||||
inkscape:export-ydpi="130.52866"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs3511">
|
||||
<rect
|
||||
x="979.28571"
|
||||
y="180.33443"
|
||||
width="132.14286"
|
||||
height="193.57143"
|
||||
id="rect2617" />
|
||||
<rect
|
||||
x="590.71429"
|
||||
y="-28.951285"
|
||||
width="167.14286"
|
||||
height="187.85714"
|
||||
id="rect2611" />
|
||||
<rect
|
||||
x="176.42857"
|
||||
y="10.334429"
|
||||
width="1260.7143"
|
||||
height="265.71428"
|
||||
id="rect227" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.98994949"
|
||||
inkscape:cx="687.91388"
|
||||
inkscape:cy="158.08887"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="text225"
|
||||
showgrid="true"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1002"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-grids="false"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:snap-to-guides="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<metadata
|
||||
id="metadata3514">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-40.547253,-883.62329)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#314cb0;fill-opacity:1;fill-rule:nonzero;stroke:#1d2d68;stroke-width:4.85049;stroke-miterlimit:4;stroke-opacity:1"
|
||||
d="m 1056.9425,902.1018 h 114.392 c 0.5127,0 0.9255,0.43179 0.9255,0.96814 v 109.79556 c 0,0.5364 -0.4128,0.9682 -0.9255,0.9682 h -114.392 c -0.5128,0 -0.9255,-0.4318 -0.9255,-0.9682 V 903.06994 c 0,-0.53635 0.4127,-0.96814 0.9255,-0.96814 z"
|
||||
id="rect3438-4-1-6-9-8-4" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3008"
|
||||
style="font-style:normal;font-weight:normal;font-size:120.794px;line-height:122%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
d="m 1085.5333,948.38598 16.9276,-21.41018 c 3.4602,-4.36453 5.1903,-7.62815 5.1904,-9.79088 -10e-5,-0.78633 -0.098,-1.41546 -0.2949,-1.8874 h 29.5495 c -2.2413,1.25835 -5.0528,3.91251 -8.4343,7.96246 -0.9044,1.06175 -2.4969,3.00812 -4.7774,5.83914 l -22.177,27.60321 26.4826,36.45038 c 1.6121,2.20197 3.6567,4.67918 6.134,7.43169 0.6684,0.7078 1.6318,1.5532 2.8901,2.5362 h -30.3163 c 0.2358,-0.9044 0.3538,-1.7105 0.3539,-2.4183 -10e-5,-2.16264 -1.4943,-5.28864 -4.4826,-9.37801 l -17.0456,-23.23859 v 23.5335 c 0,5.4656 1.101,9.2994 3.303,11.5014 h -27.7212 c 1.5335,-1.5335 2.5165,-3.28335 2.9491,-5.24939 0.2359,-1.10098 0.3538,-3.16532 0.3538,-6.19303 v -64.9383 c 0,-3.02763 -0.1179,-5.09197 -0.3538,-6.19303 -0.4326,-1.96596 -1.4156,-3.71573 -2.9491,-5.24933 h 27.7212 c -2.202,2.20205 -3.303,6.01617 -3.303,11.44236 v 21.6461" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3010"
|
||||
style="font-style:normal;font-weight:normal;font-size:120.794px;line-height:122%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
d="m 1164.7451,938.5951 v 54.14474 c -1e-4,2.83111 0.079,4.67918 0.2359,5.54423 0.3145,1.76943 1.1403,3.38163 2.4772,4.83653 h -25.9517 c 1.3369,-1.4549 2.1626,-3.0671 2.4772,-4.83653 0.1573,-0.86505 0.2359,-2.71312 0.2359,-5.54423 v -43.88201 c 0,-2.83104 -0.079,-4.65946 -0.2359,-5.48526 -0.3146,-1.73005 -1.1207,-3.32254 -2.4182,-4.77747 h 23.1796 m -10.2628,-30.84717 c 3.2636,9e-5 6.0358,1.16006 8.3164,3.47989 2.3199,2.2807 3.4798,5.05281 3.4799,8.31635 -1e-4,3.34235 -1.1404,6.15378 -3.4209,8.43431 -2.2414,2.24136 -5.0331,3.362 -8.3754,3.36193 -3.2636,7e-5 -6.0554,-1.14023 -8.3753,-3.42091 -2.2806,-2.31985 -3.4209,-5.11162 -3.4209,-8.37533 0,-3.34218 1.1206,-6.13396 3.3619,-8.37533 2.2806,-2.28051 5.0921,-3.42082 8.4343,-3.42091" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#314cb0;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 1140.5539,906.77745 h 28.3151 v 27.03082 h -28.3151 z"
|
||||
id="rect4352-5-1" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ff7700;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 1166.4321,902.13073 c 0,6.60244 -5.3681,11.95472 -11.9901,11.95472 -6.622,0 -11.9902,-5.35228 -11.9902,-11.95472 0,-6.60244 5.3682,-11.9548 11.9902,-11.9548 6.622,0 11.9901,5.35236 11.9901,11.9548 z"
|
||||
id="path3552-6-3-7" />
|
||||
<g
|
||||
aria-label="Fabrication Tool t"
|
||||
transform="translate(-30.187122,883.52127)"
|
||||
id="text225"
|
||||
style="font-size:120px;line-height:1.25;white-space:pre;shape-inside:url(#rect227);fill:#1b2b63">
|
||||
<path
|
||||
d="m 234.61133,51.200341 h -29.64844 v 20.625 h 27.24609 V 87.176904 H 204.96289 V 119.81362 H 186.03711 V 35.790185 h 48.57422 z"
|
||||
style="font-weight:bold;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI Bold'"
|
||||
id="path2671" />
|
||||
<path
|
||||
d="m 292.9707,119.81362 h -17.51953 v -8.61328 h -0.23437 q -6.03516,10.07813 -17.8711,10.07813 -8.73047,0 -13.76953,-4.92188 -4.98047,-4.98047 -4.98047,-13.24219 0,-17.460934 20.6836,-20.156246 l 16.28906,-2.167969 q 0,-9.84375 -10.66406,-9.84375 -10.72266,0 -20.39063,6.386719 V 63.387841 q 3.86719,-1.992187 10.54688,-3.515625 6.73828,-1.523437 12.24609,-1.523437 25.66406,0 25.66406,25.605468 z M 275.56836,95.438622 v -4.042968 l -10.89844,1.40625 q -9.02344,1.171875 -9.02344,8.144526 0,3.16407 2.16797,5.21485 2.22657,1.99219 5.97657,1.99219 5.21484,0 8.49609,-3.57422 3.28125,-3.63282 3.28125,-9.140628 z"
|
||||
style="font-weight:bold;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI Bold'"
|
||||
id="path2673" />
|
||||
<path
|
||||
d="m 326.36914,112.84097 h -0.23437 v 6.97265 H 307.61914 V 30.985497 h 18.51563 V 68.83706 h 0.23437 q 6.85547,-10.488281 19.51172,-10.488281 11.60156,0 17.87109,7.96875 6.26953,7.96875 6.26953,21.796875 0,14.999996 -7.32421,24.082026 -7.32422,9.08204 -19.57032,9.08204 -11.07422,0 -16.75781,-8.4375 z M 325.8418,87.528466 v 6.152344 q 0,5.800781 3.33984,9.60937 3.33984,3.8086 8.55469,3.8086 6.32812,0 9.78515,-4.86328 3.51563,-4.921878 3.51563,-13.886721 0,-7.441407 -3.22266,-11.601563 -3.16406,-4.21875 -9.08203,-4.21875 -5.5664,0 -9.25781,4.160156 -3.63281,4.160157 -3.63281,10.839844 z"
|
||||
style="font-weight:bold;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI Bold'"
|
||||
id="path2675" />
|
||||
<path
|
||||
d="m 421.17383,76.512841 q -3.33985,-1.816406 -7.79297,-1.816406 -6.03516,0 -9.43359,4.453125 -3.39844,4.394531 -3.39844,12.011719 V 119.81362 H 382.0332 V 59.813622 h 18.51563 v 11.132813 h 0.23437 q 4.39453,-12.1875 15.82032,-12.1875 2.92968,0 4.57031,0.703125 z"
|
||||
style="font-weight:bold;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI Bold'"
|
||||
id="path2677" />
|
||||
<path
|
||||
d="m 439.63086,50.321435 q -4.6875,0 -7.67578,-2.753906 -2.98828,-2.8125 -2.98828,-6.855469 0,-4.160156 2.98828,-6.796875 2.98828,-2.636719 7.67578,-2.636719 4.74609,0 7.67578,2.636719 2.98828,2.636719 2.98828,6.796875 0,4.21875 -2.98828,6.914062 -2.92969,2.695313 -7.67578,2.695313 z m 9.14062,69.492185 H 430.25586 V 59.813622 h 18.51562 z"
|
||||
style="font-weight:bold;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI Bold'"
|
||||
id="path2679" />
|
||||
<path
|
||||
d="m 509.82617,117.64565 q -6.26953,3.63282 -18.10547,3.63282 -13.82812,0 -22.38281,-8.37891 -8.55469,-8.37891 -8.55469,-21.621094 0,-15.292969 9.14063,-24.082031 9.19922,-8.847656 24.55078,-8.847656 10.60547,0 15.35156,2.8125 v 15.703125 q -5.80078,-4.335938 -12.94922,-4.335938 -7.96875,0 -12.65625,4.6875 -4.6289,4.628906 -4.6289,12.832031 0,7.96875 4.45312,12.539063 4.45313,4.51172 12.2461,4.51172 6.91406,0 13.53515,-4.33594 z"
|
||||
style="font-weight:bold;font-family:'Segoe UI';-inkscape-font-specification:'Segoe UI Bold'"
|
||||
id="path2681" />
|
||||
<path
|
||||
d="m 571.87695,119.81362 h -17.51953 v -8.61328 h -0.23437 q -6.03516,10.07813 -17.8711,10.07813 -8.73047,0 -13.76953,-4.92188 -4.98047,-4.98047 -4.98047,-13.24219 0,-17.460934 20.6836,-20.156246 l 16.28906,-2.167969 q 0,-9.84375 -10.66406,-9.84375 -10.72266,0 -20.39063,6.386719 V 63.387841 q 3.86719,-1.992187 10.54688,-3.515625 6.73828,-1.523437 12.24609,-1.523437 25.66406,0 25.66406,25.605468 z M 554.47461,95.438622 v -4.042968 l -10.89844,1.40625 q -9.02344,1.171875 -9.02344,8.144526 0,3.16407 2.16797,5.21485 2.22657,1.99219 5.97657,1.99219 5.21484,0 8.49609,-3.57422 3.28125,-3.63282 3.28125,-9.140628 z"
|
||||
id="path2683" />
|
||||
<path
|
||||
d="m 622.79492,119.1105 q -4.10156,2.16797 -12.36328,2.16797 -19.57031,0 -19.57031,-20.33204 V 73.465966 h -9.72656 V 59.813622 h 9.72656 V 46.864404 l 18.45703,-5.273438 v 18.222656 h 13.47656 v 13.652344 h -13.47656 v 24.257813 q 0,9.375001 7.44141,9.375001 2.92968,0 6.03515,-1.69922 z"
|
||||
id="path2685" />
|
||||
<path
|
||||
d="m 642.59961,50.321435 q -4.6875,0 -7.67578,-2.753906 -2.98828,-2.8125 -2.98828,-6.855469 0,-4.160156 2.98828,-6.796875 2.98828,-2.636719 7.67578,-2.636719 4.74609,0 7.67578,2.636719 2.98828,2.636719 2.98828,6.796875 0,4.21875 -2.98828,6.914062 -2.92969,2.695313 -7.67578,2.695313 z m 9.14062,69.492185 H 633.22461 V 59.813622 h 18.51562 z"
|
||||
id="path2687" />
|
||||
<path
|
||||
d="m 695.91992,121.27847 q -15,0 -23.61328,-8.37891 -8.55469,-8.4375 -8.55469,-22.851563 0,-14.882812 8.90625,-23.261718 8.90625,-8.4375 24.08203,-8.4375 14.94141,0 23.4375,8.4375 8.4961,8.378906 8.4961,22.207031 0,14.94141 -8.78906,23.61328 -8.73047,8.67188 -23.96485,8.67188 z m 0.46875,-48.750004 q -6.5625,0 -10.19531,4.511719 -3.63281,4.511719 -3.63281,12.773437 0,17.285158 13.94531,17.285158 13.30078,0 13.30078,-17.753908 0,-16.816406 -13.41797,-16.816406 z"
|
||||
id="path2689" />
|
||||
<path
|
||||
d="M 798.63477,119.81362 H 780.17773 V 86.473779 q 0,-13.945313 -9.96093,-13.945313 -4.80469,0 -7.91016,3.691406 -3.10547,3.691407 -3.10547,9.375 V 119.81362 H 740.68555 V 59.813622 h 18.51562 v 9.492188 h 0.23438 q 6.62109,-10.957031 19.27734,-10.957031 19.92188,0 19.92188,24.726562 z"
|
||||
id="path2691" />
|
||||
<path
|
||||
d="M 907.20898,51.200341 H 883.24414 V 119.81362 H 864.25977 V 51.200341 H 840.41211 V 35.790185 h 66.79687 z"
|
||||
id="path2693" />
|
||||
<path
|
||||
d="m 934.51367,121.27847 q -15,0 -23.61328,-8.37891 -8.55469,-8.4375 -8.55469,-22.851563 0,-14.882812 8.90625,-23.261718 8.90625,-8.4375 24.08203,-8.4375 14.94141,0 23.4375,8.4375 8.4961,8.378906 8.4961,22.207031 0,14.94141 -8.78906,23.61328 -8.73047,8.67188 -23.96485,8.67188 z m 0.46875,-48.750004 q -6.5625,0 -10.19531,4.511719 -3.63281,4.511719 -3.63281,12.773437 0,17.285158 13.94531,17.285158 13.30078,0 13.30078,-17.753908 0,-16.816406 -13.41797,-16.816406 z"
|
||||
id="path2695" />
|
||||
<path
|
||||
d="m 1007.873,121.27847 q -14.99995,0 -23.61323,-8.37891 -8.55469,-8.4375 -8.55469,-22.851563 0,-14.882812 8.90625,-23.261718 8.90625,-8.4375 24.08207,-8.4375 14.9414,0 23.4375,8.4375 8.4961,8.378906 8.4961,22.207031 0,14.94141 -8.7891,23.61328 -8.7305,8.67188 -23.9649,8.67188 z m 0.4688,-48.750004 q -6.5625,0 -10.19532,4.511719 -3.63281,4.511719 -3.63281,12.773437 0,17.285158 13.94533,17.285158 13.3008,0 13.3008,-17.753908 0,-16.816406 -13.418,-16.816406 z"
|
||||
id="path2697" />
|
||||
<path
|
||||
d="m 1071.1543,119.81362 h -18.5156 V 30.985497 h 18.5156 z"
|
||||
id="path2699" />
|
||||
<path
|
||||
d="m 1255.4316,119.1105 q -4.1015,2.16797 -12.3632,2.16797 -19.5704,0 -19.5704,-20.33204 V 73.465966 h -9.7265 V 59.813622 h 9.7265 V 46.864404 l 18.4571,-5.273438 v 18.222656 h 13.4765 v 13.652344 h -13.4765 v 24.257813 q 0,9.375001 7.4414,9.375001 2.9297,0 6.0351,-1.69922 z"
|
||||
id="path2701" />
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
transform="translate(40.547253,883.62329)"
|
||||
id="text2609"
|
||||
style="fill:black;fill-opacity:1;line-height:1.25;stroke:none;font-family:sans-serif;font-style:normal;font-weight:normal;font-size:40px;white-space:pre;shape-inside:url(#rect2611)" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
transform="translate(40.547253,883.62329)"
|
||||
id="text2615"
|
||||
style="fill:black;fill-opacity:1;line-height:1.25;stroke:none;font-family:sans-serif;font-style:normal;font-weight:normal;font-size:40px;white-space:pre;shape-inside:url(#rect2617)" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
+1
-1
@@ -29,7 +29,7 @@
|
||||
],
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.3.2",
|
||||
"version": "0.4.2",
|
||||
"status": "testing",
|
||||
"kicad_version": "6.00"
|
||||
}
|
||||
|
||||
+2
-2
@@ -4,5 +4,5 @@ try:
|
||||
plugin.register()
|
||||
except Exception as e:
|
||||
import logging
|
||||
root = logging.getLogger()
|
||||
root.debug(repr(e))
|
||||
logger = logging.getLogger()
|
||||
logger.debug(repr(e))
|
||||
|
||||
@@ -14,6 +14,8 @@ plotPlan = [
|
||||
("B.Cu", pcbnew.B_Cu, "Bottom Layer"),
|
||||
("In1.Cu", pcbnew.In1_Cu, "Internal plane 1"),
|
||||
("In2.Cu", pcbnew.In2_Cu, "Internal plane 2"),
|
||||
("In3.Cu", pcbnew.In3_Cu, "Internal plane 3"),
|
||||
("In4.Cu", pcbnew.In4_Cu, "Internal plane 4"),
|
||||
("F.SilkS", pcbnew.F_SilkS, "Top Silkscreen"),
|
||||
("B.SilkS", pcbnew.B_SilkS, "Bottom Silkscreen"),
|
||||
("F.Mask", pcbnew.F_Mask, "Top Soldermask"),
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import wx
|
||||
|
||||
STATUS_EVENT_ID = wx.NewId()
|
||||
|
||||
|
||||
class StatusEvent(wx.PyEvent):
|
||||
def __init__(self, data):
|
||||
wx.PyEvent.__init__(self)
|
||||
self.SetEventType(STATUS_EVENT_ID)
|
||||
self.data = data
|
||||
|
||||
@staticmethod
|
||||
def invoke(window, function):
|
||||
window.Connect(-1, -1, STATUS_EVENT_ID, function)
|
||||
+9
-7
@@ -1,10 +1,12 @@
|
||||
import os
|
||||
import wx
|
||||
import pcbnew
|
||||
|
||||
from .thread import *
|
||||
from .result_event import *
|
||||
from .thread import ProcessThread
|
||||
from .events import StatusEvent
|
||||
|
||||
|
||||
# WX GUI form that show the plugin progress
|
||||
class KiCadToJLCForm(wx.Frame):
|
||||
def __init__(self):
|
||||
wx.Dialog.__init__(
|
||||
@@ -32,9 +34,10 @@ class KiCadToJLCForm(wx.Frame):
|
||||
|
||||
self.Centre(wx.BOTH)
|
||||
|
||||
EVT_RESULT(self, self.updateDisplay)
|
||||
StatusEvent.invoke(self, self.updateDisplay)
|
||||
ProcessThread(self)
|
||||
|
||||
|
||||
def updateDisplay(self, status):
|
||||
if status.data == -1:
|
||||
pcbnew.Refresh()
|
||||
@@ -43,6 +46,7 @@ class KiCadToJLCForm(wx.Frame):
|
||||
self.m_gaugeStatus.SetValue(status.data)
|
||||
|
||||
|
||||
# Plugin definition
|
||||
class Plugin(pcbnew.ActionPlugin):
|
||||
def __init__(self):
|
||||
self.name = "Fabrication Toolkit"
|
||||
@@ -50,10 +54,8 @@ class Plugin(pcbnew.ActionPlugin):
|
||||
self.description = "Toolkit for automating PCB fabrication process with KiCad and JLC PCB"
|
||||
self.pcbnew_icon_support = hasattr(self, "show_toolbar_button")
|
||||
self.show_toolbar_button = True
|
||||
self.icon_file_name = os.path.join(
|
||||
os.path.dirname(__file__), 'icon.png')
|
||||
self.dark_icon_file_name = os.path.join(
|
||||
os.path.dirname(__file__), 'icon.png')
|
||||
self.icon_file_name = os.path.join(os.path.dirname(__file__), 'icon.png')
|
||||
self.dark_icon_file_name = os.path.join(os.path.dirname(__file__), 'icon.png')
|
||||
|
||||
def Run(self):
|
||||
KiCadToJLCForm().Show()
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
import json
|
||||
import requests
|
||||
import os
|
||||
import csv
|
||||
import shutil
|
||||
import pcbnew
|
||||
from collections import defaultdict
|
||||
from .config import *
|
||||
|
||||
|
||||
class ProcessManager:
|
||||
def __init__(self):
|
||||
self.board = pcbnew.GetBoard()
|
||||
self.bom = []
|
||||
self.components = []
|
||||
|
||||
def generate_gerber(self, temp_dir):
|
||||
settings = self.board.GetDesignSettings()
|
||||
settings.m_SolderMaskMargin = 0
|
||||
settings.m_SolderMaskMinWidth = 0
|
||||
|
||||
plot_controller = pcbnew.PLOT_CONTROLLER(self.board)
|
||||
|
||||
plot_options = plot_controller.GetPlotOptions()
|
||||
plot_options.SetOutputDirectory(temp_dir)
|
||||
plot_options.SetPlotFrameRef(False)
|
||||
plot_options.SetSketchPadLineWidth(pcbnew.FromMM(0.1))
|
||||
plot_options.SetAutoScale(False)
|
||||
plot_options.SetScale(1)
|
||||
plot_options.SetMirror(False)
|
||||
plot_options.SetUseGerberAttributes(True)
|
||||
plot_options.SetExcludeEdgeLayer(True)
|
||||
plot_options.SetUseGerberProtelExtensions(False)
|
||||
plot_options.SetUseAuxOrigin(True)
|
||||
plot_options.SetSubtractMaskFromSilk(False)
|
||||
plot_options.SetDrillMarksType(0) # NO_DRILL_SHAPE
|
||||
|
||||
for layer_info in plotPlan:
|
||||
if self.board.IsLayerEnabled(layer_info[1]):
|
||||
plot_controller.SetLayer(layer_info[1])
|
||||
plot_controller.OpenPlotfile(
|
||||
layer_info[0],
|
||||
pcbnew.PLOT_FORMAT_GERBER,
|
||||
layer_info[2])
|
||||
plot_controller.PlotLayer()
|
||||
|
||||
plot_controller.ClosePlot()
|
||||
|
||||
def generate_drills(self, temp_dir):
|
||||
drill_writer = pcbnew.EXCELLON_WRITER(self.board)
|
||||
|
||||
drill_writer.SetOptions(
|
||||
False,
|
||||
True,
|
||||
self.board.GetDesignSettings().GetAuxOrigin(),
|
||||
False)
|
||||
drill_writer.SetFormat(False)
|
||||
drill_writer.CreateDrillandMapFilesSet(temp_dir, True, False)
|
||||
|
||||
def generate_netlist(self, temp_dir):
|
||||
netlist_writer = pcbnew.IPC356D_WRITER(self.board)
|
||||
netlist_writer.Write(os.path.join(temp_dir, netlistFileName))
|
||||
|
||||
def generate_positions(self, temp_dir):
|
||||
if hasattr(self.board, 'GetModules'):
|
||||
footprints = list(self.board.GetModules())
|
||||
else:
|
||||
footprints = list(self.board.GetFootprints())
|
||||
|
||||
# unique designator dictionary
|
||||
footprint_designators = defaultdict(int)
|
||||
for i, footprint in enumerate(footprints):
|
||||
# count unique designators
|
||||
footprint_designators[footprint.GetReference()] += 1
|
||||
bom_designators = footprint_designators.copy()
|
||||
|
||||
with open((os.path.join(temp_dir, designatorsFileName)), 'w', encoding='utf-8') as f:
|
||||
for key, value in footprint_designators.items():
|
||||
f.write('%s:%s\n' % (key, value))
|
||||
|
||||
for i, footprint in enumerate(footprints):
|
||||
try:
|
||||
footprint_name = str(footprint.GetFPID().GetFootprintName())
|
||||
except AttributeError:
|
||||
footprint_name = str(footprint.GetFPID().GetLibItemName())
|
||||
|
||||
layer = {
|
||||
pcbnew.F_Cu: 'top',
|
||||
pcbnew.B_Cu: 'bottom',
|
||||
}.get(footprint.GetLayer())
|
||||
|
||||
# mount_type = {
|
||||
# 0: 'smt',
|
||||
# 1: 'tht',
|
||||
# 2: 'smt'
|
||||
# }.get(footprint.GetAttributes())
|
||||
|
||||
if not footprint.GetAttributes() & pcbnew.FP_EXCLUDE_FROM_POS_FILES:
|
||||
# append unique ID if duplicate footprint designator
|
||||
unique_id = ""
|
||||
if footprint_designators[footprint.GetReference()] > 1:
|
||||
unique_id = str(footprint_designators[footprint.GetReference()])
|
||||
footprint_designators[footprint.GetReference()] -= 1
|
||||
|
||||
self.components.append({
|
||||
'Designator': "{}{}{}".format(footprint.GetReference(), "" if unique_id == "" else "_", unique_id),
|
||||
'Mid X': (footprint.GetPosition()[0] - self.board.GetDesignSettings().GetAuxOrigin()[0]) / 1000000.0,
|
||||
'Mid Y': (footprint.GetPosition()[1] - self.board.GetDesignSettings().GetAuxOrigin()[1]) * -1.0 / 1000000.0,
|
||||
'Rotation': footprint.GetOrientation() / 10.0,
|
||||
'Layer': layer,
|
||||
})
|
||||
|
||||
if not footprint.GetAttributes() & pcbnew.FP_EXCLUDE_FROM_BOM:
|
||||
# append unique ID if we are dealing with duplicate bom designator
|
||||
unique_id = ""
|
||||
if bom_designators[footprint.GetReference()] > 1:
|
||||
unique_id = str(bom_designators[footprint.GetReference()])
|
||||
bom_designators[footprint.GetReference()] -= 1
|
||||
|
||||
# todo: merge similar parts into single entry
|
||||
|
||||
self.bom.append({
|
||||
'Designator': "{}{}{}".format(footprint.GetReference(), "" if unique_id == "" else "_", unique_id),
|
||||
'Footprint': footprint_name,
|
||||
'Quantity': 1,
|
||||
'Value': footprint.GetValue(),
|
||||
# 'Mount': mount_type,
|
||||
'LCSC Part #': self._getMpnFromFootprint(footprint),
|
||||
})
|
||||
|
||||
with open((os.path.join(temp_dir, placementFileName)), 'w', newline='', encoding='utf-8') as outfile:
|
||||
header = True
|
||||
csv_writer = csv.writer(outfile)
|
||||
|
||||
for component in self.components:
|
||||
if header:
|
||||
# writing headers of CSV file
|
||||
csv_writer.writerow(component.keys())
|
||||
header = False
|
||||
|
||||
# writing data of CSV file
|
||||
if ('**' not in component['Designator']):
|
||||
csv_writer.writerow(component.values())
|
||||
|
||||
def generate_bom(self, temp_dir):
|
||||
|
||||
with open((os.path.join(temp_dir, bomFileName)), 'w', newline='', encoding='utf-8') as outfile:
|
||||
header = True
|
||||
csv_writer = csv.writer(outfile)
|
||||
|
||||
for component in self.bom:
|
||||
if header:
|
||||
# writing headers of CSV file
|
||||
csv_writer.writerow(component.keys())
|
||||
header = False
|
||||
|
||||
# writing data of CSV file
|
||||
if ('**' not in component['Designator']):
|
||||
csv_writer.writerow(component.values())
|
||||
|
||||
def generate_archive(self, temp_dir, temp_file):
|
||||
temp_file = shutil.make_archive(temp_file, 'zip', temp_dir)
|
||||
temp_file = shutil.move(temp_file, temp_dir)
|
||||
|
||||
# remove non essential files
|
||||
for item in os.listdir(temp_dir):
|
||||
if not item.endswith(".zip") and not item.endswith(".csv") and not item.endswith(".ipc"):
|
||||
os.remove(os.path.join(temp_dir, item))
|
||||
|
||||
return temp_file
|
||||
|
||||
def upload_archive(self, temp_file):
|
||||
boardWidth = pcbnew.Iu2Millimeter(self.board.GetBoardEdgesBoundingBox().GetWidth())
|
||||
boardHeight = pcbnew.Iu2Millimeter(self.board.GetBoardEdgesBoundingBox().GetHeight())
|
||||
boardLayer = self.board.GetCopperLayerCount()
|
||||
|
||||
files = { 'upload[file]': open(temp_file, 'rb') }
|
||||
data = { 'boardWidth': boardWidth, 'boardHeight': boardHeight, 'boardLayer': boardLayer }
|
||||
upload_url = baseUrl + '/Common/KiCadUpFile/'
|
||||
|
||||
response = requests.post(upload_url, files=files, data=data)
|
||||
urls = json.loads(response.content)
|
||||
|
||||
def _getMpnFromFootprint(self, footprint):
|
||||
keys = ['mpn', 'Mpn', 'MPN', 'JLC_MPN', 'LCSC_MPN', 'LCSC Part #', 'JLC', 'LCSC']
|
||||
for key in keys:
|
||||
if footprint.HasProperty(key):
|
||||
return footprint.GetProperty(key)
|
||||
@@ -1,14 +0,0 @@
|
||||
import wx
|
||||
|
||||
EVT_RESULT_ID = wx.NewId()
|
||||
|
||||
|
||||
def EVT_RESULT(win, func):
|
||||
win.Connect(-1, -1, EVT_RESULT_ID, func)
|
||||
|
||||
|
||||
class ResultEvent(wx.PyEvent):
|
||||
def __init__(self, data):
|
||||
wx.PyEvent.__init__(self)
|
||||
self.SetEventType(EVT_RESULT_ID)
|
||||
self.data = data
|
||||
+35
-185
@@ -1,233 +1,83 @@
|
||||
# original copyright Aisler and licensed under the MIT license.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# from urllib import response
|
||||
# import json
|
||||
# import requests
|
||||
import os
|
||||
import wx
|
||||
import csv
|
||||
import shutil
|
||||
import tempfile
|
||||
import webbrowser
|
||||
from collections import defaultdict
|
||||
from threading import Thread
|
||||
from .result_event import *
|
||||
from .events import StatusEvent
|
||||
from .process import ProcessManager
|
||||
from .config import *
|
||||
|
||||
|
||||
class ProcessThread(Thread):
|
||||
def __init__(self, wxObject):
|
||||
def __init__(self, wx):
|
||||
Thread.__init__(self)
|
||||
self.wxObject = wxObject
|
||||
|
||||
self.process_manager = ProcessManager()
|
||||
self.wx = wx
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
# initializing
|
||||
self.report(0)
|
||||
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
_, temp_file = tempfile.mkstemp()
|
||||
board = pcbnew.GetBoard()
|
||||
title_block = board.GetTitleBlock()
|
||||
project_directory = os.path.dirname(self.process_manager.board.GetFileName())
|
||||
output_path = os.path.join(project_directory, outputFolder)
|
||||
|
||||
# configure gerber
|
||||
# configure and generate gerber
|
||||
self.report(5)
|
||||
|
||||
settings = board.GetDesignSettings()
|
||||
settings.m_SolderMaskMargin = 0
|
||||
settings.m_SolderMaskMinWidth = 0
|
||||
|
||||
pctl = pcbnew.PLOT_CONTROLLER(board)
|
||||
|
||||
popt = pctl.GetPlotOptions()
|
||||
popt.SetOutputDirectory(temp_dir)
|
||||
popt.SetPlotFrameRef(False)
|
||||
popt.SetSketchPadLineWidth(pcbnew.FromMM(0.1))
|
||||
popt.SetAutoScale(False)
|
||||
popt.SetScale(1)
|
||||
popt.SetMirror(False)
|
||||
popt.SetUseGerberAttributes(True)
|
||||
popt.SetExcludeEdgeLayer(True)
|
||||
popt.SetUseGerberProtelExtensions(False)
|
||||
popt.SetUseAuxOrigin(True)
|
||||
popt.SetSubtractMaskFromSilk(False)
|
||||
popt.SetDrillMarksType(0) # NO_DRILL_SHAPE
|
||||
|
||||
# generate gerber
|
||||
self.report(10)
|
||||
for layer_info in plotPlan:
|
||||
if board.IsLayerEnabled(layer_info[1]):
|
||||
pctl.SetLayer(layer_info[1])
|
||||
pctl.OpenPlotfile(
|
||||
layer_info[0],
|
||||
pcbnew.PLOT_FORMAT_GERBER,
|
||||
layer_info[2])
|
||||
pctl.PlotLayer()
|
||||
|
||||
pctl.ClosePlot()
|
||||
self.process_manager.generate_gerber(temp_dir)
|
||||
|
||||
# generate drill file
|
||||
self.report(15)
|
||||
drlwriter = pcbnew.EXCELLON_WRITER(board)
|
||||
|
||||
drlwriter.SetOptions(
|
||||
False,
|
||||
True,
|
||||
board.GetDesignSettings().GetAuxOrigin(),
|
||||
False)
|
||||
drlwriter.SetFormat(False)
|
||||
drlwriter.CreateDrillandMapFilesSet(pctl.GetPlotDirName(), True, False)
|
||||
|
||||
self.process_manager.generate_drills(temp_dir)
|
||||
|
||||
# generate netlist
|
||||
self.report(25)
|
||||
netlist_writer = pcbnew.IPC356D_WRITER(board)
|
||||
netlist_writer.Write(os.path.join(temp_dir, netlistFileName))
|
||||
self.process_manager.generate_netlist(temp_dir)
|
||||
|
||||
# generate pick and place file
|
||||
self.report(40)
|
||||
components = []
|
||||
bom = []
|
||||
if hasattr(board, 'GetModules'):
|
||||
footprints = list(board.GetModules())
|
||||
else:
|
||||
footprints = list(board.GetFootprints())
|
||||
|
||||
# unique designator dictionary
|
||||
footprint_designators = defaultdict(int)
|
||||
for i, footprint in enumerate(footprints):
|
||||
# count unique designators
|
||||
footprint_designators[footprint.GetReference()] += 1
|
||||
bom_designators = footprint_designators.copy()
|
||||
|
||||
with open((os.path.join(temp_dir, designatorsFileName)), 'w') as f:
|
||||
for key, value in footprint_designators.items():
|
||||
f.write('%s:%s\n' % (key, value))
|
||||
|
||||
for i, footprint in enumerate(footprints):
|
||||
try:
|
||||
footprint_name = str(footprint.GetFPID().GetFootprintName())
|
||||
except AttributeError:
|
||||
footprint_name = str(footprint.GetFPID().GetLibItemName())
|
||||
|
||||
layer = {
|
||||
pcbnew.F_Cu: 'top',
|
||||
pcbnew.B_Cu: 'bottom',
|
||||
}.get(footprint.GetLayer())
|
||||
|
||||
# mount_type = {
|
||||
# 0: 'smt',
|
||||
# 1: 'tht',
|
||||
# 2: 'smt'
|
||||
# }.get(footprint.GetAttributes())
|
||||
|
||||
if not footprint.GetAttributes() & pcbnew.FP_EXCLUDE_FROM_POS_FILES:
|
||||
# append unique ID if duplicate footprint designator
|
||||
unique_id = ""
|
||||
if footprint_designators[footprint.GetReference()] > 1:
|
||||
unique_id = str(footprint_designators[footprint.GetReference()])
|
||||
footprint_designators[footprint.GetReference()] -= 1
|
||||
|
||||
components.append({
|
||||
'Designator': "{}{}{}".format(footprint.GetReference(), "" if unique_id == "" else "_", unique_id),
|
||||
'Mid X': (footprint.GetPosition()[0] - board.GetDesignSettings().GetAuxOrigin()[0]) / 1000000.0,
|
||||
'Mid Y': (footprint.GetPosition()[1] - board.GetDesignSettings().GetAuxOrigin()[1]) * -1.0 / 1000000.0,
|
||||
'Rotation': footprint.GetOrientation() / 10.0,
|
||||
'Layer': layer,
|
||||
})
|
||||
|
||||
if not footprint.GetAttributes() & pcbnew.FP_EXCLUDE_FROM_BOM:
|
||||
# append unique ID if we are dealing with duplicate bom designator
|
||||
unique_id = ""
|
||||
if bom_designators[footprint.GetReference()] > 1:
|
||||
unique_id = str(bom_designators[footprint.GetReference()])
|
||||
bom_designators[footprint.GetReference()] -= 1
|
||||
|
||||
# todo: merge similar parts into single entry
|
||||
|
||||
bom.append({
|
||||
'Designator': "{}{}{}".format(footprint.GetReference(), "" if unique_id == "" else "_", unique_id),
|
||||
'Footprint': footprint_name,
|
||||
'Quantity': 1,
|
||||
'Value': footprint.GetValue(),
|
||||
# 'Mount': mount_type,
|
||||
'LCSC Part #': self.getMpnFromFootprint(footprint),
|
||||
})
|
||||
|
||||
with open((os.path.join(temp_dir, placementFileName)), 'w', newline='') as outfile:
|
||||
header = True
|
||||
csv_writer = csv.writer(outfile)
|
||||
|
||||
for component in components:
|
||||
if header:
|
||||
# writing headers of CSV file
|
||||
csv_writer.writerow(component.keys())
|
||||
header = False
|
||||
|
||||
# writing data of CSV file
|
||||
if ('**' not in component['Designator']):
|
||||
csv_writer.writerow(component.values())
|
||||
self.process_manager.generate_positions(temp_dir)
|
||||
|
||||
# generate BOM file
|
||||
self.report(60)
|
||||
with open((os.path.join(temp_dir, bomFileName)), 'w', newline='') as outfile:
|
||||
header = True
|
||||
csv_writer = csv.writer(outfile)
|
||||
|
||||
for component in bom:
|
||||
if header:
|
||||
# writing headers of CSV file
|
||||
csv_writer.writerow(component.keys())
|
||||
header = False
|
||||
|
||||
# writing data of CSV file
|
||||
if ('**' not in component['Designator']):
|
||||
csv_writer.writerow(component.values())
|
||||
self.process_manager.generate_bom(temp_dir)
|
||||
|
||||
# generate production archive
|
||||
self.report(75)
|
||||
temp_file = shutil.make_archive(temp_file, 'zip', temp_dir)
|
||||
temp_file = shutil.move(temp_file, temp_dir)
|
||||
|
||||
# remove non essential files
|
||||
for item in os.listdir(temp_dir):
|
||||
if not item.endswith(".zip") and not item.endswith(".csv") and not item.endswith(".ipc"):
|
||||
os.remove(os.path.join(temp_dir, item))
|
||||
temp_file = self.process_manager.generate_archive(temp_dir, temp_file)
|
||||
|
||||
# upload files
|
||||
self.report(87.5)
|
||||
boardWidth = pcbnew.Iu2Millimeter(board.GetBoardEdgesBoundingBox().GetWidth())
|
||||
boardHeight = pcbnew.Iu2Millimeter(board.GetBoardEdgesBoundingBox().GetHeight())
|
||||
boardLayer = board.GetCopperLayerCount()
|
||||
#self.report(87.5)
|
||||
#self.process_manager.upload_archive(temp_file)
|
||||
|
||||
# files = {'upload[file]': open(temp_file, 'rb')}
|
||||
# upload_url = baseUrl + '/Common/KiCadUpFile/'
|
||||
|
||||
# response = requests.post(
|
||||
# upload_url, files=files, data={'boardWidth':boardWidth,'boardHeight':boardHeight,'boardLayer':boardLayer})
|
||||
|
||||
# urls = json.loads(response.content)
|
||||
|
||||
readsofar = 0
|
||||
totalsize = os.path.getsize(temp_file)
|
||||
# progress bar done animation
|
||||
read_so_far = 0
|
||||
total_size = os.path.getsize(temp_file)
|
||||
with open(temp_file, 'rb') as file:
|
||||
while True:
|
||||
data = file.read(10)
|
||||
if not data:
|
||||
break
|
||||
readsofar += len(data)
|
||||
percent = readsofar * 1e2 / totalsize
|
||||
read_so_far += len(data)
|
||||
percent = read_so_far * 1e2 / total_size
|
||||
self.report(75 + percent / 8)
|
||||
|
||||
os.rename(temp_file, os.path.join(temp_dir, gerberArchiveName))
|
||||
|
||||
webbrowser.open(temp_dir)
|
||||
# webbrowser.open(urls['redirect'])
|
||||
try:
|
||||
if os.path.exists(output_path):
|
||||
shutil.rmtree(output_path)
|
||||
|
||||
shutil.copytree(temp_dir, output_path)
|
||||
webbrowser.open("file://%s" % (output_path))
|
||||
except Exception as e:
|
||||
webbrowser.open("file://%s" % (temp_dir))
|
||||
|
||||
self.report(-1)
|
||||
|
||||
def report(self, status):
|
||||
wx.PostEvent(self.wxObject, ResultEvent(status))
|
||||
|
||||
def getMpnFromFootprint(self, footprint):
|
||||
keys = ['mpn', 'Mpn', 'MPN', 'JLC_MPN', 'LCSC_MPN', 'LCSC Part #', 'JLC', 'LCSC']
|
||||
for key in keys:
|
||||
if footprint.HasProperty(key):
|
||||
return footprint.GetProperty(key)
|
||||
wx.PostEvent(self.wx, StatusEvent(status))
|
||||
|
||||
Reference in New Issue
Block a user