Added: text vars expansion for 3D model paths

Closes #172
This commit is contained in:
Salvador E. Tropea 2022-03-31 14:58:39 -03:00
parent 1a73dd5e29
commit dad1f91439
8 changed files with 280 additions and 13 deletions

View File

@ -70,7 +70,7 @@ test_docker_local_1:
# Run in the same directory to make the __pycache__ valid
# Also change the owner of the files to the current user (we run as root like in GitHub)
docker run --rm -v $(CWD):$(CWD) --workdir="$(CWD)" setsoft/kicad_auto_test:latest \
/bin/bash -c "flake8 . --count --statistics ; python3-coverage run -a src/kibot --help-outputs > /dev/null; pytest-3 --log-cli-level debug -k 'test_erc_warning_2' --test_dir output ; $(PY_COV) html; chown -R $(USER_ID):$(GROUP_ID) output/ tests/board_samples/ tests/.config/kiplot/plugins/__pycache__/ tests/test_plot/fake_pcbnew/__pycache__/ tests/.config/kibot/plugins/__pycache__/ .coverage htmlcov/"
/bin/bash -c "flake8 . --count --statistics ; python3-coverage run -a src/kibot --help-outputs > /dev/null; pytest-3 --log-cli-level debug -k 'test_step_2' --test_dir output ; $(PY_COV) html; chown -R $(USER_ID):$(GROUP_ID) output/ tests/board_samples/ tests/.config/kiplot/plugins/__pycache__/ tests/test_plot/fake_pcbnew/__pycache__/ tests/.config/kibot/plugins/__pycache__/ .coverage htmlcov/"
#$(PY_COV) report
#x-www-browser htmlcov/index.html
rm .coverage

View File

@ -55,11 +55,17 @@ def un_quote(val):
return val
def expand_env(val, env):
def expand_env(val, env, extra_env, used_extra=None):
""" Expand KiCad environment variables """
if used_extra is None:
used_extra = [False]
used_extra[0] = False
for var in re.findall(r'\$\{(\S+)\}', val):
if var in env:
val = val.replace('${'+var+'}', env[var])
elif var in extra_env:
val = val.replace('${'+var+'}', extra_env[var])
used_extra[0] = True
else:
logger.error('Unable to expand `{}` in `{}`'.format(var, val))
return val
@ -80,14 +86,14 @@ class LibAlias(object):
self.descr = None
@staticmethod
def parse(options, cline, env):
def parse(options, cline, env, extra_env):
m = LibAlias.libs_re.match(options)
if not m:
raise KiConfError('Malformed lib entry', SYM_LIB_TABLE, cline, options)
lib = LibAlias()
lib.name = un_quote(m.group(1))
lib.legacy = m.group(2) == 'Legacy'
lib.uri = os.path.abspath(expand_env(un_quote(m.group(3)), env))
lib.uri = os.path.abspath(expand_env(un_quote(m.group(3)), env, extra_env))
lib.options = un_quote(m.group(4))
lib.descr = un_quote(m.group(5))
return lib
@ -337,7 +343,7 @@ class KiConf(object):
while line and line[0] != ')':
m = re.match(r'\s*\(lib\s*(.*)\)', line)
if m:
alias = LibAlias.parse(m.group(1), cline, KiConf.kicad_env)
alias = LibAlias.parse(m.group(1), cline, KiConf.kicad_env, {})
if GS.debug_level > 1:
logger.debug('- Adding lib alias '+str(alias))
KiConf.lib_aliases[alias.name] = alias
@ -375,5 +381,7 @@ class KiConf(object):
# Load the project's table
KiConf.load_lib_aliases(os.path.join(KiConf.dirname, SYM_LIB_TABLE))
def expand_env(name):
return os.path.abspath(expand_env(un_quote(name), KiConf.kicad_env))
def expand_env(name, used_extra=None):
if used_extra is None:
used_extra = [False]
return os.path.abspath(expand_env(un_quote(name), KiConf.kicad_env, GS.load_pro_variables(), used_extra))

View File

@ -106,6 +106,7 @@ class Base3DOptions(VariantOptions):
# List of models we already downloaded
downloaded = set()
self.undo_3d_models = {}
extra_debug = GS.debug_level > 3
# Look for all the footprints
for m in GS.get_modules():
ref = m.GetReference()
@ -118,8 +119,13 @@ class Base3DOptions(VariantOptions):
for m3d in models_l:
if m3d.m_Filename.endswith(DISABLE_TEXT):
# Skip models we intentionally disabled using a bogus name
if extra_debug:
logger.debug("- Skipping {} (disabled)".format(m3d.m_Filename))
continue
full_name = KiConf.expand_env(m3d.m_Filename)
used_extra = [False]
full_name = KiConf.expand_env(m3d.m_Filename, used_extra)
if extra_debug:
logger.debug("- Expanded {} -> {}".format(m3d.m_Filename, full_name))
if not os.path.isfile(full_name):
# Missing 3D model
if full_name not in downloaded:
@ -148,6 +154,14 @@ class Base3DOptions(VariantOptions):
if replace:
m3d.m_Filename = replace
models_replaced = True
else: # File was found
if used_extra[0]:
# 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
if not models_replaced and extra_debug:
logger.debug('- Modifying models with text vars')
models_replaced = True
# Push the models back
for model in models_l:
models.push_front(model)
@ -182,11 +196,12 @@ class Base3DOptions(VariantOptions):
This mechanism uses the MTEXT attributes. """
# The magic text is %variant:slot1,slot2...%
field_regex = re.compile(r'\%([^:]+):(.*)\%')
if GS.debug_level > 3:
extra_debug = GS.debug_level > 3
if extra_debug:
logger.debug("{} 3D models that aren't for this variant".format('Enable' if enable else 'Disable'))
len_disable = len(DISABLE_TEXT)
for m in GS.get_modules():
if GS.debug_level > 3:
if extra_debug:
logger.debug("Processing module " + m.GetReference())
# Look for text objects
for gi in m.GraphicalItems():
@ -207,7 +222,7 @@ class Base3DOptions(VariantOptions):
while not models.empty():
m_objs.insert(0, models.pop())
for i, m3d in enumerate(m_objs):
if GS.debug_level > 3:
if extra_debug:
logger.debug('- {} {} {}'.format(i+1, i+1 in slots, m3d.m_Filename))
if i+1 not in slots:
if enable:

View File

@ -3,3 +3,4 @@ print_err.pro
test_v5/
zone-refill.pro
fp-info-cache
*-bak

View File

@ -105,7 +105,7 @@
(net 2 "Net-(C1-Pad1)") (tstamp a1823eb2-fb0d-4ed8-8b96-04184ac3a9d5))
(pad "2" smd roundrect locked (at 0.9375 0) (size 0.975 1.4) (layers "F.Cu" "F.Paste" "F.Mask") (roundrect_rratio 0.25)
(net 1 "GND") (tstamp 03c52831-5dc5-43c5-a442-8d23643b46fb))
(model "${KIPRJMOD}/../../data/R_0805_2012Metric.step"
(model "${MYVAR}/R_0805_2012Metric.step"
(offset (xyz 0 0 0))
(scale (xyz 1 1 1))
(rotate (xyz 0 0 0))

View File

@ -0,0 +1,220 @@
{
"board": {
"design_settings": {
"defaults": {
"board_outline_line_width": 0.049999999999999996,
"copper_line_width": 0.19999999999999998,
"copper_text_italic": false,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"copper_text_thickness": 0.3,
"copper_text_upright": false,
"courtyard_line_width": 0.049999999999999996,
"dimension_precision": 4,
"dimension_units": 3,
"dimensions": {
"arrow_length": 1270000,
"extension_offset": 500000,
"keep_text_aligned": true,
"suppress_zeroes": false,
"text_position": 0,
"units_format": 1
},
"fab_line_width": 0.09999999999999999,
"fab_text_italic": false,
"fab_text_size_h": 1.0,
"fab_text_size_v": 1.0,
"fab_text_thickness": 0.15,
"fab_text_upright": false,
"other_line_width": 0.09999999999999999,
"other_text_italic": false,
"other_text_size_h": 1.0,
"other_text_size_v": 1.0,
"other_text_thickness": 0.15,
"other_text_upright": false,
"pads": {
"drill": 0.762,
"height": 1.524,
"width": 1.524
},
"silk_line_width": 0.12,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.15,
"silk_text_upright": false,
"zones": {
"45_degree_only": true,
"min_clearance": 0.508
}
},
"diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [],
"meta": {
"filename": "board_design_settings.json",
"version": 2
},
"rule_severities": {
"annular_width": "error",
"clearance": "error",
"copper_edge_clearance": "error",
"courtyards_overlap": "error",
"diff_pair_gap_out_of_range": "error",
"diff_pair_uncoupled_length_too_long": "error",
"drill_out_of_range": "error",
"duplicate_footprints": "warning",
"extra_footprint": "warning",
"footprint_type_mismatch": "error",
"hole_clearance": "error",
"hole_near_hole": "error",
"invalid_outline": "error",
"item_on_disabled_layer": "error",
"items_not_allowed": "error",
"length_out_of_range": "error",
"malformed_courtyard": "error",
"microvia_drill_out_of_range": "error",
"missing_courtyard": "ignore",
"missing_footprint": "warning",
"net_conflict": "warning",
"npth_inside_courtyard": "ignore",
"padstack": "error",
"pth_inside_courtyard": "ignore",
"shorting_items": "error",
"silk_over_copper": "warning",
"silk_overlap": "warning",
"skew_out_of_range": "error",
"through_hole_pad_without_hole": "error",
"too_many_vias": "error",
"track_dangling": "warning",
"track_width": "error",
"tracks_crossing": "error",
"unconnected_items": "error",
"unresolved_variable": "error",
"via_dangling": "warning",
"zone_has_empty_net": "error",
"zones_intersect": "error"
},
"rules": {
"allow_blind_buried_vias": false,
"allow_microvias": false,
"max_error": 0.005,
"min_clearance": 0.0,
"min_copper_edge_clearance": 0.024999999999999998,
"min_hole_clearance": 0.25,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.19999999999999998,
"min_microvia_drill": 0.09999999999999999,
"min_silk_clearance": 0.0,
"min_through_hole_diameter": 0.2032,
"min_track_width": 0.127,
"min_via_annular_width": 0.049999999999999996,
"min_via_diameter": 0.4572,
"use_height_for_length_calcs": true
},
"track_widths": [
0.0,
0.1524,
0.3048,
0.635
],
"via_dimensions": [
{
"diameter": 0.0,
"drill": 0.0
},
{
"diameter": 0.508,
"drill": 0.254
},
{
"diameter": 0.889,
"drill": 0.508
}
],
"zones_allow_external_fillets": false,
"zones_use_no_outline": true
},
"layer_presets": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "light_control.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12.0,
"clearance": 0.1524,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.1524,
"via_diameter": 0.508,
"via_drill": 0.254,
"wire_width": 6.0
},
{
"bus_width": 12.0,
"clearance": 0.1524,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Power",
"nets": [],
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.635,
"via_diameter": 0.889,
"via_drill": 0.508,
"wire_width": 6.0
}
],
"meta": {
"version": 2
},
"net_colors": null
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"specctra_dsn": "",
"step": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"legacy_lib_dir": "",
"legacy_lib_list": []
},
"sheets": [],
"text_variables": {
"MYVAR": "tests/data",
"PRUEBITA": "Hola!"
}
}

View File

@ -39,11 +39,15 @@ def test_step_1(test_dir):
def test_step_2(test_dir):
prj = 'bom_fake_models'
yaml = 'step_simple_2'
if context.ki6():
yaml += '_k6'
ctx = context.TestContext(test_dir, 'STEP_2', prj, 'step_simple_2', STEP_DIR)
ctx.run()
# Check all outputs are there
ctx.expect_out_file(os.path.join(STEP_DIR, prj+'-3D.step'))
ctx.clean_up()
ctx.search_err(['Missing 3D model for C1', 'Could not add 3D model to C1'], invert=True)
ctx.clean_up(keep_project=True)
def test_step_3(test_dir):

View File

@ -0,0 +1,19 @@
kiplot:
version: 1
preflight:
set_text_variables:
- name: "MYVAR"
text: "tests/data"
outputs:
- name: Step
comment: "Generate 3D model (STEP)"
type: step
dir: 3D
options:
metric_units: false
origin: "3.2,-10" # "grid" or "drill" o "X,Y" i.e. 3.2,-10
no_virtual: true # exclude 3D models for components with 'virtual' attribute
min_distance: 0.0004 # Minimum distance between points to treat them as separate ones (default 0.01 mm)
#output: project.step