powertrain-build 1.13.1__py3-none-any.whl → 1.13.3.dev3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- powertrain_build/__init__.py +40 -40
- powertrain_build/__main__.py +6 -6
- powertrain_build/a2l.py +582 -582
- powertrain_build/a2l_merge.py +650 -650
- powertrain_build/a2l_templates.py +717 -717
- powertrain_build/build.py +985 -985
- powertrain_build/build_defs.py +309 -309
- powertrain_build/build_proj_config.py +690 -690
- powertrain_build/check_interface.py +575 -575
- powertrain_build/cli.py +141 -141
- powertrain_build/config.py +542 -542
- powertrain_build/core.py +395 -395
- powertrain_build/core_dummy.py +343 -343
- powertrain_build/create_conversion_table.py +73 -73
- powertrain_build/dids.py +916 -916
- powertrain_build/dummy.py +157 -157
- powertrain_build/dummy_spm.py +252 -252
- powertrain_build/environmentcheck.py +52 -52
- powertrain_build/ext_dbg.py +255 -255
- powertrain_build/ext_var.py +327 -327
- powertrain_build/feature_configs.py +301 -301
- powertrain_build/gen_allsysteminfo.py +227 -227
- powertrain_build/gen_label_split.py +449 -449
- powertrain_build/handcode_replacer.py +124 -124
- powertrain_build/html_report.py +133 -133
- powertrain_build/interface/__init__.py +4 -4
- powertrain_build/interface/application.py +511 -511
- powertrain_build/interface/base.py +500 -500
- powertrain_build/interface/csp_api.py +490 -490
- powertrain_build/interface/device_proxy.py +677 -677
- powertrain_build/interface/ems.py +67 -67
- powertrain_build/interface/export_global_vars.py +121 -121
- powertrain_build/interface/generate_adapters.py +132 -132
- powertrain_build/interface/generate_hi_interface.py +87 -87
- powertrain_build/interface/generate_service.py +69 -69
- powertrain_build/interface/generate_wrappers.py +147 -147
- powertrain_build/interface/generation_utils.py +142 -142
- powertrain_build/interface/hal.py +194 -194
- powertrain_build/interface/model_yaml_verification.py +348 -348
- powertrain_build/interface/service.py +296 -296
- powertrain_build/interface/simulink.py +249 -249
- powertrain_build/interface/update_call_sources.py +180 -180
- powertrain_build/interface/update_model_yaml.py +186 -186
- powertrain_build/interface/zone_controller.py +362 -362
- powertrain_build/lib/__init__.py +4 -4
- powertrain_build/lib/helper_functions.py +127 -127
- powertrain_build/lib/logger.py +55 -55
- powertrain_build/matlab_scripts/CodeGen/BuildAutomationPyBuild.m +78 -78
- powertrain_build/matlab_scripts/CodeGen/Generate_A2L.m +154 -154
- powertrain_build/matlab_scripts/CodeGen/generateTLUnit.m +239 -239
- powertrain_build/matlab_scripts/CodeGen/getAsilClassification.m +28 -28
- powertrain_build/matlab_scripts/CodeGen/modelConfiguredForTL.m +28 -28
- powertrain_build/matlab_scripts/CodeGen/moveDefOutports.m +88 -88
- powertrain_build/matlab_scripts/CodeGen/parseCalMeasData.m +410 -410
- powertrain_build/matlab_scripts/CodeGen/parseCoreIdentifiers.m +139 -139
- powertrain_build/matlab_scripts/CodeGen/parseDIDs.m +141 -141
- powertrain_build/matlab_scripts/CodeGen/parseInPorts.m +106 -106
- powertrain_build/matlab_scripts/CodeGen/parseIncludeConfigs.m +25 -25
- powertrain_build/matlab_scripts/CodeGen/parseModelInfo.m +38 -38
- powertrain_build/matlab_scripts/CodeGen/parseNVM.m +81 -81
- powertrain_build/matlab_scripts/CodeGen/parseOutPorts.m +120 -120
- powertrain_build/matlab_scripts/CodeGen/parsePreProcBlks.m +23 -23
- powertrain_build/matlab_scripts/CodeGen/struct2JSON.m +128 -128
- powertrain_build/matlab_scripts/CodeGen/updateCodeSwConfig.m +31 -31
- powertrain_build/matlab_scripts/Init_PyBuild.m +91 -91
- powertrain_build/matlab_scripts/__init__.py +2 -2
- powertrain_build/matlab_scripts/helperFunctions/Get_Full_Name.m +46 -46
- powertrain_build/matlab_scripts/helperFunctions/Get_SrcLines.m +12 -12
- powertrain_build/matlab_scripts/helperFunctions/Init_Models.m +78 -78
- powertrain_build/matlab_scripts/helperFunctions/Init_Projects.m +67 -67
- powertrain_build/matlab_scripts/helperFunctions/Read_Units.m +34 -34
- powertrain_build/matlab_scripts/helperFunctions/SetProjectTimeSamples.m +26 -26
- powertrain_build/matlab_scripts/helperFunctions/Strip_Suffix.m +16 -16
- powertrain_build/matlab_scripts/helperFunctions/followLink.m +118 -118
- powertrain_build/matlab_scripts/helperFunctions/getCodeSwitches.m +50 -50
- powertrain_build/matlab_scripts/helperFunctions/getConsumerBlocks.m +30 -30
- powertrain_build/matlab_scripts/helperFunctions/getDefBlock.m +39 -39
- powertrain_build/matlab_scripts/helperFunctions/getDefOutport.m +58 -58
- powertrain_build/matlab_scripts/helperFunctions/getDstBlocks.m +19 -19
- powertrain_build/matlab_scripts/helperFunctions/getDstLines.m +13 -13
- powertrain_build/matlab_scripts/helperFunctions/getInterfaceSignals.m +37 -37
- powertrain_build/matlab_scripts/helperFunctions/getName.m +37 -37
- powertrain_build/matlab_scripts/helperFunctions/getPath.m +6 -6
- powertrain_build/matlab_scripts/helperFunctions/getProperValue.m +21 -21
- powertrain_build/matlab_scripts/helperFunctions/getSrcBlocks.m +19 -19
- powertrain_build/matlab_scripts/helperFunctions/getSrcLines.m +13 -13
- powertrain_build/matlab_scripts/helperFunctions/loadLibraries.m +10 -10
- powertrain_build/matlab_scripts/helperFunctions/loadjson.m +6 -6
- powertrain_build/matlab_scripts/helperFunctions/modifyEnumStructField.m +21 -21
- powertrain_build/matlab_scripts/helperFunctions/removeConfigDuplicates.m +31 -31
- powertrain_build/matlab_scripts/helperFunctions/sortSystemByClass.m +26 -26
- powertrain_build/matlab_scripts/helperFunctions/tl_getfast.m +89 -89
- powertrain_build/matlab_scripts/helperFunctions/topLevelSystem.m +20 -20
- powertrain_build/matlab_scripts/helperFunctions/updateModels.m +131 -131
- powertrain_build/memory_section.py +224 -224
- powertrain_build/nvm_def.py +729 -729
- powertrain_build/problem_logger.py +86 -86
- powertrain_build/pt_matlab.py +430 -430
- powertrain_build/pt_win32.py +144 -144
- powertrain_build/replace_compu_tab_ref.py +105 -105
- powertrain_build/rte_dummy.py +254 -254
- powertrain_build/sched_funcs.py +209 -207
- powertrain_build/signal.py +7 -7
- powertrain_build/signal_if_html_rep.py +221 -221
- powertrain_build/signal_if_html_rep_all.py +302 -302
- powertrain_build/signal_incons_html_rep.py +180 -180
- powertrain_build/signal_incons_html_rep_all.py +366 -366
- powertrain_build/signal_incons_html_rep_base.py +168 -168
- powertrain_build/signal_inconsistency_check.py +641 -641
- powertrain_build/signal_interfaces.py +864 -864
- powertrain_build/templates/Index_SigCheck_All.html +22 -22
- powertrain_build/templates/Index_SigIf_All.html +19 -19
- powertrain_build/types.py +218 -218
- powertrain_build/unit_configs.py +419 -419
- powertrain_build/user_defined_types.py +660 -660
- powertrain_build/versioncheck.py +66 -66
- powertrain_build/wrapper.py +512 -512
- powertrain_build/xlrd_csv.py +87 -87
- powertrain_build/zone_controller/__init__.py +4 -4
- powertrain_build/zone_controller/calibration.py +176 -176
- powertrain_build/zone_controller/composition_yaml.py +880 -878
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/METADATA +100 -100
- powertrain_build-1.13.3.dev3.dist-info/RECORD +130 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/WHEEL +1 -1
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/LICENSE +202 -202
- powertrain_build-1.13.3.dev3.dist-info/pbr.json +1 -0
- powertrain_build-1.13.1.dist-info/RECORD +0 -130
- powertrain_build-1.13.1.dist-info/pbr.json +0 -1
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/entry_points.txt +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/AUTHORS +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/NOTICE +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/top_level.txt +0 -0
|
@@ -1,511 +1,511 @@
|
|
|
1
|
-
# Copyright 2024 Volvo Car Corporation
|
|
2
|
-
# Licensed under Apache 2.0.
|
|
3
|
-
|
|
4
|
-
# -*- coding: utf-8 -*-
|
|
5
|
-
|
|
6
|
-
"""Python module for abstracting Pybuild applications"""
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
import json
|
|
9
|
-
|
|
10
|
-
from powertrain_build.interface.base import BaseApplication, Signal, MultipleProducersError, Domain, Interface
|
|
11
|
-
from powertrain_build.lib import logger
|
|
12
|
-
from powertrain_build.build_proj_config import BuildProjConfig
|
|
13
|
-
from powertrain_build.feature_configs import FeatureConfigs
|
|
14
|
-
from powertrain_build.unit_configs import UnitConfigs
|
|
15
|
-
from powertrain_build.user_defined_types import UserDefinedTypes
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
LOGGER = logger.create_logger("application")
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def get_raster_to_raster_interfaces(rasters):
|
|
22
|
-
"""Generate a list of Interfaces for internal raster-to-raster signals.
|
|
23
|
-
|
|
24
|
-
Args:
|
|
25
|
-
rasters (list): Input rasters (from app.get_rasters())
|
|
26
|
-
Returns:
|
|
27
|
-
interfaces (list(interfaces)): List of unique raster-to-raster-interfaces.
|
|
28
|
-
"""
|
|
29
|
-
raster_pairs = []
|
|
30
|
-
for current_raster in rasters:
|
|
31
|
-
for corresponding_raster in [r for r in rasters if r != current_raster]:
|
|
32
|
-
# If we have interface a_b, no need to produce b_a.
|
|
33
|
-
if (corresponding_raster, current_raster) not in raster_pairs:
|
|
34
|
-
raster_pairs.append((current_raster, corresponding_raster))
|
|
35
|
-
|
|
36
|
-
return [Interface(raster[0], raster[1]) for raster in raster_pairs]
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def get_internal_domain(rasters):
|
|
40
|
-
""" Create an internal domain of signals
|
|
41
|
-
|
|
42
|
-
Loops through all raster<->raster communications and adds them to a domain object
|
|
43
|
-
|
|
44
|
-
Args:
|
|
45
|
-
rasters (list(Raster)): rasters to calculate communication for
|
|
46
|
-
Returns:
|
|
47
|
-
domain (Domain): signals belonging to the same domain
|
|
48
|
-
"""
|
|
49
|
-
internal = Domain()
|
|
50
|
-
internal.set_name("internal")
|
|
51
|
-
for interface in get_raster_to_raster_interfaces(rasters):
|
|
52
|
-
internal.add_interface(interface)
|
|
53
|
-
|
|
54
|
-
return internal
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def get_active_signals(signals, feature_cfg):
|
|
58
|
-
""" Filter out inactive signals. """
|
|
59
|
-
LOGGER.debug('Filtering %s', signals)
|
|
60
|
-
return [signal for signal in signals if feature_cfg.check_if_active_in_config(signal.properties['configs'])]
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class Application(BaseApplication):
|
|
64
|
-
""" Object for holding information about a pybuild project """
|
|
65
|
-
def __init__(self):
|
|
66
|
-
self.name = None
|
|
67
|
-
self.pybuild = {'build_cfg': None,
|
|
68
|
-
'feature_cfg': None,
|
|
69
|
-
'unit_vars': {}}
|
|
70
|
-
self._insignals = None
|
|
71
|
-
self._outsignals = None
|
|
72
|
-
self._signals = None
|
|
73
|
-
self._raster_definitions = []
|
|
74
|
-
self._services = None
|
|
75
|
-
self._methods = []
|
|
76
|
-
self._enumerations = None
|
|
77
|
-
self._structs = None
|
|
78
|
-
|
|
79
|
-
def parse_definition(self, definition):
|
|
80
|
-
""" Parse ProjectCfg.json, get code switch values and read config.json files.
|
|
81
|
-
Add the information to the object.
|
|
82
|
-
|
|
83
|
-
Args:
|
|
84
|
-
definition (Path): Path to ProjectCfg.json
|
|
85
|
-
"""
|
|
86
|
-
self.pybuild['build_cfg'] = BuildProjConfig(str(definition))
|
|
87
|
-
self.name = self.pybuild['build_cfg'].name
|
|
88
|
-
self.pybuild['feature_cfg'] = FeatureConfigs(self.pybuild['build_cfg'])
|
|
89
|
-
unit_cfg = UnitConfigs(self.pybuild['build_cfg'], self.pybuild['feature_cfg'])
|
|
90
|
-
self.pybuild['unit_vars'] = unit_cfg.get_per_unit_cfg()
|
|
91
|
-
self.pybuild['user_defined_types'] = UserDefinedTypes(self.pybuild['build_cfg'], unit_cfg)
|
|
92
|
-
|
|
93
|
-
def get_domain_names(self):
|
|
94
|
-
""" Get domain names. """
|
|
95
|
-
return self.pybuild['build_cfg'].device_domains.values()
|
|
96
|
-
|
|
97
|
-
def get_domain_mapping(self):
|
|
98
|
-
""" Get device to signal domain mapping. """
|
|
99
|
-
return self.pybuild['build_cfg'].device_domains
|
|
100
|
-
|
|
101
|
-
def get_methods(self):
|
|
102
|
-
""" Get csp methods. """
|
|
103
|
-
if self._signals is None:
|
|
104
|
-
self._get_signals()
|
|
105
|
-
return self._methods
|
|
106
|
-
|
|
107
|
-
@property
|
|
108
|
-
def enumerations(self):
|
|
109
|
-
""" Get enumerations defined in the project. """
|
|
110
|
-
if self._enumerations is None:
|
|
111
|
-
self._enumerations = self.pybuild['user_defined_types'].get_enumerations()
|
|
112
|
-
return self._enumerations
|
|
113
|
-
|
|
114
|
-
@property
|
|
115
|
-
def structs(self):
|
|
116
|
-
""" Get structs defined in the project. """
|
|
117
|
-
if self._structs is None:
|
|
118
|
-
self._structs = self.pybuild['user_defined_types'].get_structs()
|
|
119
|
-
return self._structs
|
|
120
|
-
|
|
121
|
-
@property
|
|
122
|
-
def services(self):
|
|
123
|
-
""" Get interface to service mapping. """
|
|
124
|
-
if self._services is None:
|
|
125
|
-
services_file = self.get_services_file()
|
|
126
|
-
self._services = self.pybuild['build_cfg'].get_services(services_file)
|
|
127
|
-
return self._services
|
|
128
|
-
|
|
129
|
-
def get_service_mapping(self):
|
|
130
|
-
""" Get interface to service mapping. """
|
|
131
|
-
return self.services
|
|
132
|
-
|
|
133
|
-
def get_services_file(self):
|
|
134
|
-
""" Get path to file specifying interface to service mapping. """
|
|
135
|
-
return self.pybuild['build_cfg'].services_file
|
|
136
|
-
|
|
137
|
-
def get_name(self, definition):
|
|
138
|
-
""" Parse ProjectCfg.json and return the specified project name """
|
|
139
|
-
if self.name is None:
|
|
140
|
-
return BuildProjConfig(str(definition)).name
|
|
141
|
-
return self.name
|
|
142
|
-
|
|
143
|
-
def _get_signals(self):
|
|
144
|
-
""" Calculate parse all inport and outports of all models """
|
|
145
|
-
self._insignals = set()
|
|
146
|
-
self._outsignals = set()
|
|
147
|
-
defined_ports = {'inports': set(), 'outports': set()}
|
|
148
|
-
for unit, data in self.pybuild['unit_vars'].items():
|
|
149
|
-
self.parse_ports(data, defined_ports, self.pybuild['feature_cfg'], unit)
|
|
150
|
-
self.parse_csp_methods(data, self.pybuild['feature_cfg'], unit)
|
|
151
|
-
|
|
152
|
-
def parse_ports(self, port_data, defined_ports, feature_cfg, unit):
|
|
153
|
-
""" Parse ports for one model, based on code switch values.
|
|
154
|
-
Modifies the defined_ports dict and the object.
|
|
155
|
-
|
|
156
|
-
Args:
|
|
157
|
-
port_data (dict): port data for a model/unit
|
|
158
|
-
defined_ports (set): all known signals
|
|
159
|
-
feature_cfg (FeatureConfigs): pybuild parsed object for code switches
|
|
160
|
-
unit (string): Name of model/unit
|
|
161
|
-
"""
|
|
162
|
-
if self._signals is None:
|
|
163
|
-
self._signals = {}
|
|
164
|
-
for port_type, outport in {'outports': True, 'inports': False}.items():
|
|
165
|
-
for port_name, data in port_data.get(port_type, {}).items():
|
|
166
|
-
# Get what signals we are dealing with
|
|
167
|
-
if not feature_cfg.check_if_active_in_config(data['configs']):
|
|
168
|
-
continue
|
|
169
|
-
if port_name not in self._signals:
|
|
170
|
-
signal = Signal(port_name, self)
|
|
171
|
-
self._signals.update({port_name: signal})
|
|
172
|
-
else:
|
|
173
|
-
signal = self._signals[port_name]
|
|
174
|
-
# Add information about which models are involved while we are reading it
|
|
175
|
-
if outport:
|
|
176
|
-
try:
|
|
177
|
-
signal.set_producer(unit)
|
|
178
|
-
except MultipleProducersError as mpe:
|
|
179
|
-
LOGGER.debug(mpe.message)
|
|
180
|
-
signal.force_producer(unit)
|
|
181
|
-
self._outsignals.add(port_name)
|
|
182
|
-
else:
|
|
183
|
-
signal.consumers = unit
|
|
184
|
-
self._insignals.add(port_name)
|
|
185
|
-
defined_ports[port_type].add(port_name)
|
|
186
|
-
|
|
187
|
-
def parse_csp_methods(self, port_data, feature_cfg, unit):
|
|
188
|
-
""" Parse csp methods.
|
|
189
|
-
|
|
190
|
-
Args:
|
|
191
|
-
port_data (dict): port data for a model/unit.
|
|
192
|
-
feature_cfg (FeatureConfigs): pybuild parsed object for code switches
|
|
193
|
-
unit (string): Name of model/unit
|
|
194
|
-
"""
|
|
195
|
-
if self._signals is None:
|
|
196
|
-
self._signals = {}
|
|
197
|
-
methods = port_data.get('csp', {}).get('methods', {})
|
|
198
|
-
for method_name, data in methods.items():
|
|
199
|
-
if feature_cfg.check_if_active_in_config(data['configs']):
|
|
200
|
-
method = Method(self, unit)
|
|
201
|
-
method.parse_definition((method_name, data))
|
|
202
|
-
self._methods.append(method)
|
|
203
|
-
|
|
204
|
-
def get_signal_properties(self, signal):
|
|
205
|
-
""" Get properties for the signal from powertrain_build definition.
|
|
206
|
-
|
|
207
|
-
Args:
|
|
208
|
-
signal (Signal): Signal object
|
|
209
|
-
Returns:
|
|
210
|
-
properties (dict): Properties of the signal in pybuild
|
|
211
|
-
"""
|
|
212
|
-
# Hack: Take the first consumer or producer if any exists
|
|
213
|
-
for producer in signal.producer:
|
|
214
|
-
return self.pybuild['unit_vars'][producer]['outports'][signal.name]
|
|
215
|
-
for consumer in signal.consumers:
|
|
216
|
-
return self.pybuild['unit_vars'][consumer]['inports'][signal.name]
|
|
217
|
-
return {}
|
|
218
|
-
|
|
219
|
-
def get_rasters(self):
|
|
220
|
-
""" Get rasters parsed from powertrain_build.
|
|
221
|
-
|
|
222
|
-
Returns:
|
|
223
|
-
rasters (list): rasters parsed from powertrain_build
|
|
224
|
-
"""
|
|
225
|
-
if self._signals is None:
|
|
226
|
-
self._get_signals()
|
|
227
|
-
raster_definition = self.pybuild['build_cfg'].get_units_raster_cfg()
|
|
228
|
-
rasters = []
|
|
229
|
-
for raster_field, raster_content in raster_definition.items():
|
|
230
|
-
if raster_field in ['SampleTimes']:
|
|
231
|
-
continue
|
|
232
|
-
for name, content in raster_content.items():
|
|
233
|
-
if name in ['NoSched']:
|
|
234
|
-
continue
|
|
235
|
-
raster = Raster(self)
|
|
236
|
-
raster.parse_definition((name, content, self._signals))
|
|
237
|
-
rasters.append(raster)
|
|
238
|
-
return rasters
|
|
239
|
-
|
|
240
|
-
def get_models(self):
|
|
241
|
-
""" Get models and parse their config files.
|
|
242
|
-
|
|
243
|
-
Returns:
|
|
244
|
-
models (list(Model)): config.jsons parsed
|
|
245
|
-
"""
|
|
246
|
-
rasters = self.get_rasters()
|
|
247
|
-
# Since one model can exist in many rasters. Find all unique model names first.
|
|
248
|
-
cfg_dirs = self.pybuild['build_cfg'].get_unit_cfg_dirs()
|
|
249
|
-
model_names = set()
|
|
250
|
-
for raster in rasters:
|
|
251
|
-
model_names = model_names.union(raster.models)
|
|
252
|
-
models = []
|
|
253
|
-
for model_name in model_names:
|
|
254
|
-
if model_name not in cfg_dirs:
|
|
255
|
-
LOGGER.debug("%s is generated code. It does not have a config.", model_name)
|
|
256
|
-
continue
|
|
257
|
-
model = Model(self)
|
|
258
|
-
cfg_dir = cfg_dirs[model_name]
|
|
259
|
-
config = Path(cfg_dir, f'config_{model_name}.json')
|
|
260
|
-
model.parse_definition((model_name, config))
|
|
261
|
-
models.append(model)
|
|
262
|
-
return models
|
|
263
|
-
|
|
264
|
-
def get_translation_files(self):
|
|
265
|
-
""" Find all yaml files in translation file dirs.
|
|
266
|
-
|
|
267
|
-
Returns:
|
|
268
|
-
translation_files (list(Path)): translation files
|
|
269
|
-
"""
|
|
270
|
-
translation_files = []
|
|
271
|
-
cfg_dirs = self.pybuild['build_cfg'].get_translation_files_dirs()
|
|
272
|
-
for cfg_dir in cfg_dirs.values():
|
|
273
|
-
cfg_path = Path(cfg_dir)
|
|
274
|
-
translation_files.extend(cfg_path.glob('*.yaml'))
|
|
275
|
-
translation_files = list(set(translation_files))
|
|
276
|
-
return translation_files
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
class Raster(BaseApplication):
|
|
280
|
-
""" Object for holding information about a raster """
|
|
281
|
-
def __init__(self, app):
|
|
282
|
-
"""Construct a new Raster object.
|
|
283
|
-
|
|
284
|
-
Args:
|
|
285
|
-
app (powertrain_build.interface.application.Application): Pybuild project raster is part of
|
|
286
|
-
"""
|
|
287
|
-
self.app = app
|
|
288
|
-
self.name = str()
|
|
289
|
-
self._insignals = None
|
|
290
|
-
self._outsignals = None
|
|
291
|
-
self._available_signals = None
|
|
292
|
-
self.models = set()
|
|
293
|
-
|
|
294
|
-
def parse_definition(self, definition):
|
|
295
|
-
""" Parse the definition from powertrain_build.
|
|
296
|
-
|
|
297
|
-
Args:
|
|
298
|
-
definition (tuple):
|
|
299
|
-
name (string): Name of the raster
|
|
300
|
-
content (list): Models in the raster
|
|
301
|
-
app_signals (dict): All signals in all rasters
|
|
302
|
-
"""
|
|
303
|
-
self.name = definition[0]
|
|
304
|
-
self.models = set(definition[1])
|
|
305
|
-
self._available_signals = definition[2]
|
|
306
|
-
|
|
307
|
-
def _get_signals(self):
|
|
308
|
-
""" Add signals from the project to the raster if they are used here
|
|
309
|
-
|
|
310
|
-
Modifies the object itself.
|
|
311
|
-
"""
|
|
312
|
-
self._insignals = set()
|
|
313
|
-
self._outsignals = set()
|
|
314
|
-
self._signals = {}
|
|
315
|
-
if self._available_signals is None:
|
|
316
|
-
return
|
|
317
|
-
for signal in self._available_signals.values():
|
|
318
|
-
for consumer in signal.consumers:
|
|
319
|
-
if consumer in self.models:
|
|
320
|
-
self._signals.update({signal.name: signal})
|
|
321
|
-
self._insignals.add(signal.name)
|
|
322
|
-
if isinstance(signal.producer, set):
|
|
323
|
-
for producer in signal.producer:
|
|
324
|
-
if producer in self.models:
|
|
325
|
-
self._signals.update({signal.name: signal})
|
|
326
|
-
self._outsignals.add(signal.name)
|
|
327
|
-
else:
|
|
328
|
-
if signal.producer in self.models:
|
|
329
|
-
self._signals.update({signal.name: signal})
|
|
330
|
-
self._outsignals.add(signal.name)
|
|
331
|
-
|
|
332
|
-
def get_signal_properties(self, signal):
|
|
333
|
-
""" Get properties for the signal from powertrain_build definition.
|
|
334
|
-
|
|
335
|
-
Args:
|
|
336
|
-
signal (Signal): Signal object
|
|
337
|
-
Returns:
|
|
338
|
-
properties (dict): Properties of the signal in pybuild
|
|
339
|
-
"""
|
|
340
|
-
for producer in signal.producer:
|
|
341
|
-
if producer in self.app.pybuild['unit_vars']:
|
|
342
|
-
return self.app.get_signal_properties(signal)
|
|
343
|
-
return {}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
class Model(BaseApplication):
|
|
347
|
-
""" Object for holding information about a model """
|
|
348
|
-
def __init__(self, app):
|
|
349
|
-
self.app = app
|
|
350
|
-
self.name = str()
|
|
351
|
-
self.config = None
|
|
352
|
-
self._insignals = None
|
|
353
|
-
self._outsignals = None
|
|
354
|
-
self._signal_specs = None
|
|
355
|
-
|
|
356
|
-
def get_signal_properties(self, signal):
|
|
357
|
-
""" Get properties for the signal from powertrain_build definition.
|
|
358
|
-
|
|
359
|
-
Args:
|
|
360
|
-
signal (Signal): Signal object
|
|
361
|
-
Returns:
|
|
362
|
-
properties (dict): Properties of the signal in pybuild
|
|
363
|
-
"""
|
|
364
|
-
if self._signal_specs is None:
|
|
365
|
-
self._get_signals()
|
|
366
|
-
if signal.name in self._signal_specs:
|
|
367
|
-
return self._signal_specs[signal.name]
|
|
368
|
-
return {}
|
|
369
|
-
|
|
370
|
-
def _get_signals(self):
|
|
371
|
-
""" Add signals from the project to the model if they are used here
|
|
372
|
-
|
|
373
|
-
Modifies the object itself.
|
|
374
|
-
Entrypoint for finding signals from the base class.
|
|
375
|
-
"""
|
|
376
|
-
self._insignals = set()
|
|
377
|
-
self._outsignals = set()
|
|
378
|
-
self._signals = {}
|
|
379
|
-
self._signal_specs = {}
|
|
380
|
-
self._parse_unit_config(self.config)
|
|
381
|
-
|
|
382
|
-
def _parse_unit_config(self, path):
|
|
383
|
-
""" Parse a unit config file.
|
|
384
|
-
|
|
385
|
-
Broken out of get_signals to be recursive for included configs.
|
|
386
|
-
"""
|
|
387
|
-
cfg = self._load_json(path)
|
|
388
|
-
for signal_spec in cfg['inports'].values():
|
|
389
|
-
signal = Signal(signal_spec['name'], self)
|
|
390
|
-
self._insignals.add(signal.name)
|
|
391
|
-
self._signals.update({signal.name: signal})
|
|
392
|
-
self._signal_specs[signal.name] = signal_spec
|
|
393
|
-
for signal_spec in cfg['outports'].values():
|
|
394
|
-
signal = Signal(signal_spec['name'], self)
|
|
395
|
-
self._outsignals.add(signal.name)
|
|
396
|
-
self._signals.update({signal.name: signal})
|
|
397
|
-
self._signal_specs[signal.name] = signal_spec
|
|
398
|
-
for include_cfg in cfg.get('includes', []):
|
|
399
|
-
LOGGER.debug('%s includes %s in %s', self.name, include_cfg, path.parent)
|
|
400
|
-
include_path = Path(path.parent, f'config_{include_cfg}.json')
|
|
401
|
-
self._parse_unit_config(include_path)
|
|
402
|
-
|
|
403
|
-
@staticmethod
|
|
404
|
-
def _load_json(path):
|
|
405
|
-
""" Small function that opens and loads a json file.
|
|
406
|
-
|
|
407
|
-
Exists to be mocked in unittests
|
|
408
|
-
"""
|
|
409
|
-
with open(path, encoding="utf-8") as fhndl:
|
|
410
|
-
return json.load(fhndl)
|
|
411
|
-
|
|
412
|
-
def parse_definition(self, definition):
|
|
413
|
-
""" Parse the definition from powertrain_build.
|
|
414
|
-
|
|
415
|
-
Args:
|
|
416
|
-
definition (tuple):
|
|
417
|
-
name (string): Name of the model
|
|
418
|
-
configuration (Path): Path to config file
|
|
419
|
-
"""
|
|
420
|
-
self.name = definition[0]
|
|
421
|
-
self.config = definition[1]
|
|
422
|
-
self._get_signals()
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
class Method(BaseApplication):
|
|
426
|
-
""" Object for holding information about a csp method call """
|
|
427
|
-
def __init__(self, app, unit):
|
|
428
|
-
"""Construct a new Method object.
|
|
429
|
-
|
|
430
|
-
Args:
|
|
431
|
-
app (powertrain_build.interface.application.Application): Pybuild project raster is part of.
|
|
432
|
-
unit (str): Model that the method is defined in.
|
|
433
|
-
"""
|
|
434
|
-
self.app = app
|
|
435
|
-
self.unit = unit
|
|
436
|
-
self.name = str()
|
|
437
|
-
self.namespace = str()
|
|
438
|
-
self.adapter = str()
|
|
439
|
-
self.description = None
|
|
440
|
-
self._signals = {}
|
|
441
|
-
self._insignals = set()
|
|
442
|
-
self._outsignals = set()
|
|
443
|
-
self._primitives = {}
|
|
444
|
-
self._properties = {}
|
|
445
|
-
|
|
446
|
-
def parse_definition(self, definition):
|
|
447
|
-
""" Parse the definition from powertrain_build.
|
|
448
|
-
|
|
449
|
-
Args:
|
|
450
|
-
definition (tuple):
|
|
451
|
-
name (string): Name of the model
|
|
452
|
-
configuration (dict): Configuration of method
|
|
453
|
-
"""
|
|
454
|
-
name = definition[0]
|
|
455
|
-
configuration = definition[1]
|
|
456
|
-
self.name = name
|
|
457
|
-
self.adapter = configuration['adapter']
|
|
458
|
-
self.namespace = configuration['namespace']
|
|
459
|
-
self._primitives[name] = configuration['primitive']
|
|
460
|
-
if 'description' in configuration:
|
|
461
|
-
self.description = configuration['description']
|
|
462
|
-
signals = configuration.get('ports', {})
|
|
463
|
-
outsignals = signals.get('out', {})
|
|
464
|
-
for signal_name, signal_data in outsignals.items():
|
|
465
|
-
signal = self._add_signal(signal_name)
|
|
466
|
-
signal.consumers = name
|
|
467
|
-
signal.set_producer(name)
|
|
468
|
-
self._primitives[signal_name] = signal_data['primitive']
|
|
469
|
-
self._properties[signal_name] = signal_data
|
|
470
|
-
self._outsignals.add(signal_name)
|
|
471
|
-
insignals = signals.get('in', {})
|
|
472
|
-
for signal_name, signal_data in insignals.items():
|
|
473
|
-
signal = self._add_signal(signal_name)
|
|
474
|
-
signal.consumers = name
|
|
475
|
-
signal.set_producer(name)
|
|
476
|
-
self._insignals.add(signal_name)
|
|
477
|
-
self._primitives[signal_name] = signal_data['primitive']
|
|
478
|
-
self._properties[signal_name] = signal_data
|
|
479
|
-
|
|
480
|
-
def _add_signal(self, signal_name):
|
|
481
|
-
""" Add a signal used by the method.
|
|
482
|
-
|
|
483
|
-
Args:
|
|
484
|
-
signal_name (str): Name of the signal
|
|
485
|
-
"""
|
|
486
|
-
if signal_name not in self._signals:
|
|
487
|
-
signal = Signal(signal_name, self)
|
|
488
|
-
self._signals.update({signal_name: signal})
|
|
489
|
-
else:
|
|
490
|
-
signal = self._signals[signal_name]
|
|
491
|
-
return signal
|
|
492
|
-
|
|
493
|
-
def get_signal_properties(self, signal):
|
|
494
|
-
""" Get properties for the signal from csp method configuration.
|
|
495
|
-
|
|
496
|
-
Args:
|
|
497
|
-
signal (Signal): Signal object
|
|
498
|
-
Returns:
|
|
499
|
-
properties (dict): Properties of the signal in pybuild
|
|
500
|
-
"""
|
|
501
|
-
return self._properties[signal.name]
|
|
502
|
-
|
|
503
|
-
def get_primitive(self, primitive_name):
|
|
504
|
-
""" Get primitive.
|
|
505
|
-
|
|
506
|
-
Args:
|
|
507
|
-
primitive_name (str): Name of primitive part
|
|
508
|
-
Returns:
|
|
509
|
-
primitive (str): Primitive
|
|
510
|
-
"""
|
|
511
|
-
return self._primitives[primitive_name]
|
|
1
|
+
# Copyright 2024 Volvo Car Corporation
|
|
2
|
+
# Licensed under Apache 2.0.
|
|
3
|
+
|
|
4
|
+
# -*- coding: utf-8 -*-
|
|
5
|
+
|
|
6
|
+
"""Python module for abstracting Pybuild applications"""
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
from powertrain_build.interface.base import BaseApplication, Signal, MultipleProducersError, Domain, Interface
|
|
11
|
+
from powertrain_build.lib import logger
|
|
12
|
+
from powertrain_build.build_proj_config import BuildProjConfig
|
|
13
|
+
from powertrain_build.feature_configs import FeatureConfigs
|
|
14
|
+
from powertrain_build.unit_configs import UnitConfigs
|
|
15
|
+
from powertrain_build.user_defined_types import UserDefinedTypes
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
LOGGER = logger.create_logger("application")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_raster_to_raster_interfaces(rasters):
|
|
22
|
+
"""Generate a list of Interfaces for internal raster-to-raster signals.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
rasters (list): Input rasters (from app.get_rasters())
|
|
26
|
+
Returns:
|
|
27
|
+
interfaces (list(interfaces)): List of unique raster-to-raster-interfaces.
|
|
28
|
+
"""
|
|
29
|
+
raster_pairs = []
|
|
30
|
+
for current_raster in rasters:
|
|
31
|
+
for corresponding_raster in [r for r in rasters if r != current_raster]:
|
|
32
|
+
# If we have interface a_b, no need to produce b_a.
|
|
33
|
+
if (corresponding_raster, current_raster) not in raster_pairs:
|
|
34
|
+
raster_pairs.append((current_raster, corresponding_raster))
|
|
35
|
+
|
|
36
|
+
return [Interface(raster[0], raster[1]) for raster in raster_pairs]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_internal_domain(rasters):
|
|
40
|
+
""" Create an internal domain of signals
|
|
41
|
+
|
|
42
|
+
Loops through all raster<->raster communications and adds them to a domain object
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
rasters (list(Raster)): rasters to calculate communication for
|
|
46
|
+
Returns:
|
|
47
|
+
domain (Domain): signals belonging to the same domain
|
|
48
|
+
"""
|
|
49
|
+
internal = Domain()
|
|
50
|
+
internal.set_name("internal")
|
|
51
|
+
for interface in get_raster_to_raster_interfaces(rasters):
|
|
52
|
+
internal.add_interface(interface)
|
|
53
|
+
|
|
54
|
+
return internal
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_active_signals(signals, feature_cfg):
|
|
58
|
+
""" Filter out inactive signals. """
|
|
59
|
+
LOGGER.debug('Filtering %s', signals)
|
|
60
|
+
return [signal for signal in signals if feature_cfg.check_if_active_in_config(signal.properties['configs'])]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class Application(BaseApplication):
|
|
64
|
+
""" Object for holding information about a pybuild project """
|
|
65
|
+
def __init__(self):
|
|
66
|
+
self.name = None
|
|
67
|
+
self.pybuild = {'build_cfg': None,
|
|
68
|
+
'feature_cfg': None,
|
|
69
|
+
'unit_vars': {}}
|
|
70
|
+
self._insignals = None
|
|
71
|
+
self._outsignals = None
|
|
72
|
+
self._signals = None
|
|
73
|
+
self._raster_definitions = []
|
|
74
|
+
self._services = None
|
|
75
|
+
self._methods = []
|
|
76
|
+
self._enumerations = None
|
|
77
|
+
self._structs = None
|
|
78
|
+
|
|
79
|
+
def parse_definition(self, definition):
|
|
80
|
+
""" Parse ProjectCfg.json, get code switch values and read config.json files.
|
|
81
|
+
Add the information to the object.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
definition (Path): Path to ProjectCfg.json
|
|
85
|
+
"""
|
|
86
|
+
self.pybuild['build_cfg'] = BuildProjConfig(str(definition))
|
|
87
|
+
self.name = self.pybuild['build_cfg'].name
|
|
88
|
+
self.pybuild['feature_cfg'] = FeatureConfigs(self.pybuild['build_cfg'])
|
|
89
|
+
unit_cfg = UnitConfigs(self.pybuild['build_cfg'], self.pybuild['feature_cfg'])
|
|
90
|
+
self.pybuild['unit_vars'] = unit_cfg.get_per_unit_cfg()
|
|
91
|
+
self.pybuild['user_defined_types'] = UserDefinedTypes(self.pybuild['build_cfg'], unit_cfg)
|
|
92
|
+
|
|
93
|
+
def get_domain_names(self):
|
|
94
|
+
""" Get domain names. """
|
|
95
|
+
return self.pybuild['build_cfg'].device_domains.values()
|
|
96
|
+
|
|
97
|
+
def get_domain_mapping(self):
|
|
98
|
+
""" Get device to signal domain mapping. """
|
|
99
|
+
return self.pybuild['build_cfg'].device_domains
|
|
100
|
+
|
|
101
|
+
def get_methods(self):
|
|
102
|
+
""" Get csp methods. """
|
|
103
|
+
if self._signals is None:
|
|
104
|
+
self._get_signals()
|
|
105
|
+
return self._methods
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def enumerations(self):
|
|
109
|
+
""" Get enumerations defined in the project. """
|
|
110
|
+
if self._enumerations is None:
|
|
111
|
+
self._enumerations = self.pybuild['user_defined_types'].get_enumerations()
|
|
112
|
+
return self._enumerations
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def structs(self):
|
|
116
|
+
""" Get structs defined in the project. """
|
|
117
|
+
if self._structs is None:
|
|
118
|
+
self._structs = self.pybuild['user_defined_types'].get_structs()
|
|
119
|
+
return self._structs
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def services(self):
|
|
123
|
+
""" Get interface to service mapping. """
|
|
124
|
+
if self._services is None:
|
|
125
|
+
services_file = self.get_services_file()
|
|
126
|
+
self._services = self.pybuild['build_cfg'].get_services(services_file)
|
|
127
|
+
return self._services
|
|
128
|
+
|
|
129
|
+
def get_service_mapping(self):
|
|
130
|
+
""" Get interface to service mapping. """
|
|
131
|
+
return self.services
|
|
132
|
+
|
|
133
|
+
def get_services_file(self):
|
|
134
|
+
""" Get path to file specifying interface to service mapping. """
|
|
135
|
+
return self.pybuild['build_cfg'].services_file
|
|
136
|
+
|
|
137
|
+
def get_name(self, definition):
|
|
138
|
+
""" Parse ProjectCfg.json and return the specified project name """
|
|
139
|
+
if self.name is None:
|
|
140
|
+
return BuildProjConfig(str(definition)).name
|
|
141
|
+
return self.name
|
|
142
|
+
|
|
143
|
+
def _get_signals(self):
|
|
144
|
+
""" Calculate parse all inport and outports of all models """
|
|
145
|
+
self._insignals = set()
|
|
146
|
+
self._outsignals = set()
|
|
147
|
+
defined_ports = {'inports': set(), 'outports': set()}
|
|
148
|
+
for unit, data in self.pybuild['unit_vars'].items():
|
|
149
|
+
self.parse_ports(data, defined_ports, self.pybuild['feature_cfg'], unit)
|
|
150
|
+
self.parse_csp_methods(data, self.pybuild['feature_cfg'], unit)
|
|
151
|
+
|
|
152
|
+
def parse_ports(self, port_data, defined_ports, feature_cfg, unit):
|
|
153
|
+
""" Parse ports for one model, based on code switch values.
|
|
154
|
+
Modifies the defined_ports dict and the object.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
port_data (dict): port data for a model/unit
|
|
158
|
+
defined_ports (set): all known signals
|
|
159
|
+
feature_cfg (FeatureConfigs): pybuild parsed object for code switches
|
|
160
|
+
unit (string): Name of model/unit
|
|
161
|
+
"""
|
|
162
|
+
if self._signals is None:
|
|
163
|
+
self._signals = {}
|
|
164
|
+
for port_type, outport in {'outports': True, 'inports': False}.items():
|
|
165
|
+
for port_name, data in port_data.get(port_type, {}).items():
|
|
166
|
+
# Get what signals we are dealing with
|
|
167
|
+
if not feature_cfg.check_if_active_in_config(data['configs']):
|
|
168
|
+
continue
|
|
169
|
+
if port_name not in self._signals:
|
|
170
|
+
signal = Signal(port_name, self)
|
|
171
|
+
self._signals.update({port_name: signal})
|
|
172
|
+
else:
|
|
173
|
+
signal = self._signals[port_name]
|
|
174
|
+
# Add information about which models are involved while we are reading it
|
|
175
|
+
if outport:
|
|
176
|
+
try:
|
|
177
|
+
signal.set_producer(unit)
|
|
178
|
+
except MultipleProducersError as mpe:
|
|
179
|
+
LOGGER.debug(mpe.message)
|
|
180
|
+
signal.force_producer(unit)
|
|
181
|
+
self._outsignals.add(port_name)
|
|
182
|
+
else:
|
|
183
|
+
signal.consumers = unit
|
|
184
|
+
self._insignals.add(port_name)
|
|
185
|
+
defined_ports[port_type].add(port_name)
|
|
186
|
+
|
|
187
|
+
def parse_csp_methods(self, port_data, feature_cfg, unit):
|
|
188
|
+
""" Parse csp methods.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
port_data (dict): port data for a model/unit.
|
|
192
|
+
feature_cfg (FeatureConfigs): pybuild parsed object for code switches
|
|
193
|
+
unit (string): Name of model/unit
|
|
194
|
+
"""
|
|
195
|
+
if self._signals is None:
|
|
196
|
+
self._signals = {}
|
|
197
|
+
methods = port_data.get('csp', {}).get('methods', {})
|
|
198
|
+
for method_name, data in methods.items():
|
|
199
|
+
if feature_cfg.check_if_active_in_config(data['configs']):
|
|
200
|
+
method = Method(self, unit)
|
|
201
|
+
method.parse_definition((method_name, data))
|
|
202
|
+
self._methods.append(method)
|
|
203
|
+
|
|
204
|
+
def get_signal_properties(self, signal):
|
|
205
|
+
""" Get properties for the signal from powertrain_build definition.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
signal (Signal): Signal object
|
|
209
|
+
Returns:
|
|
210
|
+
properties (dict): Properties of the signal in pybuild
|
|
211
|
+
"""
|
|
212
|
+
# Hack: Take the first consumer or producer if any exists
|
|
213
|
+
for producer in signal.producer:
|
|
214
|
+
return self.pybuild['unit_vars'][producer]['outports'][signal.name]
|
|
215
|
+
for consumer in signal.consumers:
|
|
216
|
+
return self.pybuild['unit_vars'][consumer]['inports'][signal.name]
|
|
217
|
+
return {}
|
|
218
|
+
|
|
219
|
+
def get_rasters(self):
|
|
220
|
+
""" Get rasters parsed from powertrain_build.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
rasters (list): rasters parsed from powertrain_build
|
|
224
|
+
"""
|
|
225
|
+
if self._signals is None:
|
|
226
|
+
self._get_signals()
|
|
227
|
+
raster_definition = self.pybuild['build_cfg'].get_units_raster_cfg()
|
|
228
|
+
rasters = []
|
|
229
|
+
for raster_field, raster_content in raster_definition.items():
|
|
230
|
+
if raster_field in ['SampleTimes']:
|
|
231
|
+
continue
|
|
232
|
+
for name, content in raster_content.items():
|
|
233
|
+
if name in ['NoSched']:
|
|
234
|
+
continue
|
|
235
|
+
raster = Raster(self)
|
|
236
|
+
raster.parse_definition((name, content, self._signals))
|
|
237
|
+
rasters.append(raster)
|
|
238
|
+
return rasters
|
|
239
|
+
|
|
240
|
+
def get_models(self):
|
|
241
|
+
""" Get models and parse their config files.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
models (list(Model)): config.jsons parsed
|
|
245
|
+
"""
|
|
246
|
+
rasters = self.get_rasters()
|
|
247
|
+
# Since one model can exist in many rasters. Find all unique model names first.
|
|
248
|
+
cfg_dirs = self.pybuild['build_cfg'].get_unit_cfg_dirs()
|
|
249
|
+
model_names = set()
|
|
250
|
+
for raster in rasters:
|
|
251
|
+
model_names = model_names.union(raster.models)
|
|
252
|
+
models = []
|
|
253
|
+
for model_name in model_names:
|
|
254
|
+
if model_name not in cfg_dirs:
|
|
255
|
+
LOGGER.debug("%s is generated code. It does not have a config.", model_name)
|
|
256
|
+
continue
|
|
257
|
+
model = Model(self)
|
|
258
|
+
cfg_dir = cfg_dirs[model_name]
|
|
259
|
+
config = Path(cfg_dir, f'config_{model_name}.json')
|
|
260
|
+
model.parse_definition((model_name, config))
|
|
261
|
+
models.append(model)
|
|
262
|
+
return models
|
|
263
|
+
|
|
264
|
+
def get_translation_files(self):
|
|
265
|
+
""" Find all yaml files in translation file dirs.
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
translation_files (list(Path)): translation files
|
|
269
|
+
"""
|
|
270
|
+
translation_files = []
|
|
271
|
+
cfg_dirs = self.pybuild['build_cfg'].get_translation_files_dirs()
|
|
272
|
+
for cfg_dir in cfg_dirs.values():
|
|
273
|
+
cfg_path = Path(cfg_dir)
|
|
274
|
+
translation_files.extend(cfg_path.glob('*.yaml'))
|
|
275
|
+
translation_files = list(set(translation_files))
|
|
276
|
+
return translation_files
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class Raster(BaseApplication):
|
|
280
|
+
""" Object for holding information about a raster """
|
|
281
|
+
def __init__(self, app):
|
|
282
|
+
"""Construct a new Raster object.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
app (powertrain_build.interface.application.Application): Pybuild project raster is part of
|
|
286
|
+
"""
|
|
287
|
+
self.app = app
|
|
288
|
+
self.name = str()
|
|
289
|
+
self._insignals = None
|
|
290
|
+
self._outsignals = None
|
|
291
|
+
self._available_signals = None
|
|
292
|
+
self.models = set()
|
|
293
|
+
|
|
294
|
+
def parse_definition(self, definition):
|
|
295
|
+
""" Parse the definition from powertrain_build.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
definition (tuple):
|
|
299
|
+
name (string): Name of the raster
|
|
300
|
+
content (list): Models in the raster
|
|
301
|
+
app_signals (dict): All signals in all rasters
|
|
302
|
+
"""
|
|
303
|
+
self.name = definition[0]
|
|
304
|
+
self.models = set(definition[1])
|
|
305
|
+
self._available_signals = definition[2]
|
|
306
|
+
|
|
307
|
+
def _get_signals(self):
|
|
308
|
+
""" Add signals from the project to the raster if they are used here
|
|
309
|
+
|
|
310
|
+
Modifies the object itself.
|
|
311
|
+
"""
|
|
312
|
+
self._insignals = set()
|
|
313
|
+
self._outsignals = set()
|
|
314
|
+
self._signals = {}
|
|
315
|
+
if self._available_signals is None:
|
|
316
|
+
return
|
|
317
|
+
for signal in self._available_signals.values():
|
|
318
|
+
for consumer in signal.consumers:
|
|
319
|
+
if consumer in self.models:
|
|
320
|
+
self._signals.update({signal.name: signal})
|
|
321
|
+
self._insignals.add(signal.name)
|
|
322
|
+
if isinstance(signal.producer, set):
|
|
323
|
+
for producer in signal.producer:
|
|
324
|
+
if producer in self.models:
|
|
325
|
+
self._signals.update({signal.name: signal})
|
|
326
|
+
self._outsignals.add(signal.name)
|
|
327
|
+
else:
|
|
328
|
+
if signal.producer in self.models:
|
|
329
|
+
self._signals.update({signal.name: signal})
|
|
330
|
+
self._outsignals.add(signal.name)
|
|
331
|
+
|
|
332
|
+
def get_signal_properties(self, signal):
|
|
333
|
+
""" Get properties for the signal from powertrain_build definition.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
signal (Signal): Signal object
|
|
337
|
+
Returns:
|
|
338
|
+
properties (dict): Properties of the signal in pybuild
|
|
339
|
+
"""
|
|
340
|
+
for producer in signal.producer:
|
|
341
|
+
if producer in self.app.pybuild['unit_vars']:
|
|
342
|
+
return self.app.get_signal_properties(signal)
|
|
343
|
+
return {}
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
class Model(BaseApplication):
|
|
347
|
+
""" Object for holding information about a model """
|
|
348
|
+
def __init__(self, app):
|
|
349
|
+
self.app = app
|
|
350
|
+
self.name = str()
|
|
351
|
+
self.config = None
|
|
352
|
+
self._insignals = None
|
|
353
|
+
self._outsignals = None
|
|
354
|
+
self._signal_specs = None
|
|
355
|
+
|
|
356
|
+
def get_signal_properties(self, signal):
|
|
357
|
+
""" Get properties for the signal from powertrain_build definition.
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
signal (Signal): Signal object
|
|
361
|
+
Returns:
|
|
362
|
+
properties (dict): Properties of the signal in pybuild
|
|
363
|
+
"""
|
|
364
|
+
if self._signal_specs is None:
|
|
365
|
+
self._get_signals()
|
|
366
|
+
if signal.name in self._signal_specs:
|
|
367
|
+
return self._signal_specs[signal.name]
|
|
368
|
+
return {}
|
|
369
|
+
|
|
370
|
+
def _get_signals(self):
|
|
371
|
+
""" Add signals from the project to the model if they are used here
|
|
372
|
+
|
|
373
|
+
Modifies the object itself.
|
|
374
|
+
Entrypoint for finding signals from the base class.
|
|
375
|
+
"""
|
|
376
|
+
self._insignals = set()
|
|
377
|
+
self._outsignals = set()
|
|
378
|
+
self._signals = {}
|
|
379
|
+
self._signal_specs = {}
|
|
380
|
+
self._parse_unit_config(self.config)
|
|
381
|
+
|
|
382
|
+
def _parse_unit_config(self, path):
|
|
383
|
+
""" Parse a unit config file.
|
|
384
|
+
|
|
385
|
+
Broken out of get_signals to be recursive for included configs.
|
|
386
|
+
"""
|
|
387
|
+
cfg = self._load_json(path)
|
|
388
|
+
for signal_spec in cfg['inports'].values():
|
|
389
|
+
signal = Signal(signal_spec['name'], self)
|
|
390
|
+
self._insignals.add(signal.name)
|
|
391
|
+
self._signals.update({signal.name: signal})
|
|
392
|
+
self._signal_specs[signal.name] = signal_spec
|
|
393
|
+
for signal_spec in cfg['outports'].values():
|
|
394
|
+
signal = Signal(signal_spec['name'], self)
|
|
395
|
+
self._outsignals.add(signal.name)
|
|
396
|
+
self._signals.update({signal.name: signal})
|
|
397
|
+
self._signal_specs[signal.name] = signal_spec
|
|
398
|
+
for include_cfg in cfg.get('includes', []):
|
|
399
|
+
LOGGER.debug('%s includes %s in %s', self.name, include_cfg, path.parent)
|
|
400
|
+
include_path = Path(path.parent, f'config_{include_cfg}.json')
|
|
401
|
+
self._parse_unit_config(include_path)
|
|
402
|
+
|
|
403
|
+
@staticmethod
|
|
404
|
+
def _load_json(path):
|
|
405
|
+
""" Small function that opens and loads a json file.
|
|
406
|
+
|
|
407
|
+
Exists to be mocked in unittests
|
|
408
|
+
"""
|
|
409
|
+
with open(path, encoding="utf-8") as fhndl:
|
|
410
|
+
return json.load(fhndl)
|
|
411
|
+
|
|
412
|
+
def parse_definition(self, definition):
|
|
413
|
+
""" Parse the definition from powertrain_build.
|
|
414
|
+
|
|
415
|
+
Args:
|
|
416
|
+
definition (tuple):
|
|
417
|
+
name (string): Name of the model
|
|
418
|
+
configuration (Path): Path to config file
|
|
419
|
+
"""
|
|
420
|
+
self.name = definition[0]
|
|
421
|
+
self.config = definition[1]
|
|
422
|
+
self._get_signals()
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
class Method(BaseApplication):
|
|
426
|
+
""" Object for holding information about a csp method call """
|
|
427
|
+
def __init__(self, app, unit):
|
|
428
|
+
"""Construct a new Method object.
|
|
429
|
+
|
|
430
|
+
Args:
|
|
431
|
+
app (powertrain_build.interface.application.Application): Pybuild project raster is part of.
|
|
432
|
+
unit (str): Model that the method is defined in.
|
|
433
|
+
"""
|
|
434
|
+
self.app = app
|
|
435
|
+
self.unit = unit
|
|
436
|
+
self.name = str()
|
|
437
|
+
self.namespace = str()
|
|
438
|
+
self.adapter = str()
|
|
439
|
+
self.description = None
|
|
440
|
+
self._signals = {}
|
|
441
|
+
self._insignals = set()
|
|
442
|
+
self._outsignals = set()
|
|
443
|
+
self._primitives = {}
|
|
444
|
+
self._properties = {}
|
|
445
|
+
|
|
446
|
+
def parse_definition(self, definition):
|
|
447
|
+
""" Parse the definition from powertrain_build.
|
|
448
|
+
|
|
449
|
+
Args:
|
|
450
|
+
definition (tuple):
|
|
451
|
+
name (string): Name of the model
|
|
452
|
+
configuration (dict): Configuration of method
|
|
453
|
+
"""
|
|
454
|
+
name = definition[0]
|
|
455
|
+
configuration = definition[1]
|
|
456
|
+
self.name = name
|
|
457
|
+
self.adapter = configuration['adapter']
|
|
458
|
+
self.namespace = configuration['namespace']
|
|
459
|
+
self._primitives[name] = configuration['primitive']
|
|
460
|
+
if 'description' in configuration:
|
|
461
|
+
self.description = configuration['description']
|
|
462
|
+
signals = configuration.get('ports', {})
|
|
463
|
+
outsignals = signals.get('out', {})
|
|
464
|
+
for signal_name, signal_data in outsignals.items():
|
|
465
|
+
signal = self._add_signal(signal_name)
|
|
466
|
+
signal.consumers = name
|
|
467
|
+
signal.set_producer(name)
|
|
468
|
+
self._primitives[signal_name] = signal_data['primitive']
|
|
469
|
+
self._properties[signal_name] = signal_data
|
|
470
|
+
self._outsignals.add(signal_name)
|
|
471
|
+
insignals = signals.get('in', {})
|
|
472
|
+
for signal_name, signal_data in insignals.items():
|
|
473
|
+
signal = self._add_signal(signal_name)
|
|
474
|
+
signal.consumers = name
|
|
475
|
+
signal.set_producer(name)
|
|
476
|
+
self._insignals.add(signal_name)
|
|
477
|
+
self._primitives[signal_name] = signal_data['primitive']
|
|
478
|
+
self._properties[signal_name] = signal_data
|
|
479
|
+
|
|
480
|
+
def _add_signal(self, signal_name):
|
|
481
|
+
""" Add a signal used by the method.
|
|
482
|
+
|
|
483
|
+
Args:
|
|
484
|
+
signal_name (str): Name of the signal
|
|
485
|
+
"""
|
|
486
|
+
if signal_name not in self._signals:
|
|
487
|
+
signal = Signal(signal_name, self)
|
|
488
|
+
self._signals.update({signal_name: signal})
|
|
489
|
+
else:
|
|
490
|
+
signal = self._signals[signal_name]
|
|
491
|
+
return signal
|
|
492
|
+
|
|
493
|
+
def get_signal_properties(self, signal):
|
|
494
|
+
""" Get properties for the signal from csp method configuration.
|
|
495
|
+
|
|
496
|
+
Args:
|
|
497
|
+
signal (Signal): Signal object
|
|
498
|
+
Returns:
|
|
499
|
+
properties (dict): Properties of the signal in pybuild
|
|
500
|
+
"""
|
|
501
|
+
return self._properties[signal.name]
|
|
502
|
+
|
|
503
|
+
def get_primitive(self, primitive_name):
|
|
504
|
+
""" Get primitive.
|
|
505
|
+
|
|
506
|
+
Args:
|
|
507
|
+
primitive_name (str): Name of primitive part
|
|
508
|
+
Returns:
|
|
509
|
+
primitive (str): Primitive
|
|
510
|
+
"""
|
|
511
|
+
return self._primitives[primitive_name]
|