vss-tools 0.5.0.dev0__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.
- vspec/__init__.py +996 -0
- vspec/loggingconfig.py +22 -0
- vspec/model/__init__.py +7 -0
- vspec/model/constants.py +307 -0
- vspec/model/exceptions.py +29 -0
- vspec/model/vsstree.py +602 -0
- vspec/py.typed +0 -0
- vspec/utils/__init__.py +23 -0
- vspec/utils/idgen_utils.py +97 -0
- vspec/utils/stringstyle.py +25 -0
- vspec/utils/vss2id_val.py +165 -0
- vspec/vspec2vss_config.py +53 -0
- vspec/vspec2x.py +191 -0
- vspec/vss2x.py +38 -0
- vspec/vssexporters/__init__.py +7 -0
- vspec/vssexporters/vss2binary.py +147 -0
- vspec/vssexporters/vss2csv.py +82 -0
- vspec/vssexporters/vss2ddsidl.py +295 -0
- vspec/vssexporters/vss2franca.py +98 -0
- vspec/vssexporters/vss2graphql.py +143 -0
- vspec/vssexporters/vss2id.py +172 -0
- vspec/vssexporters/vss2json.py +116 -0
- vspec/vssexporters/vss2jsonschema.py +159 -0
- vspec/vssexporters/vss2protobuf.py +171 -0
- vspec/vssexporters/vss2yaml.py +122 -0
- vss_tools-0.5.0.dev0.data/scripts/vspec2csv.py +29 -0
- vss_tools-0.5.0.dev0.data/scripts/vspec2ddsidl.py +29 -0
- vss_tools-0.5.0.dev0.data/scripts/vspec2franca.py +29 -0
- vss_tools-0.5.0.dev0.data/scripts/vspec2graphql.py +29 -0
- vss_tools-0.5.0.dev0.data/scripts/vspec2id.py +28 -0
- vss_tools-0.5.0.dev0.data/scripts/vspec2json.py +29 -0
- vss_tools-0.5.0.dev0.data/scripts/vspec2jsonschema.py +29 -0
- vss_tools-0.5.0.dev0.data/scripts/vspec2protobuf.py +29 -0
- vss_tools-0.5.0.dev0.data/scripts/vspec2yaml.py +29 -0
- vss_tools-0.5.0.dev0.dist-info/LICENSE +373 -0
- vss_tools-0.5.0.dev0.dist-info/METADATA +65 -0
- vss_tools-0.5.0.dev0.dist-info/RECORD +39 -0
- vss_tools-0.5.0.dev0.dist-info/WHEEL +5 -0
- vss_tools-0.5.0.dev0.dist-info/top_level.txt +1 -0
vspec/loggingconfig.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2023 Contributors to COVESA
|
|
4
|
+
#
|
|
5
|
+
# This program and the accompanying materials are made available under the
|
|
6
|
+
# terms of the Mozilla Public License 2.0 which is available at
|
|
7
|
+
# https://www.mozilla.org/en-US/MPL/2.0/
|
|
8
|
+
#
|
|
9
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
# Convert vspec files to various other formats
|
|
13
|
+
#
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def initLogging():
|
|
19
|
+
"""
|
|
20
|
+
Initialize logging
|
|
21
|
+
"""
|
|
22
|
+
logging.basicConfig(level=logging.INFO, format='%(levelname)-8s %(message)s')
|
vspec/model/__init__.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Copyright (c) 2021 Contributors to COVESA
|
|
2
|
+
#
|
|
3
|
+
# This program and the accompanying materials are made available under the
|
|
4
|
+
# terms of the Mozilla Public License 2.0 which is available at
|
|
5
|
+
# https://www.mozilla.org/en-US/MPL/2.0/
|
|
6
|
+
#
|
|
7
|
+
# SPDX-License-Identifier: MPL-2.0
|
vspec/model/constants.py
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2021 Contributors to COVESA
|
|
4
|
+
#
|
|
5
|
+
# This program and the accompanying materials are made available under the
|
|
6
|
+
# terms of the Mozilla Public License 2.0 which is available at
|
|
7
|
+
# https://www.mozilla.org/en-US/MPL/2.0/
|
|
8
|
+
#
|
|
9
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
# Constant Types and Mappings
|
|
13
|
+
#
|
|
14
|
+
# noinspection PyPackageRequirements
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
import logging
|
|
17
|
+
import re
|
|
18
|
+
import sys
|
|
19
|
+
from collections import abc
|
|
20
|
+
from collections.abc import Sequence
|
|
21
|
+
from enum import Enum, EnumMeta
|
|
22
|
+
from typing import TextIO, TypeVar
|
|
23
|
+
|
|
24
|
+
import yaml
|
|
25
|
+
|
|
26
|
+
NON_ALPHANUMERIC_WORD = re.compile("[^A-Za-z0-9]+")
|
|
27
|
+
|
|
28
|
+
T = TypeVar("T")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class VSSUnit(str):
|
|
32
|
+
"""String subclass for storing unit information."""
|
|
33
|
+
|
|
34
|
+
id: str # Typically abbreviation like "V"
|
|
35
|
+
unit: str | None = None # Typically full name like "Volt"
|
|
36
|
+
definition: str | None = None
|
|
37
|
+
quantity: str | None = None # Typically quantity, like "Voltage"
|
|
38
|
+
allowed_datatypes: list[str] | None = None # Typically quantity, like "Voltage"
|
|
39
|
+
|
|
40
|
+
def __new__(
|
|
41
|
+
cls,
|
|
42
|
+
id: str,
|
|
43
|
+
unit: str | None = None,
|
|
44
|
+
definition: str | None = None,
|
|
45
|
+
quantity: str | None = None,
|
|
46
|
+
allowed_datatypes: list[str] | None = None,
|
|
47
|
+
) -> VSSUnit:
|
|
48
|
+
self = super().__new__(cls, id)
|
|
49
|
+
self.id = id
|
|
50
|
+
self.unit = unit
|
|
51
|
+
self.definition = definition
|
|
52
|
+
self.quantity = quantity
|
|
53
|
+
self.allowed_datatypes = allowed_datatypes
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def value(self):
|
|
58
|
+
return self
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class VSSQuantity(str):
|
|
62
|
+
"""String subclass for storing quantity information."""
|
|
63
|
+
|
|
64
|
+
id: str # Identifier preferably taken from a standard, like ISO 80000
|
|
65
|
+
definition: str # Explanation of quantity, for example reference to standard
|
|
66
|
+
remark: str | None = None # remark as defined in for example ISO 80000
|
|
67
|
+
comment: str | None = None
|
|
68
|
+
|
|
69
|
+
def __new__(
|
|
70
|
+
cls,
|
|
71
|
+
id: str,
|
|
72
|
+
definition: str,
|
|
73
|
+
remark: str | None = None,
|
|
74
|
+
comment: str | None = None,
|
|
75
|
+
) -> VSSQuantity:
|
|
76
|
+
self = super().__new__(cls, id)
|
|
77
|
+
self.id = id
|
|
78
|
+
self.definition = definition
|
|
79
|
+
self.remark = remark
|
|
80
|
+
self.comment = comment
|
|
81
|
+
return self
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def value(self):
|
|
85
|
+
return self
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class EnumMetaWithReverseLookup(EnumMeta):
|
|
89
|
+
"""This class extends EnumMeta and adds:
|
|
90
|
+
- from_str(str): reverse lookup
|
|
91
|
+
- values(): sequence of values
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def __new__(typ, *args, **kwargs):
|
|
95
|
+
cls = super().__new__(typ, *args, **kwargs)
|
|
96
|
+
if not hasattr(cls, "__reverse_lookup__"):
|
|
97
|
+
cls.__reverse_lookup__ = {v.value: v for v in cls.__members__.values()}
|
|
98
|
+
if not hasattr(cls, "__values__"):
|
|
99
|
+
cls.__values__ = tuple(v.value for v in cls.__members__.values())
|
|
100
|
+
return cls
|
|
101
|
+
|
|
102
|
+
def from_str(cls: type[T], value: str) -> T:
|
|
103
|
+
return cls.__reverse_lookup__[value] # type: ignore[attr-defined]
|
|
104
|
+
|
|
105
|
+
def values(cls: type[T]) -> Sequence[str]:
|
|
106
|
+
return cls.__values__ # type: ignore[attr-defined]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class VSSType(Enum, metaclass=EnumMetaWithReverseLookup):
|
|
110
|
+
BRANCH = "branch"
|
|
111
|
+
ATTRIBUTE = "attribute"
|
|
112
|
+
SENSOR = "sensor"
|
|
113
|
+
ACTUATOR = "actuator"
|
|
114
|
+
STRUCT = "struct"
|
|
115
|
+
PROPERTY = "property"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class VSSDataType(Enum, metaclass=EnumMetaWithReverseLookup):
|
|
119
|
+
INT8 = "int8"
|
|
120
|
+
UINT8 = "uint8"
|
|
121
|
+
INT16 = "int16"
|
|
122
|
+
UINT16 = "uint16"
|
|
123
|
+
INT32 = "int32"
|
|
124
|
+
UINT32 = "uint32"
|
|
125
|
+
INT64 = "int64"
|
|
126
|
+
UINT64 = "uint64"
|
|
127
|
+
BOOLEAN = "boolean"
|
|
128
|
+
FLOAT = "float"
|
|
129
|
+
DOUBLE = "double"
|
|
130
|
+
STRING = "string"
|
|
131
|
+
INT8_ARRAY = "int8[]"
|
|
132
|
+
UINT8_ARRAY = "uint8[]"
|
|
133
|
+
INT16_ARRAY = "int16[]"
|
|
134
|
+
UINT16_ARRAY = "uint16[]"
|
|
135
|
+
INT32_ARRAY = "int32[]"
|
|
136
|
+
UINT32_ARRAY = "uint32[]"
|
|
137
|
+
INT64_ARRAY = "int64[]"
|
|
138
|
+
UINT64_ARRAY = "uint64[]"
|
|
139
|
+
BOOLEAN_ARRAY = "boolean[]"
|
|
140
|
+
FLOAT_ARRAY = "float[]"
|
|
141
|
+
DOUBLE_ARRAY = "double[]"
|
|
142
|
+
STRING_ARRAY = "string[]"
|
|
143
|
+
|
|
144
|
+
@classmethod
|
|
145
|
+
def is_numeric(cls, datatype):
|
|
146
|
+
"""
|
|
147
|
+
Return true if this datatype accepts numerical values
|
|
148
|
+
"""
|
|
149
|
+
if datatype in [
|
|
150
|
+
VSSDataType.STRING,
|
|
151
|
+
VSSDataType.STRING_ARRAY,
|
|
152
|
+
VSSDataType.BOOLEAN,
|
|
153
|
+
VSSDataType.BOOLEAN_ARRAY,
|
|
154
|
+
]:
|
|
155
|
+
return False
|
|
156
|
+
return True
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class VSSUnitCollection:
|
|
160
|
+
units: dict[str, VSSUnit] = dict()
|
|
161
|
+
|
|
162
|
+
@staticmethod
|
|
163
|
+
def get_config_dict(yaml_file: TextIO, key: str) -> dict[str, dict[str, str]]:
|
|
164
|
+
yaml_config = yaml.safe_load(yaml_file)
|
|
165
|
+
if (len(yaml_config) == 1) and (key in yaml_config):
|
|
166
|
+
# Old style unit file
|
|
167
|
+
configs = yaml_config.get(key, {})
|
|
168
|
+
else:
|
|
169
|
+
# New style unit file
|
|
170
|
+
configs = yaml_config
|
|
171
|
+
return configs
|
|
172
|
+
|
|
173
|
+
@classmethod
|
|
174
|
+
def reset_units(cls):
|
|
175
|
+
cls.units = dict()
|
|
176
|
+
|
|
177
|
+
@classmethod
|
|
178
|
+
def load_config_file(cls, config_file: str) -> int:
|
|
179
|
+
added_configs = 0
|
|
180
|
+
with open(config_file) as my_yaml_file:
|
|
181
|
+
my_units = cls.get_config_dict(my_yaml_file, "units")
|
|
182
|
+
added_configs = len(my_units)
|
|
183
|
+
for k, v in my_units.items():
|
|
184
|
+
unit = k
|
|
185
|
+
if "unit" in v:
|
|
186
|
+
unit = v["unit"]
|
|
187
|
+
elif "label" in v:
|
|
188
|
+
# Old syntax
|
|
189
|
+
unit = v["label"]
|
|
190
|
+
definition = None
|
|
191
|
+
if "definition" in v:
|
|
192
|
+
definition = v["definition"]
|
|
193
|
+
elif "description" in v:
|
|
194
|
+
# Old syntax
|
|
195
|
+
definition = v["description"]
|
|
196
|
+
|
|
197
|
+
quantity = None
|
|
198
|
+
if "quantity" in v:
|
|
199
|
+
quantity = v["quantity"]
|
|
200
|
+
elif "domain" in v:
|
|
201
|
+
# Old syntax
|
|
202
|
+
quantity = v["domain"]
|
|
203
|
+
else:
|
|
204
|
+
logging.error("No quantity (domain) found for unit %s", k)
|
|
205
|
+
sys.exit(-1)
|
|
206
|
+
|
|
207
|
+
if (VSSQuantityCollection.nbr_quantities() > 0) and (
|
|
208
|
+
VSSQuantityCollection.get_quantity(quantity) is None
|
|
209
|
+
):
|
|
210
|
+
# Only give info on first occurance and only if quantities exist at all
|
|
211
|
+
logging.info(
|
|
212
|
+
"Quantity %s used by unit %s has not been defined", quantity, k
|
|
213
|
+
)
|
|
214
|
+
VSSQuantityCollection.add_quantity(quantity)
|
|
215
|
+
|
|
216
|
+
allowed_datatypes = None
|
|
217
|
+
|
|
218
|
+
if "allowed_datatypes" in v:
|
|
219
|
+
allowed_datatypes = []
|
|
220
|
+
for datatype in v["allowed_datatypes"]:
|
|
221
|
+
allowed_datatypes.append(datatype)
|
|
222
|
+
if datatype == "numeric":
|
|
223
|
+
# Symbolic type for all numeric types
|
|
224
|
+
continue
|
|
225
|
+
try:
|
|
226
|
+
VSSDataType.from_str(datatype)
|
|
227
|
+
except KeyError:
|
|
228
|
+
logging.error(
|
|
229
|
+
"Unknown datatype %s in unit definition", datatype
|
|
230
|
+
)
|
|
231
|
+
sys.exit(-1)
|
|
232
|
+
|
|
233
|
+
unit_node = VSSUnit(k, unit, definition, quantity, allowed_datatypes)
|
|
234
|
+
if k in cls.units:
|
|
235
|
+
logging.warning("Redefinition of unit %s", k)
|
|
236
|
+
cls.units[k] = unit_node
|
|
237
|
+
return added_configs
|
|
238
|
+
|
|
239
|
+
@classmethod
|
|
240
|
+
def get_unit(cls, id: str) -> VSSUnit | None:
|
|
241
|
+
if id in cls.units:
|
|
242
|
+
return cls.units[id]
|
|
243
|
+
else:
|
|
244
|
+
return None
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class VSSQuantityCollection:
|
|
248
|
+
quantities: dict[str, VSSQuantity] = dict()
|
|
249
|
+
|
|
250
|
+
@classmethod
|
|
251
|
+
def reset_quantities(cls):
|
|
252
|
+
cls.quantities = dict()
|
|
253
|
+
|
|
254
|
+
@classmethod
|
|
255
|
+
def load_config_file(cls, config_file: str) -> int:
|
|
256
|
+
added_quantities = 0
|
|
257
|
+
with open(config_file) as my_yaml_file:
|
|
258
|
+
my_quantities = yaml.safe_load(my_yaml_file)
|
|
259
|
+
added_quantities = len(my_quantities)
|
|
260
|
+
for k, v in my_quantities.items():
|
|
261
|
+
if isinstance(v, abc.Mapping) and "definition" in v:
|
|
262
|
+
definition = v["definition"]
|
|
263
|
+
else:
|
|
264
|
+
logging.error("No definition found for quantity %s", k)
|
|
265
|
+
sys.exit(-1)
|
|
266
|
+
remark = None
|
|
267
|
+
if "remark" in v:
|
|
268
|
+
remark = v["remark"]
|
|
269
|
+
comment = None
|
|
270
|
+
if "comment" in v:
|
|
271
|
+
comment = v["comment"]
|
|
272
|
+
|
|
273
|
+
quantity_node = VSSQuantity(k, definition, remark, comment)
|
|
274
|
+
if k in cls.quantities:
|
|
275
|
+
logging.warning("Redefinition of quantity %s", k)
|
|
276
|
+
cls.quantities[k] = quantity_node
|
|
277
|
+
return added_quantities
|
|
278
|
+
|
|
279
|
+
@classmethod
|
|
280
|
+
def get_quantity(cls, id: str) -> VSSQuantity | None:
|
|
281
|
+
if id in cls.quantities:
|
|
282
|
+
return cls.quantities[id]
|
|
283
|
+
else:
|
|
284
|
+
return None
|
|
285
|
+
|
|
286
|
+
@classmethod
|
|
287
|
+
def nbr_quantities(cls) -> int:
|
|
288
|
+
return len(cls.quantities)
|
|
289
|
+
|
|
290
|
+
@classmethod
|
|
291
|
+
def add_quantity(cls, id: str) -> None:
|
|
292
|
+
if id not in cls.quantities:
|
|
293
|
+
quantity_node = VSSQuantity(id, "Automatically generated quantity")
|
|
294
|
+
cls.quantities[id] = quantity_node
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
class VSSTreeType(Enum, metaclass=EnumMetaWithReverseLookup):
|
|
298
|
+
SIGNAL_TREE = "signal_tree"
|
|
299
|
+
DATA_TYPE_TREE = "data_type_tree"
|
|
300
|
+
|
|
301
|
+
def available_types(self):
|
|
302
|
+
if self.value == "signal_tree":
|
|
303
|
+
available_types = {"branch", "sensor", "actuator", "attribute"}
|
|
304
|
+
else:
|
|
305
|
+
available_types = {"branch", "struct", "property"}
|
|
306
|
+
|
|
307
|
+
return available_types
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2023 Contributors to COVESA
|
|
4
|
+
#
|
|
5
|
+
# This program and the accompanying materials are made available under the
|
|
6
|
+
# terms of the Mozilla Public License 2.0 which is available at
|
|
7
|
+
# https://www.mozilla.org/en-US/MPL/2.0/
|
|
8
|
+
#
|
|
9
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
10
|
+
|
|
11
|
+
class NameStyleValidationException(Exception):
|
|
12
|
+
def __init__(self, message):
|
|
13
|
+
|
|
14
|
+
# Call the base class constructor with the parameters it needs
|
|
15
|
+
super().__init__(message)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ImpossibleMergeException(Exception):
|
|
19
|
+
def __init__(self, message):
|
|
20
|
+
|
|
21
|
+
# Call the base class constructor with the parameters it needs
|
|
22
|
+
super().__init__(message)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class IncompleteElementException(Exception):
|
|
26
|
+
def __init__(self, message):
|
|
27
|
+
|
|
28
|
+
# Call the base class constructor with the parameters it needs
|
|
29
|
+
super().__init__(message)
|