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.
Files changed (132) hide show
  1. powertrain_build/__init__.py +40 -40
  2. powertrain_build/__main__.py +6 -6
  3. powertrain_build/a2l.py +582 -582
  4. powertrain_build/a2l_merge.py +650 -650
  5. powertrain_build/a2l_templates.py +717 -717
  6. powertrain_build/build.py +985 -985
  7. powertrain_build/build_defs.py +309 -309
  8. powertrain_build/build_proj_config.py +690 -690
  9. powertrain_build/check_interface.py +575 -575
  10. powertrain_build/cli.py +141 -141
  11. powertrain_build/config.py +542 -542
  12. powertrain_build/core.py +395 -395
  13. powertrain_build/core_dummy.py +343 -343
  14. powertrain_build/create_conversion_table.py +73 -73
  15. powertrain_build/dids.py +916 -916
  16. powertrain_build/dummy.py +157 -157
  17. powertrain_build/dummy_spm.py +252 -252
  18. powertrain_build/environmentcheck.py +52 -52
  19. powertrain_build/ext_dbg.py +255 -255
  20. powertrain_build/ext_var.py +327 -327
  21. powertrain_build/feature_configs.py +301 -301
  22. powertrain_build/gen_allsysteminfo.py +227 -227
  23. powertrain_build/gen_label_split.py +449 -449
  24. powertrain_build/handcode_replacer.py +124 -124
  25. powertrain_build/html_report.py +133 -133
  26. powertrain_build/interface/__init__.py +4 -4
  27. powertrain_build/interface/application.py +511 -511
  28. powertrain_build/interface/base.py +500 -500
  29. powertrain_build/interface/csp_api.py +490 -490
  30. powertrain_build/interface/device_proxy.py +677 -677
  31. powertrain_build/interface/ems.py +67 -67
  32. powertrain_build/interface/export_global_vars.py +121 -121
  33. powertrain_build/interface/generate_adapters.py +132 -132
  34. powertrain_build/interface/generate_hi_interface.py +87 -87
  35. powertrain_build/interface/generate_service.py +69 -69
  36. powertrain_build/interface/generate_wrappers.py +147 -147
  37. powertrain_build/interface/generation_utils.py +142 -142
  38. powertrain_build/interface/hal.py +194 -194
  39. powertrain_build/interface/model_yaml_verification.py +348 -348
  40. powertrain_build/interface/service.py +296 -296
  41. powertrain_build/interface/simulink.py +249 -249
  42. powertrain_build/interface/update_call_sources.py +180 -180
  43. powertrain_build/interface/update_model_yaml.py +186 -186
  44. powertrain_build/interface/zone_controller.py +362 -362
  45. powertrain_build/lib/__init__.py +4 -4
  46. powertrain_build/lib/helper_functions.py +127 -127
  47. powertrain_build/lib/logger.py +55 -55
  48. powertrain_build/matlab_scripts/CodeGen/BuildAutomationPyBuild.m +78 -78
  49. powertrain_build/matlab_scripts/CodeGen/Generate_A2L.m +154 -154
  50. powertrain_build/matlab_scripts/CodeGen/generateTLUnit.m +239 -239
  51. powertrain_build/matlab_scripts/CodeGen/getAsilClassification.m +28 -28
  52. powertrain_build/matlab_scripts/CodeGen/modelConfiguredForTL.m +28 -28
  53. powertrain_build/matlab_scripts/CodeGen/moveDefOutports.m +88 -88
  54. powertrain_build/matlab_scripts/CodeGen/parseCalMeasData.m +410 -410
  55. powertrain_build/matlab_scripts/CodeGen/parseCoreIdentifiers.m +139 -139
  56. powertrain_build/matlab_scripts/CodeGen/parseDIDs.m +141 -141
  57. powertrain_build/matlab_scripts/CodeGen/parseInPorts.m +106 -106
  58. powertrain_build/matlab_scripts/CodeGen/parseIncludeConfigs.m +25 -25
  59. powertrain_build/matlab_scripts/CodeGen/parseModelInfo.m +38 -38
  60. powertrain_build/matlab_scripts/CodeGen/parseNVM.m +81 -81
  61. powertrain_build/matlab_scripts/CodeGen/parseOutPorts.m +120 -120
  62. powertrain_build/matlab_scripts/CodeGen/parsePreProcBlks.m +23 -23
  63. powertrain_build/matlab_scripts/CodeGen/struct2JSON.m +128 -128
  64. powertrain_build/matlab_scripts/CodeGen/updateCodeSwConfig.m +31 -31
  65. powertrain_build/matlab_scripts/Init_PyBuild.m +91 -91
  66. powertrain_build/matlab_scripts/__init__.py +2 -2
  67. powertrain_build/matlab_scripts/helperFunctions/Get_Full_Name.m +46 -46
  68. powertrain_build/matlab_scripts/helperFunctions/Get_SrcLines.m +12 -12
  69. powertrain_build/matlab_scripts/helperFunctions/Init_Models.m +78 -78
  70. powertrain_build/matlab_scripts/helperFunctions/Init_Projects.m +67 -67
  71. powertrain_build/matlab_scripts/helperFunctions/Read_Units.m +34 -34
  72. powertrain_build/matlab_scripts/helperFunctions/SetProjectTimeSamples.m +26 -26
  73. powertrain_build/matlab_scripts/helperFunctions/Strip_Suffix.m +16 -16
  74. powertrain_build/matlab_scripts/helperFunctions/followLink.m +118 -118
  75. powertrain_build/matlab_scripts/helperFunctions/getCodeSwitches.m +50 -50
  76. powertrain_build/matlab_scripts/helperFunctions/getConsumerBlocks.m +30 -30
  77. powertrain_build/matlab_scripts/helperFunctions/getDefBlock.m +39 -39
  78. powertrain_build/matlab_scripts/helperFunctions/getDefOutport.m +58 -58
  79. powertrain_build/matlab_scripts/helperFunctions/getDstBlocks.m +19 -19
  80. powertrain_build/matlab_scripts/helperFunctions/getDstLines.m +13 -13
  81. powertrain_build/matlab_scripts/helperFunctions/getInterfaceSignals.m +37 -37
  82. powertrain_build/matlab_scripts/helperFunctions/getName.m +37 -37
  83. powertrain_build/matlab_scripts/helperFunctions/getPath.m +6 -6
  84. powertrain_build/matlab_scripts/helperFunctions/getProperValue.m +21 -21
  85. powertrain_build/matlab_scripts/helperFunctions/getSrcBlocks.m +19 -19
  86. powertrain_build/matlab_scripts/helperFunctions/getSrcLines.m +13 -13
  87. powertrain_build/matlab_scripts/helperFunctions/loadLibraries.m +10 -10
  88. powertrain_build/matlab_scripts/helperFunctions/loadjson.m +6 -6
  89. powertrain_build/matlab_scripts/helperFunctions/modifyEnumStructField.m +21 -21
  90. powertrain_build/matlab_scripts/helperFunctions/removeConfigDuplicates.m +31 -31
  91. powertrain_build/matlab_scripts/helperFunctions/sortSystemByClass.m +26 -26
  92. powertrain_build/matlab_scripts/helperFunctions/tl_getfast.m +89 -89
  93. powertrain_build/matlab_scripts/helperFunctions/topLevelSystem.m +20 -20
  94. powertrain_build/matlab_scripts/helperFunctions/updateModels.m +131 -131
  95. powertrain_build/memory_section.py +224 -224
  96. powertrain_build/nvm_def.py +729 -729
  97. powertrain_build/problem_logger.py +86 -86
  98. powertrain_build/pt_matlab.py +430 -430
  99. powertrain_build/pt_win32.py +144 -144
  100. powertrain_build/replace_compu_tab_ref.py +105 -105
  101. powertrain_build/rte_dummy.py +254 -254
  102. powertrain_build/sched_funcs.py +209 -207
  103. powertrain_build/signal.py +7 -7
  104. powertrain_build/signal_if_html_rep.py +221 -221
  105. powertrain_build/signal_if_html_rep_all.py +302 -302
  106. powertrain_build/signal_incons_html_rep.py +180 -180
  107. powertrain_build/signal_incons_html_rep_all.py +366 -366
  108. powertrain_build/signal_incons_html_rep_base.py +168 -168
  109. powertrain_build/signal_inconsistency_check.py +641 -641
  110. powertrain_build/signal_interfaces.py +864 -864
  111. powertrain_build/templates/Index_SigCheck_All.html +22 -22
  112. powertrain_build/templates/Index_SigIf_All.html +19 -19
  113. powertrain_build/types.py +218 -218
  114. powertrain_build/unit_configs.py +419 -419
  115. powertrain_build/user_defined_types.py +660 -660
  116. powertrain_build/versioncheck.py +66 -66
  117. powertrain_build/wrapper.py +512 -512
  118. powertrain_build/xlrd_csv.py +87 -87
  119. powertrain_build/zone_controller/__init__.py +4 -4
  120. powertrain_build/zone_controller/calibration.py +176 -176
  121. powertrain_build/zone_controller/composition_yaml.py +880 -878
  122. {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/METADATA +100 -100
  123. powertrain_build-1.13.3.dev3.dist-info/RECORD +130 -0
  124. {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/WHEEL +1 -1
  125. {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/LICENSE +202 -202
  126. powertrain_build-1.13.3.dev3.dist-info/pbr.json +1 -0
  127. powertrain_build-1.13.1.dist-info/RECORD +0 -130
  128. powertrain_build-1.13.1.dist-info/pbr.json +0 -1
  129. {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/entry_points.txt +0 -0
  130. {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/AUTHORS +0 -0
  131. {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/NOTICE +0 -0
  132. {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]