powertrain-build 1.13.1__py3-none-any.whl → 1.13.3.dev3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- powertrain_build/__init__.py +40 -40
- powertrain_build/__main__.py +6 -6
- powertrain_build/a2l.py +582 -582
- powertrain_build/a2l_merge.py +650 -650
- powertrain_build/a2l_templates.py +717 -717
- powertrain_build/build.py +985 -985
- powertrain_build/build_defs.py +309 -309
- powertrain_build/build_proj_config.py +690 -690
- powertrain_build/check_interface.py +575 -575
- powertrain_build/cli.py +141 -141
- powertrain_build/config.py +542 -542
- powertrain_build/core.py +395 -395
- powertrain_build/core_dummy.py +343 -343
- powertrain_build/create_conversion_table.py +73 -73
- powertrain_build/dids.py +916 -916
- powertrain_build/dummy.py +157 -157
- powertrain_build/dummy_spm.py +252 -252
- powertrain_build/environmentcheck.py +52 -52
- powertrain_build/ext_dbg.py +255 -255
- powertrain_build/ext_var.py +327 -327
- powertrain_build/feature_configs.py +301 -301
- powertrain_build/gen_allsysteminfo.py +227 -227
- powertrain_build/gen_label_split.py +449 -449
- powertrain_build/handcode_replacer.py +124 -124
- powertrain_build/html_report.py +133 -133
- powertrain_build/interface/__init__.py +4 -4
- powertrain_build/interface/application.py +511 -511
- powertrain_build/interface/base.py +500 -500
- powertrain_build/interface/csp_api.py +490 -490
- powertrain_build/interface/device_proxy.py +677 -677
- powertrain_build/interface/ems.py +67 -67
- powertrain_build/interface/export_global_vars.py +121 -121
- powertrain_build/interface/generate_adapters.py +132 -132
- powertrain_build/interface/generate_hi_interface.py +87 -87
- powertrain_build/interface/generate_service.py +69 -69
- powertrain_build/interface/generate_wrappers.py +147 -147
- powertrain_build/interface/generation_utils.py +142 -142
- powertrain_build/interface/hal.py +194 -194
- powertrain_build/interface/model_yaml_verification.py +348 -348
- powertrain_build/interface/service.py +296 -296
- powertrain_build/interface/simulink.py +249 -249
- powertrain_build/interface/update_call_sources.py +180 -180
- powertrain_build/interface/update_model_yaml.py +186 -186
- powertrain_build/interface/zone_controller.py +362 -362
- powertrain_build/lib/__init__.py +4 -4
- powertrain_build/lib/helper_functions.py +127 -127
- powertrain_build/lib/logger.py +55 -55
- powertrain_build/matlab_scripts/CodeGen/BuildAutomationPyBuild.m +78 -78
- powertrain_build/matlab_scripts/CodeGen/Generate_A2L.m +154 -154
- powertrain_build/matlab_scripts/CodeGen/generateTLUnit.m +239 -239
- powertrain_build/matlab_scripts/CodeGen/getAsilClassification.m +28 -28
- powertrain_build/matlab_scripts/CodeGen/modelConfiguredForTL.m +28 -28
- powertrain_build/matlab_scripts/CodeGen/moveDefOutports.m +88 -88
- powertrain_build/matlab_scripts/CodeGen/parseCalMeasData.m +410 -410
- powertrain_build/matlab_scripts/CodeGen/parseCoreIdentifiers.m +139 -139
- powertrain_build/matlab_scripts/CodeGen/parseDIDs.m +141 -141
- powertrain_build/matlab_scripts/CodeGen/parseInPorts.m +106 -106
- powertrain_build/matlab_scripts/CodeGen/parseIncludeConfigs.m +25 -25
- powertrain_build/matlab_scripts/CodeGen/parseModelInfo.m +38 -38
- powertrain_build/matlab_scripts/CodeGen/parseNVM.m +81 -81
- powertrain_build/matlab_scripts/CodeGen/parseOutPorts.m +120 -120
- powertrain_build/matlab_scripts/CodeGen/parsePreProcBlks.m +23 -23
- powertrain_build/matlab_scripts/CodeGen/struct2JSON.m +128 -128
- powertrain_build/matlab_scripts/CodeGen/updateCodeSwConfig.m +31 -31
- powertrain_build/matlab_scripts/Init_PyBuild.m +91 -91
- powertrain_build/matlab_scripts/__init__.py +2 -2
- powertrain_build/matlab_scripts/helperFunctions/Get_Full_Name.m +46 -46
- powertrain_build/matlab_scripts/helperFunctions/Get_SrcLines.m +12 -12
- powertrain_build/matlab_scripts/helperFunctions/Init_Models.m +78 -78
- powertrain_build/matlab_scripts/helperFunctions/Init_Projects.m +67 -67
- powertrain_build/matlab_scripts/helperFunctions/Read_Units.m +34 -34
- powertrain_build/matlab_scripts/helperFunctions/SetProjectTimeSamples.m +26 -26
- powertrain_build/matlab_scripts/helperFunctions/Strip_Suffix.m +16 -16
- powertrain_build/matlab_scripts/helperFunctions/followLink.m +118 -118
- powertrain_build/matlab_scripts/helperFunctions/getCodeSwitches.m +50 -50
- powertrain_build/matlab_scripts/helperFunctions/getConsumerBlocks.m +30 -30
- powertrain_build/matlab_scripts/helperFunctions/getDefBlock.m +39 -39
- powertrain_build/matlab_scripts/helperFunctions/getDefOutport.m +58 -58
- powertrain_build/matlab_scripts/helperFunctions/getDstBlocks.m +19 -19
- powertrain_build/matlab_scripts/helperFunctions/getDstLines.m +13 -13
- powertrain_build/matlab_scripts/helperFunctions/getInterfaceSignals.m +37 -37
- powertrain_build/matlab_scripts/helperFunctions/getName.m +37 -37
- powertrain_build/matlab_scripts/helperFunctions/getPath.m +6 -6
- powertrain_build/matlab_scripts/helperFunctions/getProperValue.m +21 -21
- powertrain_build/matlab_scripts/helperFunctions/getSrcBlocks.m +19 -19
- powertrain_build/matlab_scripts/helperFunctions/getSrcLines.m +13 -13
- powertrain_build/matlab_scripts/helperFunctions/loadLibraries.m +10 -10
- powertrain_build/matlab_scripts/helperFunctions/loadjson.m +6 -6
- powertrain_build/matlab_scripts/helperFunctions/modifyEnumStructField.m +21 -21
- powertrain_build/matlab_scripts/helperFunctions/removeConfigDuplicates.m +31 -31
- powertrain_build/matlab_scripts/helperFunctions/sortSystemByClass.m +26 -26
- powertrain_build/matlab_scripts/helperFunctions/tl_getfast.m +89 -89
- powertrain_build/matlab_scripts/helperFunctions/topLevelSystem.m +20 -20
- powertrain_build/matlab_scripts/helperFunctions/updateModels.m +131 -131
- powertrain_build/memory_section.py +224 -224
- powertrain_build/nvm_def.py +729 -729
- powertrain_build/problem_logger.py +86 -86
- powertrain_build/pt_matlab.py +430 -430
- powertrain_build/pt_win32.py +144 -144
- powertrain_build/replace_compu_tab_ref.py +105 -105
- powertrain_build/rte_dummy.py +254 -254
- powertrain_build/sched_funcs.py +209 -207
- powertrain_build/signal.py +7 -7
- powertrain_build/signal_if_html_rep.py +221 -221
- powertrain_build/signal_if_html_rep_all.py +302 -302
- powertrain_build/signal_incons_html_rep.py +180 -180
- powertrain_build/signal_incons_html_rep_all.py +366 -366
- powertrain_build/signal_incons_html_rep_base.py +168 -168
- powertrain_build/signal_inconsistency_check.py +641 -641
- powertrain_build/signal_interfaces.py +864 -864
- powertrain_build/templates/Index_SigCheck_All.html +22 -22
- powertrain_build/templates/Index_SigIf_All.html +19 -19
- powertrain_build/types.py +218 -218
- powertrain_build/unit_configs.py +419 -419
- powertrain_build/user_defined_types.py +660 -660
- powertrain_build/versioncheck.py +66 -66
- powertrain_build/wrapper.py +512 -512
- powertrain_build/xlrd_csv.py +87 -87
- powertrain_build/zone_controller/__init__.py +4 -4
- powertrain_build/zone_controller/calibration.py +176 -176
- powertrain_build/zone_controller/composition_yaml.py +880 -878
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/METADATA +100 -100
- powertrain_build-1.13.3.dev3.dist-info/RECORD +130 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/WHEEL +1 -1
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/LICENSE +202 -202
- powertrain_build-1.13.3.dev3.dist-info/pbr.json +1 -0
- powertrain_build-1.13.1.dist-info/RECORD +0 -130
- powertrain_build-1.13.1.dist-info/pbr.json +0 -1
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/entry_points.txt +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/AUTHORS +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/NOTICE +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/top_level.txt +0 -0
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)
|