powertrain-build 1.13.1__py3-none-any.whl → 1.13.3.dev3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- powertrain_build/__init__.py +40 -40
- powertrain_build/__main__.py +6 -6
- powertrain_build/a2l.py +582 -582
- powertrain_build/a2l_merge.py +650 -650
- powertrain_build/a2l_templates.py +717 -717
- powertrain_build/build.py +985 -985
- powertrain_build/build_defs.py +309 -309
- powertrain_build/build_proj_config.py +690 -690
- powertrain_build/check_interface.py +575 -575
- powertrain_build/cli.py +141 -141
- powertrain_build/config.py +542 -542
- powertrain_build/core.py +395 -395
- powertrain_build/core_dummy.py +343 -343
- powertrain_build/create_conversion_table.py +73 -73
- powertrain_build/dids.py +916 -916
- powertrain_build/dummy.py +157 -157
- powertrain_build/dummy_spm.py +252 -252
- powertrain_build/environmentcheck.py +52 -52
- powertrain_build/ext_dbg.py +255 -255
- powertrain_build/ext_var.py +327 -327
- powertrain_build/feature_configs.py +301 -301
- powertrain_build/gen_allsysteminfo.py +227 -227
- powertrain_build/gen_label_split.py +449 -449
- powertrain_build/handcode_replacer.py +124 -124
- powertrain_build/html_report.py +133 -133
- powertrain_build/interface/__init__.py +4 -4
- powertrain_build/interface/application.py +511 -511
- powertrain_build/interface/base.py +500 -500
- powertrain_build/interface/csp_api.py +490 -490
- powertrain_build/interface/device_proxy.py +677 -677
- powertrain_build/interface/ems.py +67 -67
- powertrain_build/interface/export_global_vars.py +121 -121
- powertrain_build/interface/generate_adapters.py +132 -132
- powertrain_build/interface/generate_hi_interface.py +87 -87
- powertrain_build/interface/generate_service.py +69 -69
- powertrain_build/interface/generate_wrappers.py +147 -147
- powertrain_build/interface/generation_utils.py +142 -142
- powertrain_build/interface/hal.py +194 -194
- powertrain_build/interface/model_yaml_verification.py +348 -348
- powertrain_build/interface/service.py +296 -296
- powertrain_build/interface/simulink.py +249 -249
- powertrain_build/interface/update_call_sources.py +180 -180
- powertrain_build/interface/update_model_yaml.py +186 -186
- powertrain_build/interface/zone_controller.py +362 -362
- powertrain_build/lib/__init__.py +4 -4
- powertrain_build/lib/helper_functions.py +127 -127
- powertrain_build/lib/logger.py +55 -55
- powertrain_build/matlab_scripts/CodeGen/BuildAutomationPyBuild.m +78 -78
- powertrain_build/matlab_scripts/CodeGen/Generate_A2L.m +154 -154
- powertrain_build/matlab_scripts/CodeGen/generateTLUnit.m +239 -239
- powertrain_build/matlab_scripts/CodeGen/getAsilClassification.m +28 -28
- powertrain_build/matlab_scripts/CodeGen/modelConfiguredForTL.m +28 -28
- powertrain_build/matlab_scripts/CodeGen/moveDefOutports.m +88 -88
- powertrain_build/matlab_scripts/CodeGen/parseCalMeasData.m +410 -410
- powertrain_build/matlab_scripts/CodeGen/parseCoreIdentifiers.m +139 -139
- powertrain_build/matlab_scripts/CodeGen/parseDIDs.m +141 -141
- powertrain_build/matlab_scripts/CodeGen/parseInPorts.m +106 -106
- powertrain_build/matlab_scripts/CodeGen/parseIncludeConfigs.m +25 -25
- powertrain_build/matlab_scripts/CodeGen/parseModelInfo.m +38 -38
- powertrain_build/matlab_scripts/CodeGen/parseNVM.m +81 -81
- powertrain_build/matlab_scripts/CodeGen/parseOutPorts.m +120 -120
- powertrain_build/matlab_scripts/CodeGen/parsePreProcBlks.m +23 -23
- powertrain_build/matlab_scripts/CodeGen/struct2JSON.m +128 -128
- powertrain_build/matlab_scripts/CodeGen/updateCodeSwConfig.m +31 -31
- powertrain_build/matlab_scripts/Init_PyBuild.m +91 -91
- powertrain_build/matlab_scripts/__init__.py +2 -2
- powertrain_build/matlab_scripts/helperFunctions/Get_Full_Name.m +46 -46
- powertrain_build/matlab_scripts/helperFunctions/Get_SrcLines.m +12 -12
- powertrain_build/matlab_scripts/helperFunctions/Init_Models.m +78 -78
- powertrain_build/matlab_scripts/helperFunctions/Init_Projects.m +67 -67
- powertrain_build/matlab_scripts/helperFunctions/Read_Units.m +34 -34
- powertrain_build/matlab_scripts/helperFunctions/SetProjectTimeSamples.m +26 -26
- powertrain_build/matlab_scripts/helperFunctions/Strip_Suffix.m +16 -16
- powertrain_build/matlab_scripts/helperFunctions/followLink.m +118 -118
- powertrain_build/matlab_scripts/helperFunctions/getCodeSwitches.m +50 -50
- powertrain_build/matlab_scripts/helperFunctions/getConsumerBlocks.m +30 -30
- powertrain_build/matlab_scripts/helperFunctions/getDefBlock.m +39 -39
- powertrain_build/matlab_scripts/helperFunctions/getDefOutport.m +58 -58
- powertrain_build/matlab_scripts/helperFunctions/getDstBlocks.m +19 -19
- powertrain_build/matlab_scripts/helperFunctions/getDstLines.m +13 -13
- powertrain_build/matlab_scripts/helperFunctions/getInterfaceSignals.m +37 -37
- powertrain_build/matlab_scripts/helperFunctions/getName.m +37 -37
- powertrain_build/matlab_scripts/helperFunctions/getPath.m +6 -6
- powertrain_build/matlab_scripts/helperFunctions/getProperValue.m +21 -21
- powertrain_build/matlab_scripts/helperFunctions/getSrcBlocks.m +19 -19
- powertrain_build/matlab_scripts/helperFunctions/getSrcLines.m +13 -13
- powertrain_build/matlab_scripts/helperFunctions/loadLibraries.m +10 -10
- powertrain_build/matlab_scripts/helperFunctions/loadjson.m +6 -6
- powertrain_build/matlab_scripts/helperFunctions/modifyEnumStructField.m +21 -21
- powertrain_build/matlab_scripts/helperFunctions/removeConfigDuplicates.m +31 -31
- powertrain_build/matlab_scripts/helperFunctions/sortSystemByClass.m +26 -26
- powertrain_build/matlab_scripts/helperFunctions/tl_getfast.m +89 -89
- powertrain_build/matlab_scripts/helperFunctions/topLevelSystem.m +20 -20
- powertrain_build/matlab_scripts/helperFunctions/updateModels.m +131 -131
- powertrain_build/memory_section.py +224 -224
- powertrain_build/nvm_def.py +729 -729
- powertrain_build/problem_logger.py +86 -86
- powertrain_build/pt_matlab.py +430 -430
- powertrain_build/pt_win32.py +144 -144
- powertrain_build/replace_compu_tab_ref.py +105 -105
- powertrain_build/rte_dummy.py +254 -254
- powertrain_build/sched_funcs.py +209 -207
- powertrain_build/signal.py +7 -7
- powertrain_build/signal_if_html_rep.py +221 -221
- powertrain_build/signal_if_html_rep_all.py +302 -302
- powertrain_build/signal_incons_html_rep.py +180 -180
- powertrain_build/signal_incons_html_rep_all.py +366 -366
- powertrain_build/signal_incons_html_rep_base.py +168 -168
- powertrain_build/signal_inconsistency_check.py +641 -641
- powertrain_build/signal_interfaces.py +864 -864
- powertrain_build/templates/Index_SigCheck_All.html +22 -22
- powertrain_build/templates/Index_SigIf_All.html +19 -19
- powertrain_build/types.py +218 -218
- powertrain_build/unit_configs.py +419 -419
- powertrain_build/user_defined_types.py +660 -660
- powertrain_build/versioncheck.py +66 -66
- powertrain_build/wrapper.py +512 -512
- powertrain_build/xlrd_csv.py +87 -87
- powertrain_build/zone_controller/__init__.py +4 -4
- powertrain_build/zone_controller/calibration.py +176 -176
- powertrain_build/zone_controller/composition_yaml.py +880 -878
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/METADATA +100 -100
- powertrain_build-1.13.3.dev3.dist-info/RECORD +130 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/WHEEL +1 -1
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/LICENSE +202 -202
- powertrain_build-1.13.3.dev3.dist-info/pbr.json +1 -0
- powertrain_build-1.13.1.dist-info/RECORD +0 -130
- powertrain_build-1.13.1.dist-info/pbr.json +0 -1
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/entry_points.txt +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/AUTHORS +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/licenses/NOTICE +0 -0
- {powertrain_build-1.13.1.dist-info → powertrain_build-1.13.3.dev3.dist-info}/top_level.txt +0 -0
|
@@ -1,490 +1,490 @@
|
|
|
1
|
-
# Copyright 2024 Volvo Car Corporation
|
|
2
|
-
# Licensed under Apache 2.0.
|
|
3
|
-
|
|
4
|
-
"""Module for CSP API abstraction."""
|
|
5
|
-
|
|
6
|
-
import enum
|
|
7
|
-
from ruamel.yaml import YAML
|
|
8
|
-
from abc import abstractmethod
|
|
9
|
-
from powertrain_build.interface.base import BaseApplication, Signal
|
|
10
|
-
from powertrain_build.lib import logger
|
|
11
|
-
|
|
12
|
-
LOGGER = logger.create_logger("service")
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class MissingApi(Exception):
|
|
16
|
-
"""Exception to raise when api is missing"""
|
|
17
|
-
def __init__(self, api, map_file):
|
|
18
|
-
self.message = f"Api {api} missing from {map_file}"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class BadYamlFormat(Exception):
|
|
22
|
-
"""Exception to raise when in/out signal is not defined."""
|
|
23
|
-
def __init__(self, api, signal_name):
|
|
24
|
-
self.message = f"Signal {signal_name} for {api} should be set as insignal or outsignal."
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class CspApi(BaseApplication):
|
|
28
|
-
"""Abstraction for HAL and SFW"""
|
|
29
|
-
position = enum.Enum(
|
|
30
|
-
'Position',
|
|
31
|
-
names=[
|
|
32
|
-
"property_name",
|
|
33
|
-
"property_type",
|
|
34
|
-
"variable_type",
|
|
35
|
-
"offset",
|
|
36
|
-
"factor",
|
|
37
|
-
"default",
|
|
38
|
-
"length",
|
|
39
|
-
"min",
|
|
40
|
-
"max",
|
|
41
|
-
"enum",
|
|
42
|
-
"init",
|
|
43
|
-
"description",
|
|
44
|
-
"unit",
|
|
45
|
-
"endpoint",
|
|
46
|
-
"api",
|
|
47
|
-
"variant",
|
|
48
|
-
"strategy",
|
|
49
|
-
"debug",
|
|
50
|
-
"dependability"
|
|
51
|
-
]
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
def __init__(self, base_application, read_strategy='Always'):
|
|
55
|
-
"""Create the interface object
|
|
56
|
-
|
|
57
|
-
Args:
|
|
58
|
-
base_application (BaseApplication): Primary object of an interface
|
|
59
|
-
Usually a raster, but can be an application or a model too.
|
|
60
|
-
"""
|
|
61
|
-
self.name = ""
|
|
62
|
-
self.translations = {}
|
|
63
|
-
self.api_side = False
|
|
64
|
-
# we only care when generating models for csp.
|
|
65
|
-
self.filter = None
|
|
66
|
-
self.signal_names = {
|
|
67
|
-
"api": {"insignals": set(), "outsignals": set()},
|
|
68
|
-
"app": {"insignals": set(), "outsignals": set()},
|
|
69
|
-
}
|
|
70
|
-
self.base_application = base_application
|
|
71
|
-
self.map_file = self.get_map_file()
|
|
72
|
-
self.api_map = self.get_map()
|
|
73
|
-
self.translations_files = []
|
|
74
|
-
self.signal_primitives_list = []
|
|
75
|
-
self.default_read_strategy = read_strategy
|
|
76
|
-
|
|
77
|
-
def get_signal_properties(self, signal):
|
|
78
|
-
"""Get signal properties for signal
|
|
79
|
-
|
|
80
|
-
Calls self.base_application to get signal properties
|
|
81
|
-
|
|
82
|
-
Args:
|
|
83
|
-
signal (Signal): Signal to get properties for
|
|
84
|
-
"""
|
|
85
|
-
self.base_application.get_signal_properties(signal)
|
|
86
|
-
|
|
87
|
-
def _get_signals(self):
|
|
88
|
-
"""Read signals"""
|
|
89
|
-
self.parse_definition(self.translations_files)
|
|
90
|
-
|
|
91
|
-
@property
|
|
92
|
-
def insignals(self):
|
|
93
|
-
return self.get_signals(self.hal_name, 'insignals')
|
|
94
|
-
|
|
95
|
-
@property
|
|
96
|
-
def outsignals(self):
|
|
97
|
-
return self.get_signals(self.hal_name, 'outsignals')
|
|
98
|
-
|
|
99
|
-
def get_signals(self, api, signal_type='insignals'):
|
|
100
|
-
"""Get signals to and from an api abstraction
|
|
101
|
-
|
|
102
|
-
self.api_side configures if we look at the api side.
|
|
103
|
-
If it is set to False, we look at the application side.
|
|
104
|
-
|
|
105
|
-
Args:
|
|
106
|
-
api (str): Name of the api
|
|
107
|
-
signal_type (str): insignals or outsignals
|
|
108
|
-
Returns:
|
|
109
|
-
signals (list): Signals in the interface
|
|
110
|
-
"""
|
|
111
|
-
if self.api_side:
|
|
112
|
-
signal_names = self.signal_names['api'][signal_type]
|
|
113
|
-
else:
|
|
114
|
-
signal_names = self.signal_names['app'][signal_type]
|
|
115
|
-
|
|
116
|
-
signals = []
|
|
117
|
-
for signal_name, specs in self.translations.items():
|
|
118
|
-
for spec in specs:
|
|
119
|
-
signal = None
|
|
120
|
-
if api is not None:
|
|
121
|
-
if spec['api'] == api:
|
|
122
|
-
if self.api_side:
|
|
123
|
-
if spec['property'] in signal_names:
|
|
124
|
-
signal = Signal(spec['property'], self)
|
|
125
|
-
else:
|
|
126
|
-
if signal_name in signal_names:
|
|
127
|
-
signal = Signal(signal_name, self)
|
|
128
|
-
else:
|
|
129
|
-
if self.api_side:
|
|
130
|
-
if spec['property'] in signal_names:
|
|
131
|
-
signal = Signal(spec['property'], self)
|
|
132
|
-
else:
|
|
133
|
-
if signal_name in signal_names:
|
|
134
|
-
signal = Signal(signal_name, self)
|
|
135
|
-
if signal is not None:
|
|
136
|
-
signals.append(signal)
|
|
137
|
-
return signals
|
|
138
|
-
|
|
139
|
-
def clear_signal_names(self):
|
|
140
|
-
"""Clear signal names
|
|
141
|
-
|
|
142
|
-
Clears defined signal names (but not signal properties).
|
|
143
|
-
"""
|
|
144
|
-
self.signal_names = {
|
|
145
|
-
"api": {"insignals": set(), "outsignals": set()},
|
|
146
|
-
"app": {"insignals": set(), "outsignals": set()},
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
def parse_definition(self, definition):
|
|
150
|
-
"""Parses all definition files
|
|
151
|
-
|
|
152
|
-
Args:
|
|
153
|
-
definition (list(Path)): Definition files
|
|
154
|
-
"""
|
|
155
|
-
for translation in definition:
|
|
156
|
-
raw = self.read_translation(translation)
|
|
157
|
-
self.extract_endpoint_definitions(raw)
|
|
158
|
-
|
|
159
|
-
@staticmethod
|
|
160
|
-
def get_api_name(api_name):
|
|
161
|
-
"""Return the api name
|
|
162
|
-
|
|
163
|
-
Args:
|
|
164
|
-
api_name (str): Name of the api
|
|
165
|
-
|
|
166
|
-
Returns:
|
|
167
|
-
(str): Name of the api
|
|
168
|
-
"""
|
|
169
|
-
return api_name
|
|
170
|
-
|
|
171
|
-
def verify_api(self, api_name):
|
|
172
|
-
"""Verify that the api is in the map
|
|
173
|
-
|
|
174
|
-
Args:
|
|
175
|
-
api_name (str): Name of the api
|
|
176
|
-
"""
|
|
177
|
-
if api_name not in self.api_map:
|
|
178
|
-
raise MissingApi(api_name, self.map_file)
|
|
179
|
-
|
|
180
|
-
def add_signals(self, signals, signal_type='insignals', properties=None):
|
|
181
|
-
"""Add signal names and properties to already set ones
|
|
182
|
-
|
|
183
|
-
Args:
|
|
184
|
-
signals (list(Signals)): Signals to use
|
|
185
|
-
signal_type (str): 'insignals' or 'outsignals'
|
|
186
|
-
properties (list(str)): signal definition properties, default = []
|
|
187
|
-
"""
|
|
188
|
-
opposite = {'insignals': 'outsignals', 'outsignals': 'insignals'}
|
|
189
|
-
api_type = opposite[signal_type]
|
|
190
|
-
properties = [] if properties is None else properties
|
|
191
|
-
for signal in signals:
|
|
192
|
-
LOGGER.debug("Adding signal: %s", signal)
|
|
193
|
-
temp_set = set()
|
|
194
|
-
for translation in self.translations.get(signal.name, []):
|
|
195
|
-
temp_list = list(translation)
|
|
196
|
-
api_name = translation[self.position.api.value]
|
|
197
|
-
variant_name = translation[self.position.variant.value]
|
|
198
|
-
endpoint = translation[self.position.endpoint.value]
|
|
199
|
-
api_signal = translation[self.position.property_name.value]
|
|
200
|
-
self.check_signal_property(api_name, variant_name, endpoint,
|
|
201
|
-
api_signal, signal_type)
|
|
202
|
-
self.signal_names['api'][api_type].add(api_signal)
|
|
203
|
-
for enum_property in properties:
|
|
204
|
-
LOGGER.debug("Modifying property: %s", enum_property)
|
|
205
|
-
value = signal.properties[enum_property["source"]]
|
|
206
|
-
if value == "-":
|
|
207
|
-
value = enum_property["default"]
|
|
208
|
-
temp_list[
|
|
209
|
-
self.position[enum_property["destination"]].value
|
|
210
|
-
] = value
|
|
211
|
-
temp_set.add(tuple(temp_list))
|
|
212
|
-
self.translations[signal.name] = temp_set
|
|
213
|
-
self.signal_names['app'][signal_type].add(signal.name)
|
|
214
|
-
self.check_endpoints()
|
|
215
|
-
LOGGER.debug('Registered signal names: %s', self.signal_names)
|
|
216
|
-
|
|
217
|
-
def check_signal_property(self, api, variant, endpoint, property_name, signal_type):
|
|
218
|
-
"""Check if we have only one signal written for the same property.
|
|
219
|
-
|
|
220
|
-
Args:
|
|
221
|
-
api (str): interface name
|
|
222
|
-
variant (str): variant value. "properties" or "methods" for service
|
|
223
|
-
"hals" for hal
|
|
224
|
-
endpoint (str): signal endpoint
|
|
225
|
-
property_name (str): signal property
|
|
226
|
-
signal_type (str): 'insignals' or 'outsignals'
|
|
227
|
-
"""
|
|
228
|
-
primitive_value = ""
|
|
229
|
-
for value in [api, variant, endpoint, property_name]:
|
|
230
|
-
if value:
|
|
231
|
-
if primitive_value == "":
|
|
232
|
-
primitive_value = value
|
|
233
|
-
else:
|
|
234
|
-
primitive_value = primitive_value + '.' + value
|
|
235
|
-
if primitive_value == "":
|
|
236
|
-
raise Exception("The primitive does not contain any value!")
|
|
237
|
-
directional_primitive = f"{primitive_value}.{signal_type}"
|
|
238
|
-
self.check_property(directional_primitive, signal_type)
|
|
239
|
-
|
|
240
|
-
def check_property(self, property_spec, signal_type):
|
|
241
|
-
"""Check if we have only one signal written for the same property.
|
|
242
|
-
|
|
243
|
-
Args:
|
|
244
|
-
property_spec (str): property specification
|
|
245
|
-
signal_type (str): 'insignals' or 'outsignals'
|
|
246
|
-
"""
|
|
247
|
-
if property_spec in self.signal_primitives_list:
|
|
248
|
-
error_msg = (f"You can't write {property_spec} as "
|
|
249
|
-
f"{signal_type} since this primitive has been used."
|
|
250
|
-
" Run model_yaml_verification to identify exact models.")
|
|
251
|
-
raise Exception(error_msg)
|
|
252
|
-
self.signal_primitives_list.append(property_spec)
|
|
253
|
-
|
|
254
|
-
@abstractmethod
|
|
255
|
-
def check_endpoints(self):
|
|
256
|
-
"""Should be implemented by subclasses."""
|
|
257
|
-
|
|
258
|
-
@staticmethod
|
|
259
|
-
def read_translation(translation_file):
|
|
260
|
-
"""Read specification of the format:
|
|
261
|
-
|
|
262
|
-
service:
|
|
263
|
-
interface:
|
|
264
|
-
properties:
|
|
265
|
-
- endpoint_name:
|
|
266
|
-
- signal: name
|
|
267
|
-
property: name
|
|
268
|
-
- signal: name
|
|
269
|
-
property: name
|
|
270
|
-
hal:
|
|
271
|
-
hal_name:
|
|
272
|
-
- primitive_endpoint:
|
|
273
|
-
- insignal: name
|
|
274
|
-
hal_name:
|
|
275
|
-
- struct_endpoint:
|
|
276
|
-
- insignal: name1
|
|
277
|
-
property: member1
|
|
278
|
-
- insignal: name2
|
|
279
|
-
property: member2
|
|
280
|
-
ecm:
|
|
281
|
-
- signal: name
|
|
282
|
-
signals:
|
|
283
|
-
tvrl:
|
|
284
|
-
- signal: name
|
|
285
|
-
property: can_name
|
|
286
|
-
offset: offset
|
|
287
|
-
factor: scaling
|
|
288
|
-
|
|
289
|
-
Args:
|
|
290
|
-
translation_file (Path): file with specs
|
|
291
|
-
|
|
292
|
-
Returns:
|
|
293
|
-
yaml_data (dict): Loaded YAML data as dict, empty if not found
|
|
294
|
-
"""
|
|
295
|
-
if not translation_file.is_file():
|
|
296
|
-
LOGGER.warning("No file found for %s", translation_file)
|
|
297
|
-
return {}
|
|
298
|
-
with open(translation_file, encoding="utf-8") as translation:
|
|
299
|
-
yaml = YAML(typ='safe', pure=True)
|
|
300
|
-
raw = yaml.load(translation)
|
|
301
|
-
return raw
|
|
302
|
-
|
|
303
|
-
def parse_api_definitions(self, api_definitions):
|
|
304
|
-
"""Parses group definitions.
|
|
305
|
-
|
|
306
|
-
Args:
|
|
307
|
-
api_definitions (dict): endpoints in parsed yaml file.
|
|
308
|
-
"""
|
|
309
|
-
for api_from_spec, definition in api_definitions.items():
|
|
310
|
-
for variant, variant_endpoints in self.extract_definition(definition).items():
|
|
311
|
-
for endpoints in variant_endpoints:
|
|
312
|
-
for endpoint, signals in endpoints.items():
|
|
313
|
-
self.parse_property_definitions({api_from_spec: signals}, endpoint, variant)
|
|
314
|
-
|
|
315
|
-
def parse_property_definitions(self, endpoint_definitions, endpoint, variant):
|
|
316
|
-
"""Parse signal definitions.
|
|
317
|
-
|
|
318
|
-
Args:
|
|
319
|
-
endpoint_definitions (dict): parsed yaml file.
|
|
320
|
-
endpoint (str): Name of the endpoint to use
|
|
321
|
-
"""
|
|
322
|
-
def _get_property_name(specification):
|
|
323
|
-
""" Handle cases when there are no propery name.
|
|
324
|
-
|
|
325
|
-
If there is no property name, the "group" is set to the signal.
|
|
326
|
-
This should be used when the property is not a struct in the interface api.
|
|
327
|
-
|
|
328
|
-
Args:
|
|
329
|
-
specification (dict): signal specification
|
|
330
|
-
Returns:
|
|
331
|
-
property_name (str): name of the potential internal property
|
|
332
|
-
"""
|
|
333
|
-
property_name = specification.get('property', '')
|
|
334
|
-
if not property_name:
|
|
335
|
-
return None
|
|
336
|
-
return property_name
|
|
337
|
-
|
|
338
|
-
enumerations = self.base_application.enumerations
|
|
339
|
-
|
|
340
|
-
for api, specifications in endpoint_definitions.items():
|
|
341
|
-
self.verify_api(api)
|
|
342
|
-
for specification in specifications:
|
|
343
|
-
in_out_signal = [key for key in specification.keys() if 'signal' in key]
|
|
344
|
-
base_signal = None
|
|
345
|
-
signal_name = None
|
|
346
|
-
if "in" in in_out_signal[0]:
|
|
347
|
-
for signal in self.base_application.insignals:
|
|
348
|
-
if signal.name == specification["insignal"]:
|
|
349
|
-
base_signal = signal
|
|
350
|
-
signal_name = signal.name
|
|
351
|
-
elif "out" in in_out_signal[0]:
|
|
352
|
-
for signal in self.base_application.outsignals:
|
|
353
|
-
if signal.name == specification["outsignal"]:
|
|
354
|
-
base_signal = signal
|
|
355
|
-
signal_name = signal.name
|
|
356
|
-
else:
|
|
357
|
-
raise BadYamlFormat(api, specification[in_out_signal[0]])
|
|
358
|
-
if base_signal is None:
|
|
359
|
-
continue
|
|
360
|
-
base_properties = self.base_application.get_signal_properties(
|
|
361
|
-
base_signal
|
|
362
|
-
)
|
|
363
|
-
|
|
364
|
-
if base_properties["type"] in enumerations:
|
|
365
|
-
underlying_data_type = enumerations[base_properties['type']]['underlying_data_type']
|
|
366
|
-
interface_type = underlying_data_type
|
|
367
|
-
if 'init' not in specification:
|
|
368
|
-
if enumerations[base_properties['type']]['default_value'] is not None:
|
|
369
|
-
init_value = enumerations[base_properties['type']]['default_value']
|
|
370
|
-
else:
|
|
371
|
-
LOGGER.warning('Initializing enumeration %s to "zero".', base_properties['type'])
|
|
372
|
-
init_value = [
|
|
373
|
-
k for k, v in enumerations[base_properties['type']]['members'].items() if v == 0
|
|
374
|
-
][0]
|
|
375
|
-
else:
|
|
376
|
-
init_value = specification.get("init", 0)
|
|
377
|
-
else:
|
|
378
|
-
interface_type = base_properties["type"]
|
|
379
|
-
init_value = specification.get("init", 0)
|
|
380
|
-
|
|
381
|
-
if "out" in in_out_signal[0] and "strategy" in specification:
|
|
382
|
-
LOGGER.warning('Cannot set read strategy for outsignal %s, using "Always".', signal_name)
|
|
383
|
-
strategy = "Always"
|
|
384
|
-
else:
|
|
385
|
-
strategy = specification.get("strategy", self.default_read_strategy)
|
|
386
|
-
if strategy not in self.read_strategies:
|
|
387
|
-
LOGGER.warning('Invalid strategy %s, using "Always" instead.', strategy)
|
|
388
|
-
strategy = self.default_read_strategy
|
|
389
|
-
|
|
390
|
-
if signal_name not in self.translations:
|
|
391
|
-
self.translations[signal_name] = set()
|
|
392
|
-
self.translations[signal_name].add(
|
|
393
|
-
(
|
|
394
|
-
"enum_0", # Enum starts at position 1.
|
|
395
|
-
_get_property_name(specification),
|
|
396
|
-
interface_type,
|
|
397
|
-
specification.get("type"),
|
|
398
|
-
specification.get("offset"),
|
|
399
|
-
specification.get("factor"),
|
|
400
|
-
specification.get("default"),
|
|
401
|
-
specification.get("length"),
|
|
402
|
-
specification.get("min"),
|
|
403
|
-
specification.get("max"),
|
|
404
|
-
specification.get("enum"),
|
|
405
|
-
init_value,
|
|
406
|
-
specification.get("description"),
|
|
407
|
-
specification.get("unit"),
|
|
408
|
-
endpoint,
|
|
409
|
-
api,
|
|
410
|
-
variant,
|
|
411
|
-
strategy,
|
|
412
|
-
specification.get("debug", False),
|
|
413
|
-
specification.get("dependability", False)
|
|
414
|
-
)
|
|
415
|
-
)
|
|
416
|
-
|
|
417
|
-
def spec_to_dict(self, signal_spec, signal_name):
|
|
418
|
-
"""Convert signal specification to dict
|
|
419
|
-
|
|
420
|
-
Args:
|
|
421
|
-
signal_spec (dict): signal specification
|
|
422
|
-
signal_name (str): signal name
|
|
423
|
-
Returns:
|
|
424
|
-
(dict): signal specification as dict
|
|
425
|
-
"""
|
|
426
|
-
return {
|
|
427
|
-
'variable': signal_name,
|
|
428
|
-
'variable_type': signal_spec[self.position.variable_type.value],
|
|
429
|
-
'property': signal_spec[self.position.property_name.value],
|
|
430
|
-
'property_type': signal_spec[self.position.property_type.value],
|
|
431
|
-
"default": signal_spec[self.position.default.value],
|
|
432
|
-
"length": signal_spec[self.position.length.value],
|
|
433
|
-
'offset': signal_spec[self.position.offset.value],
|
|
434
|
-
'factor': signal_spec[self.position.factor.value],
|
|
435
|
-
'range': {
|
|
436
|
-
'min': signal_spec[self.position.min.value],
|
|
437
|
-
'max': signal_spec[self.position.max.value]
|
|
438
|
-
},
|
|
439
|
-
'init': signal_spec[self.position.init.value],
|
|
440
|
-
'description': signal_spec[self.position.description.value],
|
|
441
|
-
'unit': signal_spec[self.position.unit.value],
|
|
442
|
-
'endpoint': signal_spec[self.position.endpoint.value],
|
|
443
|
-
'api': self.get_api_name(signal_spec[self.position.api.value]),
|
|
444
|
-
'variant': signal_spec[self.position.variant.value],
|
|
445
|
-
'strategy': signal_spec[self.position.strategy.value],
|
|
446
|
-
'debug': signal_spec[self.position.debug.value],
|
|
447
|
-
'dependability': signal_spec[self.position.dependability.value]
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
def to_dict(self, client="app"):
|
|
451
|
-
"""Method to generate dict to be saved as yaml
|
|
452
|
-
|
|
453
|
-
Returns:
|
|
454
|
-
spec (dict): Signalling specification
|
|
455
|
-
"""
|
|
456
|
-
spec = {"consumer": [], "producer": []}
|
|
457
|
-
direction = {
|
|
458
|
-
"app": ["consumer", "producer"],
|
|
459
|
-
"api": ["producer", "consumer"]}
|
|
460
|
-
for signal_name, signal_spec in self._generator(self.signal_names["app"]["insignals"]):
|
|
461
|
-
spec[direction[client][0]].append(
|
|
462
|
-
self.spec_to_dict(signal_spec, signal_name)
|
|
463
|
-
)
|
|
464
|
-
for signal_name, signal_spec in self._generator(self.signal_names["app"]["outsignals"]):
|
|
465
|
-
spec[direction[client][1]].append(
|
|
466
|
-
self.spec_to_dict(signal_spec, signal_name)
|
|
467
|
-
)
|
|
468
|
-
return spec
|
|
469
|
-
|
|
470
|
-
def _generator(self, signal_names, unique_names=False):
|
|
471
|
-
"""Iterate over signals for allowed services
|
|
472
|
-
|
|
473
|
-
If unique_names is True, the iterator does not yield the same signal twice
|
|
474
|
-
if unique_names is False, it yields each allowed signal spec with the signal name
|
|
475
|
-
|
|
476
|
-
Args:
|
|
477
|
-
signal_names (list): allowed signals
|
|
478
|
-
Yields:
|
|
479
|
-
name (str): Name of the signal
|
|
480
|
-
specification (dict): signal specification for allowed service
|
|
481
|
-
"""
|
|
482
|
-
for signal_name, specifications in (
|
|
483
|
-
(name, spec) for name, spec in sorted(
|
|
484
|
-
self.translations.items())
|
|
485
|
-
if name in signal_names):
|
|
486
|
-
for specification in specifications:
|
|
487
|
-
if unique_names:
|
|
488
|
-
yield signal_name, specification
|
|
489
|
-
break
|
|
490
|
-
yield signal_name, specification
|
|
1
|
+
# Copyright 2024 Volvo Car Corporation
|
|
2
|
+
# Licensed under Apache 2.0.
|
|
3
|
+
|
|
4
|
+
"""Module for CSP API abstraction."""
|
|
5
|
+
|
|
6
|
+
import enum
|
|
7
|
+
from ruamel.yaml import YAML
|
|
8
|
+
from abc import abstractmethod
|
|
9
|
+
from powertrain_build.interface.base import BaseApplication, Signal
|
|
10
|
+
from powertrain_build.lib import logger
|
|
11
|
+
|
|
12
|
+
LOGGER = logger.create_logger("service")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MissingApi(Exception):
|
|
16
|
+
"""Exception to raise when api is missing"""
|
|
17
|
+
def __init__(self, api, map_file):
|
|
18
|
+
self.message = f"Api {api} missing from {map_file}"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BadYamlFormat(Exception):
|
|
22
|
+
"""Exception to raise when in/out signal is not defined."""
|
|
23
|
+
def __init__(self, api, signal_name):
|
|
24
|
+
self.message = f"Signal {signal_name} for {api} should be set as insignal or outsignal."
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CspApi(BaseApplication):
|
|
28
|
+
"""Abstraction for HAL and SFW"""
|
|
29
|
+
position = enum.Enum(
|
|
30
|
+
'Position',
|
|
31
|
+
names=[
|
|
32
|
+
"property_name",
|
|
33
|
+
"property_type",
|
|
34
|
+
"variable_type",
|
|
35
|
+
"offset",
|
|
36
|
+
"factor",
|
|
37
|
+
"default",
|
|
38
|
+
"length",
|
|
39
|
+
"min",
|
|
40
|
+
"max",
|
|
41
|
+
"enum",
|
|
42
|
+
"init",
|
|
43
|
+
"description",
|
|
44
|
+
"unit",
|
|
45
|
+
"endpoint",
|
|
46
|
+
"api",
|
|
47
|
+
"variant",
|
|
48
|
+
"strategy",
|
|
49
|
+
"debug",
|
|
50
|
+
"dependability"
|
|
51
|
+
]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def __init__(self, base_application, read_strategy='Always'):
|
|
55
|
+
"""Create the interface object
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
base_application (BaseApplication): Primary object of an interface
|
|
59
|
+
Usually a raster, but can be an application or a model too.
|
|
60
|
+
"""
|
|
61
|
+
self.name = ""
|
|
62
|
+
self.translations = {}
|
|
63
|
+
self.api_side = False
|
|
64
|
+
# we only care when generating models for csp.
|
|
65
|
+
self.filter = None
|
|
66
|
+
self.signal_names = {
|
|
67
|
+
"api": {"insignals": set(), "outsignals": set()},
|
|
68
|
+
"app": {"insignals": set(), "outsignals": set()},
|
|
69
|
+
}
|
|
70
|
+
self.base_application = base_application
|
|
71
|
+
self.map_file = self.get_map_file()
|
|
72
|
+
self.api_map = self.get_map()
|
|
73
|
+
self.translations_files = []
|
|
74
|
+
self.signal_primitives_list = []
|
|
75
|
+
self.default_read_strategy = read_strategy
|
|
76
|
+
|
|
77
|
+
def get_signal_properties(self, signal):
|
|
78
|
+
"""Get signal properties for signal
|
|
79
|
+
|
|
80
|
+
Calls self.base_application to get signal properties
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
signal (Signal): Signal to get properties for
|
|
84
|
+
"""
|
|
85
|
+
self.base_application.get_signal_properties(signal)
|
|
86
|
+
|
|
87
|
+
def _get_signals(self):
|
|
88
|
+
"""Read signals"""
|
|
89
|
+
self.parse_definition(self.translations_files)
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def insignals(self):
|
|
93
|
+
return self.get_signals(self.hal_name, 'insignals')
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def outsignals(self):
|
|
97
|
+
return self.get_signals(self.hal_name, 'outsignals')
|
|
98
|
+
|
|
99
|
+
def get_signals(self, api, signal_type='insignals'):
|
|
100
|
+
"""Get signals to and from an api abstraction
|
|
101
|
+
|
|
102
|
+
self.api_side configures if we look at the api side.
|
|
103
|
+
If it is set to False, we look at the application side.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
api (str): Name of the api
|
|
107
|
+
signal_type (str): insignals or outsignals
|
|
108
|
+
Returns:
|
|
109
|
+
signals (list): Signals in the interface
|
|
110
|
+
"""
|
|
111
|
+
if self.api_side:
|
|
112
|
+
signal_names = self.signal_names['api'][signal_type]
|
|
113
|
+
else:
|
|
114
|
+
signal_names = self.signal_names['app'][signal_type]
|
|
115
|
+
|
|
116
|
+
signals = []
|
|
117
|
+
for signal_name, specs in self.translations.items():
|
|
118
|
+
for spec in specs:
|
|
119
|
+
signal = None
|
|
120
|
+
if api is not None:
|
|
121
|
+
if spec['api'] == api:
|
|
122
|
+
if self.api_side:
|
|
123
|
+
if spec['property'] in signal_names:
|
|
124
|
+
signal = Signal(spec['property'], self)
|
|
125
|
+
else:
|
|
126
|
+
if signal_name in signal_names:
|
|
127
|
+
signal = Signal(signal_name, self)
|
|
128
|
+
else:
|
|
129
|
+
if self.api_side:
|
|
130
|
+
if spec['property'] in signal_names:
|
|
131
|
+
signal = Signal(spec['property'], self)
|
|
132
|
+
else:
|
|
133
|
+
if signal_name in signal_names:
|
|
134
|
+
signal = Signal(signal_name, self)
|
|
135
|
+
if signal is not None:
|
|
136
|
+
signals.append(signal)
|
|
137
|
+
return signals
|
|
138
|
+
|
|
139
|
+
def clear_signal_names(self):
|
|
140
|
+
"""Clear signal names
|
|
141
|
+
|
|
142
|
+
Clears defined signal names (but not signal properties).
|
|
143
|
+
"""
|
|
144
|
+
self.signal_names = {
|
|
145
|
+
"api": {"insignals": set(), "outsignals": set()},
|
|
146
|
+
"app": {"insignals": set(), "outsignals": set()},
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
def parse_definition(self, definition):
|
|
150
|
+
"""Parses all definition files
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
definition (list(Path)): Definition files
|
|
154
|
+
"""
|
|
155
|
+
for translation in definition:
|
|
156
|
+
raw = self.read_translation(translation)
|
|
157
|
+
self.extract_endpoint_definitions(raw)
|
|
158
|
+
|
|
159
|
+
@staticmethod
|
|
160
|
+
def get_api_name(api_name):
|
|
161
|
+
"""Return the api name
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
api_name (str): Name of the api
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
(str): Name of the api
|
|
168
|
+
"""
|
|
169
|
+
return api_name
|
|
170
|
+
|
|
171
|
+
def verify_api(self, api_name):
|
|
172
|
+
"""Verify that the api is in the map
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
api_name (str): Name of the api
|
|
176
|
+
"""
|
|
177
|
+
if api_name not in self.api_map:
|
|
178
|
+
raise MissingApi(api_name, self.map_file)
|
|
179
|
+
|
|
180
|
+
def add_signals(self, signals, signal_type='insignals', properties=None):
|
|
181
|
+
"""Add signal names and properties to already set ones
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
signals (list(Signals)): Signals to use
|
|
185
|
+
signal_type (str): 'insignals' or 'outsignals'
|
|
186
|
+
properties (list(str)): signal definition properties, default = []
|
|
187
|
+
"""
|
|
188
|
+
opposite = {'insignals': 'outsignals', 'outsignals': 'insignals'}
|
|
189
|
+
api_type = opposite[signal_type]
|
|
190
|
+
properties = [] if properties is None else properties
|
|
191
|
+
for signal in signals:
|
|
192
|
+
LOGGER.debug("Adding signal: %s", signal)
|
|
193
|
+
temp_set = set()
|
|
194
|
+
for translation in self.translations.get(signal.name, []):
|
|
195
|
+
temp_list = list(translation)
|
|
196
|
+
api_name = translation[self.position.api.value]
|
|
197
|
+
variant_name = translation[self.position.variant.value]
|
|
198
|
+
endpoint = translation[self.position.endpoint.value]
|
|
199
|
+
api_signal = translation[self.position.property_name.value]
|
|
200
|
+
self.check_signal_property(api_name, variant_name, endpoint,
|
|
201
|
+
api_signal, signal_type)
|
|
202
|
+
self.signal_names['api'][api_type].add(api_signal)
|
|
203
|
+
for enum_property in properties:
|
|
204
|
+
LOGGER.debug("Modifying property: %s", enum_property)
|
|
205
|
+
value = signal.properties[enum_property["source"]]
|
|
206
|
+
if value == "-":
|
|
207
|
+
value = enum_property["default"]
|
|
208
|
+
temp_list[
|
|
209
|
+
self.position[enum_property["destination"]].value
|
|
210
|
+
] = value
|
|
211
|
+
temp_set.add(tuple(temp_list))
|
|
212
|
+
self.translations[signal.name] = temp_set
|
|
213
|
+
self.signal_names['app'][signal_type].add(signal.name)
|
|
214
|
+
self.check_endpoints()
|
|
215
|
+
LOGGER.debug('Registered signal names: %s', self.signal_names)
|
|
216
|
+
|
|
217
|
+
def check_signal_property(self, api, variant, endpoint, property_name, signal_type):
|
|
218
|
+
"""Check if we have only one signal written for the same property.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
api (str): interface name
|
|
222
|
+
variant (str): variant value. "properties" or "methods" for service
|
|
223
|
+
"hals" for hal
|
|
224
|
+
endpoint (str): signal endpoint
|
|
225
|
+
property_name (str): signal property
|
|
226
|
+
signal_type (str): 'insignals' or 'outsignals'
|
|
227
|
+
"""
|
|
228
|
+
primitive_value = ""
|
|
229
|
+
for value in [api, variant, endpoint, property_name]:
|
|
230
|
+
if value:
|
|
231
|
+
if primitive_value == "":
|
|
232
|
+
primitive_value = value
|
|
233
|
+
else:
|
|
234
|
+
primitive_value = primitive_value + '.' + value
|
|
235
|
+
if primitive_value == "":
|
|
236
|
+
raise Exception("The primitive does not contain any value!")
|
|
237
|
+
directional_primitive = f"{primitive_value}.{signal_type}"
|
|
238
|
+
self.check_property(directional_primitive, signal_type)
|
|
239
|
+
|
|
240
|
+
def check_property(self, property_spec, signal_type):
|
|
241
|
+
"""Check if we have only one signal written for the same property.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
property_spec (str): property specification
|
|
245
|
+
signal_type (str): 'insignals' or 'outsignals'
|
|
246
|
+
"""
|
|
247
|
+
if property_spec in self.signal_primitives_list:
|
|
248
|
+
error_msg = (f"You can't write {property_spec} as "
|
|
249
|
+
f"{signal_type} since this primitive has been used."
|
|
250
|
+
" Run model_yaml_verification to identify exact models.")
|
|
251
|
+
raise Exception(error_msg)
|
|
252
|
+
self.signal_primitives_list.append(property_spec)
|
|
253
|
+
|
|
254
|
+
@abstractmethod
|
|
255
|
+
def check_endpoints(self):
|
|
256
|
+
"""Should be implemented by subclasses."""
|
|
257
|
+
|
|
258
|
+
@staticmethod
|
|
259
|
+
def read_translation(translation_file):
|
|
260
|
+
"""Read specification of the format:
|
|
261
|
+
|
|
262
|
+
service:
|
|
263
|
+
interface:
|
|
264
|
+
properties:
|
|
265
|
+
- endpoint_name:
|
|
266
|
+
- signal: name
|
|
267
|
+
property: name
|
|
268
|
+
- signal: name
|
|
269
|
+
property: name
|
|
270
|
+
hal:
|
|
271
|
+
hal_name:
|
|
272
|
+
- primitive_endpoint:
|
|
273
|
+
- insignal: name
|
|
274
|
+
hal_name:
|
|
275
|
+
- struct_endpoint:
|
|
276
|
+
- insignal: name1
|
|
277
|
+
property: member1
|
|
278
|
+
- insignal: name2
|
|
279
|
+
property: member2
|
|
280
|
+
ecm:
|
|
281
|
+
- signal: name
|
|
282
|
+
signals:
|
|
283
|
+
tvrl:
|
|
284
|
+
- signal: name
|
|
285
|
+
property: can_name
|
|
286
|
+
offset: offset
|
|
287
|
+
factor: scaling
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
translation_file (Path): file with specs
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
yaml_data (dict): Loaded YAML data as dict, empty if not found
|
|
294
|
+
"""
|
|
295
|
+
if not translation_file.is_file():
|
|
296
|
+
LOGGER.warning("No file found for %s", translation_file)
|
|
297
|
+
return {}
|
|
298
|
+
with open(translation_file, encoding="utf-8") as translation:
|
|
299
|
+
yaml = YAML(typ='safe', pure=True)
|
|
300
|
+
raw = yaml.load(translation)
|
|
301
|
+
return raw
|
|
302
|
+
|
|
303
|
+
def parse_api_definitions(self, api_definitions):
|
|
304
|
+
"""Parses group definitions.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
api_definitions (dict): endpoints in parsed yaml file.
|
|
308
|
+
"""
|
|
309
|
+
for api_from_spec, definition in api_definitions.items():
|
|
310
|
+
for variant, variant_endpoints in self.extract_definition(definition).items():
|
|
311
|
+
for endpoints in variant_endpoints:
|
|
312
|
+
for endpoint, signals in endpoints.items():
|
|
313
|
+
self.parse_property_definitions({api_from_spec: signals}, endpoint, variant)
|
|
314
|
+
|
|
315
|
+
def parse_property_definitions(self, endpoint_definitions, endpoint, variant):
|
|
316
|
+
"""Parse signal definitions.
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
endpoint_definitions (dict): parsed yaml file.
|
|
320
|
+
endpoint (str): Name of the endpoint to use
|
|
321
|
+
"""
|
|
322
|
+
def _get_property_name(specification):
|
|
323
|
+
""" Handle cases when there are no propery name.
|
|
324
|
+
|
|
325
|
+
If there is no property name, the "group" is set to the signal.
|
|
326
|
+
This should be used when the property is not a struct in the interface api.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
specification (dict): signal specification
|
|
330
|
+
Returns:
|
|
331
|
+
property_name (str): name of the potential internal property
|
|
332
|
+
"""
|
|
333
|
+
property_name = specification.get('property', '')
|
|
334
|
+
if not property_name:
|
|
335
|
+
return None
|
|
336
|
+
return property_name
|
|
337
|
+
|
|
338
|
+
enumerations = self.base_application.enumerations
|
|
339
|
+
|
|
340
|
+
for api, specifications in endpoint_definitions.items():
|
|
341
|
+
self.verify_api(api)
|
|
342
|
+
for specification in specifications:
|
|
343
|
+
in_out_signal = [key for key in specification.keys() if 'signal' in key]
|
|
344
|
+
base_signal = None
|
|
345
|
+
signal_name = None
|
|
346
|
+
if "in" in in_out_signal[0]:
|
|
347
|
+
for signal in self.base_application.insignals:
|
|
348
|
+
if signal.name == specification["insignal"]:
|
|
349
|
+
base_signal = signal
|
|
350
|
+
signal_name = signal.name
|
|
351
|
+
elif "out" in in_out_signal[0]:
|
|
352
|
+
for signal in self.base_application.outsignals:
|
|
353
|
+
if signal.name == specification["outsignal"]:
|
|
354
|
+
base_signal = signal
|
|
355
|
+
signal_name = signal.name
|
|
356
|
+
else:
|
|
357
|
+
raise BadYamlFormat(api, specification[in_out_signal[0]])
|
|
358
|
+
if base_signal is None:
|
|
359
|
+
continue
|
|
360
|
+
base_properties = self.base_application.get_signal_properties(
|
|
361
|
+
base_signal
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
if base_properties["type"] in enumerations:
|
|
365
|
+
underlying_data_type = enumerations[base_properties['type']]['underlying_data_type']
|
|
366
|
+
interface_type = underlying_data_type
|
|
367
|
+
if 'init' not in specification:
|
|
368
|
+
if enumerations[base_properties['type']]['default_value'] is not None:
|
|
369
|
+
init_value = enumerations[base_properties['type']]['default_value']
|
|
370
|
+
else:
|
|
371
|
+
LOGGER.warning('Initializing enumeration %s to "zero".', base_properties['type'])
|
|
372
|
+
init_value = [
|
|
373
|
+
k for k, v in enumerations[base_properties['type']]['members'].items() if v == 0
|
|
374
|
+
][0]
|
|
375
|
+
else:
|
|
376
|
+
init_value = specification.get("init", 0)
|
|
377
|
+
else:
|
|
378
|
+
interface_type = base_properties["type"]
|
|
379
|
+
init_value = specification.get("init", 0)
|
|
380
|
+
|
|
381
|
+
if "out" in in_out_signal[0] and "strategy" in specification:
|
|
382
|
+
LOGGER.warning('Cannot set read strategy for outsignal %s, using "Always".', signal_name)
|
|
383
|
+
strategy = "Always"
|
|
384
|
+
else:
|
|
385
|
+
strategy = specification.get("strategy", self.default_read_strategy)
|
|
386
|
+
if strategy not in self.read_strategies:
|
|
387
|
+
LOGGER.warning('Invalid strategy %s, using "Always" instead.', strategy)
|
|
388
|
+
strategy = self.default_read_strategy
|
|
389
|
+
|
|
390
|
+
if signal_name not in self.translations:
|
|
391
|
+
self.translations[signal_name] = set()
|
|
392
|
+
self.translations[signal_name].add(
|
|
393
|
+
(
|
|
394
|
+
"enum_0", # Enum starts at position 1.
|
|
395
|
+
_get_property_name(specification),
|
|
396
|
+
interface_type,
|
|
397
|
+
specification.get("type"),
|
|
398
|
+
specification.get("offset"),
|
|
399
|
+
specification.get("factor"),
|
|
400
|
+
specification.get("default"),
|
|
401
|
+
specification.get("length"),
|
|
402
|
+
specification.get("min"),
|
|
403
|
+
specification.get("max"),
|
|
404
|
+
specification.get("enum"),
|
|
405
|
+
init_value,
|
|
406
|
+
specification.get("description"),
|
|
407
|
+
specification.get("unit"),
|
|
408
|
+
endpoint,
|
|
409
|
+
api,
|
|
410
|
+
variant,
|
|
411
|
+
strategy,
|
|
412
|
+
specification.get("debug", False),
|
|
413
|
+
specification.get("dependability", False)
|
|
414
|
+
)
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
def spec_to_dict(self, signal_spec, signal_name):
|
|
418
|
+
"""Convert signal specification to dict
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
signal_spec (dict): signal specification
|
|
422
|
+
signal_name (str): signal name
|
|
423
|
+
Returns:
|
|
424
|
+
(dict): signal specification as dict
|
|
425
|
+
"""
|
|
426
|
+
return {
|
|
427
|
+
'variable': signal_name,
|
|
428
|
+
'variable_type': signal_spec[self.position.variable_type.value],
|
|
429
|
+
'property': signal_spec[self.position.property_name.value],
|
|
430
|
+
'property_type': signal_spec[self.position.property_type.value],
|
|
431
|
+
"default": signal_spec[self.position.default.value],
|
|
432
|
+
"length": signal_spec[self.position.length.value],
|
|
433
|
+
'offset': signal_spec[self.position.offset.value],
|
|
434
|
+
'factor': signal_spec[self.position.factor.value],
|
|
435
|
+
'range': {
|
|
436
|
+
'min': signal_spec[self.position.min.value],
|
|
437
|
+
'max': signal_spec[self.position.max.value]
|
|
438
|
+
},
|
|
439
|
+
'init': signal_spec[self.position.init.value],
|
|
440
|
+
'description': signal_spec[self.position.description.value],
|
|
441
|
+
'unit': signal_spec[self.position.unit.value],
|
|
442
|
+
'endpoint': signal_spec[self.position.endpoint.value],
|
|
443
|
+
'api': self.get_api_name(signal_spec[self.position.api.value]),
|
|
444
|
+
'variant': signal_spec[self.position.variant.value],
|
|
445
|
+
'strategy': signal_spec[self.position.strategy.value],
|
|
446
|
+
'debug': signal_spec[self.position.debug.value],
|
|
447
|
+
'dependability': signal_spec[self.position.dependability.value]
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
def to_dict(self, client="app"):
|
|
451
|
+
"""Method to generate dict to be saved as yaml
|
|
452
|
+
|
|
453
|
+
Returns:
|
|
454
|
+
spec (dict): Signalling specification
|
|
455
|
+
"""
|
|
456
|
+
spec = {"consumer": [], "producer": []}
|
|
457
|
+
direction = {
|
|
458
|
+
"app": ["consumer", "producer"],
|
|
459
|
+
"api": ["producer", "consumer"]}
|
|
460
|
+
for signal_name, signal_spec in self._generator(self.signal_names["app"]["insignals"]):
|
|
461
|
+
spec[direction[client][0]].append(
|
|
462
|
+
self.spec_to_dict(signal_spec, signal_name)
|
|
463
|
+
)
|
|
464
|
+
for signal_name, signal_spec in self._generator(self.signal_names["app"]["outsignals"]):
|
|
465
|
+
spec[direction[client][1]].append(
|
|
466
|
+
self.spec_to_dict(signal_spec, signal_name)
|
|
467
|
+
)
|
|
468
|
+
return spec
|
|
469
|
+
|
|
470
|
+
def _generator(self, signal_names, unique_names=False):
|
|
471
|
+
"""Iterate over signals for allowed services
|
|
472
|
+
|
|
473
|
+
If unique_names is True, the iterator does not yield the same signal twice
|
|
474
|
+
if unique_names is False, it yields each allowed signal spec with the signal name
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
signal_names (list): allowed signals
|
|
478
|
+
Yields:
|
|
479
|
+
name (str): Name of the signal
|
|
480
|
+
specification (dict): signal specification for allowed service
|
|
481
|
+
"""
|
|
482
|
+
for signal_name, specifications in (
|
|
483
|
+
(name, spec) for name, spec in sorted(
|
|
484
|
+
self.translations.items())
|
|
485
|
+
if name in signal_names):
|
|
486
|
+
for specification in specifications:
|
|
487
|
+
if unique_names:
|
|
488
|
+
yield signal_name, specification
|
|
489
|
+
break
|
|
490
|
+
yield signal_name, specification
|