aas-standard-parser 0.3.2__tar.gz → 0.3.4__tar.gz
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.
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/PKG-INFO +1 -1
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/__init__.py +6 -4
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/aid_parser.py +54 -40
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/aimc_parser.py +3 -3
- aas_standard_parser-0.3.4/aas_standard_parser/classes/descriptor_json_helper_classes.py +12 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/collection_helpers.py +3 -0
- aas_standard_parser-0.3.4/aas_standard_parser/descriptor_json_helper.py +64 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/utils.py +23 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser.egg-info/PKG-INFO +1 -1
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser.egg-info/SOURCES.txt +5 -2
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/pyproject.toml +1 -1
- aas_standard_parser-0.3.4/tests/test_descriptor_json_helper.py +18 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/LICENSE +0 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/README.md +0 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/aas_parser.py +0 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/classes/aimc_parser_classes.py +0 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/demo/demo_process.py +0 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/demo/logging_handler.py +0 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/reference_helpers.py +0 -0
- /aas_standard_parser-0.3.2/aas_standard_parser/submodel_json_parser.py → /aas_standard_parser-0.3.4/aas_standard_parser/submodel_json_helper.py +0 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/submodel_parser.py +0 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/version_check.py +0 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser.egg-info/dependency_links.txt +0 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser.egg-info/requires.txt +0 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser.egg-info/top_level.txt +0 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/setup.cfg +0 -0
- {aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/tests/test_aimc_parser.py +0 -0
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
"""AAS Standard parser Package."""
|
|
2
2
|
|
|
3
3
|
import importlib.metadata
|
|
4
|
-
from datetime import
|
|
4
|
+
from datetime import UTC, datetime
|
|
5
5
|
|
|
6
6
|
from aas_standard_parser import (
|
|
7
7
|
aas_parser,
|
|
8
8
|
aid_parser,
|
|
9
9
|
aimc_parser,
|
|
10
10
|
collection_helpers,
|
|
11
|
+
descriptor_json_helper,
|
|
11
12
|
reference_helpers,
|
|
12
|
-
|
|
13
|
+
submodel_json_helper,
|
|
13
14
|
submodel_parser,
|
|
14
15
|
utils,
|
|
15
16
|
)
|
|
16
17
|
from aas_standard_parser.aid_parser import AIDParser
|
|
17
18
|
from aas_standard_parser.version_check import check_for_update
|
|
18
19
|
|
|
19
|
-
__copyright__ = f"Copyright (C) {datetime.now(tz=
|
|
20
|
+
__copyright__ = f"Copyright (C) {datetime.now(tz=UTC).year} Fluid 4.0. All rights reserved."
|
|
20
21
|
__author__ = "Daniel Klein, Celina Adelhardt, Tom Gneuß"
|
|
21
22
|
|
|
22
23
|
try:
|
|
@@ -37,8 +38,9 @@ __all__ = [
|
|
|
37
38
|
"aid_parser",
|
|
38
39
|
"aimc_parser",
|
|
39
40
|
"collection_helpers",
|
|
41
|
+
"descriptor_json_helper",
|
|
40
42
|
"reference_helpers",
|
|
41
|
-
"
|
|
43
|
+
"submodel_json_helper",
|
|
42
44
|
"submodel_parser",
|
|
43
45
|
"utils",
|
|
44
46
|
]
|
|
@@ -1,76 +1,88 @@
|
|
|
1
1
|
"""This module provides functions to parse AID Submodels and extract MQTT interface descriptions."""
|
|
2
2
|
|
|
3
3
|
import base64
|
|
4
|
-
from typing import Dict, List
|
|
5
4
|
|
|
6
|
-
from basyx.aas
|
|
7
|
-
Property,
|
|
8
|
-
SubmodelElement,
|
|
9
|
-
SubmodelElementCollection,
|
|
10
|
-
SubmodelElementList,
|
|
11
|
-
)
|
|
5
|
+
from basyx.aas import model
|
|
12
6
|
|
|
13
7
|
from aas_standard_parser.collection_helpers import find_all_by_semantic_id, find_by_id_short, find_by_semantic_id
|
|
14
8
|
|
|
15
9
|
|
|
16
10
|
class IProtocolBinding:
|
|
11
|
+
"""Interface for protocol binding details."""
|
|
12
|
+
|
|
17
13
|
def __init__(self):
|
|
18
|
-
|
|
14
|
+
"""Initialize IProtocolBinding."""
|
|
19
15
|
|
|
20
16
|
|
|
21
17
|
class HttpProtocolBinding(IProtocolBinding):
|
|
22
|
-
|
|
18
|
+
"""HTTP protocol binding details."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, method_name: str, headers: dict[str, str]):
|
|
21
|
+
"""Initialize HttpProtocolBinding."""
|
|
23
22
|
super().__init__()
|
|
24
23
|
self.method_name = method_name
|
|
25
24
|
self.headers = headers
|
|
26
25
|
|
|
27
26
|
|
|
28
27
|
class PropertyDetails:
|
|
29
|
-
|
|
28
|
+
"""Details of a property in an AID interface."""
|
|
29
|
+
|
|
30
|
+
def __init__(self, href: str, keys: list[str], protocol_binding: IProtocolBinding = None):
|
|
31
|
+
"""Initialize PropertyDetails."""
|
|
30
32
|
self.href = href
|
|
31
33
|
self.keys = keys
|
|
32
34
|
self.protocol_binding = protocol_binding
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
class IAuthenticationDetails:
|
|
38
|
+
"""Interface for authentication details."""
|
|
39
|
+
|
|
36
40
|
def __init__(self):
|
|
41
|
+
"""Initialize IAuthenticationDetails."""
|
|
37
42
|
# TODO: different implementations for different AID versions
|
|
38
|
-
pass
|
|
39
43
|
|
|
40
44
|
|
|
41
45
|
class BasicAuthenticationDetails(IAuthenticationDetails):
|
|
46
|
+
"""Basic authentication details."""
|
|
47
|
+
|
|
42
48
|
def __init__(self, user: str, password: str):
|
|
49
|
+
"""Initialize BasicAuthenticationDetails."""
|
|
43
50
|
self.user = user
|
|
44
51
|
self.password = password
|
|
45
52
|
super().__init__()
|
|
46
53
|
|
|
47
54
|
|
|
48
55
|
class NoAuthenticationDetails(IAuthenticationDetails):
|
|
56
|
+
"""No authentication details."""
|
|
57
|
+
|
|
49
58
|
def __init__(self):
|
|
59
|
+
"""Initialize NoAuthenticationDetails."""
|
|
50
60
|
super().__init__()
|
|
51
61
|
|
|
52
62
|
|
|
53
63
|
class AIDParser:
|
|
64
|
+
"""Parser for Asset Interface Descriptions (AID) Submodels."""
|
|
65
|
+
|
|
54
66
|
def __init__(self):
|
|
55
|
-
|
|
67
|
+
"""Initialize the AIDParser."""
|
|
56
68
|
|
|
57
|
-
def parse_base(self, aid_interface: SubmodelElementCollection) -> str:
|
|
69
|
+
def parse_base(self, aid_interface: model.SubmodelElementCollection) -> str:
|
|
58
70
|
"""Get the base address (EndpointMetadata.base) from a SMC describing an interface in the AID."""
|
|
59
|
-
|
|
60
|
-
endpoint_metadata: SubmodelElementCollection | None = find_by_semantic_id(
|
|
71
|
+
endpoint_metadata: model.SubmodelElementCollection | None = find_by_semantic_id(
|
|
61
72
|
aid_interface.value, "https://admin-shell.io/idta/AssetInterfacesDescription/1/0/EndpointMetadata"
|
|
62
73
|
)
|
|
63
74
|
if endpoint_metadata is None:
|
|
64
75
|
raise ValueError(f"'EndpointMetadata' SMC not found in the provided '{aid_interface.id_short}' SMC.")
|
|
65
76
|
|
|
66
|
-
base: Property | None = find_by_semantic_id(endpoint_metadata.value, "https://www.w3.org/2019/wot/td#baseURI")
|
|
77
|
+
base: model.Property | None = find_by_semantic_id(endpoint_metadata.value, "https://www.w3.org/2019/wot/td#baseURI")
|
|
67
78
|
if base is None:
|
|
68
79
|
raise ValueError("'base' Property not found in 'EndpointMetadata' SMC.")
|
|
69
80
|
|
|
70
81
|
return base.value
|
|
71
82
|
|
|
72
|
-
def parse_properties(self, aid_interface: SubmodelElementCollection) ->
|
|
83
|
+
def parse_properties(self, aid_interface: model.SubmodelElementCollection) -> dict[str, PropertyDetails]:
|
|
73
84
|
"""Find all first-level and nested properties in a provided SMC describing one interface in the AID.
|
|
85
|
+
|
|
74
86
|
Map each property (either top-level or nested) to the according 'href' attribute.
|
|
75
87
|
Nested properties are further mapped to the hierarchical list of keys
|
|
76
88
|
that are necessary to extract their value from the payload of the interface.
|
|
@@ -78,32 +90,32 @@ class AIDParser:
|
|
|
78
90
|
:param aid_interface: An SMC describing an interface in the AID.
|
|
79
91
|
:return: A dictionary mapping each property (represented by its idShort-path) to PropertyDetails.
|
|
80
92
|
"""
|
|
81
|
-
mapping:
|
|
93
|
+
mapping: dict[str, PropertyDetails] = {}
|
|
82
94
|
|
|
83
|
-
interaction_metadata: SubmodelElementCollection | None = find_by_semantic_id(
|
|
95
|
+
interaction_metadata: model.SubmodelElementCollection | None = find_by_semantic_id(
|
|
84
96
|
aid_interface.value, "https://admin-shell.io/idta/AssetInterfacesDescription/1/0/InteractionMetadata"
|
|
85
97
|
)
|
|
86
98
|
if interaction_metadata is None:
|
|
87
99
|
raise ValueError(f"'InteractionMetadata' SMC not found in the provided '{aid_interface.id_short}' SMC.")
|
|
88
100
|
|
|
89
|
-
properties: SubmodelElementCollection | None = find_by_semantic_id(
|
|
101
|
+
properties: model.SubmodelElementCollection | None = find_by_semantic_id(
|
|
90
102
|
interaction_metadata.value, "https://www.w3.org/2019/wot/td#PropertyAffordance"
|
|
91
103
|
)
|
|
92
104
|
if properties is None:
|
|
93
105
|
raise ValueError("'properties' SMC not found in 'InteractionMetadata' SMC.")
|
|
94
106
|
|
|
95
|
-
fl_properties:
|
|
107
|
+
fl_properties: list[model.SubmodelElement] = find_all_by_semantic_id(
|
|
96
108
|
properties.value, "https://admin-shell.io/idta/AssetInterfacesDescription/1/0/PropertyDefinition"
|
|
97
109
|
)
|
|
98
110
|
if fl_properties is None:
|
|
99
|
-
print(
|
|
111
|
+
print("WARN: No first-level 'property' SMC not found in 'properties' SMC.")
|
|
100
112
|
return {}
|
|
101
113
|
|
|
102
114
|
def traverse_property(
|
|
103
|
-
smc: SubmodelElementCollection,
|
|
115
|
+
smc: model.SubmodelElementCollection,
|
|
104
116
|
parent_path: str,
|
|
105
117
|
href: str,
|
|
106
|
-
key_path:
|
|
118
|
+
key_path: list[str | int],
|
|
107
119
|
is_items=False,
|
|
108
120
|
idx=None,
|
|
109
121
|
is_top_level=False,
|
|
@@ -151,13 +163,13 @@ class AIDParser:
|
|
|
151
163
|
"https://www.w3.org/2019/wot/json-schema#properties",
|
|
152
164
|
"https://www.w3.org/2019/wot/json-schema#items",
|
|
153
165
|
]:
|
|
154
|
-
nested_group: SubmodelElementCollection | None = find_by_semantic_id(smc.value, nested_sem_id)
|
|
166
|
+
nested_group: model.SubmodelElementCollection | None = find_by_semantic_id(smc.value, nested_sem_id)
|
|
155
167
|
if nested_group:
|
|
156
168
|
# attach the name of that SMC ("items" or "properties" or similar) to the key_path
|
|
157
169
|
full_path += "." + nested_group.id_short
|
|
158
170
|
|
|
159
171
|
# find all nested properties/items by semantic-ID
|
|
160
|
-
nested_properties:
|
|
172
|
+
nested_properties: list[model.SubmodelElement] = find_all_by_semantic_id(
|
|
161
173
|
nested_group.value, "https://www.w3.org/2019/wot/json-schema#propertyName"
|
|
162
174
|
)
|
|
163
175
|
|
|
@@ -171,11 +183,11 @@ class AIDParser:
|
|
|
171
183
|
|
|
172
184
|
# process all first-level properties
|
|
173
185
|
for fl_property in fl_properties: # type: SubmodelElementCollection
|
|
174
|
-
forms: SubmodelElementCollection | None = find_by_semantic_id(fl_property.value, "https://www.w3.org/2019/wot/td#hasForm")
|
|
186
|
+
forms: model.SubmodelElementCollection | None = find_by_semantic_id(fl_property.value, "https://www.w3.org/2019/wot/td#hasForm")
|
|
175
187
|
if forms is None:
|
|
176
188
|
raise ValueError(f"'forms' SMC not found in '{fl_property.id_short}' SMC.")
|
|
177
189
|
|
|
178
|
-
href: Property | None = find_by_semantic_id(forms.value, "https://www.w3.org/2019/wot/hypermedia#hasTarget")
|
|
190
|
+
href: model.Property | None = find_by_semantic_id(forms.value, "https://www.w3.org/2019/wot/hypermedia#hasTarget")
|
|
179
191
|
if href is None:
|
|
180
192
|
raise ValueError("'href' Property not found in 'forms' SMC.")
|
|
181
193
|
|
|
@@ -192,14 +204,14 @@ class AIDParser:
|
|
|
192
204
|
protocol_binding: IProtocolBinding = None
|
|
193
205
|
|
|
194
206
|
# ... try HTTP ("htv_methodName" must be present)
|
|
195
|
-
htv_method_name: Property | None = find_by_semantic_id(forms.value, "https://www.w3.org/2011/http#methodName")
|
|
207
|
+
htv_method_name: model.Property | None = find_by_semantic_id(forms.value, "https://www.w3.org/2011/http#methodName")
|
|
196
208
|
if htv_method_name is not None:
|
|
197
209
|
protocol_binding: HttpProtocolBinding = HttpProtocolBinding(htv_method_name.value, {})
|
|
198
|
-
htv_headers: SubmodelElementCollection | None = find_by_semantic_id(forms.value, "https://www.w3.org/2011/http#headers")
|
|
210
|
+
htv_headers: model.SubmodelElementCollection | None = find_by_semantic_id(forms.value, "https://www.w3.org/2011/http#headers")
|
|
199
211
|
if htv_headers is not None:
|
|
200
|
-
for header in htv_headers.value: # type: SubmodelElementCollection
|
|
201
|
-
htv_field_name: Property | None = find_by_semantic_id(header.value, "https://www.w3.org/2011/http#fieldName")
|
|
202
|
-
htv_field_value: Property | None = find_by_semantic_id(header.value, "https://www.w3.org/2011/http#fieldValue")
|
|
212
|
+
for header in htv_headers.value: # type: ignore # type: SubmodelElementCollection
|
|
213
|
+
htv_field_name: model.Property | None = find_by_semantic_id(header.value, "https://www.w3.org/2011/http#fieldName")
|
|
214
|
+
htv_field_value: model.Property | None = find_by_semantic_id(header.value, "https://www.w3.org/2011/http#fieldValue")
|
|
203
215
|
protocol_binding.headers[htv_field_name.value] = htv_field_value.value
|
|
204
216
|
|
|
205
217
|
# TODO: the other protocols
|
|
@@ -220,19 +232,21 @@ class AIDParser:
|
|
|
220
232
|
|
|
221
233
|
return mapping
|
|
222
234
|
|
|
223
|
-
def parse_security(self, aid_interface: SubmodelElementCollection) -> IAuthenticationDetails:
|
|
235
|
+
def parse_security(self, aid_interface: model.SubmodelElementCollection) -> IAuthenticationDetails:
|
|
224
236
|
"""Extract the authentication details (EndpointMetadata.security) from the provided interface in the AID.
|
|
225
237
|
|
|
226
238
|
:param aid_interface: An SMC describing an interface in the AID.
|
|
227
239
|
:return: A subtype of IAuthenticationDetails with details depending on the specified authentication method for the interface.
|
|
228
240
|
"""
|
|
229
|
-
endpoint_metadata: SubmodelElementCollection | None = find_by_semantic_id(
|
|
241
|
+
endpoint_metadata: model.SubmodelElementCollection | None = find_by_semantic_id(
|
|
230
242
|
aid_interface.value, "https://admin-shell.io/idta/AssetInterfacesDescription/1/0/EndpointMetadata"
|
|
231
243
|
)
|
|
232
244
|
if endpoint_metadata is None:
|
|
233
245
|
raise ValueError(f"'EndpointMetadata' SMC not found in the provided '{aid_interface.id_short}' SMC.")
|
|
234
246
|
|
|
235
|
-
security: SubmodelElementList | None = find_by_semantic_id(
|
|
247
|
+
security: model.SubmodelElementList | None = find_by_semantic_id(
|
|
248
|
+
endpoint_metadata.value, "https://www.w3.org/2019/wot/td#hasSecurityConfiguration"
|
|
249
|
+
)
|
|
236
250
|
if security is None:
|
|
237
251
|
raise ValueError("'security' SML not found in 'EndpointMetadata' SMC.")
|
|
238
252
|
|
|
@@ -244,19 +258,19 @@ class AIDParser:
|
|
|
244
258
|
sc_idshort = security.value[0].value.key[-1].value
|
|
245
259
|
|
|
246
260
|
# get the securityDefinitions SMC
|
|
247
|
-
security_definitions: SubmodelElementCollection | None = find_by_semantic_id(
|
|
261
|
+
security_definitions: model.SubmodelElementCollection | None = find_by_semantic_id(
|
|
248
262
|
endpoint_metadata.value, "https://www.w3.org/2019/wot/td#definesSecurityScheme"
|
|
249
263
|
)
|
|
250
264
|
if security_definitions is None:
|
|
251
265
|
raise ValueError("'securityDefinitions' SMC not found in 'EndpointMetadata' SMC.")
|
|
252
266
|
|
|
253
267
|
# find the security scheme SMC with the same idShort as mentioned in the reference "sc"
|
|
254
|
-
referenced_security: SubmodelElementCollection | None = find_by_id_short(security_definitions.value, sc_idshort)
|
|
268
|
+
referenced_security: model.SubmodelElementCollection | None = find_by_id_short(security_definitions.value, sc_idshort)
|
|
255
269
|
if referenced_security is None:
|
|
256
270
|
raise ValueError(f"Referenced security scheme '{sc_idshort}' SMC not found in 'securityDefinitions' SMC")
|
|
257
271
|
|
|
258
272
|
# get the name of the security scheme
|
|
259
|
-
scheme: Property | None = find_by_semantic_id(referenced_security.value, "https://www.w3.org/2019/wot/security#SecurityScheme")
|
|
273
|
+
scheme: model.Property | None = find_by_semantic_id(referenced_security.value, "https://www.w3.org/2019/wot/security#SecurityScheme")
|
|
260
274
|
if scheme is None:
|
|
261
275
|
raise ValueError(f"'scheme' Property not found in referenced security scheme '{sc_idshort}' SMC.")
|
|
262
276
|
|
|
@@ -267,7 +281,7 @@ class AIDParser:
|
|
|
267
281
|
auth_details = NoAuthenticationDetails()
|
|
268
282
|
|
|
269
283
|
case "basic":
|
|
270
|
-
basic_sc_name: Property | None = find_by_semantic_id(referenced_security.value, "https://www.w3.org/2019/wot/security#name")
|
|
284
|
+
basic_sc_name: model.Property | None = find_by_semantic_id(referenced_security.value, "https://www.w3.org/2019/wot/security#name")
|
|
271
285
|
if basic_sc_name is None:
|
|
272
286
|
raise ValueError("'name' Property not found in 'basic_sc' SMC")
|
|
273
287
|
|
|
@@ -53,11 +53,11 @@ def get_mapping_configuration_elements(aimc_submodel: model.Submodel) -> list[mo
|
|
|
53
53
|
return mapping_configurations
|
|
54
54
|
|
|
55
55
|
|
|
56
|
-
def parse_mapping_configurations(aimc_submodel: model.Submodel) -> MappingConfigurations:
|
|
56
|
+
def parse_mapping_configurations(aimc_submodel: model.Submodel) -> MappingConfigurations | None:
|
|
57
57
|
"""Parse all mapping configurations in the AIMC submodel.
|
|
58
58
|
|
|
59
59
|
:param aimc_submodel: The AIMC submodel to parse mapping configurations from.
|
|
60
|
-
:return: A list of parsed mapping configurations.
|
|
60
|
+
:return: A list of parsed mapping configurations or None if parsing failed.
|
|
61
61
|
"""
|
|
62
62
|
logger.info("Parse mapping configurations from AIMC submodel.")
|
|
63
63
|
|
|
@@ -67,7 +67,7 @@ def parse_mapping_configurations(aimc_submodel: model.Submodel) -> MappingConfig
|
|
|
67
67
|
mapping_configurations_elements = get_mapping_configuration_elements(aimc_submodel)
|
|
68
68
|
if mapping_configurations_elements is None:
|
|
69
69
|
logger.error("No mapping configuration elements found in AIMC submodel.")
|
|
70
|
-
return
|
|
70
|
+
return None
|
|
71
71
|
|
|
72
72
|
# parse each mapping configuration element
|
|
73
73
|
for mc_element in mapping_configurations_elements:
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Helper classes for descriptor JSON parsing."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class EndPointHrefData:
|
|
5
|
+
"""Class to represent an endpoint href data structure."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, href: str):
|
|
8
|
+
"""Initialize the EndPointHrefData with the given href."""
|
|
9
|
+
self.href: str = href
|
|
10
|
+
self.base_url: str = ""
|
|
11
|
+
self.identifier: str = ""
|
|
12
|
+
self.identifier_encoded: str = ""
|
{aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/collection_helpers.py
RENAMED
|
@@ -87,5 +87,8 @@ def contains_supplemental_semantic_id(element: SubmodelElement, semantic_id_valu
|
|
|
87
87
|
:param semantic_id_value: The supplemental semantic ID value to search for.
|
|
88
88
|
:return: True if the element contains the supplemental semantic ID, False otherwise.
|
|
89
89
|
"""
|
|
90
|
+
if element.supplemental_semantic_id is None or len(element.supplemental_semantic_id.key) == 0:
|
|
91
|
+
return False
|
|
92
|
+
|
|
90
93
|
reference: Reference = ExternalReference((Key(type_=KeyTypes.GLOBAL_REFERENCE, value=semantic_id_value),))
|
|
91
94
|
return element.supplemental_semantic_id.__contains__(reference)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Module for parsing descriptor JSON data."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
from aas_standard_parser.classes.descriptor_json_helper_classes import EndPointHrefData
|
|
6
|
+
from aas_standard_parser.utils import decode_base_64, encode_base_64
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_endpoint_href_by_index(descriptor_data: dict, endpoint_index: int = 0) -> str | None:
|
|
12
|
+
"""Get the href from a descriptor's endpoints.
|
|
13
|
+
|
|
14
|
+
:param descriptor_data: The descriptor data containing endpoints.
|
|
15
|
+
:param endpoint_index: The index of the endpoint to extract the href from.
|
|
16
|
+
:return: The href string if found, otherwise None.
|
|
17
|
+
"""
|
|
18
|
+
endpoints = get_endpoint_hrefs(descriptor_data)
|
|
19
|
+
|
|
20
|
+
if not endpoints or len(endpoints) == 0:
|
|
21
|
+
logger.warning(f"No endpoints found in descriptor {descriptor_data}")
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
if endpoint_index >= len(endpoints):
|
|
25
|
+
logger.warning(f"Endpoint index {endpoint_index} out of range for descriptor {descriptor_data}")
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
return endpoints[endpoint_index]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_endpoint_hrefs(descriptor_data: dict) -> list[str]:
|
|
32
|
+
"""Get all hrefs from a descriptor's endpoints.
|
|
33
|
+
|
|
34
|
+
:param descriptor_data: The descriptor data containing endpoints.
|
|
35
|
+
:return: A list of href strings extracted from the endpoints.
|
|
36
|
+
"""
|
|
37
|
+
return [endpoint.get("protocolInformation", {}).get("href", "") for endpoint in descriptor_data.get("endpoints", [])]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def parse_endpoint_href(href: str) -> EndPointHrefData | None:
|
|
41
|
+
"""Parse the endpoint href into its components.
|
|
42
|
+
|
|
43
|
+
:param href: The href string to parse.
|
|
44
|
+
:return: An EndPointHrefData object containing parsed components.
|
|
45
|
+
"""
|
|
46
|
+
if "shells/" not in href and "submodels/" not in href:
|
|
47
|
+
logger.warning(f"Invalid href format: {href}")
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
split_str = ""
|
|
51
|
+
if "/shells/" in href:
|
|
52
|
+
split_str = "/shells/"
|
|
53
|
+
elif "/submodels/" in href:
|
|
54
|
+
split_str = "/submodels/"
|
|
55
|
+
|
|
56
|
+
base_url: str = href.split(split_str, maxsplit=1)[0]
|
|
57
|
+
identifier: str = href.split(split_str)[1]
|
|
58
|
+
|
|
59
|
+
href_data = EndPointHrefData(href)
|
|
60
|
+
href_data.base_url = base_url
|
|
61
|
+
href_data.identifier = identifier
|
|
62
|
+
href_data.identifier_encoded = encode_base_64(identifier)
|
|
63
|
+
|
|
64
|
+
return href_data
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""Utility functions for AAS standard parser."""
|
|
2
2
|
|
|
3
|
+
import base64
|
|
3
4
|
import json
|
|
4
5
|
import logging
|
|
5
6
|
from pathlib import Path
|
|
@@ -48,3 +49,25 @@ def _convert_to_object(content: dict) -> Any | None:
|
|
|
48
49
|
logger.error(f"Decoding error: {e}")
|
|
49
50
|
logger.error(f"In JSON: {content}")
|
|
50
51
|
return None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def decode_base_64(text: str) -> str:
|
|
55
|
+
"""Decode a Base64 encoded string.
|
|
56
|
+
|
|
57
|
+
:param text: Base64 encoded string to decode
|
|
58
|
+
:return: Decoded string
|
|
59
|
+
"""
|
|
60
|
+
text_bytes = text.encode("utf-8")
|
|
61
|
+
base64_bytes = base64.b64encode(text_bytes)
|
|
62
|
+
return base64_bytes.decode("utf-8")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def encode_base_64(text: str) -> str:
|
|
66
|
+
"""Encode a string to Base64.
|
|
67
|
+
|
|
68
|
+
:param text: String to encode
|
|
69
|
+
:return: Base64 encoded string
|
|
70
|
+
"""
|
|
71
|
+
text_bytes = text.encode("utf-8")
|
|
72
|
+
base64_bytes = base64.b64decode(text_bytes)
|
|
73
|
+
return base64_bytes.decode("utf-8")
|
{aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser.egg-info/SOURCES.txt
RENAMED
|
@@ -6,8 +6,9 @@ aas_standard_parser/aas_parser.py
|
|
|
6
6
|
aas_standard_parser/aid_parser.py
|
|
7
7
|
aas_standard_parser/aimc_parser.py
|
|
8
8
|
aas_standard_parser/collection_helpers.py
|
|
9
|
+
aas_standard_parser/descriptor_json_helper.py
|
|
9
10
|
aas_standard_parser/reference_helpers.py
|
|
10
|
-
aas_standard_parser/
|
|
11
|
+
aas_standard_parser/submodel_json_helper.py
|
|
11
12
|
aas_standard_parser/submodel_parser.py
|
|
12
13
|
aas_standard_parser/utils.py
|
|
13
14
|
aas_standard_parser/version_check.py
|
|
@@ -17,6 +18,8 @@ aas_standard_parser.egg-info/dependency_links.txt
|
|
|
17
18
|
aas_standard_parser.egg-info/requires.txt
|
|
18
19
|
aas_standard_parser.egg-info/top_level.txt
|
|
19
20
|
aas_standard_parser/classes/aimc_parser_classes.py
|
|
21
|
+
aas_standard_parser/classes/descriptor_json_helper_classes.py
|
|
20
22
|
aas_standard_parser/demo/demo_process.py
|
|
21
23
|
aas_standard_parser/demo/logging_handler.py
|
|
22
|
-
tests/test_aimc_parser.py
|
|
24
|
+
tests/test_aimc_parser.py
|
|
25
|
+
tests/test_descriptor_json_helper.py
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from basyx.aas import model
|
|
3
|
+
|
|
4
|
+
from aas_standard_parser.descriptor_json_helper import parse_endpoint_href
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.fixture(scope="module")
|
|
8
|
+
def href() -> model.Property:
|
|
9
|
+
# create a Submodel
|
|
10
|
+
return "http://aas-server:8075/shells/aHR0cHM6Ly9mbHVpZDQwLmRlL2lkcy9zaGVsbC81NzkzXzU0NDlfNzgzMF80MjIz"
|
|
11
|
+
|
|
12
|
+
def test_001_get_mapping_configuration_root_element(href: str):
|
|
13
|
+
href_data = parse_endpoint_href(href)
|
|
14
|
+
|
|
15
|
+
assert href_data is not None
|
|
16
|
+
assert href_data.base_url == "http://aas-server:8075"
|
|
17
|
+
assert href_data.identifier == "aHR0cHM6Ly9mbHVpZDQwLmRlL2lkcy9zaGVsbC81NzkzXzU0NDlfNzgzMF80MjIz"
|
|
18
|
+
assert href_data.identifier_encoded == "https://fluid40.de/ids/shell/5793_5449_7830_4223"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/demo/demo_process.py
RENAMED
|
File without changes
|
{aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/demo/logging_handler.py
RENAMED
|
File without changes
|
{aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/reference_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
{aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/submodel_parser.py
RENAMED
|
File without changes
|
{aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser/version_check.py
RENAMED
|
File without changes
|
|
File without changes
|
{aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser.egg-info/requires.txt
RENAMED
|
File without changes
|
{aas_standard_parser-0.3.2 → aas_standard_parser-0.3.4}/aas_standard_parser.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|