Custom reports for plot outputs (i.e. custom gerber job generation)
This commit is contained in:
parent
aa3025b348
commit
9f4763c36b
|
|
@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Now layers can be selected using the default KiCad names.
|
||||
- More control over the name of the drill and gerber files.
|
||||
- More options to customize the excellon output.
|
||||
- Custom reports for plot outputs (i.e. custom gerber job generation)
|
||||
|
||||
### Changed
|
||||
- Now the default output name applies to the DRC and ERC report names.
|
||||
|
|
|
|||
35
README.md
35
README.md
|
|
@ -662,6 +662,11 @@ Next time you need this list just use an alias, like this:
|
|||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `dxf` output.
|
||||
* Valid keys:
|
||||
- `custom_reports`: [list(dict)] A list of customized reports for the manufacturer.
|
||||
* Valid keys:
|
||||
- `content`: [string=''] Content for the report. Use ${basename} for the project name without extension.
|
||||
Use ${filename(LAYER)} for the file corresponding to LAYER.
|
||||
- `output`: [string='Custom_report.txt'] File name for the custom report.
|
||||
- `dnf_filter`: [string|list(string)=''] Name of the filter to mark components as not fitted.
|
||||
A short-cut to use for simple cases where a variant is an overkill.
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
|
|
@ -759,6 +764,11 @@ Next time you need this list just use an alias, like this:
|
|||
* Valid keys:
|
||||
- `create_gerber_job_file`: [boolean=true] Creates a file with information about all the generated gerbers.
|
||||
You can use it in gerbview to load all gerbers at once.
|
||||
- `custom_reports`: [list(dict)] A list of customized reports for the manufacturer.
|
||||
* Valid keys:
|
||||
- `content`: [string=''] Content for the report. Use ${basename} for the project name without extension.
|
||||
Use ${filename(LAYER)} for the file corresponding to LAYER.
|
||||
- `output`: [string='Custom_report.txt'] File name for the custom report.
|
||||
- `disable_aperture_macros`: [boolean=false] Disable aperture macros (workaround for buggy CAM software) (KiCad 6).
|
||||
- `dnf_filter`: [string|list(string)=''] Name of the filter to mark components as not fitted.
|
||||
A short-cut to use for simple cases where a variant is an overkill.
|
||||
|
|
@ -801,6 +811,11 @@ Next time you need this list just use an alias, like this:
|
|||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `hpgl` output.
|
||||
* Valid keys:
|
||||
- `custom_reports`: [list(dict)] A list of customized reports for the manufacturer.
|
||||
* Valid keys:
|
||||
- `content`: [string=''] Content for the report. Use ${basename} for the project name without extension.
|
||||
Use ${filename(LAYER)} for the file corresponding to LAYER.
|
||||
- `output`: [string='Custom_report.txt'] File name for the custom report.
|
||||
- `dnf_filter`: [string|list(string)=''] Name of the filter to mark components as not fitted.
|
||||
A short-cut to use for simple cases where a variant is an overkill.
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
|
|
@ -1025,6 +1040,11 @@ Next time you need this list just use an alias, like this:
|
|||
This output is what you get from the File/Plot menu in pcbnew.
|
||||
* Valid keys:
|
||||
- `comment`: [string=''] A comment for documentation purposes.
|
||||
- `custom_reports`: [list(dict)] A list of customized reports for the manufacturer.
|
||||
* Valid keys:
|
||||
- `content`: [string=''] Content for the report. Use ${basename} for the project name without extension.
|
||||
Use ${filename(LAYER)} for the file corresponding to LAYER.
|
||||
- `output`: [string='Custom_report.txt'] File name for the custom report.
|
||||
- `dir`: [string='.'] Output directory for the generated files.
|
||||
- `dnf_filter`: [string|list(string)=''] Name of the filter to mark components as not fitted.
|
||||
A short-cut to use for simple cases where a variant is an overkill.
|
||||
|
|
@ -1045,6 +1065,11 @@ Next time you need this list just use an alias, like this:
|
|||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `pdf` output.
|
||||
* Valid keys:
|
||||
- `custom_reports`: [list(dict)] A list of customized reports for the manufacturer.
|
||||
* Valid keys:
|
||||
- `content`: [string=''] Content for the report. Use ${basename} for the project name without extension.
|
||||
Use ${filename(LAYER)} for the file corresponding to LAYER.
|
||||
- `output`: [string='Custom_report.txt'] File name for the custom report.
|
||||
- `dnf_filter`: [string|list(string)=''] Name of the filter to mark components as not fitted.
|
||||
A short-cut to use for simple cases where a variant is an overkill.
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
|
|
@ -1160,6 +1185,11 @@ Next time you need this list just use an alias, like this:
|
|||
- `options`: [dict] Options for the `ps` output.
|
||||
* Valid keys:
|
||||
- `a4_output`: [boolean=true] force A4 paper size.
|
||||
- `custom_reports`: [list(dict)] A list of customized reports for the manufacturer.
|
||||
* Valid keys:
|
||||
- `content`: [string=''] Content for the report. Use ${basename} for the project name without extension.
|
||||
Use ${filename(LAYER)} for the file corresponding to LAYER.
|
||||
- `output`: [string='Custom_report.txt'] File name for the custom report.
|
||||
- `dnf_filter`: [string|list(string)=''] Name of the filter to mark components as not fitted.
|
||||
A short-cut to use for simple cases where a variant is an overkill.
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
|
|
@ -1243,6 +1273,11 @@ Next time you need this list just use an alias, like this:
|
|||
- `name`: [string=''] Used to identify this particular output definition.
|
||||
- `options`: [dict] Options for the `svg` output.
|
||||
* Valid keys:
|
||||
- `custom_reports`: [list(dict)] A list of customized reports for the manufacturer.
|
||||
* Valid keys:
|
||||
- `content`: [string=''] Content for the report. Use ${basename} for the project name without extension.
|
||||
Use ${filename(LAYER)} for the file corresponding to LAYER.
|
||||
- `output`: [string='Custom_report.txt'] File name for the custom report.
|
||||
- `dnf_filter`: [string|list(string)=''] Name of the filter to mark components as not fitted.
|
||||
A short-cut to use for simple cases where a variant is an overkill.
|
||||
- `drill_marks`: [string='full'] what to use to indicate the drill places, can be none, small or full (for real scale).
|
||||
|
|
|
|||
|
|
@ -168,6 +168,13 @@ outputs:
|
|||
type: 'dxf'
|
||||
dir: 'Example/dxf_dir'
|
||||
options:
|
||||
# [list(dict)] A list of customized reports for the manufacturer
|
||||
custom_reports:
|
||||
# [string=''] Content for the report. Use ${basename} for the project name without extension.
|
||||
# Use ${filename(LAYER)} for the file corresponding to LAYER
|
||||
- content: ''
|
||||
# [string='Custom_report.txt'] File name for the custom report
|
||||
output: 'Custom_report.txt'
|
||||
# [string|list(string)=''] 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: ''
|
||||
|
|
@ -291,6 +298,13 @@ outputs:
|
|||
# [boolean=true] Creates a file with information about all the generated gerbers.
|
||||
# You can use it in gerbview to load all gerbers at once
|
||||
create_gerber_job_file: true
|
||||
# [list(dict)] A list of customized reports for the manufacturer
|
||||
custom_reports:
|
||||
# [string=''] Content for the report. Use ${basename} for the project name without extension.
|
||||
# Use ${filename(LAYER)} for the file corresponding to LAYER
|
||||
- content: ''
|
||||
# [string='Custom_report.txt'] File name for the custom report
|
||||
output: 'Custom_report.txt'
|
||||
# [boolean=false] Disable aperture macros (workaround for buggy CAM software) (KiCad 6)
|
||||
disable_aperture_macros: false
|
||||
# [string|list(string)=''] Name of the filter to mark components as not fitted.
|
||||
|
|
@ -347,6 +361,13 @@ outputs:
|
|||
type: 'hpgl'
|
||||
dir: 'Example/hpgl_dir'
|
||||
options:
|
||||
# [list(dict)] A list of customized reports for the manufacturer
|
||||
custom_reports:
|
||||
# [string=''] Content for the report. Use ${basename} for the project name without extension.
|
||||
# Use ${filename(LAYER)} for the file corresponding to LAYER
|
||||
- content: ''
|
||||
# [string='Custom_report.txt'] File name for the custom report
|
||||
output: 'Custom_report.txt'
|
||||
# [string|list(string)=''] 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: ''
|
||||
|
|
@ -663,6 +684,13 @@ outputs:
|
|||
type: 'pdf'
|
||||
dir: 'Example/pdf_dir'
|
||||
options:
|
||||
# [list(dict)] A list of customized reports for the manufacturer
|
||||
custom_reports:
|
||||
# [string=''] Content for the report. Use ${basename} for the project name without extension.
|
||||
# Use ${filename(LAYER)} for the file corresponding to LAYER
|
||||
- content: ''
|
||||
# [string='Custom_report.txt'] File name for the custom report
|
||||
output: 'Custom_report.txt'
|
||||
# [string|list(string)=''] 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: ''
|
||||
|
|
@ -789,6 +817,13 @@ outputs:
|
|||
options:
|
||||
# [boolean=true] force A4 paper size
|
||||
a4_output: true
|
||||
# [list(dict)] A list of customized reports for the manufacturer
|
||||
custom_reports:
|
||||
# [string=''] Content for the report. Use ${basename} for the project name without extension.
|
||||
# Use ${filename(LAYER)} for the file corresponding to LAYER
|
||||
- content: ''
|
||||
# [string='Custom_report.txt'] File name for the custom report
|
||||
output: 'Custom_report.txt'
|
||||
# [string|list(string)=''] 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: ''
|
||||
|
|
@ -891,6 +926,13 @@ outputs:
|
|||
type: 'svg'
|
||||
dir: 'Example/svg_dir'
|
||||
options:
|
||||
# [list(dict)] A list of customized reports for the manufacturer
|
||||
custom_reports:
|
||||
# [string=''] Content for the report. Use ${basename} for the project name without extension.
|
||||
# Use ${filename(LAYER)} for the file corresponding to LAYER
|
||||
- content: ''
|
||||
# [string='Custom_report.txt'] File name for the custom report
|
||||
output: 'Custom_report.txt'
|
||||
# [string|list(string)=''] 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: ''
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@
|
|||
# Project: KiBot (formerly KiPlot)
|
||||
# Adapted from: https://github.com/johnbeard/kiplot
|
||||
import os
|
||||
import re
|
||||
from pcbnew import GERBER_JOBFILE_WRITER, PLOT_CONTROLLER, IsCopperLayer, F_Cu, B_Cu, Edge_Cuts
|
||||
from .out_base import (BaseOutput)
|
||||
from .error import (PlotError, KiPlotConfigurationError)
|
||||
from .optionable import Optionable
|
||||
from .out_base import BaseOutput
|
||||
from .error import PlotError, KiPlotConfigurationError
|
||||
from .layer import Layer
|
||||
from .gs import GS
|
||||
from .misc import KICAD_VERSION_5_99, W_NOLAYER
|
||||
|
|
@ -19,6 +21,17 @@ from . import log
|
|||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
class CustomReport(Optionable):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
with document:
|
||||
self.output = 'Custom_report.txt'
|
||||
""" File name for the custom report """
|
||||
self.content = ''
|
||||
""" Content for the report. Use ${basename} for the project name without extension.
|
||||
Use ${filename(LAYER)} for the file corresponding to LAYER """
|
||||
|
||||
|
||||
class AnyLayerOptions(VariantOptions):
|
||||
""" Base class for: DXF, Gerber, HPGL, PDF, PS and SVG """
|
||||
def __init__(self):
|
||||
|
|
@ -47,8 +60,15 @@ class AnyLayerOptions(VariantOptions):
|
|||
Example '.g%n' """
|
||||
self.edge_cut_extension = ''
|
||||
""" Used to configure the edge cuts layer extension for Protel mode """
|
||||
self.custom_reports = CustomReport
|
||||
""" [list(dict)] A list of customized reports for the manufacturer """
|
||||
super().__init__()
|
||||
|
||||
def config(self):
|
||||
super().config()
|
||||
if isinstance(self.custom_reports, type):
|
||||
self.custom_reports = []
|
||||
|
||||
def _configure_plot_ctrl(self, po, output_dir):
|
||||
logger.debug("Configuring plot controller for output")
|
||||
po.SetOutputDirectory(output_dir)
|
||||
|
|
@ -93,6 +113,7 @@ class AnyLayerOptions(VariantOptions):
|
|||
# Apply the variants and filters
|
||||
exclude = self.filter_components(board)
|
||||
# Plot every layer in the output
|
||||
generated = {}
|
||||
layers = Layer.solve(layers)
|
||||
for la in layers:
|
||||
suffix = la.suffix
|
||||
|
|
@ -134,9 +155,29 @@ class AnyLayerOptions(VariantOptions):
|
|||
os.rename(k_filename, filename)
|
||||
if create_job:
|
||||
jobfile_writer.AddGbrFile(id, os.path.basename(filename))
|
||||
generated[la.layer] = os.path.basename(filename)
|
||||
# Create the job file
|
||||
if create_job:
|
||||
jobfile_writer.CreateJobFile(self.expand_filename(output_dir, po.gerber_job_file, 'job', 'gbrjob'))
|
||||
# Custom reports
|
||||
regex_fname = re.compile(r'\$\{filename\(.*\)\}')
|
||||
for report in self.custom_reports:
|
||||
filename = report.output
|
||||
content = report.content
|
||||
# Replace special white spaces
|
||||
content = content.replace('\\r', chr(13))
|
||||
content = content.replace('\\n', chr(10))
|
||||
content = content.replace('\\t', chr(9))
|
||||
# Replace file names, compatible with gerber_zipper_action
|
||||
content = content.replace('${basename}', GS.pcb_basename)
|
||||
for name, file in generated.items():
|
||||
content = content.replace('${filename('+name+')}', file)
|
||||
# Replace unused layers
|
||||
content = regex_fname.sub('', content)
|
||||
# Create the report
|
||||
logger.debug('Creating custom report `'+filename+'`')
|
||||
with open(os.path.join(output_dir, filename), 'wt') as f:
|
||||
f.write(content)
|
||||
# Restore the eliminated layers
|
||||
if exclude:
|
||||
self.unfilter_components(board)
|
||||
|
|
|
|||
Loading…
Reference in New Issue