Add feature for ZC DIDs and DTCs

Change-Id: I170192eb10727b8c569920f925ccfc1a7f82ca08
This commit is contained in:
Henrik Wahlqvist 2024-07-02 16:27:52 +02:00
parent a6e0d18f5c
commit a7b1979768
15 changed files with 879 additions and 12 deletions

View File

@ -20,10 +20,10 @@ from pathlib import Path
from pybuild import __config_version__, __version__, build_defs from pybuild import __config_version__, __version__, build_defs
from pybuild.a2l_merge import A2lMerge from pybuild.a2l_merge import A2lMerge
from pybuild.build_proj_config import BuildProjConfig from pybuild.build_proj_config import BuildProjConfig
from pybuild.core import Core, HICore from pybuild.core import Core, HICore, ZCCore
from pybuild.core_dummy import CoreDummy from pybuild.core_dummy import CoreDummy
from pybuild.create_conversion_table import create_conversion_table from pybuild.create_conversion_table import create_conversion_table
from pybuild.dids import DIDs, HIDIDs from pybuild.dids import DIDs, HIDIDs, ZCDIDs
from pybuild.dummy import DummyVar from pybuild.dummy import DummyVar
from pybuild.dummy_spm import DummySpm from pybuild.dummy_spm import DummySpm
from pybuild.ext_dbg import ExtDbg from pybuild.ext_dbg import ExtDbg
@ -834,6 +834,11 @@ def build(
LOG.info("Generating DID files") LOG.info("Generating DID files")
dids = HIDIDs(build_cfg, unit_cfg) dids = HIDIDs(build_cfg, unit_cfg)
dids.generate_did_files() dids.generate_did_files()
elif ecu_supplier in ["ZC"]:
LOG.info("******************************************************")
LOG.info("Generating Core header")
zc_core = ZCCore(build_cfg, unit_cfg)
zc_core.generate_dtc_files()
else: else:
generate_did_files(build_cfg, unit_cfg) generate_did_files(build_cfg, unit_cfg)
# generate core dummy files if requested # generate core dummy files if requested
@ -920,15 +925,19 @@ def build(
create_conversion_table(ctable_json, ctable_a2l) create_conversion_table(ctable_json, ctable_a2l)
merged_a2l = merge_a2l_files(build_cfg, unit_cfg, complete_a2l, silver_a2l) merged_a2l = merge_a2l_files(build_cfg, unit_cfg, complete_a2l, silver_a2l)
if ecu_supplier in ["ZC"]: if ecu_supplier in ["ZC"]:
zc_dids = ZCDIDs(build_cfg, unit_cfg)
axis_data = merged_a2l.get_characteristic_axis_data() axis_data = merged_a2l.get_characteristic_axis_data()
composition_yaml = CompositionYaml( composition_yaml = CompositionYaml(
build_cfg, signal_if.composition_spec, unit_cfg, axis_data build_cfg, signal_if.composition_spec, unit_cfg, zc_core, zc_dids, axis_data
) )
composition_yaml.generate_yaml() composition_yaml.generate_yaml()
zc_calibration = ZoneControllerCalibration( zc_calibration = ZoneControllerCalibration(
build_cfg, composition_yaml.cal_class_info["tl"] build_cfg, composition_yaml.cal_class_info["tl"]
) )
zc_calibration.generate_calibration_interface_files() zc_calibration.generate_calibration_interface_files()
LOG.info("******************************************************")
LOG.info("Generating DID files")
zc_dids.generate_did_files()
a2l_file_path = Path(build_cfg.get_src_code_dst_dir(), build_cfg.get_a2l_name()) a2l_file_path = Path(build_cfg.get_src_code_dst_dir(), build_cfg.get_a2l_name())
replace_tab_verb(a2l_file_path) replace_tab_verb(a2l_file_path)

View File

@ -294,3 +294,106 @@ class HICore(ProblemLogger):
file_path = Path(src_dst_dir, self.FILE_NAME + extension) file_path = Path(src_dst_dir, self.FILE_NAME + extension)
with file_path.open(mode='w', encoding='utf-8') as file_handler: with file_path.open(mode='w', encoding='utf-8') as file_handler:
file_handler.writelines(content) file_handler.writelines(content)
class ZCCore(ProblemLogger):
"""A class holding ZC core configuration data."""
FILE_NAME = 'VcCoreSupplierAbstraction'
def __init__(self, project_cfg, unit_cfgs):
"""Parse the config files to an internal representation.
Args:
project_cfg (BuildProjConfig): Project configuration.
unit_cfgs (UnitConfigs): Unit definitions.
"""
super().__init__()
self._prj_cfg = project_cfg
self._unit_cfgs = unit_cfgs
self.project_dtcs = self._get_project_dtcs()
def _get_project_dtcs(self):
"""Return a set with DTCs in the currently included SW-Units.
Returns:
(set): Set of DTCs in the currently included SW-Units.
"""
project_dtcs = set()
unit_cfg = self._unit_cfgs.get_per_unit_cfg()
for unit, data in unit_cfg.items():
event_data = data.get('core', {}).get('Events')
if event_data is None:
self.critical(f'No "core" or "core.Events" key in unit config for {unit}.')
continue
project_dtcs |= set(event_data.keys())
return project_dtcs
def get_diagnostic_trouble_codes(self, event_data):
"""Return a set of DTCs appearing in both the project and the project yaml file.
Args:
event_data (dict): Diagnositc event data.
Returns:
(dict): Dict of DTCs, project yaml dict where the keys also appear in the project.
"""
yaml_dtcs = set(event_data.keys())
dtcs_not_in_yaml = self.project_dtcs - yaml_dtcs
for key in dtcs_not_in_yaml:
self.warning(f'Ignoring DTC {key} since it does not appear in the project diagnostics yaml file.')
valid_dtcs = {}
supported_operations = {"SetEventStatus"}
for dtc_name, dtc_data in event_data.items():
if dtc_name not in self.project_dtcs:
self.warning(f'Ignoring DTC {dtc_name}, not defined in any model.')
continue
valid_dtcs[dtc_name] = dtc_data
operations = set(dtc_data["operations"])
if not operations.issubset(supported_operations):
self.warning(f'Ignoring unsupported operations {supported_operations - operations} for DTC {dtc_name}.')
valid_dtcs[dtc_name]["operations"] = list(operations & supported_operations)
return valid_dtcs
def get_header_content(self):
"""Get content for the DTC header file.
Returns:
(list(str)): List of lines to write to the DTC header file.
"""
name = self._prj_cfg.get_swc_name()
header_guard = f'{self.FILE_NAME.upper()}_H'
header = [
f'#ifndef {header_guard}\n',
f'#define {header_guard}\n',
'\n',
'/* Core API Supplier Abstraction */\n',
'\n',
'#include "tl_basetypes.h"\n',
f'#include "Rte_{name}.h"\n',
'\n'
]
footer = [f'\n#endif /* {header_guard} */\n']
body = [
'/* enum EventStatus {passed=0, failed=1, prepassed=2, prefailed=3} */\n',
'#define Dem_SetEventStatus(EventName, EventStatus)',
' ',
'Rte_Call_Event_##EventName##_SetEventStatus(EventStatus)\n'
]
return header + body + footer
def generate_dtc_files(self):
"""Generate required ZC Core header files.
Only use for some projects, which doesn't copy static code."""
file_contents = {
'.h': self.get_header_content()
}
src_dst_dir = self._prj_cfg.get_src_code_dst_dir()
for extension, content in file_contents.items():
file_path = Path(src_dst_dir, self.FILE_NAME + extension)
with file_path.open(mode='w', encoding='utf-8') as file_handler:
file_handler.writelines(content)

View File

@ -602,3 +602,181 @@ class HIDIDs(ProblemLogger):
file_path = Path(src_dst_dir, self.FILE_NAME + extension) file_path = Path(src_dst_dir, self.FILE_NAME + extension)
with file_path.open(mode='w', encoding='utf-8') as file_handler: with file_path.open(mode='w', encoding='utf-8') as file_handler:
file_handler.writelines(content) file_handler.writelines(content)
class ZCDIDs(ProblemLogger):
"""A class for handling of ZC DID definitions."""
FILE_NAME = 'VcDIDAPI'
def __init__(self, build_cfg, unit_cfgs):
"""Init.
Args:
build_cfg (BuildProjConfig): Project configuration
unit_cfgs (UnitConfigs): Unit definitions
"""
super().__init__()
self._build_cfg = build_cfg
self._unit_cfgs = unit_cfgs
self._valid_dids = None
self.project_dids = self._get_project_dids()
@property
def valid_dids(self):
return self._valid_dids
@valid_dids.setter
def valid_dids(self, yaml_dids):
"""Return a set of DIDs appearing in both the project and the project yaml file.
Args:
yaml_dids (dict): DIDs listed in the DID configuration yaml file.
Returns:
valid_dids (dict): Validated DIDs listed in both DID configuration yaml file as well as project.
"""
self._valid_dids = {}
yaml_did_names = set(yaml_dids.keys())
dids_not_in_project = yaml_did_names - set(self.project_dids.keys())
for did in dids_not_in_project:
self.warning(f'Ignoring DID {did}, not defined in any model.')
supported_operations = set(self.get_operation_data().keys())
for did, did_data in self.project_dids.items():
if did not in yaml_dids:
self.warning(f'DID {did} not defined in project diagnostics yaml file.')
continue
yaml_operations = set(yaml_dids[did]['operations'].keys())
if not yaml_operations.issubset(supported_operations):
self.warning(f'Ignoring unsupported operations {yaml_operations - supported_operations} for DID {did}.')
operations = {
"operations": {
operation: {} for operation in supported_operations
}
}
self._valid_dids[did] = deep_dict_update(did_data, operations)
def _get_project_dids(self):
"""Return a dict with DIDs defined in the project.
Throws a critical error if something goes wrong.
Returns:
project_dids (dict): a dict with all dids in the project.
"""
get_did_error_messages, project_dids = get_dids_in_prj(self._unit_cfgs)
if get_did_error_messages:
self.critical('\n'.join(get_did_error_messages))
return {}
return project_dids
def get_operation_data(self, operation=None):
"""Get operation function data of supported operations.
Args:
operation (str): Operation to get data for.
Returns:
(dict): Operation function data.
"""
operation_data = {
'ReadData': {
'declaration': 'UInt8 Run_{did_name}_ReadData({data_type} *Data)',
'body': (
'{{\n'
' *Data = {did_name};\n'
' return 0U;\n'
'}}\n'
),
}
}
if operation is None:
return operation_data
return operation_data[operation]
def _get_header_file_content(self):
"""Get content for the DID API header file.
Returns:
(list(str)): List of lines to write to DID API header file.
"""
name = self._build_cfg.get_swc_name()
header_guard = f'{self.FILE_NAME.upper()}_H'
header = [
f'#ifndef {header_guard}\n',
f'#define {header_guard}\n',
'\n',
'#include "tl_basetypes.h"\n',
f'#include "Rte_{name}.h"\n',
'\n'
]
footer = [f'\n#endif /* {header_guard} */\n']
if not self.valid_dids:
return header + footer
body = [f'#include "{build_defs.PREDECL_DISP_ASIL_D_START}"\n']
for did_data in self.valid_dids.values():
define = did_data["class"].split('/')[-1] # E.q. for ASIL D it is ASIL_D/CVC_DISP_ASIL_D
body.append(f'extern {define} {did_data["type"]} {did_data["name"]};\n')
body.append(f'#include "{build_defs.PREDECL_DISP_ASIL_D_END}"\n')
body.append(f'\n#include "{build_defs.PREDECL_CODE_ASIL_D_START}"\n')
for did_data in self.valid_dids.values():
body.extend(
[
self.get_operation_data(operation)['declaration'].format(
did_name=did_data["name"],
data_type=did_data["type"]
) + ';\n' for operation in did_data["operations"]
]
)
body.append(f'#include "{build_defs.PREDECL_CODE_ASIL_D_END}"\n')
return header + body + footer
def _get_source_file_content(self):
"""Get content for the DID API source file.
Returns:
(list(str)): List of lines to write to DID API source file.
"""
header = [
f'#include "{self.FILE_NAME}.h"\n',
'\n'
]
if not self.valid_dids:
return header
body = [f'#include "{build_defs.CVC_CODE_ASIL_D_START}"\n']
for did_data in self.valid_dids.values():
for operation in did_data["operations"]:
body.append(
self.get_operation_data(operation)['declaration'].format(
did_name=did_data["name"],
data_type=did_data["type"]
) + '\n' + self.get_operation_data(operation)['body'].format(did_name=did_data["name"])
)
body.append(f'#include "{build_defs.CVC_CODE_ASIL_D_END}"\n')
return header + body
def generate_did_files(self):
"""Generate required DID API files.
Only use for some projects, which doesn't copy static code."""
if self.valid_dids is None:
self.critical('Valid DIDs not set. Cannot generate DID files.')
return
file_contents = {
'.h': self._get_header_file_content(),
'.c': self._get_source_file_content()
}
src_dst_dir = self._build_cfg.get_src_code_dst_dir()
for extension, content in file_contents.items():
file_path = Path(src_dst_dir, self.FILE_NAME + extension)
with file_path.open(mode='w', encoding='utf-8') as file_handler:
file_handler.writelines(content)

View File

@ -142,7 +142,7 @@ class ZCAL(BaseApplication):
raw = self.read_translation_files(definition) raw = self.read_translation_files(definition)
self.composition_spec = { self.composition_spec = {
key: raw[key] for key in ("port_interfaces", "data_types", "calls") if key in raw key: raw[key] for key in ("port_interfaces", "data_types", "calls", "Diagnostics") if key in raw
} }
ports_info = {} ports_info = {}
for port_name, port in raw.get("ports", {}).items(): for port_name, port in raw.get("ports", {}).items():

View File

@ -15,13 +15,15 @@ from pybuild.zone_controller.calibration import ZoneControllerCalibration as ZCC
class CompositionYaml(ProblemLogger): class CompositionYaml(ProblemLogger):
"""Class for handling ZoneController composition yaml generation.""" """Class for handling ZoneController composition yaml generation."""
def __init__(self, build_cfg, composition_spec, unit_cfg, a2l_axis_data): def __init__(self, build_cfg, composition_spec, unit_cfg, zc_core, zc_dids, a2l_axis_data):
"""Init. """Init.
Args: Args:
build_cfg (BuildProjConfig): Object with build configuration settings. build_cfg (BuildProjConfig): Object with build configuration settings.
composition_spec (dict): Dict with port interface information. composition_spec (dict): Dict with port interface information.
unit_cfg (UnitConfig): Object with unit configurations. unit_cfg (UnitConfig): Object with unit configurations.
zc_core (ZCCore): Object with zone controller diagnositic event information.
zc_dids (ZCDIDs): Object with zone controller diagnostic DID information.
a2l_axis_data (dict): Dict with characteristic axis data from A2L file. a2l_axis_data (dict): Dict with characteristic axis data from A2L file.
""" """
self.tl_to_autosar_base_types = { self.tl_to_autosar_base_types = {
@ -38,6 +40,8 @@ class CompositionYaml(ProblemLogger):
self.unit_src_dirs = build_cfg.get_unit_src_dirs() self.unit_src_dirs = build_cfg.get_unit_src_dirs()
self.composition_spec = composition_spec self.composition_spec = composition_spec
self.unit_cfg = unit_cfg self.unit_cfg = unit_cfg
self.zc_core = zc_core
self.zc_dids = zc_dids
self.a2l_axis_data = a2l_axis_data self.a2l_axis_data = a2l_axis_data
base_data_types = self.get_base_data_types() # Might not be necessary in the long run base_data_types = self.get_base_data_types() # Might not be necessary in the long run
self.data_types = { self.data_types = {
@ -294,6 +298,47 @@ class CompositionYaml(ProblemLogger):
return trigger_signal return trigger_signal
def _get_diagnostic_event_info(self, event_dict):
"""Get diagnostic event information from an even dictionary.
Args:
event_dict (dict): Dict with event information.
Returns:
valid_event_dict (dict): Dict with diagnostic event information supported by yaml2arxml script.
"""
valid_event_dict = {}
dtcs = self.zc_core.get_diagnostic_trouble_codes(event_dict)
for dtc_name, dtc_data in dtcs.items():
valid_event_dict[dtc_name] = {"operations": dtc_data["operations"], "runnable": dtc_data["runnable"]}
return valid_event_dict
def _get_diagnostic_did_info(self, did_dict):
"""Get diagnostic DID information from a DID dictionary.
NOTE: This function sets the valid_dids property of the ZCDIDs object.
Args:
did_dict (dict): Dict with DID information.
Returns:
valid_did_dict (dict): Dict with diagnostic DID information supported by yaml2arxml script.
"""
valid_did_dict = {}
self.zc_dids.valid_dids = did_dict
for did_name, did_data in self.zc_dids.valid_dids.items():
valid_did_dict[did_name] = {"operations": did_data["operations"]}
return valid_did_dict
def _get_diagnostic_info(self):
"""Get diagnostic information from composition spec.
Returns:
(dict): Dict containing diagnostic information.
"""
diag_dict = self.composition_spec.get("Diagnostics", {})
return {
"events": self._get_diagnostic_event_info(diag_dict.get("events", {})),
"dids": self._get_diagnostic_did_info(diag_dict.get("dids", {})),
}
def _get_ports_info(self): def _get_ports_info(self):
"""Creates a dict containing port information. """Creates a dict containing port information.
@ -385,6 +430,7 @@ class CompositionYaml(ProblemLogger):
swcs[software_component_name]["shared"] = self.cal_class_info["autosar"]["class_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"] 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]["ports"] = self._get_ports_info()
swcs[software_component_name]["diagnostics"] = self._get_diagnostic_info()
return swcs, data_types return swcs, data_types
def _get_variables(self): def _get_variables(self):

View File

@ -0,0 +1,155 @@
# Copyright 2024 Volvo Car Corporation
# Licensed under Apache 2.0.
"""Unit test data for pybuild.dids.ZCDIDs."""
dummy_project_dids = {
'dummy_did_one': {
'handle': 'VcDummy/VcDummy/Subsystem/VcDummy/VcDummy/1_VcDummy/Rel',
'name': 'dummy_did_one',
'configs': '((ALWAYS_ACTIVE))',
'description': 'Dummy DID',
'type': 'UInt8',
'unit': '',
'offset': '',
'lsb': '',
'min': '-',
'max': '-',
'class': 'ASIL_D/CVC_DISP_ASIL_D'
},
'dummy_did_two': {
'handle': 'VcDummyTwo/VcDummyTwo/Subsystem/VcDummyTwo/VcDummyTwo/1_VcDummyTwo/Rel',
'name': 'dummy_did_two',
'configs': '((ALWAYS_ACTIVE))',
'description': 'Dummy DID number 2',
'type': 'UInt8',
'unit': '',
'offset': '',
'lsb': '',
'min': '-',
'max': '-',
'class': 'ASIL_D/CVC_DISP_ASIL_D'
}
}
valid_dids = {
'dummy_did_one': {
'operations': {
'ReadData': {},
},
},
'dummy_did_two': {
'operations': {
'ReadData': {},
},
}
}
bad_valid_dids = {
'dummy_did_one': {
'operations': {
'ReadData': {'random_data': {}},
'WriteData': {'random_data': {}},
'ShortTermAdjustment': {'random_data': {}},
'ReturnControlToECU': {'random_data': {}},
},
},
'dummy_did_two': {
'operations': {
'ReadData': {'random_data': {}},
'WriteData': {'random_data': {}},
'ShortTermAdjustment': {'random_data': {}},
'ReturnControlToECU': {'random_data': {}},
},
},
'dummy_did_three': {
'operations': {
'ReadData': {'random_data': {}},
'WriteData': {'random_data': {}},
'ShortTermAdjustment': {'random_data': {}},
'ReturnControlToECU': {'random_data': {}},
},
},
}
test_valid_dids_setter_expected = {
'dummy_did_one': {
'handle': 'VcDummy/VcDummy/Subsystem/VcDummy/VcDummy/1_VcDummy/Rel',
'name': 'dummy_did_one',
'configs': '((ALWAYS_ACTIVE))',
'description': 'Dummy DID',
'type': 'UInt8',
'unit': '',
'offset': '',
'lsb': '',
'min': '-',
'max': '-',
'class': 'ASIL_D/CVC_DISP_ASIL_D',
'operations': {
'ReadData': {},
},
},
'dummy_did_two': {
'handle': 'VcDummyTwo/VcDummyTwo/Subsystem/VcDummyTwo/VcDummyTwo/1_VcDummyTwo/Rel',
'name': 'dummy_did_two',
'configs': '((ALWAYS_ACTIVE))',
'description': 'Dummy DID number 2',
'type': 'UInt8',
'unit': '',
'offset': '',
'lsb': '',
'min': '-',
'max': '-',
'class': 'ASIL_D/CVC_DISP_ASIL_D',
'operations': {
'ReadData': {},
},
},
}
test_get_operation_data_expected = {
'ReadData': {
'declaration': 'UInt8 Run_{did_name}_ReadData({data_type} *Data)',
'body': (
'{{\n'
' *Data = {did_name};\n'
' return 0U;\n'
'}}\n'
),
}
}
TEST_GET_HEADER_FILE_CONTENT_EXPECTED = (
'#ifndef VCDIDAPI_H\n'
'#define VCDIDAPI_H\n'
'\n'
'#include "tl_basetypes.h"\n'
'#include "Rte_DUMMY.h"\n'
'\n'
'#include "PREDECL_DISP_ASIL_D_START.h"\n'
'extern CVC_DISP_ASIL_D UInt8 dummy_did_one;\n'
'extern CVC_DISP_ASIL_D UInt8 dummy_did_two;\n'
'#include "PREDECL_DISP_ASIL_D_END.h"\n'
'\n#include "PREDECL_CODE_ASIL_D_START.h"\n'
'UInt8 Run_dummy_did_one_ReadData(UInt8 *Data);\n'
'UInt8 Run_dummy_did_two_ReadData(UInt8 *Data);\n'
'#include "PREDECL_CODE_ASIL_D_END.h"\n'
'\n#endif /* VCDIDAPI_H */\n'
)
TEST_GET_SOURCE_FILE_CONTENT_EXPECTED = (
'#include "VcDIDAPI.h"\n'
'\n'
'#include "CVC_CODE_ASIL_D_START.h"\n'
'UInt8 Run_dummy_did_one_ReadData(UInt8 *Data)\n'
'{\n'
' *Data = dummy_did_one;\n'
' return 0U;\n'
'}\n'
'UInt8 Run_dummy_did_two_ReadData(UInt8 *Data)\n'
'{\n'
' *Data = dummy_did_two;\n'
' return 0U;\n'
'}\n'
'#include "CVC_CODE_ASIL_D_END.h"\n'
)

View File

@ -26,6 +26,7 @@ expected_result = {
"accesses": composition_yaml_setup.base_accesses "accesses": composition_yaml_setup.base_accesses
} }
}, },
"diagnostics": {"events": {}, "dids": {}},
"static": composition_yaml_setup.base_static, "static": composition_yaml_setup.base_static,
"shared": composition_yaml_setup.base_shared, "shared": composition_yaml_setup.base_shared,
"ports": { "ports": {

View File

@ -188,6 +188,7 @@ expected_result = {
] ]
} }
}, },
"diagnostics": {"events": {}, "dids": {}},
"static": composition_yaml_setup.base_static, "static": composition_yaml_setup.base_static,
"shared": { "shared": {
"ctestName_SC_TriggerReadRteCData": { "ctestName_SC_TriggerReadRteCData": {

View File

@ -32,6 +32,7 @@ expected_result = {
"accesses": composition_yaml_setup.base_accesses "accesses": composition_yaml_setup.base_accesses
} }
}, },
"diagnostics": {"events": {}, "dids": {}},
"static": composition_yaml_setup.base_static, "static": composition_yaml_setup.base_static,
"shared": composition_yaml_setup.base_shared, "shared": composition_yaml_setup.base_shared,
"ports": { "ports": {

View File

@ -31,6 +31,7 @@ expected_result = {
"accesses": composition_yaml_setup.base_accesses "accesses": composition_yaml_setup.base_accesses
} }
}, },
"diagnostics": {"events": {}, "dids": {}},
"static": composition_yaml_setup.base_static, "static": composition_yaml_setup.base_static,
"shared": composition_yaml_setup.base_shared, "shared": composition_yaml_setup.base_shared,
"ports": { "ports": {

View File

@ -0,0 +1,81 @@
# Copyright 2024 Volvo Car Corporation
# Licensed under Apache 2.0.
"""Unit test data for pybuild.zone_controller.composition_yaml with DIDs."""
from test_data.zone_controller.test_composition_yaml import composition_yaml_setup
diagnostics = {
"dids": {
"DID1": {
"identifier": "34EE",
"numberOfParameters": 1,
"totalNumberOfBytes": 2,
"operations": {
"ReadData": {
"securityAccess": "",
"readLocalVariables": ["MyIRV"],
"writtenLocalVariables": ["MyIRV"],
},
"WriteData": {
"securityAccess": "",
"readLocalVariables": ["MyIRV3"],
"writtenLocalVariables": ["MyIRV3"],
},
"ShortTermAdjustment": {
"securityAccess": "",
"readLocalVariables": ["MyIRV4"],
"writtenLocalVariables": ["MyIRV4"],
},
"ReturnControlToECU": {
"securityAccess": "",
"readLocalVariables": ["MyIRV5"],
"writtenLocalVariables": ["MyIRV6"],
},
},
},
},
}
expected_result = {
"SoftwareComponents": {
"testName_SC": {
"type": "SWC",
"template": "ARTCSC",
"runnables": {
"AR_prefix_VcExtINI": {
"type": "INIT",
"accesses": composition_yaml_setup.base_accesses
},
"AR_prefix_testRunnable": {
"period": 10,
"type": "PERIODIC",
"accesses": composition_yaml_setup.base_accesses,
},
"AR_testName_SC_ZcCalibrationStep": {
"period": 0.1,
"type": "PERIODIC",
"accesses": composition_yaml_setup.base_accesses
}
},
"diagnostics": {
"events": {},
"dids": {
"DID1": {
"operations": {
"ReadData": {},
},
},
}
},
"static": composition_yaml_setup.base_static,
"shared": composition_yaml_setup.base_shared,
"ports": {
"GlobSignNme": {"direction": "IN", "interface": "PIGlobSignNme"},
}
}
},
"DataTypes": composition_yaml_setup.base_data_types,
"PortInterfaces": composition_yaml_setup.base_port_interfaces,
"ExternalFiles": composition_yaml_setup.base_configuration
}

View File

@ -0,0 +1,71 @@
# Copyright 2024 Volvo Car Corporation
# Licensed under Apache 2.0.
"""Unit test data for pybuild.zone_controller.composition_yaml with DTCs."""
from test_data.zone_controller.test_composition_yaml import composition_yaml_setup
diagnostics = {
"events": {
"DTC1": {
"operations": ["SetEventStatus", "SomethingElse"],
"runnable": ["dummy"],
"identifier": "34EA",
"ThresholdUnconfirmed": 1,
"StepDown": 0,
"JumpDown": True,
"JumpDownInit": 1,
"StepUp": 1,
"JumpUp": True,
"JumpUpInit": 127,
"TestFailedLimit": 0,
"TestPassedLimit": 0,
"AgedDTCLimit": 0,
"ConfirmedDTCLimit": 1,
"DTCEventPriority": 1,
"OperationCycle": "Battery",
},
},
}
expected_result = {
"SoftwareComponents": {
"testName_SC": {
"type": "SWC",
"template": "ARTCSC",
"runnables": {
"AR_prefix_VcExtINI": {
"type": "INIT",
"accesses": composition_yaml_setup.base_accesses
},
"AR_prefix_testRunnable": {
"period": 10,
"type": "PERIODIC",
"accesses": composition_yaml_setup.base_accesses,
},
"AR_testName_SC_ZcCalibrationStep": {
"period": 0.1,
"type": "PERIODIC",
"accesses": composition_yaml_setup.base_accesses
}
},
"diagnostics": {
"events": {
"DTC1": {
"operations": ["SetEventStatus"],
"runnable": ["dummy"],
},
},
"dids": {}
},
"static": composition_yaml_setup.base_static,
"shared": composition_yaml_setup.base_shared,
"ports": {
"GlobSignNme": {"direction": "IN", "interface": "PIGlobSignNme"},
}
}
},
"DataTypes": composition_yaml_setup.base_data_types,
"PortInterfaces": composition_yaml_setup.base_port_interfaces,
"ExternalFiles": composition_yaml_setup.base_configuration
}

View File

@ -7,7 +7,7 @@ from unittest.mock import MagicMock
from pathlib import Path from pathlib import Path
from pybuild.build_proj_config import BuildProjConfig from pybuild.build_proj_config import BuildProjConfig
from pybuild.unit_configs import UnitConfigs from pybuild.unit_configs import UnitConfigs
from pybuild.core import Core, HICore from pybuild.core import Core, HICore, ZCCore
from .core_cnfg import CORE_CFG from .core_cnfg import CORE_CFG
@ -168,7 +168,6 @@ class TestHICore(unittest.TestCase):
def test_get_source_content(self): def test_get_source_content(self):
"""Test get_source_content.""" """Test get_source_content."""
self.maxDiff = None
self.hi_core.diagnostic_trouble_codes = self.dummy_yaml_dtcs self.hi_core.diagnostic_trouble_codes = self.dummy_yaml_dtcs
result = self.hi_core.get_source_content() result = self.hi_core.get_source_content()
expected = [ expected = [
@ -206,3 +205,107 @@ class TestHICore(unittest.TestCase):
'\n' '\n'
] ]
self.assertListEqual(expected, result) self.assertListEqual(expected, result)
class TestZCCore(unittest.TestCase):
"""Test case for testing class ZCCore."""
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'
unit_configs = MagicMock()
unit_configs.get_per_unit_cfg.return_value = {}
self.zc_core = ZCCore(project_config, unit_configs)
self.zc_core.project_dtcs = {'VcEventOne', 'VcEventTwo', 'VcEventThree'}
self.dummy_yaml_dtcs = {
'VcEventOne': {
'operations': ["SetEventStatus"],
'random_data': {},
},
'VcEventTwo': {
'operations': ["SetEventStatus"],
'random_data': {},
},
'VcEventThree': {
'operations': ["SetEventStatus"],
'random_data': {},
},
}
def test_get_diagnostic_trouble_codes(self):
"""Test the get_diagnostic_trouble_codes function."""
result = self.zc_core.get_diagnostic_trouble_codes(self.dummy_yaml_dtcs)
self.assertEqual(result, self.dummy_yaml_dtcs)
def test_get_diagnostic_trouble_codes_missing_in_project(self):
"""Test the get_diagnostic_trouble_codes function, when a DTC is in yaml but not project."""
self.zc_core.project_dtcs = {'VcEventOne', 'VcEventTwo'}
result = self.zc_core.get_diagnostic_trouble_codes(self.dummy_yaml_dtcs)
expected = {
'VcEventOne': {
'operations': ["SetEventStatus"],
'random_data': {},
},
'VcEventTwo': {
'operations': ["SetEventStatus"],
'random_data': {},
},
}
self.assertEqual(result, expected)
def test_get_diagnostic_trouble_codes_missing_in_yaml(self):
"""Test the get_diagnostic_trouble_codes function, when a DTC is in project but not in yaml."""
dummy_yaml_dtcs = {
'VcEventOne': {
'operations': ["SetEventStatus"],
'random_data': {},
},
'VcEventTwo': {
'operations': ["SetEventStatus"],
'random_data': {},
},
}
result = self.zc_core.get_diagnostic_trouble_codes(dummy_yaml_dtcs)
self.assertEqual(result, dummy_yaml_dtcs)
def test_get_diagnostic_trouble_codes_unsupported_operation(self):
"""Test the get_diagnostic_trouble_codes function, when an operation is not supported."""
dummy_yaml_dtcs = {
'VcEventOne': {
'operations': ["SetEventStatus"],
'random_data': {},
},
'VcEventTwo': {
'operations': ["SetEventStatus"],
'random_data': {},
},
'VcEventThree': {
'operations': ["SetEventStatus", "SomeUnsupportedOperation"],
'random_data': {},
},
}
result = self.zc_core.get_diagnostic_trouble_codes(dummy_yaml_dtcs)
self.assertEqual(result, self.dummy_yaml_dtcs)
def test_get_header_content(self):
"""Test get_header_content."""
result = self.zc_core.get_header_content()
expected = [
'#ifndef VCCORESUPPLIERABSTRACTION_H\n',
'#define VCCORESUPPLIERABSTRACTION_H\n',
'\n',
'/* Core API Supplier Abstraction */\n',
'\n',
'#include "tl_basetypes.h"\n',
'#include "Rte_DUMMY.h"\n',
'\n',
'/* enum EventStatus {passed=0, failed=1, prepassed=2, prefailed=3} */\n',
'#define Dem_SetEventStatus(EventName, EventStatus)',
' ',
'Rte_Call_Event_##EventName##_SetEventStatus(EventStatus)\n',
'\n#endif /* VCCORESUPPLIERABSTRACTION_H */\n',
]
self.assertListEqual(expected, result)

View File

@ -9,7 +9,16 @@ from unittest.mock import patch
from unittest.mock import mock_open from unittest.mock import mock_open
from pathlib import Path from pathlib import Path
from pybuild.build_proj_config import BuildProjConfig from pybuild.build_proj_config import BuildProjConfig
from pybuild.dids import DIDs, HIDIDs from pybuild.dids import DIDs, HIDIDs, ZCDIDs
from test_data.pybuild.test_dids.zc_dids import (
dummy_project_dids,
valid_dids,
bad_valid_dids,
test_valid_dids_setter_expected,
test_get_operation_data_expected,
TEST_GET_HEADER_FILE_CONTENT_EXPECTED,
TEST_GET_SOURCE_FILE_CONTENT_EXPECTED,
)
class TestDIDsTL(unittest.TestCase): class TestDIDsTL(unittest.TestCase):
@ -966,3 +975,63 @@ class TestHIDIDs(unittest.TestCase):
'\n' '\n'
] ]
self.assertListEqual(expected, result) self.assertListEqual(expected, result)
class TestZCDIDs(unittest.TestCase):
"""Test case for testing ZCDIDs class."""
def setUp(self):
build_cfg = MagicMock()
build_cfg.get_swc_name.return_value = 'DUMMY'
unit_cfg = MagicMock()
self.zc_dids = ZCDIDs(build_cfg, unit_cfg)
self.zc_dids.project_dids = dummy_project_dids
self.zc_dids.valid_dids = valid_dids
def test_valid_dids_setter(self):
"""Test setting property ZCDIDs.valid_dids."""
self.zc_dids.valid_dids = {}
self.assertDictEqual(self.zc_dids.valid_dids, {})
self.zc_dids.valid_dids = bad_valid_dids
self.assertDictEqual(self.zc_dids.valid_dids, test_valid_dids_setter_expected)
def test_get_operation_data(self):
"""Test ZCDIDs.get_operation_data."""
result = self.zc_dids.get_operation_data()
self.assertDictEqual(result, test_get_operation_data_expected)
def test_get_header_file_content_no_dids(self):
"""Test ZCDIDs._get_header_file_content without DIDs."""
self.zc_dids.valid_dids = {}
result = self.zc_dids._get_header_file_content()
expected = [
'#ifndef VCDIDAPI_H\n',
'#define VCDIDAPI_H\n',
'\n',
'#include "tl_basetypes.h"\n',
'#include "Rte_DUMMY.h"\n',
'\n',
'\n#endif /* VCDIDAPI_H */\n'
]
self.assertListEqual(expected, result)
def test_get_source_file_content_no_dids(self):
"""Test ZCDIDs._get_source_file_content without DIDs."""
self.zc_dids.valid_dids = {}
result = self.zc_dids._get_source_file_content()
expected = [
'#include "VcDIDAPI.h"\n',
'\n'
]
self.assertListEqual(expected, result)
def test_get_header_file_content(self):
"""Test ZCDIDs._get_header_file_content."""
result = ''.join(self.zc_dids._get_header_file_content())
self.assertEqual(result, TEST_GET_HEADER_FILE_CONTENT_EXPECTED)
def test_get_source_file_content(self):
"""Test ZCDIDs._get_source_file_content."""
result = ''.join(self.zc_dids._get_source_file_content())
self.assertEqual(result, TEST_GET_SOURCE_FILE_CONTENT_EXPECTED)

View File

@ -10,6 +10,8 @@ from pathlib import Path
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from pybuild.build_proj_config import BuildProjConfig from pybuild.build_proj_config import BuildProjConfig
from pybuild.core import ZCCore
from pybuild.dids import ZCDIDs
from pybuild.unit_configs import UnitConfigs from pybuild.unit_configs import UnitConfigs
from pybuild.zone_controller.composition_yaml import CompositionYaml from pybuild.zone_controller.composition_yaml import CompositionYaml
from test_data.zone_controller.test_composition_yaml import ( from test_data.zone_controller.test_composition_yaml import (
@ -18,6 +20,8 @@ from test_data.zone_controller.test_composition_yaml import (
composition_yaml_with_a2l_axis_data, composition_yaml_with_a2l_axis_data,
composition_yaml_with_calls_all_fields, composition_yaml_with_calls_all_fields,
composition_yaml_with_calls_no_optional_fields, composition_yaml_with_calls_no_optional_fields,
composition_yaml_with_dids,
composition_yaml_with_dtcs,
) )
SRC_DIR = Path(__file__).parent SRC_DIR = Path(__file__).parent
@ -54,6 +58,12 @@ class TestCompositionYaml(unittest.TestCase):
composition_yaml_setup.get_per_cfg_unit_cfg_return_value composition_yaml_setup.get_per_cfg_unit_cfg_return_value
) )
with patch.object(ZCCore, "_get_project_dtcs", return_value=set()):
self.zc_core = ZCCore(self.build_cfg, self.unit_cfg)
with patch.object(ZCDIDs, "_get_project_dids", return_value={}):
self.zc_dids = ZCDIDs(self.build_cfg, self.unit_cfg)
self.zc_spec = copy.deepcopy(composition_yaml_setup.zc_spec) self.zc_spec = copy.deepcopy(composition_yaml_setup.zc_spec)
self.calibration_definitions = copy.deepcopy(composition_yaml_setup.calibration_definitions) self.calibration_definitions = copy.deepcopy(composition_yaml_setup.calibration_definitions)
@ -63,7 +73,9 @@ class TestCompositionYaml(unittest.TestCase):
"_get_all_calibration_definitions", "_get_all_calibration_definitions",
return_value=self.calibration_definitions return_value=self.calibration_definitions
): ):
self.composition_yaml = CompositionYaml(self.build_cfg, self.zc_spec, self.unit_cfg, {}) self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
)
# Common expected results variables # Common expected results variables
self.base_configuration = copy.deepcopy(composition_yaml_setup.base_configuration) self.base_configuration = copy.deepcopy(composition_yaml_setup.base_configuration)
@ -103,7 +115,9 @@ class TestCompositionYaml(unittest.TestCase):
calibration_definitions = \ calibration_definitions = \
self.calibration_definitions + composition_yaml_with_a2l_axis_data.calibration_definitions self.calibration_definitions + composition_yaml_with_a2l_axis_data.calibration_definitions
with patch.object(CompositionYaml, "_get_all_calibration_definitions", return_value=calibration_definitions): with patch.object(CompositionYaml, "_get_all_calibration_definitions", return_value=calibration_definitions):
self.composition_yaml = CompositionYaml(self.build_cfg, self.zc_spec, self.unit_cfg, a2l_axis_data) self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, a2l_axis_data
)
result = self.composition_yaml.gather_yaml_info() result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml_with_a2l_axis_data.expected_result, result) self.assertDictEqual(composition_yaml_with_a2l_axis_data.expected_result, result)
@ -123,7 +137,9 @@ class TestCompositionYaml(unittest.TestCase):
"_get_all_calibration_definitions", "_get_all_calibration_definitions",
return_value=self.calibration_definitions return_value=self.calibration_definitions
): ):
self.composition_yaml = CompositionYaml(self.build_cfg, self.zc_spec, self.unit_cfg, {}) self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
)
result = self.composition_yaml.gather_yaml_info() result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml_with_calls_all_fields.expected_result, result) self.assertDictEqual(composition_yaml_with_calls_all_fields.expected_result, result)
@ -140,10 +156,42 @@ class TestCompositionYaml(unittest.TestCase):
"_get_all_calibration_definitions", "_get_all_calibration_definitions",
return_value=self.calibration_definitions return_value=self.calibration_definitions
): ):
self.composition_yaml = CompositionYaml(self.build_cfg, self.zc_spec, self.unit_cfg, {}) self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
)
result = self.composition_yaml.gather_yaml_info() result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml_with_calls_no_optional_fields.expected_result, result) self.assertDictEqual(composition_yaml_with_calls_no_optional_fields.expected_result, result)
def test_composition_yaml_with_dids(self):
"""Checking that the dict is generated correctly, with DIDs."""
self.zc_dids.project_dids = {"DID1": {"dummy_data": {}}}
self.zc_spec["Diagnostics"] = composition_yaml_with_dids.diagnostics
with patch.object(
CompositionYaml,
"_get_all_calibration_definitions",
return_value=self.calibration_definitions
):
self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
)
result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml_with_dids.expected_result, result)
def test_composition_yaml_with_dtcs(self):
"""Checking that the dict is generated correctly, with DTCs."""
self.zc_core.project_dtcs = {"DTC1"}
self.zc_spec["Diagnostics"] = composition_yaml_with_dtcs.diagnostics
with patch.object(
CompositionYaml,
"_get_all_calibration_definitions",
return_value=self.calibration_definitions
):
self.composition_yaml = CompositionYaml(
self.build_cfg, self.zc_spec, self.unit_cfg, self.zc_core, self.zc_dids, {}
)
result = self.composition_yaml.gather_yaml_info()
self.assertDictEqual(composition_yaml_with_dtcs.expected_result, result)
def test_get_init_values_expecting_failure(self): def test_get_init_values_expecting_failure(self):
"""Test CompositionYaml.get_init_values with a non-existing calibration definition.""" """Test CompositionYaml.get_init_values with a non-existing calibration definition."""
self.composition_yaml.clear_log() self.composition_yaml.clear_log()