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
powertrain_build/dids.py CHANGED
@@ -1,916 +1,916 @@
1
- # Copyright 2024 Volvo Car Corporation
2
- # Licensed under Apache 2.0.
3
-
4
- # -*- coding: utf-8 -*-
5
- """Module containing classes for DID definitions.
6
-
7
- This module is used to parse DID definition files and merge with the unit definitions to find DIDs in a project.
8
- It then generates the DID definition c-files for the supplier DID API.
9
- """
10
-
11
- import csv
12
- import os
13
- from pathlib import Path
14
- from ruamel.yaml import YAML
15
- from powertrain_build import build_defs
16
- from powertrain_build.lib.helper_functions import deep_dict_update
17
- from powertrain_build.problem_logger import ProblemLogger
18
- from powertrain_build.types import byte_size, get_ec_type, get_float32_types
19
- from powertrain_build.unit_configs import CodeGenerators
20
-
21
-
22
- def get_dids_in_prj(unit_cfgs):
23
- """Return a dict with DIDs in the currently included SW-Units.
24
-
25
- Args:
26
- unit_cfgs (UnitConfigs): Unit definitions.
27
- Returns:
28
- error_message (str): Message in case something went wrong.
29
- dict: a dict with all dids in the project, in the below format:
30
-
31
- ::
32
-
33
- {'DID_VARIABLE_NAME': {
34
- 'handle': 'VcRegCh/VcRegCh/Subsystem/VcRegCh/1000_VcRegCh/1600_DID/Gain14',
35
- 'configs': ['all'],
36
- 'type': 'UInt32',
37
- 'unit': '-',
38
- 'lsb': 1,
39
- 'max': 20,
40
- 'min': 0,
41
- 'offset': 0,
42
- 'description': 'Actual Regen State',
43
- 'name': 'DID_VARIABLE_NAME',
44
- 'class': 'CVC_DISP'
45
- }
46
- }
47
- """
48
- dids_prj = {}
49
- error_messages = []
50
- unit_cfg = unit_cfgs.get_per_unit_cfg()
51
- for unit, data in unit_cfg.items():
52
- dids = data.get('dids')
53
- if dids is None:
54
- error_messages.append(f'No "dids" key in unit config for {unit}.')
55
- continue
56
- for name, did in dids.items():
57
- dids_prj[name] = did
58
- return error_messages, dids_prj
59
-
60
-
61
- class DIDs(ProblemLogger):
62
- """A class for handling of DID definitions."""
63
-
64
- def __init__(self, build_cfg, unit_cfgs):
65
- """Parse DID definition files referenced by project config.
66
-
67
- Args:
68
- build_cfg (BuildProjConfig): Project configuration
69
- unit_cfgs (UnitConfigs): Unit definitions
70
- """
71
- super().__init__()
72
- self._build_cfg = build_cfg
73
- self._unit_cfgs = unit_cfgs
74
- did_filename = self._build_cfg.get_did_cfg_file_name()
75
- cfg_dir = self._build_cfg.get_prj_cfg_dir()
76
- did_f32_cfg_file = os.path.join(cfg_dir, did_filename + '_Float32.csv')
77
- did_u32_cfg_file = os.path.join(cfg_dir, did_filename + '_UInt32.csv')
78
- self._dids_f32 = self._load_did_config_files(did_f32_cfg_file)
79
- self._dids_u32 = self._load_did_config_files(did_u32_cfg_file)
80
- self.fh_h = None
81
- self.fh_c = None
82
- get_did_error_messages, self._did_dict = get_dids_in_prj(unit_cfgs)
83
- self._did_defs = self.get_did_config()
84
- self._float32_types = get_float32_types()
85
- if get_did_error_messages:
86
- self.critical('\n'.join(get_did_error_messages))
87
-
88
- def _load_did_config_files(self, config_file):
89
- """Load the did config files."""
90
- dids = {}
91
- with open(config_file, mode='r', encoding='utf-8') as did_fh:
92
- csv_did = csv.reader(did_fh, delimiter=';')
93
- did = list(csv_did)
94
- dids['dids'] = {row[0]: int(row[1], 16) for row in did[3:]}
95
- dids['start_did'] = int(did[1][0], 16)
96
- dids['end_did'] = int(did[1][1], 16)
97
- self._check_dids(dids)
98
- return dids
99
-
100
- @staticmethod
101
- def _check_dids(dids):
102
- """Check that all dids are within the start and end values."""
103
- start_did = dids['start_did']
104
- end_did = dids['end_did']
105
-
106
- for var, did in dids['dids'].items():
107
- if did < start_did:
108
- raise ValueError(f'{var} has a too low did 0x{did:X} start did is 0x{start_did:X}')
109
- if did > end_did:
110
- raise ValueError(f'{var} has a too high did 0x{did:X} start did is 0x{start_did:X}')
111
-
112
- def gen_did_def_files(self, filename):
113
- """Generate the VcDidDefinitions.c & h files used by the Did-API."""
114
- with open(filename + '.h', 'w', encoding="utf-8") as self.fh_h:
115
- with open(filename + '.c', 'w', encoding="utf-8") as self.fh_c:
116
- dids_f32, dids_u32, errors = self._check_and_reformat_dids()
117
- self._gen_did_def_c_file(dids_f32, dids_u32, errors)
118
- self._gen_did_def_h_file(dids_f32, dids_u32)
119
- return errors
120
-
121
- def _check_and_reformat_dids(self):
122
- """Check that DIDs are defined and create two new dicts."""
123
- dids_f32 = {}
124
- dids_u32 = {}
125
- did_def_f32s = self._did_defs['Float32']['dids']
126
- did_def_u32s = self._did_defs['UInt32']['dids']
127
- errors = []
128
- for sig in sorted(self._did_dict.keys()):
129
- did = self._did_dict[sig]
130
- if did['type'] in self._float32_types:
131
- if sig in did_def_f32s:
132
- dids_f32[did_def_f32s[sig]] = did
133
- else:
134
- msg = f'Did for Float32 signal "{sig}" not defined'
135
- self.critical(msg)
136
- errors.append(msg)
137
- else:
138
- if sig in did_def_u32s:
139
- dids_u32[did_def_u32s[sig]] = did
140
- else:
141
- msg = f'Did for UInt32 signal "{sig}" not defined'
142
- self.critical(msg)
143
- errors.append(msg)
144
- return (dids_f32, dids_u32, errors)
145
-
146
- def _get_datatypes(self):
147
- tl_types = ['UInt8', 'Int8', 'UInt16', 'Int16', 'UInt32', 'Int32', 'Float32', 'Bool']
148
- data_types_tl = [f'{tl_type}_' for tl_type in tl_types]
149
- data_types_ec = [f'{get_ec_type(tl_type)}_' for tl_type in tl_types]
150
- if len(self._unit_cfgs.code_generators) > 1:
151
- self.warning('Cannot generate DIDs for more than one generator.'
152
- 'Defaulting to TargetLink')
153
- return ', '.join(data_types_tl)
154
- if CodeGenerators.target_link in self._unit_cfgs.code_generators:
155
- return ', '.join(data_types_tl)
156
- return ', '.join(data_types_ec)
157
-
158
- def _get_type(self, tl_type):
159
- if CodeGenerators.target_link in self._unit_cfgs.code_generators:
160
- return tl_type
161
- return get_ec_type(tl_type)
162
-
163
- def _gen_did_def_h_file(self, dids_f32, dids_u32):
164
- """Generate the VcDidDefinitions.h files used by the Did-API."""
165
- _, f_name = os.path.split(self.fh_h.name)
166
- header_def_name = f_name.upper().replace('.', '_')
167
- self.fh_h.write(f'#ifndef {header_def_name}\n')
168
- self.fh_h.write(f'#define {header_def_name}\n\n')
169
- self.fh_h.write(self._unit_cfgs.base_types_headers)
170
- self.fh_h.write(f'enum Datatypes {{{self._get_datatypes()}}};\n\n')
171
- self.fh_h.write(f'#define DID_DATASTRUCT_LEN_FLOAT32 {len(dids_f32)}\n')
172
- self.fh_h.write(f'#define DID_DATASTRUCT_LEN_UINT32 {len(dids_u32)}\n\n')
173
- uint16_type = self._get_type('UInt16')
174
- float32_type = self._get_type('Float32')
175
- self.fh_h.write('struct DID_Mapping_UInt32 {\n\t'
176
- f'{uint16_type} DID;'
177
- '\n\tvoid* data;\n\tenum Datatypes type;\n};\n\n')
178
- self.fh_h.write('struct DID_Mapping_Float32 {\n\t'
179
- f'{uint16_type} DID;'
180
- '\n\t'
181
- f'{float32_type}* data;'
182
- '\n};\n\n')
183
-
184
- self.fh_h.write(f'#include "{build_defs.PREDECL_START}"\n')
185
-
186
- self.fh_h.write('extern const struct DID_Mapping_UInt32 DID_data_struct_UInt32[];\n')
187
- self.fh_h.write('extern const struct DID_Mapping_Float32 DID_data_struct_Float32[];\n')
188
-
189
- self.fh_h.write('/* Floats */\n')
190
-
191
- for key in sorted(dids_f32.keys()):
192
- did = dids_f32[key]
193
- self.fh_h.write(f'extern {did["type"]} {did["name"]}; /* Did id: 0x{key:X} */\n')
194
-
195
- self.fh_h.write('/* Integers & Bools */\n')
196
-
197
- for key in sorted(dids_u32.keys()):
198
- did = dids_u32[key]
199
- self.fh_h.write(f'extern {did["type"]} {did["name"]}; /* Did id: 0x{key:X} */\n')
200
-
201
- self.fh_h.write(f'#include "{build_defs.PREDECL_END}"\n')
202
- self.fh_h.write(f'\n#endif /* {header_def_name} */\n')
203
-
204
- def _gen_did_def_c_file(self, dids_f32, dids_u32, errors):
205
- """Generate the VcDidDefinitions.c files used by the Did-API."""
206
- _, filename = os.path.split(self.fh_h.name)
207
- self.fh_c.write(f'#include "{filename}"\n\n')
208
- self.fh_c.write(f'#include "{build_defs.CVC_CODE_START}"\n\n')
209
- self.fh_c.write('/* The table shall be sorted in ascending Did is order!\n'
210
- ' If not the search algorithm does not work */\n')
211
- self.fh_c.write('const struct DID_Mapping_Float32 DID_data_struct_Float32[] = {\n')
212
-
213
- keys = sorted(dids_f32.keys())
214
- for key in keys:
215
- did = dids_f32[key]
216
- if key == keys[-1]:
217
- delim = ' '
218
- else:
219
- delim = ','
220
- self.fh_c.write('\t{0x%X, &%s}%c /* %s */ \n' %
221
- (key, did['name'], delim, did['handle']))
222
- if not keys:
223
- self.fh_c.write('\t{0x0000, 0L} /* Dummy entry */ \n')
224
- self.fh_c.write('};\n\n')
225
-
226
- self.fh_c.write('const struct DID_Mapping_UInt32 DID_data_struct_UInt32[] = {\n')
227
- keys = sorted(dids_u32.keys())
228
- for key in keys:
229
- did = dids_u32[key]
230
- if key == keys[-1]:
231
- delim = ' '
232
- else:
233
- delim = ','
234
- self.fh_c.write('\t{0x%X, &%s, %s_}%c /* %s */ \n' %
235
- (key, did['name'], did['type'], delim, did['handle']))
236
-
237
- if not keys:
238
- self.fh_c.write(f'\t{{0x0000, 0L, {self._get_type("UInt32")}_}} /* Dummy entry */ \n')
239
- self.fh_c.write('};\n\n')
240
-
241
- if errors:
242
- self.fh_c.write('/* *** DIDs not in the definition file! ****\n')
243
- for error in errors:
244
- self.fh_c.write(f'{error}\n')
245
- self.fh_c.write('*/\n')
246
-
247
- self.fh_c.write(f'\n#include "{build_defs.CVC_CODE_END}"\n')
248
- self.fh_c.write('\n/*------------------------------------------------------'
249
- '----------------------*\\\n END OF FILE\n\\*-------------'
250
- '---------------------------------------------------------------*/')
251
-
252
- def gen_did_carcom_extract(self, filename):
253
- """Generate the csv-file used for carcom database import."""
254
- with open(filename, 'w', encoding="utf-8") as carcom_file:
255
- for sig in sorted(self._did_dict.keys()):
256
- did = self._did_dict[sig]
257
- carcom_file.write(self._format_did_csv_line(did))
258
-
259
- @staticmethod
260
- def _convert_value(value, type, default_value=0):
261
- if value in ['', '-']:
262
- return type(default_value)
263
- return type(value)
264
-
265
- @staticmethod
266
- def _hex_location(value):
267
- return hex(value).upper().lstrip('0X')
268
-
269
- def _format_did_csv_line(self, did):
270
- """Format the line based on the did.
271
-
272
- Arguments:
273
- did (dict): DID data
274
- """
275
- did_line = '{' + '};{'.join(['location',
276
- 'description',
277
- 'name',
278
- 'name',
279
- 'bytes',
280
- 'offset',
281
- 'bits',
282
- 'data_type',
283
- 'nine',
284
- 'ten',
285
- 'low',
286
- 'high',
287
- 'scaling',
288
- 'compare',
289
- 'unit',
290
- 'sixteen',
291
- 'service',
292
- 'eighteen',
293
- 'sessions']) + '}\n'
294
- float_format = '06'
295
- compare = ''
296
- did_bytes = 4
297
- did_offset = 0 # Always use 0. Not sure why.
298
- did_bits = 8 * did_bytes
299
- service = 17
300
- sessions = '22: 01 02 03'
301
- unknown = '' # Fields were empty in old system
302
- did_def_f32s = self._did_defs['Float32']['dids']
303
- did_def_u32s = self._did_defs['UInt32']['dids']
304
- if did['name'] in did_def_f32s:
305
- location = self._hex_location(did_def_f32s[did['name']])
306
- elif did['name'] in did_def_u32s:
307
- location = self._hex_location(did_def_u32s[did['name']])
308
- else:
309
- self.warning('Could not find location for %s', did['name'])
310
- location = unknown
311
- if did['type'] in self._float32_types:
312
- did_type = '4-byte float'
313
- scaling = 'x*1'
314
- else:
315
- did_type = 'Unsigned'
316
- u32_scaling_base = '(x-2147483647){{operator}}{{lsb:{float_format}}} {{sign}} {{offset:{float_format}}}'
317
- u32_scaling = u32_scaling_base.format(float_format=float_format)
318
- offset = self._convert_value(did['offset'], float, 0)
319
- if offset > 0:
320
- sign = '+'
321
- else:
322
- sign = '-'
323
- lsb = self._convert_value(did['lsb'], float, 1)
324
- if lsb > 0:
325
- operator = '*'
326
- else:
327
- operator = '/'
328
- lsb = 1.0/lsb # Why we do this, I do not know.
329
- scaling = u32_scaling.format(operator=operator,
330
- lsb=lsb,
331
- sign=sign,
332
- offset=offset)
333
-
334
- return did_line.format(location=location,
335
- name=did['name'],
336
- description=did['description'],
337
- bytes=did_bytes,
338
- offset=did_offset,
339
- bits=did_bits,
340
- data_type=did_type,
341
- nine=unknown,
342
- ten=unknown,
343
- low=did['min'],
344
- high=did['max'],
345
- scaling=scaling,
346
- compare=compare,
347
- unit=did['unit'],
348
- sixteen=unknown,
349
- service=service,
350
- eighteen=unknown,
351
- sessions=sessions)
352
-
353
- def get_did_config(self):
354
- """Return a dict with the defined DIDs for all configs.
355
-
356
- Returns:
357
- dict: a dict with the DIDs defined for all configs
358
-
359
- """
360
- # self._checkConfig()
361
- return {'Float32': self._dids_f32, 'UInt32': self._dids_u32}
362
-
363
-
364
- class HIDIDs(ProblemLogger):
365
- """A class for handling of HI DID definitions."""
366
-
367
- def __init__(self, build_cfg, unit_cfgs):
368
- """Init.
369
-
370
- Args:
371
- build_cfg (BuildProjConfig): Project configuration
372
- unit_cfgs (UnitConfigs): Unit definitions
373
- """
374
- super().__init__()
375
- self._build_cfg = build_cfg
376
- self._unit_cfgs = unit_cfgs
377
- self.file_name = 'VcDIDAPI'
378
- self.did_dict = self._compose_did_data()
379
-
380
- def _load_did_config_files(self, config_file):
381
- """Load the did config files.
382
-
383
- Args:
384
- config_file (str): Path to DID configuration file.
385
- Returns:
386
- dids (dict): Parsed DIDs from the configuration file.
387
- """
388
- dids = {}
389
- config_file_path = Path(config_file)
390
- if config_file_path.exists():
391
- with config_file_path.open(mode='r', encoding='utf-8') as did_fh:
392
- yaml = YAML(typ='safe', pure=True)
393
- dids = self._verify_did_config_dict(yaml.load(did_fh))
394
- else:
395
- self.warning(f'Unable to parse DIDs. Cannot find file: {config_file_path.as_posix()}.')
396
- return dids
397
-
398
- def _verify_did_config_dict(self, dids):
399
- """Verify the structure of the dict from the DID configuration file.
400
- Missing keys will be added but also produce critical errors.
401
-
402
- Args:
403
- dids (dict): DIDs parsed from DID configuration file.
404
- Returns:
405
- (dict): Updated DID dict.
406
- """
407
- optional_keys = {
408
- 'nr_of_bytes',
409
- }
410
- expected_keys = {
411
- 'id',
412
- 'data_type',
413
- 'function_type',
414
- }
415
- expected_function_type_keys = {
416
- 'read_data',
417
- 'read_data_max',
418
- 'read_data_min',
419
- 'condition_check',
420
- 'condition_check_max',
421
- 'condition_check_min',
422
- }
423
- for did, did_data in dids.items():
424
- did_keys = set(did_data.keys())
425
- used_optional_keys = did_keys & optional_keys
426
- unknown_keys = did_keys - (expected_keys | optional_keys)
427
- missing_keys = expected_keys - did_keys
428
- for key in used_optional_keys:
429
- self.info(f'Using optional key {key} for DID {did}.')
430
- for key in unknown_keys:
431
- self.warning(f'Ignoring unknown element {key} for DID {did}.')
432
- del did_data[key]
433
- for key in missing_keys:
434
- self.critical(f'DID {did} is missing element {key}.')
435
- did_data[key] = '<missing>'
436
- if did_data['function_type'] not in expected_function_type_keys:
437
- self.critical(f"DID {did} lists unknown function type {did_data['function_type']}")
438
- did_data['function_type'] = '<missing>'
439
- return dids
440
-
441
- def _compose_did_data(self):
442
- """Gather and merge DID data from project simulink models and DID configuration file.
443
-
444
- Returns:
445
- did_dict (dict): Dict containing project DID data.
446
- """
447
- get_did_error_messages, project_dids = get_dids_in_prj(self._unit_cfgs)
448
- if get_did_error_messages:
449
- self.critical('\n'.join(get_did_error_messages))
450
- return {}
451
-
452
- did_filename = self._build_cfg.get_did_cfg_file_name()
453
- config_directory = self._build_cfg.get_prj_cfg_dir()
454
- did_config_file = os.path.join(config_directory, did_filename)
455
- dids = self._load_did_config_files(did_config_file)
456
-
457
- did_dict = self.verify_dids(project_dids, dids)
458
- for data in did_dict.values():
459
- data['function'] = self.compose_did_function(data)
460
-
461
- return did_dict
462
-
463
- @staticmethod
464
- def compose_did_function(did_data):
465
- """Compose DID function calls.
466
- Args:
467
- did_data (dict): Dict describing a DID in the project.
468
- Returns:
469
- function (str): Function to generate for given DID.
470
- """
471
- did_id = did_data["id"]
472
- data_type = did_data["data_type"]
473
- type_to_function_map = {
474
- '<missing>': f'DID_{did_id}_Missing({data_type} *Data)',
475
- 'read_data': f'DID_{did_id}_Runnable_ReadData({data_type} *Data)',
476
- 'read_data_max': f'DID_{did_id}_Runnable_MAX_ReadData({data_type} *Data)',
477
- 'read_data_min': f'DID_{did_id}_Runnable_MIN_ReadData({data_type} *Data)',
478
- 'condition_check': f'DID_{did_id}_Runnable_ConditionCheckRead({data_type} *ErrorCode)',
479
- 'condition_check_max': f'DID_{did_id}_Runnable_MAX_ConditionCheckRead({data_type} *ErrorCode)',
480
- 'condition_check_min': f'DID_{did_id}_Runnable_MIN_ConditionCheckRead({data_type} *ErrorCode)'
481
- }
482
- return type_to_function_map[did_data['function_type']]
483
-
484
- def verify_dids(self, project_dids, dids):
485
- """Verify the DIDs.
486
-
487
- * Model DIDs must be defined in DID configuration file.
488
- * ID numbers can only appear once per function type.
489
-
490
- Args:
491
- project_dids (dict): DIDs listed in project/simulink models.
492
- dids (dict): DIDs listed in the DID configuration file.
493
- Returns:
494
- valid_dids (dict): Validated DIDs listed in both DID configuration file as well as project.
495
- """
496
- valid_dids = {}
497
- did_id_usage = {}
498
-
499
- if not project_dids:
500
- for did in dids:
501
- self.warning(f'Ignoring DID {did}, not defined in any model.')
502
- return valid_dids
503
-
504
- for name in project_dids:
505
- if name not in dids:
506
- self.warning(f'DID {name} not defined in DID defintion file.')
507
- continue
508
-
509
- did_id = dids[name]['id']
510
- function_type = dids[name]['function_type']
511
- if did_id in did_id_usage:
512
- if function_type in did_id_usage[did_id]:
513
- self.critical(
514
- f'ID {did_id} is '
515
- f'already used for DID {did_id_usage[did_id][function_type]} of '
516
- f'function type {function_type}.'
517
- )
518
- continue
519
- did_id_usage[did_id][function_type] = name
520
- else:
521
- did_id_usage[did_id] = {function_type: name}
522
-
523
- valid_dids[name] = deep_dict_update(dids[name], project_dids[name])
524
-
525
- return valid_dids
526
-
527
- def get_header_file_content(self):
528
- """Get content for the DID API header file.
529
-
530
- Returns:
531
- (list(str)): List of lines to write to DID API header file.
532
- """
533
- name = self._build_cfg.get_a2l_cfg()['name']
534
- header_guard = f'{self.file_name.upper()}_H'
535
- header = [
536
- f'#ifndef {header_guard}\n',
537
- f'#define {header_guard}\n',
538
- '\n',
539
- '#include "tl_basetypes.h"\n',
540
- f'#include "Rte_{name}.h"\n',
541
- '\n'
542
- ]
543
- footer = [f'\n#endif /* {header_guard} */\n']
544
-
545
- if not self.did_dict:
546
- return header + footer
547
-
548
- body = [f'#include "{build_defs.PREDECL_DISP_ASIL_D_START}"\n']
549
- for did_data in self.did_dict.values():
550
- define = did_data["class"].split('/')[-1] # E.q. for ASIL D it is ASIL_D/CVC_DISP_ASIL_D
551
- body.append(f'extern {define} {did_data["type"]} {did_data["name"]};\n')
552
- body.append(f'#include "{build_defs.PREDECL_DISP_ASIL_D_END}"\n')
553
-
554
- body.append(f'\n#include "{build_defs.PREDECL_CODE_ASIL_D_START}"\n')
555
- for did_data in self.did_dict.values():
556
- body.append(f'void {did_data["function"]};\n')
557
- body.append(f'#include "{build_defs.PREDECL_CODE_ASIL_D_END}"\n')
558
-
559
- return header + body + footer
560
-
561
- def get_source_file_content(self):
562
- """Get content for the DID API source file.
563
-
564
- Returns:
565
- (list(str)): List of lines to write to DID API source file.
566
- """
567
- header = [
568
- f'#include "{self.file_name}.h"\n',
569
- '\n'
570
- ]
571
-
572
- if not self.did_dict:
573
- return header
574
-
575
- body = [f'#include "{build_defs.CVC_CODE_ASIL_D_START}"\n']
576
- for did, did_data in self.did_dict.items():
577
- size = f'{did_data["nr_of_bytes"]}' if 'nr_of_bytes' in did_data else f'sizeof({did_data["data_type"]})'
578
- if 'ConditionCheckRead' in did_data["function"]:
579
- argument = 'ErrorCode'
580
- else:
581
- argument = 'Data'
582
- body.extend([
583
- f'void {did_data["function"]}\n',
584
- '{\n',
585
- f' memcpy({argument}, &{did}, {size});\n',
586
- '}\n'
587
- ])
588
- body.append(f'#include "{build_defs.CVC_CODE_ASIL_D_END}"\n')
589
-
590
- return header + body
591
-
592
- def generate_did_files(self):
593
- """Generate required DID API files.
594
- Only use for some projects, which doesn't copy static code."""
595
- file_contents = {
596
- '.h': self.get_header_file_content(),
597
- '.c': self.get_source_file_content()
598
- }
599
- src_dst_dir = self._build_cfg.get_src_code_dst_dir()
600
- for extension, content in file_contents.items():
601
- file_path = Path(src_dst_dir, self.file_name + extension)
602
- with file_path.open(mode='w', encoding='utf-8') as file_handler:
603
- file_handler.writelines(content)
604
-
605
-
606
- class ZCDIDs(ProblemLogger):
607
- """A class for handling of ZC DID definitions."""
608
-
609
- def __init__(self, build_cfg, unit_cfgs):
610
- """Init.
611
-
612
- Args:
613
- build_cfg (BuildProjConfig): Project configuration
614
- unit_cfgs (UnitConfigs): Unit definitions
615
- """
616
- super().__init__()
617
- self._build_cfg = build_cfg
618
- self._unit_cfgs = unit_cfgs
619
- self._valid_dids = None
620
- prefix = self._build_cfg.get_scheduler_prefix()
621
- self.operation_file_name = 'VcDIDAPI'
622
- self.sender_receiver_file_name = f'{prefix}UpdatingDIDValues'
623
- self.project_dids = self._get_project_dids()
624
-
625
- @property
626
- def valid_dids(self):
627
- return self._valid_dids
628
-
629
- @valid_dids.setter
630
- def valid_dids(self, yaml_dids):
631
- """Return a set of DIDs appearing in both the project and the project yaml file.
632
-
633
- Args:
634
- yaml_dids (dict): DIDs listed in the DID configuration yaml file.
635
- Returns:
636
- valid_dids (dict): Validated DIDs listed in both DID configuration yaml file as well as project.
637
- """
638
- self._valid_dids = {}
639
-
640
- dids_not_in_yaml = set(self.project_dids.keys()) - set(yaml_dids.keys())
641
- for did in dids_not_in_yaml:
642
- self.critical(f'DID {did} not defined in project diagnostics yaml file.')
643
-
644
- for did, did_data in yaml_dids.items():
645
- if did_data.get('manual', False):
646
- self._valid_dids[did] = {k: v for k, v in did_data.items() if k != "manual"}
647
- continue
648
- if did not in self.project_dids:
649
- self.warning(f'Ignoring DID {did}, not defined in any model.')
650
- continue
651
- data_type = self.project_dids[did]['type']
652
- if not data_type.startswith('UInt'):
653
- self.warning(f'Ignoring DID {did} of type {data_type}, only unsigned integers are supported.')
654
- continue
655
- self._valid_dids[did] = did_data
656
-
657
- def _get_project_dids(self):
658
- """Return a dict with DIDs defined in the project.
659
- Throws a critical error if something goes wrong.
660
-
661
- Returns:
662
- project_dids (dict): a dict with all dids in the project.
663
- """
664
- get_did_error_messages, project_dids = get_dids_in_prj(self._unit_cfgs)
665
- if get_did_error_messages:
666
- self.critical('\n'.join(get_did_error_messages))
667
- return {}
668
- return project_dids
669
-
670
- def _get_sender_receiver_header_file_content(self):
671
- """Get content for the S/R DID API header file.
672
-
673
- The function in this file is a runnable generated by yaml2arxml.
674
-
675
- Returns:
676
- (bool): True if any S/R DIDs are defined.
677
- (list(str)): List of lines to write to the S/R DID API header file.
678
- """
679
- name = self._build_cfg.get_composition_config("softwareComponentName")
680
- header_guard = f'{self.sender_receiver_file_name.upper()}_H'
681
- header = [
682
- f'#ifndef {header_guard}\n',
683
- f'#define {header_guard}\n',
684
- '\n',
685
- '#include "tl_basetypes.h"\n',
686
- f'#include "Rte_{name}.h"\n',
687
- '\n'
688
- ]
689
- footer = [f'\n#endif /* {header_guard} */\n']
690
-
691
- if not self.valid_dids:
692
- return False, header + footer
693
-
694
- variable_declarations = []
695
- function_declarations = []
696
- sender_receiver_dids_exist = False
697
- for did, did_data in self.valid_dids.items():
698
- project_did_data = self.project_dids[did]
699
- if did_data.get('PortType', 'dummy') not in ['BOTH', 'SENDER-RECEIVER']:
700
- continue # C/R DIDs are handled in _get_operation... functions
701
- if not project_did_data['type'].startswith('UInt'):
702
- self.warning(
703
- f'Ignoring DID {did} of type {project_did_data["type"]}, only unsigned integers are supported.'
704
- )
705
- continue
706
- sender_receiver_dids_exist = True
707
- define = self.project_dids[did]["class"].split('/')[-1] # E.q. for ASIL D it is ASIL_D/CVC_DISP_ASIL_D
708
- variable_declarations.append(
709
- f'extern {define} {self.project_dids[did]["type"]} {self.project_dids[did]["name"]};\n'
710
- )
711
- function_declarations.append(
712
- f'extern UInt8 Rte_Write_DataServices_DID_{did}_data({project_did_data["type"]} {did});\n'
713
- )
714
-
715
- function_declarations.append(f'\nvoid Run_{self.sender_receiver_file_name}(void);\n')
716
-
717
- body = [
718
- f'#include "{build_defs.PREDECL_DISP_ASIL_D_START}"\n',
719
- *variable_declarations,
720
- f'#include "{build_defs.PREDECL_DISP_ASIL_D_END}"\n',
721
- f'\n#include "{build_defs.PREDECL_CODE_ASIL_D_START}"\n',
722
- *function_declarations,
723
- f'#include "{build_defs.PREDECL_CODE_ASIL_D_END}"\n',
724
- ]
725
-
726
- return sender_receiver_dids_exist, header + body + footer
727
-
728
- def _get_sender_receiver_source_file_content(self):
729
- """Get content for the S/R DID API source file.
730
-
731
- The function in this file is a runnable generated by yaml2arxml.
732
-
733
- Returns:
734
- (bool): True if any S/R DIDs are defined.
735
- (list(str)): List of lines to write to the S/R DID API source file.
736
- """
737
- header = [
738
- f'#include "{self.sender_receiver_file_name}.h"\n',
739
- '\n'
740
- ]
741
-
742
- if not self.valid_dids:
743
- return False, header
744
-
745
- sender_receiver_dids_exist = False
746
- body = [
747
- f'#include "{build_defs.CVC_CODE_ASIL_D_START}"\n',
748
- f'void Run_{self.sender_receiver_file_name}(void)\n',
749
- '{\n'
750
- ]
751
- for did, did_data in self.valid_dids.items():
752
- project_did_data = self.project_dids[did]
753
- if did_data.get('PortType', 'dummy') not in ['BOTH', 'SENDER-RECEIVER']:
754
- continue # C/R DIDs are handled in _get_operation... functions
755
- if not project_did_data['type'].startswith('UInt'):
756
- self.warning(
757
- f'Ignoring DID {did} of type {project_did_data["type"]}, only unsigned integers are supported.'
758
- )
759
- continue
760
- sender_receiver_dids_exist = True
761
- body.append(f' Rte_Write_DataServices_DID_{did}_data({did});\n')
762
- body.extend([
763
- '}\n',
764
- f'#include "{build_defs.CVC_CODE_ASIL_D_END}"\n'
765
- ])
766
-
767
- return sender_receiver_dids_exist, header + body
768
-
769
- def _get_operation_data(self, operation, did_data):
770
- """Get operation function data of supported operations.
771
-
772
- Args:
773
- operation (str): Operation to get data for.
774
- did_data (dict): DID data.
775
- Returns:
776
- (dict): Operation function data.
777
- """
778
- array_size = byte_size(did_data['type'])
779
- if array_size > 1:
780
- read_data_declaration = f'UInt8 Run_{did_data["name"]}_ReadData(UInt8 Data[{array_size}])'
781
- read_data_definition = (
782
- '{\n'
783
- f' for (UInt8 i = 0U; i < {array_size}; i++) {{\n'
784
- f' Data[{array_size} - 1 - i] = ({did_data["name"]} >> (8 * i)) & 0xFF;\n'
785
- ' }\n'
786
- ' return 0U;\n'
787
- '}\n'
788
- )
789
- else:
790
- read_data_declaration = f'UInt8 Run_{did_data["name"]}_ReadData(UInt8 *Data)'
791
- read_data_definition = (
792
- '{\n'
793
- f' *Data = {did_data["name"]};\n'
794
- ' return 0U;\n'
795
- '}\n'
796
- )
797
- operation_data = {
798
- 'ReadData': {
799
- 'declaration': read_data_declaration,
800
- 'body': read_data_definition,
801
- }
802
- }
803
- if operation not in operation_data:
804
- return None
805
- return operation_data[operation]
806
-
807
- def _get_operation_header_file_content(self):
808
- """Get content for the DID API header file.
809
-
810
- Returns:
811
- (list(str)): List of lines to write to the DID API header file.
812
- """
813
- name = self._build_cfg.get_composition_config("softwareComponentName")
814
- header_guard = f'{self.operation_file_name.upper()}_H'
815
- header = [
816
- f'#ifndef {header_guard}\n',
817
- f'#define {header_guard}\n',
818
- '\n',
819
- '#include "tl_basetypes.h"\n',
820
- f'#include "Rte_{name}.h"\n',
821
- '\n'
822
- ]
823
- footer = [f'\n#endif /* {header_guard} */\n']
824
-
825
- if not self.valid_dids:
826
- return header + footer
827
-
828
- variable_declarations = []
829
- function_declarations = []
830
- for did, did_data in self.valid_dids.items():
831
- if did_data.get('PortType', 'dummy') == 'SENDER-RECEIVER':
832
- continue # S/R DIDs are handled in _get_sender_receiver... functions
833
- define = self.project_dids[did]["class"].split('/')[-1] # E.q. for ASIL D it is ASIL_D/CVC_DISP_ASIL_D
834
- variable_declarations.append(
835
- f'extern {define} {self.project_dids[did]["type"]} {self.project_dids[did]["name"]};\n'
836
- )
837
- for operation in did_data["operations"]:
838
- operation_data = self._get_operation_data(operation, self.project_dids[did])
839
- if operation_data is None:
840
- self.warning(
841
- f'Will not generate code for unsupported operation {operation}. Add manually for DID {did}.'
842
- )
843
- continue
844
- function_declarations.append(operation_data['declaration'] + ';\n')
845
-
846
- body = [
847
- f'#include "{build_defs.PREDECL_DISP_ASIL_D_START}"\n',
848
- *variable_declarations,
849
- f'#include "{build_defs.PREDECL_DISP_ASIL_D_END}"\n',
850
- f'\n#include "{build_defs.PREDECL_CODE_ASIL_D_START}"\n',
851
- *function_declarations,
852
- f'#include "{build_defs.PREDECL_CODE_ASIL_D_END}"\n',
853
- ]
854
-
855
- return header + body + footer
856
-
857
- def _get_operation_source_file_content(self):
858
- """Get content for the DID API source file.
859
-
860
- Returns:
861
- (list(str)): List of lines to write to the DID API source file.
862
- """
863
- header = [
864
- f'#include "{self.operation_file_name}.h"\n',
865
- '\n'
866
- ]
867
-
868
- if not self.valid_dids:
869
- return header
870
-
871
- body = [f'#include "{build_defs.CVC_CODE_ASIL_D_START}"\n']
872
- for did, did_data in self.valid_dids.items():
873
- if did_data.get('PortType', 'dummy') == 'SENDER-RECEIVER':
874
- continue # S/R DIDs are handled in _get_sender_receiver... functions
875
- for operation in did_data["operations"]:
876
- operation_data = self._get_operation_data(operation, self.project_dids[did])
877
- if operation_data is None:
878
- continue # Warning already given in header generation
879
- body.append(operation_data['declaration'] + '\n' + operation_data['body'])
880
- body.append(f'#include "{build_defs.CVC_CODE_ASIL_D_END}"\n')
881
-
882
- return header + body
883
-
884
- def generate_did_files(self):
885
- """Generate required DID API files.
886
- Only use for some projects, which doesn't copy static code."""
887
- if self.valid_dids is None:
888
- includeDiagnostics = self._build_cfg.get_composition_config("includeDiagnostics")
889
- if includeDiagnostics in ["manual", "manual_dids"]:
890
- self.warning(f'includeDiagnostics is set to {includeDiagnostics}, not generating DID files.')
891
- return
892
- self.critical('Valid DIDs not set. Cannot generate DID files.')
893
- return
894
-
895
- src_dst_dir = self._build_cfg.get_src_code_dst_dir()
896
-
897
- # CLIENT-SERVER DIDs
898
- file_contents = {
899
- '.h': self._get_operation_header_file_content(),
900
- '.c': self._get_operation_source_file_content()
901
- }
902
- for extension, content in file_contents.items():
903
- file_path = Path(src_dst_dir, self.operation_file_name + extension)
904
- with file_path.open(mode='w', encoding='utf-8') as file_handler:
905
- file_handler.writelines(content)
906
-
907
- # SENDER-RECEIVER DIDs
908
- sender_receiver_dids_exist_header, header_contents = self._get_sender_receiver_header_file_content()
909
- sender_receiver_dids_exist_source, source_contents = self._get_sender_receiver_source_file_content()
910
- if sender_receiver_dids_exist_header and sender_receiver_dids_exist_source:
911
- file_path = Path(src_dst_dir, self.sender_receiver_file_name + '.h')
912
- with file_path.open(mode='w', encoding='utf-8') as file_handler:
913
- file_handler.writelines(header_contents)
914
- file_path = Path(src_dst_dir, self.sender_receiver_file_name + '.c')
915
- with file_path.open(mode='w', encoding='utf-8') as file_handler:
916
- file_handler.writelines(source_contents)
1
+ # Copyright 2024 Volvo Car Corporation
2
+ # Licensed under Apache 2.0.
3
+
4
+ # -*- coding: utf-8 -*-
5
+ """Module containing classes for DID definitions.
6
+
7
+ This module is used to parse DID definition files and merge with the unit definitions to find DIDs in a project.
8
+ It then generates the DID definition c-files for the supplier DID API.
9
+ """
10
+
11
+ import csv
12
+ import os
13
+ from pathlib import Path
14
+ from ruamel.yaml import YAML
15
+ from powertrain_build import build_defs
16
+ from powertrain_build.lib.helper_functions import deep_dict_update
17
+ from powertrain_build.problem_logger import ProblemLogger
18
+ from powertrain_build.types import byte_size, get_ec_type, get_float32_types
19
+ from powertrain_build.unit_configs import CodeGenerators
20
+
21
+
22
+ def get_dids_in_prj(unit_cfgs):
23
+ """Return a dict with DIDs in the currently included SW-Units.
24
+
25
+ Args:
26
+ unit_cfgs (UnitConfigs): Unit definitions.
27
+ Returns:
28
+ error_message (str): Message in case something went wrong.
29
+ dict: a dict with all dids in the project, in the below format:
30
+
31
+ ::
32
+
33
+ {'DID_VARIABLE_NAME': {
34
+ 'handle': 'VcRegCh/VcRegCh/Subsystem/VcRegCh/1000_VcRegCh/1600_DID/Gain14',
35
+ 'configs': ['all'],
36
+ 'type': 'UInt32',
37
+ 'unit': '-',
38
+ 'lsb': 1,
39
+ 'max': 20,
40
+ 'min': 0,
41
+ 'offset': 0,
42
+ 'description': 'Actual Regen State',
43
+ 'name': 'DID_VARIABLE_NAME',
44
+ 'class': 'CVC_DISP'
45
+ }
46
+ }
47
+ """
48
+ dids_prj = {}
49
+ error_messages = []
50
+ unit_cfg = unit_cfgs.get_per_unit_cfg()
51
+ for unit, data in unit_cfg.items():
52
+ dids = data.get('dids')
53
+ if dids is None:
54
+ error_messages.append(f'No "dids" key in unit config for {unit}.')
55
+ continue
56
+ for name, did in dids.items():
57
+ dids_prj[name] = did
58
+ return error_messages, dids_prj
59
+
60
+
61
+ class DIDs(ProblemLogger):
62
+ """A class for handling of DID definitions."""
63
+
64
+ def __init__(self, build_cfg, unit_cfgs):
65
+ """Parse DID definition files referenced by project config.
66
+
67
+ Args:
68
+ build_cfg (BuildProjConfig): Project configuration
69
+ unit_cfgs (UnitConfigs): Unit definitions
70
+ """
71
+ super().__init__()
72
+ self._build_cfg = build_cfg
73
+ self._unit_cfgs = unit_cfgs
74
+ did_filename = self._build_cfg.get_did_cfg_file_name()
75
+ cfg_dir = self._build_cfg.get_prj_cfg_dir()
76
+ did_f32_cfg_file = os.path.join(cfg_dir, did_filename + '_Float32.csv')
77
+ did_u32_cfg_file = os.path.join(cfg_dir, did_filename + '_UInt32.csv')
78
+ self._dids_f32 = self._load_did_config_files(did_f32_cfg_file)
79
+ self._dids_u32 = self._load_did_config_files(did_u32_cfg_file)
80
+ self.fh_h = None
81
+ self.fh_c = None
82
+ get_did_error_messages, self._did_dict = get_dids_in_prj(unit_cfgs)
83
+ self._did_defs = self.get_did_config()
84
+ self._float32_types = get_float32_types()
85
+ if get_did_error_messages:
86
+ self.critical('\n'.join(get_did_error_messages))
87
+
88
+ def _load_did_config_files(self, config_file):
89
+ """Load the did config files."""
90
+ dids = {}
91
+ with open(config_file, mode='r', encoding='utf-8') as did_fh:
92
+ csv_did = csv.reader(did_fh, delimiter=';')
93
+ did = list(csv_did)
94
+ dids['dids'] = {row[0]: int(row[1], 16) for row in did[3:]}
95
+ dids['start_did'] = int(did[1][0], 16)
96
+ dids['end_did'] = int(did[1][1], 16)
97
+ self._check_dids(dids)
98
+ return dids
99
+
100
+ @staticmethod
101
+ def _check_dids(dids):
102
+ """Check that all dids are within the start and end values."""
103
+ start_did = dids['start_did']
104
+ end_did = dids['end_did']
105
+
106
+ for var, did in dids['dids'].items():
107
+ if did < start_did:
108
+ raise ValueError(f'{var} has a too low did 0x{did:X} start did is 0x{start_did:X}')
109
+ if did > end_did:
110
+ raise ValueError(f'{var} has a too high did 0x{did:X} start did is 0x{start_did:X}')
111
+
112
+ def gen_did_def_files(self, filename):
113
+ """Generate the VcDidDefinitions.c & h files used by the Did-API."""
114
+ with open(filename + '.h', 'w', encoding="utf-8") as self.fh_h:
115
+ with open(filename + '.c', 'w', encoding="utf-8") as self.fh_c:
116
+ dids_f32, dids_u32, errors = self._check_and_reformat_dids()
117
+ self._gen_did_def_c_file(dids_f32, dids_u32, errors)
118
+ self._gen_did_def_h_file(dids_f32, dids_u32)
119
+ return errors
120
+
121
+ def _check_and_reformat_dids(self):
122
+ """Check that DIDs are defined and create two new dicts."""
123
+ dids_f32 = {}
124
+ dids_u32 = {}
125
+ did_def_f32s = self._did_defs['Float32']['dids']
126
+ did_def_u32s = self._did_defs['UInt32']['dids']
127
+ errors = []
128
+ for sig in sorted(self._did_dict.keys()):
129
+ did = self._did_dict[sig]
130
+ if did['type'] in self._float32_types:
131
+ if sig in did_def_f32s:
132
+ dids_f32[did_def_f32s[sig]] = did
133
+ else:
134
+ msg = f'Did for Float32 signal "{sig}" not defined'
135
+ self.critical(msg)
136
+ errors.append(msg)
137
+ else:
138
+ if sig in did_def_u32s:
139
+ dids_u32[did_def_u32s[sig]] = did
140
+ else:
141
+ msg = f'Did for UInt32 signal "{sig}" not defined'
142
+ self.critical(msg)
143
+ errors.append(msg)
144
+ return (dids_f32, dids_u32, errors)
145
+
146
+ def _get_datatypes(self):
147
+ tl_types = ['UInt8', 'Int8', 'UInt16', 'Int16', 'UInt32', 'Int32', 'Float32', 'Bool']
148
+ data_types_tl = [f'{tl_type}_' for tl_type in tl_types]
149
+ data_types_ec = [f'{get_ec_type(tl_type)}_' for tl_type in tl_types]
150
+ if len(self._unit_cfgs.code_generators) > 1:
151
+ self.warning('Cannot generate DIDs for more than one generator.'
152
+ 'Defaulting to TargetLink')
153
+ return ', '.join(data_types_tl)
154
+ if CodeGenerators.target_link in self._unit_cfgs.code_generators:
155
+ return ', '.join(data_types_tl)
156
+ return ', '.join(data_types_ec)
157
+
158
+ def _get_type(self, tl_type):
159
+ if CodeGenerators.target_link in self._unit_cfgs.code_generators:
160
+ return tl_type
161
+ return get_ec_type(tl_type)
162
+
163
+ def _gen_did_def_h_file(self, dids_f32, dids_u32):
164
+ """Generate the VcDidDefinitions.h files used by the Did-API."""
165
+ _, f_name = os.path.split(self.fh_h.name)
166
+ header_def_name = f_name.upper().replace('.', '_')
167
+ self.fh_h.write(f'#ifndef {header_def_name}\n')
168
+ self.fh_h.write(f'#define {header_def_name}\n\n')
169
+ self.fh_h.write(self._unit_cfgs.base_types_headers)
170
+ self.fh_h.write(f'enum Datatypes {{{self._get_datatypes()}}};\n\n')
171
+ self.fh_h.write(f'#define DID_DATASTRUCT_LEN_FLOAT32 {len(dids_f32)}\n')
172
+ self.fh_h.write(f'#define DID_DATASTRUCT_LEN_UINT32 {len(dids_u32)}\n\n')
173
+ uint16_type = self._get_type('UInt16')
174
+ float32_type = self._get_type('Float32')
175
+ self.fh_h.write('struct DID_Mapping_UInt32 {\n\t'
176
+ f'{uint16_type} DID;'
177
+ '\n\tvoid* data;\n\tenum Datatypes type;\n};\n\n')
178
+ self.fh_h.write('struct DID_Mapping_Float32 {\n\t'
179
+ f'{uint16_type} DID;'
180
+ '\n\t'
181
+ f'{float32_type}* data;'
182
+ '\n};\n\n')
183
+
184
+ self.fh_h.write(f'#include "{build_defs.PREDECL_START}"\n')
185
+
186
+ self.fh_h.write('extern const struct DID_Mapping_UInt32 DID_data_struct_UInt32[];\n')
187
+ self.fh_h.write('extern const struct DID_Mapping_Float32 DID_data_struct_Float32[];\n')
188
+
189
+ self.fh_h.write('/* Floats */\n')
190
+
191
+ for key in sorted(dids_f32.keys()):
192
+ did = dids_f32[key]
193
+ self.fh_h.write(f'extern {did["type"]} {did["name"]}; /* Did id: 0x{key:X} */\n')
194
+
195
+ self.fh_h.write('/* Integers & Bools */\n')
196
+
197
+ for key in sorted(dids_u32.keys()):
198
+ did = dids_u32[key]
199
+ self.fh_h.write(f'extern {did["type"]} {did["name"]}; /* Did id: 0x{key:X} */\n')
200
+
201
+ self.fh_h.write(f'#include "{build_defs.PREDECL_END}"\n')
202
+ self.fh_h.write(f'\n#endif /* {header_def_name} */\n')
203
+
204
+ def _gen_did_def_c_file(self, dids_f32, dids_u32, errors):
205
+ """Generate the VcDidDefinitions.c files used by the Did-API."""
206
+ _, filename = os.path.split(self.fh_h.name)
207
+ self.fh_c.write(f'#include "{filename}"\n\n')
208
+ self.fh_c.write(f'#include "{build_defs.CVC_CODE_START}"\n\n')
209
+ self.fh_c.write('/* The table shall be sorted in ascending Did is order!\n'
210
+ ' If not the search algorithm does not work */\n')
211
+ self.fh_c.write('const struct DID_Mapping_Float32 DID_data_struct_Float32[] = {\n')
212
+
213
+ keys = sorted(dids_f32.keys())
214
+ for key in keys:
215
+ did = dids_f32[key]
216
+ if key == keys[-1]:
217
+ delim = ' '
218
+ else:
219
+ delim = ','
220
+ self.fh_c.write('\t{0x%X, &%s}%c /* %s */ \n' %
221
+ (key, did['name'], delim, did['handle']))
222
+ if not keys:
223
+ self.fh_c.write('\t{0x0000, 0L} /* Dummy entry */ \n')
224
+ self.fh_c.write('};\n\n')
225
+
226
+ self.fh_c.write('const struct DID_Mapping_UInt32 DID_data_struct_UInt32[] = {\n')
227
+ keys = sorted(dids_u32.keys())
228
+ for key in keys:
229
+ did = dids_u32[key]
230
+ if key == keys[-1]:
231
+ delim = ' '
232
+ else:
233
+ delim = ','
234
+ self.fh_c.write('\t{0x%X, &%s, %s_}%c /* %s */ \n' %
235
+ (key, did['name'], did['type'], delim, did['handle']))
236
+
237
+ if not keys:
238
+ self.fh_c.write(f'\t{{0x0000, 0L, {self._get_type("UInt32")}_}} /* Dummy entry */ \n')
239
+ self.fh_c.write('};\n\n')
240
+
241
+ if errors:
242
+ self.fh_c.write('/* *** DIDs not in the definition file! ****\n')
243
+ for error in errors:
244
+ self.fh_c.write(f'{error}\n')
245
+ self.fh_c.write('*/\n')
246
+
247
+ self.fh_c.write(f'\n#include "{build_defs.CVC_CODE_END}"\n')
248
+ self.fh_c.write('\n/*------------------------------------------------------'
249
+ '----------------------*\\\n END OF FILE\n\\*-------------'
250
+ '---------------------------------------------------------------*/')
251
+
252
+ def gen_did_carcom_extract(self, filename):
253
+ """Generate the csv-file used for carcom database import."""
254
+ with open(filename, 'w', encoding="utf-8") as carcom_file:
255
+ for sig in sorted(self._did_dict.keys()):
256
+ did = self._did_dict[sig]
257
+ carcom_file.write(self._format_did_csv_line(did))
258
+
259
+ @staticmethod
260
+ def _convert_value(value, type, default_value=0):
261
+ if value in ['', '-']:
262
+ return type(default_value)
263
+ return type(value)
264
+
265
+ @staticmethod
266
+ def _hex_location(value):
267
+ return hex(value).upper().lstrip('0X')
268
+
269
+ def _format_did_csv_line(self, did):
270
+ """Format the line based on the did.
271
+
272
+ Arguments:
273
+ did (dict): DID data
274
+ """
275
+ did_line = '{' + '};{'.join(['location',
276
+ 'description',
277
+ 'name',
278
+ 'name',
279
+ 'bytes',
280
+ 'offset',
281
+ 'bits',
282
+ 'data_type',
283
+ 'nine',
284
+ 'ten',
285
+ 'low',
286
+ 'high',
287
+ 'scaling',
288
+ 'compare',
289
+ 'unit',
290
+ 'sixteen',
291
+ 'service',
292
+ 'eighteen',
293
+ 'sessions']) + '}\n'
294
+ float_format = '06'
295
+ compare = ''
296
+ did_bytes = 4
297
+ did_offset = 0 # Always use 0. Not sure why.
298
+ did_bits = 8 * did_bytes
299
+ service = 17
300
+ sessions = '22: 01 02 03'
301
+ unknown = '' # Fields were empty in old system
302
+ did_def_f32s = self._did_defs['Float32']['dids']
303
+ did_def_u32s = self._did_defs['UInt32']['dids']
304
+ if did['name'] in did_def_f32s:
305
+ location = self._hex_location(did_def_f32s[did['name']])
306
+ elif did['name'] in did_def_u32s:
307
+ location = self._hex_location(did_def_u32s[did['name']])
308
+ else:
309
+ self.warning('Could not find location for %s', did['name'])
310
+ location = unknown
311
+ if did['type'] in self._float32_types:
312
+ did_type = '4-byte float'
313
+ scaling = 'x*1'
314
+ else:
315
+ did_type = 'Unsigned'
316
+ u32_scaling_base = '(x-2147483647){{operator}}{{lsb:{float_format}}} {{sign}} {{offset:{float_format}}}'
317
+ u32_scaling = u32_scaling_base.format(float_format=float_format)
318
+ offset = self._convert_value(did['offset'], float, 0)
319
+ if offset > 0:
320
+ sign = '+'
321
+ else:
322
+ sign = '-'
323
+ lsb = self._convert_value(did['lsb'], float, 1)
324
+ if lsb > 0:
325
+ operator = '*'
326
+ else:
327
+ operator = '/'
328
+ lsb = 1.0/lsb # Why we do this, I do not know.
329
+ scaling = u32_scaling.format(operator=operator,
330
+ lsb=lsb,
331
+ sign=sign,
332
+ offset=offset)
333
+
334
+ return did_line.format(location=location,
335
+ name=did['name'],
336
+ description=did['description'],
337
+ bytes=did_bytes,
338
+ offset=did_offset,
339
+ bits=did_bits,
340
+ data_type=did_type,
341
+ nine=unknown,
342
+ ten=unknown,
343
+ low=did['min'],
344
+ high=did['max'],
345
+ scaling=scaling,
346
+ compare=compare,
347
+ unit=did['unit'],
348
+ sixteen=unknown,
349
+ service=service,
350
+ eighteen=unknown,
351
+ sessions=sessions)
352
+
353
+ def get_did_config(self):
354
+ """Return a dict with the defined DIDs for all configs.
355
+
356
+ Returns:
357
+ dict: a dict with the DIDs defined for all configs
358
+
359
+ """
360
+ # self._checkConfig()
361
+ return {'Float32': self._dids_f32, 'UInt32': self._dids_u32}
362
+
363
+
364
+ class HIDIDs(ProblemLogger):
365
+ """A class for handling of HI DID definitions."""
366
+
367
+ def __init__(self, build_cfg, unit_cfgs):
368
+ """Init.
369
+
370
+ Args:
371
+ build_cfg (BuildProjConfig): Project configuration
372
+ unit_cfgs (UnitConfigs): Unit definitions
373
+ """
374
+ super().__init__()
375
+ self._build_cfg = build_cfg
376
+ self._unit_cfgs = unit_cfgs
377
+ self.file_name = 'VcDIDAPI'
378
+ self.did_dict = self._compose_did_data()
379
+
380
+ def _load_did_config_files(self, config_file):
381
+ """Load the did config files.
382
+
383
+ Args:
384
+ config_file (str): Path to DID configuration file.
385
+ Returns:
386
+ dids (dict): Parsed DIDs from the configuration file.
387
+ """
388
+ dids = {}
389
+ config_file_path = Path(config_file)
390
+ if config_file_path.exists():
391
+ with config_file_path.open(mode='r', encoding='utf-8') as did_fh:
392
+ yaml = YAML(typ='safe', pure=True)
393
+ dids = self._verify_did_config_dict(yaml.load(did_fh))
394
+ else:
395
+ self.warning(f'Unable to parse DIDs. Cannot find file: {config_file_path.as_posix()}.')
396
+ return dids
397
+
398
+ def _verify_did_config_dict(self, dids):
399
+ """Verify the structure of the dict from the DID configuration file.
400
+ Missing keys will be added but also produce critical errors.
401
+
402
+ Args:
403
+ dids (dict): DIDs parsed from DID configuration file.
404
+ Returns:
405
+ (dict): Updated DID dict.
406
+ """
407
+ optional_keys = {
408
+ 'nr_of_bytes',
409
+ }
410
+ expected_keys = {
411
+ 'id',
412
+ 'data_type',
413
+ 'function_type',
414
+ }
415
+ expected_function_type_keys = {
416
+ 'read_data',
417
+ 'read_data_max',
418
+ 'read_data_min',
419
+ 'condition_check',
420
+ 'condition_check_max',
421
+ 'condition_check_min',
422
+ }
423
+ for did, did_data in dids.items():
424
+ did_keys = set(did_data.keys())
425
+ used_optional_keys = did_keys & optional_keys
426
+ unknown_keys = did_keys - (expected_keys | optional_keys)
427
+ missing_keys = expected_keys - did_keys
428
+ for key in used_optional_keys:
429
+ self.info(f'Using optional key {key} for DID {did}.')
430
+ for key in unknown_keys:
431
+ self.warning(f'Ignoring unknown element {key} for DID {did}.')
432
+ del did_data[key]
433
+ for key in missing_keys:
434
+ self.critical(f'DID {did} is missing element {key}.')
435
+ did_data[key] = '<missing>'
436
+ if did_data['function_type'] not in expected_function_type_keys:
437
+ self.critical(f"DID {did} lists unknown function type {did_data['function_type']}")
438
+ did_data['function_type'] = '<missing>'
439
+ return dids
440
+
441
+ def _compose_did_data(self):
442
+ """Gather and merge DID data from project simulink models and DID configuration file.
443
+
444
+ Returns:
445
+ did_dict (dict): Dict containing project DID data.
446
+ """
447
+ get_did_error_messages, project_dids = get_dids_in_prj(self._unit_cfgs)
448
+ if get_did_error_messages:
449
+ self.critical('\n'.join(get_did_error_messages))
450
+ return {}
451
+
452
+ did_filename = self._build_cfg.get_did_cfg_file_name()
453
+ config_directory = self._build_cfg.get_prj_cfg_dir()
454
+ did_config_file = os.path.join(config_directory, did_filename)
455
+ dids = self._load_did_config_files(did_config_file)
456
+
457
+ did_dict = self.verify_dids(project_dids, dids)
458
+ for data in did_dict.values():
459
+ data['function'] = self.compose_did_function(data)
460
+
461
+ return did_dict
462
+
463
+ @staticmethod
464
+ def compose_did_function(did_data):
465
+ """Compose DID function calls.
466
+ Args:
467
+ did_data (dict): Dict describing a DID in the project.
468
+ Returns:
469
+ function (str): Function to generate for given DID.
470
+ """
471
+ did_id = did_data["id"]
472
+ data_type = did_data["data_type"]
473
+ type_to_function_map = {
474
+ '<missing>': f'DID_{did_id}_Missing({data_type} *Data)',
475
+ 'read_data': f'DID_{did_id}_Runnable_ReadData({data_type} *Data)',
476
+ 'read_data_max': f'DID_{did_id}_Runnable_MAX_ReadData({data_type} *Data)',
477
+ 'read_data_min': f'DID_{did_id}_Runnable_MIN_ReadData({data_type} *Data)',
478
+ 'condition_check': f'DID_{did_id}_Runnable_ConditionCheckRead({data_type} *ErrorCode)',
479
+ 'condition_check_max': f'DID_{did_id}_Runnable_MAX_ConditionCheckRead({data_type} *ErrorCode)',
480
+ 'condition_check_min': f'DID_{did_id}_Runnable_MIN_ConditionCheckRead({data_type} *ErrorCode)'
481
+ }
482
+ return type_to_function_map[did_data['function_type']]
483
+
484
+ def verify_dids(self, project_dids, dids):
485
+ """Verify the DIDs.
486
+
487
+ * Model DIDs must be defined in DID configuration file.
488
+ * ID numbers can only appear once per function type.
489
+
490
+ Args:
491
+ project_dids (dict): DIDs listed in project/simulink models.
492
+ dids (dict): DIDs listed in the DID configuration file.
493
+ Returns:
494
+ valid_dids (dict): Validated DIDs listed in both DID configuration file as well as project.
495
+ """
496
+ valid_dids = {}
497
+ did_id_usage = {}
498
+
499
+ if not project_dids:
500
+ for did in dids:
501
+ self.warning(f'Ignoring DID {did}, not defined in any model.')
502
+ return valid_dids
503
+
504
+ for name in project_dids:
505
+ if name not in dids:
506
+ self.warning(f'DID {name} not defined in DID defintion file.')
507
+ continue
508
+
509
+ did_id = dids[name]['id']
510
+ function_type = dids[name]['function_type']
511
+ if did_id in did_id_usage:
512
+ if function_type in did_id_usage[did_id]:
513
+ self.critical(
514
+ f'ID {did_id} is '
515
+ f'already used for DID {did_id_usage[did_id][function_type]} of '
516
+ f'function type {function_type}.'
517
+ )
518
+ continue
519
+ did_id_usage[did_id][function_type] = name
520
+ else:
521
+ did_id_usage[did_id] = {function_type: name}
522
+
523
+ valid_dids[name] = deep_dict_update(dids[name], project_dids[name])
524
+
525
+ return valid_dids
526
+
527
+ def get_header_file_content(self):
528
+ """Get content for the DID API header file.
529
+
530
+ Returns:
531
+ (list(str)): List of lines to write to DID API header file.
532
+ """
533
+ name = self._build_cfg.get_a2l_cfg()['name']
534
+ header_guard = f'{self.file_name.upper()}_H'
535
+ header = [
536
+ f'#ifndef {header_guard}\n',
537
+ f'#define {header_guard}\n',
538
+ '\n',
539
+ '#include "tl_basetypes.h"\n',
540
+ f'#include "Rte_{name}.h"\n',
541
+ '\n'
542
+ ]
543
+ footer = [f'\n#endif /* {header_guard} */\n']
544
+
545
+ if not self.did_dict:
546
+ return header + footer
547
+
548
+ body = [f'#include "{build_defs.PREDECL_DISP_ASIL_D_START}"\n']
549
+ for did_data in self.did_dict.values():
550
+ define = did_data["class"].split('/')[-1] # E.q. for ASIL D it is ASIL_D/CVC_DISP_ASIL_D
551
+ body.append(f'extern {define} {did_data["type"]} {did_data["name"]};\n')
552
+ body.append(f'#include "{build_defs.PREDECL_DISP_ASIL_D_END}"\n')
553
+
554
+ body.append(f'\n#include "{build_defs.PREDECL_CODE_ASIL_D_START}"\n')
555
+ for did_data in self.did_dict.values():
556
+ body.append(f'void {did_data["function"]};\n')
557
+ body.append(f'#include "{build_defs.PREDECL_CODE_ASIL_D_END}"\n')
558
+
559
+ return header + body + footer
560
+
561
+ def get_source_file_content(self):
562
+ """Get content for the DID API source file.
563
+
564
+ Returns:
565
+ (list(str)): List of lines to write to DID API source file.
566
+ """
567
+ header = [
568
+ f'#include "{self.file_name}.h"\n',
569
+ '\n'
570
+ ]
571
+
572
+ if not self.did_dict:
573
+ return header
574
+
575
+ body = [f'#include "{build_defs.CVC_CODE_ASIL_D_START}"\n']
576
+ for did, did_data in self.did_dict.items():
577
+ size = f'{did_data["nr_of_bytes"]}' if 'nr_of_bytes' in did_data else f'sizeof({did_data["data_type"]})'
578
+ if 'ConditionCheckRead' in did_data["function"]:
579
+ argument = 'ErrorCode'
580
+ else:
581
+ argument = 'Data'
582
+ body.extend([
583
+ f'void {did_data["function"]}\n',
584
+ '{\n',
585
+ f' memcpy({argument}, &{did}, {size});\n',
586
+ '}\n'
587
+ ])
588
+ body.append(f'#include "{build_defs.CVC_CODE_ASIL_D_END}"\n')
589
+
590
+ return header + body
591
+
592
+ def generate_did_files(self):
593
+ """Generate required DID API files.
594
+ Only use for some projects, which doesn't copy static code."""
595
+ file_contents = {
596
+ '.h': self.get_header_file_content(),
597
+ '.c': self.get_source_file_content()
598
+ }
599
+ src_dst_dir = self._build_cfg.get_src_code_dst_dir()
600
+ for extension, content in file_contents.items():
601
+ file_path = Path(src_dst_dir, self.file_name + extension)
602
+ with file_path.open(mode='w', encoding='utf-8') as file_handler:
603
+ file_handler.writelines(content)
604
+
605
+
606
+ class ZCDIDs(ProblemLogger):
607
+ """A class for handling of ZC DID definitions."""
608
+
609
+ def __init__(self, build_cfg, unit_cfgs):
610
+ """Init.
611
+
612
+ Args:
613
+ build_cfg (BuildProjConfig): Project configuration
614
+ unit_cfgs (UnitConfigs): Unit definitions
615
+ """
616
+ super().__init__()
617
+ self._build_cfg = build_cfg
618
+ self._unit_cfgs = unit_cfgs
619
+ self._valid_dids = None
620
+ prefix = self._build_cfg.get_scheduler_prefix()
621
+ self.operation_file_name = 'VcDIDAPI'
622
+ self.sender_receiver_file_name = f'{prefix}UpdatingDIDValues'
623
+ self.project_dids = self._get_project_dids()
624
+
625
+ @property
626
+ def valid_dids(self):
627
+ return self._valid_dids
628
+
629
+ @valid_dids.setter
630
+ def valid_dids(self, yaml_dids):
631
+ """Return a set of DIDs appearing in both the project and the project yaml file.
632
+
633
+ Args:
634
+ yaml_dids (dict): DIDs listed in the DID configuration yaml file.
635
+ Returns:
636
+ valid_dids (dict): Validated DIDs listed in both DID configuration yaml file as well as project.
637
+ """
638
+ self._valid_dids = {}
639
+
640
+ dids_not_in_yaml = set(self.project_dids.keys()) - set(yaml_dids.keys())
641
+ for did in dids_not_in_yaml:
642
+ self.critical(f'DID {did} not defined in project diagnostics yaml file.')
643
+
644
+ for did, did_data in yaml_dids.items():
645
+ if did_data.get('manual', False):
646
+ self._valid_dids[did] = {k: v for k, v in did_data.items() if k != "manual"}
647
+ continue
648
+ if did not in self.project_dids:
649
+ self.warning(f'Ignoring DID {did}, not defined in any model.')
650
+ continue
651
+ data_type = self.project_dids[did]['type']
652
+ if not data_type.startswith('UInt'):
653
+ self.warning(f'Ignoring DID {did} of type {data_type}, only unsigned integers are supported.')
654
+ continue
655
+ self._valid_dids[did] = did_data
656
+
657
+ def _get_project_dids(self):
658
+ """Return a dict with DIDs defined in the project.
659
+ Throws a critical error if something goes wrong.
660
+
661
+ Returns:
662
+ project_dids (dict): a dict with all dids in the project.
663
+ """
664
+ get_did_error_messages, project_dids = get_dids_in_prj(self._unit_cfgs)
665
+ if get_did_error_messages:
666
+ self.critical('\n'.join(get_did_error_messages))
667
+ return {}
668
+ return project_dids
669
+
670
+ def _get_sender_receiver_header_file_content(self):
671
+ """Get content for the S/R DID API header file.
672
+
673
+ The function in this file is a runnable generated by yaml2arxml.
674
+
675
+ Returns:
676
+ (bool): True if any S/R DIDs are defined.
677
+ (list(str)): List of lines to write to the S/R DID API header file.
678
+ """
679
+ name = self._build_cfg.get_composition_config("softwareComponentName")
680
+ header_guard = f'{self.sender_receiver_file_name.upper()}_H'
681
+ header = [
682
+ f'#ifndef {header_guard}\n',
683
+ f'#define {header_guard}\n',
684
+ '\n',
685
+ '#include "tl_basetypes.h"\n',
686
+ f'#include "Rte_{name}.h"\n',
687
+ '\n'
688
+ ]
689
+ footer = [f'\n#endif /* {header_guard} */\n']
690
+
691
+ if not self.valid_dids:
692
+ return False, header + footer
693
+
694
+ variable_declarations = []
695
+ function_declarations = []
696
+ sender_receiver_dids_exist = False
697
+ for did, did_data in self.valid_dids.items():
698
+ project_did_data = self.project_dids[did]
699
+ if did_data.get('PortType', 'dummy') not in ['BOTH', 'SENDER-RECEIVER']:
700
+ continue # C/R DIDs are handled in _get_operation... functions
701
+ if not project_did_data['type'].startswith('UInt'):
702
+ self.warning(
703
+ f'Ignoring DID {did} of type {project_did_data["type"]}, only unsigned integers are supported.'
704
+ )
705
+ continue
706
+ sender_receiver_dids_exist = True
707
+ define = self.project_dids[did]["class"].split('/')[-1] # E.q. for ASIL D it is ASIL_D/CVC_DISP_ASIL_D
708
+ variable_declarations.append(
709
+ f'extern {define} {self.project_dids[did]["type"]} {self.project_dids[did]["name"]};\n'
710
+ )
711
+ function_declarations.append(
712
+ f'extern UInt8 Rte_Write_DataServices_DID_{did}_data({project_did_data["type"]} {did});\n'
713
+ )
714
+
715
+ function_declarations.append(f'\nvoid Run_{self.sender_receiver_file_name}(void);\n')
716
+
717
+ body = [
718
+ f'#include "{build_defs.PREDECL_DISP_ASIL_D_START}"\n',
719
+ *variable_declarations,
720
+ f'#include "{build_defs.PREDECL_DISP_ASIL_D_END}"\n',
721
+ f'\n#include "{build_defs.PREDECL_CODE_ASIL_D_START}"\n',
722
+ *function_declarations,
723
+ f'#include "{build_defs.PREDECL_CODE_ASIL_D_END}"\n',
724
+ ]
725
+
726
+ return sender_receiver_dids_exist, header + body + footer
727
+
728
+ def _get_sender_receiver_source_file_content(self):
729
+ """Get content for the S/R DID API source file.
730
+
731
+ The function in this file is a runnable generated by yaml2arxml.
732
+
733
+ Returns:
734
+ (bool): True if any S/R DIDs are defined.
735
+ (list(str)): List of lines to write to the S/R DID API source file.
736
+ """
737
+ header = [
738
+ f'#include "{self.sender_receiver_file_name}.h"\n',
739
+ '\n'
740
+ ]
741
+
742
+ if not self.valid_dids:
743
+ return False, header
744
+
745
+ sender_receiver_dids_exist = False
746
+ body = [
747
+ f'#include "{build_defs.CVC_CODE_ASIL_D_START}"\n',
748
+ f'void Run_{self.sender_receiver_file_name}(void)\n',
749
+ '{\n'
750
+ ]
751
+ for did, did_data in self.valid_dids.items():
752
+ project_did_data = self.project_dids[did]
753
+ if did_data.get('PortType', 'dummy') not in ['BOTH', 'SENDER-RECEIVER']:
754
+ continue # C/R DIDs are handled in _get_operation... functions
755
+ if not project_did_data['type'].startswith('UInt'):
756
+ self.warning(
757
+ f'Ignoring DID {did} of type {project_did_data["type"]}, only unsigned integers are supported.'
758
+ )
759
+ continue
760
+ sender_receiver_dids_exist = True
761
+ body.append(f' Rte_Write_DataServices_DID_{did}_data({did});\n')
762
+ body.extend([
763
+ '}\n',
764
+ f'#include "{build_defs.CVC_CODE_ASIL_D_END}"\n'
765
+ ])
766
+
767
+ return sender_receiver_dids_exist, header + body
768
+
769
+ def _get_operation_data(self, operation, did_data):
770
+ """Get operation function data of supported operations.
771
+
772
+ Args:
773
+ operation (str): Operation to get data for.
774
+ did_data (dict): DID data.
775
+ Returns:
776
+ (dict): Operation function data.
777
+ """
778
+ array_size = byte_size(did_data['type'])
779
+ if array_size > 1:
780
+ read_data_declaration = f'UInt8 Run_{did_data["name"]}_ReadData(UInt8 Data[{array_size}])'
781
+ read_data_definition = (
782
+ '{\n'
783
+ f' for (UInt8 i = 0U; i < {array_size}; i++) {{\n'
784
+ f' Data[{array_size} - 1 - i] = ({did_data["name"]} >> (8 * i)) & 0xFF;\n'
785
+ ' }\n'
786
+ ' return 0U;\n'
787
+ '}\n'
788
+ )
789
+ else:
790
+ read_data_declaration = f'UInt8 Run_{did_data["name"]}_ReadData(UInt8 *Data)'
791
+ read_data_definition = (
792
+ '{\n'
793
+ f' *Data = {did_data["name"]};\n'
794
+ ' return 0U;\n'
795
+ '}\n'
796
+ )
797
+ operation_data = {
798
+ 'ReadData': {
799
+ 'declaration': read_data_declaration,
800
+ 'body': read_data_definition,
801
+ }
802
+ }
803
+ if operation not in operation_data:
804
+ return None
805
+ return operation_data[operation]
806
+
807
+ def _get_operation_header_file_content(self):
808
+ """Get content for the DID API header file.
809
+
810
+ Returns:
811
+ (list(str)): List of lines to write to the DID API header file.
812
+ """
813
+ name = self._build_cfg.get_composition_config("softwareComponentName")
814
+ header_guard = f'{self.operation_file_name.upper()}_H'
815
+ header = [
816
+ f'#ifndef {header_guard}\n',
817
+ f'#define {header_guard}\n',
818
+ '\n',
819
+ '#include "tl_basetypes.h"\n',
820
+ f'#include "Rte_{name}.h"\n',
821
+ '\n'
822
+ ]
823
+ footer = [f'\n#endif /* {header_guard} */\n']
824
+
825
+ if not self.valid_dids:
826
+ return header + footer
827
+
828
+ variable_declarations = []
829
+ function_declarations = []
830
+ for did, did_data in self.valid_dids.items():
831
+ if did_data.get('PortType', 'dummy') == 'SENDER-RECEIVER':
832
+ continue # S/R DIDs are handled in _get_sender_receiver... functions
833
+ define = self.project_dids[did]["class"].split('/')[-1] # E.q. for ASIL D it is ASIL_D/CVC_DISP_ASIL_D
834
+ variable_declarations.append(
835
+ f'extern {define} {self.project_dids[did]["type"]} {self.project_dids[did]["name"]};\n'
836
+ )
837
+ for operation in did_data["operations"]:
838
+ operation_data = self._get_operation_data(operation, self.project_dids[did])
839
+ if operation_data is None:
840
+ self.warning(
841
+ f'Will not generate code for unsupported operation {operation}. Add manually for DID {did}.'
842
+ )
843
+ continue
844
+ function_declarations.append(operation_data['declaration'] + ';\n')
845
+
846
+ body = [
847
+ f'#include "{build_defs.PREDECL_DISP_ASIL_D_START}"\n',
848
+ *variable_declarations,
849
+ f'#include "{build_defs.PREDECL_DISP_ASIL_D_END}"\n',
850
+ f'\n#include "{build_defs.PREDECL_CODE_ASIL_D_START}"\n',
851
+ *function_declarations,
852
+ f'#include "{build_defs.PREDECL_CODE_ASIL_D_END}"\n',
853
+ ]
854
+
855
+ return header + body + footer
856
+
857
+ def _get_operation_source_file_content(self):
858
+ """Get content for the DID API source file.
859
+
860
+ Returns:
861
+ (list(str)): List of lines to write to the DID API source file.
862
+ """
863
+ header = [
864
+ f'#include "{self.operation_file_name}.h"\n',
865
+ '\n'
866
+ ]
867
+
868
+ if not self.valid_dids:
869
+ return header
870
+
871
+ body = [f'#include "{build_defs.CVC_CODE_ASIL_D_START}"\n']
872
+ for did, did_data in self.valid_dids.items():
873
+ if did_data.get('PortType', 'dummy') == 'SENDER-RECEIVER':
874
+ continue # S/R DIDs are handled in _get_sender_receiver... functions
875
+ for operation in did_data["operations"]:
876
+ operation_data = self._get_operation_data(operation, self.project_dids[did])
877
+ if operation_data is None:
878
+ continue # Warning already given in header generation
879
+ body.append(operation_data['declaration'] + '\n' + operation_data['body'])
880
+ body.append(f'#include "{build_defs.CVC_CODE_ASIL_D_END}"\n')
881
+
882
+ return header + body
883
+
884
+ def generate_did_files(self):
885
+ """Generate required DID API files.
886
+ Only use for some projects, which doesn't copy static code."""
887
+ if self.valid_dids is None:
888
+ includeDiagnostics = self._build_cfg.get_composition_config("includeDiagnostics")
889
+ if includeDiagnostics in ["manual", "manual_dids"]:
890
+ self.warning(f'includeDiagnostics is set to {includeDiagnostics}, not generating DID files.')
891
+ return
892
+ self.critical('Valid DIDs not set. Cannot generate DID files.')
893
+ return
894
+
895
+ src_dst_dir = self._build_cfg.get_src_code_dst_dir()
896
+
897
+ # CLIENT-SERVER DIDs
898
+ file_contents = {
899
+ '.h': self._get_operation_header_file_content(),
900
+ '.c': self._get_operation_source_file_content()
901
+ }
902
+ for extension, content in file_contents.items():
903
+ file_path = Path(src_dst_dir, self.operation_file_name + extension)
904
+ with file_path.open(mode='w', encoding='utf-8') as file_handler:
905
+ file_handler.writelines(content)
906
+
907
+ # SENDER-RECEIVER DIDs
908
+ sender_receiver_dids_exist_header, header_contents = self._get_sender_receiver_header_file_content()
909
+ sender_receiver_dids_exist_source, source_contents = self._get_sender_receiver_source_file_content()
910
+ if sender_receiver_dids_exist_header and sender_receiver_dids_exist_source:
911
+ file_path = Path(src_dst_dir, self.sender_receiver_file_name + '.h')
912
+ with file_path.open(mode='w', encoding='utf-8') as file_handler:
913
+ file_handler.writelines(header_contents)
914
+ file_path = Path(src_dst_dir, self.sender_receiver_file_name + '.c')
915
+ with file_path.open(mode='w', encoding='utf-8') as file_handler:
916
+ file_handler.writelines(source_contents)