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,500 +1,500 @@
|
|
|
1
|
-
# Copyright 2024 Volvo Car Corporation
|
|
2
|
-
# Licensed under Apache 2.0.
|
|
3
|
-
|
|
4
|
-
# -*- coding: utf-8 -*-
|
|
5
|
-
"""Python module used for abstracting an application that should be interfacing others."""
|
|
6
|
-
from abc import abstractmethod
|
|
7
|
-
from ruamel.yaml import YAML
|
|
8
|
-
from powertrain_build.lib import logger
|
|
9
|
-
|
|
10
|
-
LOGGER = logger.create_logger('base')
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def filter_signals(signals, domain):
|
|
14
|
-
""" Take a list of signals and remove all domains belonging to a domain
|
|
15
|
-
|
|
16
|
-
If the signal is part of the domain, it is not part of the resulting list
|
|
17
|
-
|
|
18
|
-
Arguments:
|
|
19
|
-
signals (list(Signal)): signals to filter
|
|
20
|
-
domain (Domain): domain that the signals should not be part of
|
|
21
|
-
"""
|
|
22
|
-
filtered_signals = []
|
|
23
|
-
for signal in signals:
|
|
24
|
-
if signal.name not in domain.signals:
|
|
25
|
-
filtered_signals.append(signal)
|
|
26
|
-
return filtered_signals
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class MultipleProducersError(Exception):
|
|
30
|
-
"""Error when setting a producer and there already exists one"""
|
|
31
|
-
def __init__(self, signal, old_producer, new_producer):
|
|
32
|
-
"""Set error message
|
|
33
|
-
|
|
34
|
-
Args:
|
|
35
|
-
signal (Signal): Signal object
|
|
36
|
-
old_producer (BaseApplication): Producer already registered
|
|
37
|
-
new_producer (BaseApplication): Producer attempted to be registered
|
|
38
|
-
"""
|
|
39
|
-
super().__init__()
|
|
40
|
-
self.message = (f"{signal.name}:"
|
|
41
|
-
f" Attempting to set producer {new_producer}"
|
|
42
|
-
f" when {old_producer} is already set")
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class BaseApplication:
|
|
46
|
-
"""Base application to build other adapters on"""
|
|
47
|
-
name = str()
|
|
48
|
-
_signals = None
|
|
49
|
-
node = str() # Used to calculate interface
|
|
50
|
-
|
|
51
|
-
read_strategies = {
|
|
52
|
-
"Always",
|
|
53
|
-
"OnChanged",
|
|
54
|
-
"OnUpdated"
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
def __repr__(self):
|
|
58
|
-
"""String representation for logging and debugging
|
|
59
|
-
|
|
60
|
-
Returns:
|
|
61
|
-
repr (string): Name of the application, the number of insignals and outsignals
|
|
62
|
-
"""
|
|
63
|
-
return f"<{self.name}" \
|
|
64
|
-
f" insignals:{len(self.insignals)}" \
|
|
65
|
-
f" outsignals:{len(self.outsignals)}>"
|
|
66
|
-
|
|
67
|
-
def parse_signals(self):
|
|
68
|
-
"""API interface to read all signals in any child object"""
|
|
69
|
-
self._get_signals()
|
|
70
|
-
|
|
71
|
-
@property
|
|
72
|
-
def insignals(self):
|
|
73
|
-
""" Insignals to the raster.
|
|
74
|
-
Calculated as all read ports - all written ports
|
|
75
|
-
|
|
76
|
-
Returns:
|
|
77
|
-
signals (list): List of Signal objects.
|
|
78
|
-
"""
|
|
79
|
-
if self._insignals is None:
|
|
80
|
-
self._get_signals()
|
|
81
|
-
return [self._signals[port] for port in self._insignals - self._outsignals]
|
|
82
|
-
|
|
83
|
-
@property
|
|
84
|
-
def outsignals(self):
|
|
85
|
-
""" All outports.
|
|
86
|
-
Since we might consume some of the signals that should also be sent elsewhere,
|
|
87
|
-
we do not remove internally consumed signals.
|
|
88
|
-
|
|
89
|
-
Returns:
|
|
90
|
-
signals (list): List of Signal objects.
|
|
91
|
-
"""
|
|
92
|
-
if self._outsignals is None:
|
|
93
|
-
self._get_signals()
|
|
94
|
-
return [self._signals[port] for port in self._outsignals]
|
|
95
|
-
|
|
96
|
-
@property
|
|
97
|
-
def signals(self):
|
|
98
|
-
"""API interface property in any child object
|
|
99
|
-
|
|
100
|
-
All cached signals.
|
|
101
|
-
If no cache exists, reads all signals and save to cache.
|
|
102
|
-
|
|
103
|
-
Returns:
|
|
104
|
-
signals (list): Signal objects
|
|
105
|
-
"""
|
|
106
|
-
if self._signals is None:
|
|
107
|
-
self.parse_signals()
|
|
108
|
-
return self._signals.values()
|
|
109
|
-
|
|
110
|
-
@abstractmethod
|
|
111
|
-
def _get_signals(self):
|
|
112
|
-
"""Stub to implement in child object"""
|
|
113
|
-
|
|
114
|
-
@abstractmethod
|
|
115
|
-
def get_signal_properties(self, signal):
|
|
116
|
-
"""Stub to implement in child object
|
|
117
|
-
|
|
118
|
-
Ideally, this should be moved to the signal.
|
|
119
|
-
Currently, getting the properties depends on how we read and define the signals.
|
|
120
|
-
"""
|
|
121
|
-
|
|
122
|
-
@abstractmethod
|
|
123
|
-
def parse_definition(self, definition):
|
|
124
|
-
"""Stub for parsing a defintion after the object has been initialized.
|
|
125
|
-
|
|
126
|
-
Raises NotImplementedError if called without being implemented.
|
|
127
|
-
|
|
128
|
-
Args:
|
|
129
|
-
definition: Definition of the Application. Type depends on the application.
|
|
130
|
-
"""
|
|
131
|
-
raise NotImplementedError('This is a stub')
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
class Signal:
|
|
135
|
-
"""Signal object
|
|
136
|
-
|
|
137
|
-
The signal should behave the same way independently of where we define it.
|
|
138
|
-
"""
|
|
139
|
-
def __repr__(self):
|
|
140
|
-
"""String representation for logging and debugging
|
|
141
|
-
|
|
142
|
-
Returns:
|
|
143
|
-
repr (string): Name of the application, the number of insignals and outsignals
|
|
144
|
-
"""
|
|
145
|
-
return (f"<{self.name} in {self.applications}"
|
|
146
|
-
f" producer:{self.producer}"
|
|
147
|
-
f" consumers:{self.consumers}>")
|
|
148
|
-
|
|
149
|
-
def __init__(self, name, application):
|
|
150
|
-
"""Define base properties of the signal object
|
|
151
|
-
|
|
152
|
-
The application object is used to read properties of a signal.
|
|
153
|
-
TODO: Do this when we define the signal and add properties known in other
|
|
154
|
-
systems when we encounter them.
|
|
155
|
-
|
|
156
|
-
Args:
|
|
157
|
-
name (string): Signal name
|
|
158
|
-
application (BaseApplication): Application defining the signal
|
|
159
|
-
"""
|
|
160
|
-
self.name = name
|
|
161
|
-
self.applications = {} # Add applications to a dict to prevent duplicates
|
|
162
|
-
if application is not None:
|
|
163
|
-
self.applications[application.name] = application
|
|
164
|
-
self._consumers = set()
|
|
165
|
-
self._producer = None
|
|
166
|
-
|
|
167
|
-
def add_application(self, application):
|
|
168
|
-
"""Add an application to find properties from
|
|
169
|
-
|
|
170
|
-
Args:
|
|
171
|
-
application (BaseApplication): Application to read properties from
|
|
172
|
-
"""
|
|
173
|
-
if application.name in self.applications:
|
|
174
|
-
return
|
|
175
|
-
self.applications[application.name] = application
|
|
176
|
-
|
|
177
|
-
@property
|
|
178
|
-
def consumers(self):
|
|
179
|
-
"""Get all consumers of a signal
|
|
180
|
-
|
|
181
|
-
Returns:
|
|
182
|
-
consumers (set): All consumers of a signal
|
|
183
|
-
"""
|
|
184
|
-
if isinstance(self._consumers, set):
|
|
185
|
-
return self._consumers
|
|
186
|
-
return set()
|
|
187
|
-
|
|
188
|
-
@consumers.setter
|
|
189
|
-
def consumers(self, consumers):
|
|
190
|
-
"""Set consumers of a signal
|
|
191
|
-
|
|
192
|
-
If the consumers is a list or set, iterate over each consumer
|
|
193
|
-
Otherwise, add the consumer to the set of consumers
|
|
194
|
-
|
|
195
|
-
Args:
|
|
196
|
-
consumers (list/set/string): consumer(s) of a signal
|
|
197
|
-
"""
|
|
198
|
-
if isinstance(consumers, (list, set)):
|
|
199
|
-
for consumer in consumers:
|
|
200
|
-
self._consumers.add(consumer)
|
|
201
|
-
else:
|
|
202
|
-
self._consumers.add(consumers)
|
|
203
|
-
|
|
204
|
-
@property
|
|
205
|
-
def producer(self):
|
|
206
|
-
"""Get the producer of a signal
|
|
207
|
-
|
|
208
|
-
Since we have some strange signals with multiple producers,
|
|
209
|
-
such as counters for dep, this returns a set.
|
|
210
|
-
Returns:
|
|
211
|
-
producer (set): Producer(s) of a signal
|
|
212
|
-
"""
|
|
213
|
-
if isinstance(self._producer, set):
|
|
214
|
-
return self._producer
|
|
215
|
-
return set()
|
|
216
|
-
|
|
217
|
-
@producer.setter
|
|
218
|
-
def producer(self, producer):
|
|
219
|
-
"""Set producer of a signal
|
|
220
|
-
|
|
221
|
-
Args:
|
|
222
|
-
producer (string/set): Name of the producer
|
|
223
|
-
"""
|
|
224
|
-
if isinstance(producer, set):
|
|
225
|
-
self._producer = producer
|
|
226
|
-
else:
|
|
227
|
-
self._producer = {producer}
|
|
228
|
-
|
|
229
|
-
def set_producer(self, producer):
|
|
230
|
-
"""Set producer of a signal
|
|
231
|
-
|
|
232
|
-
If there already is a registered producer of the signal,
|
|
233
|
-
raise MultipleProducersError
|
|
234
|
-
|
|
235
|
-
This can be expected and force_producer can be called to override this.
|
|
236
|
-
That must be explicit in each instance.
|
|
237
|
-
|
|
238
|
-
Args:
|
|
239
|
-
producer (string): Name of the producer
|
|
240
|
-
application (BaseApplication): Application defining the signal. Optional
|
|
241
|
-
"""
|
|
242
|
-
if isinstance(producer, set):
|
|
243
|
-
if self._producer is not None and producer - self._producer:
|
|
244
|
-
raise MultipleProducersError(self, self._producer, producer)
|
|
245
|
-
self.producer = producer
|
|
246
|
-
else:
|
|
247
|
-
if self._producer is not None \
|
|
248
|
-
and isinstance(producer, str) \
|
|
249
|
-
and producer not in self._producer:
|
|
250
|
-
raise MultipleProducersError(self, self._producer, producer)
|
|
251
|
-
self.producer = {producer}
|
|
252
|
-
|
|
253
|
-
def force_producer(self, producer):
|
|
254
|
-
"""Forcefully update add producers of a signal
|
|
255
|
-
|
|
256
|
-
This is needed since we have some signals that are written by multiple model
|
|
257
|
-
Args:
|
|
258
|
-
producers (string): Producer of a signal
|
|
259
|
-
application (BaseApplication): Application defining the signal. Optional
|
|
260
|
-
"""
|
|
261
|
-
|
|
262
|
-
self._producer.add(producer)
|
|
263
|
-
|
|
264
|
-
@property
|
|
265
|
-
def properties(self):
|
|
266
|
-
"""Properties of a signal
|
|
267
|
-
|
|
268
|
-
Currently not homogenized.
|
|
269
|
-
Therefore we read the properties from the application that defined the signal.
|
|
270
|
-
|
|
271
|
-
Returns:
|
|
272
|
-
properties (dict): properties of a signal
|
|
273
|
-
"""
|
|
274
|
-
properties = {}
|
|
275
|
-
for application in self.applications.values():
|
|
276
|
-
LOGGER.debug('Getting properties for %s from %s', self.name, application.name)
|
|
277
|
-
application_properties = application.get_signal_properties(self)
|
|
278
|
-
LOGGER.debug(application_properties)
|
|
279
|
-
for key, value in application_properties.items():
|
|
280
|
-
LOGGER.debug('Looking at %s: %s', key, value)
|
|
281
|
-
if key in properties and value != properties[key]:
|
|
282
|
-
LOGGER.debug('Signal %s already has %s with value %s, ignoring %s from %s',
|
|
283
|
-
self.name, key, properties[key], value, application.name)
|
|
284
|
-
continue
|
|
285
|
-
properties[key] = value
|
|
286
|
-
return properties
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
class Interface:
|
|
290
|
-
"""Interface between two objects"""
|
|
291
|
-
def __repr__(self):
|
|
292
|
-
"""String representation for logging and debugging
|
|
293
|
-
|
|
294
|
-
Returns:
|
|
295
|
-
repr (string): Name of the interface, and the length of received and transmitted signals
|
|
296
|
-
"""
|
|
297
|
-
return (f"<{self.name}"
|
|
298
|
-
f" a->b:{len(self.get_directional_signals(self.current, self.corresponding))}"
|
|
299
|
-
f" b->a:{len(self.get_directional_signals(self.corresponding, self.current))}>")
|
|
300
|
-
|
|
301
|
-
def debug(self):
|
|
302
|
-
"""Debug an interface object to stdout"""
|
|
303
|
-
LOGGER.info('name: %s', self.name)
|
|
304
|
-
for signal in self.get_directional_signals(self.current, self.corresponding):
|
|
305
|
-
LOGGER.info('insignal: %s', signal)
|
|
306
|
-
for signal in self.get_directional_signals(self.corresponding, self.current):
|
|
307
|
-
LOGGER.info('outsignal: %s', signal)
|
|
308
|
-
|
|
309
|
-
def __init__(self, current, corresponding):
|
|
310
|
-
"""Create the interface object
|
|
311
|
-
|
|
312
|
-
Args:
|
|
313
|
-
current (BaseApplication): Primary object of an interface
|
|
314
|
-
corresponding (BaseApplication): Secondary object of an interface
|
|
315
|
-
"""
|
|
316
|
-
self.name = current.name + '_' + corresponding.name
|
|
317
|
-
self.current = current
|
|
318
|
-
self.corresponding = corresponding
|
|
319
|
-
|
|
320
|
-
@staticmethod
|
|
321
|
-
def get_directional_signals(producer, consumer):
|
|
322
|
-
"""Get signals going from producer to consumer
|
|
323
|
-
|
|
324
|
-
Args:
|
|
325
|
-
producer (BaseApplication): producer of the signals
|
|
326
|
-
consumer (BaseApplication): consumer of the signals
|
|
327
|
-
Returns:
|
|
328
|
-
signals (list): Signals sent from producer and received in consumer
|
|
329
|
-
"""
|
|
330
|
-
outsignals = {signal.name: signal for signal in producer.outsignals}
|
|
331
|
-
signals = []
|
|
332
|
-
for signal in consumer.insignals:
|
|
333
|
-
if signal.name in outsignals:
|
|
334
|
-
signal.set_producer(outsignals[signal.name].producer)
|
|
335
|
-
signal.add_application(producer)
|
|
336
|
-
signal.add_application(consumer)
|
|
337
|
-
signals.append(signal)
|
|
338
|
-
return signals
|
|
339
|
-
|
|
340
|
-
def get_produced_signals(self, producer_name):
|
|
341
|
-
"""Get signals going from producer to consumer
|
|
342
|
-
|
|
343
|
-
This function can be used if you are lacking some objects
|
|
344
|
-
|
|
345
|
-
Args:
|
|
346
|
-
consumer_name (string): name of the consumer of the signals
|
|
347
|
-
Returns:
|
|
348
|
-
signals (list): Signals sent from producer and received in consumer
|
|
349
|
-
"""
|
|
350
|
-
if producer_name == self.current.name:
|
|
351
|
-
consumer = self.corresponding
|
|
352
|
-
producer = self.current
|
|
353
|
-
elif producer_name == self.corresponding.name:
|
|
354
|
-
consumer = self.current
|
|
355
|
-
producer = self.corresponding
|
|
356
|
-
else:
|
|
357
|
-
LOGGER.error('%s not in [%s, %s]',
|
|
358
|
-
producer_name,
|
|
359
|
-
self.current.name,
|
|
360
|
-
self.corresponding.name)
|
|
361
|
-
return self.get_directional_signals(producer, consumer)
|
|
362
|
-
|
|
363
|
-
def get_consumed_signals(self, consumer_name):
|
|
364
|
-
"""Get signals going from producer to consumer
|
|
365
|
-
|
|
366
|
-
This function can be used if you are lacking some objects
|
|
367
|
-
|
|
368
|
-
Args:
|
|
369
|
-
consumer_name (string): name of the consumer of the signals
|
|
370
|
-
Returns:
|
|
371
|
-
signals (list): Signals sent from producer and received in consumer
|
|
372
|
-
"""
|
|
373
|
-
if consumer_name == self.current.name:
|
|
374
|
-
consumer = self.current
|
|
375
|
-
producer = self.corresponding
|
|
376
|
-
elif consumer_name == self.corresponding.name:
|
|
377
|
-
consumer = self.corresponding
|
|
378
|
-
producer = self.current
|
|
379
|
-
else:
|
|
380
|
-
LOGGER.error('%s not in [%s, %s]',
|
|
381
|
-
consumer_name,
|
|
382
|
-
self.current.name,
|
|
383
|
-
self.corresponding.name)
|
|
384
|
-
return self.get_directional_signals(producer, consumer)
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
class Domain:
|
|
388
|
-
"""Domain with interacting interfaces"""
|
|
389
|
-
def __repr__(self):
|
|
390
|
-
"""String representation for logging and debugging
|
|
391
|
-
|
|
392
|
-
Returns:
|
|
393
|
-
repr (string): Name of the domain, and all clients for that domain
|
|
394
|
-
"""
|
|
395
|
-
return f"<{self.name}: {self.clients}>"
|
|
396
|
-
|
|
397
|
-
def __init__(self):
|
|
398
|
-
"""Initialize the object"""
|
|
399
|
-
self.name = ''
|
|
400
|
-
self.signals = {}
|
|
401
|
-
self.clients = set()
|
|
402
|
-
self._clients = {}
|
|
403
|
-
|
|
404
|
-
def set_name(self, name):
|
|
405
|
-
"""Set the name of the domain
|
|
406
|
-
|
|
407
|
-
Args:
|
|
408
|
-
name (string): Name of the domain
|
|
409
|
-
"""
|
|
410
|
-
self.name = name
|
|
411
|
-
|
|
412
|
-
def add_interface(self, interface):
|
|
413
|
-
"""Add an interface to a domain
|
|
414
|
-
|
|
415
|
-
Args:
|
|
416
|
-
interface (Interface): Interface object
|
|
417
|
-
"""
|
|
418
|
-
self._process_interface(interface)
|
|
419
|
-
|
|
420
|
-
def _process_interface(self, interface):
|
|
421
|
-
"""Process interface to add signals to the domain
|
|
422
|
-
|
|
423
|
-
Args:
|
|
424
|
-
interface (Interface): Interface object
|
|
425
|
-
"""
|
|
426
|
-
for signal in interface.get_directional_signals(interface.current, interface.corresponding):
|
|
427
|
-
self._process_signal(signal.name, interface.current, interface.corresponding)
|
|
428
|
-
for signal in interface.get_directional_signals(interface.corresponding, interface.current):
|
|
429
|
-
self._process_signal(signal.name, interface.corresponding, interface.current)
|
|
430
|
-
|
|
431
|
-
def _process_signal(self, signal_name, producer, consumer):
|
|
432
|
-
"""Process signal to add to the domain
|
|
433
|
-
|
|
434
|
-
Args:
|
|
435
|
-
signal_name (string): Name of the signal
|
|
436
|
-
consumer (BaseApplication): Consumer application of the signal
|
|
437
|
-
producer (BaseApplication): Producer application of the signal
|
|
438
|
-
"""
|
|
439
|
-
if signal_name not in self.signals:
|
|
440
|
-
self.signals[signal_name] = Signal(signal_name, producer)
|
|
441
|
-
self.signals[signal_name].consumers = consumer.name
|
|
442
|
-
self.signals[signal_name].add_application(producer)
|
|
443
|
-
self.signals[signal_name].add_application(consumer)
|
|
444
|
-
signal = self.signals[signal_name]
|
|
445
|
-
if producer.name not in self.clients:
|
|
446
|
-
self.clients.add(producer.name)
|
|
447
|
-
self._clients[producer.name] = {'producer': [], 'consumer': []}
|
|
448
|
-
self._clients[producer.name]['producer'].append(signal)
|
|
449
|
-
if consumer.name not in self.clients:
|
|
450
|
-
self.clients.add(producer.name)
|
|
451
|
-
self._clients[producer.name] = {'producer': [], 'consumer': []}
|
|
452
|
-
self._clients[producer.name]['consumer'].append(signal)
|
|
453
|
-
signal.consumers = consumer.name
|
|
454
|
-
try:
|
|
455
|
-
signal.producer = producer.name
|
|
456
|
-
except MultipleProducersError as mpe:
|
|
457
|
-
LOGGER.debug(mpe.message)
|
|
458
|
-
|
|
459
|
-
def create_groups(self):
|
|
460
|
-
"""Create groups of signals going from each producer
|
|
461
|
-
|
|
462
|
-
Returns:
|
|
463
|
-
signal_groups (dict): Signal groups
|
|
464
|
-
"""
|
|
465
|
-
signal_groups = {}
|
|
466
|
-
for signal in self.signals.values():
|
|
467
|
-
# Producer is always a set, to handle pass-through signals
|
|
468
|
-
for producer in signal.producer - set(signal_groups.keys()):
|
|
469
|
-
signal_groups[producer] = []
|
|
470
|
-
for producer in signal.producer:
|
|
471
|
-
signal_groups[producer].append(signal)
|
|
472
|
-
return signal_groups
|
|
473
|
-
|
|
474
|
-
def create_selective_groups(self, a_names, b_names):
|
|
475
|
-
"""Create groups for the a_list communicating with the b_names
|
|
476
|
-
|
|
477
|
-
Returns:
|
|
478
|
-
signal_groups (dict): Signal groups
|
|
479
|
-
"""
|
|
480
|
-
signal_groups = {name: {'consumer': [], 'producer': []} for name in a_names}
|
|
481
|
-
for signal in self.signals.values():
|
|
482
|
-
for producer in set(signal.producer):
|
|
483
|
-
if producer in a_names and signal.consumers & b_names:
|
|
484
|
-
signal_groups[producer]['producer'].append(signal)
|
|
485
|
-
for consumer in signal.consumers:
|
|
486
|
-
if consumer in a_names and set(signal.producer) & a_names:
|
|
487
|
-
signal_groups[consumer]['consumer'].append(signal)
|
|
488
|
-
return signal_groups
|
|
489
|
-
|
|
490
|
-
@staticmethod
|
|
491
|
-
def to_yaml(spec, output):
|
|
492
|
-
"""Writes spec to yaml file
|
|
493
|
-
|
|
494
|
-
Args:
|
|
495
|
-
spec (dict): data for the yaml
|
|
496
|
-
output (Path): file to write to
|
|
497
|
-
"""
|
|
498
|
-
with open(output, 'w', encoding="utf-8") as yaml_file:
|
|
499
|
-
yaml = YAML()
|
|
500
|
-
yaml.dump(spec, yaml_file)
|
|
1
|
+
# Copyright 2024 Volvo Car Corporation
|
|
2
|
+
# Licensed under Apache 2.0.
|
|
3
|
+
|
|
4
|
+
# -*- coding: utf-8 -*-
|
|
5
|
+
"""Python module used for abstracting an application that should be interfacing others."""
|
|
6
|
+
from abc import abstractmethod
|
|
7
|
+
from ruamel.yaml import YAML
|
|
8
|
+
from powertrain_build.lib import logger
|
|
9
|
+
|
|
10
|
+
LOGGER = logger.create_logger('base')
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def filter_signals(signals, domain):
|
|
14
|
+
""" Take a list of signals and remove all domains belonging to a domain
|
|
15
|
+
|
|
16
|
+
If the signal is part of the domain, it is not part of the resulting list
|
|
17
|
+
|
|
18
|
+
Arguments:
|
|
19
|
+
signals (list(Signal)): signals to filter
|
|
20
|
+
domain (Domain): domain that the signals should not be part of
|
|
21
|
+
"""
|
|
22
|
+
filtered_signals = []
|
|
23
|
+
for signal in signals:
|
|
24
|
+
if signal.name not in domain.signals:
|
|
25
|
+
filtered_signals.append(signal)
|
|
26
|
+
return filtered_signals
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class MultipleProducersError(Exception):
|
|
30
|
+
"""Error when setting a producer and there already exists one"""
|
|
31
|
+
def __init__(self, signal, old_producer, new_producer):
|
|
32
|
+
"""Set error message
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
signal (Signal): Signal object
|
|
36
|
+
old_producer (BaseApplication): Producer already registered
|
|
37
|
+
new_producer (BaseApplication): Producer attempted to be registered
|
|
38
|
+
"""
|
|
39
|
+
super().__init__()
|
|
40
|
+
self.message = (f"{signal.name}:"
|
|
41
|
+
f" Attempting to set producer {new_producer}"
|
|
42
|
+
f" when {old_producer} is already set")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class BaseApplication:
|
|
46
|
+
"""Base application to build other adapters on"""
|
|
47
|
+
name = str()
|
|
48
|
+
_signals = None
|
|
49
|
+
node = str() # Used to calculate interface
|
|
50
|
+
|
|
51
|
+
read_strategies = {
|
|
52
|
+
"Always",
|
|
53
|
+
"OnChanged",
|
|
54
|
+
"OnUpdated"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
def __repr__(self):
|
|
58
|
+
"""String representation for logging and debugging
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
repr (string): Name of the application, the number of insignals and outsignals
|
|
62
|
+
"""
|
|
63
|
+
return f"<{self.name}" \
|
|
64
|
+
f" insignals:{len(self.insignals)}" \
|
|
65
|
+
f" outsignals:{len(self.outsignals)}>"
|
|
66
|
+
|
|
67
|
+
def parse_signals(self):
|
|
68
|
+
"""API interface to read all signals in any child object"""
|
|
69
|
+
self._get_signals()
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def insignals(self):
|
|
73
|
+
""" Insignals to the raster.
|
|
74
|
+
Calculated as all read ports - all written ports
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
signals (list): List of Signal objects.
|
|
78
|
+
"""
|
|
79
|
+
if self._insignals is None:
|
|
80
|
+
self._get_signals()
|
|
81
|
+
return [self._signals[port] for port in self._insignals - self._outsignals]
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def outsignals(self):
|
|
85
|
+
""" All outports.
|
|
86
|
+
Since we might consume some of the signals that should also be sent elsewhere,
|
|
87
|
+
we do not remove internally consumed signals.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
signals (list): List of Signal objects.
|
|
91
|
+
"""
|
|
92
|
+
if self._outsignals is None:
|
|
93
|
+
self._get_signals()
|
|
94
|
+
return [self._signals[port] for port in self._outsignals]
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def signals(self):
|
|
98
|
+
"""API interface property in any child object
|
|
99
|
+
|
|
100
|
+
All cached signals.
|
|
101
|
+
If no cache exists, reads all signals and save to cache.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
signals (list): Signal objects
|
|
105
|
+
"""
|
|
106
|
+
if self._signals is None:
|
|
107
|
+
self.parse_signals()
|
|
108
|
+
return self._signals.values()
|
|
109
|
+
|
|
110
|
+
@abstractmethod
|
|
111
|
+
def _get_signals(self):
|
|
112
|
+
"""Stub to implement in child object"""
|
|
113
|
+
|
|
114
|
+
@abstractmethod
|
|
115
|
+
def get_signal_properties(self, signal):
|
|
116
|
+
"""Stub to implement in child object
|
|
117
|
+
|
|
118
|
+
Ideally, this should be moved to the signal.
|
|
119
|
+
Currently, getting the properties depends on how we read and define the signals.
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
@abstractmethod
|
|
123
|
+
def parse_definition(self, definition):
|
|
124
|
+
"""Stub for parsing a defintion after the object has been initialized.
|
|
125
|
+
|
|
126
|
+
Raises NotImplementedError if called without being implemented.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
definition: Definition of the Application. Type depends on the application.
|
|
130
|
+
"""
|
|
131
|
+
raise NotImplementedError('This is a stub')
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class Signal:
|
|
135
|
+
"""Signal object
|
|
136
|
+
|
|
137
|
+
The signal should behave the same way independently of where we define it.
|
|
138
|
+
"""
|
|
139
|
+
def __repr__(self):
|
|
140
|
+
"""String representation for logging and debugging
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
repr (string): Name of the application, the number of insignals and outsignals
|
|
144
|
+
"""
|
|
145
|
+
return (f"<{self.name} in {self.applications}"
|
|
146
|
+
f" producer:{self.producer}"
|
|
147
|
+
f" consumers:{self.consumers}>")
|
|
148
|
+
|
|
149
|
+
def __init__(self, name, application):
|
|
150
|
+
"""Define base properties of the signal object
|
|
151
|
+
|
|
152
|
+
The application object is used to read properties of a signal.
|
|
153
|
+
TODO: Do this when we define the signal and add properties known in other
|
|
154
|
+
systems when we encounter them.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
name (string): Signal name
|
|
158
|
+
application (BaseApplication): Application defining the signal
|
|
159
|
+
"""
|
|
160
|
+
self.name = name
|
|
161
|
+
self.applications = {} # Add applications to a dict to prevent duplicates
|
|
162
|
+
if application is not None:
|
|
163
|
+
self.applications[application.name] = application
|
|
164
|
+
self._consumers = set()
|
|
165
|
+
self._producer = None
|
|
166
|
+
|
|
167
|
+
def add_application(self, application):
|
|
168
|
+
"""Add an application to find properties from
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
application (BaseApplication): Application to read properties from
|
|
172
|
+
"""
|
|
173
|
+
if application.name in self.applications:
|
|
174
|
+
return
|
|
175
|
+
self.applications[application.name] = application
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def consumers(self):
|
|
179
|
+
"""Get all consumers of a signal
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
consumers (set): All consumers of a signal
|
|
183
|
+
"""
|
|
184
|
+
if isinstance(self._consumers, set):
|
|
185
|
+
return self._consumers
|
|
186
|
+
return set()
|
|
187
|
+
|
|
188
|
+
@consumers.setter
|
|
189
|
+
def consumers(self, consumers):
|
|
190
|
+
"""Set consumers of a signal
|
|
191
|
+
|
|
192
|
+
If the consumers is a list or set, iterate over each consumer
|
|
193
|
+
Otherwise, add the consumer to the set of consumers
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
consumers (list/set/string): consumer(s) of a signal
|
|
197
|
+
"""
|
|
198
|
+
if isinstance(consumers, (list, set)):
|
|
199
|
+
for consumer in consumers:
|
|
200
|
+
self._consumers.add(consumer)
|
|
201
|
+
else:
|
|
202
|
+
self._consumers.add(consumers)
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def producer(self):
|
|
206
|
+
"""Get the producer of a signal
|
|
207
|
+
|
|
208
|
+
Since we have some strange signals with multiple producers,
|
|
209
|
+
such as counters for dep, this returns a set.
|
|
210
|
+
Returns:
|
|
211
|
+
producer (set): Producer(s) of a signal
|
|
212
|
+
"""
|
|
213
|
+
if isinstance(self._producer, set):
|
|
214
|
+
return self._producer
|
|
215
|
+
return set()
|
|
216
|
+
|
|
217
|
+
@producer.setter
|
|
218
|
+
def producer(self, producer):
|
|
219
|
+
"""Set producer of a signal
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
producer (string/set): Name of the producer
|
|
223
|
+
"""
|
|
224
|
+
if isinstance(producer, set):
|
|
225
|
+
self._producer = producer
|
|
226
|
+
else:
|
|
227
|
+
self._producer = {producer}
|
|
228
|
+
|
|
229
|
+
def set_producer(self, producer):
|
|
230
|
+
"""Set producer of a signal
|
|
231
|
+
|
|
232
|
+
If there already is a registered producer of the signal,
|
|
233
|
+
raise MultipleProducersError
|
|
234
|
+
|
|
235
|
+
This can be expected and force_producer can be called to override this.
|
|
236
|
+
That must be explicit in each instance.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
producer (string): Name of the producer
|
|
240
|
+
application (BaseApplication): Application defining the signal. Optional
|
|
241
|
+
"""
|
|
242
|
+
if isinstance(producer, set):
|
|
243
|
+
if self._producer is not None and producer - self._producer:
|
|
244
|
+
raise MultipleProducersError(self, self._producer, producer)
|
|
245
|
+
self.producer = producer
|
|
246
|
+
else:
|
|
247
|
+
if self._producer is not None \
|
|
248
|
+
and isinstance(producer, str) \
|
|
249
|
+
and producer not in self._producer:
|
|
250
|
+
raise MultipleProducersError(self, self._producer, producer)
|
|
251
|
+
self.producer = {producer}
|
|
252
|
+
|
|
253
|
+
def force_producer(self, producer):
|
|
254
|
+
"""Forcefully update add producers of a signal
|
|
255
|
+
|
|
256
|
+
This is needed since we have some signals that are written by multiple model
|
|
257
|
+
Args:
|
|
258
|
+
producers (string): Producer of a signal
|
|
259
|
+
application (BaseApplication): Application defining the signal. Optional
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
self._producer.add(producer)
|
|
263
|
+
|
|
264
|
+
@property
|
|
265
|
+
def properties(self):
|
|
266
|
+
"""Properties of a signal
|
|
267
|
+
|
|
268
|
+
Currently not homogenized.
|
|
269
|
+
Therefore we read the properties from the application that defined the signal.
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
properties (dict): properties of a signal
|
|
273
|
+
"""
|
|
274
|
+
properties = {}
|
|
275
|
+
for application in self.applications.values():
|
|
276
|
+
LOGGER.debug('Getting properties for %s from %s', self.name, application.name)
|
|
277
|
+
application_properties = application.get_signal_properties(self)
|
|
278
|
+
LOGGER.debug(application_properties)
|
|
279
|
+
for key, value in application_properties.items():
|
|
280
|
+
LOGGER.debug('Looking at %s: %s', key, value)
|
|
281
|
+
if key in properties and value != properties[key]:
|
|
282
|
+
LOGGER.debug('Signal %s already has %s with value %s, ignoring %s from %s',
|
|
283
|
+
self.name, key, properties[key], value, application.name)
|
|
284
|
+
continue
|
|
285
|
+
properties[key] = value
|
|
286
|
+
return properties
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
class Interface:
|
|
290
|
+
"""Interface between two objects"""
|
|
291
|
+
def __repr__(self):
|
|
292
|
+
"""String representation for logging and debugging
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
repr (string): Name of the interface, and the length of received and transmitted signals
|
|
296
|
+
"""
|
|
297
|
+
return (f"<{self.name}"
|
|
298
|
+
f" a->b:{len(self.get_directional_signals(self.current, self.corresponding))}"
|
|
299
|
+
f" b->a:{len(self.get_directional_signals(self.corresponding, self.current))}>")
|
|
300
|
+
|
|
301
|
+
def debug(self):
|
|
302
|
+
"""Debug an interface object to stdout"""
|
|
303
|
+
LOGGER.info('name: %s', self.name)
|
|
304
|
+
for signal in self.get_directional_signals(self.current, self.corresponding):
|
|
305
|
+
LOGGER.info('insignal: %s', signal)
|
|
306
|
+
for signal in self.get_directional_signals(self.corresponding, self.current):
|
|
307
|
+
LOGGER.info('outsignal: %s', signal)
|
|
308
|
+
|
|
309
|
+
def __init__(self, current, corresponding):
|
|
310
|
+
"""Create the interface object
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
current (BaseApplication): Primary object of an interface
|
|
314
|
+
corresponding (BaseApplication): Secondary object of an interface
|
|
315
|
+
"""
|
|
316
|
+
self.name = current.name + '_' + corresponding.name
|
|
317
|
+
self.current = current
|
|
318
|
+
self.corresponding = corresponding
|
|
319
|
+
|
|
320
|
+
@staticmethod
|
|
321
|
+
def get_directional_signals(producer, consumer):
|
|
322
|
+
"""Get signals going from producer to consumer
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
producer (BaseApplication): producer of the signals
|
|
326
|
+
consumer (BaseApplication): consumer of the signals
|
|
327
|
+
Returns:
|
|
328
|
+
signals (list): Signals sent from producer and received in consumer
|
|
329
|
+
"""
|
|
330
|
+
outsignals = {signal.name: signal for signal in producer.outsignals}
|
|
331
|
+
signals = []
|
|
332
|
+
for signal in consumer.insignals:
|
|
333
|
+
if signal.name in outsignals:
|
|
334
|
+
signal.set_producer(outsignals[signal.name].producer)
|
|
335
|
+
signal.add_application(producer)
|
|
336
|
+
signal.add_application(consumer)
|
|
337
|
+
signals.append(signal)
|
|
338
|
+
return signals
|
|
339
|
+
|
|
340
|
+
def get_produced_signals(self, producer_name):
|
|
341
|
+
"""Get signals going from producer to consumer
|
|
342
|
+
|
|
343
|
+
This function can be used if you are lacking some objects
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
consumer_name (string): name of the consumer of the signals
|
|
347
|
+
Returns:
|
|
348
|
+
signals (list): Signals sent from producer and received in consumer
|
|
349
|
+
"""
|
|
350
|
+
if producer_name == self.current.name:
|
|
351
|
+
consumer = self.corresponding
|
|
352
|
+
producer = self.current
|
|
353
|
+
elif producer_name == self.corresponding.name:
|
|
354
|
+
consumer = self.current
|
|
355
|
+
producer = self.corresponding
|
|
356
|
+
else:
|
|
357
|
+
LOGGER.error('%s not in [%s, %s]',
|
|
358
|
+
producer_name,
|
|
359
|
+
self.current.name,
|
|
360
|
+
self.corresponding.name)
|
|
361
|
+
return self.get_directional_signals(producer, consumer)
|
|
362
|
+
|
|
363
|
+
def get_consumed_signals(self, consumer_name):
|
|
364
|
+
"""Get signals going from producer to consumer
|
|
365
|
+
|
|
366
|
+
This function can be used if you are lacking some objects
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
consumer_name (string): name of the consumer of the signals
|
|
370
|
+
Returns:
|
|
371
|
+
signals (list): Signals sent from producer and received in consumer
|
|
372
|
+
"""
|
|
373
|
+
if consumer_name == self.current.name:
|
|
374
|
+
consumer = self.current
|
|
375
|
+
producer = self.corresponding
|
|
376
|
+
elif consumer_name == self.corresponding.name:
|
|
377
|
+
consumer = self.corresponding
|
|
378
|
+
producer = self.current
|
|
379
|
+
else:
|
|
380
|
+
LOGGER.error('%s not in [%s, %s]',
|
|
381
|
+
consumer_name,
|
|
382
|
+
self.current.name,
|
|
383
|
+
self.corresponding.name)
|
|
384
|
+
return self.get_directional_signals(producer, consumer)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
class Domain:
|
|
388
|
+
"""Domain with interacting interfaces"""
|
|
389
|
+
def __repr__(self):
|
|
390
|
+
"""String representation for logging and debugging
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
repr (string): Name of the domain, and all clients for that domain
|
|
394
|
+
"""
|
|
395
|
+
return f"<{self.name}: {self.clients}>"
|
|
396
|
+
|
|
397
|
+
def __init__(self):
|
|
398
|
+
"""Initialize the object"""
|
|
399
|
+
self.name = ''
|
|
400
|
+
self.signals = {}
|
|
401
|
+
self.clients = set()
|
|
402
|
+
self._clients = {}
|
|
403
|
+
|
|
404
|
+
def set_name(self, name):
|
|
405
|
+
"""Set the name of the domain
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
name (string): Name of the domain
|
|
409
|
+
"""
|
|
410
|
+
self.name = name
|
|
411
|
+
|
|
412
|
+
def add_interface(self, interface):
|
|
413
|
+
"""Add an interface to a domain
|
|
414
|
+
|
|
415
|
+
Args:
|
|
416
|
+
interface (Interface): Interface object
|
|
417
|
+
"""
|
|
418
|
+
self._process_interface(interface)
|
|
419
|
+
|
|
420
|
+
def _process_interface(self, interface):
|
|
421
|
+
"""Process interface to add signals to the domain
|
|
422
|
+
|
|
423
|
+
Args:
|
|
424
|
+
interface (Interface): Interface object
|
|
425
|
+
"""
|
|
426
|
+
for signal in interface.get_directional_signals(interface.current, interface.corresponding):
|
|
427
|
+
self._process_signal(signal.name, interface.current, interface.corresponding)
|
|
428
|
+
for signal in interface.get_directional_signals(interface.corresponding, interface.current):
|
|
429
|
+
self._process_signal(signal.name, interface.corresponding, interface.current)
|
|
430
|
+
|
|
431
|
+
def _process_signal(self, signal_name, producer, consumer):
|
|
432
|
+
"""Process signal to add to the domain
|
|
433
|
+
|
|
434
|
+
Args:
|
|
435
|
+
signal_name (string): Name of the signal
|
|
436
|
+
consumer (BaseApplication): Consumer application of the signal
|
|
437
|
+
producer (BaseApplication): Producer application of the signal
|
|
438
|
+
"""
|
|
439
|
+
if signal_name not in self.signals:
|
|
440
|
+
self.signals[signal_name] = Signal(signal_name, producer)
|
|
441
|
+
self.signals[signal_name].consumers = consumer.name
|
|
442
|
+
self.signals[signal_name].add_application(producer)
|
|
443
|
+
self.signals[signal_name].add_application(consumer)
|
|
444
|
+
signal = self.signals[signal_name]
|
|
445
|
+
if producer.name not in self.clients:
|
|
446
|
+
self.clients.add(producer.name)
|
|
447
|
+
self._clients[producer.name] = {'producer': [], 'consumer': []}
|
|
448
|
+
self._clients[producer.name]['producer'].append(signal)
|
|
449
|
+
if consumer.name not in self.clients:
|
|
450
|
+
self.clients.add(producer.name)
|
|
451
|
+
self._clients[producer.name] = {'producer': [], 'consumer': []}
|
|
452
|
+
self._clients[producer.name]['consumer'].append(signal)
|
|
453
|
+
signal.consumers = consumer.name
|
|
454
|
+
try:
|
|
455
|
+
signal.producer = producer.name
|
|
456
|
+
except MultipleProducersError as mpe:
|
|
457
|
+
LOGGER.debug(mpe.message)
|
|
458
|
+
|
|
459
|
+
def create_groups(self):
|
|
460
|
+
"""Create groups of signals going from each producer
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
signal_groups (dict): Signal groups
|
|
464
|
+
"""
|
|
465
|
+
signal_groups = {}
|
|
466
|
+
for signal in self.signals.values():
|
|
467
|
+
# Producer is always a set, to handle pass-through signals
|
|
468
|
+
for producer in signal.producer - set(signal_groups.keys()):
|
|
469
|
+
signal_groups[producer] = []
|
|
470
|
+
for producer in signal.producer:
|
|
471
|
+
signal_groups[producer].append(signal)
|
|
472
|
+
return signal_groups
|
|
473
|
+
|
|
474
|
+
def create_selective_groups(self, a_names, b_names):
|
|
475
|
+
"""Create groups for the a_list communicating with the b_names
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
signal_groups (dict): Signal groups
|
|
479
|
+
"""
|
|
480
|
+
signal_groups = {name: {'consumer': [], 'producer': []} for name in a_names}
|
|
481
|
+
for signal in self.signals.values():
|
|
482
|
+
for producer in set(signal.producer):
|
|
483
|
+
if producer in a_names and signal.consumers & b_names:
|
|
484
|
+
signal_groups[producer]['producer'].append(signal)
|
|
485
|
+
for consumer in signal.consumers:
|
|
486
|
+
if consumer in a_names and set(signal.producer) & a_names:
|
|
487
|
+
signal_groups[consumer]['consumer'].append(signal)
|
|
488
|
+
return signal_groups
|
|
489
|
+
|
|
490
|
+
@staticmethod
|
|
491
|
+
def to_yaml(spec, output):
|
|
492
|
+
"""Writes spec to yaml file
|
|
493
|
+
|
|
494
|
+
Args:
|
|
495
|
+
spec (dict): data for the yaml
|
|
496
|
+
output (Path): file to write to
|
|
497
|
+
"""
|
|
498
|
+
with open(output, 'w', encoding="utf-8") as yaml_file:
|
|
499
|
+
yaml = YAML()
|
|
500
|
+
yaml.dump(spec, yaml_file)
|