powertrain-build 1.13.1__py3-none-any.whl → 1.13.3.dev3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- powertrain_build/__init__.py +40 -40
- powertrain_build/__main__.py +6 -6
- powertrain_build/a2l.py +582 -582
- powertrain_build/a2l_merge.py +650 -650
- powertrain_build/a2l_templates.py +717 -717
- powertrain_build/build.py +985 -985
- powertrain_build/build_defs.py +309 -309
- powertrain_build/build_proj_config.py +690 -690
- powertrain_build/check_interface.py +575 -575
- powertrain_build/cli.py +141 -141
- powertrain_build/config.py +542 -542
- powertrain_build/core.py +395 -395
- powertrain_build/core_dummy.py +343 -343
- powertrain_build/create_conversion_table.py +73 -73
- powertrain_build/dids.py +916 -916
- powertrain_build/dummy.py +157 -157
- powertrain_build/dummy_spm.py +252 -252
- powertrain_build/environmentcheck.py +52 -52
- powertrain_build/ext_dbg.py +255 -255
- powertrain_build/ext_var.py +327 -327
- powertrain_build/feature_configs.py +301 -301
- powertrain_build/gen_allsysteminfo.py +227 -227
- powertrain_build/gen_label_split.py +449 -449
- powertrain_build/handcode_replacer.py +124 -124
- powertrain_build/html_report.py +133 -133
- powertrain_build/interface/__init__.py +4 -4
- powertrain_build/interface/application.py +511 -511
- powertrain_build/interface/base.py +500 -500
- powertrain_build/interface/csp_api.py +490 -490
- powertrain_build/interface/device_proxy.py +677 -677
- powertrain_build/interface/ems.py +67 -67
- powertrain_build/interface/export_global_vars.py +121 -121
- powertrain_build/interface/generate_adapters.py +132 -132
- powertrain_build/interface/generate_hi_interface.py +87 -87
- powertrain_build/interface/generate_service.py +69 -69
- powertrain_build/interface/generate_wrappers.py +147 -147
- powertrain_build/interface/generation_utils.py +142 -142
- powertrain_build/interface/hal.py +194 -194
- powertrain_build/interface/model_yaml_verification.py +348 -348
- powertrain_build/interface/service.py +296 -296
- powertrain_build/interface/simulink.py +249 -249
- powertrain_build/interface/update_call_sources.py +180 -180
- powertrain_build/interface/update_model_yaml.py +186 -186
- powertrain_build/interface/zone_controller.py +362 -362
- powertrain_build/lib/__init__.py +4 -4
- powertrain_build/lib/helper_functions.py +127 -127
- powertrain_build/lib/logger.py +55 -55
- powertrain_build/matlab_scripts/CodeGen/BuildAutomationPyBuild.m +78 -78
- powertrain_build/matlab_scripts/CodeGen/Generate_A2L.m +154 -154
- powertrain_build/matlab_scripts/CodeGen/generateTLUnit.m +239 -239
- powertrain_build/matlab_scripts/CodeGen/getAsilClassification.m +28 -28
- powertrain_build/matlab_scripts/CodeGen/modelConfiguredForTL.m +28 -28
- powertrain_build/matlab_scripts/CodeGen/moveDefOutports.m +88 -88
- powertrain_build/matlab_scripts/CodeGen/parseCalMeasData.m +410 -410
- powertrain_build/matlab_scripts/CodeGen/parseCoreIdentifiers.m +139 -139
- powertrain_build/matlab_scripts/CodeGen/parseDIDs.m +141 -141
- powertrain_build/matlab_scripts/CodeGen/parseInPorts.m +106 -106
- powertrain_build/matlab_scripts/CodeGen/parseIncludeConfigs.m +25 -25
- powertrain_build/matlab_scripts/CodeGen/parseModelInfo.m +38 -38
- powertrain_build/matlab_scripts/CodeGen/parseNVM.m +81 -81
- powertrain_build/matlab_scripts/CodeGen/parseOutPorts.m +120 -120
- powertrain_build/matlab_scripts/CodeGen/parsePreProcBlks.m +23 -23
- powertrain_build/matlab_scripts/CodeGen/struct2JSON.m +128 -128
- powertrain_build/matlab_scripts/CodeGen/updateCodeSwConfig.m +31 -31
- powertrain_build/matlab_scripts/Init_PyBuild.m +91 -91
- powertrain_build/matlab_scripts/__init__.py +2 -2
- powertrain_build/matlab_scripts/helperFunctions/Get_Full_Name.m +46 -46
- powertrain_build/matlab_scripts/helperFunctions/Get_SrcLines.m +12 -12
- powertrain_build/matlab_scripts/helperFunctions/Init_Models.m +78 -78
- powertrain_build/matlab_scripts/helperFunctions/Init_Projects.m +67 -67
- powertrain_build/matlab_scripts/helperFunctions/Read_Units.m +34 -34
- powertrain_build/matlab_scripts/helperFunctions/SetProjectTimeSamples.m +26 -26
- powertrain_build/matlab_scripts/helperFunctions/Strip_Suffix.m +16 -16
- powertrain_build/matlab_scripts/helperFunctions/followLink.m +118 -118
- powertrain_build/matlab_scripts/helperFunctions/getCodeSwitches.m +50 -50
- powertrain_build/matlab_scripts/helperFunctions/getConsumerBlocks.m +30 -30
- powertrain_build/matlab_scripts/helperFunctions/getDefBlock.m +39 -39
- powertrain_build/matlab_scripts/helperFunctions/getDefOutport.m +58 -58
- powertrain_build/matlab_scripts/helperFunctions/getDstBlocks.m +19 -19
- powertrain_build/matlab_scripts/helperFunctions/getDstLines.m +13 -13
- powertrain_build/matlab_scripts/helperFunctions/getInterfaceSignals.m +37 -37
- powertrain_build/matlab_scripts/helperFunctions/getName.m +37 -37
- powertrain_build/matlab_scripts/helperFunctions/getPath.m +6 -6
- powertrain_build/matlab_scripts/helperFunctions/getProperValue.m +21 -21
- powertrain_build/matlab_scripts/helperFunctions/getSrcBlocks.m +19 -19
- powertrain_build/matlab_scripts/helperFunctions/getSrcLines.m +13 -13
- powertrain_build/matlab_scripts/helperFunctions/loadLibraries.m +10 -10
- powertrain_build/matlab_scripts/helperFunctions/loadjson.m +6 -6
- powertrain_build/matlab_scripts/helperFunctions/modifyEnumStructField.m +21 -21
- powertrain_build/matlab_scripts/helperFunctions/removeConfigDuplicates.m +31 -31
- powertrain_build/matlab_scripts/helperFunctions/sortSystemByClass.m +26 -26
- powertrain_build/matlab_scripts/helperFunctions/tl_getfast.m +89 -89
- powertrain_build/matlab_scripts/helperFunctions/topLevelSystem.m +20 -20
- powertrain_build/matlab_scripts/helperFunctions/updateModels.m +131 -131
- powertrain_build/memory_section.py +224 -224
- powertrain_build/nvm_def.py +729 -729
- powertrain_build/problem_logger.py +86 -86
- powertrain_build/pt_matlab.py +430 -430
- powertrain_build/pt_win32.py +144 -144
- powertrain_build/replace_compu_tab_ref.py +105 -105
- powertrain_build/rte_dummy.py +254 -254
- powertrain_build/sched_funcs.py +209 -207
- powertrain_build/signal.py +7 -7
- powertrain_build/signal_if_html_rep.py +221 -221
- powertrain_build/signal_if_html_rep_all.py +302 -302
- powertrain_build/signal_incons_html_rep.py +180 -180
- powertrain_build/signal_incons_html_rep_all.py +366 -366
- powertrain_build/signal_incons_html_rep_base.py +168 -168
- powertrain_build/signal_inconsistency_check.py +641 -641
- powertrain_build/signal_interfaces.py +864 -864
- powertrain_build/templates/Index_SigCheck_All.html +22 -22
- powertrain_build/templates/Index_SigIf_All.html +19 -19
- powertrain_build/types.py +218 -218
- powertrain_build/unit_configs.py +419 -419
- powertrain_build/user_defined_types.py +660 -660
- powertrain_build/versioncheck.py +66 -66
- powertrain_build/wrapper.py +512 -512
- powertrain_build/xlrd_csv.py +87 -87
- powertrain_build/zone_controller/__init__.py +4 -4
- powertrain_build/zone_controller/calibration.py +176 -176
- powertrain_build/zone_controller/composition_yaml.py +880 -878
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/METADATA +100 -100
- powertrain_build-1.13.3.dev3.dist-info/RECORD +130 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/WHEEL +1 -1
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/LICENSE +202 -202
- powertrain_build-1.13.3.dev3.dist-info/pbr.json +1 -0
- powertrain_build-1.13.1.dist-info/RECORD +0 -130
- powertrain_build-1.13.1.dist-info/pbr.json +0 -1
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/entry_points.txt +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/AUTHORS +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/NOTICE +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/top_level.txt +0 -0
|
@@ -1,449 +1,449 @@
|
|
|
1
|
-
# Copyright 2024 Volvo Car Corporation
|
|
2
|
-
# Licensed under Apache 2.0.
|
|
3
|
-
|
|
4
|
-
"""Module for labelsplit files."""
|
|
5
|
-
import glob
|
|
6
|
-
import re
|
|
7
|
-
import json
|
|
8
|
-
import sys
|
|
9
|
-
from xml.etree import ElementTree
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
from powertrain_build.feature_configs import FeatureConfigs
|
|
12
|
-
from powertrain_build.unit_configs import UnitConfigs
|
|
13
|
-
from powertrain_build.signal_interfaces import CsvSignalInterfaces
|
|
14
|
-
from powertrain_build.lib import helper_functions, logger
|
|
15
|
-
|
|
16
|
-
LOGGER = logger.create_logger(__file__)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class LabelSplit:
|
|
20
|
-
""" Provides common LabelSplit functions for multiple repos.
|
|
21
|
-
"""
|
|
22
|
-
def __init__(self, project, build_cfg, cfg_json, cmt_source_folder):
|
|
23
|
-
"""Read project configuration file to internal an representation.
|
|
24
|
-
|
|
25
|
-
Args:
|
|
26
|
-
project (str): Project name.
|
|
27
|
-
build_cfg(BuildProjConfig): configures which units are active in the current project and where
|
|
28
|
-
the code switch files are located.
|
|
29
|
-
cfg_json(Path): Path to label split configuration file.
|
|
30
|
-
cmt_source_folder (Path): Path to CMT source folder.
|
|
31
|
-
"""
|
|
32
|
-
super().__init__()
|
|
33
|
-
self.project = project
|
|
34
|
-
self.build_cfg = build_cfg
|
|
35
|
-
project_a2l_file_path = Path(self.build_cfg.get_src_code_dst_dir(),
|
|
36
|
-
self.build_cfg.get_a2l_name())
|
|
37
|
-
self.feature_cfg = FeatureConfigs(self.build_cfg)
|
|
38
|
-
self.unit_cfg = UnitConfigs(self.build_cfg, self.feature_cfg)
|
|
39
|
-
self.csv_if = CsvSignalInterfaces(self.build_cfg, self.unit_cfg)
|
|
40
|
-
self.project_a2l_symbols = self.get_project_a2l_symbols(project_a2l_file_path)
|
|
41
|
-
self.labelsplit_cfg = self.read_json(cfg_json)
|
|
42
|
-
self.cmt_source_folder = cmt_source_folder
|
|
43
|
-
|
|
44
|
-
@staticmethod
|
|
45
|
-
def read_json(cfg_json):
|
|
46
|
-
"""Read label split configuration file from given location
|
|
47
|
-
If the file does not exsit in the given location, program
|
|
48
|
-
exits with error message.
|
|
49
|
-
|
|
50
|
-
Args:
|
|
51
|
-
cfg_json(Path): Path to label split configuration file
|
|
52
|
-
Returns:
|
|
53
|
-
labelsplit_cfg (dict): Dict of given file content
|
|
54
|
-
"""
|
|
55
|
-
labelsplit_cfg = None
|
|
56
|
-
if cfg_json.exists():
|
|
57
|
-
with cfg_json.open() as json_file:
|
|
58
|
-
labelsplit_cfg = json.load(json_file)
|
|
59
|
-
return labelsplit_cfg
|
|
60
|
-
LOGGER.error('Cannot find label split config file: %s', cfg_json)
|
|
61
|
-
sys.exit(1)
|
|
62
|
-
|
|
63
|
-
@staticmethod
|
|
64
|
-
def get_project_a2l_symbols(project_a2l_file_path):
|
|
65
|
-
"""Get a list of calibration symbols found in a given project A2L file.
|
|
66
|
-
|
|
67
|
-
Args:
|
|
68
|
-
project_a2l_file_path (Path): Path to project A2L file.
|
|
69
|
-
Returns:
|
|
70
|
-
symbols_in_a2l (list): List of calibration symbols found in the project A2L file.
|
|
71
|
-
"""
|
|
72
|
-
symbols_in_a2l = []
|
|
73
|
-
|
|
74
|
-
with project_a2l_file_path.open() as a2l_fh:
|
|
75
|
-
a2l_text = a2l_fh.read()
|
|
76
|
-
|
|
77
|
-
calibration_blocks = re.findall(r'(?:\s*\n)*(\s*/begin (CHARACTERISTIC|AXIS_PTS)[\n\s]*'
|
|
78
|
-
r'(\w+)([\[\d+\]]*).*?\n.*?/end \2)',
|
|
79
|
-
a2l_text,
|
|
80
|
-
flags=re.M | re.DOTALL)
|
|
81
|
-
|
|
82
|
-
for blk in calibration_blocks:
|
|
83
|
-
symbols_in_a2l.append(blk[2])
|
|
84
|
-
|
|
85
|
-
return symbols_in_a2l
|
|
86
|
-
|
|
87
|
-
@staticmethod
|
|
88
|
-
def get_sgp_symbols(sgp_file: Path):
|
|
89
|
-
"""Get symbols and symbol_groups found in a given _sgp.xml file.
|
|
90
|
-
|
|
91
|
-
Example output: {sVcExample: [(3, EC_EX_1), (4, EC_EX_2)]}, where the indices are column indices:
|
|
92
|
-
1 -> symbol name (therefore not in list of symbol groups).
|
|
93
|
-
2 -> diesel group.
|
|
94
|
-
3 -> petrol group.
|
|
95
|
-
4 -> hybrid group.
|
|
96
|
-
5 -> subsystem (therefore not in list of symbol groups).
|
|
97
|
-
|
|
98
|
-
Args:
|
|
99
|
-
sgp_file (Path): Path to an _sgp.xml file.
|
|
100
|
-
Returns:
|
|
101
|
-
found_sgp_symbols (dict): A symbol to symbol_groups dictionary found in the sgp_file.
|
|
102
|
-
"""
|
|
103
|
-
tree = ElementTree.parse(sgp_file)
|
|
104
|
-
root = tree.getroot()
|
|
105
|
-
search_string = '{{urn:schemas-microsoft-com:office:spreadsheet}}{tag}'
|
|
106
|
-
label_sheet = root.find(search_string.format(tag='Worksheet'))
|
|
107
|
-
table = label_sheet.find(search_string.format(tag='Table'))
|
|
108
|
-
rows = table.findall(search_string.format(tag='Row'))
|
|
109
|
-
|
|
110
|
-
found_sgp_symbols = {}
|
|
111
|
-
for row in rows:
|
|
112
|
-
symbol = None
|
|
113
|
-
column_counter = 1
|
|
114
|
-
cells = row.findall(search_string.format(tag='Cell'))
|
|
115
|
-
for cell in cells:
|
|
116
|
-
data = cell.find(search_string.format(tag='Data'))
|
|
117
|
-
if data is not None:
|
|
118
|
-
# Sometimes there are spaces in the symbol cell
|
|
119
|
-
# Sometimes there is a weird \ufeff character (VcDebug_sgp.xml) in the symbol cell
|
|
120
|
-
value = data.text.replace(' ', '').replace('\ufeff', '')
|
|
121
|
-
if symbol is None:
|
|
122
|
-
symbol = value
|
|
123
|
-
found_sgp_symbols[symbol] = []
|
|
124
|
-
else:
|
|
125
|
-
new_index = search_string.format(tag='Index')
|
|
126
|
-
if new_index in cell.attrib:
|
|
127
|
-
column_counter = int(cell.attrib[new_index])
|
|
128
|
-
found_sgp_symbols[symbol].append((column_counter, value))
|
|
129
|
-
column_counter += 1
|
|
130
|
-
|
|
131
|
-
return found_sgp_symbols
|
|
132
|
-
|
|
133
|
-
def get_sgp_symbol_group(self, symbol_groups_by_index):
|
|
134
|
-
"""Match _sgp.xml file indices (symbol groups) with a given project.
|
|
135
|
-
|
|
136
|
-
Args:
|
|
137
|
-
symbol_groups_by_index (list(tuple)): List of (index, symbol_group) pairs.
|
|
138
|
-
Returns:
|
|
139
|
-
symbol_group (str): The symbol group corresponding to the given project.
|
|
140
|
-
"""
|
|
141
|
-
symbol_group = ''
|
|
142
|
-
symbol_dict = self.labelsplit_cfg.get("SGP_SYMBOL_GROUPS")
|
|
143
|
-
symbol_list = [val for key, val in symbol_dict.items() if key in self.project]
|
|
144
|
-
if len(symbol_list) >= 1:
|
|
145
|
-
for index, group in symbol_groups_by_index:
|
|
146
|
-
if index == symbol_list[0]:
|
|
147
|
-
symbol_group = group
|
|
148
|
-
else:
|
|
149
|
-
LOGGER.error('Cannot match symbol group type for project: %s', self.project)
|
|
150
|
-
return symbol_group
|
|
151
|
-
|
|
152
|
-
def get_interface_symbols_and_groups(self, interface_dict, in_symbol_sgp_dict, out_symbol_sgp_dict):
|
|
153
|
-
"""Get a list of (symbol, symbol_group) pairs found in given interface and sgp files.
|
|
154
|
-
|
|
155
|
-
Args:
|
|
156
|
-
interface_dict (dict): interface to symbol map, matching a certain IO type.
|
|
157
|
-
in_symbol_sgp_dict (dict): An input symbol to symbol_groups dictionary found in an sgp_file,
|
|
158
|
-
to be compared with interface_dict inputs.
|
|
159
|
-
out_symbol_sgp_dict (dict): An output symbol to symbol_groups dictionary found in an sgp_file,
|
|
160
|
-
to be compared with interface_dict outputs.
|
|
161
|
-
Returns:
|
|
162
|
-
symbols_and_groups (list(tuple)): List of (symbol, symbol_group) pairs in: interface and sgp file.
|
|
163
|
-
"""
|
|
164
|
-
symbols_and_groups = []
|
|
165
|
-
for interface, symbol_data in interface_dict.items():
|
|
166
|
-
for symbol in symbol_data.keys():
|
|
167
|
-
debug_name = re.sub(r'\w(\w+)', r'c\1_db', symbol)
|
|
168
|
-
switch_name = re.sub(r'\w(\w+)', r'c\1_sw', symbol)
|
|
169
|
-
if 'Input' in interface and debug_name in in_symbol_sgp_dict and switch_name in in_symbol_sgp_dict:
|
|
170
|
-
debug_symbol_group = self.get_sgp_symbol_group(in_symbol_sgp_dict[debug_name])
|
|
171
|
-
switch_symbol_group = self.get_sgp_symbol_group(in_symbol_sgp_dict[switch_name])
|
|
172
|
-
symbols_and_groups.extend([(debug_name, debug_symbol_group), (switch_name, switch_symbol_group)])
|
|
173
|
-
elif 'Output' in interface and debug_name in out_symbol_sgp_dict and switch_name in out_symbol_sgp_dict:
|
|
174
|
-
debug_symbol_group = self.get_sgp_symbol_group(out_symbol_sgp_dict[debug_name])
|
|
175
|
-
switch_symbol_group = self.get_sgp_symbol_group(out_symbol_sgp_dict[switch_name])
|
|
176
|
-
symbols_and_groups.extend([(debug_name, debug_symbol_group), (switch_name, switch_symbol_group)])
|
|
177
|
-
return symbols_and_groups
|
|
178
|
-
|
|
179
|
-
def get_debug_symbols_and_groups(self):
|
|
180
|
-
"""Get a list of (symbol, symbol_group) pairs found in project interface and VcDebug*_sgp.xml files.
|
|
181
|
-
|
|
182
|
-
Returns:
|
|
183
|
-
debug_symbols_and_groups (list(tuple)): List of (symbol, symbol_group) pairs in:
|
|
184
|
-
interface and VcDebug*_sgp.xmlfiles.
|
|
185
|
-
"""
|
|
186
|
-
_unused, dep, _unused_two, debug = self.csv_if.get_io_config()
|
|
187
|
-
sgp_file_dict = self.labelsplit_cfg.get("SGP_FILE")
|
|
188
|
-
debug_sgp_file = Path(sgp_file_dict.get('cfg_folder'), sgp_file_dict.get('debug'))
|
|
189
|
-
debug_output_sgp_file = Path(sgp_file_dict.get('cfg_folder'), sgp_file_dict.get('debug_output'))
|
|
190
|
-
dep_sgp_file = Path(sgp_file_dict.get('cfg_folder'), sgp_file_dict.get('dep'))
|
|
191
|
-
dep_output_sgp_file = Path(sgp_file_dict.get('cfg_folder'), sgp_file_dict.get('dep_output'))
|
|
192
|
-
debug_sgp_symbols = self.get_sgp_symbols(debug_sgp_file)
|
|
193
|
-
debug_output_sgp_symbols = self.get_sgp_symbols(debug_output_sgp_file)
|
|
194
|
-
dep_sgp_symbols = self.get_sgp_symbols(dep_sgp_file)
|
|
195
|
-
dep_output_sgp_symbols = self.get_sgp_symbols(dep_output_sgp_file)
|
|
196
|
-
|
|
197
|
-
symbols_and_groups_tmp = []
|
|
198
|
-
debug_tmp = self.get_interface_symbols_and_groups(debug,
|
|
199
|
-
debug_sgp_symbols,
|
|
200
|
-
debug_output_sgp_symbols)
|
|
201
|
-
dep_tmp = self.get_interface_symbols_and_groups(dep,
|
|
202
|
-
dep_sgp_symbols,
|
|
203
|
-
dep_output_sgp_symbols)
|
|
204
|
-
symbols_and_groups_tmp.extend(debug_tmp)
|
|
205
|
-
symbols_and_groups_tmp.extend(dep_tmp)
|
|
206
|
-
|
|
207
|
-
debug_symbols_and_groups = []
|
|
208
|
-
for symbol, symbol_group in symbols_and_groups_tmp:
|
|
209
|
-
if symbol_group == '':
|
|
210
|
-
LOGGER.info('Debug symbol %s is missing symbol group and will be removed.', symbol)
|
|
211
|
-
else:
|
|
212
|
-
debug_symbols_and_groups.append((symbol, symbol_group))
|
|
213
|
-
|
|
214
|
-
return debug_symbols_and_groups
|
|
215
|
-
|
|
216
|
-
def check_unit_par_file(self, unit):
|
|
217
|
-
"""Check <unit>_par.m file for default sgp symbol group.
|
|
218
|
-
|
|
219
|
-
Args:
|
|
220
|
-
unit (str): Current unit/model name.
|
|
221
|
-
Returns:
|
|
222
|
-
has_sgp_default (Bool): True/False if unit is associated with default sgp value.
|
|
223
|
-
default_symbol_group (str): Name of default symbol group.
|
|
224
|
-
"""
|
|
225
|
-
has_sgp_default = False
|
|
226
|
-
default_symbol_group = ''
|
|
227
|
-
base_search_string = r'SgpDefault\.{unit}\.[A-Za-z]+\s*=\s*[\'\"]([A-Za-z_]+)[\'\"]'
|
|
228
|
-
search_string = base_search_string.format(unit=unit)
|
|
229
|
-
|
|
230
|
-
non_existent_par_file = Path('non_existent_par_file.m')
|
|
231
|
-
found_par_files = glob.glob('Models/*/' + unit + '/' + unit + '_par.m')
|
|
232
|
-
if len(found_par_files) > 1:
|
|
233
|
-
LOGGER.warning('Found more than one _par.m file, using %s', found_par_files[0])
|
|
234
|
-
par_file = Path(found_par_files[0]) if found_par_files else non_existent_par_file
|
|
235
|
-
|
|
236
|
-
if self.labelsplit_cfg.get("special_unit_prefixes"):
|
|
237
|
-
for special_prefix in self.labelsplit_cfg.get("special_unit_prefixes"):
|
|
238
|
-
if unit.startswith(special_prefix) and not par_file.is_file():
|
|
239
|
-
# Some units require special handling.
|
|
240
|
-
if '__' in unit:
|
|
241
|
-
parent = unit.replace('__', 'Mdl__')
|
|
242
|
-
else:
|
|
243
|
-
parent = unit + 'Mdl'
|
|
244
|
-
found_par_files = glob.glob('Models/*/' + parent + '/' + parent + '_par.m')
|
|
245
|
-
par_file = Path(found_par_files[0]) if found_par_files else Path(non_existent_par_file)
|
|
246
|
-
# Default symbol group is based on c-file name
|
|
247
|
-
c_name = re.sub('(Mdl)?(__.*)?', '', unit)
|
|
248
|
-
search_string = base_search_string.format(unit=c_name)
|
|
249
|
-
|
|
250
|
-
if par_file.is_file():
|
|
251
|
-
with par_file.open(encoding="latin-1") as par_fh:
|
|
252
|
-
par_text = par_fh.read()
|
|
253
|
-
sgp_default_match = re.search(search_string, par_text)
|
|
254
|
-
if sgp_default_match is not None:
|
|
255
|
-
has_sgp_default = True
|
|
256
|
-
default_symbol_group = sgp_default_match.group(1)
|
|
257
|
-
else:
|
|
258
|
-
LOGGER.info('Missing _par file for model: %s', unit)
|
|
259
|
-
|
|
260
|
-
return has_sgp_default, default_symbol_group
|
|
261
|
-
|
|
262
|
-
def get_unit_sgp_file(self, unit):
|
|
263
|
-
"""Get path to <unit>_sgp.xml file.
|
|
264
|
-
|
|
265
|
-
Args:
|
|
266
|
-
unit (str): Current unit/model name.
|
|
267
|
-
Returns:
|
|
268
|
-
sgp_file (Path): Path to <unit>_sgp.xml file.
|
|
269
|
-
"""
|
|
270
|
-
non_existent_sgp_file = Path('non_existent_sgp_file.xml')
|
|
271
|
-
found_sgp_files = glob.glob('Models/*/' + unit + '/' + unit + '_sgp.xml')
|
|
272
|
-
if len(found_sgp_files) > 1:
|
|
273
|
-
LOGGER.warning('Found more than one _sgp.xml file, using %s', found_sgp_files[0])
|
|
274
|
-
sgp_file = Path(found_sgp_files[0]) if found_sgp_files else Path(non_existent_sgp_file)
|
|
275
|
-
|
|
276
|
-
if self.labelsplit_cfg.get("special_unit_prefixes"):
|
|
277
|
-
for special_prefix in self.labelsplit_cfg.get("special_unit_prefixes"):
|
|
278
|
-
if unit.startswith(special_prefix) and not sgp_file.is_file():
|
|
279
|
-
# Some units require special handling.
|
|
280
|
-
if '__' in unit:
|
|
281
|
-
parent = unit.replace('__', 'Mdl__')
|
|
282
|
-
else:
|
|
283
|
-
parent = unit + 'Mdl'
|
|
284
|
-
found_sgp_files = glob.glob('Models/*/' + parent + '/' + parent + '_sgp.xml')
|
|
285
|
-
sgp_file = Path(found_sgp_files[0]) if found_sgp_files else Path(non_existent_sgp_file)
|
|
286
|
-
|
|
287
|
-
return sgp_file
|
|
288
|
-
|
|
289
|
-
def get_unit_symbols_and_groups(self, unit, calibration_symbols):
|
|
290
|
-
"""Get a list of (symbol, symbol_group) pairs found in A2L, <unit>_sgp/par and config_<unit>.json files.
|
|
291
|
-
|
|
292
|
-
Args:
|
|
293
|
-
unit (str): Current unit/model name.
|
|
294
|
-
calibration_symbols (list): All calibration symbols for the unit (from config_<unit>.json).
|
|
295
|
-
Returns:
|
|
296
|
-
unit_symbols_and_groups (list(tuple)): List of (symbol, symbol_group) pairs in: A2L, _sgp/_par and
|
|
297
|
-
config files.
|
|
298
|
-
"""
|
|
299
|
-
unit_symbols_and_groups = []
|
|
300
|
-
has_sgp_default, default_symbol_group = self.check_unit_par_file(unit)
|
|
301
|
-
sgp_file = self.get_unit_sgp_file(unit)
|
|
302
|
-
|
|
303
|
-
if sgp_file.is_file():
|
|
304
|
-
found_sgp_symbols = self.get_sgp_symbols(sgp_file)
|
|
305
|
-
else:
|
|
306
|
-
found_sgp_symbols = {}
|
|
307
|
-
LOGGER.info('Missing _sgp file for model: %s', unit)
|
|
308
|
-
|
|
309
|
-
for symbol in calibration_symbols:
|
|
310
|
-
if symbol not in self.project_a2l_symbols:
|
|
311
|
-
LOGGER.info('Symbol %s not in project A2L file and will be removed.', symbol)
|
|
312
|
-
continue
|
|
313
|
-
|
|
314
|
-
if symbol in found_sgp_symbols:
|
|
315
|
-
symbol_group = self.get_sgp_symbol_group(found_sgp_symbols[symbol])
|
|
316
|
-
unit_symbols_and_groups.append((symbol, symbol_group))
|
|
317
|
-
elif has_sgp_default:
|
|
318
|
-
if symbol.endswith('_sw') or symbol.endswith('_db'):
|
|
319
|
-
LOGGER.info('Debug symbol %s not in sgp file and will be removed.', symbol)
|
|
320
|
-
else:
|
|
321
|
-
unit_symbols_and_groups.append((symbol, default_symbol_group))
|
|
322
|
-
else:
|
|
323
|
-
LOGGER.info('Symbol %s missing in _sgp file and lack SgpDefault value.', symbol)
|
|
324
|
-
|
|
325
|
-
return unit_symbols_and_groups
|
|
326
|
-
|
|
327
|
-
def get_calibration_constants(self):
|
|
328
|
-
"""Get all calibration symbols for each unit in the project.
|
|
329
|
-
|
|
330
|
-
Returns:
|
|
331
|
-
calibration_symbols_per_unit (dict): A unit to symbol list dictionary.
|
|
332
|
-
"""
|
|
333
|
-
security_variables = self.labelsplit_cfg.get("security_variables")
|
|
334
|
-
u_conf_dict = self.unit_cfg.get_per_cfg_unit_cfg()
|
|
335
|
-
|
|
336
|
-
safe_calibration_symbols = {}
|
|
337
|
-
for symbol, symbol_data in u_conf_dict['calib_consts'].items():
|
|
338
|
-
if symbol not in security_variables:
|
|
339
|
-
safe_calibration_symbols.update({symbol: symbol_data})
|
|
340
|
-
|
|
341
|
-
calibration_symbols_per_unit = {}
|
|
342
|
-
for symbol, symbol_data in safe_calibration_symbols.items():
|
|
343
|
-
for unit, unit_data in symbol_data.items():
|
|
344
|
-
if 'CVC_CAL' in unit_data['class']:
|
|
345
|
-
if unit in calibration_symbols_per_unit:
|
|
346
|
-
calibration_symbols_per_unit[unit].append(symbol)
|
|
347
|
-
else:
|
|
348
|
-
calibration_symbols_per_unit[unit] = [symbol]
|
|
349
|
-
|
|
350
|
-
return calibration_symbols_per_unit
|
|
351
|
-
|
|
352
|
-
def get_symbols_and_groups(self):
|
|
353
|
-
"""Get a list of (symbol, symbol_group) pairs found in A2L, <unit>_sgp/par and config_<unit>.json files.
|
|
354
|
-
|
|
355
|
-
Returns:
|
|
356
|
-
exit_code (int): 0/1 based on successful collection of symbols and symbol groups.
|
|
357
|
-
all_symbols_and_groups (dict): A symbol to symbol_group dictionary for all A2L, _sgp/_par and config files,
|
|
358
|
-
"""
|
|
359
|
-
exit_code = 0
|
|
360
|
-
calibration_symbols_per_unit = self.get_calibration_constants()
|
|
361
|
-
debug_symbols = self.get_debug_symbols_and_groups()
|
|
362
|
-
|
|
363
|
-
all_symbol_and_group_pairs = []
|
|
364
|
-
if self.labelsplit_cfg.get("project_symbols"):
|
|
365
|
-
special_project_dict = self.labelsplit_cfg.get("project_symbols")
|
|
366
|
-
pair_list = [val for key, val in special_project_dict.items() if key in self.project]
|
|
367
|
-
if len(pair_list) == 1:
|
|
368
|
-
symbol_and_group_pairs_list = pair_list[0].items()
|
|
369
|
-
all_symbol_and_group_pairs += symbol_and_group_pairs_list
|
|
370
|
-
elif len(pair_list) > 1:
|
|
371
|
-
LOGGER.error('Project %s has does not follow the name rule', self.project)
|
|
372
|
-
return 1, {}
|
|
373
|
-
|
|
374
|
-
all_symbol_and_group_pairs.extend(debug_symbols)
|
|
375
|
-
|
|
376
|
-
for unit, symbols in calibration_symbols_per_unit.items():
|
|
377
|
-
if self.labelsplit_cfg.get("special_units"):
|
|
378
|
-
special_unit_dict = self.labelsplit_cfg.get("special_units")
|
|
379
|
-
if unit in special_unit_dict.keys():
|
|
380
|
-
# Some units require special handling.
|
|
381
|
-
LOGGER.warning('Found %s, assuming %s is used.', unit, special_unit_dict.get(unit))
|
|
382
|
-
labels = self.get_unit_symbols_and_groups(special_unit_dict.get(unit), symbols)
|
|
383
|
-
else:
|
|
384
|
-
labels = self.get_unit_symbols_and_groups(unit, symbols)
|
|
385
|
-
else:
|
|
386
|
-
labels = self.get_unit_symbols_and_groups(unit, symbols)
|
|
387
|
-
all_symbol_and_group_pairs.extend(labels)
|
|
388
|
-
|
|
389
|
-
symbol_to_group_dict = {}
|
|
390
|
-
for symbol, symbol_group in all_symbol_and_group_pairs:
|
|
391
|
-
if symbol in symbol_to_group_dict:
|
|
392
|
-
if symbol_to_group_dict[symbol] != symbol_group:
|
|
393
|
-
LOGGER.error('Symbol %s multiply defined with different symbol groups.', symbol)
|
|
394
|
-
exit_code = 1
|
|
395
|
-
else:
|
|
396
|
-
symbol_to_group_dict[symbol] = symbol_group
|
|
397
|
-
|
|
398
|
-
return exit_code, symbol_to_group_dict
|
|
399
|
-
|
|
400
|
-
def generate_label_split_xml_file(self, symbols_and_groups):
|
|
401
|
-
"""Generate a label split file, given a directory plus labels and groups to add.
|
|
402
|
-
|
|
403
|
-
Args:
|
|
404
|
-
symbols_and_groups (dict): A symbol to symbol_group dictionary given a project.
|
|
405
|
-
Returns:
|
|
406
|
-
exit_code (int): 0/1 based on successful generation of Labelsplit.xls.
|
|
407
|
-
"""
|
|
408
|
-
errors = []
|
|
409
|
-
project_root_dir = self.build_cfg.get_root_dir()
|
|
410
|
-
cmt_output_folder = helper_functions.create_dir(Path(project_root_dir, 'output', 'CMT'))
|
|
411
|
-
start_file_name = Path(self.cmt_source_folder, 'template_labelsplit_sgp_start.xml_')
|
|
412
|
-
row_count_file_name = Path(cmt_output_folder, 'labelsplit_rowcount.xml_')
|
|
413
|
-
start_2_file_name = Path(self.cmt_source_folder, 'template_labelsplit_sgp_start_2.xml_')
|
|
414
|
-
label_split_rows_filename = Path(cmt_output_folder, 'labelsplit_rows.xml_')
|
|
415
|
-
end_file_name = Path(self.cmt_source_folder, 'template_labelsplit_sgp_end.xml_')
|
|
416
|
-
files_to_merge = [start_file_name, row_count_file_name, start_2_file_name,
|
|
417
|
-
label_split_rows_filename, end_file_name]
|
|
418
|
-
|
|
419
|
-
with row_count_file_name.open('w', encoding="utf-8") as rc_fh:
|
|
420
|
-
rc_fh.write(f'{len(symbols_and_groups) + 1}') # header + data
|
|
421
|
-
|
|
422
|
-
with label_split_rows_filename.open('w', encoding="utf-8") as lsrf_fh:
|
|
423
|
-
for symbol, symbol_group in symbols_and_groups.items():
|
|
424
|
-
if symbol_group == '':
|
|
425
|
-
errors.append(f'Missing symbol group for symbol: {symbol}')
|
|
426
|
-
elif symbol_group == 'VCC_SPM_DEBUG':
|
|
427
|
-
LOGGER.info('Ignoring undistributed debug symbol: %s', symbol)
|
|
428
|
-
else:
|
|
429
|
-
lsrf_fh.write(
|
|
430
|
-
' <Row ss:AutoFitHeight="0">\n'
|
|
431
|
-
f' <Cell><Data ss:Type="String">{symbol}'
|
|
432
|
-
'</Data><NamedCell ss:Name="_FilterDatabase"/></Cell>\n'
|
|
433
|
-
f' <Cell ss:Index="5"><Data ss:Type="String">{symbol_group}'
|
|
434
|
-
'</Data><NamedCell ss:Name="_FilterDatabase"/></Cell>\n'
|
|
435
|
-
' </Row>\n'
|
|
436
|
-
)
|
|
437
|
-
|
|
438
|
-
if errors:
|
|
439
|
-
LOGGER.error('\n'.join(errors))
|
|
440
|
-
return 1
|
|
441
|
-
|
|
442
|
-
output_file_name = Path(project_root_dir, 'output', 'CMT', 'Labelsplit.xls')
|
|
443
|
-
with output_file_name.open('w', encoding="utf-8") as output_fh:
|
|
444
|
-
for file_name in files_to_merge:
|
|
445
|
-
with file_name.open(encoding="utf-8") as input_fh:
|
|
446
|
-
content = input_fh.read()
|
|
447
|
-
output_fh.write(content)
|
|
448
|
-
LOGGER.info('Delivery to: %s', str(output_file_name))
|
|
449
|
-
return 0
|
|
1
|
+
# Copyright 2024 Volvo Car Corporation
|
|
2
|
+
# Licensed under Apache 2.0.
|
|
3
|
+
|
|
4
|
+
"""Module for labelsplit files."""
|
|
5
|
+
import glob
|
|
6
|
+
import re
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
from xml.etree import ElementTree
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from powertrain_build.feature_configs import FeatureConfigs
|
|
12
|
+
from powertrain_build.unit_configs import UnitConfigs
|
|
13
|
+
from powertrain_build.signal_interfaces import CsvSignalInterfaces
|
|
14
|
+
from powertrain_build.lib import helper_functions, logger
|
|
15
|
+
|
|
16
|
+
LOGGER = logger.create_logger(__file__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LabelSplit:
|
|
20
|
+
""" Provides common LabelSplit functions for multiple repos.
|
|
21
|
+
"""
|
|
22
|
+
def __init__(self, project, build_cfg, cfg_json, cmt_source_folder):
|
|
23
|
+
"""Read project configuration file to internal an representation.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
project (str): Project name.
|
|
27
|
+
build_cfg(BuildProjConfig): configures which units are active in the current project and where
|
|
28
|
+
the code switch files are located.
|
|
29
|
+
cfg_json(Path): Path to label split configuration file.
|
|
30
|
+
cmt_source_folder (Path): Path to CMT source folder.
|
|
31
|
+
"""
|
|
32
|
+
super().__init__()
|
|
33
|
+
self.project = project
|
|
34
|
+
self.build_cfg = build_cfg
|
|
35
|
+
project_a2l_file_path = Path(self.build_cfg.get_src_code_dst_dir(),
|
|
36
|
+
self.build_cfg.get_a2l_name())
|
|
37
|
+
self.feature_cfg = FeatureConfigs(self.build_cfg)
|
|
38
|
+
self.unit_cfg = UnitConfigs(self.build_cfg, self.feature_cfg)
|
|
39
|
+
self.csv_if = CsvSignalInterfaces(self.build_cfg, self.unit_cfg)
|
|
40
|
+
self.project_a2l_symbols = self.get_project_a2l_symbols(project_a2l_file_path)
|
|
41
|
+
self.labelsplit_cfg = self.read_json(cfg_json)
|
|
42
|
+
self.cmt_source_folder = cmt_source_folder
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def read_json(cfg_json):
|
|
46
|
+
"""Read label split configuration file from given location
|
|
47
|
+
If the file does not exsit in the given location, program
|
|
48
|
+
exits with error message.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
cfg_json(Path): Path to label split configuration file
|
|
52
|
+
Returns:
|
|
53
|
+
labelsplit_cfg (dict): Dict of given file content
|
|
54
|
+
"""
|
|
55
|
+
labelsplit_cfg = None
|
|
56
|
+
if cfg_json.exists():
|
|
57
|
+
with cfg_json.open() as json_file:
|
|
58
|
+
labelsplit_cfg = json.load(json_file)
|
|
59
|
+
return labelsplit_cfg
|
|
60
|
+
LOGGER.error('Cannot find label split config file: %s', cfg_json)
|
|
61
|
+
sys.exit(1)
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def get_project_a2l_symbols(project_a2l_file_path):
|
|
65
|
+
"""Get a list of calibration symbols found in a given project A2L file.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
project_a2l_file_path (Path): Path to project A2L file.
|
|
69
|
+
Returns:
|
|
70
|
+
symbols_in_a2l (list): List of calibration symbols found in the project A2L file.
|
|
71
|
+
"""
|
|
72
|
+
symbols_in_a2l = []
|
|
73
|
+
|
|
74
|
+
with project_a2l_file_path.open() as a2l_fh:
|
|
75
|
+
a2l_text = a2l_fh.read()
|
|
76
|
+
|
|
77
|
+
calibration_blocks = re.findall(r'(?:\s*\n)*(\s*/begin (CHARACTERISTIC|AXIS_PTS)[\n\s]*'
|
|
78
|
+
r'(\w+)([\[\d+\]]*).*?\n.*?/end \2)',
|
|
79
|
+
a2l_text,
|
|
80
|
+
flags=re.M | re.DOTALL)
|
|
81
|
+
|
|
82
|
+
for blk in calibration_blocks:
|
|
83
|
+
symbols_in_a2l.append(blk[2])
|
|
84
|
+
|
|
85
|
+
return symbols_in_a2l
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def get_sgp_symbols(sgp_file: Path):
|
|
89
|
+
"""Get symbols and symbol_groups found in a given _sgp.xml file.
|
|
90
|
+
|
|
91
|
+
Example output: {sVcExample: [(3, EC_EX_1), (4, EC_EX_2)]}, where the indices are column indices:
|
|
92
|
+
1 -> symbol name (therefore not in list of symbol groups).
|
|
93
|
+
2 -> diesel group.
|
|
94
|
+
3 -> petrol group.
|
|
95
|
+
4 -> hybrid group.
|
|
96
|
+
5 -> subsystem (therefore not in list of symbol groups).
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
sgp_file (Path): Path to an _sgp.xml file.
|
|
100
|
+
Returns:
|
|
101
|
+
found_sgp_symbols (dict): A symbol to symbol_groups dictionary found in the sgp_file.
|
|
102
|
+
"""
|
|
103
|
+
tree = ElementTree.parse(sgp_file)
|
|
104
|
+
root = tree.getroot()
|
|
105
|
+
search_string = '{{urn:schemas-microsoft-com:office:spreadsheet}}{tag}'
|
|
106
|
+
label_sheet = root.find(search_string.format(tag='Worksheet'))
|
|
107
|
+
table = label_sheet.find(search_string.format(tag='Table'))
|
|
108
|
+
rows = table.findall(search_string.format(tag='Row'))
|
|
109
|
+
|
|
110
|
+
found_sgp_symbols = {}
|
|
111
|
+
for row in rows:
|
|
112
|
+
symbol = None
|
|
113
|
+
column_counter = 1
|
|
114
|
+
cells = row.findall(search_string.format(tag='Cell'))
|
|
115
|
+
for cell in cells:
|
|
116
|
+
data = cell.find(search_string.format(tag='Data'))
|
|
117
|
+
if data is not None:
|
|
118
|
+
# Sometimes there are spaces in the symbol cell
|
|
119
|
+
# Sometimes there is a weird \ufeff character (VcDebug_sgp.xml) in the symbol cell
|
|
120
|
+
value = data.text.replace(' ', '').replace('\ufeff', '')
|
|
121
|
+
if symbol is None:
|
|
122
|
+
symbol = value
|
|
123
|
+
found_sgp_symbols[symbol] = []
|
|
124
|
+
else:
|
|
125
|
+
new_index = search_string.format(tag='Index')
|
|
126
|
+
if new_index in cell.attrib:
|
|
127
|
+
column_counter = int(cell.attrib[new_index])
|
|
128
|
+
found_sgp_symbols[symbol].append((column_counter, value))
|
|
129
|
+
column_counter += 1
|
|
130
|
+
|
|
131
|
+
return found_sgp_symbols
|
|
132
|
+
|
|
133
|
+
def get_sgp_symbol_group(self, symbol_groups_by_index):
|
|
134
|
+
"""Match _sgp.xml file indices (symbol groups) with a given project.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
symbol_groups_by_index (list(tuple)): List of (index, symbol_group) pairs.
|
|
138
|
+
Returns:
|
|
139
|
+
symbol_group (str): The symbol group corresponding to the given project.
|
|
140
|
+
"""
|
|
141
|
+
symbol_group = ''
|
|
142
|
+
symbol_dict = self.labelsplit_cfg.get("SGP_SYMBOL_GROUPS")
|
|
143
|
+
symbol_list = [val for key, val in symbol_dict.items() if key in self.project]
|
|
144
|
+
if len(symbol_list) >= 1:
|
|
145
|
+
for index, group in symbol_groups_by_index:
|
|
146
|
+
if index == symbol_list[0]:
|
|
147
|
+
symbol_group = group
|
|
148
|
+
else:
|
|
149
|
+
LOGGER.error('Cannot match symbol group type for project: %s', self.project)
|
|
150
|
+
return symbol_group
|
|
151
|
+
|
|
152
|
+
def get_interface_symbols_and_groups(self, interface_dict, in_symbol_sgp_dict, out_symbol_sgp_dict):
|
|
153
|
+
"""Get a list of (symbol, symbol_group) pairs found in given interface and sgp files.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
interface_dict (dict): interface to symbol map, matching a certain IO type.
|
|
157
|
+
in_symbol_sgp_dict (dict): An input symbol to symbol_groups dictionary found in an sgp_file,
|
|
158
|
+
to be compared with interface_dict inputs.
|
|
159
|
+
out_symbol_sgp_dict (dict): An output symbol to symbol_groups dictionary found in an sgp_file,
|
|
160
|
+
to be compared with interface_dict outputs.
|
|
161
|
+
Returns:
|
|
162
|
+
symbols_and_groups (list(tuple)): List of (symbol, symbol_group) pairs in: interface and sgp file.
|
|
163
|
+
"""
|
|
164
|
+
symbols_and_groups = []
|
|
165
|
+
for interface, symbol_data in interface_dict.items():
|
|
166
|
+
for symbol in symbol_data.keys():
|
|
167
|
+
debug_name = re.sub(r'\w(\w+)', r'c\1_db', symbol)
|
|
168
|
+
switch_name = re.sub(r'\w(\w+)', r'c\1_sw', symbol)
|
|
169
|
+
if 'Input' in interface and debug_name in in_symbol_sgp_dict and switch_name in in_symbol_sgp_dict:
|
|
170
|
+
debug_symbol_group = self.get_sgp_symbol_group(in_symbol_sgp_dict[debug_name])
|
|
171
|
+
switch_symbol_group = self.get_sgp_symbol_group(in_symbol_sgp_dict[switch_name])
|
|
172
|
+
symbols_and_groups.extend([(debug_name, debug_symbol_group), (switch_name, switch_symbol_group)])
|
|
173
|
+
elif 'Output' in interface and debug_name in out_symbol_sgp_dict and switch_name in out_symbol_sgp_dict:
|
|
174
|
+
debug_symbol_group = self.get_sgp_symbol_group(out_symbol_sgp_dict[debug_name])
|
|
175
|
+
switch_symbol_group = self.get_sgp_symbol_group(out_symbol_sgp_dict[switch_name])
|
|
176
|
+
symbols_and_groups.extend([(debug_name, debug_symbol_group), (switch_name, switch_symbol_group)])
|
|
177
|
+
return symbols_and_groups
|
|
178
|
+
|
|
179
|
+
def get_debug_symbols_and_groups(self):
|
|
180
|
+
"""Get a list of (symbol, symbol_group) pairs found in project interface and VcDebug*_sgp.xml files.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
debug_symbols_and_groups (list(tuple)): List of (symbol, symbol_group) pairs in:
|
|
184
|
+
interface and VcDebug*_sgp.xmlfiles.
|
|
185
|
+
"""
|
|
186
|
+
_unused, dep, _unused_two, debug = self.csv_if.get_io_config()
|
|
187
|
+
sgp_file_dict = self.labelsplit_cfg.get("SGP_FILE")
|
|
188
|
+
debug_sgp_file = Path(sgp_file_dict.get('cfg_folder'), sgp_file_dict.get('debug'))
|
|
189
|
+
debug_output_sgp_file = Path(sgp_file_dict.get('cfg_folder'), sgp_file_dict.get('debug_output'))
|
|
190
|
+
dep_sgp_file = Path(sgp_file_dict.get('cfg_folder'), sgp_file_dict.get('dep'))
|
|
191
|
+
dep_output_sgp_file = Path(sgp_file_dict.get('cfg_folder'), sgp_file_dict.get('dep_output'))
|
|
192
|
+
debug_sgp_symbols = self.get_sgp_symbols(debug_sgp_file)
|
|
193
|
+
debug_output_sgp_symbols = self.get_sgp_symbols(debug_output_sgp_file)
|
|
194
|
+
dep_sgp_symbols = self.get_sgp_symbols(dep_sgp_file)
|
|
195
|
+
dep_output_sgp_symbols = self.get_sgp_symbols(dep_output_sgp_file)
|
|
196
|
+
|
|
197
|
+
symbols_and_groups_tmp = []
|
|
198
|
+
debug_tmp = self.get_interface_symbols_and_groups(debug,
|
|
199
|
+
debug_sgp_symbols,
|
|
200
|
+
debug_output_sgp_symbols)
|
|
201
|
+
dep_tmp = self.get_interface_symbols_and_groups(dep,
|
|
202
|
+
dep_sgp_symbols,
|
|
203
|
+
dep_output_sgp_symbols)
|
|
204
|
+
symbols_and_groups_tmp.extend(debug_tmp)
|
|
205
|
+
symbols_and_groups_tmp.extend(dep_tmp)
|
|
206
|
+
|
|
207
|
+
debug_symbols_and_groups = []
|
|
208
|
+
for symbol, symbol_group in symbols_and_groups_tmp:
|
|
209
|
+
if symbol_group == '':
|
|
210
|
+
LOGGER.info('Debug symbol %s is missing symbol group and will be removed.', symbol)
|
|
211
|
+
else:
|
|
212
|
+
debug_symbols_and_groups.append((symbol, symbol_group))
|
|
213
|
+
|
|
214
|
+
return debug_symbols_and_groups
|
|
215
|
+
|
|
216
|
+
def check_unit_par_file(self, unit):
|
|
217
|
+
"""Check <unit>_par.m file for default sgp symbol group.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
unit (str): Current unit/model name.
|
|
221
|
+
Returns:
|
|
222
|
+
has_sgp_default (Bool): True/False if unit is associated with default sgp value.
|
|
223
|
+
default_symbol_group (str): Name of default symbol group.
|
|
224
|
+
"""
|
|
225
|
+
has_sgp_default = False
|
|
226
|
+
default_symbol_group = ''
|
|
227
|
+
base_search_string = r'SgpDefault\.{unit}\.[A-Za-z]+\s*=\s*[\'\"]([A-Za-z_]+)[\'\"]'
|
|
228
|
+
search_string = base_search_string.format(unit=unit)
|
|
229
|
+
|
|
230
|
+
non_existent_par_file = Path('non_existent_par_file.m')
|
|
231
|
+
found_par_files = glob.glob('Models/*/' + unit + '/' + unit + '_par.m')
|
|
232
|
+
if len(found_par_files) > 1:
|
|
233
|
+
LOGGER.warning('Found more than one _par.m file, using %s', found_par_files[0])
|
|
234
|
+
par_file = Path(found_par_files[0]) if found_par_files else non_existent_par_file
|
|
235
|
+
|
|
236
|
+
if self.labelsplit_cfg.get("special_unit_prefixes"):
|
|
237
|
+
for special_prefix in self.labelsplit_cfg.get("special_unit_prefixes"):
|
|
238
|
+
if unit.startswith(special_prefix) and not par_file.is_file():
|
|
239
|
+
# Some units require special handling.
|
|
240
|
+
if '__' in unit:
|
|
241
|
+
parent = unit.replace('__', 'Mdl__')
|
|
242
|
+
else:
|
|
243
|
+
parent = unit + 'Mdl'
|
|
244
|
+
found_par_files = glob.glob('Models/*/' + parent + '/' + parent + '_par.m')
|
|
245
|
+
par_file = Path(found_par_files[0]) if found_par_files else Path(non_existent_par_file)
|
|
246
|
+
# Default symbol group is based on c-file name
|
|
247
|
+
c_name = re.sub('(Mdl)?(__.*)?', '', unit)
|
|
248
|
+
search_string = base_search_string.format(unit=c_name)
|
|
249
|
+
|
|
250
|
+
if par_file.is_file():
|
|
251
|
+
with par_file.open(encoding="latin-1") as par_fh:
|
|
252
|
+
par_text = par_fh.read()
|
|
253
|
+
sgp_default_match = re.search(search_string, par_text)
|
|
254
|
+
if sgp_default_match is not None:
|
|
255
|
+
has_sgp_default = True
|
|
256
|
+
default_symbol_group = sgp_default_match.group(1)
|
|
257
|
+
else:
|
|
258
|
+
LOGGER.info('Missing _par file for model: %s', unit)
|
|
259
|
+
|
|
260
|
+
return has_sgp_default, default_symbol_group
|
|
261
|
+
|
|
262
|
+
def get_unit_sgp_file(self, unit):
|
|
263
|
+
"""Get path to <unit>_sgp.xml file.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
unit (str): Current unit/model name.
|
|
267
|
+
Returns:
|
|
268
|
+
sgp_file (Path): Path to <unit>_sgp.xml file.
|
|
269
|
+
"""
|
|
270
|
+
non_existent_sgp_file = Path('non_existent_sgp_file.xml')
|
|
271
|
+
found_sgp_files = glob.glob('Models/*/' + unit + '/' + unit + '_sgp.xml')
|
|
272
|
+
if len(found_sgp_files) > 1:
|
|
273
|
+
LOGGER.warning('Found more than one _sgp.xml file, using %s', found_sgp_files[0])
|
|
274
|
+
sgp_file = Path(found_sgp_files[0]) if found_sgp_files else Path(non_existent_sgp_file)
|
|
275
|
+
|
|
276
|
+
if self.labelsplit_cfg.get("special_unit_prefixes"):
|
|
277
|
+
for special_prefix in self.labelsplit_cfg.get("special_unit_prefixes"):
|
|
278
|
+
if unit.startswith(special_prefix) and not sgp_file.is_file():
|
|
279
|
+
# Some units require special handling.
|
|
280
|
+
if '__' in unit:
|
|
281
|
+
parent = unit.replace('__', 'Mdl__')
|
|
282
|
+
else:
|
|
283
|
+
parent = unit + 'Mdl'
|
|
284
|
+
found_sgp_files = glob.glob('Models/*/' + parent + '/' + parent + '_sgp.xml')
|
|
285
|
+
sgp_file = Path(found_sgp_files[0]) if found_sgp_files else Path(non_existent_sgp_file)
|
|
286
|
+
|
|
287
|
+
return sgp_file
|
|
288
|
+
|
|
289
|
+
def get_unit_symbols_and_groups(self, unit, calibration_symbols):
|
|
290
|
+
"""Get a list of (symbol, symbol_group) pairs found in A2L, <unit>_sgp/par and config_<unit>.json files.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
unit (str): Current unit/model name.
|
|
294
|
+
calibration_symbols (list): All calibration symbols for the unit (from config_<unit>.json).
|
|
295
|
+
Returns:
|
|
296
|
+
unit_symbols_and_groups (list(tuple)): List of (symbol, symbol_group) pairs in: A2L, _sgp/_par and
|
|
297
|
+
config files.
|
|
298
|
+
"""
|
|
299
|
+
unit_symbols_and_groups = []
|
|
300
|
+
has_sgp_default, default_symbol_group = self.check_unit_par_file(unit)
|
|
301
|
+
sgp_file = self.get_unit_sgp_file(unit)
|
|
302
|
+
|
|
303
|
+
if sgp_file.is_file():
|
|
304
|
+
found_sgp_symbols = self.get_sgp_symbols(sgp_file)
|
|
305
|
+
else:
|
|
306
|
+
found_sgp_symbols = {}
|
|
307
|
+
LOGGER.info('Missing _sgp file for model: %s', unit)
|
|
308
|
+
|
|
309
|
+
for symbol in calibration_symbols:
|
|
310
|
+
if symbol not in self.project_a2l_symbols:
|
|
311
|
+
LOGGER.info('Symbol %s not in project A2L file and will be removed.', symbol)
|
|
312
|
+
continue
|
|
313
|
+
|
|
314
|
+
if symbol in found_sgp_symbols:
|
|
315
|
+
symbol_group = self.get_sgp_symbol_group(found_sgp_symbols[symbol])
|
|
316
|
+
unit_symbols_and_groups.append((symbol, symbol_group))
|
|
317
|
+
elif has_sgp_default:
|
|
318
|
+
if symbol.endswith('_sw') or symbol.endswith('_db'):
|
|
319
|
+
LOGGER.info('Debug symbol %s not in sgp file and will be removed.', symbol)
|
|
320
|
+
else:
|
|
321
|
+
unit_symbols_and_groups.append((symbol, default_symbol_group))
|
|
322
|
+
else:
|
|
323
|
+
LOGGER.info('Symbol %s missing in _sgp file and lack SgpDefault value.', symbol)
|
|
324
|
+
|
|
325
|
+
return unit_symbols_and_groups
|
|
326
|
+
|
|
327
|
+
def get_calibration_constants(self):
|
|
328
|
+
"""Get all calibration symbols for each unit in the project.
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
calibration_symbols_per_unit (dict): A unit to symbol list dictionary.
|
|
332
|
+
"""
|
|
333
|
+
security_variables = self.labelsplit_cfg.get("security_variables")
|
|
334
|
+
u_conf_dict = self.unit_cfg.get_per_cfg_unit_cfg()
|
|
335
|
+
|
|
336
|
+
safe_calibration_symbols = {}
|
|
337
|
+
for symbol, symbol_data in u_conf_dict['calib_consts'].items():
|
|
338
|
+
if symbol not in security_variables:
|
|
339
|
+
safe_calibration_symbols.update({symbol: symbol_data})
|
|
340
|
+
|
|
341
|
+
calibration_symbols_per_unit = {}
|
|
342
|
+
for symbol, symbol_data in safe_calibration_symbols.items():
|
|
343
|
+
for unit, unit_data in symbol_data.items():
|
|
344
|
+
if 'CVC_CAL' in unit_data['class']:
|
|
345
|
+
if unit in calibration_symbols_per_unit:
|
|
346
|
+
calibration_symbols_per_unit[unit].append(symbol)
|
|
347
|
+
else:
|
|
348
|
+
calibration_symbols_per_unit[unit] = [symbol]
|
|
349
|
+
|
|
350
|
+
return calibration_symbols_per_unit
|
|
351
|
+
|
|
352
|
+
def get_symbols_and_groups(self):
|
|
353
|
+
"""Get a list of (symbol, symbol_group) pairs found in A2L, <unit>_sgp/par and config_<unit>.json files.
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
exit_code (int): 0/1 based on successful collection of symbols and symbol groups.
|
|
357
|
+
all_symbols_and_groups (dict): A symbol to symbol_group dictionary for all A2L, _sgp/_par and config files,
|
|
358
|
+
"""
|
|
359
|
+
exit_code = 0
|
|
360
|
+
calibration_symbols_per_unit = self.get_calibration_constants()
|
|
361
|
+
debug_symbols = self.get_debug_symbols_and_groups()
|
|
362
|
+
|
|
363
|
+
all_symbol_and_group_pairs = []
|
|
364
|
+
if self.labelsplit_cfg.get("project_symbols"):
|
|
365
|
+
special_project_dict = self.labelsplit_cfg.get("project_symbols")
|
|
366
|
+
pair_list = [val for key, val in special_project_dict.items() if key in self.project]
|
|
367
|
+
if len(pair_list) == 1:
|
|
368
|
+
symbol_and_group_pairs_list = pair_list[0].items()
|
|
369
|
+
all_symbol_and_group_pairs += symbol_and_group_pairs_list
|
|
370
|
+
elif len(pair_list) > 1:
|
|
371
|
+
LOGGER.error('Project %s has does not follow the name rule', self.project)
|
|
372
|
+
return 1, {}
|
|
373
|
+
|
|
374
|
+
all_symbol_and_group_pairs.extend(debug_symbols)
|
|
375
|
+
|
|
376
|
+
for unit, symbols in calibration_symbols_per_unit.items():
|
|
377
|
+
if self.labelsplit_cfg.get("special_units"):
|
|
378
|
+
special_unit_dict = self.labelsplit_cfg.get("special_units")
|
|
379
|
+
if unit in special_unit_dict.keys():
|
|
380
|
+
# Some units require special handling.
|
|
381
|
+
LOGGER.warning('Found %s, assuming %s is used.', unit, special_unit_dict.get(unit))
|
|
382
|
+
labels = self.get_unit_symbols_and_groups(special_unit_dict.get(unit), symbols)
|
|
383
|
+
else:
|
|
384
|
+
labels = self.get_unit_symbols_and_groups(unit, symbols)
|
|
385
|
+
else:
|
|
386
|
+
labels = self.get_unit_symbols_and_groups(unit, symbols)
|
|
387
|
+
all_symbol_and_group_pairs.extend(labels)
|
|
388
|
+
|
|
389
|
+
symbol_to_group_dict = {}
|
|
390
|
+
for symbol, symbol_group in all_symbol_and_group_pairs:
|
|
391
|
+
if symbol in symbol_to_group_dict:
|
|
392
|
+
if symbol_to_group_dict[symbol] != symbol_group:
|
|
393
|
+
LOGGER.error('Symbol %s multiply defined with different symbol groups.', symbol)
|
|
394
|
+
exit_code = 1
|
|
395
|
+
else:
|
|
396
|
+
symbol_to_group_dict[symbol] = symbol_group
|
|
397
|
+
|
|
398
|
+
return exit_code, symbol_to_group_dict
|
|
399
|
+
|
|
400
|
+
def generate_label_split_xml_file(self, symbols_and_groups):
|
|
401
|
+
"""Generate a label split file, given a directory plus labels and groups to add.
|
|
402
|
+
|
|
403
|
+
Args:
|
|
404
|
+
symbols_and_groups (dict): A symbol to symbol_group dictionary given a project.
|
|
405
|
+
Returns:
|
|
406
|
+
exit_code (int): 0/1 based on successful generation of Labelsplit.xls.
|
|
407
|
+
"""
|
|
408
|
+
errors = []
|
|
409
|
+
project_root_dir = self.build_cfg.get_root_dir()
|
|
410
|
+
cmt_output_folder = helper_functions.create_dir(Path(project_root_dir, 'output', 'CMT'))
|
|
411
|
+
start_file_name = Path(self.cmt_source_folder, 'template_labelsplit_sgp_start.xml_')
|
|
412
|
+
row_count_file_name = Path(cmt_output_folder, 'labelsplit_rowcount.xml_')
|
|
413
|
+
start_2_file_name = Path(self.cmt_source_folder, 'template_labelsplit_sgp_start_2.xml_')
|
|
414
|
+
label_split_rows_filename = Path(cmt_output_folder, 'labelsplit_rows.xml_')
|
|
415
|
+
end_file_name = Path(self.cmt_source_folder, 'template_labelsplit_sgp_end.xml_')
|
|
416
|
+
files_to_merge = [start_file_name, row_count_file_name, start_2_file_name,
|
|
417
|
+
label_split_rows_filename, end_file_name]
|
|
418
|
+
|
|
419
|
+
with row_count_file_name.open('w', encoding="utf-8") as rc_fh:
|
|
420
|
+
rc_fh.write(f'{len(symbols_and_groups) + 1}') # header + data
|
|
421
|
+
|
|
422
|
+
with label_split_rows_filename.open('w', encoding="utf-8") as lsrf_fh:
|
|
423
|
+
for symbol, symbol_group in symbols_and_groups.items():
|
|
424
|
+
if symbol_group == '':
|
|
425
|
+
errors.append(f'Missing symbol group for symbol: {symbol}')
|
|
426
|
+
elif symbol_group == 'VCC_SPM_DEBUG':
|
|
427
|
+
LOGGER.info('Ignoring undistributed debug symbol: %s', symbol)
|
|
428
|
+
else:
|
|
429
|
+
lsrf_fh.write(
|
|
430
|
+
' <Row ss:AutoFitHeight="0">\n'
|
|
431
|
+
f' <Cell><Data ss:Type="String">{symbol}'
|
|
432
|
+
'</Data><NamedCell ss:Name="_FilterDatabase"/></Cell>\n'
|
|
433
|
+
f' <Cell ss:Index="5"><Data ss:Type="String">{symbol_group}'
|
|
434
|
+
'</Data><NamedCell ss:Name="_FilterDatabase"/></Cell>\n'
|
|
435
|
+
' </Row>\n'
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
if errors:
|
|
439
|
+
LOGGER.error('\n'.join(errors))
|
|
440
|
+
return 1
|
|
441
|
+
|
|
442
|
+
output_file_name = Path(project_root_dir, 'output', 'CMT', 'Labelsplit.xls')
|
|
443
|
+
with output_file_name.open('w', encoding="utf-8") as output_fh:
|
|
444
|
+
for file_name in files_to_merge:
|
|
445
|
+
with file_name.open(encoding="utf-8") as input_fh:
|
|
446
|
+
content = input_fh.read()
|
|
447
|
+
output_fh.write(content)
|
|
448
|
+
LOGGER.info('Delivery to: %s', str(output_file_name))
|
|
449
|
+
return 0
|