diff --git a/CHANGELOG.md b/CHANGELOG.md index dace7ee4..61d812e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - New output: - `vrml` export the 3D model in Virtual Reality Modeling Language (#349) +- Variants: + - Added some limited support for `kikit separate` - PCB_Print: - Images not showing in custom frames. (#352) diff --git a/README.md b/README.md index b5ac1a40..1a254686 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,7 @@ Notes: [**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`, `stencil_for_jig` +- Optional to separate multiboard projects for general use [**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` @@ -1038,6 +1039,28 @@ Note that the **_kibom_...** filters uses a field named `Config`, but you can cu Use '_var_rename' to transform VARIANT:FIELD fields. Use '_var_rename_kicost' to transform kicost.VARIANT:FIELD fields. Use '_kicost_rename' to apply KiCost field rename rules. + - `sub_pcbs`: [list(dict)] Used for multi-board workflows as defined by KiKit. + I don't recommend using it, for detail read + [this](https://github.com/INTI-CMNB/KiBot/tree/master/docs/1_SCH_2_part_PCBs). + But if you really need it you can define the sub-PCBs here. + Then you just use *VARIANT[SUB_PCB_NAME]* instead of just *VARIANT*. + * Valid keys: + - **`name`**: [string=''] Name for this sub-pcb. + - *ref*: Alias for reference. + - **`reference`**: [string=''] Use it for the annotations method. + This is the reference for the `kikit:Board` footprint used to identify the sub-PCB. + When empty the sub-PCB is specified using a rectangle. + - *bottom_right_x*: Alias for brx. + - *bottom_right_y*: Alias for bry. + - `brx`: [number|string] The X position of the bottom right corner for the rectangle that contains the sub-PCB. + - `bry`: [number|string] The Y position of the bottom right corner for the rectangle that contains the sub-PCB. + - `file_id`: [string=''] Text to use as the replacement for %v expansion. + When empty we use the parent `file_id` plus the `name` of the sub-PCB. + - `tlx`: [number|string] The X position of the top left corner for the rectangle that contains the sub-PCB. + - `tly`: [number|string] The Y position of the top left corner for the rectangle that contains the sub-PCB. + - *top_left_x*: Alias for tlx. + - *top_left_y*: Alias for tly. + - `units`: [string='mm'] [millimeters,inches,mils,mm,cm,dm,m,mil,inch,in] Units used when omitted. - `variant_field`: [string='Config'] Name of the field that stores board variant for component. - `variants_blacklist`: [string|list(string)=''] List of board variants to exclude from the BOM. - `variants_whitelist`: [string|list(string)=''] List of board variants to include in the BOM. @@ -1061,6 +1084,28 @@ Note that the **_kibom_...** filters uses a field named `Config`, but you can cu Use '_var_rename' to transform VARIANT:FIELD fields. Use '_var_rename_kicost' to transform kicost.VARIANT:FIELD fields. Use '_kicost_rename' to apply KiCost field rename rules. + - `sub_pcbs`: [list(dict)] Used for multi-board workflows as defined by KiKit. + I don't recommend using it, for detail read + [this](https://github.com/INTI-CMNB/KiBot/tree/master/docs/1_SCH_2_part_PCBs). + But if you really need it you can define the sub-PCBs here. + Then you just use *VARIANT[SUB_PCB_NAME]* instead of just *VARIANT*. + * Valid keys: + - **`name`**: [string=''] Name for this sub-pcb. + - *ref*: Alias for reference. + - **`reference`**: [string=''] Use it for the annotations method. + This is the reference for the `kikit:Board` footprint used to identify the sub-PCB. + When empty the sub-PCB is specified using a rectangle. + - *bottom_right_x*: Alias for brx. + - *bottom_right_y*: Alias for bry. + - `brx`: [number|string] The X position of the bottom right corner for the rectangle that contains the sub-PCB. + - `bry`: [number|string] The Y position of the bottom right corner for the rectangle that contains the sub-PCB. + - `file_id`: [string=''] Text to use as the replacement for %v expansion. + When empty we use the parent `file_id` plus the `name` of the sub-PCB. + - `tlx`: [number|string] The X position of the top left corner for the rectangle that contains the sub-PCB. + - `tly`: [number|string] The Y position of the top left corner for the rectangle that contains the sub-PCB. + - *top_left_x*: Alias for tlx. + - *top_left_y*: Alias for tly. + - `units`: [string='mm'] [millimeters,inches,mils,mm,cm,dm,m,mil,inch,in] Units used when omitted. - `variant`: [string|list(string)=''] Board variant(s). - `kicost`: KiCost variant style The `variant` field (configurable) contains one or more values. @@ -1086,6 +1131,28 @@ Note that the **_kibom_...** filters uses a field named `Config`, but you can cu - `separators`: [string=',;/ '] Valid separators for variants in the variant field. Each character is a valid separator. Only supported internally, don't use it if you plan to use KiCost. + - `sub_pcbs`: [list(dict)] Used for multi-board workflows as defined by KiKit. + I don't recommend using it, for detail read + [this](https://github.com/INTI-CMNB/KiBot/tree/master/docs/1_SCH_2_part_PCBs). + But if you really need it you can define the sub-PCBs here. + Then you just use *VARIANT[SUB_PCB_NAME]* instead of just *VARIANT*. + * Valid keys: + - **`name`**: [string=''] Name for this sub-pcb. + - *ref*: Alias for reference. + - **`reference`**: [string=''] Use it for the annotations method. + This is the reference for the `kikit:Board` footprint used to identify the sub-PCB. + When empty the sub-PCB is specified using a rectangle. + - *bottom_right_x*: Alias for brx. + - *bottom_right_y*: Alias for bry. + - `brx`: [number|string] The X position of the bottom right corner for the rectangle that contains the sub-PCB. + - `bry`: [number|string] The Y position of the bottom right corner for the rectangle that contains the sub-PCB. + - `file_id`: [string=''] Text to use as the replacement for %v expansion. + When empty we use the parent `file_id` plus the `name` of the sub-PCB. + - `tlx`: [number|string] The X position of the top left corner for the rectangle that contains the sub-PCB. + - `tly`: [number|string] The Y position of the top left corner for the rectangle that contains the sub-PCB. + - *top_left_x*: Alias for tlx. + - *top_left_y*: Alias for tly. + - `units`: [string='mm'] [millimeters,inches,mils,mm,cm,dm,m,mil,inch,in] Units used when omitted. - `variant`: [string=''] Variants to match (regex). - `variant_field`: [string='variant'] Name of the field that stores board variant/s for component. Only supported internally, don't use it if you plan to use KiCost. diff --git a/kibot/dep_downloader.py b/kibot/dep_downloader.py index 6e2d5fb1..13938686 100644 --- a/kibot/dep_downloader.py +++ b/kibot/dep_downloader.py @@ -73,6 +73,8 @@ Dependencies: github: yaqwsx/KiKit pypi: KiKit downloader: pytool + - from: KiKit + role: Separate multiboard projects - name: Xvfbwrapper python_module: true debian: python3-xvfbwrapper diff --git a/kibot/misc.py b/kibot/misc.py index 43eac666..47f4311b 100644 --- a/kibot/misc.py +++ b/kibot/misc.py @@ -268,6 +268,7 @@ USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 DISABLE_3D_MODEL_TEXT = '_Disabled_by_KiBot' RENDERERS = ['pcbdraw', 'render_3d'] PCB_GENERATORS = ['pcb_variant', 'panelize'] +KIKIT_UNIT_ALIASES = {'millimeters': 'mm', 'inches': 'inch', 'mils': 'mil'} class Rect(object): diff --git a/kibot/optionable.py b/kibot/optionable.py index d3cf2ff8..62eebb9f 100644 --- a/kibot/optionable.py +++ b/kibot/optionable.py @@ -501,3 +501,49 @@ class BaseOptions(Optionable): self._expand_id = cur_id self._expand_ext = cur_ext return res + + +class PanelOptions(BaseOptions): + """ A class for options that uses KiKit's units """ + _num_regex = re.compile(r'([\d\.]+)(mm|cm|dm|m|mil|inch|in)') + _ang_regex = re.compile(r'([\d\.]+)(deg|°|rad)') + + def add_units(self, ops, def_units=None): + if def_units is None: + def_units = self._parent._parent.units + for op in ops: + val = getattr(self, op) + if val is None: + continue + if isinstance(val, (int, float)): + setattr(self, op, str(val)+def_units) + else: + m = PanelOptions._num_regex.match(val) + if m is None: + raise KiPlotConfigurationError('Malformed value `{}: {}` must be a number and units'.format(op, val)) + num = m.group(1) + try: + num_d = float(num) + except ValueError: + num_d = None + if num_d is None: + raise KiPlotConfigurationError('Malformed number in `{}` ({})'.format(op, num)) + + def add_angle(self, ops, def_units=None): + if def_units is None: + def_units = self._parent._parent.units + for op in ops: + val = getattr(self, op) + if isinstance(val, (int, float)): + setattr(self, op, str(val)+def_units) + else: + m = PanelOptions._ang_regex.match(val) + if m is None: + raise KiPlotConfigurationError('Malformed angle `{}: {}` must be a number and its type'.format(op, val)) + num = m.group(1) + try: + num_d = float(num) + except ValueError: + num_d = None + if num_d is None: + raise KiPlotConfigurationError('Malformed number in `{}` ({})'.format(op, num)) diff --git a/kibot/out_base.py b/kibot/out_base.py index c16d4acc..253c6d6c 100644 --- a/kibot/out_base.py +++ b/kibot/out_base.py @@ -8,9 +8,9 @@ from glob import glob import math import os import re -from tempfile import NamedTemporaryFile, mkdtemp +from tempfile import NamedTemporaryFile, mkdtemp, TemporaryDirectory from .gs import GS -from .kiplot import load_sch, get_board_comps_data +from .kiplot import load_sch, get_board_comps_data, load_board from .misc import Rect, W_WRONGPASTE, DISABLE_3D_MODEL_TEXT, W_NOCRTYD if not GS.kicad_version_n: # When running the regression tests we need it @@ -710,27 +710,41 @@ class VariantOptions(BaseOptions): m.Models().pop() self._highlighted_3D_components = None + def apply_sub_pcb(self): + with TemporaryDirectory(prefix='kibot-separate') as d: + dest = os.path.join(d, os.path.basename(GS.pcb_file)) + self._sub_pcb.load_board(dest) + def filter_pcb_components(self, board, do_3D=False, do_2D=True, highlight=None): - if not self._comps: + if not self._comps and not self._sub_pcb: return False - self.comps_hash = self.get_refs_hash() - if do_2D: - self.cross_modules(board, self.comps_hash) - self.remove_paste_and_glue(board, self.comps_hash) - if hasattr(self, 'hide_excluded') and self.hide_excluded: - self.remove_fab(board, self.comps_hash) - if do_3D: - # Disable the models that aren't for this variant - self.apply_3D_variant_aspect(board) - # Remove the 3D models for not fitted components (also rename) - self.remove_3D_models(board, self.comps_hash) - # Highlight selected components - self.highlight_3D_models(board, highlight) + if self._comps: + self.comps_hash = self.get_refs_hash() + if do_2D: + self.cross_modules(board, self.comps_hash) + self.remove_paste_and_glue(board, self.comps_hash) + if hasattr(self, 'hide_excluded') and self.hide_excluded: + self.remove_fab(board, self.comps_hash) + if do_3D: + # Disable the models that aren't for this variant + self.apply_3D_variant_aspect(board) + # Remove the 3D models for not fitted components (also rename) + self.remove_3D_models(board, self.comps_hash) + # Highlight selected components + self.highlight_3D_models(board, highlight) + if self._sub_pcb: + # Current implementation isn't efficient + self.apply_sub_pcb() return True def unfilter_pcb_components(self, board, do_3D=False, do_2D=True): if not self._comps: return + if self._sub_pcb: + # Undo the sub-PCB: just reload the PCB + GS.board = None + load_board() + return if do_2D: self.uncross_modules(board, self.comps_hash) self.restore_paste_and_glue(board, self.comps_hash) @@ -918,4 +932,7 @@ class VariantOptions(BaseOptions): if self.variant: # Apply the variant comps = self.variant.filter(comps) + self._sub_pcb = self.variant._sub_pcb + else: + self._sub_pcb = None self._comps = comps diff --git a/kibot/out_panelize.py b/kibot/out_panelize.py index e09ecb6c..d254662d 100644 --- a/kibot/out_panelize.py +++ b/kibot/out_panelize.py @@ -17,10 +17,9 @@ from tempfile import NamedTemporaryFile from .error import KiPlotConfigurationError from .gs import GS from .kiplot import run_command, config_output - from .layer import Layer -from .misc import W_PANELEMPTY -from .optionable import BaseOptions +from .misc import W_PANELEMPTY, KIKIT_UNIT_ALIASES +from .optionable import PanelOptions from .out_base import VariantOptions from .registrable import RegOutput from .macros import macros, document, output_class # noqa: F401 @@ -40,47 +39,6 @@ def update_dict(d, u): return d -class PanelOptions(BaseOptions): - _num_regex = re.compile(r'([\d\.]+)(mm|cm|dm|m|mil|inch|in)') - _ang_regex = re.compile(r'([\d\.]+)(deg|°|rad)') - - def add_units(self, ops): - for op in ops: - val = getattr(self, op) - if val is None: - continue - if isinstance(val, (int, float)): - setattr(self, op, str(val)+self._parent._parent.units) - else: - m = PanelOptions._num_regex.match(val) - if m is None: - raise KiPlotConfigurationError('Malformed value `{}: {}` must be a number and units'.format(op, val)) - num = m.group(1) - try: - num_d = float(num) - except ValueError: - num_d = None - if num_d is None: - raise KiPlotConfigurationError('Malformed number in `{}` ({})'.format(op, num)) - - def add_angle(self, ops): - for op in ops: - val = getattr(self, op) - if isinstance(val, (int, float)): - setattr(self, op, str(val)+self._parent._parent.default_angles) - else: - m = PanelOptions._ang_regex.match(val) - if m is None: - raise KiPlotConfigurationError('Malformed angle `{}: {}` must be a number and its type'.format(op, val)) - num = m.group(1) - try: - num_d = float(num) - except ValueError: - num_d = None - if num_d is None: - raise KiPlotConfigurationError('Malformed number in `{}` ({})'.format(op, num)) - - class PanelOptionsWithPlugin(PanelOptions): def __init__(self): with document: @@ -584,7 +542,6 @@ class PanelizeConfig(PanelOptions): class PanelizeOptions(VariantOptions): _extends_regex = re.compile(r'(.+)\[(.+)\]') - _unit_alias = {'millimeters': 'mm', 'inches': 'inch', 'mils': 'mil'} def __init__(self): with document: @@ -688,7 +645,7 @@ class PanelizeOptions(VariantOptions): if configs: list(map(self.solve_extends, filter(lambda x: 'extends' in x, configs))) super().config(parent) - self.units = PanelizeOptions._unit_alias.get(self.units, self.units) + self.units = KIKIT_UNIT_ALIASES.get(self.units, self.units) if isinstance(self.configs, type): logger.warning(W_PANELEMPTY+'Generating a panel with default options, not very useful') self.configs = [] diff --git a/kibot/registrable.py b/kibot/registrable.py index 3a05bc02..83356a96 100644 --- a/kibot/registrable.py +++ b/kibot/registrable.py @@ -4,6 +4,7 @@ # License: GPL-3.0 # Project: KiBot (formerly KiPlot) from collections import OrderedDict +from copy import copy from .optionable import Optionable from .error import KiPlotConfigurationError @@ -59,7 +60,21 @@ class RegOutput(Optionable, Registrable): @staticmethod def add_variants(variants): - RegOutput._def_variants.update(variants) + for k, v in variants.items(): + # Do we have sub-PCBs + if v.sub_pcbs: + # Add a variant for each sub-PCB + for sp in v.sub_pcbs: + name = k+'['+sp.name+']' + vn = copy(v) + vn._sub_pcb = sp + if sp.file_id: + vn.file_id = sp.file_id + else: + vn.file_id += '_'+sp.name + RegOutput._def_variants[name] = vn + else: + RegOutput._def_variants[k] = v @staticmethod def is_variant(name): diff --git a/kibot/var_base.py b/kibot/var_base.py index bc2ece25..a839ccd3 100644 --- a/kibot/var_base.py +++ b/kibot/var_base.py @@ -1,14 +1,82 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2020-2021 Salvador E. Tropea -# Copyright (c) 2020-2021 Instituto Nacional de Tecnología Industrial +# Copyright (c) 2020-2022 Salvador E. Tropea +# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial # License: GPL-3.0 # Project: KiBot (formerly KiPlot) from .registrable import RegVariant -from .optionable import Optionable +from .optionable import Optionable, PanelOptions from .fil_base import apply_exclude_filter, apply_fitted_filter, apply_fixed_filter, apply_pre_transform +from .error import KiPlotConfigurationError +from .misc import KIKIT_UNIT_ALIASES +from .gs import GS +from .kiplot import load_board, run_command from .macros import macros, document # noqa: F401 +class SubPCBOptions(PanelOptions): + def __init__(self): + super().__init__() + self._unkown_is_error = True + with document: + self.name = '' + """ *Name for this sub-pcb """ + self.reference = '' + """ *Use it for the annotations method. + This is the reference for the `kikit:Board` footprint used to identify the sub-PCB. + When empty the sub-PCB is specified using a rectangle """ + self.ref = None + """ {reference} """ + self.tlx = 0 + """ [number|string] The X position of the top left corner for the rectangle that contains the sub-PCB """ + self.top_left_x = None + """ {tlx} """ + self.tly = 0 + """ [number|string] The Y position of the top left corner for the rectangle that contains the sub-PCB """ + self.top_left_y = None + """ {tly} """ + self.brx = 0 + """ [number|string] The X position of the bottom right corner for the rectangle that contains the sub-PCB """ + self.bottom_right_x = None + """ {brx} """ + self.bry = 0 + """ [number|string] The Y position of the bottom right corner for the rectangle that contains the sub-PCB """ + self.bottom_right_y = None + """ {bry} """ + self.units = 'mm' + """ [millimeters,inches,mils,mm,cm,dm,m,mil,inch,in] Units used when omitted """ + self.file_id = '' + """ Text to use as the replacement for %v expansion. + When empty we use the parent `file_id` plus the `name` of the sub-PCB """ + + def is_zero(self, val): + return isinstance(val, (int, float)) and val == 0 + + def config(self, parent): + super().config(parent) + if not self.name: + raise KiPlotConfigurationError('Sub-PCB without a name') + self.units = KIKIT_UNIT_ALIASES.get(self.units, self.units) + if (not self.reference and self.is_zero(self.tlx) and self.is_zero(self.tly) and self.is_zero(self.brx) and + self.is_zero(self.bry)): + raise KiPlotConfigurationError('No reference or rectangle specified for {} sub-PCB'.format(self.name)) + self.add_units(('tlx', 'tly', 'brx', 'bry'), self.units) + + def get_separate_source(self): + if self.reference: + return "annotation; ref: {}".format(self.reference) + return "rectangle; tlx: {}; tly: {}; brx: {}; bry: {}".format(self.tlx, self.tly, self.brx, self.bry) + + def load_board(self, dest): + # Make sure kikit is available + command = GS.ensure_tool('global', 'KiKit') + # Execute the separate + cmd = [command, 'separate', '-s', self.get_separate_source(), GS.pcb_file, dest] + run_command(cmd) + # Load this board + GS.board = None + load_board(dest) + + class BaseVariant(RegVariant): def __init__(self): super().__init__() @@ -38,6 +106,18 @@ class BaseVariant(RegVariant): self.dnc_filter = Optionable """ [string|list(string)=''] Name of the filter to mark components as 'Do Not Change'. Use '_kibom_dnc' for the default KiBoM behavior """ + self.sub_pcbs = SubPCBOptions + """ [list(dict)] Used for multi-board workflows as defined by KiKit. + I don't recommend using it, for detail read + [this](https://github.com/INTI-CMNB/KiBot/tree/master/docs/1_SCH_2_part_PCBs). + But if you really need it you can define the sub-PCBs here. + Then you just use *VARIANT[SUB_PCB_NAME]* instead of just *VARIANT* """ + self._sub_pcb = None + + def config(self, parent): + super().config(parent) + if isinstance(self.sub_pcbs, type): + self.sub_pcbs = [] def get_variant_field(self): """ Returns the name of the field used to determine if the component belongs to the variant """ diff --git a/src/kibot-check b/src/kibot-check index 6a01f8f5..bf0e2862 100755 --- a/src/kibot-check +++ b/src/kibot-check @@ -576,17 +576,24 @@ deps = '{\ "extra_arch": null,\ "extra_deb": null,\ "help_option": "--version",\ - "importance": 30000,\ + "importance": 30100,\ "in_debian": false,\ "is_kicad_plugin": false,\ "is_python": false,\ "name": "KiKit",\ "no_cmd_line_version": false,\ "no_cmd_line_version_old": false,\ - "output": "panelize",\ + "output": "global",\ "plugin_dirs": null,\ "pypi_name": "KiKit",\ "roles": [\ + {\ + "desc": "Separate multiboard projects",\ + "mandatory": false,\ + "max_version": null,\ + "output": "global",\ + "version": null\ + },\ {\ "desc": null,\ "mandatory": true,\ diff --git a/tests/board_samples/kicad_5/multiboard.kicad_pcb b/tests/board_samples/kicad_5/multiboard.kicad_pcb new file mode 100644 index 00000000..a26be08a --- /dev/null +++ b/tests/board_samples/kicad_5/multiboard.kicad_pcb @@ -0,0 +1,173 @@ +(kicad_pcb (version 20171130) (host pcbnew 5.1.9-73d0e3b20d~88~ubuntu20.04.1) + + (general + (thickness 1.6) + (drawings 18) + (tracks 0) + (zones 0) + (modules 3) + (nets 1) + ) + + (page A4) + (layers + (0 F.Cu signal) + (31 B.Cu signal) + (32 B.Adhes user) + (33 F.Adhes user) + (34 B.Paste user) + (35 F.Paste user) + (36 B.SilkS user) + (37 F.SilkS user) + (38 B.Mask user) + (39 F.Mask user) + (40 Dwgs.User user) + (41 Cmts.User user) + (42 Eco1.User user) + (43 Eco2.User user) + (44 Edge.Cuts user) + (45 Margin user) + (46 B.CrtYd user) + (47 F.CrtYd user) + (48 B.Fab user) + (49 F.Fab user) + ) + + (setup + (last_trace_width 0.25) + (trace_clearance 0.2) + (zone_clearance 0.508) + (zone_45_only no) + (trace_min 0.2) + (via_size 0.8) + (via_drill 0.4) + (via_min_size 0.4) + (via_min_drill 0.3) + (uvia_size 0.3) + (uvia_drill 0.1) + (uvias_allowed no) + (uvia_min_size 0.2) + (uvia_min_drill 0.1) + (edge_width 0.2) + (segment_width 0.2) + (pcb_text_width 0.3) + (pcb_text_size 1.5 1.5) + (mod_edge_width 0.12) + (mod_text_size 1 1) + (mod_text_width 0.15) + (pad_size 1.524 1.524) + (pad_drill 0.762) + (pad_to_mask_clearance 0) + (aux_axis_origin 0 0) + (visible_elements FFFFFF7F) + (pcbplotparams + (layerselection 0x010fc_ffffffff) + (usegerberextensions false) + (usegerberattributes true) + (usegerberadvancedattributes true) + (creategerberjobfile true) + (excludeedgelayer true) + (linewidth 0.150000) + (plotframeref false) + (viasonmask false) + (mode 1) + (useauxorigin false) + (hpglpennumber 1) + (hpglpenspeed 20) + (hpglpendiameter 15.000000) + (psnegative false) + (psa4output false) + (plotreference true) + (plotvalue true) + (plotinvisibletext false) + (padsonsilk false) + (subtractmaskfromsilk false) + (outputformat 1) + (mirror false) + (drillshape 1) + (scaleselection 1) + (outputdirectory "")) + ) + + (net 0 "") + + (net_class Default "This is the default net class." + (clearance 0.2) + (trace_width 0.25) + (via_dia 0.8) + (via_drill 0.4) + (uvia_dia 0.3) + (uvia_drill 0.1) + ) + + (module kikit:Board (layer F.Cu) (tedit 605A1488) (tstamp 605A707C) + (at 185 90) + (descr "Mark board for extraction") + (fp_text reference B3 (at -4.5 -5) (layer Dwgs.User) + (effects (font (size 1 1) (thickness 0.15)) (justify left)) + ) + (fp_text value Board (at -7.25 -5) (layer Dwgs.User) + (effects (font (size 1 1) (thickness 0.15))) + ) + (fp_line (start 0 0) (end -1.25 -4) (layer Dwgs.User) (width 0.2)) + (fp_line (start -1.25 -4) (end -9.25 -4) (layer Dwgs.User) (width 0.2)) + (fp_line (start 0 0) (end 0.5 -1) (layer Dwgs.User) (width 0.2)) + (fp_line (start 0 0) (end -1 -0.5) (layer Dwgs.User) (width 0.2)) + ) + + (module kikit:Board (layer F.Cu) (tedit 605A1488) (tstamp 605A706E) + (at 135 90) + (descr "Mark board for extraction") + (fp_text reference B2 (at -4.5 -5) (layer Dwgs.User) + (effects (font (size 1 1) (thickness 0.15)) (justify left)) + ) + (fp_text value Board (at -7.25 -5) (layer Dwgs.User) + (effects (font (size 1 1) (thickness 0.15))) + ) + (fp_line (start 0 0) (end -1 -0.5) (layer Dwgs.User) (width 0.2)) + (fp_line (start 0 0) (end 0.5 -1) (layer Dwgs.User) (width 0.2)) + (fp_line (start -1.25 -4) (end -9.25 -4) (layer Dwgs.User) (width 0.2)) + (fp_line (start 0 0) (end -1.25 -4) (layer Dwgs.User) (width 0.2)) + ) + + (module kikit:Board (layer F.Cu) (tedit 605A1488) (tstamp 605A7057) + (at 100 90) + (descr "Mark board for extraction") + (fp_text reference B1 (at -4.5 -5) (layer Dwgs.User) + (effects (font (size 1 1) (thickness 0.15)) (justify left)) + ) + (fp_text value Board (at -7.25 -5) (layer Dwgs.User) + (effects (font (size 1 1) (thickness 0.15))) + ) + (fp_line (start 0 0) (end -1.25 -4) (layer Dwgs.User) (width 0.2)) + (fp_line (start -1.25 -4) (end -9.25 -4) (layer Dwgs.User) (width 0.2)) + (fp_line (start 0 0) (end 0.5 -1) (layer Dwgs.User) (width 0.2)) + (fp_line (start 0 0) (end -1 -0.5) (layer Dwgs.User) (width 0.2)) + ) + + (gr_text C (at 185 100) (layer F.SilkS) (tstamp 605A709F) + (effects (font (size 5 5) (thickness 1))) + ) + (gr_text B (at 145 100) (layer F.SilkS) (tstamp 605A709D) + (effects (font (size 5 5) (thickness 1))) + ) + (gr_text A (at 100 100) (layer F.SilkS) + (effects (font (size 5 5) (thickness 1))) + ) + (gr_line (start 200 100) (end 190 100) (layer Edge.Cuts) (width 0.2) (tstamp 605A142A)) + (gr_line (start 200 105) (end 200 100) (layer Edge.Cuts) (width 0.2)) + (gr_line (start 190 105) (end 200 105) (layer Edge.Cuts) (width 0.2)) + (gr_line (start 190 100) (end 190 105) (layer Edge.Cuts) (width 0.2)) + (gr_line (start 210 90) (end 180 90) (layer Edge.Cuts) (width 0.2) (tstamp 605A1425)) + (gr_line (start 210 110) (end 210 90) (layer Edge.Cuts) (width 0.2)) + (gr_line (start 180 110) (end 210 110) (layer Edge.Cuts) (width 0.2)) + (gr_line (start 180 90) (end 180 110) (layer Edge.Cuts) (width 0.2)) + (gr_line (start 150 110) (end 135 110) (layer Edge.Cuts) (width 0.2) (tstamp 605A1415)) + (gr_line (start 150 100) (end 150 110) (layer Edge.Cuts) (width 0.2)) + (gr_line (start 160 100) (end 150 100) (layer Edge.Cuts) (width 0.2)) + (gr_line (start 160 90) (end 160 100) (layer Edge.Cuts) (width 0.2)) + (gr_line (start 135 90) (end 160 90) (layer Edge.Cuts) (width 0.2)) + (gr_line (start 135 110) (end 135 90) (layer Edge.Cuts) (width 0.2)) + (gr_circle (center 100 100) (end 110 100) (layer Edge.Cuts) (width 0.2)) + +) diff --git a/tests/board_samples/kicad_5/multiboard.sch b/tests/board_samples/kicad_5/multiboard.sch new file mode 100644 index 00000000..60a384f1 --- /dev/null +++ b/tests/board_samples/kicad_5/multiboard.sch @@ -0,0 +1,16 @@ +EESchema Schematic File Version 4 +EELAYER 30 0 +EELAYER END +$Descr A4 11693 8268 +encoding utf-8 +Sheet 1 1 +Title "" +Date "" +Rev "" +Comp "" +Comment1 "" +Comment2 "" +Comment3 "" +Comment4 "" +$EndDescr +$EndSCHEMATC diff --git a/tests/board_samples/kicad_6/multiboard.kicad_pcb b/tests/board_samples/kicad_6/multiboard.kicad_pcb new file mode 100644 index 00000000..0ac18787 --- /dev/null +++ b/tests/board_samples/kicad_6/multiboard.kicad_pcb @@ -0,0 +1,152 @@ +(kicad_pcb (version 20211014) (generator pcbnew) + + (general + (thickness 1.6) + ) + + (paper "A4") + (layers + (0 "F.Cu" signal) + (31 "B.Cu" signal) + (32 "B.Adhes" user "B.Adhesive") + (33 "F.Adhes" user "F.Adhesive") + (34 "B.Paste" user) + (35 "F.Paste" user) + (36 "B.SilkS" user "B.Silkscreen") + (37 "F.SilkS" user "F.Silkscreen") + (38 "B.Mask" user) + (39 "F.Mask" user) + (40 "Dwgs.User" user "User.Drawings") + (41 "Cmts.User" user "User.Comments") + (42 "Eco1.User" user "User.Eco1") + (43 "Eco2.User" user "User.Eco2") + (44 "Edge.Cuts" user) + (45 "Margin" user) + (46 "B.CrtYd" user "B.Courtyard") + (47 "F.CrtYd" user "F.Courtyard") + (48 "B.Fab" user) + (49 "F.Fab" user) + ) + + (setup + (pad_to_mask_clearance 0) + (pcbplotparams + (layerselection 0x00010fc_ffffffff) + (disableapertmacros false) + (usegerberextensions false) + (usegerberattributes true) + (usegerberadvancedattributes true) + (creategerberjobfile true) + (svguseinch false) + (svgprecision 6) + (excludeedgelayer true) + (plotframeref false) + (viasonmask false) + (mode 1) + (useauxorigin false) + (hpglpennumber 1) + (hpglpenspeed 20) + (hpglpendiameter 15.000000) + (dxfpolygonmode true) + (dxfimperialunits true) + (dxfusepcbnewfont true) + (psnegative false) + (psa4output false) + (plotreference true) + (plotvalue true) + (plotinvisibletext false) + (sketchpadsonfab false) + (subtractmaskfromsilk false) + (outputformat 1) + (mirror false) + (drillshape 1) + (scaleselection 1) + (outputdirectory "") + ) + ) + + (net 0 "") + + (footprint "kikit:Board" (layer "F.Cu") + (tedit 605A1488) (tstamp 00000000-0000-0000-0000-0000605a7057) + (at 100 90) + (descr "Mark board for extraction") + (attr through_hole) + (fp_text reference "B1" (at -4.5 -5) (layer "Dwgs.User") + (effects (font (size 1 1) (thickness 0.15)) (justify left)) + (tstamp cc934527-b84e-4ca1-ad52-399f8e7bf3df) + ) + (fp_text value "Board" (at -7.25 -5) (layer "Dwgs.User") + (effects (font (size 1 1) (thickness 0.15))) + (tstamp 43c89b73-cf56-4390-ad99-0ff92ac35d30) + ) + (fp_line (start 0 0) (end 0.5 -1) (layer "Dwgs.User") (width 0.2) (tstamp 2d675f93-d44f-485c-8207-150c12492452)) + (fp_line (start 0 0) (end -1.25 -4) (layer "Dwgs.User") (width 0.2) (tstamp 87358536-a504-44ce-8c66-b8b876d8894f)) + (fp_line (start -1.25 -4) (end -9.25 -4) (layer "Dwgs.User") (width 0.2) (tstamp 982364b6-bcbd-41ff-a125-84aef9991943)) + (fp_line (start 0 0) (end -1 -0.5) (layer "Dwgs.User") (width 0.2) (tstamp dd80f3b3-31f9-4741-adf5-c81fa1f4c68b)) + ) + + (footprint "kikit:Board" (layer "F.Cu") + (tedit 605A1488) (tstamp 00000000-0000-0000-0000-0000605a706e) + (at 135 90) + (descr "Mark board for extraction") + (attr through_hole) + (fp_text reference "B2" (at -4.5 -5) (layer "Dwgs.User") + (effects (font (size 1 1) (thickness 0.15)) (justify left)) + (tstamp 5c8f7927-58c0-47ef-854e-3dd8cc0bf312) + ) + (fp_text value "Board" (at -7.25 -5) (layer "Dwgs.User") + (effects (font (size 1 1) (thickness 0.15))) + (tstamp bb77f266-f95c-4d63-b73d-9eb5f6c9f1bc) + ) + (fp_line (start 0 0) (end -1 -0.5) (layer "Dwgs.User") (width 0.2) (tstamp 17e1baca-0f8f-4171-b616-6601bb7efd5b)) + (fp_line (start -1.25 -4) (end -9.25 -4) (layer "Dwgs.User") (width 0.2) (tstamp 2cc0aca6-d467-4eed-bd95-c60b0cdb0ccc)) + (fp_line (start 0 0) (end 0.5 -1) (layer "Dwgs.User") (width 0.2) (tstamp 3605dbc5-2619-4e06-bf86-49f1c1817220)) + (fp_line (start 0 0) (end -1.25 -4) (layer "Dwgs.User") (width 0.2) (tstamp 500b75b0-5ef8-4f0f-8bab-ed9123c39485)) + ) + + (footprint "kikit:Board" (layer "F.Cu") + (tedit 605A1488) (tstamp 00000000-0000-0000-0000-0000605a707c) + (at 185 90) + (descr "Mark board for extraction") + (attr through_hole) + (fp_text reference "B3" (at -4.5 -5) (layer "Dwgs.User") + (effects (font (size 1 1) (thickness 0.15)) (justify left)) + (tstamp 01bee0de-da40-4390-898d-9738e5a86bcc) + ) + (fp_text value "Board" (at -7.25 -5) (layer "Dwgs.User") + (effects (font (size 1 1) (thickness 0.15))) + (tstamp 3f2b141b-7bc9-4979-84f7-000f6c66c53b) + ) + (fp_line (start 0 0) (end 0.5 -1) (layer "Dwgs.User") (width 0.2) (tstamp 0ef8c6c7-83b7-4ec8-8948-2e70fa334d77)) + (fp_line (start -1.25 -4) (end -9.25 -4) (layer "Dwgs.User") (width 0.2) (tstamp 181c6f00-96c3-4bb7-ba16-c3fde41797ab)) + (fp_line (start 0 0) (end -1.25 -4) (layer "Dwgs.User") (width 0.2) (tstamp 5e41870a-537a-491e-a56f-ec6759e82e36)) + (fp_line (start 0 0) (end -1 -0.5) (layer "Dwgs.User") (width 0.2) (tstamp f1ebaa83-56da-4499-929a-773021ade5b3)) + ) + + (gr_line (start 150 110) (end 135 110) (layer "Edge.Cuts") (width 0.2) (tstamp 00000000-0000-0000-0000-0000605a1415)) + (gr_line (start 210 90) (end 180 90) (layer "Edge.Cuts") (width 0.2) (tstamp 00000000-0000-0000-0000-0000605a1425)) + (gr_line (start 200 100) (end 190 100) (layer "Edge.Cuts") (width 0.2) (tstamp 00000000-0000-0000-0000-0000605a142a)) + (gr_line (start 160 100) (end 150 100) (layer "Edge.Cuts") (width 0.2) (tstamp 51ee268f-1cdb-4d7d-9c21-906f097e0468)) + (gr_line (start 180 90) (end 180 110) (layer "Edge.Cuts") (width 0.2) (tstamp 64c01c4a-0872-44ed-b267-b18d90185def)) + (gr_line (start 210 110) (end 210 90) (layer "Edge.Cuts") (width 0.2) (tstamp 7197a3a4-13b0-4964-97bb-d3c67b25d3cc)) + (gr_line (start 160 90) (end 160 100) (layer "Edge.Cuts") (width 0.2) (tstamp 993fe7e0-bb83-477b-b768-ddb19b694f3e)) + (gr_line (start 135 90) (end 160 90) (layer "Edge.Cuts") (width 0.2) (tstamp a3e28d38-69cd-42a1-b425-f1a15302e858)) + (gr_line (start 180 110) (end 210 110) (layer "Edge.Cuts") (width 0.2) (tstamp afbb53e6-3063-40ce-bb83-3604be396e1d)) + (gr_line (start 190 105) (end 200 105) (layer "Edge.Cuts") (width 0.2) (tstamp c0684255-d65e-4027-adfe-519b7fcde519)) + (gr_line (start 135 110) (end 135 90) (layer "Edge.Cuts") (width 0.2) (tstamp c30d7998-579c-409d-b75a-00fa9f4eed59)) + (gr_circle (center 100 100) (end 110 100) (layer "Edge.Cuts") (width 0.2) (fill none) (tstamp c817e371-265a-41a5-8be4-97a10008c2fc)) + (gr_line (start 150 100) (end 150 110) (layer "Edge.Cuts") (width 0.2) (tstamp d2bf3808-d0b7-47de-a1f9-f2e94b771c7b)) + (gr_line (start 190 100) (end 190 105) (layer "Edge.Cuts") (width 0.2) (tstamp da6b8bc6-f48f-49c2-9e3e-3bb909ccfe9a)) + (gr_line (start 200 105) (end 200 100) (layer "Edge.Cuts") (width 0.2) (tstamp e6c0520a-00d5-4fde-85e1-681298498d17)) + (gr_text "B" (at 145 100) (layer "F.SilkS") (tstamp 00000000-0000-0000-0000-0000605a709d) + (effects (font (size 5 5) (thickness 1))) + ) + (gr_text "C" (at 185 100) (layer "F.SilkS") (tstamp 00000000-0000-0000-0000-0000605a709f) + (effects (font (size 5 5) (thickness 1))) + ) + (gr_text "A" (at 100 100) (layer "F.SilkS") (tstamp eb565a94-7da3-44d3-82d1-76c30d6f561a) + (effects (font (size 5 5) (thickness 1))) + ) + +) diff --git a/tests/board_samples/kicad_6/multiboard.kicad_sch b/tests/board_samples/kicad_6/multiboard.kicad_sch new file mode 100644 index 00000000..348b88f1 --- /dev/null +++ b/tests/board_samples/kicad_6/multiboard.kicad_sch @@ -0,0 +1,14 @@ +(kicad_sch (version 20211123) (generator eeschema) + + (uuid 2e1c5c17-733f-432d-ac13-7421fd62239d) + + (paper "A4") + + (lib_symbols + ) + + + (sheet_instances + (path "/" (page "1")) + ) +) diff --git a/tests/yaml_samples/panelize_1.kibot.yaml b/tests/yaml_samples/panelize_1.kibot.yaml index b10d16d1..700c4c65 100644 --- a/tests/yaml_samples/panelize_1.kibot.yaml +++ b/tests/yaml_samples/panelize_1.kibot.yaml @@ -8,7 +8,7 @@ outputs: type: panelize options: title: '+ (Panel)' - default_units: mm + units: mm configs: - layout: rows: 4 diff --git a/tests/yaml_samples/pcb_variant_subpc.kibot.yaml b/tests/yaml_samples/pcb_variant_subpc.kibot.yaml new file mode 100644 index 00000000..9d94b1a9 --- /dev/null +++ b/tests/yaml_samples/pcb_variant_subpc.kibot.yaml @@ -0,0 +1,60 @@ +# Example KiBot config file +kibot: + version: 1 + +global: + hide_excluded: true + pcb_finish: ENIG + solder_mask_color: blue + +variants: + - name: 'default' + comment: 'Default variant' + type: ibom + sub_pcbs: + - name: A + tlx: 89 + tly: 89 + brx: 111 + bry: 111 + - name: B + ref: B2 + - name: C + ref: B3 + +outputs: + - name: 'pcb_A' + comment: "PCB A" + type: pcb_variant + options: + variant: default[A] + title: 'Hello %V' + + - name: 'pcb_B' + comment: "PCB B" + type: pcb_variant + options: + variant: default[B] + title: 'Hello %V' + + - name: 'pcb_C' + comment: "PCB C" + type: pcb_variant + options: + variant: default[C] + title: 'Hello %V' + + - name: draw_A + type: pcbdraw + options: + variant: default[A] + + - name: draw_B + type: pcbdraw + options: + variant: default[B] + + - name: draw_C + type: pcbdraw + options: + variant: default[C]