chore: scafolding + project dir output #3
This commit is contained in:
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
-1
@@ -29,7 +29,7 @@
|
||||
],
|
||||
"versions": [
|
||||
{
|
||||
"version": "0.3.2",
|
||||
"version": "0.4.0",
|
||||
"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,189 @@
|
||||
# from urllib import response
|
||||
# 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') 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='') 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='') 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') }
|
||||
upload_url = baseUrl + '/Common/KiCadUpFile/'
|
||||
|
||||
# response = requests.post(
|
||||
# upload_url, files=files, data={ 'boardWidth': boardWidth, 'boardHeight': boardHeight, 'boardLayer': boardLayer })
|
||||
# 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(output_path)
|
||||
except Exception as e:
|
||||
webbrowser.open(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