From e2fdf87d6ba96af84f8e5c11f8beaaa2d88fe8e4 Mon Sep 17 00:00:00 2001 From: "Salvador E. Tropea" Date: Tue, 20 Sep 2022 13:13:12 -0300 Subject: [PATCH] [Copy_Files] Now you can copy the 3D models - Also save a PCB modified to use them - Changed the syntax so we can copy other stuff --- CHANGELOG.md | 3 +- README.md | 22 +- docs/samples/generic_plot.kibot.yaml | 29 +- kibot/globals.py | 6 +- kibot/kicad/config.py | 9 +- kibot/misc.py | 1 + kibot/out_base_3d.py | 45 +- kibot/out_copy_files.py | 191 ++++++-- kibot/out_download_datasheets.py | 1 + kibot/out_position.py | 3 +- tests/board_samples/kicad_6/3d/1/test.wrl | 1 + tests/board_samples/kicad_6/3d/2/test.wrl | 1 + .../kicad_6/copy_files.kicad_pcb | 411 ++++++++++++++++++ tests/test_plot/test_misc.py | 56 +++ tests/utils/context.py | 4 +- tests/yaml_samples/copy_files_1.kibot.yaml | 51 +++ tests/yaml_samples/copy_files_2.kibot.yaml | 36 ++ 17 files changed, 798 insertions(+), 72 deletions(-) create mode 100644 tests/board_samples/kicad_6/3d/1/test.wrl create mode 100644 tests/board_samples/kicad_6/3d/2/test.wrl create mode 100644 tests/board_samples/kicad_6/copy_files.kicad_pcb create mode 100644 tests/yaml_samples/copy_files_1.kibot.yaml create mode 100644 tests/yaml_samples/copy_files_2.kibot.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index c0fa9524..ba725c6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 import). (#296) - New outputs: - PCB_Variant: saves a PCB with filters and variants applied. - - File_Copy: used to copy files to the output directory. (#279) + - Copy_Files: used to copy files to the output directory. (#279) + You can also copy the 3D models. - Support for Eurocircuits drill adjust to fix small OARs. Option `eurocircuits_reduce_holes`. (#227) - Global options: diff --git a/README.md b/README.md index e0e671fc..3493c715 100644 --- a/README.md +++ b/README.md @@ -1611,18 +1611,32 @@ Notes: - **`name`**: [string=''] Used to identify this particular output definition. - **`options`**: [dict] Options for the `copy_files` output. * Valid keys: + - **`download`**: [boolean=true] Downloads missing 3D models from KiCad git. Only applies to models in KISYS3DMOD. - **`files`**: [list(dict)] Which files will be included. * Valid keys: - - **`from_output`**: [string=''] Collect files from the selected output. - When used the `source` option is ignored. - **`source`**: [string='*'] File names to add, wildcards allowed. Use ** for recursive match. By default this pattern is applied to the current working dir. See the `from_outdir` option. - - `dest`: [string=''] Destination directory inside the output dir, empty means the same of the file. + - **`source_type`**: [string='files'] [files,out_files,output,3d_models] How to interpret `source`. + `files`: is a pattern for files relative to the working directory. + `out_files`: is a pattern for files relative to output dir specified + with `-d` command line option. + `output`: is the name of an `output`. + `3d_models`: is a pattern to match the name of the 3D models extracted + from the PCB.. + - `dest`: [string=''] Destination directory inside the output dir, empty means the same of the file + relative to the source directory. + For the `3d_models` type you can use DIR+ to create subdirs under DIR. - `filter`: [string='.*'] A regular expression that source files must match. - - `from_outdir`: [boolean=false] Use the output dir specified with `-d` command line option, not the working dir. + - `save_pcb`: [boolean=false] Only usable for the `3d_models` mode. + Save a PCB copy modified to use the copied 3D models. + - **`no_virtual`**: [boolean=false] Used to exclude 3D models for components with 'virtual' attribute. + - `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. - `follow_links`: [boolean=true] Store the file pointed by symlinks, not the symlink. + - `kicad_3d_url`: [string='https://gitlab.com/kicad/libraries/kicad-packages3D/-/raw/master/'] Base URL for the KiCad 3D models. - `link_no_copy`: [boolean=false] Create symlinks instead of copying files. + - `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. diff --git a/docs/samples/generic_plot.kibot.yaml b/docs/samples/generic_plot.kibot.yaml index 85a74da6..28b6c28a 100644 --- a/docs/samples/generic_plot.kibot.yaml +++ b/docs/samples/generic_plot.kibot.yaml @@ -426,25 +426,44 @@ outputs: type: 'copy_files' dir: 'Example/copy_files_dir' options: + # [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' + # [boolean=true] Downloads missing 3D models from KiCad git. Only applies to models in KISYS3DMOD + download: true # [list(dict)] Which files will be included files: # [string=''] Destination directory inside the output dir, empty means the same of the file + # relative to the source directory. + # For the `3d_models` type you can use DIR+ to create subdirs under DIR - dest: '' # [string='.*'] A regular expression that source files must match filter: '.*' - # [boolean=false] Use the output dir specified with `-d` command line option, not the working dir - from_outdir: false - # [string=''] Collect files from the selected output. - # When used the `source` option is ignored - from_output: '' + # [boolean=false] Only usable for the `3d_models` mode. + # Save a PCB copy modified to use the copied 3D models + save_pcb: false # [string='*'] File names to add, wildcards allowed. Use ** for recursive match. # By default this pattern is applied to the current working dir. # See the `from_outdir` option source: '*' + # [string='files'] [files,out_files,output,3d_models] How to interpret `source`. + # `files`: is a pattern for files relative to the working directory. + # `out_files`: is a pattern for files relative to output dir specified + # with `-d` command line option. + # `output`: is the name of an `output`. + # `3d_models`: is a pattern to match the name of the 3D models extracted + # from the PCB. + source_type: 'files' # [boolean=true] Store the file pointed by symlinks, not the symlink follow_links: true + # [string='https://gitlab.com/kicad/libraries/kicad-packages3D/-/raw/master/'] Base URL for the KiCad 3D models + kicad_3d_url: 'https://gitlab.com/kicad/libraries/kicad-packages3D/-/raw/master/' # [boolean=false] Create symlinks instead of copying files link_no_copy: false + # [boolean=false] Used to exclude 3D models for components with 'virtual' attribute + no_virtual: false + # [string=''] Board variant to apply + variant: '' # Diff: # Recursive git submodules aren't supported (submodules inside submodules) - name: 'diff_example' diff --git a/kibot/globals.py b/kibot/globals.py index 088bbeef..9f24a65c 100644 --- a/kibot/globals.py +++ b/kibot/globals.py @@ -44,8 +44,8 @@ class Environment(Optionable): if self.symbols: defs['KICAD_SYMBOL_DIR'] = self.symbols if self.footprints: - defs['KICAD_FOOTPRINT_DIR'] = self.symbols - defs['KISYSMOD'] = self.symbols + defs['KICAD_FOOTPRINT_DIR'] = self.footprints + defs['KISYSMOD'] = self.footprints if self.models_3d: defs['KISYS3DMOD'] = self.models_3d if self.templates: @@ -57,7 +57,7 @@ class Environment(Optionable): if self.symbols: defs['KICAD6_SYMBOL_DIR'] = self.symbols if self.footprints: - defs['KICAD6_FOOTPRINT_DIR'] = self.symbols + defs['KICAD6_FOOTPRINT_DIR'] = self.footprints if self.models_3d: defs['KICAD6_3DMODEL_DIR'] = self.models_3d if self.templates: diff --git a/kibot/kicad/config.py b/kibot/kicad/config.py index a055d397..4104f9d0 100644 --- a/kibot/kicad/config.py +++ b/kibot/kicad/config.py @@ -145,6 +145,8 @@ class KiConf(object): sym_lib_dir = None template_dir = None footprint_dir = None + models_3d_dir = None + party_3rd_dir = None kicad_env = {} lib_aliases = {} aliases_3D = {} @@ -399,6 +401,7 @@ class KiConf(object): os.environ[old] = val else: logger.warning(W_NOLIBS + 'Unable to find KiCad '+desc) + return val def load_kicad_common(): # Try to figure out KiCad configuration file @@ -583,11 +586,13 @@ class KiConf(object): return KiConf.fix_page_layout_k5(project, dry) return KiConf.fix_page_layout_k6(project, dry) - def expand_env(name, used_extra=None): + def expand_env(name, used_extra=None, ref_dir=None): if used_extra is None: used_extra = [False] if not name: return name expanded = expand_env(un_quote(name), KiConf.kicad_env, GS.load_pro_variables(), used_extra) # Don't try to get the absolute path for something that starts with a variable that we couldn't expand - return expanded if expanded.startswith('${') else os.path.abspath(expanded) + if ref_dir is None: + ref_dir = os.getcwd() + return expanded if expanded.startswith('${') else os.path.normpath(os.path.join(ref_dir, expanded)) diff --git a/kibot/misc.py b/kibot/misc.py index 9dd6b609..7e8a5ee0 100644 --- a/kibot/misc.py +++ b/kibot/misc.py @@ -232,6 +232,7 @@ W_MAXDEPTH = '(W096) ' W_3DRESVER = '(W097) ' W_DOWN3D = '(W098) ' W_MISSREF = '(W099) ' +W_COPYOVER = '(W100) ' # Somehow arbitrary, the colors are real, but can be different 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", diff --git a/kibot/out_base_3d.py b/kibot/out_base_3d.py index 30b31306..b6dc33d2 100644 --- a/kibot/out_base_3d.py +++ b/kibot/out_base_3d.py @@ -3,6 +3,7 @@ # Copyright (c) 2020-2022 Instituto Nacional de TecnologĂ­a Industrial # License: GPL-3.0 # Project: KiBot (formerly KiPlot) +from fnmatch import fnmatch import os import requests import tempfile @@ -30,7 +31,7 @@ def do_expand_env(fname, used_extra, extra_debug): force_used_extra = True if extra_debug: logger.debug("- Replaced alias {} -> {}".format(alias_name+':'+rest, fname)) - full_name = KiConf.expand_env(fname, used_extra) + full_name = KiConf.expand_env(fname, used_extra, ref_dir=GS.pcb_dir) if extra_debug: logger.debug("- Expanded {} -> {}".format(fname, full_name)) if os.path.isfile(full_name) or ':' not in fname or GS.global_disable_3d_alias_as_env: @@ -64,15 +65,20 @@ class Base3DOptions(VariantOptions): super().__init__() self._expand_id = '3D' - def download_model(self, url, fname): + def download_model(self, url, fname, rel_dirs): """ Download the 3D model from the provided URL """ logger.debug('Downloading `{}`'.format(url)) - r = requests.get(url, allow_redirects=True) - if r.status_code != 200: + failed = False + try: + r = requests.get(url, allow_redirects=True) + except Exception: + failed = True + if failed or r.status_code != 200: logger.warning(W_FAILDL+'Failed to download `{}`'.format(url)) return None if self._tmp_dir is None: self._tmp_dir = tempfile.mkdtemp() + rel_dirs.append(self._tmp_dir) logger.debug('Using `{}` as temporal dir for downloaded files'.format(self._tmp_dir)) dest = os.path.join(self._tmp_dir, fname) os.makedirs(os.path.dirname(dest), exist_ok=True) @@ -80,7 +86,7 @@ class Base3DOptions(VariantOptions): f.write(r.content) return dest - def download_models(self): + def download_models(self, rename_filter=None, rename_function=None, rename_data=None): """ Check we have the 3D models. Inform missing models. Try to download the missing models @@ -90,6 +96,10 @@ class Base3DOptions(VariantOptions): KiConf.init(GS.pcb_file) # List of models we already downloaded downloaded = set() + # For the mode where we copy the 3D models + source_models = set() + is_copy_mode = rename_filter is not None + rel_dirs = getattr(rename_data, 'rel_dirs', []) extra_debug = GS.debug_level > 3 # Look for all the footprints for m in GS.get_modules(): @@ -106,9 +116,13 @@ class Base3DOptions(VariantOptions): if extra_debug: logger.debug("- Skipping {} (disabled)".format(m3d.m_Filename)) continue + if is_copy_mode and not fnmatch(m3d.m_Filename, rename_filter): + # Skip filtered footprints + continue used_extra = [False] full_name = do_expand_env(m3d.m_Filename, used_extra, extra_debug) if not os.path.isfile(full_name): + logger.debugl(2, 'Missing 3D model file {} ({})'.format(full_name, m3d.m_Filename)) # Missing 3D model if self.download and (m3d.m_Filename.startswith('${KISYS3DMOD}/') or m3d.m_Filename.startswith('${KICAD6_3DMODEL_DIR}/')): @@ -121,26 +135,33 @@ class Base3DOptions(VariantOptions): else: # Download the model url = self.kicad_3d_url+fname - replace = self.download_model(url, fname) + replace = self.download_model(url, fname, rel_dirs) if replace: # Successfully downloaded downloaded.add(full_name) - self.undo_3d_models[replace] = m3d.m_Filename # If this is a .wrl also download the .step if url.endswith('.wrl'): url = url[:-4]+'.step' fname = fname[:-4]+'.step' - self.download_model(url, fname) + self.download_model(url, fname, rel_dirs) if replace: - m3d.m_Filename = replace + source_models.add(replace) + old_name = m3d.m_Filename + new_name = replace if not is_copy_mode else rename_function(rename_data, replace) + self.undo_3d_models[new_name] = old_name + m3d.m_Filename = new_name models_replaced = True if full_name not in downloaded: logger.warning(W_MISS3D+'Missing 3D model for {}: `{}`'.format(ref, full_name)) else: # File was found - if used_extra[0]: + if used_extra[0] or is_copy_mode: # The file is there, but we got it expanding a user defined text # This is completely valid for KiCad, but kicad2step doesn't support it - m3d.m_Filename = full_name + source_models.add(full_name) + old_name = m3d.m_Filename + new_name = full_name if not is_copy_mode else rename_function(rename_data, full_name) + self.undo_3d_models[new_name] = old_name + m3d.m_Filename = new_name if not models_replaced and extra_debug: logger.debug('- Modifying models with text vars') models_replaced = True @@ -149,7 +170,7 @@ class Base3DOptions(VariantOptions): models.push_front(model) if downloaded: logger.warning(W_DOWN3D+' {} 3D models downloaded'.format(len(downloaded))) - return models_replaced + return models_replaced if not is_copy_mode else list(source_models) def list_models(self): """ Get the list of 3D models """ diff --git a/kibot/out_copy_files.py b/kibot/out_copy_files.py index 51401ea2..772916b4 100644 --- a/kibot/out_copy_files.py +++ b/kibot/out_copy_files.py @@ -3,17 +3,19 @@ # Copyright (c) 2022 Instituto Nacional de TecnologĂ­a Industrial # License: GPL-3.0 # Project: KiBot (formerly KiPlot) -from collections import OrderedDict +import fnmatch import glob import os import re -from shutil import copy2 +from shutil import copy2, rmtree from sys import exit from .error import KiPlotConfigurationError from .gs import GS from .kiplot import config_output, get_output_dir, run_output -from .misc import WRONG_ARGUMENTS, INTERNAL_ERROR -from .optionable import Optionable, BaseOptions +from .kicad.config import KiConf +from .misc import WRONG_ARGUMENTS, INTERNAL_ERROR, W_COPYOVER +from .optionable import Optionable +from .out_base_3d import Base3DOptions from .registrable import RegOutput from .macros import macros, document, output_class # noqa: F401 from . import log @@ -21,6 +23,13 @@ from . import log logger = log.get_logger() +def may_be_rel(file): + rel_file = os.path.relpath(file) + if len(rel_file) < len(file): + return rel_file + return file + + class FilesList(Optionable): def __init__(self): super().__init__() @@ -29,18 +38,43 @@ class FilesList(Optionable): """ *File names to add, wildcards allowed. Use ** for recursive match. By default this pattern is applied to the current working dir. See the `from_outdir` option """ - self.from_outdir = False - """ Use the output dir specified with `-d` command line option, not the working dir """ - self.from_output = '' - """ *Collect files from the selected output. - When used the `source` option is ignored """ + self.source_type = 'files' + """ *[files,out_files,output,3d_models] How to interpret `source`. + `files`: is a pattern for files relative to the working directory. + `out_files`: is a pattern for files relative to output dir specified + with `-d` command line option. + `output`: is the name of an `output`. + `3d_models`: is a pattern to match the name of the 3D models extracted + from the PCB. """ self.filter = '.*' """ A regular expression that source files must match """ self.dest = '' - """ Destination directory inside the output dir, empty means the same of the file """ + """ Destination directory inside the output dir, empty means the same of the file + relative to the source directory. + For the `3d_models` type you can use DIR+ to create subdirs under DIR """ + self.save_pcb = False + """ Only usable for the `3d_models` mode. + Save a PCB copy modified to use the copied 3D models """ + + def apply_rename(self, fname): + is_abs = os.path.isabs(fname) + append_mode = self.dest and self.dest[-1] == '+' + if self.dest and not append_mode: + # A destination specified by the user + dest = os.path.basename(fname) + elif is_abs: + for d in self.rel_dirs: + if d is not None and fname.startswith(d): + dest = os.path.relpath(fname, d) + break + else: + dest = os.path.basename(fname) + else: + dest = os.path.relpath(fname, os.getcwd()) + return os.path.join(self.output_dir, dest) -class Copy_FilesOptions(BaseOptions): +class Copy_FilesOptions(Base3DOptions): def __init__(self): with document: self.files = FilesList @@ -58,38 +92,88 @@ class Copy_FilesOptions(BaseOptions): if isinstance(self.files, type): KiPlotConfigurationError('No files provided') + def get_from_output(self, f, no_out_run): + from_output = f.source + logger.debugl(2, '- From output `{}`'.format(from_output)) + out = RegOutput.get_output(from_output) + if out is not None: + config_output(out) + out_dir = get_output_dir(out.dir, out, dry=True) + files_list = out.get_targets(out_dir) + logger.debugl(2, '- List of files: {}'.format(files_list)) + else: + logger.error('Unknown output `{}` selected in {}'.format(from_output, self._parent)) + exit(WRONG_ARGUMENTS) + # Check if we must run the output to create the files + if not no_out_run: + for file in files_list: + if not os.path.isfile(file): + # The target doesn't exist + if not out._done: + # The output wasn't created in this run, try running it + run_output(out) + if not os.path.isfile(file): + # Still missing, something is wrong + logger.error('Unable to generate `{}` from {}'.format(file, out)) + exit(INTERNAL_ERROR) + return files_list + + def get_3d_models(self, f): + """ Look for the 3D models and make a list, optionally download them """ + GS.check_pcb() + dest_dir = f.dest + if dest_dir and dest_dir[-1] == '+': + dest_dir = dest_dir[:-1] + f.output_dir = dest_dir + # Apply any variant + self.filter_pcb_components(GS.board, do_3D=True, do_2D=True) + # Download missing models and rename all collect 3D models (renamed) + f.rel_dirs = self.rel_dirs + files_list = self.download_models(rename_filter=f.source, rename_function=FilesList.apply_rename, rename_data=f) + if f.save_pcb: + fname = os.path.join(self.output_dir, os.path.basename(GS.pcb_file)) + logger.debug('Saving the PCB to '+fname) + GS.board.Save(fname) + GS.copy_project(fname) + if not self._comps: + # We must undo the download/rename + self.undo_3d_models_rename(GS.board) + else: + self.unfilter_pcb_components(GS.board, do_3D=True, do_2D=True) + # Also include the step/wrl counterpart + new_list = [] + for fn in files_list: + if fn.endswith('.wrl'): + fn = fn[:-4]+'.step' + if os.path.isfile(fn): + new_list.append(fn) + elif fn.endswith('.step'): + fn = f[:-5]+'.wrl' + if os.path.isfile(fn): + new_list.append(fn) + return files_list+fnmatch.filter(new_list, f.source) + def get_files(self, no_out_run=False): - files = OrderedDict() + files = [] src_dir_cwd = os.getcwd() src_dir_outdir = self.expand_filename_sch(GS.out_dir) + self.rel_dirs = [os.path.normpath(os.path.join(GS.pcb_dir, KiConf.models_3d_dir)), + os.path.normpath(os.path.join(GS.pcb_dir, KiConf.party_3rd_dir)), + GS.pcb_dir] for f in self.files: - src_dir = src_dir_outdir if f.from_outdir else src_dir_cwd + from_outdir = False + if f.source_type == 'out_files' or f.source_type == 'output': + from_outdir = True + src_dir = src_dir_outdir if from_outdir else src_dir_cwd + mode_3d = f.source_type == '3d_models' + mode_3d_append = mode_3d and f.dest and f.dest[-1] == '+' # Get the list of candidates files_list = None - if f.from_output: - logger.debugl(2, '- From output `{}`'.format(f.from_output)) - out = RegOutput.get_output(f.from_output) - if out is not None: - config_output(out) - out_dir = get_output_dir(out.dir, out, dry=True) - files_list = out.get_targets(out_dir) - logger.debugl(2, '- List of files: {}'.format(files_list)) - else: - logger.error('Unknown output `{}` selected in {}'.format(f.from_output, self._parent)) - exit(WRONG_ARGUMENTS) - # Check if we must run the output to create the files - if not no_out_run: - for file in files_list: - if not os.path.isfile(file): - # The target doesn't exist - if not out._done: - # The output wasn't created in this run, try running it - run_output(out) - if not os.path.isfile(file): - # Still missing, something is wrong - logger.error('Unable to generate `{}` from {}'.format(file, out)) - exit(INTERNAL_ERROR) - else: + if f.source_type == 'output': + files_list = self.get_from_output(f, no_out_run) + elif mode_3d: + files_list = self.get_3d_models(f) + else: # files and out_files source = f.expand_filename_both(f.source, make_safe=False) files_list = glob.iglob(os.path.join(src_dir, source), recursive=True) if GS.debug_level > 1: @@ -100,11 +184,25 @@ class Copy_FilesOptions(BaseOptions): fname_real = os.path.realpath(fname) if self.follow_links else os.path.abspath(fname) # Compute the destination directory dest = fname - if f.dest: + is_abs = os.path.isabs(fname) + if f.dest and not mode_3d_append: + # A destination specified by the user dest = os.path.join(f.dest, os.path.basename(fname)) + elif mode_3d and is_abs: + for d in self.rel_dirs: + if d is not None and fname.startswith(d): + dest = os.path.relpath(dest, d) + break + else: + logger.error(fname) + logger.error(self.rel_dirs) + dest = os.path.basename(fname) + if mode_3d_append: + dest = os.path.join(f.dest[:-1], dest) else: dest = os.path.relpath(dest, src_dir) - files[fname_real] = dest + files.append((fname_real, dest)) + return files def get_targets(self, out_dir): @@ -122,19 +220,27 @@ class Copy_FilesOptions(BaseOptions): files = self.get_files() logger.debug('Copying files') output += os.path.sep - for k, v in files.items(): - src = k - dest = os.path.join(output, v) + copied = {} + for (src, dst) in files: + dest = os.path.join(output, dst) dest_dir = os.path.dirname(dest) if not os.path.isdir(dest_dir): os.makedirs(dest_dir) logger.debug('- {} -> {}'.format(src, dest)) + if dest in copied: + logger.warning(W_COPYOVER+'`{}` and `{}` both are copied to `{}`'. + format(may_be_rel(src), may_be_rel(copied[dest]), may_be_rel(dest))) if os.path.isfile(dest) or os.path.islink(dest): os.remove(dest) if self.link_no_copy: os.symlink(os.path.relpath(src, os.path.dirname(dest)), dest) else: copy2(src, dest) + copied[dest] = src + # Remove the downloaded 3D models + if self._tmp_dir: + rmtree(self._tmp_dir) + self._tmp_dir = None @output_class @@ -159,4 +265,5 @@ class Copy_Files(BaseOutput): # noqa: F821 def run(self, output_dir): # No output member, just a dir + self.options.output_dir = output_dir self.options.run(output_dir) diff --git a/kibot/out_download_datasheets.py b/kibot/out_download_datasheets.py index 99e1be4e..c325b863 100644 --- a/kibot/out_download_datasheets.py +++ b/kibot/out_download_datasheets.py @@ -40,6 +40,7 @@ class Download_Datasheets_Options(VariantOptions): """ Instead of download things we already downloaded use symlinks """ # Used to collect the targets self._dry = False + self._unkown_is_error = True def config(self, parent): super().config(parent) diff --git a/kibot/out_position.py b/kibot/out_position.py index 9bda3201..3bd2b11f 100644 --- a/kibot/out_position.py +++ b/kibot/out_position.py @@ -111,7 +111,8 @@ class PositionOptions(VariantOptions): topf = open(topf_name, 'w') botf = open(botf_name, 'w') else: - bothf = open(self.expand_filename(output_dir, self.output, 'both_pos', 'pos'), 'w') + fname = self.expand_filename(output_dir, self.output, 'both_pos', 'pos') + bothf = open(fname, 'w') files = [f for f in [topf, botf, bothf] if f is not None] for f in files: diff --git a/tests/board_samples/kicad_6/3d/1/test.wrl b/tests/board_samples/kicad_6/3d/1/test.wrl new file mode 100644 index 00000000..1196288d --- /dev/null +++ b/tests/board_samples/kicad_6/3d/1/test.wrl @@ -0,0 +1 @@ +Hola! diff --git a/tests/board_samples/kicad_6/3d/2/test.wrl b/tests/board_samples/kicad_6/3d/2/test.wrl new file mode 100644 index 00000000..0897f709 --- /dev/null +++ b/tests/board_samples/kicad_6/3d/2/test.wrl @@ -0,0 +1 @@ +Chau! diff --git a/tests/board_samples/kicad_6/copy_files.kicad_pcb b/tests/board_samples/kicad_6/copy_files.kicad_pcb new file mode 100644 index 00000000..c22ee0f7 --- /dev/null +++ b/tests/board_samples/kicad_6/copy_files.kicad_pcb @@ -0,0 +1,411 @@ +(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 "") + (net 1 "Net-(C1-Pad2)") + (net 2 "Net-(C1-Pad1)") + (net 3 "Net-(C2-Pad2)") + (net 4 "Net-(C2-Pad1)") + (net 5 "Net-(C3-Pad2)") + (net 6 "Net-(C3-Pad1)") + (net 7 "Net-(C4-Pad2)") + (net 8 "Net-(C4-Pad1)") + (net 9 "Net-(R1-Pad2)") + (net 10 "Net-(R1-Pad1)") + (net 11 "Net-(R2-Pad2)") + (net 12 "Net-(R2-Pad1)") + (net 13 "Net-(R3-Pad2)") + (net 14 "Net-(R3-Pad1)") + (net 15 "Net-(R4-Pad2)") + (net 16 "Net-(R4-Pad1)") + + (footprint "Capacitor_SMD:C_0805_2012Metric" (layer "F.Cu") + (tedit 5F68FEEE) (tstamp 00000000-0000-0000-0000-00006223ab1c) + (at 128.25 93 90) + (descr "Capacitor SMD 0805 (2012 Metric), square (rectangular) end terminal, IPC_7351 nominal, (Body size source: IPC-SM-782 page 76, https://www.pcb-3d.com/wordpress/wp-content/uploads/ipc-sm-782a_amendment_1_and_2.pdf, https://docs.google.com/spreadsheets/d/1BsfQQcO9C6DZCsRaXUlFlo91Tg2WpOkGARC1WS5S8t0/edit?usp=sharing), generated with kicad-footprint-generator") + (tags "capacitor") + (path "/00000000-0000-0000-0000-00006223ba95") + (attr smd) + (fp_text reference "C1" (at -1 -1.68 90) (layer "F.SilkS") + (effects (font (size 1 1) (thickness 0.15))) + (tstamp 48ab88d7-7084-4d02-b109-3ad55a30bb11) + ) + (fp_text value "1u" (at 0 1.68 90) (layer "F.Fab") + (effects (font (size 1 1) (thickness 0.15))) + (tstamp f71da641-16e6-4257-80c3-0b9d804fee4f) + ) + (fp_text user "${REFERENCE}" (at 0 0 90) (layer "F.Fab") + (effects (font (size 0.5 0.5) (thickness 0.08))) + (tstamp fd470e95-4861-44fe-b1e4-6d8a7c66e144) + ) + (fp_line (start -0.261252 0.735) (end 0.261252 0.735) (layer "F.SilkS") (width 0.12) (tstamp c41b3c8b-634e-435a-b582-96b83bbd4032)) + (fp_line (start -0.261252 -0.735) (end 0.261252 -0.735) (layer "F.SilkS") (width 0.12) (tstamp ce83728b-bebd-48c2-8734-b6a50d837931)) + (fp_line (start 1.7 -0.98) (end 1.7 0.98) (layer "F.CrtYd") (width 0.05) (tstamp 0f22151c-f260-4674-b486-4710a2c42a55)) + (fp_line (start -1.7 -0.98) (end 1.7 -0.98) (layer "F.CrtYd") (width 0.05) (tstamp 1831fb37-1c5d-42c4-b898-151be6fca9dc)) + (fp_line (start -1.7 0.98) (end -1.7 -0.98) (layer "F.CrtYd") (width 0.05) (tstamp 9340c285-5767-42d5-8b6d-63fe2a40ddf3)) + (fp_line (start 1.7 0.98) (end -1.7 0.98) (layer "F.CrtYd") (width 0.05) (tstamp fe8d9267-7834-48d6-a191-c8724b2ee78d)) + (fp_line (start 1 -0.625) (end 1 0.625) (layer "F.Fab") (width 0.1) (tstamp 0eaa98f0-9565-4637-ace3-42a5231b07f7)) + (fp_line (start 1 0.625) (end -1 0.625) (layer "F.Fab") (width 0.1) (tstamp 181abe7a-f941-42b6-bd46-aaa3131f90fb)) + (fp_line (start -1 -0.625) (end 1 -0.625) (layer "F.Fab") (width 0.1) (tstamp 704d6d51-bb34-4cbf-83d8-841e208048d8)) + (fp_line (start -1 0.625) (end -1 -0.625) (layer "F.Fab") (width 0.1) (tstamp 8174b4de-74b1-48db-ab8e-c8432251095b)) + (pad "1" smd roundrect locked (at -0.95 0 90) (size 1 1.45) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25) + (net 2 "Net-(C1-Pad1)") (tstamp 3cd1bda0-18db-417d-b581-a0c50623df68)) + (pad "2" smd roundrect locked (at 0.95 0 90) (size 1 1.45) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25) + (net 1 "Net-(C1-Pad2)") (tstamp 0b21a65d-d20b-411e-920a-75c343ac5136)) + (model "${KISYS3DMOD}/Resistor_SMD.3dshapes/R_0805_2012Metrico.wrl" + (offset (xyz 0 0 0)) + (scale (xyz 1 1 1)) + (rotate (xyz 0 0 0)) + ) + ) + + (footprint "Capacitor_SMD:C_0805_2012Metric" (layer "F.Cu") + (tedit 5F68FEEE) (tstamp 00000000-0000-0000-0000-00006223ab2d) + (at 133 93.25 90) + (descr "Capacitor SMD 0805 (2012 Metric), square (rectangular) end terminal, IPC_7351 nominal, (Body size source: IPC-SM-782 page 76, https://www.pcb-3d.com/wordpress/wp-content/uploads/ipc-sm-782a_amendment_1_and_2.pdf, https://docs.google.com/spreadsheets/d/1BsfQQcO9C6DZCsRaXUlFlo91Tg2WpOkGARC1WS5S8t0/edit?usp=sharing), generated with kicad-footprint-generator") + (tags "capacitor") + (path "/00000000-0000-0000-0000-00006223bc65") + (attr smd) + (fp_text reference "C2" (at 0 -1.68 90) (layer "F.SilkS") + (effects (font (size 1 1) (thickness 0.15))) + (tstamp 3b838d52-596d-4e4d-a6ac-e4c8e7621137) + ) + (fp_text value "2u" (at 0 1.68 90) (layer "F.Fab") + (effects (font (size 1 1) (thickness 0.15))) + (tstamp cbdcaa78-3bbc-413f-91bf-2709119373ce) + ) + (fp_text user "${REFERENCE}" (at 0 0 90) (layer "F.Fab") + (effects (font (size 0.5 0.5) (thickness 0.08))) + (tstamp 1e1b062d-fad0-427c-a622-c5b8a80b5268) + ) + (fp_line (start -0.261252 -0.735) (end 0.261252 -0.735) (layer "F.SilkS") (width 0.12) (tstamp 5038e144-5119-49db-b6cf-f7c345f1cf03)) + (fp_line (start -0.261252 0.735) (end 0.261252 0.735) (layer "F.SilkS") (width 0.12) (tstamp ac264c30-3e9a-4be2-b97a-9949b68bd497)) + (fp_line (start -1.7 0.98) (end -1.7 -0.98) (layer "F.CrtYd") (width 0.05) (tstamp 54365317-1355-4216-bb75-829375abc4ec)) + (fp_line (start -1.7 -0.98) (end 1.7 -0.98) (layer "F.CrtYd") (width 0.05) (tstamp a3e4f0ae-9f86-49e9-b386-ed8b42e012fb)) + (fp_line (start 1.7 -0.98) (end 1.7 0.98) (layer "F.CrtYd") (width 0.05) (tstamp a690fc6c-55d9-47e6-b533-faa4b67e20f3)) + (fp_line (start 1.7 0.98) (end -1.7 0.98) (layer "F.CrtYd") (width 0.05) (tstamp c144caa5-b0d4-4cef-840a-d4ad178a2102)) + (fp_line (start 1 0.625) (end -1 0.625) (layer "F.Fab") (width 0.1) (tstamp 2e642b3e-a476-4c54-9a52-dcea955640cd)) + (fp_line (start -1 -0.625) (end 1 -0.625) (layer "F.Fab") (width 0.1) (tstamp 30f15357-ce1d-48b9-93dc-7d9b1b2aa048)) + (fp_line (start 1 -0.625) (end 1 0.625) (layer "F.Fab") (width 0.1) (tstamp 87371631-aa02-498a-998a-09bdb74784c1)) + (fp_line (start -1 0.625) (end -1 -0.625) (layer "F.Fab") (width 0.1) (tstamp d8603679-3e7b-4337-8dbc-1827f5f54d8a)) + (pad "1" smd roundrect locked (at -0.95 0 90) (size 1 1.45) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25) + (net 4 "Net-(C2-Pad1)") (tstamp 5fc27c35-3e1c-4f96-817c-93b5570858a6)) + (pad "2" smd roundrect locked (at 0.95 0 90) (size 1 1.45) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25) + (net 3 "Net-(C2-Pad2)") (tstamp efeac2a2-7682-4dc7-83ee-f6f1b23da506)) + (model "${KISYS3DMOD}/Capacitor_SMD.3dshapes/C_0805_2012Metric.wrl" + (offset (xyz 0 0 0)) + (scale (xyz 1 1 1)) + (rotate (xyz 0 0 0)) + ) + ) + + (footprint "Resistor_SMD:R_0805_2012Metric" (layer "F.Cu") + (tedit 5F68FEEE) (tstamp 00000000-0000-0000-0000-00006223ab60) + (at 128.25 99) + (descr "Resistor SMD 0805 (2012 Metric), square (rectangular) end terminal, IPC_7351 nominal, (Body size source: IPC-SM-782 page 72, https://www.pcb-3d.com/wordpress/wp-content/uploads/ipc-sm-782a_amendment_1_and_2.pdf), generated with kicad-footprint-generator") + (tags "resistor") + (path "/00000000-0000-0000-0000-00006223a923") + (attr smd) + (fp_text reference "R1" (at 0 -1.65) (layer "F.SilkS") + (effects (font (size 1 1) (thickness 0.15))) + (tstamp a03e565f-d8cd-4032-aae3-b7327d4143dd) + ) + (fp_text value "1" (at 0 1.65) (layer "F.Fab") + (effects (font (size 1 1) (thickness 0.15))) + (tstamp 5b2b5c7d-f943-4634-9f0a-e9561705c49d) + ) + (fp_text user "${REFERENCE}" (at 0 0) (layer "F.Fab") + (effects (font (size 0.5 0.5) (thickness 0.08))) + (tstamp c70d9ef3-bfeb-47e0-a1e1-9aeba3da7864) + ) + (fp_line (start -0.227064 -0.735) (end 0.227064 -0.735) (layer "F.SilkS") (width 0.12) (tstamp d1262c4d-2245-4c4f-8f35-7bb32cd9e21e)) + (fp_line (start -0.227064 0.735) (end 0.227064 0.735) (layer "F.SilkS") (width 0.12) (tstamp d22e95aa-f3db-4fbc-a331-048a2523233e)) + (fp_line (start -1.68 0.95) (end -1.68 -0.95) (layer "F.CrtYd") (width 0.05) (tstamp 0d0bb7b2-a6e5-46d2-9492-a1aa6e5a7b2f)) + (fp_line (start 1.68 0.95) (end -1.68 0.95) (layer "F.CrtYd") (width 0.05) (tstamp 15875808-74d5-4210-b8ca-aa8fbc04ae21)) + (fp_line (start 1.68 -0.95) (end 1.68 0.95) (layer "F.CrtYd") (width 0.05) (tstamp 81bbc3ff-3938-49ac-8297-ce2bcc9a42bd)) + (fp_line (start -1.68 -0.95) (end 1.68 -0.95) (layer "F.CrtYd") (width 0.05) (tstamp b1169a2d-8998-4b50-a48d-c520bcc1b8e1)) + (fp_line (start 1 0.625) (end -1 0.625) (layer "F.Fab") (width 0.1) (tstamp 0147f16a-c952-4891-8f53-a9fb8cddeb8d)) + (fp_line (start -1 0.625) (end -1 -0.625) (layer "F.Fab") (width 0.1) (tstamp 4e3d7c0d-12e3-42f2-b944-e4bcdbbcac2a)) + (fp_line (start 1 -0.625) (end 1 0.625) (layer "F.Fab") (width 0.1) (tstamp 6a44418c-7bb4-4e99-8836-57f153c19721)) + (fp_line (start -1 -0.625) (end 1 -0.625) (layer "F.Fab") (width 0.1) (tstamp aa02e544-13f5-4cf8-a5f4-3e6cda006090)) + (pad "1" smd roundrect locked (at -0.9125 0) (size 1.025 1.4) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.243902) + (net 10 "Net-(R1-Pad1)") (tstamp 0a3cc030-c9dd-4d74-9d50-715ed2b361a2)) + (pad "2" smd roundrect locked (at 0.9125 0) (size 1.025 1.4) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.243902) + (net 9 "Net-(R1-Pad2)") (tstamp dd00c2e1-6027-4717-b312-4fab3ee52002)) + (model "ALIAS1:test.wrl" + (offset (xyz 0 0 0)) + (scale (xyz 1 1 1)) + (rotate (xyz 0 0 0)) + ) + ) + + (footprint "Resistor_SMD:R_0805_2012Metric" (layer "F.Cu") + (tedit 5F68FEEE) (tstamp 00000000-0000-0000-0000-00006223ab71) + (at 133 98.5) + (descr "Resistor SMD 0805 (2012 Metric), square (rectangular) end terminal, IPC_7351 nominal, (Body size source: IPC-SM-782 page 72, https://www.pcb-3d.com/wordpress/wp-content/uploads/ipc-sm-782a_amendment_1_and_2.pdf), generated with kicad-footprint-generator") + (tags "resistor") + (path "/00000000-0000-0000-0000-00006223adf1") + (attr smd) + (fp_text reference "R2" (at 0 -1.65) (layer "F.SilkS") + (effects (font (size 1 1) (thickness 0.15))) + (tstamp b3d08afa-f296-4e3b-8825-73b6331d35bf) + ) + (fp_text value "2" (at 0 1.65) (layer "F.Fab") + (effects (font (size 1 1) (thickness 0.15))) + (tstamp 98e81e80-1f85-4152-be3f-99785ea97751) + ) + (fp_text user "${REFERENCE}" (at 0 0) (layer "F.Fab") + (effects (font (size 0.5 0.5) (thickness 0.08))) + (tstamp 842e430f-0c35-45f3-a0b5-95ae7b7ae388) + ) + (fp_line (start -0.227064 0.735) (end 0.227064 0.735) (layer "F.SilkS") (width 0.12) (tstamp 58dc14f9-c158-4824-a84e-24a6a482a7a4)) + (fp_line (start -0.227064 -0.735) (end 0.227064 -0.735) (layer "F.SilkS") (width 0.12) (tstamp dde3dba8-1b81-466c-93a3-c284ff4da1ef)) + (fp_line (start 1.68 -0.95) (end 1.68 0.95) (layer "F.CrtYd") (width 0.05) (tstamp 13475e15-f37c-4de8-857e-1722b0c39513)) + (fp_line (start 1.68 0.95) (end -1.68 0.95) (layer "F.CrtYd") (width 0.05) (tstamp 2732632c-4768-42b6-bf7f-14643424019e)) + (fp_line (start -1.68 -0.95) (end 1.68 -0.95) (layer "F.CrtYd") (width 0.05) (tstamp b635b16e-60bb-4b3e-9fc3-47d34eef8381)) + (fp_line (start -1.68 0.95) (end -1.68 -0.95) (layer "F.CrtYd") (width 0.05) (tstamp f976e2cc-36f9-4479-a816-2c74d1d5da6f)) + (fp_line (start -1 -0.625) (end 1 -0.625) (layer "F.Fab") (width 0.1) (tstamp 03d88a85-11fd-47aa-954c-c318bb15294a)) + (fp_line (start 1 0.625) (end -1 0.625) (layer "F.Fab") (width 0.1) (tstamp 0dcdf1b8-13c6-48b4-bd94-5d26038ff231)) + (fp_line (start 1 -0.625) (end 1 0.625) (layer "F.Fab") (width 0.1) (tstamp 1a2f72d1-0b36-4610-afc4-4ad1660d5d3b)) + (fp_line (start -1 0.625) (end -1 -0.625) (layer "F.Fab") (width 0.1) (tstamp 51c4dc0a-5b9f-4edf-a83f-4a12881e42ef)) + (pad "1" smd roundrect locked (at -0.9125 0) (size 1.025 1.4) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.243902) + (net 12 "Net-(R2-Pad1)") (tstamp 120a7b0f-ddfd-4447-85c1-35665465acdb)) + (pad "2" smd roundrect locked (at 0.9125 0) (size 1.025 1.4) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.243902) + (net 11 "Net-(R2-Pad2)") (tstamp 854dd5d4-5fd2-4730-bd49-a9cd8299a065)) + (model "ALIAS2:test.wrl" + (offset (xyz 0 0 0)) + (scale (xyz 1 1 1)) + (rotate (xyz 0 0 0)) + ) + ) + + (footprint "Capacitor_SMD:C_0805_2012Metric" (layer "B.Cu") + (tedit 5F68FEEE) (tstamp 00000000-0000-0000-0000-00006223ab3e) + (at 128.25 93.25 -90) + (descr "Capacitor SMD 0805 (2012 Metric), square (rectangular) end terminal, IPC_7351 nominal, (Body size source: IPC-SM-782 page 76, https://www.pcb-3d.com/wordpress/wp-content/uploads/ipc-sm-782a_amendment_1_and_2.pdf, https://docs.google.com/spreadsheets/d/1BsfQQcO9C6DZCsRaXUlFlo91Tg2WpOkGARC1WS5S8t0/edit?usp=sharing), generated with kicad-footprint-generator") + (tags "capacitor") + (path "/00000000-0000-0000-0000-00006223c1b3") + (attr smd) + (fp_text reference "C3" (at 0 1.68 -90) (layer "B.SilkS") + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + (tstamp bb7f0588-d4d8-44bf-9ebf-3c533fe4d6ae) + ) + (fp_text value "3u" (at 0 -1.68 -90) (layer "B.Fab") + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + (tstamp f1830a1b-f0cc-47ae-a2c9-679c82032f14) + ) + (fp_text user "${REFERENCE}" (at 0 0 -90) (layer "B.Fab") + (effects (font (size 0.5 0.5) (thickness 0.08)) (justify mirror)) + (tstamp 6a955fc7-39d9-4c75-9a69-676ca8c0b9b2) + ) + (fp_line (start -0.261252 0.735) (end 0.261252 0.735) (layer "B.SilkS") (width 0.12) (tstamp 10109f84-4940-47f8-8640-91f185ac9bc1)) + (fp_line (start -0.261252 -0.735) (end 0.261252 -0.735) (layer "B.SilkS") (width 0.12) (tstamp 55e740a3-0735-4744-896e-2bf5437093b9)) + (fp_line (start 1.7 0.98) (end 1.7 -0.98) (layer "B.CrtYd") (width 0.05) (tstamp 47baf4b1-0938-497d-88f9-671136aa8be7)) + (fp_line (start 1.7 -0.98) (end -1.7 -0.98) (layer "B.CrtYd") (width 0.05) (tstamp 77ed3941-d133-4aef-a9af-5a39322d14eb)) + (fp_line (start -1.7 0.98) (end 1.7 0.98) (layer "B.CrtYd") (width 0.05) (tstamp c022004a-c968-410e-b59e-fbab0e561e9d)) + (fp_line (start -1.7 -0.98) (end -1.7 0.98) (layer "B.CrtYd") (width 0.05) (tstamp f4f99e3d-7269-4f6a-a759-16ad2a258779)) + (fp_line (start 1 -0.625) (end -1 -0.625) (layer "B.Fab") (width 0.1) (tstamp 71c31975-2c45-4d18-a25a-18e07a55d11e)) + (fp_line (start 1 0.625) (end 1 -0.625) (layer "B.Fab") (width 0.1) (tstamp 746ba970-8279-4e7b-aed3-f28687777c21)) + (fp_line (start -1 0.625) (end 1 0.625) (layer "B.Fab") (width 0.1) (tstamp e10b5627-3247-4c86-b9f6-ef474ca11543)) + (fp_line (start -1 -0.625) (end -1 0.625) (layer "B.Fab") (width 0.1) (tstamp e8314017-7be6-4011-9179-37449a29b311)) + (pad "1" smd roundrect locked (at -0.95 0 270) (size 1 1.45) (layers "B.Cu" "B.Paste" "B.Mask") (roundrect_rratio 0.25) + (net 6 "Net-(C3-Pad1)") (tstamp 4fb02e58-160a-4a39-9f22-d0c75e82ee72)) + (pad "2" smd roundrect locked (at 0.95 0 270) (size 1 1.45) (layers "B.Cu" "B.Paste" "B.Mask") (roundrect_rratio 0.25) + (net 5 "Net-(C3-Pad2)") (tstamp e615f7aa-337e-474d-9615-2ad82b1c44ca)) + (model "${KISYS3DMOD}/Capacitor_SMD.3dshapes/C_0805_2012Metric.wrl" + (offset (xyz 0 0 0)) + (scale (xyz 1 1 1)) + (rotate (xyz 0 0 0)) + ) + ) + + (footprint "Capacitor_SMD:C_0805_2012Metric" (layer "B.Cu") + (tedit 5F68FEEE) (tstamp 00000000-0000-0000-0000-00006223ab4f) + (at 127.75 99 90) + (descr "Capacitor SMD 0805 (2012 Metric), square (rectangular) end terminal, IPC_7351 nominal, (Body size source: IPC-SM-782 page 76, https://www.pcb-3d.com/wordpress/wp-content/uploads/ipc-sm-782a_amendment_1_and_2.pdf, https://docs.google.com/spreadsheets/d/1BsfQQcO9C6DZCsRaXUlFlo91Tg2WpOkGARC1WS5S8t0/edit?usp=sharing), generated with kicad-footprint-generator") + (tags "capacitor") + (path "/00000000-0000-0000-0000-00006223c217") + (attr smd) + (fp_text reference "C4" (at 0 1.68 90) (layer "B.SilkS") + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + (tstamp 67f6e996-3c99-493c-8f6f-e739e2ed5d7a) + ) + (fp_text value "4u" (at 0 -1.68 90) (layer "B.Fab") + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + (tstamp 32667662-ae86-4904-b198-3e95f11851bf) + ) + (fp_text user "${REFERENCE}" (at 0 0 90) (layer "B.Fab") + (effects (font (size 0.5 0.5) (thickness 0.08)) (justify mirror)) + (tstamp a05d7640-f2f6-4ba7-8c51-5a4af431fc13) + ) + (fp_line (start -0.261252 -0.735) (end 0.261252 -0.735) (layer "B.SilkS") (width 0.12) (tstamp 94c158d1-8503-4553-b511-bf42f506c2a8)) + (fp_line (start -0.261252 0.735) (end 0.261252 0.735) (layer "B.SilkS") (width 0.12) (tstamp 9ccf03e8-755a-4cd9-96fc-30e1d08fa253)) + (fp_line (start -1.7 -0.98) (end -1.7 0.98) (layer "B.CrtYd") (width 0.05) (tstamp 23bb2798-d93a-4696-a962-c305c4298a0c)) + (fp_line (start 1.7 0.98) (end 1.7 -0.98) (layer "B.CrtYd") (width 0.05) (tstamp 6e105729-aba0-497c-a99e-c32d2b3ddb6d)) + (fp_line (start -1.7 0.98) (end 1.7 0.98) (layer "B.CrtYd") (width 0.05) (tstamp 78cbdd6c-4878-4cc5-9a58-0e506478e37d)) + (fp_line (start 1.7 -0.98) (end -1.7 -0.98) (layer "B.CrtYd") (width 0.05) (tstamp 983c426c-24e0-4c65-ab69-1f1824adc5c6)) + (fp_line (start -1 -0.625) (end -1 0.625) (layer "B.Fab") (width 0.1) (tstamp 13abf99d-5265-4779-8973-e94370fd18ff)) + (fp_line (start 1 -0.625) (end -1 -0.625) (layer "B.Fab") (width 0.1) (tstamp 46918595-4a45-48e8-84c0-961b4db7f35f)) + (fp_line (start -1 0.625) (end 1 0.625) (layer "B.Fab") (width 0.1) (tstamp a7520ad3-0f8b-4788-92d4-8ffb277041e6)) + (fp_line (start 1 0.625) (end 1 -0.625) (layer "B.Fab") (width 0.1) (tstamp a795f1ba-cdd5-4cc5-9a52-08586e982934)) + (pad "1" smd roundrect locked (at -0.95 0 90) (size 1 1.45) (layers "B.Cu" "B.Paste" "B.Mask") (roundrect_rratio 0.25) + (net 8 "Net-(C4-Pad1)") (tstamp e9bb29b2-2bb9-4ea2-acd9-2bb3ca677a12)) + (pad "2" smd roundrect locked (at 0.95 0 90) (size 1 1.45) (layers "B.Cu" "B.Paste" "B.Mask") (roundrect_rratio 0.25) + (net 7 "Net-(C4-Pad2)") (tstamp c1d83899-e380-49f9-a87d-8e78bc089ebf)) + (model "${KISYS3DMOD}/Capacitor_SMD.3dshapes/C_0805_2012Metric.wrl" + (offset (xyz 0 0 0)) + (scale (xyz 1 1 1)) + (rotate (xyz 0 0 0)) + ) + ) + + (footprint "Resistor_SMD:R_0805_2012Metric" (layer "B.Cu") + (tedit 5F68FEEE) (tstamp 00000000-0000-0000-0000-00006223ab82) + (at 133 93.75) + (descr "Resistor SMD 0805 (2012 Metric), square (rectangular) end terminal, IPC_7351 nominal, (Body size source: IPC-SM-782 page 72, https://www.pcb-3d.com/wordpress/wp-content/uploads/ipc-sm-782a_amendment_1_and_2.pdf), generated with kicad-footprint-generator") + (tags "resistor") + (path "/00000000-0000-0000-0000-00006223b0e5") + (attr smd) + (fp_text reference "R3" (at -0.5 1.65) (layer "B.SilkS") + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + (tstamp afd3dbad-e7a8-4e4c-b77c-4065a69aefa2) + ) + (fp_text value "3" (at 0 -1.65) (layer "B.Fab") + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + (tstamp 1b54105e-6590-4d26-a763-ecfcf81eedc4) + ) + (fp_text user "${REFERENCE}" (at 0 0) (layer "B.Fab") + (effects (font (size 0.5 0.5) (thickness 0.08)) (justify mirror)) + (tstamp 0f41a909-27c4-4be2-9d5e-9ae2108c8ff5) + ) + (fp_line (start -0.227064 0.735) (end 0.227064 0.735) (layer "B.SilkS") (width 0.12) (tstamp dabe541b-b164-4180-97a4-5ca761b86800)) + (fp_line (start -0.227064 -0.735) (end 0.227064 -0.735) (layer "B.SilkS") (width 0.12) (tstamp e12e827e-36be-4503-8eef-6fc7e8bc5d49)) + (fp_line (start 1.68 -0.95) (end -1.68 -0.95) (layer "B.CrtYd") (width 0.05) (tstamp 0088d107-13d8-496c-8da6-7bbeb9d096b0)) + (fp_line (start -1.68 0.95) (end 1.68 0.95) (layer "B.CrtYd") (width 0.05) (tstamp 417f13e4-c121-485a-a6b5-8b55e70350b8)) + (fp_line (start -1.68 -0.95) (end -1.68 0.95) (layer "B.CrtYd") (width 0.05) (tstamp 9dab0cb7-2557-4419-963b-5ae736517f62)) + (fp_line (start 1.68 0.95) (end 1.68 -0.95) (layer "B.CrtYd") (width 0.05) (tstamp c201e1b2-fc01-4110-bdaa-a33290468c83)) + (fp_line (start -1 0.625) (end 1 0.625) (layer "B.Fab") (width 0.1) (tstamp 35354519-a28c-40c4-befd-0943e98dea53)) + (fp_line (start 1 0.625) (end 1 -0.625) (layer "B.Fab") (width 0.1) (tstamp 38f2d955-ea7a-4a21-aba6-02ae23f1bd4a)) + (fp_line (start -1 -0.625) (end -1 0.625) (layer "B.Fab") (width 0.1) (tstamp 632acde9-b7fd-4f04-8cb4-d2cbb06b3595)) + (fp_line (start 1 -0.625) (end -1 -0.625) (layer "B.Fab") (width 0.1) (tstamp 6b25f522-8e2d-4cd8-9d5d-a2b80f60133b)) + (pad "1" smd roundrect locked (at -0.9125 0) (size 1.025 1.4) (layers "B.Cu" "B.Paste" "B.Mask") (roundrect_rratio 0.243902) + (net 14 "Net-(R3-Pad1)") (tstamp 68e09be7-3bbc-4443-a838-209ce20b2bef)) + (pad "2" smd roundrect locked (at 0.9125 0) (size 1.025 1.4) (layers "B.Cu" "B.Paste" "B.Mask") (roundrect_rratio 0.243902) + (net 13 "Net-(R3-Pad2)") (tstamp 6a780180-586a-4241-a52d-dc7a5ffcc966)) + (model "${KISYS3DMOD}/Resistor_SMD.3dshapes/R_0805_2012Metric.wrl" + (offset (xyz 0 0 0)) + (scale (xyz 1 1 1)) + (rotate (xyz 0 0 0)) + ) + ) + + (footprint "Resistor_SMD:R_0805_2012Metric" (layer "B.Cu") + (tedit 5F68FEEE) (tstamp 00000000-0000-0000-0000-00006223ab93) + (at 133 98.5) + (descr "Resistor SMD 0805 (2012 Metric), square (rectangular) end terminal, IPC_7351 nominal, (Body size source: IPC-SM-782 page 72, https://www.pcb-3d.com/wordpress/wp-content/uploads/ipc-sm-782a_amendment_1_and_2.pdf), generated with kicad-footprint-generator") + (tags "resistor") + (path "/00000000-0000-0000-0000-00006223b0ef") + (attr smd) + (fp_text reference "R4" (at 0 1.65) (layer "B.SilkS") + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + (tstamp 9702d639-3b1f-4825-8985-b32b9008503d) + ) + (fp_text value "4" (at 0 -1.65) (layer "B.Fab") + (effects (font (size 1 1) (thickness 0.15)) (justify mirror)) + (tstamp 0d35483a-0b12-46cc-b9f2-896fd6831779) + ) + (fp_text user "${REFERENCE}" (at 0 0) (layer "B.Fab") + (effects (font (size 0.5 0.5) (thickness 0.08)) (justify mirror)) + (tstamp 4e66a44f-7fa6-4e16-bf9b-62ec864301a5) + ) + (fp_line (start -0.227064 -0.735) (end 0.227064 -0.735) (layer "B.SilkS") (width 0.12) (tstamp a9ec539a-d80d-40cc-803c-12b6adefe42a)) + (fp_line (start -0.227064 0.735) (end 0.227064 0.735) (layer "B.SilkS") (width 0.12) (tstamp ef1b4b98-541b-4673-a04f-2043250fc40a)) + (fp_line (start -1.68 0.95) (end 1.68 0.95) (layer "B.CrtYd") (width 0.05) (tstamp 2bf3f24b-fd30-41a7-a274-9b519491916b)) + (fp_line (start 1.68 0.95) (end 1.68 -0.95) (layer "B.CrtYd") (width 0.05) (tstamp 4831966c-bb32-4bc8-a400-0382a02ffa1c)) + (fp_line (start -1.68 -0.95) (end -1.68 0.95) (layer "B.CrtYd") (width 0.05) (tstamp c264c438-a475-4ad4-9915-0f1e6ecf3053)) + (fp_line (start 1.68 -0.95) (end -1.68 -0.95) (layer "B.CrtYd") (width 0.05) (tstamp e25ce415-914a-48fe-bf09-324317917b2e)) + (fp_line (start 1 -0.625) (end -1 -0.625) (layer "B.Fab") (width 0.1) (tstamp 34871042-9d5c-4e29-abdd-a168368c3c22)) + (fp_line (start -1 -0.625) (end -1 0.625) (layer "B.Fab") (width 0.1) (tstamp 4412226e-d975-40a2-921f-502ff4129a95)) + (fp_line (start 1 0.625) (end 1 -0.625) (layer "B.Fab") (width 0.1) (tstamp 53c85970-3e21-4fae-a84f-721cfc0513b5)) + (fp_line (start -1 0.625) (end 1 0.625) (layer "B.Fab") (width 0.1) (tstamp 7447a6e7-8205-46ba-afca-d0fa8f90c95a)) + (pad "1" smd roundrect locked (at -0.9125 0) (size 1.025 1.4) (layers "B.Cu" "B.Paste" "B.Mask") (roundrect_rratio 0.243902) + (net 16 "Net-(R4-Pad1)") (tstamp 9762c9ed-64d8-4f3e-baf6-f6ba6effc919)) + (pad "2" smd roundrect locked (at 0.9125 0) (size 1.025 1.4) (layers "B.Cu" "B.Paste" "B.Mask") (roundrect_rratio 0.243902) + (net 15 "Net-(R4-Pad2)") (tstamp 4d4b0fcd-2c79-4fc3-b5fa-7a0741601344)) + (model "${KISYS3DMOD}/Resistor_SMD.3dshapes/R_0805_2012Metric.wrl" + (offset (xyz 0 0 0)) + (scale (xyz 1 1 1)) + (rotate (xyz 0 0 0)) + ) + ) + + (gr_line (start 125 102) (end 125 91) (layer "Edge.Cuts") (width 0.05) (tstamp 00000000-0000-0000-0000-00006223ae41)) + (gr_line (start 136 91) (end 136 102) (layer "Edge.Cuts") (width 0.05) (tstamp 29e78086-2175-405e-9ba3-c48766d2f50c)) + (gr_line (start 125 91) (end 136 91) (layer "Edge.Cuts") (width 0.05) (tstamp 94a873dc-af67-4ef9-8159-1f7c93eeb3d7)) + (gr_line (start 136 102) (end 125 102) (layer "Edge.Cuts") (width 0.05) (tstamp a1823eb2-fb0d-4ed8-8b96-04184ac3a9d5)) + +) diff --git a/tests/test_plot/test_misc.py b/tests/test_plot/test_misc.py index 21b3fa37..38a5e1de 100644 --- a/tests/test_plot/test_misc.py +++ b/tests/test_plot/test_misc.py @@ -1413,3 +1413,59 @@ def test_diff_file_sch_1(test_dir): ctx.expect_out_file(prj+'-diff_sch_FILE-Current.pdf') ctx.compare_pdf(prj+'-diff_sch.pdf') ctx.clean_up(keep_project=True) + + +@pytest.mark.skipif(context.ki5(), reason="KiCad 6 aliases used") +def test_copy_files_1(test_dir): + """ Copy files and 3D models """ + prj = 'copy_files' + ctx = context.TestContext(test_dir, prj, 'copy_files_1', 'test.files') + ctx.run(kicost=True) # We use the fake web server + # The modified PCB + ctx.expect_out_file(prj+'.kicad_pcb', sub=True) + # The 3D models + ctx.expect_out_file('3d_models/C_0805_2012Metric.wrl', sub=True) + ctx.expect_out_file('3d_models/R_0805_2012Metric.wrl', sub=True) + ctx.expect_out_file('3d_models/R_0805_2012Metrico.wrl', sub=True) + ctx.expect_out_file('3d_models/test.wrl', sub=True) + # From output with dest + ctx.expect_out_file('my_position/'+prj+'-both_pos.pos', sub=True) + # From output without dest + ctx.expect_out_file('positiondir/'+prj+'-both_pos.pos', sub=True) + # From output dir + ctx.expect_out_file('my_position2/'+prj+'-both_pos.pos', sub=True) + # From outside the output dir + ctx.expect_out_file('source/test_v5.sch', sub=True) + ctx.expect_out_file('source/deeper.sch', sub=True) + ctx.expect_out_file('source/sub-sheet.sch', sub=True) + ctx.expect_out_file('source/test_v5.kicad_pcb', sub=True) + # Some warnings + ctx.search_err([r'WARNING:\(W098\) 2 3D models downloaded', # 2 models are missing and they are downloaded + r'WARNING:\(W100\)']) # 2 models has the same name + ctx.clean_up() + + +@pytest.mark.skipif(context.ki5(), reason="KiCad 6 aliases used") +def test_copy_files_2(test_dir): + """ Copy files and 3D models """ + prj = 'copy_files' + ctx = context.TestContext(test_dir, prj, 'copy_files_2', 'test.files') + ctx.run(kicost=True) # We use the fake web server + # The modified PCB + ctx.expect_out_file(prj+'.kicad_pcb', sub=True) + # The 3D models + MODELS = ['3d_models/3d/1/test.wrl', '3d_models/3d/2/test.wrl', + '3d_models/Resistor_SMD.3dshapes/R_0805_2012Metrico.step', + '3d_models/Resistor_SMD.3dshapes/R_0805_2012Metrico.wrl', + '3d_models/Capacitor_SMD.3dshapes/C_0805_2012Metric.step', + '3d_models/Capacitor_SMD.3dshapes/C_0805_2012Metric.wrl', + '3d_models/Resistor_SMD.3dshapes/R_0805_2012Metric.step', + '3d_models/Resistor_SMD.3dshapes/R_0805_2012Metric.wrl'] + for m in MODELS: + ctx.expect_out_file(m, sub=True) + # Make sure the PCB points to them + ctx.search_in_file(prj+'.kicad_pcb', ['model "{}"'.format(m) for m in MODELS if m.endswith('wrl')], sub=True) + # Some warnings + ctx.search_err(r'WARNING:\(W098\) 2 3D models downloaded') # 2 models are missing and they are downloaded + ctx.search_err(r'WARNING:\(W100\)', invert=True) # 2 models has the same name, but goes to different target + ctx.clean_up() diff --git a/tests/utils/context.py b/tests/utils/context.py index d09071db..c947d4d3 100644 --- a/tests/utils/context.py +++ b/tests/utils/context.py @@ -442,9 +442,9 @@ class TestContext(object): logging.debug('error match: `{}` (`{}`) OK'.format(text, m.group(0))) return m - def search_in_file(self, file, texts): + def search_in_file(self, file, texts, sub=False): logging.debug('Searching in "'+file+'" output') - with open(self.get_out_path(file)) as f: + with open(self.get_out_path(file, sub=sub)) as f: txt = f.read() res = [] for t in texts: diff --git a/tests/yaml_samples/copy_files_1.kibot.yaml b/tests/yaml_samples/copy_files_1.kibot.yaml new file mode 100644 index 00000000..163454d1 --- /dev/null +++ b/tests/yaml_samples/copy_files_1.kibot.yaml @@ -0,0 +1,51 @@ +# Example KiBot config file +kibot: + version: 1 + +global: + environment: + # Relative to the PCB file + models_3d: '../../data/metrico/' + aliases_for_3d_models: + - name: ALIAS1 + value: '3d/1' + - name: ALIAS2 + value: '3d/2' + +outputs: + - name: 'position' + comment: "Pick and place file" + type: position + dir: positiondir + options: + format: ASCII # CSV or ASCII format + units: millimeters # millimeters or inches + separate_files_for_front_and_back: false + only_smd: true + + - name: result + comment: 'Copy files from source, output and 3D models' + type: copy_files + dir: 'test.%x' + options: + # link_no_copy: true + kicad_3d_url: 'http://localhost:8000/' + files: + - source: tests/board_samples/kicad_5/test_v5.* + dest: source + - source: tests/board_samples/kicad_5/deeper.sch + dest: source + - source: tests/board_samples/kicad_5/sub-sheet.sch + dest: source + - source: position + source_type: output + dest: my_position + - source: position + source_type: output + - source: positiondir/*.pos + source_type: out_files + dest: my_position2 + - source: '*.wrl' + source_type: 3d_models + dest: 3d_models + save_pcb: true diff --git a/tests/yaml_samples/copy_files_2.kibot.yaml b/tests/yaml_samples/copy_files_2.kibot.yaml new file mode 100644 index 00000000..2d2366fb --- /dev/null +++ b/tests/yaml_samples/copy_files_2.kibot.yaml @@ -0,0 +1,36 @@ +# Example KiBot config file +kibot: + version: 1 + +global: + environment: + # Relative to the PCB file + models_3d: '../../data/metrico/' + aliases_for_3d_models: + - name: ALIAS1 + value: '3d/1' + - name: ALIAS2 + value: '3d/2' + +outputs: + - name: 'position' + comment: "Pick and place file" + type: position + dir: positiondir + options: + format: ASCII # CSV or ASCII format + units: millimeters # millimeters or inches + separate_files_for_front_and_back: false + only_smd: true + + - name: result + comment: 'Copy files from source, output and 3D models' + type: copy_files + dir: 'test.%x' + options: + # link_no_copy: true + kicad_3d_url: 'http://localhost:8000/' + files: + - source_type: 3d_models + dest: 3d_models+ + save_pcb: true