Added a preflight to define KiCad 6 variables.

Closes #161
This commit is contained in:
Salvador E. Tropea 2022-03-12 18:44:08 -03:00
parent c35ad9ea0f
commit 1824d56876
11 changed files with 185 additions and 9 deletions

View File

@ -52,7 +52,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
(#103)
- A mechanism to avoid running some outputs by default. (#112)
- New preflights:
- Commands to replace tags in the schematic and PCB. (#93)
- Commands to replace tags in the schematic and PCB (KiCad 5). (#93)
Also a mechanism to define variables in KiCad 6. (#161)
- Annotate power components. (#76)
- Annotate according to PCB coordinates (#93)
- Now you can compress files relative to the current working directory.

View File

@ -173,6 +173,9 @@ This section is used to specify tasks that will be executed before generating an
- `tag_delimiter`: [string='@'] Character used to indicate the beginning and the end of a tag.
Don't change it unless you really know about KiCad's file formats.
- `text`: [string=''] Text to insert instead of the tag.
- `set_text_variables`: [dict|list(dict)] Defines KiCad 6 variables.
They are expanded using ${VARIABLE}, and stored in the project file.
This preflight replaces `pcb_replace` and `sch_replace` when using KiCad 6.
- `update_qr`: [boolean=false] Update the QR codes.
Complements the `qr_lib` output.
The KiCad 6 files and the KiCad 5 PCB needs manual update, generating a new library isn't enough.

View File

@ -45,7 +45,6 @@ preflight:
command: 'git log -1 --format="%h" $KIBOT_PCB_NAME'
before: 'Git hash: <'
after: '>'
# [boolean=false] Runs the DRC (Distance Rules Check). To ensure we have a valid PCB.
# The report file name is controlled by the global output pattern (%i=drc %x=txt).
run_drc: true
@ -61,7 +60,14 @@ preflight:
command: 'git log -1 --format="%h" $KIBOT_SCH_NAME'
before: 'Git hash: <'
after: '>'
# [dict|list(dict)] Defines KiCad 6 variables.
# They are expanded using ${VARIABLE}, and stored in the project file.
# This preflight replaces `pcb_replace` and `sch_replace` when using KiCad 6.
set_text_variables:
- name: 'git_hash'
command: 'git log -1 --format="%h" $KIBOT_PCB_NAME'
before: 'Git hash: <'
after: '>'
# [boolean=false] Update the QR codes.
# Complements the `qr_lib` output.
# The KiCad 6 files and the KiCad 5 PCB needs manual update, generating a new library isn't enough.

View File

@ -145,8 +145,8 @@ def solve_schematic(a_schematic, a_board_file, config):
# Look for a schematic with a PCB and/or project
for sch in schematics:
base = os.path.splitext(sch)[0]
if os.path.isfile(base+'.pro') or os.path.isfile(base+'.kicad_pro') or \
os.path.isfile(base+'.kicad_pcb'):
if (os.path.isfile(base+'.pro') or os.path.isfile(base+'.kicad_pro') or
os.path.isfile(base+'.kicad_pcb')):
schematic = sch
break
else:

View File

@ -84,7 +84,7 @@ class Base_Replace(BasePreFlight): # noqa: F821
"\n - tag: '@git_hash@'"
"\n command: 'git log -1 --format=\"%h\" $KIBOT_{}_NAME'"
"\n before: 'Git hash: <'"
"\n after: '>'\n".format(cls._context, cls._context))
"\n after: '>'".format(cls._context, cls._context))
def replace(self, file):
logger.debug('Applying replacements to `{}`'.format(file))

View File

@ -28,6 +28,7 @@ class PCB_ReplaceOptions(Base_ReplaceOptions):
@pre_class
class PCB_Replace(Base_Replace): # noqa: F821
""" [dict] Replaces tags in the schematic. I.e. to insert the git hash or last revision date.
This is useful for KiCad 5, use `set_text_variables` when using KiCad 6.
This pre-flight modifies the PCB. Even when a back-up is done use it carefully """
_context = 'PCB'

View File

@ -32,6 +32,7 @@ class SCH_ReplaceOptions(Base_ReplaceOptions):
@pre_class
class SCH_Replace(Base_Replace): # noqa: F821
""" [dict] Replaces tags in the schematic. I.e. to insert the git hash or last revision date.
This is useful for KiCad 5, use `set_text_variables` when using KiCad 6.
This pre-flight modifies the schematics. Even when a back-up is done use it carefully """
_context = 'SCH'

View File

@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2022 Salvador E. Tropea
# Copyright (c) 2022 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0
# Project: KiBot (formerly KiPlot)
import os
import sys
import json
from subprocess import run, PIPE
from .error import KiPlotConfigurationError
from .misc import FAILED_EXECUTE, W_EMPTREP
from .optionable import Optionable
from .pre_base import BasePreFlight
from .gs import GS
from .macros import macros, document, pre_class # noqa: F401
from . import log
logger = log.get_logger()
class KiCadVariable(Optionable):
""" KiCad variable definition """
def __init__(self):
super().__init__()
self._unkown_is_error = True
with document:
self.name = ''
""" Name of the variable. The `version` variable will be expanded using `${version}` """
self.variable = None
""" {name} """
self.text = ''
""" Text to insert instead of the variable """
self.command = ''
""" Command to execute to get the text, will be used only if `text` is empty """
self.before = ''
""" Text to add before the output of `command` """
self.after = ''
""" Text to add after the output of `command` """
def config(self, parent):
super().config(parent)
if not self.name:
raise KiPlotConfigurationError("Missing variable name ({})".format(str(self._tree)))
class Set_Text_VariablesOptions(Optionable):
""" A list of KiCad variables """
def __init__(self):
super().__init__()
with document:
self.variables = KiCadVariable
""" [dict|list(dict)] Variables """
def config(self, parent):
super().config(parent)
if isinstance(self.variables, type):
self.variables = []
elif isinstance(self.variables, KiCadVariable):
self.variables = [self.variables]
@pre_class
class Set_Text_Variables(BasePreFlight): # noqa: F821
""" [dict|list(dict)] Defines KiCad 6 variables.
They are expanded using ${VARIABLE}, and stored in the project file.
This preflight replaces `pcb_replace` and `sch_replace` when using KiCad 6 """
def __init__(self, name, value):
f = Set_Text_VariablesOptions()
f.set_tree({'variables': value})
f.config(self)
super().__init__(name, f.variables)
@classmethod
def get_example(cls):
""" Returns a YAML value for the example config """
return ("\n - name: 'git_hash'"
"\n command: 'git log -1 --format=\"%h\" $KIBOT_PCB_NAME'"
"\n before: 'Git hash: <'"
"\n after: '>'")
def apply(self):
o = self._value
if len(o) == 0:
return
if GS.ki5():
raise KiPlotConfigurationError("The `set_text_variables` preflight is for KiCad 6 or newer")
pro_name = GS.pcb_file.replace('.kicad_pcb', GS.pro_ext)
if not os.path.isfile(pro_name):
raise KiPlotConfigurationError("Trying to define KiCad 6 variables but the project is missing ({})".
format(pro_name))
# Get the current definitions
with open(pro_name, 'rt') as f:
pro_text = f.read()
data = json.loads(pro_text)
text_variables = data.get('text_variables', {})
logger.debug("- Current variables: {}".format(text_variables))
# Define the requested variables
os.environ['KIBOT_PCB_NAME'] = GS.pcb_file
os.environ['KIBOT_SCH_NAME'] = GS.sch_file
for r in o:
text = r.text
if not text:
cmd = ['/bin/bash', '-c', r.command]
result = run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True)
if result.returncode:
logger.error('Failed to execute:\n{}\nreturn code {}'.format(r.command, result.returncode))
sys.exit(FAILED_EXECUTE)
if not result.stdout:
logger.warning(W_EMPTREP+"Empty value from `{}` skipping it".format(r.command))
continue
text = result.stdout.strip()
text = r.before + text + r.after
logger.debug(' - ' + r.name + ' -> ' + text)
text_variables[r.name] = text
logger.debug("- New list of variables: {}".format(text_variables))
# Store the modified project
data['text_variables'] = text_variables
GS.make_bkp(pro_name)
with open(pro_name, 'wt') as f:
f.write(json.dumps(data, sort_keys=True, indent=2))
# Force the PCB reload (will reload the project file)
GS.board = None

View File

@ -48,7 +48,13 @@
"min_clearance": 0.508
}
},
"diff_pair_dimensions": [],
"diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [],
"meta": {
"filename": "board_design_settings.json",
@ -207,5 +213,7 @@
"legacy_lib_list": []
},
"sheets": [],
"text_variables": {}
"text_variables": {
"PRUEBITA": "Hola!"
}
}

View File

@ -18,6 +18,7 @@ import os
import sys
import logging
import re
import json
from subprocess import run, PIPE
# Look for the 'utils' module from where the script is running
prev_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -231,3 +232,26 @@ def test_pcb_replace_1(test_dir):
assert m.group(1) == text
finally:
os.rename(file_back, file)
def test_set_text_variables_1(test_dir):
""" KiCad 6 variables """
prj = 'light_control'
ctx = context.TestContext(test_dir, 'test_set_text_variables_1', prj, 'set_text_variables_1', '')
ctx.run(extra=[])
file = os.path.join(ctx.get_board_dir(), ctx.board_name+context.PRO_EXT)
file_back = file + '-bak'
assert os.path.isfile(file_back), file_back
assert os.path.getsize(file_back) > 0
try:
logging.debug(file)
cmd = ['/bin/bash', '-c', "git log -1 --format='%h' " + ctx.board_file]
text = "Git_hash:'" + run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True).stdout.strip() + "'"
with open(file, 'rt') as f:
c = f.read()
data = json.loads(c)
assert 'text_variables' in data
assert 'Comment4' in data['text_variables']
assert data['text_variables']['Comment4'] == text
finally:
os.rename(file_back, file)

View File

@ -0,0 +1,10 @@
# Example KiBot config file
kibot:
version: 1
preflight:
set_text_variables:
- variable: "Comment4"
command: git log -1 --format="%h" $KIBOT_PCB_NAME
before: "Git_hash:'"
after: "'"