olindgre 2ece01e1d7 Make powertrain-build not overlap with pybuild in site-packages
Change-Id: I7b59f3f04f0f787d35db0b9389f295bf1ad24f56
2024-09-17 10:25:04 +02:00

300 lines
11 KiB
Python

# Copyright 2024 Volvo Car Corporation
# Licensed under Apache 2.0.
# -*- coding: utf-8 -*-
"""Module containing classes for VCC - Supplier signal interface."""
from powertrain_build import build_defs
from powertrain_build.types import byte_size_string, get_bitmask
from powertrain_build.a2l import A2l
from powertrain_build.problem_logger import ProblemLogger
class ExtVarBase(ProblemLogger):
"""Generate a2l- and c-files.
These which declares all variables in the interface that the
supplier platform writes to.
This is needed due to legacy handling of the interfaces between units.
Note that all variables sent from the VCC SPM to the platform should be declared in
the function that produces the signal!
"""
INPORT_INDEX = 0
OUTPORT_INDEX = 1
__data_type_size = {
"Float32": "4",
"UInt32": "4",
"Int32": "4",
"UInt16": "2",
"Int16": "2",
"UInt8": "1",
"Int8": "1",
"Bool": "1",
}
def __init__(self, variable_dict, prj_cfg, unit_cfg, user_defined_types, integrity_level=build_defs.ASIL_QM):
"""Constructor.
Args:
variable_dict (dict): dictionary with signal information.
prj_cfg (BuildProjConfig): Build project class holding where files should be stored.
user_defined_types (UserDefinedTypes): Class holding user defined data types.
integrity_level (str): integrity level of the unit from 'A' to 'D' or 'QM'.
"""
super().__init__()
self.set_integrity_level(integrity_level)
self._var_dict = variable_dict
self._ext_vars = {}
self._prj_cfg = prj_cfg
self._unit_cfg = unit_cfg
self._enumerations = user_defined_types.get_enumerations()
self._common_header_files = user_defined_types.common_header_files
def set_integrity_level(self, integrity_level):
"""Set integrity level of code generation.
Args:
integrity_level (str): integrity level of the unit from 'A' to 'D' or 'QM'
"""
self._disp_start = integrity_level["CVC"]["DISP"]["START"]
self._disp_end = integrity_level["CVC"]["DISP"]["END"]
self._decl_start = integrity_level["PREDECL"]["DISP"]["START"]
self._decl_end = integrity_level["PREDECL"]["DISP"]["END"]
def _get_byte_size_string(self, data_type):
"""Get byte size of a data type as string.
Enumeration byte sizes are derived from the underlying data type.
Args:
data_type (str): Data type.
Returns:
byte_size_string(powertrain_build.types.byte_size_string): Return result of
powertrain_build.types.byte_size_string.
"""
if data_type in self._enumerations:
return byte_size_string(self._enumerations[data_type]["underlying_data_type"])
return byte_size_string(data_type)
def _get_bitmask(self, data_type):
"""Get bitmask of a data type.
Enumeration bitmasks are derived from the underlying data type.
Args:
data_type (str): Data type.
Returns:
get_bitmask(powertrain_build.types.get_bitmask): Return result of powertrain_build.types.get_bitmask.
"""
if data_type in self._enumerations:
return get_bitmask(self._enumerations[data_type]["underlying_data_type"])
return get_bitmask(data_type)
def _restruct_input_data(self):
"""Restructure all the input variables per data-type.
This will be used for declaring the variables and generating the
A2L-file
"""
external_inports = {}
external_outports = {}
for external_port_type in self.EXTERNAL_INPORT_TYPES:
if external_port_type in self._var_dict:
for var, data in self._var_dict[external_port_type].items():
data_type_size = self._get_byte_size_string(data[self.TYPE_NAME])
external_inports.setdefault(data_type_size, {})[var] = data
for external_port_type in self.EXTERNAL_OUTPORT_TYPES:
if external_port_type in self._var_dict:
for var, data in self._var_dict[external_port_type].items():
data_type_size = self._get_byte_size_string(data[self.TYPE_NAME])
external_outports.setdefault(data_type_size, {})[var] = data
self._ext_vars = external_inports, external_outports
def _a2l_dict(self):
"""Return a dict defining all parameters for a2l-generation."""
res = {"vars": {}, "function": "VcExtVar"}
for inp in self.EXTERNAL_INPORT_TYPES:
if inp in self._var_dict:
for var, data in self._var_dict[inp].items():
if data[self.TYPE_NAME] in self._enumerations:
data_type = self._enumerations[data[self.TYPE_NAME]]["underlying_data_type"]
else:
data_type = data[self.TYPE_NAME]
resv = res["vars"]
resv.setdefault(var, {})["a2l_data"] = self.get_a2l_format(data)
resv[var]["array"] = []
resv[var]["function"] = ["VcEc"]
resv[var]["var"] = {"cvc_type": "CVC_DISP", "type": data_type, "var": var}
return res
def _generate_c_file(self, path):
"""Generate the c-file defining all the supplier input signals."""
header = path.with_suffix(".h").name
var_set = set()
with path.open("w") as fh_c:
fh_c.write(f'#include "{header}"\n')
fh_c.write(f'#include "{self._disp_start}"\n\n')
for data_type_s, ext_vars in self._ext_vars[self.INPORT_INDEX].items():
fh_c.write(f"/* Variables of size {data_type_s} bytes */\n\n")
for var in sorted(ext_vars.keys()):
data = ext_vars[var]
if var not in var_set:
fh_c.write(f"CVC_DISP {data[self.TYPE_NAME]} {var} = {data['init']};\n")
var_set.add(var)
fh_c.write("\n")
fh_c.write(f'\n#include "{self._disp_end}"\n')
self.info("Generated %s", path.name)
def _generate_h_file(self, path):
"""Generate header file externally declaring interface signals."""
filename = path.stem
guard = f"{filename.upper()}_H"
var_set = set()
with path.open("w") as fh_c:
fh_c.write(f"#ifndef {guard}\n")
fh_c.write(f"#define {guard}\n")
fh_c.write("#define CVC_DISP\n")
fh_c.write(self._unit_cfg.base_types_headers)
for common_header_file in self._common_header_files:
fh_c.write(f'#include "{common_header_file}"\n')
fh_c.write("\n")
fh_c.write(f'#include "{self._decl_start}"\n')
fh_c.write("/* VCC Inports */\n")
for data_type_s, ext_vars in self._ext_vars[self.INPORT_INDEX].items():
fh_c.write(f"/* Variables of size {data_type_s} bytes */\n\n")
for var in sorted(ext_vars.keys()):
if var not in var_set:
data = ext_vars[var]
fh_c.write(f"extern CVC_DISP {data[self.TYPE_NAME]} {var};\n")
var_set.add(var)
fh_c.write("\n")
fh_c.write("/* VCC Outports */\n")
for data_type_s, ext_vars in self._ext_vars[self.OUTPORT_INDEX].items():
fh_c.write(f"/* Variables of size {data_type_s} bytes */\n\n")
for var in sorted(ext_vars.keys()):
if var not in var_set:
data = ext_vars[var]
fh_c.write(f"extern CVC_DISP {data[self.TYPE_NAME]} {var};\n")
var_set.add(var)
fh_c.write("\n")
fh_c.write(f'#include "{self._decl_end}"\n')
fh_c.write("#endif\n")
self.info("Generated %s", path.name)
def generate_files(self, path):
"""Generate the c- and a2l-file for defining all the supplier input variables."""
self._restruct_input_data()
if not self._ext_vars[0] and not self._ext_vars[1]:
self.info(f"Skipping {path.name} as there were no corresponding vars.")
return
self._generate_c_file(path.with_suffix(".c"))
self._generate_h_file(path.with_suffix(".h"))
a2l_dict = self._a2l_dict()
a2l = A2l(a2l_dict, self._prj_cfg)
a2l.gen_a2l(path.with_suffix(".a2l"))
class ExtVarCsv(ExtVarBase):
"""Handles variable dicts from CSV files.
Variable dict shall have the following format and is generated by the
:doc:`CsvSignalInterfaces <signal_interfaces>` class::
{
'CAN-Input': {
'signal1': {
'IOType': 'd',
'description': 'Some description',
'init': 0,
'max': 1,
'min': 0,
'type': 'UInt8',
'unit': '-'
},
'signal2': {
...
}
},
'CAN-Output': {
'signal3': {
...
}
},
'xxx-Input': ...,
'xxx-Output': ...
}
"""
EXTERNAL_INPORT_TYPES = ["EMS-Input", "CAN-Input", "Private CAN-Input", "LIN-Input"]
EXTERNAL_OUTPORT_TYPES = ["EMS-Output", "CAN-Output", "Private CAN-Output", "LIN-Output"]
TYPE_NAME = "type"
def get_a2l_format(self, data):
"""Get a2l format.
Args:
data (dict): Data dictionary.
Returns:
dict: A2l format dictionary.
"""
return {
"bitmask": self._get_bitmask(data[self.TYPE_NAME]),
"description": data["description"],
"lsb": "2^0",
"max": data["max"],
"min": data["min"],
"offset": "0",
"unit": data["unit"],
"x_axis": None,
"y_axis": None,
}
class ExtVarYaml(ExtVarBase):
"""Handles variable dicts from Yaml files.
Variable dict shall have the following format and is generated by the
:doc:`YamlSignalInterfaces <signal_interfaces>` class::
{
'input': {
'sVcIhfa_D_WhlMotSysFrntLimnIndcn': {},
'sVcIhfa_D_WhlMotSysFrntModSts': {},
'sVcIhfa_I_WhlMotSysFrntIdc': {},
'sVcIhfa_U_UDcDcActHiSide1': {},
'sVcIhfa_U_WhlMotSysFrntUdc': {}
},
'output': {},
'status': {},
}
"""
EXTERNAL_INPORT_TYPES = ["input", "status"]
EXTERNAL_OUTPORT_TYPES = ["output"]
TYPE_NAME = "variable_type"
def get_a2l_format(self, data):
"""Get a2l format.
Args:
data (dict): Data dictionary.
Returns:
dict: A2l format dictionary.
"""
return {
"bitmask": self._get_bitmask(data[self.TYPE_NAME]),
"description": data["description"],
"lsb": "2^0",
"max": data["range"]["max"],
"min": data["range"]["min"],
"offset": "0",
"unit": data["unit"],
"x_axis": None,
"y_axis": None,
}