From 7888ebe2b7671f1fd1ab9ce6b95d3b1db098f0e7 Mon Sep 17 00:00:00 2001 From: olindgre Date: Wed, 9 Oct 2024 10:57:15 +0200 Subject: [PATCH] Aggregate project settings for ARXML specifications in ProjectCfg Change-Id: Ia54f7ae0164aeb62361b0839b79b178779616402 --- NOTICE | 5 +- docs/project_config.md | 50 ++++++++++++++- powertrain_build/build_proj_config.py | 61 ++++++++----------- powertrain_build/core.py | 2 +- powertrain_build/dids.py | 2 +- powertrain_build/nvm_def.py | 6 +- .../zone_controller/calibration.py | 2 +- .../zone_controller/composition_yaml.py | 40 ++++++------ tests/powertrain_build/test_core.py | 2 +- tests/powertrain_build/test_dids.py | 2 +- tests/powertrain_build/test_nvm_def.py | 2 +- tests/zone_controller/test_calibration.py | 2 +- .../zone_controller/test_composition_yaml.py | 24 +++++--- 13 files changed, 130 insertions(+), 70 deletions(-) diff --git a/NOTICE b/NOTICE index 224a05b..035bddf 100644 --- a/NOTICE +++ b/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 =============== diff --git a/docs/project_config.md b/docs/project_config.md index 40abb97..e4f1abf 100644 --- a/docs/project_config.md +++ b/docs/project_config.md @@ -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__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. diff --git a/powertrain_build/build_proj_config.py b/powertrain_build/build_proj_config.py index 6b8b24f..7a5f9ab 100644 --- a/powertrain_build/build_proj_config.py +++ b/powertrain_build/build_proj_config.py @@ -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.""" diff --git a/powertrain_build/core.py b/powertrain_build/core.py index 337d5e3..46a9497 100644 --- a/powertrain_build/core.py +++ b/powertrain_build/core.py @@ -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', diff --git a/powertrain_build/dids.py b/powertrain_build/dids.py index 826ddfe..815e8cc 100644 --- a/powertrain_build/dids.py +++ b/powertrain_build/dids.py @@ -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', diff --git a/powertrain_build/nvm_def.py b/powertrain_build/nvm_def.py index 1d92549..c1e1a02 100644 --- a/powertrain_build/nvm_def.py +++ b/powertrain_build/nvm_def.py @@ -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: diff --git a/powertrain_build/zone_controller/calibration.py b/powertrain_build/zone_controller/calibration.py index 3ca63d0..2144c87 100644 --- a/powertrain_build/zone_controller/calibration.py +++ b/powertrain_build/zone_controller/calibration.py @@ -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' diff --git a/powertrain_build/zone_controller/composition_yaml.py b/powertrain_build/zone_controller/composition_yaml.py index 6817e25..a949283 100644 --- a/powertrain_build/zone_controller/composition_yaml.py +++ b/powertrain_build/zone_controller/composition_yaml.py @@ -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\w+)\s*=\s*(?P[-\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\w+)\[(?P[\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\w+)\[(?P[\d]+)\]\[(?P[\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): diff --git a/tests/powertrain_build/test_core.py b/tests/powertrain_build/test_core.py index 04a8b90..f2cc1cf 100644 --- a/tests/powertrain_build/test_core.py +++ b/tests/powertrain_build/test_core.py @@ -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 = {} diff --git a/tests/powertrain_build/test_dids.py b/tests/powertrain_build/test_dids.py index f6a415e..dc03233 100644 --- a/tests/powertrain_build/test_dids.py +++ b/tests/powertrain_build/test_dids.py @@ -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 diff --git a/tests/powertrain_build/test_nvm_def.py b/tests/powertrain_build/test_nvm_def.py index 75ee586..cad4c7e 100644 --- a/tests/powertrain_build/test_nvm_def.py +++ b/tests/powertrain_build/test_nvm_def.py @@ -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) diff --git a/tests/zone_controller/test_calibration.py b/tests/zone_controller/test_calibration.py index 0913e18..2553e4f 100644 --- a/tests/zone_controller/test_calibration.py +++ b/tests/zone_controller/test_calibration.py @@ -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": { diff --git a/tests/zone_controller/test_composition_yaml.py b/tests/zone_controller/test_composition_yaml.py index 2355658..eaf1270 100644 --- a/tests/zone_controller/test_composition_yaml.py +++ b/tests/zone_controller/test_composition_yaml.py @@ -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)