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

250 lines
9.3 KiB
Python

# Copyright 2024 Volvo Car Corporation
# Licensed under Apache 2.0.
"""Module to handle the Simulink interface."""
from powertrain_build.lib import logger
LOGGER = logger.create_logger("simulink")
def get_interface(interface_data_types, dp_interface, hal_interface, sfw_interface, method_interface):
""" Get interface combined for dp, hal and sfw
Args:
interface_data_types (dict): User defined interface data types
dp_interface (dict): DP interface
hal_interface (dict): HAL interface
sfw_interface (dict): SFW interface
method_interface (dict): Method interface
Returns:
output ([dict]): Combined interface
"""
output = []
output = add_dp(output, interface_data_types, split_interface(dp_interface))
output = add_api(output, interface_data_types, split_interface(hal_interface))
output = add_api(output, interface_data_types, split_interface(sfw_interface))
output = add_methods(output, interface_data_types, method_interface)
# Cannot have a completely empty adapters file, CSP will break
if not output:
if dp_interface:
output = ensure_raster(output, list(dp_interface.keys())[0] + '_Nothing__Always')
elif hal_interface:
output = ensure_raster(output, list(hal_interface.keys())[0] + '_Nothing_Always')
elif sfw_interface:
output = ensure_raster(output, list(sfw_interface.keys())[0] + '_Nothing_Always')
return output
def split_interface(interface):
"""Takes a raster interface and splits it based on read strategies.
Args:
interface (dict): DP/HAL/SFW raster interface.
Returns:
strategy_split_interface (dict): DP/HAL/SFW interface divided by read strategy.
"""
strategy_split_interface = {}
for raster, raster_data in interface.items():
for port_type, signals in raster_data.items():
for signal_spec in signals:
interface_name = signal_spec.get('api') or signal_spec.get('domain').replace('_', '')
new_raster = '_'.join([raster, interface_name, signal_spec['strategy']])
if new_raster not in strategy_split_interface:
strategy_split_interface[new_raster] = {p: [] for p in raster_data}
strategy_split_interface[new_raster][port_type].append(signal_spec)
return strategy_split_interface
def ensure_raster(output, raster):
""" Ensure raster exists in the output
Args:
output ([dict]): Combined interface
raster (str): Name of raster
Returns:
output ([dict]): Combined interface
"""
for adapter in output:
if adapter['name'] == raster:
return output
output.append(
{
"name": raster,
"ports": {
"in": {},
"out": {}
}
}
)
return output
def get_adapter(interface, raster):
""" Get adapter
Args:
interface (dict): Combined interface
raster (str): Name of raster
Returns:
adapter (dict): Adapter for the raster already in the interface
"""
for adapter in interface:
if adapter['name'] == raster:
return adapter
raise KeyError(raster)
def add_dp(output, interface_data_types, interface):
""" Adds the DP interface to the combined interface
Args:
output ([dict]): Combined interface
interface_data_types (dict): User defined interface data types
interface (dict): DP interface
Returns
output (dict): Combined interface
"""
ports = {
"consumer": "in",
"producer": "out"
}
for raster, raster_data in interface.items():
if not isinstance(raster_data, dict):
LOGGER.debug('Ignoring metadata: %s', raster_data)
continue
output = ensure_raster(output, raster)
adapter = get_adapter(output, raster)
for port_type, signals in raster_data.items():
for signal in signals:
data_type = signal['variable_type']
primitive = ['signals']
primitive.append(signal['domain'])
if signal['group'] is not None:
primitive.append(signal['group'])
primitive.append(signal['property'])
adapter['ports'][ports[port_type]][signal['variable']] = {
'primitive': '.'.join(primitive),
'type': data_type
}
if 'enums' in interface_data_types and data_type in interface_data_types['enums']:
csp_enum_definition = add_csp_enum_def(data_type, interface_data_types['enums'][data_type])
if 'types' not in adapter:
adapter['types'] = []
if csp_enum_definition not in adapter['types']:
adapter['types'].append(csp_enum_definition)
return output
def add_api(output, interface_data_types, interface):
""" Adds the HAL/GenericApi/SFW interface to the combined interface
Args:
output ([dict]): Combined interface
interface_data_types (dict): User defined interface data types
interface (dict): HAL interface
Returns
output (dict): Combined interface
"""
ports = {
"consumer": "in",
"producer": "out"
}
for raster, raster_data in interface.items():
if not isinstance(raster_data, dict):
LOGGER.debug('Ignoring metadata: %s', raster_data)
continue
output = ensure_raster(output, raster)
adapter = get_adapter(output, raster)
for port_type, signals in raster_data.items():
for signal in signals:
data_type = signal['variable_type']
primitive = [signal['api'].lower()]
primitive.append(signal['endpoint'].lower())
if signal['property'] is not None:
primitive.append(signal['property'].lower())
adapter['ports'][ports[port_type]][signal['variable']] = {
'primitive': '.'.join(primitive),
'type': data_type
}
if 'enums' in interface_data_types and data_type in interface_data_types['enums']:
csp_enum_definition = add_csp_enum_def(data_type, interface_data_types['enums'][data_type])
if 'types' not in adapter:
adapter['types'] = []
if csp_enum_definition not in adapter['types']:
adapter['types'].append(csp_enum_definition)
return output
def add_methods(output, interface_data_types, methods):
""" Adds the CSP method call interfaces to the combined interface.
Args:
output ([dict]): Combined interface
interface_data_types (dict): Dict with enum definitions
methods (dict): Methods used by the application
Returns:
output (dict): Combined interface
"""
if methods == {}:
return output
output = ensure_raster(output, 'csp_methods')
adapter = get_adapter(output, 'csp_methods')
if 'methods' not in adapter:
adapter['methods'] = []
for method_data in methods.values():
method_spec = {}
method_spec['name'] = method_data['name']
method_spec['primitive_method'] = method_data['primitive']
method_spec['namespace'] = method_data['namespace']
if 'description' in method_data:
method_spec['description'] = method_data['description']
for direction in ['in', 'out']:
parameters = method_data['ports'][direction]
if len(parameters) > 0:
method_spec[direction] = []
for param_name, param_data in parameters.items():
param_spec = {}
param_spec['name'] = param_name
param_spec['primitive_name'] = param_data['primitive']
data_type = param_data['type']
param_spec['type'] = data_type
method_spec[direction].append(param_spec)
if data_type in interface_data_types.get('enums', {}):
csp_enum_definition = add_csp_enum_def(data_type, interface_data_types['enums'][data_type])
if 'types' not in adapter:
adapter['types'] = []
if csp_enum_definition not in adapter['types']:
adapter['types'].append(csp_enum_definition)
adapter['methods'].append(method_spec)
return output
def add_csp_enum_def(enum_name, interface_enum_definition):
""" Returns a CSP style enumeration definition, given an interface style enum definition.
Args:
enum_name (str): Name of the enumeration
interface_enum_definition (dict): Enumeration from interface data types
Returns:
csp_enum_definition (dict): Combined interface
"""
default = {}
enumerators = []
for enum_member_definition in interface_enum_definition:
if 'default' in enum_member_definition:
default = {enum_member_definition['in']: enum_member_definition['out']}
else:
enumerators.append({enum_member_definition['in']: enum_member_definition['out']})
csp_enum_definition = {
'name': enum_name,
'kind': 'enum',
'enumerators': enumerators,
'default': default
}
return csp_enum_definition