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,362 +1,362 @@
|
|
|
1
|
-
# Copyright 2024 Volvo Car Corporation
|
|
2
|
-
# Licensed under Apache 2.0.
|
|
3
|
-
|
|
4
|
-
"""Python module used for handling zone controller specifications"""
|
|
5
|
-
from ruamel.yaml import YAML
|
|
6
|
-
from powertrain_build.lib import logger
|
|
7
|
-
from powertrain_build.interface.base import BaseApplication
|
|
8
|
-
|
|
9
|
-
LOGGER = logger.create_logger("base")
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class BadYamlFormat(Exception):
|
|
13
|
-
"""Exception to raise when in/out signal is not defined."""
|
|
14
|
-
def __init__(self, message):
|
|
15
|
-
self.message = message
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class ZCAL(BaseApplication):
|
|
19
|
-
"""Zone controller abstraction layer"""
|
|
20
|
-
|
|
21
|
-
def __repr__(self):
|
|
22
|
-
"""String representation of ZCAL"""
|
|
23
|
-
return (
|
|
24
|
-
f"<ZCAL {self.name}"
|
|
25
|
-
f" app_side insignals: {len(self.signal_names['other']['insignals'])}"
|
|
26
|
-
f" app_side outsignals: {len(self.signal_names['other']['outsignals'])}>"
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
def __init__(self, base_application):
|
|
30
|
-
"""Create the interface object
|
|
31
|
-
|
|
32
|
-
Currently, there is no verification that the signal is used.
|
|
33
|
-
|
|
34
|
-
TODO: Check if the signal is a set or a get somewhere. Maybe in here.
|
|
35
|
-
|
|
36
|
-
Args:
|
|
37
|
-
base_application (BaseApplication): Primary object of an interface
|
|
38
|
-
Usually a raster, but can be an application or a model too.
|
|
39
|
-
"""
|
|
40
|
-
self.name = ""
|
|
41
|
-
self.zc_translations = {}
|
|
42
|
-
self.composition_spec = {}
|
|
43
|
-
self.signal_names = {}
|
|
44
|
-
self.e2e_sts_signals = set()
|
|
45
|
-
self.update_bit_signals = set()
|
|
46
|
-
self.clear_signal_names()
|
|
47
|
-
self.interface_enums = set()
|
|
48
|
-
self.base_application = base_application
|
|
49
|
-
self.translations_files = []
|
|
50
|
-
self.device_domain = base_application.get_domain_mapping()
|
|
51
|
-
self.signal_primitives_list = set()
|
|
52
|
-
|
|
53
|
-
@staticmethod
|
|
54
|
-
def read_translation_files(translation_files, keys=None):
|
|
55
|
-
""" Searches translation files (yaml) for given keys at top level. Raises an
|
|
56
|
-
error if conflicting configurations are found among the files. Return a dict
|
|
57
|
-
containing the aggregated data found in the translation files for the keys
|
|
58
|
-
provided in the function call.
|
|
59
|
-
|
|
60
|
-
Args:
|
|
61
|
-
translation_files (list): List of paths to files to search.
|
|
62
|
-
keys (list): List of keys to search among translation files for (all present keys by default).
|
|
63
|
-
|
|
64
|
-
Returns:
|
|
65
|
-
specs (dict): Dictionary containing provided keys with the aggregated data
|
|
66
|
-
stored under those keys in translation files.
|
|
67
|
-
"""
|
|
68
|
-
|
|
69
|
-
specs = {}
|
|
70
|
-
for translation_file in translation_files:
|
|
71
|
-
with open(translation_file, encoding="utf-8") as translation:
|
|
72
|
-
yaml = YAML(typ='safe', pure=True)
|
|
73
|
-
raw = yaml.load(translation)
|
|
74
|
-
used_keys = raw.keys() if keys is None else keys
|
|
75
|
-
for key in used_keys:
|
|
76
|
-
new_elements = raw.get(key, None)
|
|
77
|
-
if isinstance(new_elements, list):
|
|
78
|
-
specs[key] = specs.get(key, []) + new_elements
|
|
79
|
-
elif isinstance(new_elements, dict):
|
|
80
|
-
specs[key] = specs.get(key, {})
|
|
81
|
-
if {**specs[key], **new_elements} != {**new_elements, **specs[key]}:
|
|
82
|
-
LOGGER.error(
|
|
83
|
-
"Conflicting configuration found for key '%s' among translation files: %s",
|
|
84
|
-
key,
|
|
85
|
-
translation_files,
|
|
86
|
-
)
|
|
87
|
-
specs[key].update(new_elements)
|
|
88
|
-
return specs
|
|
89
|
-
|
|
90
|
-
def add_signals(self, signals, signal_type="insignal", *args):
|
|
91
|
-
"""Add signal names and properties
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
signals (list(Signals)): Signals to use
|
|
95
|
-
signal_type (str): 'insignals' or 'outsignals'
|
|
96
|
-
"""
|
|
97
|
-
for signal in signals:
|
|
98
|
-
if signal.name not in self.zc_translations:
|
|
99
|
-
continue
|
|
100
|
-
LOGGER.debug("Adding signal: %s", signal)
|
|
101
|
-
for translation in self.zc_translations[signal.name]:
|
|
102
|
-
signal_property = translation["property"]
|
|
103
|
-
port_name = translation["port_name"]
|
|
104
|
-
struct_name = translation["struct_name"]
|
|
105
|
-
self.check_signal_property(port_name, struct_name, signal_property, signal_type)
|
|
106
|
-
self.signal_names["zc"][signal_type].add(signal_property)
|
|
107
|
-
self.signal_names["other"][signal_type].add(signal.name)
|
|
108
|
-
for e2e_sts_signal_name in self.e2e_sts_signals:
|
|
109
|
-
if e2e_sts_signal_name not in self.signal_names["other"]["insignals"]:
|
|
110
|
-
LOGGER.warning("E2E check signal %s not used in any model.", e2e_sts_signal_name)
|
|
111
|
-
else:
|
|
112
|
-
self.signal_names["other"][signal_type].add(e2e_sts_signal_name)
|
|
113
|
-
for update_bit_signal_name in self.update_bit_signals:
|
|
114
|
-
if update_bit_signal_name not in self.signal_names["other"]["insignals"]:
|
|
115
|
-
LOGGER.warning("Update bit signal %s not used in any model.", update_bit_signal_name)
|
|
116
|
-
else:
|
|
117
|
-
self.signal_names["other"][signal_type].add(update_bit_signal_name)
|
|
118
|
-
LOGGER.debug("Registered signal names: %s", self.signal_names)
|
|
119
|
-
|
|
120
|
-
def check_signal_property(self, port_name, struct_name, property_name, signal_type):
|
|
121
|
-
"""Check if we have only one signal written for the same property.
|
|
122
|
-
|
|
123
|
-
Args:
|
|
124
|
-
port_name (str): port name
|
|
125
|
-
struct_name (str): signal struct name
|
|
126
|
-
property_name (str): signal property
|
|
127
|
-
signal_type (str): insignal or outsignal
|
|
128
|
-
"""
|
|
129
|
-
signal_primitive_spec = ".".join([port_name, struct_name, property_name, signal_type])
|
|
130
|
-
if signal_primitive_spec in self.signal_primitives_list and signal_type == "outsignal":
|
|
131
|
-
error_msg = (f"You can't write {property_name} in {struct_name} as"
|
|
132
|
-
f" {signal_type} since this primitive has been used.")
|
|
133
|
-
raise Exception(error_msg)
|
|
134
|
-
self.signal_primitives_list.add(signal_primitive_spec)
|
|
135
|
-
|
|
136
|
-
def parse_definition(self, definition):
|
|
137
|
-
"""Parses all translation files and populates class interface data.
|
|
138
|
-
|
|
139
|
-
Args:
|
|
140
|
-
definition (list(Path)): Definition files
|
|
141
|
-
"""
|
|
142
|
-
raw = self.read_translation_files(definition)
|
|
143
|
-
self.composition_spec = {
|
|
144
|
-
key: value for key, value in raw.items() if key not in ["ports"]
|
|
145
|
-
}
|
|
146
|
-
ports_info = {}
|
|
147
|
-
for port_name, port in raw.get("ports", {}).items():
|
|
148
|
-
ports_info[port_name] = {k: v for k, v in port.items() if k not in ["element"]}
|
|
149
|
-
signal_struct = port.get("element", {})
|
|
150
|
-
direction = port.get("direction", None)
|
|
151
|
-
if signal_struct:
|
|
152
|
-
for element_name, element_data in signal_struct.items():
|
|
153
|
-
self.populate_signal_translations(port_name, element_name, element_data)
|
|
154
|
-
# Loop inside function to make sure direction is consistent
|
|
155
|
-
ports_info[port_name].update(self.get_port_info(port_name, direction, signal_struct))
|
|
156
|
-
self.composition_spec["ports"] = ports_info
|
|
157
|
-
|
|
158
|
-
@staticmethod
|
|
159
|
-
def get_port_info(port_name, direction, signal_struct):
|
|
160
|
-
"""Extract port information from signal elements in port. Raises exception
|
|
161
|
-
if signal elements are not exclusively sent in one direction.
|
|
162
|
-
|
|
163
|
-
Args:
|
|
164
|
-
port_name (str): Name of the port.
|
|
165
|
-
direction (str): Direction of the port.
|
|
166
|
-
signal_struct (dict): Signal dict containing list of signal elements.
|
|
167
|
-
Returns:
|
|
168
|
-
port_info (dict): Dict containing information if any elements
|
|
169
|
-
should have an update bit associated with them.
|
|
170
|
-
"""
|
|
171
|
-
if direction is None:
|
|
172
|
-
raise BadYamlFormat(f'Port {port_name} is missing required property "direction".')
|
|
173
|
-
|
|
174
|
-
port_info = {}
|
|
175
|
-
update_elements = []
|
|
176
|
-
end_to_end_elements = {}
|
|
177
|
-
for element_name, element_data in signal_struct.items():
|
|
178
|
-
for element in element_data:
|
|
179
|
-
if "insignal" in element:
|
|
180
|
-
temp_dir = "IN"
|
|
181
|
-
elif "outsignal" in element:
|
|
182
|
-
temp_dir = "OUT"
|
|
183
|
-
else:
|
|
184
|
-
raise BadYamlFormat(f"in-/out-signal for element in { element_name } is missing.")
|
|
185
|
-
# CLIENT and SERVER direction support both in and out signals
|
|
186
|
-
if direction not in ["CLIENT", "SERVER"] and direction != temp_dir:
|
|
187
|
-
raise BadYamlFormat(f"Signal { element_name } has both in and out elements.")
|
|
188
|
-
if element.get("updateBit", False) and element_name not in update_elements:
|
|
189
|
-
update_elements.append(element_name)
|
|
190
|
-
if element.get("e2eStatus", False) and element_name not in end_to_end_elements:
|
|
191
|
-
end_to_end_elements[element_name] = {}
|
|
192
|
-
|
|
193
|
-
if update_elements:
|
|
194
|
-
port_info.update({"enable_update": update_elements})
|
|
195
|
-
if end_to_end_elements:
|
|
196
|
-
port_info.update({"end_to_end": [{k: v} for k, v in end_to_end_elements.items()]})
|
|
197
|
-
return port_info
|
|
198
|
-
|
|
199
|
-
def populate_signal_translations(self, port_name, element_name, element_data):
|
|
200
|
-
"""Populate class translations data.
|
|
201
|
-
|
|
202
|
-
Args:
|
|
203
|
-
port_name (str): Port name.
|
|
204
|
-
element_name (str): Name of a port element.
|
|
205
|
-
element_data (dict): Dict with signal structs to/from a port of an element.
|
|
206
|
-
"""
|
|
207
|
-
enumerations = self.base_application.enumerations
|
|
208
|
-
|
|
209
|
-
for signal_definition in element_data:
|
|
210
|
-
if "insignal" in signal_definition:
|
|
211
|
-
signal_name = signal_definition["insignal"]
|
|
212
|
-
base_signals = self.base_application.insignals
|
|
213
|
-
elif "outsignal" in signal_definition:
|
|
214
|
-
signal_name = signal_definition["outsignal"]
|
|
215
|
-
base_signals = self.base_application.outsignals
|
|
216
|
-
else:
|
|
217
|
-
raise BadYamlFormat(f"{ signal_name } is neither insignal nor outsignal in interface yaml file.")
|
|
218
|
-
base_properties = None
|
|
219
|
-
for base_signal in base_signals:
|
|
220
|
-
if signal_name == base_signal.name:
|
|
221
|
-
matching_base_signal = base_signal
|
|
222
|
-
base_properties = self.base_application.get_signal_properties(
|
|
223
|
-
matching_base_signal
|
|
224
|
-
)
|
|
225
|
-
if base_properties is None:
|
|
226
|
-
raise BadYamlFormat(
|
|
227
|
-
f"{ signal_name } properties is missing. "
|
|
228
|
-
"Make sure that the model is in the raster file and that it is active in the current project."
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
if base_properties["type"] in enumerations:
|
|
232
|
-
if 'init' in signal_definition:
|
|
233
|
-
init_value = signal_definition["init"]
|
|
234
|
-
else:
|
|
235
|
-
if enumerations[base_properties['type']]['default_value'] is not None:
|
|
236
|
-
init_value = enumerations[base_properties['type']]['default_value']
|
|
237
|
-
else:
|
|
238
|
-
LOGGER.warning('Initializing enumeration %s to "zero".', base_properties['type'])
|
|
239
|
-
init_value = [
|
|
240
|
-
k for k, v in enumerations[base_properties['type']]['members'].items() if v == 0
|
|
241
|
-
][0]
|
|
242
|
-
else:
|
|
243
|
-
init_value = signal_definition.get("init", 0)
|
|
244
|
-
|
|
245
|
-
update_bit = signal_definition.get("updateBit", False)
|
|
246
|
-
e2e_status = signal_definition.get("e2eStatus", False)
|
|
247
|
-
group = signal_definition.get("group", element_name)
|
|
248
|
-
translation = {
|
|
249
|
-
"range": {
|
|
250
|
-
"min": base_properties.get("min", "-"),
|
|
251
|
-
"max": base_properties.get("max", "-")
|
|
252
|
-
},
|
|
253
|
-
"offset": base_properties.get("offset", "-"),
|
|
254
|
-
"factor": base_properties.get("factor", "-"),
|
|
255
|
-
"property": signal_definition["property"],
|
|
256
|
-
"init": init_value,
|
|
257
|
-
"port_name": port_name,
|
|
258
|
-
"struct_name": element_name,
|
|
259
|
-
"variable_type": base_properties.get("type"),
|
|
260
|
-
"description": base_properties.get("description"),
|
|
261
|
-
"unit": base_properties.get("unit"),
|
|
262
|
-
"width": base_properties.get("width"),
|
|
263
|
-
"debug": base_properties.get("debug", False),
|
|
264
|
-
"dependability": e2e_status,
|
|
265
|
-
"update_bit": update_bit
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
if signal_name not in self.zc_translations:
|
|
269
|
-
self.zc_translations[signal_name] = []
|
|
270
|
-
self.zc_translations[signal_name].append(translation)
|
|
271
|
-
if update_bit:
|
|
272
|
-
update_bit_property = f"{element_name}UpdateBit"
|
|
273
|
-
update_signal_name = f"yVc{group}_B_{update_bit_property}"
|
|
274
|
-
if signal_name == update_signal_name:
|
|
275
|
-
error_msg = f"Don't put updateBit status signals ({update_signal_name}) in yaml interface files."
|
|
276
|
-
raise BadYamlFormat(error_msg)
|
|
277
|
-
self.zc_translations[update_signal_name] = [
|
|
278
|
-
{
|
|
279
|
-
"property": update_bit_property,
|
|
280
|
-
"variable_type": "Bool",
|
|
281
|
-
"property_interface_type": "Bool",
|
|
282
|
-
"offset": "-",
|
|
283
|
-
"factor": "-",
|
|
284
|
-
"range": {
|
|
285
|
-
"min": "-",
|
|
286
|
-
"max": "-",
|
|
287
|
-
},
|
|
288
|
-
"init": 1,
|
|
289
|
-
"port_name": port_name,
|
|
290
|
-
"struct_name": element_name,
|
|
291
|
-
"description": f"Update bit signal for signal {element_name}.",
|
|
292
|
-
"unit": None,
|
|
293
|
-
"dependability": False,
|
|
294
|
-
"update_bit": True
|
|
295
|
-
}
|
|
296
|
-
]
|
|
297
|
-
self.update_bit_signals.add(update_signal_name)
|
|
298
|
-
|
|
299
|
-
if e2e_status:
|
|
300
|
-
if "outsignal" in signal_definition:
|
|
301
|
-
error_msg = "E2e status not expected for outsignals"
|
|
302
|
-
raise BadYamlFormat(error_msg)
|
|
303
|
-
|
|
304
|
-
e2e_sts_property = f"{element_name}E2eSts"
|
|
305
|
-
e2e_sts_signal_name = f"sVc{group}_D_{e2e_sts_property}"
|
|
306
|
-
|
|
307
|
-
if signal_name == e2e_sts_signal_name:
|
|
308
|
-
error_msg = f"Don't put E2E status signals ({e2e_sts_signal_name}) in yaml interface files."
|
|
309
|
-
raise BadYamlFormat(error_msg)
|
|
310
|
-
if e2e_sts_signal_name not in self.zc_translations:
|
|
311
|
-
self.zc_translations[e2e_sts_signal_name] = [
|
|
312
|
-
{
|
|
313
|
-
"property": e2e_sts_property,
|
|
314
|
-
"variable_type": "UInt8",
|
|
315
|
-
"property_interface_type": "UInt8",
|
|
316
|
-
"offset": 0,
|
|
317
|
-
"factor": 1,
|
|
318
|
-
"range": {
|
|
319
|
-
"min": 0,
|
|
320
|
-
"max": 255
|
|
321
|
-
},
|
|
322
|
-
"init": 255,
|
|
323
|
-
"port_name": port_name,
|
|
324
|
-
"struct_name": element_name,
|
|
325
|
-
"description": f"E2E status code for E2E protected signal(s) {signal_name}.",
|
|
326
|
-
"unit": None,
|
|
327
|
-
"debug": False,
|
|
328
|
-
"dependability": True,
|
|
329
|
-
"update_bit": False
|
|
330
|
-
}
|
|
331
|
-
]
|
|
332
|
-
self.e2e_sts_signals.add(e2e_sts_signal_name)
|
|
333
|
-
else:
|
|
334
|
-
translation = self.zc_translations[e2e_sts_signal_name][0]
|
|
335
|
-
translation["description"] = translation["description"][:-1] + f", {signal_name}."
|
|
336
|
-
|
|
337
|
-
def clear_signal_names(self):
|
|
338
|
-
"""Clear signal names
|
|
339
|
-
|
|
340
|
-
Clears defined signal names (but not signal properties).
|
|
341
|
-
"""
|
|
342
|
-
self.signal_names = {
|
|
343
|
-
"zc": {"insignals": set(), "outsignals": set()},
|
|
344
|
-
"other": {"insignals": set(), "outsignals": set()}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
def to_dict(self):
|
|
348
|
-
"""Method to generate dict to be saved as yaml
|
|
349
|
-
|
|
350
|
-
Returns:
|
|
351
|
-
spec (dict): Signalling specification
|
|
352
|
-
"""
|
|
353
|
-
spec = {"consumer": [], "producer": []}
|
|
354
|
-
|
|
355
|
-
signal_roles = ["consumer", "producer"]
|
|
356
|
-
signal_types = ["insignals", "outsignals"]
|
|
357
|
-
for signal_role, signal_type in zip(signal_roles, signal_types):
|
|
358
|
-
for signal_name in self.signal_names["other"][signal_type]:
|
|
359
|
-
for signal_spec in self.zc_translations[signal_name]:
|
|
360
|
-
spec[signal_role].append({**signal_spec, "variable": signal_name})
|
|
361
|
-
|
|
362
|
-
return spec
|
|
1
|
+
# Copyright 2024 Volvo Car Corporation
|
|
2
|
+
# Licensed under Apache 2.0.
|
|
3
|
+
|
|
4
|
+
"""Python module used for handling zone controller specifications"""
|
|
5
|
+
from ruamel.yaml import YAML
|
|
6
|
+
from powertrain_build.lib import logger
|
|
7
|
+
from powertrain_build.interface.base import BaseApplication
|
|
8
|
+
|
|
9
|
+
LOGGER = logger.create_logger("base")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BadYamlFormat(Exception):
|
|
13
|
+
"""Exception to raise when in/out signal is not defined."""
|
|
14
|
+
def __init__(self, message):
|
|
15
|
+
self.message = message
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ZCAL(BaseApplication):
|
|
19
|
+
"""Zone controller abstraction layer"""
|
|
20
|
+
|
|
21
|
+
def __repr__(self):
|
|
22
|
+
"""String representation of ZCAL"""
|
|
23
|
+
return (
|
|
24
|
+
f"<ZCAL {self.name}"
|
|
25
|
+
f" app_side insignals: {len(self.signal_names['other']['insignals'])}"
|
|
26
|
+
f" app_side outsignals: {len(self.signal_names['other']['outsignals'])}>"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def __init__(self, base_application):
|
|
30
|
+
"""Create the interface object
|
|
31
|
+
|
|
32
|
+
Currently, there is no verification that the signal is used.
|
|
33
|
+
|
|
34
|
+
TODO: Check if the signal is a set or a get somewhere. Maybe in here.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
base_application (BaseApplication): Primary object of an interface
|
|
38
|
+
Usually a raster, but can be an application or a model too.
|
|
39
|
+
"""
|
|
40
|
+
self.name = ""
|
|
41
|
+
self.zc_translations = {}
|
|
42
|
+
self.composition_spec = {}
|
|
43
|
+
self.signal_names = {}
|
|
44
|
+
self.e2e_sts_signals = set()
|
|
45
|
+
self.update_bit_signals = set()
|
|
46
|
+
self.clear_signal_names()
|
|
47
|
+
self.interface_enums = set()
|
|
48
|
+
self.base_application = base_application
|
|
49
|
+
self.translations_files = []
|
|
50
|
+
self.device_domain = base_application.get_domain_mapping()
|
|
51
|
+
self.signal_primitives_list = set()
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def read_translation_files(translation_files, keys=None):
|
|
55
|
+
""" Searches translation files (yaml) for given keys at top level. Raises an
|
|
56
|
+
error if conflicting configurations are found among the files. Return a dict
|
|
57
|
+
containing the aggregated data found in the translation files for the keys
|
|
58
|
+
provided in the function call.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
translation_files (list): List of paths to files to search.
|
|
62
|
+
keys (list): List of keys to search among translation files for (all present keys by default).
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
specs (dict): Dictionary containing provided keys with the aggregated data
|
|
66
|
+
stored under those keys in translation files.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
specs = {}
|
|
70
|
+
for translation_file in translation_files:
|
|
71
|
+
with open(translation_file, encoding="utf-8") as translation:
|
|
72
|
+
yaml = YAML(typ='safe', pure=True)
|
|
73
|
+
raw = yaml.load(translation)
|
|
74
|
+
used_keys = raw.keys() if keys is None else keys
|
|
75
|
+
for key in used_keys:
|
|
76
|
+
new_elements = raw.get(key, None)
|
|
77
|
+
if isinstance(new_elements, list):
|
|
78
|
+
specs[key] = specs.get(key, []) + new_elements
|
|
79
|
+
elif isinstance(new_elements, dict):
|
|
80
|
+
specs[key] = specs.get(key, {})
|
|
81
|
+
if {**specs[key], **new_elements} != {**new_elements, **specs[key]}:
|
|
82
|
+
LOGGER.error(
|
|
83
|
+
"Conflicting configuration found for key '%s' among translation files: %s",
|
|
84
|
+
key,
|
|
85
|
+
translation_files,
|
|
86
|
+
)
|
|
87
|
+
specs[key].update(new_elements)
|
|
88
|
+
return specs
|
|
89
|
+
|
|
90
|
+
def add_signals(self, signals, signal_type="insignal", *args):
|
|
91
|
+
"""Add signal names and properties
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
signals (list(Signals)): Signals to use
|
|
95
|
+
signal_type (str): 'insignals' or 'outsignals'
|
|
96
|
+
"""
|
|
97
|
+
for signal in signals:
|
|
98
|
+
if signal.name not in self.zc_translations:
|
|
99
|
+
continue
|
|
100
|
+
LOGGER.debug("Adding signal: %s", signal)
|
|
101
|
+
for translation in self.zc_translations[signal.name]:
|
|
102
|
+
signal_property = translation["property"]
|
|
103
|
+
port_name = translation["port_name"]
|
|
104
|
+
struct_name = translation["struct_name"]
|
|
105
|
+
self.check_signal_property(port_name, struct_name, signal_property, signal_type)
|
|
106
|
+
self.signal_names["zc"][signal_type].add(signal_property)
|
|
107
|
+
self.signal_names["other"][signal_type].add(signal.name)
|
|
108
|
+
for e2e_sts_signal_name in self.e2e_sts_signals:
|
|
109
|
+
if e2e_sts_signal_name not in self.signal_names["other"]["insignals"]:
|
|
110
|
+
LOGGER.warning("E2E check signal %s not used in any model.", e2e_sts_signal_name)
|
|
111
|
+
else:
|
|
112
|
+
self.signal_names["other"][signal_type].add(e2e_sts_signal_name)
|
|
113
|
+
for update_bit_signal_name in self.update_bit_signals:
|
|
114
|
+
if update_bit_signal_name not in self.signal_names["other"]["insignals"]:
|
|
115
|
+
LOGGER.warning("Update bit signal %s not used in any model.", update_bit_signal_name)
|
|
116
|
+
else:
|
|
117
|
+
self.signal_names["other"][signal_type].add(update_bit_signal_name)
|
|
118
|
+
LOGGER.debug("Registered signal names: %s", self.signal_names)
|
|
119
|
+
|
|
120
|
+
def check_signal_property(self, port_name, struct_name, property_name, signal_type):
|
|
121
|
+
"""Check if we have only one signal written for the same property.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
port_name (str): port name
|
|
125
|
+
struct_name (str): signal struct name
|
|
126
|
+
property_name (str): signal property
|
|
127
|
+
signal_type (str): insignal or outsignal
|
|
128
|
+
"""
|
|
129
|
+
signal_primitive_spec = ".".join([port_name, struct_name, property_name, signal_type])
|
|
130
|
+
if signal_primitive_spec in self.signal_primitives_list and signal_type == "outsignal":
|
|
131
|
+
error_msg = (f"You can't write {property_name} in {struct_name} as"
|
|
132
|
+
f" {signal_type} since this primitive has been used.")
|
|
133
|
+
raise Exception(error_msg)
|
|
134
|
+
self.signal_primitives_list.add(signal_primitive_spec)
|
|
135
|
+
|
|
136
|
+
def parse_definition(self, definition):
|
|
137
|
+
"""Parses all translation files and populates class interface data.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
definition (list(Path)): Definition files
|
|
141
|
+
"""
|
|
142
|
+
raw = self.read_translation_files(definition)
|
|
143
|
+
self.composition_spec = {
|
|
144
|
+
key: value for key, value in raw.items() if key not in ["ports"]
|
|
145
|
+
}
|
|
146
|
+
ports_info = {}
|
|
147
|
+
for port_name, port in raw.get("ports", {}).items():
|
|
148
|
+
ports_info[port_name] = {k: v for k, v in port.items() if k not in ["element"]}
|
|
149
|
+
signal_struct = port.get("element", {})
|
|
150
|
+
direction = port.get("direction", None)
|
|
151
|
+
if signal_struct:
|
|
152
|
+
for element_name, element_data in signal_struct.items():
|
|
153
|
+
self.populate_signal_translations(port_name, element_name, element_data)
|
|
154
|
+
# Loop inside function to make sure direction is consistent
|
|
155
|
+
ports_info[port_name].update(self.get_port_info(port_name, direction, signal_struct))
|
|
156
|
+
self.composition_spec["ports"] = ports_info
|
|
157
|
+
|
|
158
|
+
@staticmethod
|
|
159
|
+
def get_port_info(port_name, direction, signal_struct):
|
|
160
|
+
"""Extract port information from signal elements in port. Raises exception
|
|
161
|
+
if signal elements are not exclusively sent in one direction.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
port_name (str): Name of the port.
|
|
165
|
+
direction (str): Direction of the port.
|
|
166
|
+
signal_struct (dict): Signal dict containing list of signal elements.
|
|
167
|
+
Returns:
|
|
168
|
+
port_info (dict): Dict containing information if any elements
|
|
169
|
+
should have an update bit associated with them.
|
|
170
|
+
"""
|
|
171
|
+
if direction is None:
|
|
172
|
+
raise BadYamlFormat(f'Port {port_name} is missing required property "direction".')
|
|
173
|
+
|
|
174
|
+
port_info = {}
|
|
175
|
+
update_elements = []
|
|
176
|
+
end_to_end_elements = {}
|
|
177
|
+
for element_name, element_data in signal_struct.items():
|
|
178
|
+
for element in element_data:
|
|
179
|
+
if "insignal" in element:
|
|
180
|
+
temp_dir = "IN"
|
|
181
|
+
elif "outsignal" in element:
|
|
182
|
+
temp_dir = "OUT"
|
|
183
|
+
else:
|
|
184
|
+
raise BadYamlFormat(f"in-/out-signal for element in { element_name } is missing.")
|
|
185
|
+
# CLIENT and SERVER direction support both in and out signals
|
|
186
|
+
if direction not in ["CLIENT", "SERVER"] and direction != temp_dir:
|
|
187
|
+
raise BadYamlFormat(f"Signal { element_name } has both in and out elements.")
|
|
188
|
+
if element.get("updateBit", False) and element_name not in update_elements:
|
|
189
|
+
update_elements.append(element_name)
|
|
190
|
+
if element.get("e2eStatus", False) and element_name not in end_to_end_elements:
|
|
191
|
+
end_to_end_elements[element_name] = {}
|
|
192
|
+
|
|
193
|
+
if update_elements:
|
|
194
|
+
port_info.update({"enable_update": update_elements})
|
|
195
|
+
if end_to_end_elements:
|
|
196
|
+
port_info.update({"end_to_end": [{k: v} for k, v in end_to_end_elements.items()]})
|
|
197
|
+
return port_info
|
|
198
|
+
|
|
199
|
+
def populate_signal_translations(self, port_name, element_name, element_data):
|
|
200
|
+
"""Populate class translations data.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
port_name (str): Port name.
|
|
204
|
+
element_name (str): Name of a port element.
|
|
205
|
+
element_data (dict): Dict with signal structs to/from a port of an element.
|
|
206
|
+
"""
|
|
207
|
+
enumerations = self.base_application.enumerations
|
|
208
|
+
|
|
209
|
+
for signal_definition in element_data:
|
|
210
|
+
if "insignal" in signal_definition:
|
|
211
|
+
signal_name = signal_definition["insignal"]
|
|
212
|
+
base_signals = self.base_application.insignals
|
|
213
|
+
elif "outsignal" in signal_definition:
|
|
214
|
+
signal_name = signal_definition["outsignal"]
|
|
215
|
+
base_signals = self.base_application.outsignals
|
|
216
|
+
else:
|
|
217
|
+
raise BadYamlFormat(f"{ signal_name } is neither insignal nor outsignal in interface yaml file.")
|
|
218
|
+
base_properties = None
|
|
219
|
+
for base_signal in base_signals:
|
|
220
|
+
if signal_name == base_signal.name:
|
|
221
|
+
matching_base_signal = base_signal
|
|
222
|
+
base_properties = self.base_application.get_signal_properties(
|
|
223
|
+
matching_base_signal
|
|
224
|
+
)
|
|
225
|
+
if base_properties is None:
|
|
226
|
+
raise BadYamlFormat(
|
|
227
|
+
f"{ signal_name } properties is missing. "
|
|
228
|
+
"Make sure that the model is in the raster file and that it is active in the current project."
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
if base_properties["type"] in enumerations:
|
|
232
|
+
if 'init' in signal_definition:
|
|
233
|
+
init_value = signal_definition["init"]
|
|
234
|
+
else:
|
|
235
|
+
if enumerations[base_properties['type']]['default_value'] is not None:
|
|
236
|
+
init_value = enumerations[base_properties['type']]['default_value']
|
|
237
|
+
else:
|
|
238
|
+
LOGGER.warning('Initializing enumeration %s to "zero".', base_properties['type'])
|
|
239
|
+
init_value = [
|
|
240
|
+
k for k, v in enumerations[base_properties['type']]['members'].items() if v == 0
|
|
241
|
+
][0]
|
|
242
|
+
else:
|
|
243
|
+
init_value = signal_definition.get("init", 0)
|
|
244
|
+
|
|
245
|
+
update_bit = signal_definition.get("updateBit", False)
|
|
246
|
+
e2e_status = signal_definition.get("e2eStatus", False)
|
|
247
|
+
group = signal_definition.get("group", element_name)
|
|
248
|
+
translation = {
|
|
249
|
+
"range": {
|
|
250
|
+
"min": base_properties.get("min", "-"),
|
|
251
|
+
"max": base_properties.get("max", "-")
|
|
252
|
+
},
|
|
253
|
+
"offset": base_properties.get("offset", "-"),
|
|
254
|
+
"factor": base_properties.get("factor", "-"),
|
|
255
|
+
"property": signal_definition["property"],
|
|
256
|
+
"init": init_value,
|
|
257
|
+
"port_name": port_name,
|
|
258
|
+
"struct_name": element_name,
|
|
259
|
+
"variable_type": base_properties.get("type"),
|
|
260
|
+
"description": base_properties.get("description"),
|
|
261
|
+
"unit": base_properties.get("unit"),
|
|
262
|
+
"width": base_properties.get("width"),
|
|
263
|
+
"debug": base_properties.get("debug", False),
|
|
264
|
+
"dependability": e2e_status,
|
|
265
|
+
"update_bit": update_bit
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if signal_name not in self.zc_translations:
|
|
269
|
+
self.zc_translations[signal_name] = []
|
|
270
|
+
self.zc_translations[signal_name].append(translation)
|
|
271
|
+
if update_bit:
|
|
272
|
+
update_bit_property = f"{element_name}UpdateBit"
|
|
273
|
+
update_signal_name = f"yVc{group}_B_{update_bit_property}"
|
|
274
|
+
if signal_name == update_signal_name:
|
|
275
|
+
error_msg = f"Don't put updateBit status signals ({update_signal_name}) in yaml interface files."
|
|
276
|
+
raise BadYamlFormat(error_msg)
|
|
277
|
+
self.zc_translations[update_signal_name] = [
|
|
278
|
+
{
|
|
279
|
+
"property": update_bit_property,
|
|
280
|
+
"variable_type": "Bool",
|
|
281
|
+
"property_interface_type": "Bool",
|
|
282
|
+
"offset": "-",
|
|
283
|
+
"factor": "-",
|
|
284
|
+
"range": {
|
|
285
|
+
"min": "-",
|
|
286
|
+
"max": "-",
|
|
287
|
+
},
|
|
288
|
+
"init": 1,
|
|
289
|
+
"port_name": port_name,
|
|
290
|
+
"struct_name": element_name,
|
|
291
|
+
"description": f"Update bit signal for signal {element_name}.",
|
|
292
|
+
"unit": None,
|
|
293
|
+
"dependability": False,
|
|
294
|
+
"update_bit": True
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
self.update_bit_signals.add(update_signal_name)
|
|
298
|
+
|
|
299
|
+
if e2e_status:
|
|
300
|
+
if "outsignal" in signal_definition:
|
|
301
|
+
error_msg = "E2e status not expected for outsignals"
|
|
302
|
+
raise BadYamlFormat(error_msg)
|
|
303
|
+
|
|
304
|
+
e2e_sts_property = f"{element_name}E2eSts"
|
|
305
|
+
e2e_sts_signal_name = f"sVc{group}_D_{e2e_sts_property}"
|
|
306
|
+
|
|
307
|
+
if signal_name == e2e_sts_signal_name:
|
|
308
|
+
error_msg = f"Don't put E2E status signals ({e2e_sts_signal_name}) in yaml interface files."
|
|
309
|
+
raise BadYamlFormat(error_msg)
|
|
310
|
+
if e2e_sts_signal_name not in self.zc_translations:
|
|
311
|
+
self.zc_translations[e2e_sts_signal_name] = [
|
|
312
|
+
{
|
|
313
|
+
"property": e2e_sts_property,
|
|
314
|
+
"variable_type": "UInt8",
|
|
315
|
+
"property_interface_type": "UInt8",
|
|
316
|
+
"offset": 0,
|
|
317
|
+
"factor": 1,
|
|
318
|
+
"range": {
|
|
319
|
+
"min": 0,
|
|
320
|
+
"max": 255
|
|
321
|
+
},
|
|
322
|
+
"init": 255,
|
|
323
|
+
"port_name": port_name,
|
|
324
|
+
"struct_name": element_name,
|
|
325
|
+
"description": f"E2E status code for E2E protected signal(s) {signal_name}.",
|
|
326
|
+
"unit": None,
|
|
327
|
+
"debug": False,
|
|
328
|
+
"dependability": True,
|
|
329
|
+
"update_bit": False
|
|
330
|
+
}
|
|
331
|
+
]
|
|
332
|
+
self.e2e_sts_signals.add(e2e_sts_signal_name)
|
|
333
|
+
else:
|
|
334
|
+
translation = self.zc_translations[e2e_sts_signal_name][0]
|
|
335
|
+
translation["description"] = translation["description"][:-1] + f", {signal_name}."
|
|
336
|
+
|
|
337
|
+
def clear_signal_names(self):
|
|
338
|
+
"""Clear signal names
|
|
339
|
+
|
|
340
|
+
Clears defined signal names (but not signal properties).
|
|
341
|
+
"""
|
|
342
|
+
self.signal_names = {
|
|
343
|
+
"zc": {"insignals": set(), "outsignals": set()},
|
|
344
|
+
"other": {"insignals": set(), "outsignals": set()}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
def to_dict(self):
|
|
348
|
+
"""Method to generate dict to be saved as yaml
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
spec (dict): Signalling specification
|
|
352
|
+
"""
|
|
353
|
+
spec = {"consumer": [], "producer": []}
|
|
354
|
+
|
|
355
|
+
signal_roles = ["consumer", "producer"]
|
|
356
|
+
signal_types = ["insignals", "outsignals"]
|
|
357
|
+
for signal_role, signal_type in zip(signal_roles, signal_types):
|
|
358
|
+
for signal_name in self.signal_names["other"][signal_type]:
|
|
359
|
+
for signal_spec in self.zc_translations[signal_name]:
|
|
360
|
+
spec[signal_role].append({**signal_spec, "variable": signal_name})
|
|
361
|
+
|
|
362
|
+
return spec
|