[Stencil_3D] New output

- KiKit's "stencil createprinted"
This commit is contained in:
Salvador E. Tropea 2022-12-01 11:15:12 -03:00
parent 5767a03868
commit 2a46ab1cff
13 changed files with 388 additions and 16 deletions

View File

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `populate` to create step-by-step assembly instructions - `populate` to create step-by-step assembly instructions
With support for `pcbdraw` and `render_3d`. With support for `pcbdraw` and `render_3d`.
- `panelize` to create a PCB panel containing N copies of the PCB. - `panelize` to create a PCB panel containing N copies of the PCB.
- `stencil_3d` to create 3D self-registering printable stencils.
- generic filters: options to filter by PCB side - generic filters: options to filter by PCB side
- BoM: - BoM:
- Option to link to Mouser site. - Option to link to Mouser site.

View File

@ -97,6 +97,8 @@ For example, it's common that you might want for each board rev:
* PCB 3D model in STEP format * PCB 3D model in STEP format
* PCB 3D render in PNG format * PCB 3D render in PNG format
* Compare PCB/SCHs * Compare PCB/SCHs
* Panelization
* Stencil creation
You want to do this in a one-touch way, and make sure everything you need to You want to do this in a one-touch way, and make sure everything you need to
do so is securely saved in version control, not on the back of an old do so is securely saved in version control, not on the back of an old
@ -142,6 +144,9 @@ Notes:
- Show KiAuto installation information for `info` (v2.0.0) - Show KiAuto installation information for `info` (v2.0.0)
- Print the page frame in GUI mode for `pcb_print` (v1.6.7) - Print the page frame in GUI mode for `pcb_print` (v1.6.7)
[**KiKit**](https://github.com/yaqwsx/KiKit) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://github.com/yaqwsx/KiKit) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png)
- Mandatory for: `panelize`, `stencil_3d`
[**LXML**](https://pypi.org/project/LXML/) [![Python module](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/Python-logo-notext-22x22.png)](https://pypi.org/project/LXML/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/python3-lxml) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) [**LXML**](https://pypi.org/project/LXML/) [![Python module](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/Python-logo-notext-22x22.png)](https://pypi.org/project/LXML/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/python3-lxml) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png)
- Mandatory for: `pcb_print`, `pcbdraw` - Mandatory for: `pcb_print`, `pcbdraw`
@ -158,12 +163,12 @@ Notes:
[**KiCad PCB/SCH Diff**](https://github.com/INTI-CMNB/KiDiff) v2.4.3 [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://github.com/INTI-CMNB/KiDiff) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) [**KiCad PCB/SCH Diff**](https://github.com/INTI-CMNB/KiDiff) v2.4.3 [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://github.com/INTI-CMNB/KiDiff) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png)
- Mandatory for `diff` - Mandatory for `diff`
[**KiKit**](https://github.com/yaqwsx/KiKit) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://github.com/yaqwsx/KiKit) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png)
- Mandatory for `panelize`
[**mistune**](https://pypi.org/project/mistune/) [![Python module](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/Python-logo-notext-22x22.png)](https://pypi.org/project/mistune/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/python3-mistune) [**mistune**](https://pypi.org/project/mistune/) [![Python module](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/Python-logo-notext-22x22.png)](https://pypi.org/project/mistune/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/python3-mistune)
- Mandatory for `populate` - Mandatory for `populate`
[**OpenSCAD**](https://openscad.org/) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://openscad.org/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/openscad)
- Mandatory for `stencil_3d`
[**QRCodeGen**](https://pypi.org/project/QRCodeGen/) [![Python module](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/Python-logo-notext-22x22.png)](https://pypi.org/project/QRCodeGen/) [![PyPi dependency](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/PyPI_logo_simplified-22x22.png)](https://pypi.org/project/QRCodeGen/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/python3-qrcodegen) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) [**QRCodeGen**](https://pypi.org/project/QRCodeGen/) [![Python module](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/Python-logo-notext-22x22.png)](https://pypi.org/project/QRCodeGen/) [![PyPi dependency](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/PyPI_logo_simplified-22x22.png)](https://pypi.org/project/QRCodeGen/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/python3-qrcodegen) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png)
- Mandatory for `qr_lib` - Mandatory for `qr_lib`
@ -3555,6 +3560,55 @@ Notes:
Internally we use 10 for low priority, 90 for high priority and 50 for most outputs. Internally we use 10 for low priority, 90 for high priority and 50 for most outputs.
- `run_by_default`: [boolean=true] When enabled this output will be created when no specific outputs are requested. - `run_by_default`: [boolean=true] When enabled this output will be created when no specific outputs are requested.
* 3D Printed Stencils
* Type: `stencil_3d`
* Description: Creates a 3D self-registering model of a stencil you can easily print on
SLA printer, you can use it to apply solder paste to your PCB.
These stencils are quick solution when you urgently need a stencil but probably
they don't last long and might come with imperfections.
It currently uses KiKit, so please read
[KiKit docs](https://github.com/yaqwsx/KiKit/blob/master/doc/stencil.md).
Note that we don't implement `--ignore` option, you should use a variant for this
* Valid keys:
- **`comment`**: [string=''] A comment for documentation purposes.
- **`dir`**: [string='./'] Output directory for the generated files.
If it starts with `+` the rest is concatenated to the default dir.
- **`name`**: [string=''] Used to identify this particular output definition.
- **`options`**: [dict] Options for the `Stencil_3D` output.
* Valid keys:
- **`output`**: [string='%f-%i%I%v.%x'] Filename for the output (%i='stencil_3d_top'|'stencil_3d_bottom'|'stencil_3d_edge',
%x='stl'|'scad'|'dxf'). Affected by global options.
- **`thickness`**: [number=0.15] Stencil thickness [mm]. Defines amount of paste dispensed.
- `cutout`: [string|list(string)] List of components to add a cutout based on the component courtyard.
This is useful when you have already pre-populated board and you want to populate more
components.
- `dnf_filter`: [string|list(string)='_none'] Name of the filter to mark components as not fitted.
A short-cut to use for simple cases where a variant is an overkill.
- *enlarge_holes*: Alias for enlarge_holes.
- `enlargeholes`: [number=0] Enlarge pad holes by x mm.
- *frame_clearance*: Alias for frameclearance.
- *frame_width*: Alias for framewidth.
- `frameclearance`: [number=0] Clearance for the stencil register [mm].
- `framewidth`: [number=1] Register frame width.
- `include_scad`: [boolean=true] Include the generated OpenSCAD files. Note that this also includes the DXF files.
- *pcb_thickness*: Alias for pcbthickness.
- `pcbthickness`: [number=0] PCB thickness [mm]. If 0 we will ask KiCad.
- `pre_transform`: [string|list(string)='_none'] Name of the filter to transform fields before applying other filters.
A short-cut to use for simple cases where a variant is an overkill.
- `side`: [string='auto'] [top,bottom,auto,both] Which side of the PCB we want. Using `auto` will detect which
side contains solder paste.
- `variant`: [string=''] Board variant to apply.
- `category`: [string|list(string)=''] The category for this output. If not specified an internally defined category is used.
Categories looks like file system paths, i.e. PCB/fabrication/gerber.
- `disable_run_by_default`: [string|boolean] Use it to disable the `run_by_default` status of other output.
Useful when this output extends another and you don't want to generate the original.
Use the boolean true value to disable the output you are extending.
- `extends`: [string=''] Copy the `options` section from the indicated output.
- `output_id`: [string=''] Text to use for the %I expansion content. To differentiate variations of this output.
- `priority`: [number=50] [0,100] Priority for this output. High priority outputs are created first.
Internally we use 10 for low priority, 90 for high priority and 50 for most outputs.
- `run_by_default`: [boolean=true] When enabled this output will be created when no specific outputs are requested.
* STEP (ISO 10303-21 Clear Text Encoding of the Exchange Structure) * STEP (ISO 10303-21 Clear Text Encoding of the Exchange Structure)
* Type: `step` * Type: `step`
* Description: Exports the PCB as a 3D model. * Description: Exports the PCB as a 3D model.

View File

@ -96,6 +96,8 @@ For example, it's common that you might want for each board rev:
* PCB 3D model in STEP format * PCB 3D model in STEP format
* PCB 3D render in PNG format * PCB 3D render in PNG format
* Compare PCB/SCHs * Compare PCB/SCHs
* Panelization
* Stencil creation
You want to do this in a one-touch way, and make sure everything you need to You want to do this in a one-touch way, and make sure everything you need to
do so is securely saved in version control, not on the back of an old do so is securely saved in version control, not on the back of an old

View File

@ -2460,6 +2460,52 @@ outputs:
title: '' title: ''
# [string=''] Board variant to apply # [string=''] Board variant to apply
variant: '' variant: ''
# 3D Printed Stencils:
# SLA printer, you can use it to apply solder paste to your PCB.
# These stencils are quick solution when you urgently need a stencil but probably
# they don't last long and might come with imperfections.
# It currently uses KiKit, so please read
# [KiKit docs](https://github.com/yaqwsx/KiKit/blob/master/doc/stencil.md).
# Note that we don't implement `--ignore` option, you should use a variant for this
- name: 'stencil_3d_example'
comment: 'Creates a 3D self-registering model of a stencil you can easily print on'
type: 'stencil_3d'
dir: 'Example/stencil_3d_dir'
options:
# [string|list(string)] List of components to add a cutout based on the component courtyard.
# This is useful when you have already pre-populated board and you want to populate more
# components
cutout: ''
# [string|list(string)='_none'] Name of the filter to mark components as not fitted.
# A short-cut to use for simple cases where a variant is an overkill
dnf_filter: '_none'
# `enlarge_holes` is an alias for `enlarge_holes`
# [number=0] Enlarge pad holes by x mm
enlargeholes: 0
# `frame_clearance` is an alias for `frameclearance`
# `frame_width` is an alias for `framewidth`
# [number=0] Clearance for the stencil register [mm]
frameclearance: 0
# [number=1] Register frame width
framewidth: 1
# [boolean=true] Include the generated OpenSCAD files. Note that this also includes the DXF files
include_scad: true
# [string='%f-%i%I%v.%x'] Filename for the output (%i='stencil_3d_top'|'stencil_3d_bottom'|'stencil_3d_edge',
# %x='stl'|'scad'|'dxf'). Affected by global options
output: '%f-%i%I%v.%x'
# `pcb_thickness` is an alias for `pcbthickness`
# [number=0] PCB thickness [mm]. If 0 we will ask KiCad
pcbthickness: 0
# [string|list(string)='_none'] Name of the filter to transform fields before applying other filters.
# A short-cut to use for simple cases where a variant is an overkill
pre_transform: '_none'
# [string='auto'] [top,bottom,auto,both] Which side of the PCB we want. Using `auto` will detect which
# side contains solder paste
side: 'auto'
# [number=0.15] Stencil thickness [mm]. Defines amount of paste dispensed
thickness: 0.15
# [string=''] Board variant to apply
variant: ''
# STEP (ISO 10303-21 Clear Text Encoding of the Exchange Structure): # STEP (ISO 10303-21 Clear Text Encoding of the Exchange Structure):
# This is the most common 3D format for exchange purposes. # This is the most common 3D format for exchange purposes.
# This output is what you get from the 'File/Export/STEP' menu in pcbnew. # This output is what you get from the 'File/Export/STEP' menu in pcbnew.

View File

@ -69,6 +69,10 @@ Dependencies:
debian: python3-lxml debian: python3-lxml
arch: python-lxml arch: python-lxml
downloader: python downloader: python
- name: KiKit
github: yaqwsx/KiKit
pypi: KiKit
downloader: pytool
""" """
import importlib import importlib
import os import os
@ -559,6 +563,9 @@ def run_command(cmd, only_first_line=False, pre_ver_text=None, no_err_2=False):
return None return None
last_stderr = res_run.stderr.decode() last_stderr = res_run.stderr.decode()
res = res_run.stdout.decode().strip() res = res_run.stdout.decode().strip()
if len(res) == 0 and len(last_stderr) != 0:
# Ok, yes, OpenSCAD prints its version to stderr!!!
res = last_stderr
if only_first_line: if only_first_line:
res = res.split('\n')[0] res = res.split('\n')[0]
pre_vers = (cmd[0]+' version ', cmd[0]+' ', pre_ver_text) pre_vers = (cmd[0]+' version ', cmd[0]+' ', pre_ver_text)

View File

@ -377,6 +377,19 @@ class GS(object):
copy2(pro_name, pro_copy) copy2(pro_name, pro_copy)
return pro_copy return pro_copy
@staticmethod
def get_pcb_and_pro_names(name):
if GS.ki5:
return [name, name.replace('kicad_pcb', 'pro')]
return [name, name.replace('kicad_pcb', 'kicad_pro'), name.replace('kicad_pcb', 'kicad_prl')]
@staticmethod
def remove_pcb_and_pro(name):
""" Used to remove temporal PCB and project files """
for fn in GS.get_pcb_and_pro_names(name):
if os.path.isfile(fn):
os.remove(fn)
@staticmethod @staticmethod
def load_board(): def load_board():
""" Will be repplaced by kiplot.py """ """ Will be repplaced by kiplot.py """

View File

@ -242,6 +242,7 @@ W_PCBDRAW = '(W103) '
W_NOCRTYD = '(W104) ' W_NOCRTYD = '(W104) '
W_PANELEMPTY = '(W105) ' W_PANELEMPTY = '(W105) '
W_ONWIN = '(W106) ' W_ONWIN = '(W106) '
W_AUTONONE = '(W106) '
# Somehow arbitrary, the colors are real, but can be different # Somehow arbitrary, the colors are real, but can be different
PCB_MAT_COLORS = {'fr1': "937042", 'fr2': "949d70", 'fr3': "adacb4", 'fr4': "332B16", 'fr5': "6cc290"} PCB_MAT_COLORS = {'fr1': "937042", 'fr2': "949d70", 'fr3': "adacb4", 'fr4': "332B16", 'fr5': "6cc290"}
PCB_FINISH_COLORS = {'hal': "8b898c", 'hasl': "8b898c", 'imag': "8b898c", 'enig': "cfb96e", 'enepig': "cfb96e", PCB_FINISH_COLORS = {'hal': "8b898c", 'hasl': "8b898c", 'imag': "8b898c", 'enig': "cfb96e", 'enepig': "cfb96e",

View File

@ -252,6 +252,11 @@ class VariantOptions(BaseOptions):
return [] return []
return [c.ref for c in self._comps if not c.fitted or not c.included] return [c.ref for c in self._comps if not c.fitted or not c.included]
# Here just to avoid pulling pcbnew for this
@staticmethod
def to_mm(val):
return ToMM(val)
@staticmethod @staticmethod
def create_module_element(m): def create_module_element(m):
if GS.ki6: if GS.ki6:
@ -334,6 +339,22 @@ class VariantOptions(BaseOptions):
for line in restore: for line in restore:
m.Remove(line) m.Remove(line)
def detect_solder_paste(self, board):
""" Detects if the top and/or bottom layer has solder paste """
fpaste = board.GetLayerID('F.Paste')
bpaste = board.GetLayerID('B.Paste')
top = bottom = False
for m in GS.get_modules_board(board):
for p in m.Pads():
pad_layers = p.GetLayerSet()
if not top and fpaste in pad_layers.Seq():
top = True
if not bottom and bpaste in pad_layers.Seq():
bottom = True
if top and bottom:
return top, bottom
return top, bottom
def remove_paste_and_glue(self, board, comps_hash): def remove_paste_and_glue(self, board, comps_hash):
""" Remove from solder paste layers the filtered components. """ """ Remove from solder paste layers the filtered components. """
if comps_hash is None or not (GS.global_remove_solder_paste_for_dnp or GS.global_remove_adhesive_for_dnp): if comps_hash is None or not (GS.global_remove_solder_paste_for_dnp or GS.global_remove_adhesive_for_dnp):

View File

@ -5,10 +5,7 @@
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
""" """
Dependencies: Dependencies:
- name: KiKit - from: KiKit
github: yaqwsx/KiKit
pypi: KiKit
downloader: pytool
role: mandatory role: mandatory
""" """
import collections import collections
@ -708,10 +705,7 @@ class PanelizeOptions(VariantOptions):
fname = self.save_tmp_board() fname = self.save_tmp_board()
self.unfilter_pcb_components(GS.board, do_3D=True) self.unfilter_pcb_components(GS.board, do_3D=True)
self.restore_title() self.restore_title()
to_remove.append(fname) to_remove.extend(GS.get_pcb_and_pro_names(fname))
to_remove.append(fname.replace('kicad_pcb', 'kicad_pro'))
to_remove.append(fname.replace('kicad_pcb', 'kicad_prl'))
to_remove.append(fname.replace('kicad_pcb', 'pro'))
logger.debug('- Modified PCB: '+fname) logger.debug('- Modified PCB: '+fname)
else: else:
fname = GS.pcb_file fname = GS.pcb_file

180
kibot/out_stencil_3d.py Normal file
View File

@ -0,0 +1,180 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2022 Salvador E. Tropea
# Copyright (c) 2022 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0
# Project: KiBot (formerly KiPlot)
"""
Dependencies:
- from: KiKit
role: mandatory
- name: OpenSCAD
url: https://openscad.org/
url_down: https://openscad.org/downloads.html
command: openscad
debian: openscad
arch: openscad
role: mandatory
"""
import os
import shutil
import tempfile
from .error import PlotError
from .gs import GS
from .kiplot import run_command
from .out_base import VariantOptions
from .misc import W_AUTONONE
from .macros import macros, document, output_class # noqa: F401
from . import log
logger = log.get_logger()
class Stencil_3D_Options(VariantOptions):
def __init__(self):
with document:
self.output = GS.def_global_output
""" *Filename for the output (%i='stencil_3d_top'|'stencil_3d_bottom'|'stencil_3d_edge',
%x='stl'|'scad'|'dxf') """
self.side = 'auto'
""" [top,bottom,auto,both] Which side of the PCB we want. Using `auto` will detect which
side contains solder paste """
self.include_scad = True
""" Include the generated OpenSCAD files. Note that this also includes the DXF files """
self.cutout = ''
""" [string|list(string)] List of components to add a cutout based on the component courtyard.
This is useful when you have already pre-populated board and you want to populate more
components """
self.pcbthickness = 0
""" PCB thickness [mm]. If 0 we will ask KiCad """
self.pcb_thickness = None
""" {pcbthickness} """
self.thickness = 0.15
""" *Stencil thickness [mm]. Defines amount of paste dispensed """
self.framewidth = 1
""" Register frame width """
self.frame_width = None
""" {framewidth} """
self.frameclearance = 0
""" Clearance for the stencil register [mm] """
self.frame_clearance = None
""" {frameclearance} """
self.enlargeholes = 0
""" Enlarge pad holes by x mm """
self.enlarge_holes = None
""" {enlarge_holes} """
super().__init__()
def config(self, parent):
super().config(parent)
self.cutout = ','.join(self.force_list(self.cutout))
def move_output(self, src_dir, src_file, id, ext, replacement=None, patch=False):
self._expand_id = id
self._expand_ext = ext
dst_name = self._parent.expand_filename(self._parent.output_dir, self.output)
src_name = os.path.join(src_dir, src_file)
if not os.path.isfile(src_name):
raise PlotError('Missing output file {}'.format(src_name))
if patch:
# Adjust the names of the DXF files
with open(src_name, 'r') as f:
content = f.read()
for k, v in replacement.items():
content = content.replace(k, v)
with open(dst_name, 'w') as f:
f.write(content)
else:
shutil.move(src_name, dst_name)
if replacement is not None:
replacement[src_name] = os.path.basename(dst_name)
def get_targets(self, out_dir):
# TODO: auto side is tricky, needs variants applied
return [self._parent.expand_filename(out_dir, self.output)]
def run(self, output):
cmd_kikit = self.ensure_tool('KiKit')
self.ensure_tool('OpenSCAD')
super().run(output)
# Apply variants and filters
filtered = self.filter_pcb_components(GS.board)
if self.side == 'auto':
detected_top, detected_bottom = self.detect_solder_paste(GS.board)
fname = self.save_tmp_board() if filtered else GS.pcb_file
if filtered:
self.unfilter_pcb_components(GS.board)
# Avoid running the tool if we will generate useless models
if self.side == 'auto' and not detected_top and not detected_bottom:
logger.warning(W_AUTONONE+'No solder paste detected, skipping 3D stencil generation')
return
# If no PCB thickness indicated ask KiCad
if not self.pcbthickness:
ds = GS.board.GetDesignSettings()
self.pcbthickness = self.to_mm(ds.GetBoardThickness())
# Create the command line
cmd = [cmd_kikit, 'stencil', 'createprinted',
'--thickness', str(self.thickness),
'--framewidth', str(self.framewidth),
'--pcbthickness', str(self.pcbthickness)]
if self.cutout:
cmd.extend(['--coutout', self.cutout])
if self.frameclearance:
cmd.extend(['--frameclearance', str(self.frameclearance)])
if self.enlargeholes:
cmd.extend(['--enlargeholes', str(self.enlargeholes)])
with tempfile.TemporaryDirectory() as tmp:
cmd.append(fname)
cmd.append(tmp)
try:
run_command(cmd)
finally:
# Remove temporal variant
if filtered:
GS.remove_pcb_and_pro(fname)
# Now copy the files we want
# - Which side?
do_top = do_bottom = False
if self.side == 'top':
do_top = True
elif self.side == 'bottom':
do_bottom = True
elif self.side == 'both':
do_top = True
do_bottom = True
else: # auto
do_top = detected_top
do_bottom = detected_bottom
prj_name = os.path.splitext(os.path.basename(fname))[0]
replacements = {}
# The edge is needed by any of the OpenSCAD files
if (do_top or do_bottom) and self.include_scad:
self.move_output(tmp, prj_name+'-EdgeCuts.dxf', 'stencil_3d_edge', 'dxf', replacements)
# Top side
if do_top:
self.move_output(tmp, 'topStencil.stl', 'stencil_3d_top', 'stl')
if self.include_scad:
self.move_output(tmp, prj_name+'-PasteTop.dxf', 'stencil_3d_top', 'dxf', replacements)
self.move_output(tmp, 'topStencil.scad', 'stencil_3d_top', 'scad', replacements, patch=True)
# Bottom side
if do_bottom:
self.move_output(tmp, 'bottomStencil.stl', 'stencil_3d_bottom', 'stl')
if self.include_scad:
self.move_output(tmp, prj_name+'-PasteBottom.dxf', 'stencil_3d_bottom', 'dxf', replacements)
self.move_output(tmp, 'bottomStencil.scad', 'stencil_3d_bottom', 'scad', replacements, patch=True)
@output_class
class Stencil_3D(BaseOutput): # noqa: F821
""" 3D Printed Stencils
Creates a 3D self-registering model of a stencil you can easily print on
SLA printer, you can use it to apply solder paste to your PCB.
These stencils are quick solution when you urgently need a stencil but probably
they don't last long and might come with imperfections.
It currently uses KiKit, so please read
[KiKit docs](https://github.com/yaqwsx/KiKit/blob/master/doc/stencil.md).
Note that we don't implement `--ignore` option, you should use a variant for this """
def __init__(self):
super().__init__()
with document:
self.options = Stencil_3D_Options
""" *[dict] Options for the `Stencil_3D` output """

View File

@ -558,7 +558,7 @@ deps = '{\
"extra_arch": null,\ "extra_arch": null,\
"extra_deb": null,\ "extra_deb": null,\
"help_option": "--version",\ "help_option": "--version",\
"importance": 10000,\ "importance": 20000,\
"in_debian": false,\ "in_debian": false,\
"is_kicad_plugin": false,\ "is_kicad_plugin": false,\
"is_python": false,\ "is_python": false,\
@ -575,6 +575,13 @@ deps = '{\
"max_version": null,\ "max_version": null,\
"output": "panelize",\ "output": "panelize",\
"version": null\ "version": null\
},\
{\
"desc": null,\
"mandatory": true,\
"max_version": null,\
"output": "stencil_3d",\
"version": null\
}\ }\
],\ ],\
"tests": [],\ "tests": [],\
@ -622,6 +629,39 @@ deps = '{\
"url": null,\ "url": null,\
"url_down": null\ "url_down": null\
},\ },\
"OpenSCAD": {\
"arch": "openscad",\
"command": "openscad",\
"comments": [],\
"deb_package": "openscad",\
"downloader": null,\
"downloader_str": null,\
"extra_arch": null,\
"extra_deb": null,\
"help_option": "--version",\
"importance": 10000,\
"in_debian": true,\
"is_kicad_plugin": false,\
"is_python": false,\
"name": "OpenSCAD",\
"no_cmd_line_version": false,\
"no_cmd_line_version_old": false,\
"output": "stencil_3d",\
"plugin_dirs": null,\
"pypi_name": "OpenSCAD",\
"roles": [\
{\
"desc": null,\
"mandatory": true,\
"max_version": null,\
"output": "stencil_3d",\
"version": null\
}\
],\
"tests": [],\
"url": "https://openscad.org/",\
"url_down": "https://openscad.org/downloads.html"\
},\
"Pandoc": {\ "Pandoc": {\
"arch": "pandoc",\ "arch": "pandoc",\
"command": "pandoc",\ "command": "pandoc",\
@ -1027,7 +1067,7 @@ def run_command(cmd, only_first_line=False, pre_ver_text=None, no_err_2=False):
cmd[0] = cmd_full cmd[0] = cmd_full
last_cmd = None last_cmd = None
try: try:
cmd_output = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) res_run = subprocess.run(cmd, check=True, capture_output=True)
last_cmd = cmd[0] last_cmd = cmd[0]
except FileNotFoundError as e: except FileNotFoundError as e:
last_ok = False last_ok = False
@ -1039,7 +1079,9 @@ def run_command(cmd, only_first_line=False, pre_ver_text=None, no_err_2=False):
print('Output from command: '+e.output.decode()) print('Output from command: '+e.output.decode())
last_ok = False last_ok = False
return UNKNOWN return UNKNOWN
res = cmd_output.decode().strip() res = res_run.stdout.decode().strip()
if len(res) == 0:
res = res_run.stderr.decode().strip()
if only_first_line: if only_first_line:
res = res.split('\n')[0] res = res.split('\n')[0]
pre_vers = (cmd[0]+' version ', cmd[0]+' ', pre_ver_text) pre_vers = (cmd[0]+' version ', cmd[0]+' ', pre_ver_text)

View File

@ -80,7 +80,7 @@ def run_command(cmd, only_first_line=False, pre_ver_text=None, no_err_2=False):
cmd[0] = cmd_full cmd[0] = cmd_full
last_cmd = None last_cmd = None
try: try:
cmd_output = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) res_run = subprocess.run(cmd, check=True, capture_output=True)
last_cmd = cmd[0] last_cmd = cmd[0]
except FileNotFoundError as e: except FileNotFoundError as e:
last_ok = False last_ok = False
@ -92,7 +92,9 @@ def run_command(cmd, only_first_line=False, pre_ver_text=None, no_err_2=False):
print('Output from command: '+e.output.decode()) print('Output from command: '+e.output.decode())
last_ok = False last_ok = False
return UNKNOWN return UNKNOWN
res = cmd_output.decode().strip() res = res_run.stdout.decode().strip()
if len(res) == 0:
res = res_run.stderr.decode().strip()
if only_first_line: if only_first_line:
res = res.split('\n')[0] res = res.split('\n')[0]
pre_vers = (cmd[0]+' version ', cmd[0]+' ', pre_ver_text) pre_vers = (cmd[0]+' version ', cmd[0]+' ', pre_ver_text)

View File

@ -0,0 +1,9 @@
# Example KiBot config file for a basic 3D stencil
kibot:
version: 1
outputs:
- name: 'stencil'
comment: "Creates a 3D printable stencil"
type: stencil_3d
dir: stencil/3D