Aggregate project settings for ARXML specifications in ProjectCfg

Change-Id: Ia54f7ae0164aeb62361b0839b79b178779616402
This commit is contained in:
olindgre 2024-10-09 10:57:15 +02:00
parent 971bd262d3
commit 7888ebe2b7
13 changed files with 130 additions and 70 deletions

5
NOTICE

@ -31,6 +31,7 @@ python-certifi 2024.7.4: https://certifiio.readthedocs.io/en/latest/ : Mozilla P
python-pluggy 1.5.0: https://pypi.python.org/pypi/pluggy : MIT License
RonnyPfannschmidt/iniconfig 2.0.0: https://github.com/RonnyPfannschmidt/iniconfig : MIT License
ruamel-yaml 0.18.6: https://pypi.org/project/ruamel.yaml/ : MIT License
ruamel.yaml.clib 0.2.12: https://sourceforge.net/p/ruamel-yaml-clib/code/ci/default/tree/ : MIT License
SciPy 1.9.1: http://www.scipy.org : BSD 3-clause "New" or "Revised" License
smmap 5.0.1: https://github.com/gitpython-developers/smmap : BSD 3-clause "New" or "Revised" License
tomli 2.0.2: https://github.com/hukkin/tomli : MIT License
@ -2692,6 +2693,8 @@ from typing import (
ruamel-yaml 0.18.6 pypi:ruamel.yaml/0.18.6: https://pypi.org/project/ruamel.yaml/
No Copyrights found
ruamel.yaml.clib 0.2.12 pypi:ruamel.yaml.clib/0.2.12: https://sourceforge.net/p/ruamel-yaml-clib/code/ci/default/tree/
No Copyrights found
SciPy 1.9.1 pypi:scipy/1.9.1: http://www.scipy.org
(c) (Col
@ -13615,7 +13618,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
---
MIT License
(exceptiongroup 1.2.2, flake8 7.1.1, pycodestyle 2.12.1, Pyflakes 3.2.0, pytest 8.3.3, python-pluggy 1.5.0, python3-charset-normalizer 3.4.0, RonnyPfannschmidt/iniconfig 2.0.0, ruamel-yaml 0.18.6, tomli 2.0.2, urllib3 2.2.3)
(exceptiongroup 1.2.2, flake8 7.1.1, pycodestyle 2.12.1, Pyflakes 3.2.0, pytest 8.3.3, python-pluggy 1.5.0, python3-charset-normalizer 3.4.0, RonnyPfannschmidt/iniconfig 2.0.0, ruamel-yaml 0.18.6, ruamel.yaml.clib 0.2.12, tomli 2.0.2, urllib3 2.2.3)
The MIT License
===============

@ -187,7 +187,7 @@ Default is False.
#### useSwcNameAsPrefix
Use the software component name as prefix in the "common" functions and structs generated by powertrain-build.
"softwareComponentName" needs to be set in [ProjectInfo](#projectinfo).
"softwareComponentName" needs to be set in [CompositionConfig](#compositionconfig).
Default is False.
## Rasters json File
@ -304,6 +304,54 @@ Use the field to choose specific code generation options in powertrain-build.
Key value pairs specified in this struct takes priority over the ones specified in [ProjectTemplates](#projecttemplates).
Afformentioned chapter also describes the available options and their default values.
### CompositionConfig
Collection of config options for autosar arxml based scheduling in the project configuration or base config.
These options affect the content of a configuration file used to complement the arxml file of the project
when building with conan.
#### compositionName
Name of the composition file used by conan to complement the arxml file.
Will be .yml unless name is given with file ending.
#### compositionArxml
Name of the arxml file containing autosar packages and elements, specifying signal interfaces, data types etc.
#### customYamlInitFunctionName
Name of Init function. Default: "AR_<scheduler_prefix>_VcExtINI"
#### generateExternalImplementationType
Specifies if external implementation types should be generated or not at the build stage (conan). Default: True
#### softwareComponentName
Name of the software component. Needs to match the software component name in the arxml.
Default: matching "A2lConfig/name".
#### softwareComponentTemplate
Name of template used by the software component. Default: None.
#### softwareComponentBase
Name of software component base used by the software component. Default: 'QM'.
#### includeStatic
Include static elements or not. Default: True.
#### includeShared
Include shared elements or not. Default: True.
#### includeDiagnostics
Include diagnostics elements or not. Default: True.
### MemoryMapConfig
This configuration is required when [generateInterfaceHeaders](#generateinterfaceheaders) is set to true.

@ -44,6 +44,7 @@ class BuildProjConfig:
if not Version.is_compatible(self._prj_cfg.get('BaseConfigFileVersion')):
raise ValueError('Incompatible base config file version.')
deep_dict_update(self._prj_cfg, self._get_code_generation_config())
self._composition_config = self._parse_composition_config(self._prj_cfg.get('CompositionConfig', {}))
self.has_yaml_interface = self._prj_cfg['ProjectInfo'].get('yamlInterface', False)
self.device_domains = self._get_device_domains()
self.services_file = self._get_services_file()
@ -247,42 +248,34 @@ class BuildProjConfig:
os.path.normpath(self._prj_cfg['ProjectInfo']
['srcCodeDstDir']))
def get_composition_name(self):
"""Return the composition name."""
name, _ = os.path.splitext(self._prj_cfg['ProjectInfo']['compositionName'])
return name
def get_composition_ending(self):
"""Return the composition ending."""
_, ending = os.path.splitext(self._prj_cfg['ProjectInfo']['compositionName'])
if ending:
return ending
return 'yml'
def get_composition_arxml(self):
"""Return the relative composition arxml path."""
return self._prj_cfg['ProjectInfo']['compositionArxml']
def get_custom_yaml_init_function_name(self):
"""Return the custom yaml init function name."""
return self._prj_cfg['ProjectInfo'].get('customYamlInitFunctionName')
def get_gen_ext_impl_type(self):
"""Return the generate external implementation type."""
return self._prj_cfg['ProjectInfo'].get('generateExternalImplementationType', True)
def get_swc_name(self):
"""Returns the software component name."""
def _parse_composition_config(self, file_config):
"""Parse the composition configuration from project config."""
a2lname = f"{self.get_a2l_cfg()['name']}_SC"
return self._prj_cfg['ProjectInfo'].get('softwareComponentName', a2lname)
self._composition_config = {
'compositionName': None,
'compositionEnding': 'yml',
'compositionArxml': file_config.get("compositionArxml", None),
'customYamlInitFunctionName': file_config.get("customYamlInitFunctionName", None),
'generateExternalImplementationType': file_config.get("generateExternalImplementationType", True),
'softwareComponentName': file_config.get("softwareComponentName", a2lname),
'softwareComponentTemplate': file_config.get('softwareComponentTemplate', None),
'softwareComponentBase': file_config.get('softwareComponentBase', 'QM'),
'includeStatic': file_config.get('includeStatic', True),
'includeShared': file_config.get('includeShared', True),
'includeDiagnostics': file_config.get('includeDiagnostics', True),
}
compositionName = file_config.get("compositionName", None)
if compositionName is not None:
self._composition_config["compositionName"] = compositionName.split(".")[0]
if "." in compositionName:
self._composition_config["compositionEnding"] = compositionName.split(".")[1]
return self._composition_config
def get_swc_template(self):
"""Returns the software component template to use."""
return self._prj_cfg['ProjectInfo'].get('softwareComponentTemplate')
def get_swc_base(self):
"""Returns the software component base to use (ASIL classification)."""
return self._prj_cfg['ProjectInfo'].get('softwareComponentBase', 'QM')
def get_composition_config(self, key=None):
"""Get the composition configuration from project config."""
if key is None:
return self._composition_config
return self._composition_config[key]
def get_car_com_dst(self):
"""Return the absolute path to the source output folder."""

@ -364,7 +364,7 @@ class ZCCore(ProblemLogger):
Returns:
(list(str)): List of lines to write to the DTC header file.
"""
name = self._prj_cfg.get_swc_name()
name = self._prj_cfg.get_composition_config("softwareComponentName")
header_guard = f'{self.FILE_NAME.upper()}_H'
header = [
f'#ifndef {header_guard}\n',

@ -708,7 +708,7 @@ class ZCDIDs(ProblemLogger):
Returns:
(list(str)): List of lines to write to DID API header file.
"""
name = self._build_cfg.get_swc_name()
name = self._build_cfg.get_composition_config("softwareComponentName")
header_guard = f'{self.FILE_NAME.upper()}_H'
header = [
f'#ifndef {header_guard}\n',

@ -157,7 +157,7 @@ class NVMDef(ProblemLogger):
use_prefix (bool): Patch the nvm header file definitions with the SWC name as prefix.
"""
res = {}
prefix = f"{self._project_config.get_swc_name()}_" if use_prefix else ""
prefix = f"{self._project_config.get_composition_config('softwareComponentName')}_" if use_prefix else ""
for var, var_attrib in self._nvm_signals.items():
res[var] = {
"var": {"var": var, "type": var_attrib["type"], "cvc_type": "CVC_NVM"},
@ -449,7 +449,7 @@ class NVMDef(ProblemLogger):
use_prefix (bool): Patch the nvm header file definitions with the SWC name as prefix.
"""
self.info("Start generating nvm header file")
prefix = f"{self._project_config.get_swc_name()}_" if use_prefix else ""
prefix = f"{self._project_config.get_composition_config('softwareComponentName')}_" if use_prefix else ""
def write_signals():
for memory_area in self._nvm_memory_areas:
@ -526,7 +526,7 @@ class NVMDef(ProblemLogger):
"""
# TODO: Add memory from previous builds!!! and mark old positions #
self.info("Start generating nvm source file")
prefix = f"{self._project_config.get_swc_name()}_" if use_prefix else ""
prefix = f"{self._project_config.get_composition_config('softwareComponentName')}_" if use_prefix else ""
src_file_dst = self._project_config.get_src_code_dst_dir()
file_name = os.path.join(src_file_dst, self._nvm_defs["fileName"])
with open(file_name + ".c", "w", encoding="utf-8") as cptr:

@ -23,7 +23,7 @@ class ZoneControllerCalibration(ProblemLogger):
build_cfg (BuildProjConfig): Object with build configuration settings.
calib_data (dict): Dictionary containing calibration data for a ZoneController project.
"""
self.swc_name = build_cfg.get_swc_name()
self.swc_name = build_cfg.get_composition_config("softwareComponentName")
self.src_code_dst_dir = build_cfg.get_src_code_dst_dir()
self.calibration_variables = calib_data['class_info']
self.calibration_interface_header = 'calibration_interface.h'

@ -118,8 +118,8 @@ class CompositionYaml(ProblemLogger):
def generate_yaml(self):
"""Generates a yaml from project/model information."""
composition_name = self.build_cfg.get_composition_name()
composition_ending = self.build_cfg.get_composition_ending()
composition_name = self.build_cfg.get_composition_config("compositionName")
composition_ending = self.build_cfg.get_composition_config("compositionEnding")
all_info = self.gather_yaml_info()
output_directory = self.build_cfg.get_src_code_dst_dir()
@ -153,8 +153,10 @@ class CompositionYaml(ProblemLogger):
all_info = {
"ExternalFiles": {
"Composition": self.build_cfg.get_composition_arxml(),
"GenerateExternalImplementationTypes": self.build_cfg.get_gen_ext_impl_type(),
"Composition": self.build_cfg.get_composition_config("compositionArxml"),
"GenerateExternalImplementationTypes": self.build_cfg.get_composition_config(
"generateExternalImplementationType"
),
},
"SoftwareComponents": software_components,
"DataTypes": {**self.data_types, **pt_build_data_types},
@ -174,16 +176,16 @@ class CompositionYaml(ProblemLogger):
value_extraction_regexes = [
(
re.compile(r"^\s*CVC_CAL[A-Z_]*\s+\w+\s+(?P<name>\w+)\s*=\s*(?P<value>[-\d\.e]+F?)\s*;"),
lambda regex_match, _: self._cast_init_value(regex_match.group("value"))
lambda regex_match, _: self._cast_init_value(regex_match.group("value")),
),
(
re.compile(r"^\s*CVC_CAL[A-Z_]*\s+\w+\s+(?P<name>\w+)\[(?P<size>[\d]+)\]\s*=\s*"),
self._get_array_init_values
self._get_array_init_values,
),
(
re.compile(r"^\s*CVC_CAL[A-Z_]*\s+\w+\s+(?P<name>\w+)\[(?P<rows>[\d]+)\]\[(?P<cols>[\d]+)\]\s*=\s*"),
self._get_matrix_init_values
)
self._get_matrix_init_values,
),
]
init_values = {}
@ -289,7 +291,7 @@ class CompositionYaml(ProblemLogger):
Returns:
trigger_signal (str): Name of variable for triggering calibration.
"""
software_component_name = self.build_cfg.get_swc_name()
software_component_name = self.build_cfg.get_composition_config("softwareComponentName")
trigger_signal = ZCC.trigger_read_rte_cdata_signal["name_template"].format(swc_name=software_component_name)
if trigger_signal in calibration_variables:
@ -354,10 +356,10 @@ class CompositionYaml(ProblemLogger):
Returns:
dict: Dict containing runnables information.
"""
swc_name = self.build_cfg.get_swc_name()
swc_name = self.build_cfg.get_composition_config("softwareComponentName")
autosar_prefix = "AR_"
swc_prefix = self.build_cfg.get_scheduler_prefix()
custom_init_function = self.build_cfg.get_custom_yaml_init_function_name()
custom_init_function = self.build_cfg.get_composition_config("customYamlInitFunctionName")
standard_init_function = autosar_prefix + swc_prefix + "VcExtINI"
init_function = custom_init_function if custom_init_function is not None else standard_init_function
calibration_variables = list(self.cal_class_info["autosar"]["class_info"].keys())
@ -396,9 +398,9 @@ class CompositionYaml(ProblemLogger):
swcs (dict): SWC information.
data_types (dict): Data types information.
"""
software_component_name = self.build_cfg.get_swc_name()
software_component_template = self.build_cfg.get_swc_template()
software_component_base = self.build_cfg.get_swc_base()
software_component_name = self.build_cfg.get_composition_config("softwareComponentName")
software_component_template = self.build_cfg.get_composition_config("softwareComponentTemplate")
software_component_base = self.build_cfg.get_composition_config("softwareComponentBase")
data_types = {
**self.cal_class_info["autosar"]["data_types"],
**self.meas_class_info["autosar"]["data_types"],
@ -410,10 +412,14 @@ class CompositionYaml(ProblemLogger):
if software_component_base is not None:
swcs[software_component_name]["swcbase"] = software_component_base
swcs[software_component_name]["runnables"] = self._get_runnable_info()
swcs[software_component_name]["shared"] = self.cal_class_info["autosar"]["class_info"]
swcs[software_component_name]["static"] = self.meas_class_info["autosar"]["class_info"]
if self.build_cfg.get_composition_config("includeShared"):
swcs[software_component_name]["shared"] = self.cal_class_info["autosar"]["class_info"]
if self.build_cfg.get_composition_config("includeStatic"):
swcs[software_component_name]["static"] = self.meas_class_info["autosar"]["class_info"]
swcs[software_component_name]["ports"] = self._get_ports_info()
swcs[software_component_name]["diagnostics"] = self._get_diagnostic_info()
diagnostic_info = self._get_diagnostic_info()
if self.build_cfg.get_composition_config("includeDiagnostics"):
swcs[software_component_name]["diagnostics"] = diagnostic_info
return swcs, data_types
def _get_variables(self):

@ -213,7 +213,7 @@ class TestZCCore(unittest.TestCase):
def setUp(self):
"""Set-up common data structures for all tests in the test case."""
project_config = MagicMock()
project_config.get_swc_name.return_value = 'DUMMY'
project_config.get_composition_config.return_value = 'DUMMY'
unit_configs = MagicMock()
unit_configs.get_per_unit_cfg.return_value = {}

@ -984,7 +984,7 @@ class TestZCDIDs(unittest.TestCase):
def setUp(self):
build_cfg = MagicMock()
build_cfg.get_swc_name.return_value = 'DUMMY'
build_cfg.get_composition_config.return_value = "DUMMY"
unit_cfg = MagicMock()
self.zc_dids = ZCDIDs(build_cfg, unit_cfg)
self.zc_dids.project_dids = dummy_project_dids

@ -210,7 +210,7 @@ class TestNVMDef(unittest.TestCase):
self.proj_cnfg.get_root_dir = MagicMock(return_value=projdir)
self.proj_cnfg.get_src_code_dst_dir = MagicMock(return_value=str(Path(SRC_DIR, 'output')))
self.proj_cnfg.get_nvm_defs = MagicMock(return_value=self.nvm_configs)
self.proj_cnfg.get_swc_name = MagicMock(return_value='DummySwc')
self.proj_cnfg.get_composition_config = MagicMock(return_value='DummySwc')
self.proj_cnfg.get_code_generation_config = MagicMock(return_value=True)
self.nvm_def = NVMDef(self.proj_cnfg, self.unit_cfg, self.nvm_vars_test)

@ -18,7 +18,7 @@ class TestZoneControllerCalibration(TestCase):
build_cfg = MagicMock()
build_cfg.name = "XVC"
build_cfg.get_src_code_dst_dir.return_value = None
build_cfg.get_swc_name.return_value = "testName_SC"
build_cfg.get_composition_config.return_value = "testName_SC"
dummy_calib_data = {
"class_info": {
"dummy_signal_one": {

@ -32,27 +32,37 @@ class BuildProjConfigMock(BuildProjConfig):
name = ""
def mocked_get_composition_config(key):
return {
"compositionArxml": "some_arxml.arxml",
"compositionName": "compositionName",
'compositionEnding': 'yml',
"softwareComponentName": "testName_SC",
"softwareComponentTemplate": "ARTCSC",
"softwareComponentBase": "QM",
"customYamlInitFunctionName": None,
"generateExternalImplementationType": True,
'includeStatic': True,
'includeShared': True,
'includeDiagnostics': True,
}[key]
class TestCompositionYaml(unittest.TestCase):
"""Test case for testing composition_yaml."""
def setUp(self):
"""Set-up common data structures for all tests in the test case."""
self.build_cfg = MagicMock(spec_set=BuildProjConfigMock)
self.build_cfg.get_composition_config.side_effect = mocked_get_composition_config
self.build_cfg.name = "XVC"
self.build_cfg.get_scheduler_prefix = MagicMock(return_value="prefix_")
self.build_cfg.get_src_code_dst_dir = MagicMock(
return_value=os.path.abspath("output")
)
self.build_cfg.get_composition_arxml = MagicMock(return_value="some_arxml.arxml")
self.build_cfg.get_units_raster_cfg = MagicMock(
return_value=({"SampleTimes": {"testRunnable": 10}})
)
self.build_cfg.get_composition_name = MagicMock(return_value="compositionName")
self.build_cfg.get_swc_name = MagicMock(return_value="testName_SC")
self.build_cfg.get_swc_template = MagicMock(return_value="ARTCSC")
self.build_cfg.get_swc_base = MagicMock(return_value="QM")
self.build_cfg.get_custom_yaml_init_function_name = MagicMock(return_value=None)
self.build_cfg.get_gen_ext_impl_type = MagicMock(return_value=True)
self.build_cfg.get_code_generation_config = MagicMock(return_value=False)
self.unit_cfg = MagicMock(spec_set=UnitConfigs)