Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c08e18ef9 |
@@ -1,13 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: bennymeg
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
@@ -1 +0,0 @@
|
||||
- Hildo Guillardi Júnior
|
||||
@@ -6,8 +6,6 @@
|
||||
| **JLC PCB Plug-in for KiCad** |
|
||||
|:-----:|
|
||||
|
||||
[](https://github.com/sponsors/bennymeg)
|
||||
|
||||
</div>
|
||||
|
||||
## Features
|
||||
@@ -19,82 +17,27 @@
|
||||
## Installation
|
||||
|
||||
### Official Installation
|
||||
Fabrication Toolkit is distributed with the official releases of KiCad 6+. Open the "Plugin and Content Manager" from the KiCad main menu and install the "<ins>Fabrication Toolkit</ins>" plugin from the selection list.
|
||||
|
||||
<img src="https://github.com/bennymeg/JLC-Plugin-for-KiCad/blob/master/assets/manager.png?raw=true" height=275>
|
||||
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 release](https://github.com/bennymeg/JLC-Plugin-for-KiCad/releases) ZIP file. Open the "Plugin and Content Manager" from the KiCads main window and install the ZIP file via "Install from File".
|
||||
|
||||
## Usage
|
||||
Click on the Fabrication Toolkit <img src="https://github.com/bennymeg/JLC-Plugin-for-KiCad/blob/master/resources/icon.png?raw=true" style="magin-bottom: 8px;" alt="Logo" height=24> button on the top tool box inside KiCad pcb editor (pcbnew).
|
||||
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".
|
||||
|
||||
## Options
|
||||
|
||||
### Include Component Part Number in Production Files
|
||||
Add an 'LCSC Part #'* field with the LCSC component part number to the symbol's fields property.
|
||||
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>
|
||||
|
||||
#### Primary Fields*:
|
||||
| 'LCSC Part #' | 'JLCPCB Part #' |
|
||||
| --- | --- |
|
||||
|
||||
_The fields will be query in the order denoted above._
|
||||
|
||||
#### Fallback Fields*:
|
||||
| 'lcsc#' | 'LCSC' | 'JLC' | 'MPN' | 'Mpn' | 'mpn' |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
|
||||
_The fields will be query in the order denoted above._
|
||||
#### Similar Fields*:
|
||||
|'mpn' | 'Mpn' | 'MPN' | 'JLC_MPN' | 'LCSC_MPN' | 'LCSC Part #' | 'JLC' | 'LCSC'|
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
|
||||
### Ignore Footprint in Production Files
|
||||
Select 'Exclude from board' or 'Exclude from BOM' in the symbol's attributes property 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>
|
||||
|
||||
Select 'Exclude from position files' or 'Exclude from BOM' in the footprint's fabrication 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/fabrication.png?raw=true" height=505>
|
||||
|
||||
### Offset Component Rotation
|
||||
The rotation of components in KiCad Footprints does not always match the orientation in the JLC library because KiCad and JLCPB used different variation of the same standard. Most of the rotations may be corrected by the `rotations.cf` definitions. To the exception cases: add an 'JLC Rotation Offset' field with an counter-clockwise orientation offset in degrees to correct this.
|
||||
|
||||
<img src="https://github.com/bennymeg/JLC-Plugin-for-KiCad/blob/master/assets/rotation-jlc.png?raw=true" height=164>
|
||||
|
||||
If the JLC preview shows a footprint like this, enter a rotation offset of -90 to rotate pin 1 to the lower right corner.
|
||||
|
||||
<img src="https://github.com/bennymeg/JLC-Plugin-for-KiCad/blob/master/assets/rotation.png?raw=true" height=420>
|
||||
|
||||
#### Primary Fields*:
|
||||
| 'JLCPCB Rotation Offset' |
|
||||
| --- |
|
||||
|
||||
_The fields will be queried in the order denoted above._
|
||||
|
||||
#### Fallback Fields*:
|
||||
| 'JlcRotOffset' | 'JLCRotOffset' |
|
||||
| --- | --- |
|
||||
|
||||
_The fields will be queried in the order denoted above._
|
||||
|
||||
### Offset Component Position
|
||||
The position of components in KiCad Footprints does not always match the orientation in the JLC library because KiCad and JLCPB used different variation of the same standard. To the exception cases: add an 'JLC Position Offset' field with an comma seperated x,y position offset to correct it.
|
||||
|
||||
<img src="https://github.com/bennymeg/JLC-Plugin-for-KiCad/blob/master/assets/position.png?raw=true" height=420>
|
||||
|
||||
#### Primary Fields*:
|
||||
| 'JLCPCB Position Offset' |
|
||||
| --- |
|
||||
|
||||
_The fields will be queried in the order denoted above._
|
||||
|
||||
#### Fallback Fields*:
|
||||
| 'JlcPosOffset' | 'JLCPosOffset' |
|
||||
| --- | --- |
|
||||
|
||||
_The fields will be queried in the order denoted above._
|
||||
|
||||
## Author
|
||||
|
||||
Benny Megidish
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 114 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 189 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 250 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 248 KiB |
+4
-4
@@ -2,7 +2,7 @@
|
||||
"$schema": "https://go.kicad.org/pcm/schemas/v1",
|
||||
"name": "Fabrication Toolkit",
|
||||
"description": "JLC PCB fabrication toolkit",
|
||||
"description_full": "Toolkit for automating PCB fabrication process with KiCad and JLC PCB. \n\n OPTIONS: \n\u2022 Include Component Part Number in Production Files \n Add an 'MPN' field with the LCSC component part number to the footprint component options. \n\n\u2022 Ignore Footprint in Production Files \n Select 'Exclude from position files' or 'Exclude from BOM' in the footprint properties fabrication attributes in order to ignore the footprint from the relevant file. \n\n\u2022 Rotate components \n Add an 'JLCPCB Rotation Offset' field with the additional counter-clockwise rotation in degrees to correct a mismatch between KiCad and JLC footprint orientation.",
|
||||
"description_full": "Toolkit for automating PCB fabrication process with KiCad and JLC PCB. \n\n OPTIONS: \n\u2022 Include Component Part Number in Production Files \n Add an 'MPN' field with the LCSC component part number to the footprint component options. \n\n\u2022 Ignore Footprint in Production Files \n Select 'Exclude from position files' or 'Exclude from BOM' in the footprint component options to in order ignore the footprint from the relevant file.",
|
||||
"identifier": "com.github.bennymeg.JLC-Plugin-for-KiCad",
|
||||
"type": "plugin",
|
||||
"author": {
|
||||
@@ -29,9 +29,9 @@
|
||||
],
|
||||
"versions": [
|
||||
{
|
||||
"version": "2.1.0",
|
||||
"status": "stable",
|
||||
"version": "0.4.2",
|
||||
"status": "testing",
|
||||
"kicad_version": "6.00"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -43,7 +43,7 @@ class KiCadToJLCForm(wx.Frame):
|
||||
pcbnew.Refresh()
|
||||
self.Destroy()
|
||||
else:
|
||||
self.m_gaugeStatus.SetValue(int(status.data))
|
||||
self.m_gaugeStatus.SetValue(status.data)
|
||||
|
||||
|
||||
# Plugin definition
|
||||
|
||||
+59
-176
@@ -1,18 +1,10 @@
|
||||
# For better annotation.
|
||||
from __future__ import annotations
|
||||
|
||||
# System base libraries
|
||||
import json
|
||||
import requests
|
||||
import os
|
||||
import csv
|
||||
import math
|
||||
import shutil
|
||||
from collections import defaultdict
|
||||
import re
|
||||
|
||||
# Interaction with KiCad.
|
||||
import pcbnew
|
||||
|
||||
# Application definitions.
|
||||
from collections import defaultdict
|
||||
from .config import *
|
||||
|
||||
|
||||
@@ -21,49 +13,8 @@ class ProcessManager:
|
||||
self.board = pcbnew.GetBoard()
|
||||
self.bom = []
|
||||
self.components = []
|
||||
self.__rotation_db = self.__read_rotation_db()
|
||||
|
||||
@staticmethod
|
||||
def __read_rotation_db(filename: str = os.path.join(os.path.dirname(__file__), 'rotations.cf')) -> dict[str, float]:
|
||||
'''Read the rotations.cf config file so we know what rotations
|
||||
to apply later.
|
||||
'''
|
||||
db = {}
|
||||
|
||||
with open(filename, 'r') as fh:
|
||||
for line in fh:
|
||||
line = line.rstrip()
|
||||
line = re.sub('#.*$', '', line) # remove anything after a comment
|
||||
line = re.sub('\s*$', '', line) # remove all trailing space
|
||||
|
||||
if (line == ""):
|
||||
continue
|
||||
|
||||
match = re.match('^([^\s]+)\s+(\d+)$', line)
|
||||
|
||||
if match:
|
||||
db.update({ match.group(1): int(match.group(2)) })
|
||||
|
||||
return db
|
||||
|
||||
def _get_rotation_from_db(self, footprint: str) -> float:
|
||||
'''Get the rotation to be added from the database file.'''
|
||||
# Look for regular expression math of the footprint name and not its root library.
|
||||
fpshort = footprint.split(':')[-1]
|
||||
|
||||
for expression, delta in self.db.items():
|
||||
fp = fpshort
|
||||
|
||||
if (re.search(':', expression)):
|
||||
fp = footprint
|
||||
|
||||
if(re.search(expression, fp)):
|
||||
return delta
|
||||
|
||||
return 0.0
|
||||
|
||||
def generate_gerber(self, temp_dir):
|
||||
'''Generate the Gerber files.'''
|
||||
settings = self.board.GetDesignSettings()
|
||||
settings.m_SolderMaskMargin = 0
|
||||
settings.m_SolderMaskMinWidth = 0
|
||||
@@ -78,13 +29,11 @@ class ProcessManager:
|
||||
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(True)
|
||||
plot_options.SetSubtractMaskFromSilk(False)
|
||||
plot_options.SetDrillMarksType(0) # NO_DRILL_SHAPE
|
||||
|
||||
if hasattr(plot_options, "SetExcludeEdgeLayer"):
|
||||
plot_options.SetExcludeEdgeLayer(True)
|
||||
|
||||
for layer_info in plotPlan:
|
||||
if self.board.IsLayerEnabled(layer_info[1]):
|
||||
@@ -98,7 +47,6 @@ class ProcessManager:
|
||||
plot_controller.ClosePlot()
|
||||
|
||||
def generate_drills(self, temp_dir):
|
||||
'''Generate the drill file.'''
|
||||
drill_writer = pcbnew.EXCELLON_WRITER(self.board)
|
||||
|
||||
drill_writer.SetOptions(
|
||||
@@ -110,20 +58,15 @@ class ProcessManager:
|
||||
drill_writer.CreateDrillandMapFilesSet(temp_dir, True, False)
|
||||
|
||||
def generate_netlist(self, temp_dir):
|
||||
'''Generate the connection netlist.'''
|
||||
netlist_writer = pcbnew.IPC356D_WRITER(self.board)
|
||||
netlist_writer.Write(os.path.join(temp_dir, netlistFileName))
|
||||
|
||||
def generate_positions(self, temp_dir):
|
||||
'''Generate the position files.'''
|
||||
if hasattr(self.board, 'GetModules'):
|
||||
footprints = list(self.board.GetModules())
|
||||
else:
|
||||
footprints = list(self.board.GetFootprints())
|
||||
|
||||
# sort footprint after designator
|
||||
footprints.sort(key=lambda x: x.GetReference())
|
||||
|
||||
# unique designator dictionary
|
||||
footprint_designators = defaultdict(int)
|
||||
for i, footprint in enumerate(footprints):
|
||||
@@ -131,10 +74,9 @@ class ProcessManager:
|
||||
footprint_designators[footprint.GetReference()] += 1
|
||||
bom_designators = footprint_designators.copy()
|
||||
|
||||
if len(footprint_designators.items()) > 0:
|
||||
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))
|
||||
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:
|
||||
@@ -160,27 +102,11 @@ class ProcessManager:
|
||||
unique_id = str(footprint_designators[footprint.GetReference()])
|
||||
footprint_designators[footprint.GetReference()] -= 1
|
||||
|
||||
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().AsDegrees() if hasattr(footprint.GetOrientation(), 'AsDegrees') else footprint.GetOrientation() / 10.0
|
||||
# Get the rotation offset to be added to the actual rotation prioritizing the explicated by the
|
||||
# designer at the standards symbol fields. If not specified use the internal database.
|
||||
rotation_offset = self._get_rotation_offset_from_footprint(footprint) #or self._get_rotation_from_db(footprint)
|
||||
rotation = (rotation + rotation_offset) % 360.0
|
||||
|
||||
# position offset needs to take rotation into account
|
||||
pos_offset = self._get_position_offset_from_footprint(footprint)
|
||||
rsin = math.sin(rotation / 180 * math.pi)
|
||||
rcos = math.cos(rotation / 180 * math.pi)
|
||||
pos_offset = ( pos_offset[0] * rcos - pos_offset[1] * rsin, pos_offset[0] * rsin + pos_offset[1] * rcos )
|
||||
mid_x, mid_y = tuple(map(sum,zip((mid_x, mid_y), pos_offset)))
|
||||
|
||||
self.components.append({
|
||||
'Designator': designator,
|
||||
'Mid X': mid_x,
|
||||
'Mid Y': mid_y,
|
||||
'Rotation': rotation,
|
||||
'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,
|
||||
})
|
||||
|
||||
@@ -191,51 +117,48 @@ class ProcessManager:
|
||||
unique_id = str(bom_designators[footprint.GetReference()])
|
||||
bom_designators[footprint.GetReference()] -= 1
|
||||
|
||||
# merge similar parts into single entry
|
||||
insert = True
|
||||
for component in self.bom:
|
||||
if component['Footprint'] == self._normalize_footprint_name(footprint_name) and component['Value'].upper() == footprint.GetValue().upper():
|
||||
component['Designator'] += ", " + "{}{}{}".format(footprint.GetReference(), "" if unique_id == "" else "_", unique_id)
|
||||
component['Quantity'] += 1
|
||||
insert = False
|
||||
# todo: merge similar parts into single entry
|
||||
|
||||
# add component to BOM
|
||||
if insert:
|
||||
self.bom.append({
|
||||
'Designator': "{}{}{}".format(footprint.GetReference(), "" if unique_id == "" else "_", unique_id),
|
||||
'Footprint': self._normalize_footprint_name(footprint_name),
|
||||
'Quantity': 1,
|
||||
'Value': footprint.GetValue(),
|
||||
# 'Mount': mount_type,
|
||||
'LCSC Part #': self._get_mpn_from_footprint(footprint),
|
||||
})
|
||||
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),
|
||||
})
|
||||
|
||||
if len(self.components) > 0:
|
||||
with open((os.path.join(temp_dir, placementFileName)), 'w', newline='', encoding='utf-8') as outfile:
|
||||
csv_writer = csv.writer(outfile)
|
||||
# writing headers of CSV file
|
||||
csv_writer.writerow(self.components[0].keys())
|
||||
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:
|
||||
# writing data of CSV file
|
||||
if ('**' not in component['Designator']):
|
||||
csv_writer.writerow(component.values())
|
||||
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):
|
||||
if len(self.bom) > 0:
|
||||
with open((os.path.join(temp_dir, bomFileName)), 'w', newline='', encoding='utf-8') as outfile:
|
||||
csv_writer = csv.writer(outfile)
|
||||
# writing headers of CSV file
|
||||
csv_writer.writerow(self.bom[0].keys())
|
||||
|
||||
# Output all of the component information
|
||||
for component in self.bom:
|
||||
# writing data of CSV file
|
||||
if ('**' not in component['Designator']):
|
||||
csv_writer.writerow(component.values())
|
||||
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):
|
||||
'''Generate the files.'''
|
||||
temp_file = shutil.make_archive(temp_file, 'zip', temp_dir)
|
||||
temp_file = shutil.move(temp_file, temp_dir)
|
||||
|
||||
@@ -246,60 +169,20 @@ class ProcessManager:
|
||||
|
||||
return temp_file
|
||||
|
||||
def _get_mpn_from_footprint(self, footprint: str):
|
||||
''''Get the MPN/LCSC stock code from standard symbol fields.'''
|
||||
keys = ['LCSC Part #', 'JLCPCB Part #']
|
||||
fallback_keys = ['LCSC', 'JLC', 'MPN', 'Mpn', 'mpn']
|
||||
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)
|
||||
|
||||
for key in fallback_keys:
|
||||
if footprint.HasProperty(key):
|
||||
return footprint.GetProperty(key)
|
||||
|
||||
def _get_rotation_offset_from_footprint(self, footprint: str) -> float:
|
||||
'''Get the rotation from standard symbol fields.'''
|
||||
keys = ['JLCPCB Rotation Offset']
|
||||
fallback_keys = ['JlcRotOffset', 'JLCRotOffset']
|
||||
|
||||
offset = None
|
||||
|
||||
for key in keys + fallback_keys:
|
||||
if footprint.HasProperty(key):
|
||||
offset = footprint.GetProperty(key)
|
||||
break
|
||||
|
||||
if offset is None or offset == "":
|
||||
return 0
|
||||
else:
|
||||
try:
|
||||
return float(offset)
|
||||
except ValueError:
|
||||
raise RuntimeError("Rotation offset of {} is not a valid number".format(footprint.GetReference()))
|
||||
|
||||
def _get_position_offset_from_footprint(self, footprint):
|
||||
keys = ['JLCPCB Position Offset']
|
||||
fallback_keys = ['JlcPosOffset', 'JLCPosOffset']
|
||||
|
||||
offset = None
|
||||
|
||||
for key in keys + fallback_keys:
|
||||
if footprint.HasProperty(key):
|
||||
offset = footprint.GetProperty(key)
|
||||
break
|
||||
|
||||
if offset is None or offset == "":
|
||||
return (0, 0)
|
||||
else:
|
||||
try:
|
||||
return ( float(offset.split(",")[0]), float(offset.split(",")[1]) )
|
||||
except ValueError:
|
||||
raise RuntimeError("Position offset of {} is not a valid pair of numbers".format(footprint.GetReference()))
|
||||
|
||||
def _normalize_footprint_name(self, footprint):
|
||||
# replace footprint names of resistors, capacitors, inductors, diodes, LEDs, fuses etc, with the footprint size only
|
||||
pattern = re.compile(r'^(\w*_SMD:)?\w{1,4}_(\d+)_\d+Metric.*$')
|
||||
|
||||
return pattern.sub(r'\2', footprint)
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
#
|
||||
# Rotations for jlcpcba
|
||||
#
|
||||
# This defines additional rotations we need to apply to ensure the
|
||||
# footprints are correctly orientated to work with the JLCPCB PBCA
|
||||
# service
|
||||
#
|
||||
# These are regular expressions that are matched by default against
|
||||
# the short footprint name (without the library name), however if there
|
||||
# is a colon in the regex then the full name (with the library name) is
|
||||
# used.
|
||||
#
|
||||
# The matches are checked against one by one, and the first match
|
||||
# is used
|
||||
#
|
||||
# The regex and the rotation value is separated by any amount of
|
||||
# whitespace, blank lines and comments are ignored
|
||||
#
|
||||
|
||||
#
|
||||
# Normal short-name matches
|
||||
#
|
||||
^SOT-223 180
|
||||
^SOT-23 180
|
||||
^D_SOT-23 180
|
||||
^TSOT-23 180
|
||||
^SOT-353 180
|
||||
^QFN- 90
|
||||
^qfn- 90
|
||||
^LQFP- 270
|
||||
^TQFP- 270
|
||||
^MSOP- 270
|
||||
^TSSOP- 270
|
||||
^DFN- 270
|
||||
^SOIC-8_ 270
|
||||
^SOIC-16_ 270
|
||||
^VSSOP-10_- 270
|
||||
|
||||
#
|
||||
# Polarised caps are 180 out
|
||||
#
|
||||
^CP_Elec_ 180
|
||||
^C_Elec_ 180
|
||||
^CP_EIA- 180
|
||||
|
||||
#
|
||||
# Long name matches
|
||||
#
|
||||
#^Lees_Footprints: 270
|
||||
^LED_WS2812B_PLCC4 180
|
||||
|
||||
# More rotation from https://github.com/matthewlai/JLCKicadTools/tree/master/jlc_kicad_tools
|
||||
^R_Array_Convex_ 90
|
||||
^R_Array_Concave_ 90
|
||||
^SOP-4_ 0
|
||||
^SOP-(?!18_) 270
|
||||
^SOP-18_ 0
|
||||
^VSSOP-8_ 270
|
||||
^Bosch_LGA- 90
|
||||
^PowerPAK_SO-8_Single 270
|
||||
^HTSSOP- 270
|
||||
+22
-23
@@ -26,34 +26,33 @@ class ProcessThread(Thread):
|
||||
project_directory = os.path.dirname(self.process_manager.board.GetFileName())
|
||||
output_path = os.path.join(project_directory, outputFolder)
|
||||
|
||||
try:
|
||||
# configure and generate gerber
|
||||
self.report(5)
|
||||
self.process_manager.generate_gerber(temp_dir)
|
||||
# configure and generate gerber
|
||||
self.report(5)
|
||||
self.process_manager.generate_gerber(temp_dir)
|
||||
|
||||
# generate drill file
|
||||
self.report(15)
|
||||
self.process_manager.generate_drills(temp_dir)
|
||||
# generate drill file
|
||||
self.report(15)
|
||||
self.process_manager.generate_drills(temp_dir)
|
||||
|
||||
# generate netlist
|
||||
self.report(25)
|
||||
self.process_manager.generate_netlist(temp_dir)
|
||||
|
||||
# generate netlist
|
||||
self.report(25)
|
||||
self.process_manager.generate_netlist(temp_dir)
|
||||
# generate pick and place file
|
||||
self.report(40)
|
||||
self.process_manager.generate_positions(temp_dir)
|
||||
|
||||
# generate pick and place file
|
||||
self.report(40)
|
||||
self.process_manager.generate_positions(temp_dir)
|
||||
# generate BOM file
|
||||
self.report(60)
|
||||
self.process_manager.generate_bom(temp_dir)
|
||||
|
||||
# generate BOM file
|
||||
self.report(60)
|
||||
self.process_manager.generate_bom(temp_dir)
|
||||
# generate production archive
|
||||
self.report(75)
|
||||
temp_file = self.process_manager.generate_archive(temp_dir, temp_file)
|
||||
|
||||
# generate production archive
|
||||
self.report(75)
|
||||
temp_file = self.process_manager.generate_archive(temp_dir, temp_file)
|
||||
except Exception as e:
|
||||
wx.MessageBox(str(e), "Error", wx.OK | wx.ICON_ERROR)
|
||||
self.report(-1)
|
||||
return
|
||||
# upload files
|
||||
#self.report(87.5)
|
||||
#self.process_manager.upload_archive(temp_file)
|
||||
|
||||
# progress bar done animation
|
||||
read_so_far = 0
|
||||
|
||||
Reference in New Issue
Block a user