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:
parent
ef4b2f8f95
commit
42d1f14056
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue