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)
This commit is contained in:
Salvador E. Tropea 2020-06-29 12:55:02 -03:00
parent ef4b2f8f95
commit 42d1f14056
8 changed files with 97 additions and 126 deletions

View File

@ -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. - Drill outputs: map.type and report.filename now should be map and report.
The old mechanism is currently supported, but deprecated. The old mechanism is currently supported, but deprecated.
- Now the command line usage is more clearly documented, but also more strict. - 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 ### Added
- Help for the supported outputs (--help-list-outputs, --help-outputs and - Help for the supported outputs (--help-list-outputs, --help-outputs and

View File

@ -4,7 +4,7 @@
Usage: Usage:
kiplot [-b BOARD] [-c CONFIG] [-d OUT_DIR] [-s PRE] [-q | -v...] [-i] kiplot [-b BOARD] [-c CONFIG] [-d OUT_DIR] [-s PRE] [-q | -v...] [-i]
[TARGET...] [TARGET...]
kiplot [-b BOARD] [-c PLOT_CONFIG] --list kiplot [-c PLOT_CONFIG] --list
kiplot --help-list-outputs kiplot --help-list-outputs
kiplot --help-output=HELP_OUTPUT kiplot --help-output=HELP_OUTPUT
kiplot --help-outputs kiplot --help-outputs
@ -90,30 +90,6 @@ def main():
print_preflights_help() print_preflights_help()
sys.exit(0) 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 # Determine the YAML file
if args.plot_config is None: if args.plot_config is None:
plot_configs = glob('*.kiplot.yaml') plot_configs = glob('*.kiplot.yaml')
@ -151,6 +127,30 @@ def main():
list_pre_and_outs(logger, outputs) list_pre_and_outs(logger, outputs)
sys.exit(0) 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) generate_outputs(outputs, args.target, args.invert_sel, args.skip_pre)

View File

@ -2,14 +2,11 @@
Class to read KiPlot config files Class to read KiPlot config files
""" """
import re
import sys import sys
from collections import OrderedDict from collections import OrderedDict
import pcbnew
from .error import (KiPlotConfigurationError) 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 .misc import (NO_YAML_MODULE, EXIT_BAD_CONFIG, EXIT_BAD_ARGS)
from mcpy import activate # noqa: F401 from mcpy import activate # noqa: F401
# Output classes # Output classes
@ -69,52 +66,6 @@ class CfgYamlReader(object):
config_error("Unknown KiPlot config version: "+str(version)) config_error("Unknown KiPlot config version: "+str(version))
return 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): def _parse_layers(self, layers_to_parse):
# Check we have a list of layers # Check we have a list of layers
if not isinstance(layers_to_parse, list): if not isinstance(layers_to_parse, list):
@ -139,9 +90,7 @@ class CfgYamlReader(object):
if layer is None: if layer is None:
raise KiPlotConfigurationError("Missing `layer` attribute for layer entry ({})".format(l)) raise KiPlotConfigurationError("Missing `layer` attribute for layer entry ({})".format(l))
# Create an object for it # Create an object for it
layer = self._get_layer_from_str(layer) layers.append(Layer(layer, suffix, description))
layer.set_extra(suffix, description)
layers.append(layer)
return layers return layers
def _parse_output(self, o_obj): def _parse_output(self, o_obj):

View File

@ -36,53 +36,78 @@ class GS(object):
out_dir = None out_dir = None
filter_file = None filter_file = None
debug_enabled = False debug_enabled = False
pcb_layers = None
class Layer(object): class Layer(object):
""" A layer description """ """ A layer description """
def __init__(self, id, is_inner, name): # Default names
self.id = id DEFAULT_LAYER_NAMES = {
self.is_inner = is_inner 'F.Cu': pcbnew.F_Cu,
self.name = name 'B.Cu': pcbnew.B_Cu,
self.suffix = None 'F.Adhes': pcbnew.F_Adhes,
self.desc = None '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.suffix = suffix
self.desc = desc 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): def __str__(self):
return "{} ({} '{}' {})".format(self.name, self.id, self.desc, self.suffix) 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): def check_version(command, version):
cmd = [command, '--version'] cmd = [command, '--version']
logger.debug('Running: '+str(cmd)) logger.debug('Running: '+str(cmd))
@ -120,6 +145,8 @@ def load_board():
board = pcbnew.LoadBoard(GS.pcb_file) board = pcbnew.LoadBoard(GS.pcb_file)
if BasePreFlight.get_option('check_zone_fills'): if BasePreFlight.get_option('check_zone_fills'):
pcbnew.ZONE_FILLER(board).Fill(board.Zones()) 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: except OSError as e:
logger.error('Error loading PCB file. Currupted?') logger.error('Error loading PCB file. Currupted?')
logger.error(e) logger.error(e)

View File

@ -99,11 +99,7 @@ class AnyLayer(BaseOutput):
for l in self._layers: for l in self._layers:
suffix = l.suffix suffix = l.suffix
desc = l.desc desc = l.desc
id = l.id id = l.get_layer_id_from_name(layer_cnt)
# 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))
# Set current layer # Set current layer
plot_ctrl.SetLayer(id) plot_ctrl.SetLayer(id)
# Skipping NPTH is controlled by whether or not this is # Skipping NPTH is controlled by whether or not this is

View File

@ -1,7 +1,7 @@
import os import os
from subprocess import (call) from subprocess import (call)
from .pre_base import BasePreFlight from .pre_base import BasePreFlight
from .error import (KiPlotConfigurationError, PlotError) from .error import (KiPlotConfigurationError)
from .kiplot import (check_script, GS) from .kiplot import (check_script, GS)
from .misc import (CMD_PCBNEW_PRINT_LAYERS, URL_PCBNEW_PRINT_LAYERS, PDF_PCB_PRINT) from .misc import (CMD_PCBNEW_PRINT_LAYERS, URL_PCBNEW_PRINT_LAYERS, PDF_PCB_PRINT)
from kiplot.macros import macros, document, output_class # noqa: F401 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 # Verify the inner layers
layer_cnt = board.GetCopperLayerCount() layer_cnt = board.GetCopperLayerCount()
for l in self._layers: for l in self._layers:
# for inner layers, we can now check if the layer exists l.get_layer_id_from_name(layer_cnt)
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))
# Output file name # Output file name
output = self.output_name output = self.output_name
if not output: if not output:

View File

@ -206,7 +206,7 @@ def test_auto_pcb_and_cfg_2():
def test_list(): def test_list():
ctx = context.TestContext('List', '3Rs', 'pre_and_position', POS_DIR) 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_erc: True')
assert ctx.search_err('run_drc: True') assert ctx.search_err('run_drc: True')

View File

@ -99,7 +99,7 @@ def test_drill_map_wrong_type():
def test_wrong_layer_1(): def test_wrong_layer_1():
ctx = context.TestContext('ErrorWrongLayer1', '3Rs', 'error_wrong_layer_1', None) 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") assert ctx.search_err("Unknown layer name: F.Bogus")
ctx.clean_up() ctx.clean_up()
@ -113,7 +113,7 @@ def test_wrong_layer_2():
def test_wrong_layer_3(): def test_wrong_layer_3():
ctx = context.TestContext('ErrorWrongLayer3', '3Rs', 'error_wrong_layer_3', None) 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,") assert ctx.search_err("Malformed inner layer name: Inner_1,")
ctx.clean_up() ctx.clean_up()