From 42d1f14056d8cc083dcc0bafd63a42244fc874f0 Mon Sep 17 00:00:00 2001 From: "Salvador E. Tropea" Date: Mon, 29 Jun 2020 12:55:02 -0300 Subject: [PATCH] Now layers are solved when we really need it. So we can do some operations (like --list) without loading the board. Also: now the names for the layers are asked to pcbnew classes (not from file) --- CHANGELOG.md | 2 + kiplot/__main__.py | 50 +++++++-------- kiplot/config_reader.py | 55 +--------------- kiplot/kiplot.py | 97 ++++++++++++++++++----------- kiplot/out_any_layer.py | 6 +- kiplot/out_pdf_pcb_print.py | 7 +-- tests/test_plot/test_misc.py | 2 +- tests/test_plot/test_yaml_errors.py | 4 +- 8 files changed, 97 insertions(+), 126 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4f95427..26c385fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Drill outputs: map.type and report.filename now should be map and report. The old mechanism is currently supported, but deprecated. - Now the command line usage is more clearly documented, but also more strict. +- The --list option doesn't need a PCB file anymore. + Note that passing it is now considered an error. ### Added - Help for the supported outputs (--help-list-outputs, --help-outputs and diff --git a/kiplot/__main__.py b/kiplot/__main__.py index d0e6896f..0a883439 100644 --- a/kiplot/__main__.py +++ b/kiplot/__main__.py @@ -4,7 +4,7 @@ Usage: kiplot [-b BOARD] [-c CONFIG] [-d OUT_DIR] [-s PRE] [-q | -v...] [-i] [TARGET...] - kiplot [-b BOARD] [-c PLOT_CONFIG] --list + kiplot [-c PLOT_CONFIG] --list kiplot --help-list-outputs kiplot --help-output=HELP_OUTPUT kiplot --help-outputs @@ -90,30 +90,6 @@ def main(): print_preflights_help() sys.exit(0) - # Determine the PCB file - if args.board_file is None: - board_files = glob('*.kicad_pcb') - if len(board_files) == 1: - board_file = board_files[0] - logger.info('Using PCB file: '+board_file) - elif len(board_files) > 1: - board_file = board_files[0] - logger.warning('More than one PCB file found in current directory.\n' - ' Using '+board_file+' if you want to use another use -b option.') - else: - logger.error('No PCB file found (*.kicad_pcb), use -b to specify one.') - sys.exit(EXIT_BAD_ARGS) - else: - board_file = args.board_file - if not os.path.isfile(board_file): - logger.error("Board file not found: "+board_file) - sys.exit(NO_PCB_FILE) - GS.pcb_file = board_file - GS.sch_file = os.path.splitext(board_file)[0] + '.sch' - if not os.path.isfile(GS.sch_file): - logger.warning('Missing schematic file: ' + GS.sch_file) - GS.sch_file = None - # Determine the YAML file if args.plot_config is None: plot_configs = glob('*.kiplot.yaml') @@ -151,6 +127,30 @@ def main(): list_pre_and_outs(logger, outputs) sys.exit(0) + # Determine the PCB file + if args.board_file is None: + board_files = glob('*.kicad_pcb') + if len(board_files) == 1: + board_file = board_files[0] + logger.info('Using PCB file: '+board_file) + elif len(board_files) > 1: + board_file = board_files[0] + logger.warning('More than one PCB file found in current directory.\n' + ' Using '+board_file+' if you want to use another use -b option.') + else: + logger.error('No PCB file found (*.kicad_pcb), use -b to specify one.') + sys.exit(EXIT_BAD_ARGS) + else: + board_file = args.board_file + if not os.path.isfile(board_file): + logger.error("Board file not found: "+board_file) + sys.exit(NO_PCB_FILE) + GS.pcb_file = board_file + GS.sch_file = os.path.splitext(board_file)[0] + '.sch' + if not os.path.isfile(GS.sch_file): + logger.warning('Missing schematic file: ' + GS.sch_file) + GS.sch_file = None + generate_outputs(outputs, args.target, args.invert_sel, args.skip_pre) diff --git a/kiplot/config_reader.py b/kiplot/config_reader.py index 0bf194de..731e74f8 100644 --- a/kiplot/config_reader.py +++ b/kiplot/config_reader.py @@ -2,14 +2,11 @@ Class to read KiPlot config files """ -import re import sys from collections import OrderedDict -import pcbnew - from .error import (KiPlotConfigurationError) -from .kiplot import (Layer, get_layer_id_from_pcb) +from .kiplot import (Layer) from .misc import (NO_YAML_MODULE, EXIT_BAD_CONFIG, EXIT_BAD_ARGS) from mcpy import activate # noqa: F401 # Output classes @@ -69,52 +66,6 @@ class CfgYamlReader(object): config_error("Unknown KiPlot config version: "+str(version)) return version - def _get_layer_from_str(self, s): - """ - Get the pcbnew layer from a string in the config - """ - D = { - 'F.Cu': pcbnew.F_Cu, - 'B.Cu': pcbnew.B_Cu, - 'F.Adhes': pcbnew.F_Adhes, - 'B.Adhes': pcbnew.B_Adhes, - 'F.Paste': pcbnew.F_Paste, - 'B.Paste': pcbnew.B_Paste, - 'F.SilkS': pcbnew.F_SilkS, - 'B.SilkS': pcbnew.B_SilkS, - 'F.Mask': pcbnew.F_Mask, - 'B.Mask': pcbnew.B_Mask, - 'Dwgs.User': pcbnew.Dwgs_User, - 'Cmts.User': pcbnew.Cmts_User, - 'Eco1.User': pcbnew.Eco1_User, - 'Eco2.User': pcbnew.Eco2_User, - 'Edge.Cuts': pcbnew.Edge_Cuts, - 'Margin': pcbnew.Margin, - 'F.CrtYd': pcbnew.F_CrtYd, - 'B.CrtYd': pcbnew.B_CrtYd, - 'F.Fab': pcbnew.F_Fab, - 'B.Fab': pcbnew.B_Fab, - } - layer = None - # Priority - # 1) Internal list - if s in D: - layer = Layer(D[s], False, s) - else: - id = get_layer_id_from_pcb(s) - if id is not None: - # 2) List from the PCB - layer = Layer(id, id < pcbnew.B_Cu, s) - elif s.startswith("Inner"): - # 3) Inner.N names - m = re.match(r"^Inner\.([0-9]+)$", s) - if not m: - raise KiPlotConfigurationError("Malformed inner layer name: {}, use Inner.N".format(s)) - layer = Layer(int(m.group(1)), True, s) - else: - raise KiPlotConfigurationError('Unknown layer name: '+s) - return layer - def _parse_layers(self, layers_to_parse): # Check we have a list of layers if not isinstance(layers_to_parse, list): @@ -139,9 +90,7 @@ class CfgYamlReader(object): if layer is None: raise KiPlotConfigurationError("Missing `layer` attribute for layer entry ({})".format(l)) # Create an object for it - layer = self._get_layer_from_str(layer) - layer.set_extra(suffix, description) - layers.append(layer) + layers.append(Layer(layer, suffix, description)) return layers def _parse_output(self, o_obj): diff --git a/kiplot/kiplot.py b/kiplot/kiplot.py index ee87c89f..7ce2e236 100644 --- a/kiplot/kiplot.py +++ b/kiplot/kiplot.py @@ -36,53 +36,78 @@ class GS(object): out_dir = None filter_file = None debug_enabled = False - pcb_layers = None class Layer(object): """ A layer description """ - def __init__(self, id, is_inner, name): - self.id = id - self.is_inner = is_inner - self.name = name - self.suffix = None - self.desc = None + # Default names + DEFAULT_LAYER_NAMES = { + 'F.Cu': pcbnew.F_Cu, + 'B.Cu': pcbnew.B_Cu, + 'F.Adhes': pcbnew.F_Adhes, + 'B.Adhes': pcbnew.B_Adhes, + 'F.Paste': pcbnew.F_Paste, + 'B.Paste': pcbnew.B_Paste, + 'F.SilkS': pcbnew.F_SilkS, + 'B.SilkS': pcbnew.B_SilkS, + 'F.Mask': pcbnew.F_Mask, + 'B.Mask': pcbnew.B_Mask, + 'Dwgs.User': pcbnew.Dwgs_User, + 'Cmts.User': pcbnew.Cmts_User, + 'Eco1.User': pcbnew.Eco1_User, + 'Eco2.User': pcbnew.Eco2_User, + 'Edge.Cuts': pcbnew.Edge_Cuts, + 'Margin': pcbnew.Margin, + 'F.CrtYd': pcbnew.F_CrtYd, + 'B.CrtYd': pcbnew.B_CrtYd, + 'F.Fab': pcbnew.F_Fab, + 'B.Fab': pcbnew.B_Fab, + } + # Names from the board file + pcb_layers = {} - def set_extra(self, suffix, desc): + def __init__(self, name, suffix, desc): + self.id = pcbnew.UNDEFINED_LAYER + self.is_inner = False + self.name = name self.suffix = suffix self.desc = desc + @staticmethod + def set_pcb_layers(board): + for id in range(pcbnew.PCBNEW_LAYER_ID_START, pcbnew.PCB_LAYER_ID_COUNT): + Layer.pcb_layers[board.GetLayerName(id)] = id + + def get_layer_id_from_name(self, layer_cnt): + """ Get the pcbnew layer from the string provided in the config """ + # Priority + # 1) Internal list + if self.name in Layer.DEFAULT_LAYER_NAMES: + self.id = Layer.DEFAULT_LAYER_NAMES[self.name] + else: + id = Layer.pcb_layers.get(self.name) + if id is not None: + # 2) List from the PCB + self.id = id + self.is_inner = id < pcbnew.B_Cu + elif self.name.startswith("Inner"): + # 3) Inner.N names + m = re.match(r"^Inner\.([0-9]+)$", self.name) + if not m: + raise PlotError("Malformed inner layer name: {}, use Inner.N".format(self.name)) + self.id = int(m.group(1)) + self.is_inner = True + else: + raise PlotError("Unknown layer name: "+self.name) + # Check if the layer is in use + if self.is_inner and (self.id < 1 or self.id >= layer_cnt - 1): + raise PlotError("Inner layer `{}` is not valid for this board".format(self)) + return self.id + def __str__(self): return "{} ({} '{}' {})".format(self.name, self.id, self.desc, self.suffix) -def load_pcb_layers(): - """ Load layer names from the PCB """ - GS.pcb_layers = {} - with open(GS.pcb_file, "r") as pcb_file: - collect_layers = False - for line in pcb_file: - if collect_layers: - z = re.match(r'\s+\((\d+)\s+(\S+)', line) - if z: - res = z.groups() - # print(res[1]+'->'+res[0]) - GS.pcb_layers[res[1]] = int(res[0]) - else: - if re.search(r'^\s+\)$', line): - collect_layers = False - break - else: - if re.search(r'\s+\(layers', line): - collect_layers = True - - -def get_layer_id_from_pcb(name): - if GS.pcb_layers is None: - load_pcb_layers() - return GS.pcb_layers.get(name) - - def check_version(command, version): cmd = [command, '--version'] logger.debug('Running: '+str(cmd)) @@ -120,6 +145,8 @@ def load_board(): board = pcbnew.LoadBoard(GS.pcb_file) if BasePreFlight.get_option('check_zone_fills'): pcbnew.ZONE_FILLER(board).Fill(board.Zones()) + # Now we know the names of the layers for this board + Layer.set_pcb_layers(board) except OSError as e: logger.error('Error loading PCB file. Currupted?') logger.error(e) diff --git a/kiplot/out_any_layer.py b/kiplot/out_any_layer.py index 6ea5a6ae..ed416d84 100644 --- a/kiplot/out_any_layer.py +++ b/kiplot/out_any_layer.py @@ -99,11 +99,7 @@ class AnyLayer(BaseOutput): for l in self._layers: suffix = l.suffix desc = l.desc - id = l.id - # for inner layers, we can now check if the layer exists - if l.is_inner: - if id < 1 or id >= layer_cnt - 1: - raise PlotError("Inner layer `{}` is not valid for this board".format(l)) + id = l.get_layer_id_from_name(layer_cnt) # Set current layer plot_ctrl.SetLayer(id) # Skipping NPTH is controlled by whether or not this is diff --git a/kiplot/out_pdf_pcb_print.py b/kiplot/out_pdf_pcb_print.py index 8f5f324b..aa02c98a 100644 --- a/kiplot/out_pdf_pcb_print.py +++ b/kiplot/out_pdf_pcb_print.py @@ -1,7 +1,7 @@ import os from subprocess import (call) from .pre_base import BasePreFlight -from .error import (KiPlotConfigurationError, PlotError) +from .error import (KiPlotConfigurationError) from .kiplot import (check_script, GS) from .misc import (CMD_PCBNEW_PRINT_LAYERS, URL_PCBNEW_PRINT_LAYERS, PDF_PCB_PRINT) from kiplot.macros import macros, document, output_class # noqa: F401 @@ -34,10 +34,7 @@ class PDF_Pcb_Print(BaseOutput): # noqa: F821 # Verify the inner layers layer_cnt = board.GetCopperLayerCount() for l in self._layers: - # for inner layers, we can now check if the layer exists - if l.is_inner: - if l.id < 1 or l.id >= layer_cnt - 1: - raise PlotError("Inner layer `{}` is not valid for this board".format(l)) + l.get_layer_id_from_name(layer_cnt) # Output file name output = self.output_name if not output: diff --git a/tests/test_plot/test_misc.py b/tests/test_plot/test_misc.py index faef9aa1..25214a61 100644 --- a/tests/test_plot/test_misc.py +++ b/tests/test_plot/test_misc.py @@ -206,7 +206,7 @@ def test_auto_pcb_and_cfg_2(): def test_list(): ctx = context.TestContext('List', '3Rs', 'pre_and_position', POS_DIR) - ctx.run(extra=['--list'], no_verbose=True, no_out_dir=True) + ctx.run(extra=['--list'], no_verbose=True, no_out_dir=True, no_board_file=True) assert ctx.search_err('run_erc: True') assert ctx.search_err('run_drc: True') diff --git a/tests/test_plot/test_yaml_errors.py b/tests/test_plot/test_yaml_errors.py index 16d680bd..9d0b008c 100644 --- a/tests/test_plot/test_yaml_errors.py +++ b/tests/test_plot/test_yaml_errors.py @@ -99,7 +99,7 @@ def test_drill_map_wrong_type(): def test_wrong_layer_1(): ctx = context.TestContext('ErrorWrongLayer1', '3Rs', 'error_wrong_layer_1', None) - ctx.run(EXIT_BAD_CONFIG) + ctx.run(PLOT_ERROR) assert ctx.search_err("Unknown layer name: F.Bogus") ctx.clean_up() @@ -113,7 +113,7 @@ def test_wrong_layer_2(): def test_wrong_layer_3(): ctx = context.TestContext('ErrorWrongLayer3', '3Rs', 'error_wrong_layer_3', None) - ctx.run(EXIT_BAD_CONFIG) + ctx.run(PLOT_ERROR) assert ctx.search_err("Malformed inner layer name: Inner_1,") ctx.clean_up()