Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
bennymeg committed Dec 5, 2024
2 parents c4f876c + ff77eca commit f623083
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 11 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.venv
dist/
.DS_Store
.DS_Store
*.pyc
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,35 @@ _The fields will be queried in the order denoted above._
_The fields will be queried in the order denoted above._


## CLI-Usage
The plugin can also be used via the linux commandline. This can be particularly useful if you need to embed the plugin into an automation pipeline or environment.
The plugin can be called with the command below:
```
python3 -m plugins.cli -p /myProject/myBoard.kicad_pcb
```

The all options from the GUI are also available via the cli interface:
```
python3 -m plugins.cli -h
usage: Fabrication Toolkit [-h] --path PATH [--additionalLayers ADDITIONALLAYERS] [--user1VCut] [--user2AltVCut] [--autoTranslate] [--autoFill]
[--excludeDNP] [--allActiveLayers] [--openBrowser]
Generates JLCPCB production files from a KiCAD board file
options:
-h, --help show this help message and exit
--path PATH, -p PATH Path to KiCAD board file
--additionalLayers ADDITIONALLAYERS, -aL ADDITIONALLAYERS Additional layers(comma-separated)
--user1VCut, -u1 Set User.1 as V-Cut layer
--user2AltVCut, -u2 Use User.2 for alternative Edge-Cut layer
--autoTranslate, -t Apply automatic position/rotation translations
--autoFill, -f Apply automatic fill for all zones
--excludeDNP, -e Exclude DNP components from BOM
--allActiveLayers, -aaL Export all active layers instead of only commonly used ones
--openBrowser, -b Open webbrowser with directory file overview after generation
```


## Author

Expand Down
36 changes: 36 additions & 0 deletions plugins/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import argparse as ap

from .thread import ProcessThread
from .options import *


if __name__ == '__main__':
parser = ap.ArgumentParser(prog="Fabrication Toolkit",
description="Generates JLCPCB production files from a KiCAD board file")

parser.add_argument("--path", "-p", type=str, help="Path to KiCAD board file", required=True)
parser.add_argument("--additionalLayers", "-aL", type=str, help="Additional layers(comma-separated)")
parser.add_argument("--user1VCut", "-u1", action="store_true", help="Set User.1 as V-Cut layer")
parser.add_argument("--user2AltVCut", "-u2", action="store_true", help="Use User.2 for alternative Edge-Cut layer")
parser.add_argument("--autoTranslate", "-t", action="store_true", help="Apply automatic position/rotation translations")
parser.add_argument("--autoFill", "-f", action="store_true", help="Apply automatic fill for all zones")
parser.add_argument("--excludeDNP", "-e", action="store_true", help="Exclude DNP components from BOM")
parser.add_argument("--allActiveLayers", "-aaL",action="store_true", help="Export all active layers instead of only commonly used ones")
parser.add_argument("--openBrowser", "-b", action="store_true", help="Open webbrowser with directory file overview after generation")
args = parser.parse_args()

options = dict()
options[AUTO_TRANSLATE_OPT] = args.autoTranslate
options[AUTO_FILL_OPT] = args.autoFill
options[EXCLUDE_DNP_OPT] = args.excludeDNP
options[EXTEND_EDGE_CUT_OPT] = args.user1VCut
options[ALTERNATIVE_EDGE_CUT_OPT] = args.user2AltVCut
options[ALL_ACTIVE_LAYERS_OPT] = args.allActiveLayers
options[EXTRA_LAYERS] = args.additionalLayers

openBrowser = args.openBrowser


path = args.path

ProcessThread(wx=None, cli=path, options=options, openBrowser=openBrowser)
8 changes: 6 additions & 2 deletions plugins/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@
from .config import *

class ProcessManager:
def __init__(self):
self.board = pcbnew.GetBoard()
def __init__(self, board = None):
# if no board is already loaded by cli mode getBoard from kicad environment
if board is None:
self.board = pcbnew.GetBoard()
else:
self.board = board
self.bom = []
self.components = []
self.__rotation_db = self.__read_rotation_db()
Expand Down
45 changes: 37 additions & 8 deletions plugins/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,38 @@
import tempfile
import webbrowser
import datetime
import logging
from threading import Thread
from .events import StatusEvent
from .process import ProcessManager
from .config import *
from .options import *
from .utils import print_cli_progress_bar


class ProcessThread(Thread):
def __init__(self, wx, options):
def __init__(self, wx, options, cli = None, openBrowser = True):
Thread.__init__(self)

self.process_manager = ProcessManager()
# prevent use of cli and grapgical mode at the same time
if (wx is None and cli is None) or (wx is not None and cli is not None):
logging.error("Specify either graphical or cli use!")
return

if cli is not None:
try:
self.board = pcbnew.LoadBoard(cli)
except Exception as e:
logging.error("Fabrication Toolkit - Error" + str(e))
return
else:
self.board = None

self.process_manager = ProcessManager(self.board)
self.wx = wx
self.cli = cli
self.options = options
self.openBrowser = openBrowser
self.start()

def run(self):
Expand Down Expand Up @@ -70,7 +88,10 @@ def run(self):
shutil.rmtree(temp_dir_gerber)
temp_file = os.path.join(temp_dir, os.path.basename(temp_file))
except Exception as e:
wx.MessageBox(str(e), "Fabrication Toolkit - Error", wx.OK | wx.ICON_ERROR)
if self.wx is None:
logging.error("Fabrication Toolkit - Error" + str(e))
else:
wx.MessageBox(str(e), "Fabrication Toolkit - Error", wx.OK | wx.ICON_ERROR)
self.progress(-1)
return

Expand Down Expand Up @@ -118,12 +139,20 @@ def run(self):
# copy to & open output dir
try:
shutil.copytree(temp_dir, output_path, dirs_exist_ok=True)
webbrowser.open("file://%s" % (output_path))
if self.openBrowser:
webbrowser.open("file://%s" % (output_path))
shutil.rmtree(temp_dir)
except Exception as e:
webbrowser.open("file://%s" % (temp_dir))
except Exception as e:
if self.openBrowser:
webbrowser.open("file://%s" % (temp_dir))

self.progress(-1)
if self.wx is None:
self.progress(100)
else:
self.progress(-1)

def progress(self, percent):
wx.PostEvent(self.wx, StatusEvent(percent))
if self.wx is None:
print_cli_progress_bar(percent, prefix = 'Progress:', suffix = 'Complete', length = 50)
else:
wx.PostEvent(self.wx, StatusEvent(percent))
22 changes: 22 additions & 0 deletions plugins/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,25 @@ def get_layer_names(board, active_only=True):
"""Returns a list of (active) layer names of the current board"""
plotPlan = get_plot_plan(board, active_only)
return [layer_info[0] for layer_info in plotPlan]

def print_cli_progress_bar(percent, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', printEnd = "\r"):
"""
Call in a loop to create terminal progress bar string
@params:
percent - Required : current percentage (Int)
prefix - Optional : prefix string (Str)
suffix - Optional : suffix string (Str)
decimals - Optional : positive number of decimals in percent complete (Int)
length - Optional : character length of bar (Int)
fill - Optional : bar fill character (Str)
printEnd - Optional : end character (e.g. "\r", "\r\n") (Str)
"""
if percent == -1:
percent = 0
filledLength = int(length * (percent / 100 ))
bar = fill * filledLength + '-' * (length - filledLength)
percent2dec = "%.2f" % percent
print(f'\r{prefix} |{bar}| {percent2dec}% {suffix}', end = printEnd)
# Print New Line on Complete
if percent == 100:
print()

0 comments on commit f623083

Please sign in to comment.