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,864 +1,864 @@
1
- # Copyright 2024 Volvo Car Corporation
2
- # Licensed under Apache 2.0.
3
-
4
- """Signal interface module.
5
-
6
- Module for parsing the external signal interface definition files, and
7
- checking the signal interfaces between Supplier and VCC sw,
8
- and also internally between VCC sw-units.
9
- """
10
- import re
11
- import csv
12
- import time
13
- import copy
14
- from pprint import pformat
15
- from pathlib import Path
16
- from collections import defaultdict
17
-
18
- from powertrain_build.build_proj_config import BuildProjConfig
19
- from powertrain_build.feature_configs import FeatureConfigs
20
- from powertrain_build.lib.helper_functions import deep_dict_update
21
- from powertrain_build.problem_logger import ProblemLogger
22
- from powertrain_build.types import get_ec_type
23
- from powertrain_build.unit_configs import UnitConfigs, CodeGenerators
24
- from powertrain_build.user_defined_types import UserDefinedTypes
25
-
26
-
27
- class CsvReaderCounter:
28
- """Csv wrapper to keep track of file row."""
29
-
30
- def __init__(self, _csvreader):
31
- """Init."""
32
- self.csvreader = _csvreader
33
- self.counter = 0
34
-
35
- def __iter__(self):
36
- """Iterate."""
37
- return self
38
-
39
- def __next__(self):
40
- """Next."""
41
- self.counter += 1
42
- return next(self.csvreader)
43
-
44
- def get_count(self):
45
- """Get current row number."""
46
- return self.counter
47
-
48
-
49
- class InterfaceValueException(Exception):
50
- """Exception for errors interface list."""
51
-
52
- def __init__(self, signal_name, erroneous_field):
53
- """Init."""
54
- self.erroneous_field = erroneous_field
55
- self.signal_name = signal_name
56
-
57
-
58
- class SignalInterfaces(ProblemLogger):
59
- """Base class for signal interfaces."""
60
-
61
- def __init__(self, unit_cfg, model_names=None):
62
-
63
- self._unit_cfg = unit_cfg
64
- self._signals_ext_nok = {'inports': {}, 'outports': {}}
65
- self._signals_ext_ok = {'inports': {}, 'outports': {}}
66
- self._result = self.__ddict_factory()
67
-
68
- if model_names is None:
69
- self.models_to_check = set()
70
- else:
71
- self.models_to_check = set(model_names)
72
-
73
- def get_external_outputs(self):
74
- """Get the external outputs.
75
-
76
- Should be implemented by the subclasses.
77
-
78
- Returns:
79
- dict: External outputs
80
- """
81
- raise NotImplementedError
82
-
83
- def get_external_inputs(self):
84
- """Get the internal outputs.
85
-
86
- Should be implemented by the subclasses.
87
-
88
- Returns:
89
- dict: Internal outputs
90
- """
91
- raise NotImplementedError
92
-
93
- @classmethod
94
- def __ddict_factory(cls):
95
- """Generate recursive defaultdict."""
96
- return defaultdict(cls.__ddict_factory)
97
-
98
- @property
99
- def should_all_models_be_checked(self):
100
- """True if there are no specific models to be checked
101
-
102
- :rtype: bool
103
- """
104
- return len(self.models_to_check) == 0
105
-
106
- def should_check_model(self, model_name):
107
- """True if MODEL_NAME is in SELF.MODEL_NAMES
108
-
109
- :rtype: bool
110
- """
111
- return self.should_all_models_be_checked or model_name in self.models_to_check
112
-
113
- def contains_model_to_check(self, model_names):
114
- """True if MODEL_NAMES contains a model in SELF.MODEL_NAMES
115
-
116
- :param model_names: iterable
117
- :rtype: bool
118
- """
119
- return any((m in self.models_to_check for m in model_names))
120
-
121
- @staticmethod
122
- def _eq_var(var_a, var_b, par):
123
- """Check if variables are equal, with some modifications.
124
-
125
- par contains the key for which type of parameter is compared
126
- E.g. nan is equal to '-' and ''. Furthermore, all variables
127
- are converted to strings before making the comparison.
128
- """
129
- a_s = str(var_a)
130
- b_s = str(var_b)
131
- if par in ['min', 'max']:
132
- a_s = re.sub('nan', '-', a_s)
133
- b_s = re.sub('nan', '-', b_s)
134
- if re.match(r'[-+.0-9eE]+', a_s) and re.match(r'[-+.0-9eE]+', b_s):
135
- try:
136
- a_f = float(var_a)
137
- b_f = float(var_b)
138
- return a_f == b_f
139
- except ValueError:
140
- pass
141
- return a_s == b_s
142
-
143
- def _check_var_def(self, ext_def, unit_def):
144
- """Check that the parameters for the variable definitions are the same.
145
-
146
- Check that the parameters are the same for all units that use the parameter.
147
- """
148
- res = {}
149
- ignored_parameters = ['class', 'configs', 'handle', 'default', 'description', 'unit']
150
- for unit, unit_definitions in unit_def.items():
151
- for parameter, definition in ext_def.items():
152
- if parameter in ignored_parameters or parameter not in unit_definitions:
153
- continue
154
- unit_definition = unit_definitions[parameter]
155
- if not self._eq_var(definition, unit_definition, parameter):
156
- res.setdefault(unit, {})[parameter] = f"{definition} != {unit_definition}"
157
-
158
- return res if res else None
159
-
160
- def _add_incons_to_ext_dict(self, res, var, incons):
161
- """Add inconsistencies to the ext dict."""
162
- if incons is not None:
163
- for unit, incon in incons.items():
164
- if self.should_check_model(unit):
165
- if var in res['inconsistent_defs'][unit]:
166
- res['inconsistent_defs'][unit][var].update(incon)
167
- else:
168
- res['inconsistent_defs'][unit][var] = incon
169
-
170
- def _add_incons_to_dict(self, res, var, incons):
171
- """Add iconsistent data to a dict."""
172
- if incons is not None:
173
- for unit, incon in incons.items():
174
- if self.should_check_model(unit):
175
- if var in res[unit]['inconsistent_defs']:
176
- res[unit]['inconsistent_defs'][var].update(incon)
177
- else:
178
- res[unit]['inconsistent_defs'][var] = incon
179
-
180
- def _gen_int_outp_set(self, tot_unit_cfg):
181
- """Generate a set of signals to check for internal output parameters."""
182
- tot_cfg = tot_unit_cfg.get('outports', {})
183
- external_ports = {**self._signals_ext_ok['outports'], **self._signals_ext_nok['outports']}
184
-
185
- # only check outports that are not defined in the external io definition
186
- non_ext_outp = set(tot_cfg.keys()) - set(external_ports.keys())
187
- return non_ext_outp
188
-
189
- def _gen_int_inp_set(self, tot_unit_cfg):
190
- """Generate a set of signals to check for internal input parameters."""
191
- tot_cfg = tot_unit_cfg.get('inports', {})
192
- external_ports = {**self._signals_ext_ok['inports'], **self._signals_ext_nok['inports']}
193
-
194
- # only check inports that are not defined in the external io definition
195
- non_ext_inp = set(tot_cfg.keys()) - set(external_ports.keys())
196
- return non_ext_inp
197
-
198
- def _gen_unit_var_dict(self, res, chk_type, variables, in_out):
199
- """Generate a dict with vars as keys, and units in which they are used as a list."""
200
- tot_cfg = self._unit_cfg.get_per_cfg_unit_cfg()
201
- for var in variables:
202
- for unit in tot_cfg[in_out][var]:
203
- if self.should_check_model(unit):
204
- if var in res[unit][chk_type]:
205
- res[unit][chk_type][var] = {}
206
- else:
207
- res[unit][chk_type][var] = {}
208
-
209
- def _check_external_outp(self):
210
- """Check that all external outputs (VCC -> supplier).
211
-
212
- Check that all external outputs are produced,
213
- that consumed signals (Supplier -> VCC) are defined as inputs
214
- in the external IO definition file, and that the signal
215
- definitions are consistent between io and unit definitions.
216
- """
217
- ext_out = self.get_external_outputs()
218
- tot_cfg = self._unit_cfg.get_per_cfg_unit_cfg()
219
- res = self._result['sigs']['ext']
220
- # check that all output signals are produced by VCC SW
221
- if 'outports' in tot_cfg:
222
- for _, data in ext_out.items():
223
- for var, var_def in data.items():
224
- if var not in tot_cfg['outports']:
225
- res['missing'][var] = {}
226
- self._signals_ext_nok['outports'][var] = var_def
227
- else:
228
- self._signals_ext_ok['outports'][var] = var_def
229
- tmp = self._check_var_def(var_def, tot_cfg['outports'][var])
230
- self._add_incons_to_ext_dict(res, var, tmp)
231
-
232
- def _check_external_inp(self):
233
- """Check all external input signals.
234
-
235
- Check that outputs are produced and,
236
- that consumed signals are defined as inputs
237
- in the external IO definition file.
238
- """
239
- ext_inp = self.get_external_inputs()
240
- # self.debug('ext_inp: %s', pformat(ext_inp))
241
- tot_cfg = self._unit_cfg.get_per_cfg_unit_cfg()
242
- res = self._result['sigs']['ext']
243
- # check that all input signals are used by VCC SW
244
- if 'inports' in tot_cfg:
245
- for data in ext_inp.values():
246
- for var, var_def in data.items():
247
- if var not in tot_cfg['inports']:
248
- if var not in res['unused']:
249
- res['unused'][var] = {}
250
- self._signals_ext_nok['inports'][var] = var_def
251
- else:
252
- self._signals_ext_ok['inports'][var] = var_def
253
- tmp = self._check_var_def(var_def, tot_cfg['inports'][var])
254
- self._add_incons_to_ext_dict(res, var, tmp)
255
-
256
- def _check_internal_io(self):
257
- """Check internal signal io.
258
-
259
- Function which checks that:
260
- 1. all signals consumed in models, are produced
261
- in antoher model.
262
- 2. all outputs are consumed (warning)
263
- 3. check that all signal definitions are the same as the producing unit.
264
- 4. a signal is only produced in one model (per prj).
265
- this function return a tuple with the above content
266
-
267
- TODO:
268
- Shall we add a check for multiple inputs of the same signal?*
269
- """
270
- tot_cfg = self._unit_cfg.get_per_cfg_unit_cfg()
271
- internal_inport_signals = self._gen_int_inp_set(tot_cfg)
272
- internal_outport_signals = self._gen_int_outp_set(tot_cfg)
273
- all_outports = set(tot_cfg.get('outports', {}).keys())
274
- res = self._result['sigs']['int']
275
- # 1. all signals consumed in models, are produced in another model.
276
- missing = internal_inport_signals - all_outports
277
- self._gen_unit_var_dict(res, 'missing', missing, 'inports') # error!
278
- # 2. all outputs are consumed (warning)
279
- unused = internal_outport_signals - internal_inport_signals
280
- self._gen_unit_var_dict(res, 'unused', unused, 'outports') # warning?
281
- # 3. check that all signal definitions are the same as the producing
282
- # unit.
283
- input_signals_to_check = internal_inport_signals - missing
284
- for signal in input_signals_to_check:
285
- unit_key = list(tot_cfg['outports'][signal].keys())
286
- # if output is defined in multiple functions, test #4 will catch this
287
- if len(unit_key) == 1:
288
- outport_def = tot_cfg['outports'][signal][unit_key[0]]
289
- tmp = self._check_var_def(outport_def,
290
- tot_cfg['inports'][signal])
291
- self._add_incons_to_dict(res, signal, tmp)
292
-
293
- # 4. check that we have not screwed up any of the consuming models
294
- external_signals_to_check = internal_outport_signals - unused
295
- for signal in external_signals_to_check:
296
- unit_keys = list(tot_cfg['inports'][signal].keys())
297
- my_outport_definition = tot_cfg['outports'][signal]
298
- for unit_key in unit_keys:
299
- others_inport_def = tot_cfg['inports'][signal][unit_key]
300
- tmp = self._check_var_def(others_inport_def,
301
- my_outport_definition)
302
- self._add_incons_to_dict(res, signal, tmp)
303
-
304
- # 5. there should only be one outport per config.
305
- multiple_defs = []
306
- if 'outports' in tot_cfg:
307
- multiple_defs = [x for x in tot_cfg['outports']
308
- if len(tot_cfg['outports'][x]) > 1]
309
- self._gen_unit_var_dict(res, 'multiple_defs', multiple_defs, 'outports')
310
-
311
- def _check_config(self):
312
- """Check the interfaces given a certain project config.
313
-
314
- The result of the checks is stored in the dict self._result
315
- """
316
- # reset the handled external signals for each configuration
317
- self._signals_ext_ok = {'inports': {}, 'outports': {}}
318
- # has to be done in this order since there is an implicit dependency
319
- # to self._signals_ext_ok
320
- self._check_external_outp()
321
- self._check_external_inp()
322
- self._check_internal_io()
323
-
324
- def check_config(self):
325
- """Check configurations for specific project.
326
-
327
- Returns:
328
- dict: the result of the check with the following format
329
-
330
- ::
331
-
332
- {
333
- "sigs": { "ext": {"missing": {},
334
- "unused": {},
335
- "inconsistent_defs": {}},
336
- "int": {"UNIT_NAME": {"missing": {},
337
- "unused": {},
338
- "multiple_defs": {}
339
- "inconsistent_defs": {}}
340
- }
341
- }
342
-
343
- """
344
- self._result = self.__ddict_factory()
345
- self._check_config()
346
- # self.debug("%s", pformat(self._result))
347
- # restore the current conofiguration
348
-
349
- return self._result
350
-
351
-
352
- class YamlSignalInterfaces(SignalInterfaces):
353
- """Interface configurations defined in yaml files."""
354
-
355
- @staticmethod
356
- def from_config_file(project_config_path):
357
- """Create a YamlSignalInterfaces instance from a project config file.
358
-
359
- Args:
360
- project_config_path (str): path to the project config file
361
-
362
- Returns:
363
- YamlSignalInterfaces: instance of YamlSignalInterfaces
364
- """
365
- build_project_config = BuildProjConfig(project_config_path)
366
- feature_cfg = FeatureConfigs(build_project_config)
367
- unit_config = UnitConfigs(build_project_config, feature_cfg)
368
- user_defined_types = UserDefinedTypes(build_project_config, unit_config)
369
-
370
- return YamlSignalInterfaces(
371
- prj_cfg=build_project_config,
372
- unit_cfg=unit_config,
373
- feature_cfg=feature_cfg,
374
- user_defined_types=user_defined_types
375
- )
376
-
377
- def __init__(self, prj_cfg, unit_cfg, feature_cfg, user_defined_types, model_names=None):
378
- """Class initializer.
379
-
380
- Args:
381
- prj_cfg (BuildProjConfig): configures which units are active in the current project.
382
- unit_cfg (UnitConfigs): class instance containing all the unit configuration parameters.
383
- feature_cfg (FeatureConfig): Feature configs from SPM_Codeswitch_Setup.
384
- user_defined_types (UserDefinedTypes): Class holding user defined data types.
385
- model_names (set): models that should be included in the check, default is all models
386
- """
387
- super().__init__(unit_cfg, model_names=model_names)
388
-
389
- # Postpone imports to here to work on machines without PyYaml installed
390
- from powertrain_build.interface.base import filter_signals
391
- from powertrain_build.interface.application import Application, get_internal_domain
392
- from powertrain_build.interface.generation_utils import get_interface, get_method_interface
393
- from powertrain_build.interface.hal import HALA
394
- from powertrain_build.interface.device_proxy import DPAL
395
- from powertrain_build.interface.zone_controller import ZCAL
396
- from powertrain_build.interface.service import ServiceFramework
397
-
398
- app = Application()
399
- app.pybuild['build_cfg'] = prj_cfg
400
- app.name = app.pybuild['build_cfg'].name
401
- app.pybuild['feature_cfg'] = feature_cfg
402
- app.pybuild['unit_vars'] = unit_cfg.get_per_unit_cfg_total()
403
- app.pybuild['user_defined_types'] = user_defined_types
404
-
405
- translation_files = app.get_translation_files()
406
-
407
- ecu_supplier = prj_cfg.get_ecu_info()[0]
408
- self.zc_spec = {}
409
- self.hal_spec = {}
410
- self.dp_spec = {}
411
- self.sfw_spec = {}
412
- self.sa_spec = {}
413
- self.service_spec = {}
414
- self.mthd_spec = {}
415
- if prj_cfg.get_code_generation_config(item='generateYamlInterfaceFile'):
416
- zc_app = ZCAL(app)
417
- self.zc_spec = get_interface(app, zc_app)
418
- self.composition_spec = zc_app.composition_spec
419
- elif ecu_supplier == 'HI':
420
- hi_app = DPAL(app)
421
- self.dp_spec = get_interface(app, hi_app)
422
- else:
423
- hala = HALA(app)
424
- dp = DPAL(app)
425
- swf = ServiceFramework(app)
426
- hala.parse_definition(translation_files)
427
- self.dp_spec = get_interface(app, dp)
428
- self.sfw_spec = get_interface(app, swf)
429
- self.mthd_spec = get_method_interface(app)
430
- self.service_spec = self.get_availability(app, unit_cfg.code_generators)
431
- rasters = app.get_rasters()
432
- self.debug('Rasters: %s', rasters)
433
- internal = get_internal_domain(rasters)
434
- properties_from_json = [
435
- {"destination": "min", "source": "min", "default": "-"},
436
- {"destination": "max", "source": "max", "default": "-"},
437
- {"destination": "variable_type", "source": "type"},
438
- {"destination": "offset", "source": "offset", "default": "-"},
439
- {"destination": "factor", "source": "lsb", "default": 1},
440
- {"destination": "description", "source": "description"},
441
- {"destination": "unit", "source": "unit", "default": "-"},
442
- ]
443
- for raster in rasters:
444
- hala.name = raster.name
445
- hala.clear_signal_names()
446
- hala.add_signals(filter_signals(raster.insignals, internal), 'insignals', properties_from_json)
447
- hala.add_signals(raster.outsignals, 'outsignals', properties_from_json)
448
- self.debug('Current HALA: %s', hala)
449
- self.hal_spec[raster.name] = hala.to_dict()
450
-
451
- @staticmethod
452
- def get_availability(app, code_generators={CodeGenerators.target_link}):
453
- """Get the availability of services.
454
-
455
- Args:
456
- app (Application): Application instance
457
- code_generators (set): Code generators to include in the availability check
458
-
459
- Returns:
460
- dict: Availability of services
461
- """
462
- tl_type = 'Bool'
463
- variable_type = tl_type if CodeGenerators.target_link in code_generators else get_ec_type(tl_type)
464
- services = app.get_service_mapping()
465
- spec = {}
466
- for interface, service in services.items():
467
- camel_interface = ''.join(part.title() for part in interface.split('_'))
468
- spec[interface] = {
469
- 'variable': f'yVcSfw_B_{camel_interface}IsAvailable',
470
- 'variable_type': variable_type,
471
- 'property_type': 'bool',
472
- 'service': service,
473
- 'default': 0,
474
- 'length': 1,
475
- 'property': 'inherent',
476
- 'offset': '-',
477
- 'factor': 1,
478
- 'range': {'min': '0', 'max': '1'},
479
- 'init': 0,
480
- 'description': f'Availability of {interface} in {service}',
481
- 'unit': '-',
482
- 'group': None,
483
- 'model': interface
484
- }
485
- return spec
486
-
487
- def get_externally_defined_ports(self):
488
- """Get ports defined by suppliers."""
489
- outports = []
490
- for raster in self.dp_spec.values():
491
- for spec in raster['consumer']:
492
- outports.append(spec['variable'])
493
- for raster in self.hal_spec.values():
494
- for spec in raster['consumer']:
495
- outports.append(spec['variable'])
496
- for raster in self.sfw_spec.values():
497
- for spec in raster['consumer']:
498
- outports.append(spec['variable'])
499
- for raster in self.sa_spec.values():
500
- for spec in raster['consumer']:
501
- outports.append(spec['variable'])
502
- for raster in self.zc_spec.values():
503
- for spec in raster['consumer']:
504
- outports.append(spec['variable'])
505
- return outports
506
-
507
- def get_external_io(self):
508
- """Get the variable definitions for the signal IO for a given config.
509
-
510
- Returns:
511
- dict: Variable definitions for the supplier in-/out-put signals for the configuration
512
- """
513
- def normalize_spec(spec):
514
- """ Convert Yaml spec to normal pybuild spec.
515
-
516
- Arguments:
517
- spec (dict): Yaml specification of signal
518
- Returns:
519
- spec (dict): Pybuild specification of signal
520
- """
521
- spec2 = copy.copy(spec) # copy by value
522
- spec2['type'] = spec['variable_type']
523
- spec2['min'] = spec['range']['min']
524
- spec2['max'] = spec['range']['max']
525
- return spec2
526
-
527
- def set_spec(target):
528
- new_spec = normalize_spec(spec)
529
- if new_spec.get('debug'):
530
- debug[target][spec['variable']] = new_spec
531
- elif spec.get('dependability'):
532
- dependability[target][spec['variable']] = new_spec
533
- else:
534
- normal[target][spec['variable']] = new_spec
535
-
536
- normal = {}
537
- dependability = {}
538
- secure = {} # Only supported by CSV interfaces
539
- debug = {}
540
-
541
- for field in ['input', 'output', 'status']:
542
- normal[field] = {}
543
- dependability[field] = {}
544
- debug[field] = {}
545
-
546
- spec_confs = [self.dp_spec, self.sfw_spec, self.sa_spec, self.hal_spec, self.zc_spec]
547
- for spec_conf in spec_confs:
548
- for raster in spec_conf.values():
549
- for spec in raster['consumer']:
550
- set_spec("input")
551
- for spec in raster['producer']:
552
- set_spec("output")
553
- for spec in self.service_spec.values():
554
- set_spec('status')
555
- for method_data in self.mthd_spec.values():
556
- for _, spec in method_data['ports']['out'].items():
557
- set_spec('input')
558
- for _, spec in method_data['ports']['in'].items():
559
- set_spec('output')
560
- return normal, dependability, secure, debug
561
-
562
- def get_external_signals(self, find_output=True):
563
- """Get the external signals.
564
-
565
- Args:
566
- find_output (bool): True if output signals should be returned, False if input signals should be returned
567
-
568
- Returns:
569
- dict: External signals
570
- """
571
- io_type = self.get_external_io() # (normal, dependability, secure (unsupported), debug)
572
- directional_io = defaultdict(dict)
573
- for io in io_type:
574
- for signal_type, signals in io.items():
575
- if find_output and signal_type == 'output':
576
- directional_io[signal_type].update(signals)
577
- elif not find_output and signal_type == 'input':
578
- directional_io[signal_type].update(signals)
579
- return directional_io
580
-
581
- def get_external_outputs(self):
582
- return self.get_external_signals(True)
583
-
584
- def get_external_inputs(self):
585
- return self.get_external_signals(False)
586
-
587
-
588
- class CsvSignalInterfaces(SignalInterfaces):
589
- """Interface configurations for all units and all configs.
590
-
591
- Provides methods for retrieving the currently
592
- used signal configurations of a unit.
593
- """
594
-
595
- convs = (('~=', '!='), ('~', ' not '), ('!', ' not '), (r'\&\&', ' and '),
596
- (r'\|\|', ' or '))
597
- # Common excel sheet definitions, adapted for CVS.
598
- VAR_COL = 0 # Variable name
599
- DATA_COL = 2 # Type, min, max, unit, comment, init
600
- PROJ_COL = 9 # Variable number of projects
601
- HEADER_ROW = 3 # which row the header info is found
602
- DATA_ROW = 5 # the row which data starts
603
- WS_NAMES = ['EMS-Output', 'EMS-Input', 'LIN-Output', 'LIN-Input',
604
- 'CAN-Output', 'CAN-Input', 'Private CAN-Output',
605
- 'Private CAN-Input']
606
-
607
- def __init__(self, prj_cfg, unit_cfg, models=None):
608
- """Class initializer.
609
-
610
- Args:
611
- prj_cfg (BuildProjConfig): configures which units are active in the
612
- current project
613
- unit_cfg (UnitConfigs): class instance containing all the unit
614
- configuration parameters
615
- models (str or list): Models to get interface for. default: 'all'
616
-
617
- """
618
- super().__init__(unit_cfg, model_names=models)
619
- self.interfaces = {}
620
-
621
- self.__prjs = {}
622
- self.__out_dict = {}
623
- self.__all_prjs = set()
624
- self.__prj2col = {}
625
- self.__prj_col_range = {}
626
-
627
- prj2index = {}
628
- self.name = prj_cfg.get_prj_config()
629
- file_path = prj_cfg.get_if_cfg_dir()
630
- self._prj_cfg = prj_cfg
631
- self._parse_io_cnfg(file_path)
632
- for k in self.__prj2col:
633
- self.__prj2col[k] = prj2index
634
-
635
- def _parse_io_cnfg(self, interface_directory):
636
- """Parse the CSV config files."""
637
- start_time = time.time()
638
- self.info('******************************************************')
639
- self.info('Start parsing SPM-Interface definition files')
640
- for ws_name in self.WS_NAMES:
641
- self.debug("read sheet %s start", ws_name)
642
- file_path = Path(interface_directory, ws_name + '.csv')
643
- try:
644
- with open(file_path, newline='') as fhandle:
645
- if ws_name not in self.interfaces:
646
- self.interfaces.update({ws_name: {}})
647
- interface = self.interfaces[ws_name]
648
- csvreader = csv.reader(fhandle, delimiter=';', strict=True)
649
- csvreader = CsvReaderCounter(csvreader)
650
- try:
651
- for _ in range(self.HEADER_ROW-1):
652
- next(csvreader)
653
- row = next(csvreader)
654
- # TODO: Find why excel export script sometimes skips first field
655
- # on each line if field is empty.
656
- # Workaround:
657
- row = row[1:] if row[0] == '' else row
658
- try:
659
- col = self._get_proj_col(row)
660
- except IndexError:
661
- self.warning('Project %s not defined in %s', self.name, file_path.stem)
662
- continue
663
-
664
- for _ in range(self.HEADER_ROW, self.DATA_ROW-1):
665
- next(csvreader)
666
- except StopIteration:
667
- self.critical('File %s has bad format, not enough header rows', file_path.stem)
668
- for row in csvreader:
669
- row = row[1:] if row[0] == '' else row
670
- try:
671
- signal, data = self._get_var_def(row)
672
- data.update({'element_index': csvreader.get_count()})
673
- except InterfaceValueException as interface_exception:
674
- self.critical('Missing value for key "%s" in list "%s" at index "%s"',
675
- interface_exception.erroneous_field,
676
- file_path.stem, csvreader.get_count())
677
- continue
678
- if signal not in interface:
679
- interface.update({signal: data})
680
- interface[signal]['IOType'] = row[col].lower()
681
- self.debug("read sheet %s end", ws_name)
682
- except FileNotFoundError:
683
- self.info('Project %s does not have a %s', self.name, ws_name)
684
- self.info('Finished parsing SPM-Interface definition file (in %4.2f s)', time.time() - start_time)
685
-
686
- def __repr__(self):
687
- """Get string representation of object."""
688
- return pformat(self.__out_dict)
689
-
690
- def _get_proj_col(self, row):
691
- """Get the projects in the config document.
692
-
693
- Parses supplied worksheet row and finds all the projects defined, and
694
- stores the result in the class variable __all_prjs.
695
- Furthermore, a dict prj2col, maps the project name to a list index
696
- in the prj key in the __out_dict dict, and
697
- sets the class variables __prj2col, and __prj_col_range contains
698
- a list with column indexes in the excel sheet, used for parsing the
699
- excel, the result is stored in the interal __out_dict
700
-
701
- interfaces example:
702
-
703
- ::
704
-
705
- {
706
- 'CAN-Input': {
707
- 'sVCcm_D_HvacCoolgEnaRe': {
708
- 'IOType': 'd'
709
- 'description': 'Enable Ac rear HVAC',
710
- 'init': 0,
711
- 'max': 1,
712
- 'min': 0,
713
- 'type': 'UInt8',
714
- 'unit': '-'
715
- }
716
- }
717
- }
718
-
719
- """
720
- col = self.PROJ_COL
721
- while row[col] is not None and row[col] != '':
722
- if row[col] == self.name:
723
- return col
724
- col += 1
725
- if col > 200:
726
- break
727
-
728
- raise IndexError
729
-
730
- @staticmethod
731
- def _get_var_def(row):
732
- """Get the variable definition from a row in a sheet.
733
-
734
- Returns:
735
- dict: with var definitions
736
-
737
- """
738
- keys = ['type', 'min', 'max', 'unit', 'description', 'init']
739
-
740
- vals = []
741
- for col in [0, 2, 3, 4, 5, 6, 7]:
742
- val = row[col]
743
- if isinstance(val, str):
744
- val = val.strip()
745
- if re.fullmatch('[+-]?[0-9]+', val):
746
- val = int(val)
747
- elif re.fullmatch('[+-]?[0-9.,]+', val):
748
- val = float(val.replace(',', '.'))
749
- vals.append(val)
750
- critical_attributes = ['type']
751
- signal_name = vals[0]
752
- signal_attributes = dict(zip(keys, vals[1:]))
753
- for field in critical_attributes:
754
- if signal_attributes[field] == '':
755
- raise InterfaceValueException(signal_name, field)
756
- return signal_name, signal_attributes
757
-
758
- def get_raw_io_cnfg(self):
759
- """Get the raw IO-Config parsed from the config file.
760
-
761
- Returns:
762
- dict: raw configuration information from the config file
763
-
764
- dict example:
765
-
766
- ::
767
-
768
- {
769
- 'CAN-Input': {
770
- 'sVCcm_D_HvacCoolgEnaRe': {
771
- 'description': 'Enable Ac rear HVAC',
772
- 'init': 0,
773
- 'max': 1,
774
- 'min': 0,
775
- 'project_def': 'D',
776
- 'type': 'UInt8',
777
- 'unit': '-'
778
- }
779
- }
780
- }
781
-
782
-
783
- """
784
- return self.interfaces
785
-
786
- def get_io_config(self):
787
- """Get an IO-Config for a specific project.
788
-
789
- Returns:
790
- tuple: a tuple with three dicts - (nrm_dict, dep_dict, dbg_dict)
791
-
792
- """
793
- normal = {}
794
- dep = {}
795
- secure = {}
796
- debug = {}
797
- for interface_name, interface in self.interfaces.items():
798
- normal[interface_name] = {}
799
- dep[interface_name] = {}
800
- secure[interface_name] = {}
801
- debug[interface_name] = {}
802
- for signal_name, signal in interface.items():
803
- prj_def = signal.get('IOType', '-')
804
- if prj_def in 'xd':
805
- normal[interface_name][signal_name] = copy.deepcopy(signal)
806
- elif prj_def in 's':
807
- dep[interface_name][signal_name] = copy.deepcopy(signal)
808
- elif prj_def == 'sc':
809
- secure[interface_name][signal_name] = copy.deepcopy(signal)
810
- if prj_def in 'd':
811
- debug[interface_name][signal_name] = copy.deepcopy(signal)
812
- return normal, dep, secure, debug
813
-
814
- def get_external_io(self):
815
- """Get the variable definitions for the signal IO for a given config.
816
-
817
- Returns:
818
- dict: Variable definitions for the supplier in-/out-put signals for the configuration
819
-
820
- """
821
- return self.get_io_config()
822
-
823
- def get_external_inputs(self):
824
- """Get variable definitions for the supplier signal Inputs.
825
-
826
- Returns:
827
- dict: variable definitions for the supplier input signals
828
-
829
- """
830
- cfg, cfg_dep, cfg_sec = self.get_io_config()[:3]
831
- deep_dict_update(cfg, cfg_dep)
832
- deep_dict_update(cfg, cfg_sec)
833
- out = {}
834
- for key, value in cfg.items():
835
- if re.match('.*Input$', key) is not None:
836
- out[key] = value
837
- return out
838
-
839
- def get_external_outputs(self):
840
- """Get variable definitions for the supplier signal Outputs.
841
-
842
- Returns:
843
- dict: variable definitions for the supplier output signals
844
-
845
- """
846
- cfg, cfg_dep, cfg_sec = self.get_io_config()[:3]
847
- deep_dict_update(cfg, cfg_dep)
848
- deep_dict_update(cfg, cfg_sec)
849
- out = {}
850
- for key, value in cfg.items():
851
- if re.match(r'.*Output$', key) is not None:
852
- out[key] = value
853
- return out
854
-
855
- def get_externally_defined_ports(self):
856
- """Get ports defined by suppliers."""
857
- externally_defined_ports = set()
858
- external_outports = self.get_external_outputs()
859
- for external_ports in external_outports.values():
860
- externally_defined_ports.update(external_ports)
861
- external_inports = self.get_external_inputs()
862
- for external_ports in external_inports.values():
863
- externally_defined_ports.update(external_ports)
864
- return list(externally_defined_ports)
1
+ # Copyright 2024 Volvo Car Corporation
2
+ # Licensed under Apache 2.0.
3
+
4
+ """Signal interface module.
5
+
6
+ Module for parsing the external signal interface definition files, and
7
+ checking the signal interfaces between Supplier and VCC sw,
8
+ and also internally between VCC sw-units.
9
+ """
10
+ import re
11
+ import csv
12
+ import time
13
+ import copy
14
+ from pprint import pformat
15
+ from pathlib import Path
16
+ from collections import defaultdict
17
+
18
+ from powertrain_build.build_proj_config import BuildProjConfig
19
+ from powertrain_build.feature_configs import FeatureConfigs
20
+ from powertrain_build.lib.helper_functions import deep_dict_update
21
+ from powertrain_build.problem_logger import ProblemLogger
22
+ from powertrain_build.types import get_ec_type
23
+ from powertrain_build.unit_configs import UnitConfigs, CodeGenerators
24
+ from powertrain_build.user_defined_types import UserDefinedTypes
25
+
26
+
27
+ class CsvReaderCounter:
28
+ """Csv wrapper to keep track of file row."""
29
+
30
+ def __init__(self, _csvreader):
31
+ """Init."""
32
+ self.csvreader = _csvreader
33
+ self.counter = 0
34
+
35
+ def __iter__(self):
36
+ """Iterate."""
37
+ return self
38
+
39
+ def __next__(self):
40
+ """Next."""
41
+ self.counter += 1
42
+ return next(self.csvreader)
43
+
44
+ def get_count(self):
45
+ """Get current row number."""
46
+ return self.counter
47
+
48
+
49
+ class InterfaceValueException(Exception):
50
+ """Exception for errors interface list."""
51
+
52
+ def __init__(self, signal_name, erroneous_field):
53
+ """Init."""
54
+ self.erroneous_field = erroneous_field
55
+ self.signal_name = signal_name
56
+
57
+
58
+ class SignalInterfaces(ProblemLogger):
59
+ """Base class for signal interfaces."""
60
+
61
+ def __init__(self, unit_cfg, model_names=None):
62
+
63
+ self._unit_cfg = unit_cfg
64
+ self._signals_ext_nok = {'inports': {}, 'outports': {}}
65
+ self._signals_ext_ok = {'inports': {}, 'outports': {}}
66
+ self._result = self.__ddict_factory()
67
+
68
+ if model_names is None:
69
+ self.models_to_check = set()
70
+ else:
71
+ self.models_to_check = set(model_names)
72
+
73
+ def get_external_outputs(self):
74
+ """Get the external outputs.
75
+
76
+ Should be implemented by the subclasses.
77
+
78
+ Returns:
79
+ dict: External outputs
80
+ """
81
+ raise NotImplementedError
82
+
83
+ def get_external_inputs(self):
84
+ """Get the internal outputs.
85
+
86
+ Should be implemented by the subclasses.
87
+
88
+ Returns:
89
+ dict: Internal outputs
90
+ """
91
+ raise NotImplementedError
92
+
93
+ @classmethod
94
+ def __ddict_factory(cls):
95
+ """Generate recursive defaultdict."""
96
+ return defaultdict(cls.__ddict_factory)
97
+
98
+ @property
99
+ def should_all_models_be_checked(self):
100
+ """True if there are no specific models to be checked
101
+
102
+ :rtype: bool
103
+ """
104
+ return len(self.models_to_check) == 0
105
+
106
+ def should_check_model(self, model_name):
107
+ """True if MODEL_NAME is in SELF.MODEL_NAMES
108
+
109
+ :rtype: bool
110
+ """
111
+ return self.should_all_models_be_checked or model_name in self.models_to_check
112
+
113
+ def contains_model_to_check(self, model_names):
114
+ """True if MODEL_NAMES contains a model in SELF.MODEL_NAMES
115
+
116
+ :param model_names: iterable
117
+ :rtype: bool
118
+ """
119
+ return any((m in self.models_to_check for m in model_names))
120
+
121
+ @staticmethod
122
+ def _eq_var(var_a, var_b, par):
123
+ """Check if variables are equal, with some modifications.
124
+
125
+ par contains the key for which type of parameter is compared
126
+ E.g. nan is equal to '-' and ''. Furthermore, all variables
127
+ are converted to strings before making the comparison.
128
+ """
129
+ a_s = str(var_a)
130
+ b_s = str(var_b)
131
+ if par in ['min', 'max']:
132
+ a_s = re.sub('nan', '-', a_s)
133
+ b_s = re.sub('nan', '-', b_s)
134
+ if re.match(r'[-+.0-9eE]+', a_s) and re.match(r'[-+.0-9eE]+', b_s):
135
+ try:
136
+ a_f = float(var_a)
137
+ b_f = float(var_b)
138
+ return a_f == b_f
139
+ except ValueError:
140
+ pass
141
+ return a_s == b_s
142
+
143
+ def _check_var_def(self, ext_def, unit_def):
144
+ """Check that the parameters for the variable definitions are the same.
145
+
146
+ Check that the parameters are the same for all units that use the parameter.
147
+ """
148
+ res = {}
149
+ ignored_parameters = ['class', 'configs', 'handle', 'default', 'description', 'unit']
150
+ for unit, unit_definitions in unit_def.items():
151
+ for parameter, definition in ext_def.items():
152
+ if parameter in ignored_parameters or parameter not in unit_definitions:
153
+ continue
154
+ unit_definition = unit_definitions[parameter]
155
+ if not self._eq_var(definition, unit_definition, parameter):
156
+ res.setdefault(unit, {})[parameter] = f"{definition} != {unit_definition}"
157
+
158
+ return res if res else None
159
+
160
+ def _add_incons_to_ext_dict(self, res, var, incons):
161
+ """Add inconsistencies to the ext dict."""
162
+ if incons is not None:
163
+ for unit, incon in incons.items():
164
+ if self.should_check_model(unit):
165
+ if var in res['inconsistent_defs'][unit]:
166
+ res['inconsistent_defs'][unit][var].update(incon)
167
+ else:
168
+ res['inconsistent_defs'][unit][var] = incon
169
+
170
+ def _add_incons_to_dict(self, res, var, incons):
171
+ """Add iconsistent data to a dict."""
172
+ if incons is not None:
173
+ for unit, incon in incons.items():
174
+ if self.should_check_model(unit):
175
+ if var in res[unit]['inconsistent_defs']:
176
+ res[unit]['inconsistent_defs'][var].update(incon)
177
+ else:
178
+ res[unit]['inconsistent_defs'][var] = incon
179
+
180
+ def _gen_int_outp_set(self, tot_unit_cfg):
181
+ """Generate a set of signals to check for internal output parameters."""
182
+ tot_cfg = tot_unit_cfg.get('outports', {})
183
+ external_ports = {**self._signals_ext_ok['outports'], **self._signals_ext_nok['outports']}
184
+
185
+ # only check outports that are not defined in the external io definition
186
+ non_ext_outp = set(tot_cfg.keys()) - set(external_ports.keys())
187
+ return non_ext_outp
188
+
189
+ def _gen_int_inp_set(self, tot_unit_cfg):
190
+ """Generate a set of signals to check for internal input parameters."""
191
+ tot_cfg = tot_unit_cfg.get('inports', {})
192
+ external_ports = {**self._signals_ext_ok['inports'], **self._signals_ext_nok['inports']}
193
+
194
+ # only check inports that are not defined in the external io definition
195
+ non_ext_inp = set(tot_cfg.keys()) - set(external_ports.keys())
196
+ return non_ext_inp
197
+
198
+ def _gen_unit_var_dict(self, res, chk_type, variables, in_out):
199
+ """Generate a dict with vars as keys, and units in which they are used as a list."""
200
+ tot_cfg = self._unit_cfg.get_per_cfg_unit_cfg()
201
+ for var in variables:
202
+ for unit in tot_cfg[in_out][var]:
203
+ if self.should_check_model(unit):
204
+ if var in res[unit][chk_type]:
205
+ res[unit][chk_type][var] = {}
206
+ else:
207
+ res[unit][chk_type][var] = {}
208
+
209
+ def _check_external_outp(self):
210
+ """Check that all external outputs (VCC -> supplier).
211
+
212
+ Check that all external outputs are produced,
213
+ that consumed signals (Supplier -> VCC) are defined as inputs
214
+ in the external IO definition file, and that the signal
215
+ definitions are consistent between io and unit definitions.
216
+ """
217
+ ext_out = self.get_external_outputs()
218
+ tot_cfg = self._unit_cfg.get_per_cfg_unit_cfg()
219
+ res = self._result['sigs']['ext']
220
+ # check that all output signals are produced by VCC SW
221
+ if 'outports' in tot_cfg:
222
+ for _, data in ext_out.items():
223
+ for var, var_def in data.items():
224
+ if var not in tot_cfg['outports']:
225
+ res['missing'][var] = {}
226
+ self._signals_ext_nok['outports'][var] = var_def
227
+ else:
228
+ self._signals_ext_ok['outports'][var] = var_def
229
+ tmp = self._check_var_def(var_def, tot_cfg['outports'][var])
230
+ self._add_incons_to_ext_dict(res, var, tmp)
231
+
232
+ def _check_external_inp(self):
233
+ """Check all external input signals.
234
+
235
+ Check that outputs are produced and,
236
+ that consumed signals are defined as inputs
237
+ in the external IO definition file.
238
+ """
239
+ ext_inp = self.get_external_inputs()
240
+ # self.debug('ext_inp: %s', pformat(ext_inp))
241
+ tot_cfg = self._unit_cfg.get_per_cfg_unit_cfg()
242
+ res = self._result['sigs']['ext']
243
+ # check that all input signals are used by VCC SW
244
+ if 'inports' in tot_cfg:
245
+ for data in ext_inp.values():
246
+ for var, var_def in data.items():
247
+ if var not in tot_cfg['inports']:
248
+ if var not in res['unused']:
249
+ res['unused'][var] = {}
250
+ self._signals_ext_nok['inports'][var] = var_def
251
+ else:
252
+ self._signals_ext_ok['inports'][var] = var_def
253
+ tmp = self._check_var_def(var_def, tot_cfg['inports'][var])
254
+ self._add_incons_to_ext_dict(res, var, tmp)
255
+
256
+ def _check_internal_io(self):
257
+ """Check internal signal io.
258
+
259
+ Function which checks that:
260
+ 1. all signals consumed in models, are produced
261
+ in antoher model.
262
+ 2. all outputs are consumed (warning)
263
+ 3. check that all signal definitions are the same as the producing unit.
264
+ 4. a signal is only produced in one model (per prj).
265
+ this function return a tuple with the above content
266
+
267
+ TODO:
268
+ Shall we add a check for multiple inputs of the same signal?*
269
+ """
270
+ tot_cfg = self._unit_cfg.get_per_cfg_unit_cfg()
271
+ internal_inport_signals = self._gen_int_inp_set(tot_cfg)
272
+ internal_outport_signals = self._gen_int_outp_set(tot_cfg)
273
+ all_outports = set(tot_cfg.get('outports', {}).keys())
274
+ res = self._result['sigs']['int']
275
+ # 1. all signals consumed in models, are produced in another model.
276
+ missing = internal_inport_signals - all_outports
277
+ self._gen_unit_var_dict(res, 'missing', missing, 'inports') # error!
278
+ # 2. all outputs are consumed (warning)
279
+ unused = internal_outport_signals - internal_inport_signals
280
+ self._gen_unit_var_dict(res, 'unused', unused, 'outports') # warning?
281
+ # 3. check that all signal definitions are the same as the producing
282
+ # unit.
283
+ input_signals_to_check = internal_inport_signals - missing
284
+ for signal in input_signals_to_check:
285
+ unit_key = list(tot_cfg['outports'][signal].keys())
286
+ # if output is defined in multiple functions, test #4 will catch this
287
+ if len(unit_key) == 1:
288
+ outport_def = tot_cfg['outports'][signal][unit_key[0]]
289
+ tmp = self._check_var_def(outport_def,
290
+ tot_cfg['inports'][signal])
291
+ self._add_incons_to_dict(res, signal, tmp)
292
+
293
+ # 4. check that we have not screwed up any of the consuming models
294
+ external_signals_to_check = internal_outport_signals - unused
295
+ for signal in external_signals_to_check:
296
+ unit_keys = list(tot_cfg['inports'][signal].keys())
297
+ my_outport_definition = tot_cfg['outports'][signal]
298
+ for unit_key in unit_keys:
299
+ others_inport_def = tot_cfg['inports'][signal][unit_key]
300
+ tmp = self._check_var_def(others_inport_def,
301
+ my_outport_definition)
302
+ self._add_incons_to_dict(res, signal, tmp)
303
+
304
+ # 5. there should only be one outport per config.
305
+ multiple_defs = []
306
+ if 'outports' in tot_cfg:
307
+ multiple_defs = [x for x in tot_cfg['outports']
308
+ if len(tot_cfg['outports'][x]) > 1]
309
+ self._gen_unit_var_dict(res, 'multiple_defs', multiple_defs, 'outports')
310
+
311
+ def _check_config(self):
312
+ """Check the interfaces given a certain project config.
313
+
314
+ The result of the checks is stored in the dict self._result
315
+ """
316
+ # reset the handled external signals for each configuration
317
+ self._signals_ext_ok = {'inports': {}, 'outports': {}}
318
+ # has to be done in this order since there is an implicit dependency
319
+ # to self._signals_ext_ok
320
+ self._check_external_outp()
321
+ self._check_external_inp()
322
+ self._check_internal_io()
323
+
324
+ def check_config(self):
325
+ """Check configurations for specific project.
326
+
327
+ Returns:
328
+ dict: the result of the check with the following format
329
+
330
+ ::
331
+
332
+ {
333
+ "sigs": { "ext": {"missing": {},
334
+ "unused": {},
335
+ "inconsistent_defs": {}},
336
+ "int": {"UNIT_NAME": {"missing": {},
337
+ "unused": {},
338
+ "multiple_defs": {}
339
+ "inconsistent_defs": {}}
340
+ }
341
+ }
342
+
343
+ """
344
+ self._result = self.__ddict_factory()
345
+ self._check_config()
346
+ # self.debug("%s", pformat(self._result))
347
+ # restore the current conofiguration
348
+
349
+ return self._result
350
+
351
+
352
+ class YamlSignalInterfaces(SignalInterfaces):
353
+ """Interface configurations defined in yaml files."""
354
+
355
+ @staticmethod
356
+ def from_config_file(project_config_path):
357
+ """Create a YamlSignalInterfaces instance from a project config file.
358
+
359
+ Args:
360
+ project_config_path (str): path to the project config file
361
+
362
+ Returns:
363
+ YamlSignalInterfaces: instance of YamlSignalInterfaces
364
+ """
365
+ build_project_config = BuildProjConfig(project_config_path)
366
+ feature_cfg = FeatureConfigs(build_project_config)
367
+ unit_config = UnitConfigs(build_project_config, feature_cfg)
368
+ user_defined_types = UserDefinedTypes(build_project_config, unit_config)
369
+
370
+ return YamlSignalInterfaces(
371
+ prj_cfg=build_project_config,
372
+ unit_cfg=unit_config,
373
+ feature_cfg=feature_cfg,
374
+ user_defined_types=user_defined_types
375
+ )
376
+
377
+ def __init__(self, prj_cfg, unit_cfg, feature_cfg, user_defined_types, model_names=None):
378
+ """Class initializer.
379
+
380
+ Args:
381
+ prj_cfg (BuildProjConfig): configures which units are active in the current project.
382
+ unit_cfg (UnitConfigs): class instance containing all the unit configuration parameters.
383
+ feature_cfg (FeatureConfig): Feature configs from SPM_Codeswitch_Setup.
384
+ user_defined_types (UserDefinedTypes): Class holding user defined data types.
385
+ model_names (set): models that should be included in the check, default is all models
386
+ """
387
+ super().__init__(unit_cfg, model_names=model_names)
388
+
389
+ # Postpone imports to here to work on machines without PyYaml installed
390
+ from powertrain_build.interface.base import filter_signals
391
+ from powertrain_build.interface.application import Application, get_internal_domain
392
+ from powertrain_build.interface.generation_utils import get_interface, get_method_interface
393
+ from powertrain_build.interface.hal import HALA
394
+ from powertrain_build.interface.device_proxy import DPAL
395
+ from powertrain_build.interface.zone_controller import ZCAL
396
+ from powertrain_build.interface.service import ServiceFramework
397
+
398
+ app = Application()
399
+ app.pybuild['build_cfg'] = prj_cfg
400
+ app.name = app.pybuild['build_cfg'].name
401
+ app.pybuild['feature_cfg'] = feature_cfg
402
+ app.pybuild['unit_vars'] = unit_cfg.get_per_unit_cfg_total()
403
+ app.pybuild['user_defined_types'] = user_defined_types
404
+
405
+ translation_files = app.get_translation_files()
406
+
407
+ ecu_supplier = prj_cfg.get_ecu_info()[0]
408
+ self.zc_spec = {}
409
+ self.hal_spec = {}
410
+ self.dp_spec = {}
411
+ self.sfw_spec = {}
412
+ self.sa_spec = {}
413
+ self.service_spec = {}
414
+ self.mthd_spec = {}
415
+ if prj_cfg.get_code_generation_config(item='generateYamlInterfaceFile'):
416
+ zc_app = ZCAL(app)
417
+ self.zc_spec = get_interface(app, zc_app)
418
+ self.composition_spec = zc_app.composition_spec
419
+ elif ecu_supplier == 'HI':
420
+ hi_app = DPAL(app)
421
+ self.dp_spec = get_interface(app, hi_app)
422
+ else:
423
+ hala = HALA(app)
424
+ dp = DPAL(app)
425
+ swf = ServiceFramework(app)
426
+ hala.parse_definition(translation_files)
427
+ self.dp_spec = get_interface(app, dp)
428
+ self.sfw_spec = get_interface(app, swf)
429
+ self.mthd_spec = get_method_interface(app)
430
+ self.service_spec = self.get_availability(app, unit_cfg.code_generators)
431
+ rasters = app.get_rasters()
432
+ self.debug('Rasters: %s', rasters)
433
+ internal = get_internal_domain(rasters)
434
+ properties_from_json = [
435
+ {"destination": "min", "source": "min", "default": "-"},
436
+ {"destination": "max", "source": "max", "default": "-"},
437
+ {"destination": "variable_type", "source": "type"},
438
+ {"destination": "offset", "source": "offset", "default": "-"},
439
+ {"destination": "factor", "source": "lsb", "default": 1},
440
+ {"destination": "description", "source": "description"},
441
+ {"destination": "unit", "source": "unit", "default": "-"},
442
+ ]
443
+ for raster in rasters:
444
+ hala.name = raster.name
445
+ hala.clear_signal_names()
446
+ hala.add_signals(filter_signals(raster.insignals, internal), 'insignals', properties_from_json)
447
+ hala.add_signals(raster.outsignals, 'outsignals', properties_from_json)
448
+ self.debug('Current HALA: %s', hala)
449
+ self.hal_spec[raster.name] = hala.to_dict()
450
+
451
+ @staticmethod
452
+ def get_availability(app, code_generators={CodeGenerators.target_link}):
453
+ """Get the availability of services.
454
+
455
+ Args:
456
+ app (Application): Application instance
457
+ code_generators (set): Code generators to include in the availability check
458
+
459
+ Returns:
460
+ dict: Availability of services
461
+ """
462
+ tl_type = 'Bool'
463
+ variable_type = tl_type if CodeGenerators.target_link in code_generators else get_ec_type(tl_type)
464
+ services = app.get_service_mapping()
465
+ spec = {}
466
+ for interface, service in services.items():
467
+ camel_interface = ''.join(part.title() for part in interface.split('_'))
468
+ spec[interface] = {
469
+ 'variable': f'yVcSfw_B_{camel_interface}IsAvailable',
470
+ 'variable_type': variable_type,
471
+ 'property_type': 'bool',
472
+ 'service': service,
473
+ 'default': 0,
474
+ 'length': 1,
475
+ 'property': 'inherent',
476
+ 'offset': '-',
477
+ 'factor': 1,
478
+ 'range': {'min': '0', 'max': '1'},
479
+ 'init': 0,
480
+ 'description': f'Availability of {interface} in {service}',
481
+ 'unit': '-',
482
+ 'group': None,
483
+ 'model': interface
484
+ }
485
+ return spec
486
+
487
+ def get_externally_defined_ports(self):
488
+ """Get ports defined by suppliers."""
489
+ outports = []
490
+ for raster in self.dp_spec.values():
491
+ for spec in raster['consumer']:
492
+ outports.append(spec['variable'])
493
+ for raster in self.hal_spec.values():
494
+ for spec in raster['consumer']:
495
+ outports.append(spec['variable'])
496
+ for raster in self.sfw_spec.values():
497
+ for spec in raster['consumer']:
498
+ outports.append(spec['variable'])
499
+ for raster in self.sa_spec.values():
500
+ for spec in raster['consumer']:
501
+ outports.append(spec['variable'])
502
+ for raster in self.zc_spec.values():
503
+ for spec in raster['consumer']:
504
+ outports.append(spec['variable'])
505
+ return outports
506
+
507
+ def get_external_io(self):
508
+ """Get the variable definitions for the signal IO for a given config.
509
+
510
+ Returns:
511
+ dict: Variable definitions for the supplier in-/out-put signals for the configuration
512
+ """
513
+ def normalize_spec(spec):
514
+ """ Convert Yaml spec to normal pybuild spec.
515
+
516
+ Arguments:
517
+ spec (dict): Yaml specification of signal
518
+ Returns:
519
+ spec (dict): Pybuild specification of signal
520
+ """
521
+ spec2 = copy.copy(spec) # copy by value
522
+ spec2['type'] = spec['variable_type']
523
+ spec2['min'] = spec['range']['min']
524
+ spec2['max'] = spec['range']['max']
525
+ return spec2
526
+
527
+ def set_spec(target):
528
+ new_spec = normalize_spec(spec)
529
+ if new_spec.get('debug'):
530
+ debug[target][spec['variable']] = new_spec
531
+ elif spec.get('dependability'):
532
+ dependability[target][spec['variable']] = new_spec
533
+ else:
534
+ normal[target][spec['variable']] = new_spec
535
+
536
+ normal = {}
537
+ dependability = {}
538
+ secure = {} # Only supported by CSV interfaces
539
+ debug = {}
540
+
541
+ for field in ['input', 'output', 'status']:
542
+ normal[field] = {}
543
+ dependability[field] = {}
544
+ debug[field] = {}
545
+
546
+ spec_confs = [self.dp_spec, self.sfw_spec, self.sa_spec, self.hal_spec, self.zc_spec]
547
+ for spec_conf in spec_confs:
548
+ for raster in spec_conf.values():
549
+ for spec in raster['consumer']:
550
+ set_spec("input")
551
+ for spec in raster['producer']:
552
+ set_spec("output")
553
+ for spec in self.service_spec.values():
554
+ set_spec('status')
555
+ for method_data in self.mthd_spec.values():
556
+ for _, spec in method_data['ports']['out'].items():
557
+ set_spec('input')
558
+ for _, spec in method_data['ports']['in'].items():
559
+ set_spec('output')
560
+ return normal, dependability, secure, debug
561
+
562
+ def get_external_signals(self, find_output=True):
563
+ """Get the external signals.
564
+
565
+ Args:
566
+ find_output (bool): True if output signals should be returned, False if input signals should be returned
567
+
568
+ Returns:
569
+ dict: External signals
570
+ """
571
+ io_type = self.get_external_io() # (normal, dependability, secure (unsupported), debug)
572
+ directional_io = defaultdict(dict)
573
+ for io in io_type:
574
+ for signal_type, signals in io.items():
575
+ if find_output and signal_type == 'output':
576
+ directional_io[signal_type].update(signals)
577
+ elif not find_output and signal_type == 'input':
578
+ directional_io[signal_type].update(signals)
579
+ return directional_io
580
+
581
+ def get_external_outputs(self):
582
+ return self.get_external_signals(True)
583
+
584
+ def get_external_inputs(self):
585
+ return self.get_external_signals(False)
586
+
587
+
588
+ class CsvSignalInterfaces(SignalInterfaces):
589
+ """Interface configurations for all units and all configs.
590
+
591
+ Provides methods for retrieving the currently
592
+ used signal configurations of a unit.
593
+ """
594
+
595
+ convs = (('~=', '!='), ('~', ' not '), ('!', ' not '), (r'\&\&', ' and '),
596
+ (r'\|\|', ' or '))
597
+ # Common excel sheet definitions, adapted for CVS.
598
+ VAR_COL = 0 # Variable name
599
+ DATA_COL = 2 # Type, min, max, unit, comment, init
600
+ PROJ_COL = 9 # Variable number of projects
601
+ HEADER_ROW = 3 # which row the header info is found
602
+ DATA_ROW = 5 # the row which data starts
603
+ WS_NAMES = ['EMS-Output', 'EMS-Input', 'LIN-Output', 'LIN-Input',
604
+ 'CAN-Output', 'CAN-Input', 'Private CAN-Output',
605
+ 'Private CAN-Input']
606
+
607
+ def __init__(self, prj_cfg, unit_cfg, models=None):
608
+ """Class initializer.
609
+
610
+ Args:
611
+ prj_cfg (BuildProjConfig): configures which units are active in the
612
+ current project
613
+ unit_cfg (UnitConfigs): class instance containing all the unit
614
+ configuration parameters
615
+ models (str or list): Models to get interface for. default: 'all'
616
+
617
+ """
618
+ super().__init__(unit_cfg, model_names=models)
619
+ self.interfaces = {}
620
+
621
+ self.__prjs = {}
622
+ self.__out_dict = {}
623
+ self.__all_prjs = set()
624
+ self.__prj2col = {}
625
+ self.__prj_col_range = {}
626
+
627
+ prj2index = {}
628
+ self.name = prj_cfg.get_prj_config()
629
+ file_path = prj_cfg.get_if_cfg_dir()
630
+ self._prj_cfg = prj_cfg
631
+ self._parse_io_cnfg(file_path)
632
+ for k in self.__prj2col:
633
+ self.__prj2col[k] = prj2index
634
+
635
+ def _parse_io_cnfg(self, interface_directory):
636
+ """Parse the CSV config files."""
637
+ start_time = time.time()
638
+ self.info('******************************************************')
639
+ self.info('Start parsing SPM-Interface definition files')
640
+ for ws_name in self.WS_NAMES:
641
+ self.debug("read sheet %s start", ws_name)
642
+ file_path = Path(interface_directory, ws_name + '.csv')
643
+ try:
644
+ with open(file_path, newline='') as fhandle:
645
+ if ws_name not in self.interfaces:
646
+ self.interfaces.update({ws_name: {}})
647
+ interface = self.interfaces[ws_name]
648
+ csvreader = csv.reader(fhandle, delimiter=';', strict=True)
649
+ csvreader = CsvReaderCounter(csvreader)
650
+ try:
651
+ for _ in range(self.HEADER_ROW-1):
652
+ next(csvreader)
653
+ row = next(csvreader)
654
+ # TODO: Find why excel export script sometimes skips first field
655
+ # on each line if field is empty.
656
+ # Workaround:
657
+ row = row[1:] if row[0] == '' else row
658
+ try:
659
+ col = self._get_proj_col(row)
660
+ except IndexError:
661
+ self.warning('Project %s not defined in %s', self.name, file_path.stem)
662
+ continue
663
+
664
+ for _ in range(self.HEADER_ROW, self.DATA_ROW-1):
665
+ next(csvreader)
666
+ except StopIteration:
667
+ self.critical('File %s has bad format, not enough header rows', file_path.stem)
668
+ for row in csvreader:
669
+ row = row[1:] if row[0] == '' else row
670
+ try:
671
+ signal, data = self._get_var_def(row)
672
+ data.update({'element_index': csvreader.get_count()})
673
+ except InterfaceValueException as interface_exception:
674
+ self.critical('Missing value for key "%s" in list "%s" at index "%s"',
675
+ interface_exception.erroneous_field,
676
+ file_path.stem, csvreader.get_count())
677
+ continue
678
+ if signal not in interface:
679
+ interface.update({signal: data})
680
+ interface[signal]['IOType'] = row[col].lower()
681
+ self.debug("read sheet %s end", ws_name)
682
+ except FileNotFoundError:
683
+ self.info('Project %s does not have a %s', self.name, ws_name)
684
+ self.info('Finished parsing SPM-Interface definition file (in %4.2f s)', time.time() - start_time)
685
+
686
+ def __repr__(self):
687
+ """Get string representation of object."""
688
+ return pformat(self.__out_dict)
689
+
690
+ def _get_proj_col(self, row):
691
+ """Get the projects in the config document.
692
+
693
+ Parses supplied worksheet row and finds all the projects defined, and
694
+ stores the result in the class variable __all_prjs.
695
+ Furthermore, a dict prj2col, maps the project name to a list index
696
+ in the prj key in the __out_dict dict, and
697
+ sets the class variables __prj2col, and __prj_col_range contains
698
+ a list with column indexes in the excel sheet, used for parsing the
699
+ excel, the result is stored in the interal __out_dict
700
+
701
+ interfaces example:
702
+
703
+ ::
704
+
705
+ {
706
+ 'CAN-Input': {
707
+ 'sVCcm_D_HvacCoolgEnaRe': {
708
+ 'IOType': 'd'
709
+ 'description': 'Enable Ac rear HVAC',
710
+ 'init': 0,
711
+ 'max': 1,
712
+ 'min': 0,
713
+ 'type': 'UInt8',
714
+ 'unit': '-'
715
+ }
716
+ }
717
+ }
718
+
719
+ """
720
+ col = self.PROJ_COL
721
+ while row[col] is not None and row[col] != '':
722
+ if row[col] == self.name:
723
+ return col
724
+ col += 1
725
+ if col > 200:
726
+ break
727
+
728
+ raise IndexError
729
+
730
+ @staticmethod
731
+ def _get_var_def(row):
732
+ """Get the variable definition from a row in a sheet.
733
+
734
+ Returns:
735
+ dict: with var definitions
736
+
737
+ """
738
+ keys = ['type', 'min', 'max', 'unit', 'description', 'init']
739
+
740
+ vals = []
741
+ for col in [0, 2, 3, 4, 5, 6, 7]:
742
+ val = row[col]
743
+ if isinstance(val, str):
744
+ val = val.strip()
745
+ if re.fullmatch('[+-]?[0-9]+', val):
746
+ val = int(val)
747
+ elif re.fullmatch('[+-]?[0-9.,]+', val):
748
+ val = float(val.replace(',', '.'))
749
+ vals.append(val)
750
+ critical_attributes = ['type']
751
+ signal_name = vals[0]
752
+ signal_attributes = dict(zip(keys, vals[1:]))
753
+ for field in critical_attributes:
754
+ if signal_attributes[field] == '':
755
+ raise InterfaceValueException(signal_name, field)
756
+ return signal_name, signal_attributes
757
+
758
+ def get_raw_io_cnfg(self):
759
+ """Get the raw IO-Config parsed from the config file.
760
+
761
+ Returns:
762
+ dict: raw configuration information from the config file
763
+
764
+ dict example:
765
+
766
+ ::
767
+
768
+ {
769
+ 'CAN-Input': {
770
+ 'sVCcm_D_HvacCoolgEnaRe': {
771
+ 'description': 'Enable Ac rear HVAC',
772
+ 'init': 0,
773
+ 'max': 1,
774
+ 'min': 0,
775
+ 'project_def': 'D',
776
+ 'type': 'UInt8',
777
+ 'unit': '-'
778
+ }
779
+ }
780
+ }
781
+
782
+
783
+ """
784
+ return self.interfaces
785
+
786
+ def get_io_config(self):
787
+ """Get an IO-Config for a specific project.
788
+
789
+ Returns:
790
+ tuple: a tuple with three dicts - (nrm_dict, dep_dict, dbg_dict)
791
+
792
+ """
793
+ normal = {}
794
+ dep = {}
795
+ secure = {}
796
+ debug = {}
797
+ for interface_name, interface in self.interfaces.items():
798
+ normal[interface_name] = {}
799
+ dep[interface_name] = {}
800
+ secure[interface_name] = {}
801
+ debug[interface_name] = {}
802
+ for signal_name, signal in interface.items():
803
+ prj_def = signal.get('IOType', '-')
804
+ if prj_def in 'xd':
805
+ normal[interface_name][signal_name] = copy.deepcopy(signal)
806
+ elif prj_def in 's':
807
+ dep[interface_name][signal_name] = copy.deepcopy(signal)
808
+ elif prj_def == 'sc':
809
+ secure[interface_name][signal_name] = copy.deepcopy(signal)
810
+ if prj_def in 'd':
811
+ debug[interface_name][signal_name] = copy.deepcopy(signal)
812
+ return normal, dep, secure, debug
813
+
814
+ def get_external_io(self):
815
+ """Get the variable definitions for the signal IO for a given config.
816
+
817
+ Returns:
818
+ dict: Variable definitions for the supplier in-/out-put signals for the configuration
819
+
820
+ """
821
+ return self.get_io_config()
822
+
823
+ def get_external_inputs(self):
824
+ """Get variable definitions for the supplier signal Inputs.
825
+
826
+ Returns:
827
+ dict: variable definitions for the supplier input signals
828
+
829
+ """
830
+ cfg, cfg_dep, cfg_sec = self.get_io_config()[:3]
831
+ deep_dict_update(cfg, cfg_dep)
832
+ deep_dict_update(cfg, cfg_sec)
833
+ out = {}
834
+ for key, value in cfg.items():
835
+ if re.match('.*Input$', key) is not None:
836
+ out[key] = value
837
+ return out
838
+
839
+ def get_external_outputs(self):
840
+ """Get variable definitions for the supplier signal Outputs.
841
+
842
+ Returns:
843
+ dict: variable definitions for the supplier output signals
844
+
845
+ """
846
+ cfg, cfg_dep, cfg_sec = self.get_io_config()[:3]
847
+ deep_dict_update(cfg, cfg_dep)
848
+ deep_dict_update(cfg, cfg_sec)
849
+ out = {}
850
+ for key, value in cfg.items():
851
+ if re.match(r'.*Output$', key) is not None:
852
+ out[key] = value
853
+ return out
854
+
855
+ def get_externally_defined_ports(self):
856
+ """Get ports defined by suppliers."""
857
+ externally_defined_ports = set()
858
+ external_outports = self.get_external_outputs()
859
+ for external_ports in external_outports.values():
860
+ externally_defined_ports.update(external_ports)
861
+ external_inports = self.get_external_inputs()
862
+ for external_ports in external_inports.values():
863
+ externally_defined_ports.update(external_ports)
864
+ return list(externally_defined_ports)