aas-standard-parser 0.1.6__tar.gz → 0.1.7__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.1.6 → aas_standard_parser-0.1.7}/PKG-INFO +1 -1
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser/aid_parser.py +94 -37
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser.egg-info/PKG-INFO +1 -1
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/pyproject.toml +1 -1
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/LICENSE +0 -0
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/README.md +0 -0
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser/__init__.py +0 -0
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser/aimc_parser.py +0 -0
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser/collection_helpers.py +0 -0
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser/reference_helpers.py +0 -0
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser/submodel_parser.py +0 -0
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser.egg-info/SOURCES.txt +0 -0
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser.egg-info/dependency_links.txt +0 -0
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser.egg-info/requires.txt +0 -0
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser.egg-info/top_level.txt +0 -0
- {aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/setup.cfg +0 -0
|
@@ -5,17 +5,32 @@ from typing import Dict, List
|
|
|
5
5
|
from basyx.aas.model import (
|
|
6
6
|
Property,
|
|
7
7
|
SubmodelElement,
|
|
8
|
-
SubmodelElementCollection, SubmodelElementList,
|
|
8
|
+
SubmodelElementCollection, SubmodelElementList,
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
from aas_standard_parser.collection_helpers import find_by_semantic_id, find_all_by_semantic_id, find_by_id_short
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
class IProtocolBinding:
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class HttpProtocolBinding(IProtocolBinding):
|
|
21
|
+
|
|
22
|
+
def __init__(self, method_name: str, headers: Dict[str, str]):
|
|
23
|
+
super().__init__()
|
|
24
|
+
self.method_name = method_name
|
|
25
|
+
self.headers = headers
|
|
26
|
+
|
|
27
|
+
|
|
14
28
|
class PropertyDetails:
|
|
15
29
|
|
|
16
|
-
def __init__(self, href: str, keys: List[str]):
|
|
30
|
+
def __init__(self, href: str, keys: List[str], protocol_binding: IProtocolBinding = None):
|
|
17
31
|
self.href = href
|
|
18
32
|
self.keys = keys
|
|
33
|
+
self.protocol_binding = protocol_binding
|
|
19
34
|
|
|
20
35
|
|
|
21
36
|
class IAuthenticationDetails:
|
|
@@ -44,7 +59,7 @@ class AIDParser():
|
|
|
44
59
|
def __init__(self):
|
|
45
60
|
pass
|
|
46
61
|
|
|
47
|
-
def
|
|
62
|
+
def parse_base(self, aid_interface: SubmodelElementCollection) -> str:
|
|
48
63
|
"""Get the base address (EndpointMetadata.base) from a SMC describing an interface in the AID."""
|
|
49
64
|
|
|
50
65
|
endpoint_metadata: SubmodelElementCollection | None = find_by_semantic_id(
|
|
@@ -62,7 +77,7 @@ class AIDParser():
|
|
|
62
77
|
return base.value
|
|
63
78
|
|
|
64
79
|
|
|
65
|
-
def
|
|
80
|
+
def parse_properties(self, aid_interface: SubmodelElementCollection) -> Dict[str, PropertyDetails]:
|
|
66
81
|
"""Find all first-level and nested properties in a provided SMC describing one interface in the AID.
|
|
67
82
|
Map each property (either top-level or nested) to the according 'href' attribute.
|
|
68
83
|
Nested properties are further mapped to the hierarchical list of keys
|
|
@@ -88,38 +103,53 @@ class AIDParser():
|
|
|
88
103
|
fl_properties: List[SubmodelElement] = find_all_by_semantic_id(
|
|
89
104
|
properties.value, "https://admin-shell.io/idta/AssetInterfacesDescription/1/0/PropertyDefinition"
|
|
90
105
|
)
|
|
91
|
-
# TODO: some AIDs have typos in that semanticId but we only support the official ones
|
|
92
|
-
#fl_properties_alternative: List[SubmodelElement] = find_all_by_semantic_id(
|
|
93
|
-
# properties.value, "https://admin-shell.io/idta/AssetInterfaceDescription/1/0/PropertyDefinition"
|
|
94
|
-
#)
|
|
95
|
-
#fl_properties.extend(fl_properties_alternative)
|
|
96
106
|
if fl_properties is None:
|
|
97
|
-
|
|
107
|
+
print(f"WARN: No first-level 'property' SMC not found in 'properties' SMC.")
|
|
98
108
|
return {}
|
|
99
109
|
|
|
100
|
-
def traverse_property(smc: SubmodelElementCollection, parent_path: str, href: str,
|
|
101
|
-
is_items=False, idx=None, is_top_level=False
|
|
110
|
+
def traverse_property(smc: SubmodelElementCollection, parent_path: str, href: str,
|
|
111
|
+
key_path: List[str | int], is_items=False, idx=None, is_top_level=False,
|
|
112
|
+
protocol_binding: IProtocolBinding = None):
|
|
102
113
|
# determine local key only if not top-level
|
|
103
114
|
if not is_top_level:
|
|
104
115
|
if is_items and idx is not None:
|
|
105
|
-
|
|
116
|
+
# is a nested "items" property -> add index to the list of keys
|
|
117
|
+
local_key = idx
|
|
106
118
|
else:
|
|
119
|
+
# is a nested "properties" property -> add value of "key" attribute or idShort to list of keys
|
|
107
120
|
key_prop = find_by_semantic_id(
|
|
108
121
|
smc.value, "https://admin-shell.io/idta/AssetInterfacesDescription/1/0/key"
|
|
109
122
|
)
|
|
110
|
-
local_key = key_prop.value if key_prop else smc.id_short
|
|
123
|
+
local_key = key_prop.value if key_prop else smc.id_short
|
|
111
124
|
new_key_path = key_path + [local_key]
|
|
112
125
|
else:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
126
|
+
# TODO: use the key of the first-level property (or its idShort otherwise)
|
|
127
|
+
# is a top-level property
|
|
128
|
+
#key_prop: Property | None = find_by_semantic_id(
|
|
129
|
+
# smc.value, "https://admin-shell.io/idta/AssetInterfacesDescription/1/0/key"
|
|
130
|
+
#)
|
|
131
|
+
#local_key = key_prop.value if key_prop else smc.id_short
|
|
132
|
+
#new_key_path = [local_key]
|
|
133
|
+
|
|
134
|
+
new_key_path = key_path
|
|
135
|
+
# NOTE (Tom Gneuß, 2025-10-20)
|
|
136
|
+
# See GitHub Issue: https://github.com/admin-shell-io/submodel-templates/issues/197
|
|
137
|
+
# First-level properties are allowed to have a "key" attribute - otherwise the idShort path is used.
|
|
138
|
+
# However, complex first-level properties would represent, e.g., the JSON payload (object) itself.
|
|
139
|
+
# This JSON payload does only have keys for nested elements.
|
|
140
|
+
# So, using the key (or idShort) of the first-level property to get the JSON object from the payload
|
|
141
|
+
# is not possible.
|
|
142
|
+
# On the other hand: the first-level property could intentionally be something within the JSON object.
|
|
143
|
+
# In that case, having a "key" (or using the idSort) is entirely valid.
|
|
144
|
+
# How to distinguish both cases?
|
|
145
|
+
|
|
146
|
+
# create the idShort path of this property
|
|
116
147
|
full_path = f"{parent_path}.{smc.id_short}"
|
|
117
|
-
mapping
|
|
148
|
+
# add this property with all its details to the mapping -> href (from top-level parent if this is nested),
|
|
149
|
+
# protocol bindings (from top-level parent if this is nested), list of keys
|
|
150
|
+
mapping[full_path] = PropertyDetails(href, new_key_path, protocol_binding)
|
|
118
151
|
|
|
119
152
|
# traverse nested "properties" or "items"
|
|
120
|
-
# (nested properties = object members, nested items = array elements)
|
|
121
|
-
# TODO: some apparently use the wrong semanticId:
|
|
122
|
-
# "https://www.w3.org/2019/wot/td#PropertyAffordance"
|
|
123
153
|
for nested_sem_id in [
|
|
124
154
|
"https://www.w3.org/2019/wot/json-schema#properties",
|
|
125
155
|
"https://www.w3.org/2019/wot/json-schema#items",
|
|
@@ -133,26 +163,17 @@ class AIDParser():
|
|
|
133
163
|
nested_properties: List[SubmodelElement] = find_all_by_semantic_id(
|
|
134
164
|
nested_group.value, "https://www.w3.org/2019/wot/json-schema#propertyName"
|
|
135
165
|
)
|
|
136
|
-
# TODO: some AIDs have typos or use wrong semanticIds but we only support the official ones
|
|
137
|
-
#nested_properties_alternative1: List[SubmodelElement] = find_all_by_semantic_id(
|
|
138
|
-
# nested_group.value, "https://admin-shell.io/idta/AssetInterfaceDescription/1/0/PropertyDefinition"
|
|
139
|
-
#)
|
|
140
|
-
# nested_properties_alternative2: List[SubmodelElement] = find_all_by_semantic_id(
|
|
141
|
-
# nested_group.value, "https://admin-shell.io/idta/AssetInterfacesDescription/1/0/PropertyDefinition"
|
|
142
|
-
# )
|
|
143
|
-
#nested_properties.extend(nested_properties_alternative1)
|
|
144
|
-
#nested_properties.extend(nested_properties_alternative2)
|
|
145
166
|
|
|
146
167
|
# traverse all nested properties/items recursively
|
|
147
168
|
for idx, nested in enumerate(nested_properties):
|
|
148
169
|
if nested_sem_id.endswith("#items"):
|
|
149
|
-
# for arrays: append index instead of
|
|
170
|
+
# for arrays: append index instead of "key" attribute
|
|
150
171
|
traverse_property(nested, full_path, href, new_key_path, is_items=True, idx=idx)
|
|
151
172
|
else:
|
|
152
173
|
traverse_property(nested, full_path, href, new_key_path)
|
|
153
174
|
|
|
154
175
|
# process all first-level properties
|
|
155
|
-
for fl_property in fl_properties:
|
|
176
|
+
for fl_property in fl_properties: # type: SubmodelElementCollection
|
|
156
177
|
forms: SubmodelElementCollection | None = find_by_semantic_id(
|
|
157
178
|
fl_property.value, "https://www.w3.org/2019/wot/td#hasForm"
|
|
158
179
|
)
|
|
@@ -165,15 +186,51 @@ class AIDParser():
|
|
|
165
186
|
if href is None:
|
|
166
187
|
raise ValueError("'href' Property not found in 'forms' SMC.")
|
|
167
188
|
|
|
189
|
+
# get the href value of the first-level property
|
|
168
190
|
href_value = href.value
|
|
191
|
+
|
|
192
|
+
# construct the idShort path up to "Interface_.InteractionMetadata.properties"
|
|
193
|
+
# will be used as prefix for the idShort paths of the first-level and nested properties
|
|
169
194
|
idshort_path_prefix = f"{aid_interface.id_short}.{interaction_metadata.id_short}.{properties.id_short}"
|
|
170
195
|
|
|
196
|
+
# check which protocol-specific subtype of forms is used
|
|
197
|
+
# there is no clean solution for determining the subtype (e.g., a supplSemId)
|
|
198
|
+
# -> can only be figured out if the specific fields are present
|
|
199
|
+
protocol_binding: IProtocolBinding = None
|
|
200
|
+
|
|
201
|
+
# ... try HTTP ("htv_methodName" must be present)
|
|
202
|
+
htv_method_name: Property | None = find_by_semantic_id(
|
|
203
|
+
forms.value, "https://www.w3.org/2011/http#methodName"
|
|
204
|
+
)
|
|
205
|
+
if htv_method_name is not None:
|
|
206
|
+
protocol_binding: HttpProtocolBinding = HttpProtocolBinding(htv_method_name.value, {})
|
|
207
|
+
htv_headers: SubmodelElementCollection | None = find_by_semantic_id(
|
|
208
|
+
forms.value, "https://www.w3.org/2011/http#headers"
|
|
209
|
+
)
|
|
210
|
+
if htv_headers is not None:
|
|
211
|
+
for header in htv_headers.value: # type: SubmodelElementCollection
|
|
212
|
+
htv_field_name: Property | None = find_by_semantic_id(
|
|
213
|
+
header.value, "https://www.w3.org/2011/http#fieldName"
|
|
214
|
+
)
|
|
215
|
+
htv_field_value: Property | None = find_by_semantic_id(
|
|
216
|
+
header.value, "https://www.w3.org/2011/http#fieldValue"
|
|
217
|
+
)
|
|
218
|
+
protocol_binding.headers[htv_field_name.value] = htv_field_value.value
|
|
219
|
+
|
|
220
|
+
# TODO: the other protocols
|
|
221
|
+
# ... try Modbus
|
|
222
|
+
# ... try MQTT
|
|
223
|
+
|
|
224
|
+
# recursively parse the first-level property and its nested properties (if any)
|
|
171
225
|
traverse_property(
|
|
172
|
-
fl_property,
|
|
173
|
-
idshort_path_prefix,
|
|
174
|
-
href_value,
|
|
175
|
-
[],
|
|
176
|
-
|
|
226
|
+
smc=fl_property,
|
|
227
|
+
parent_path=idshort_path_prefix,
|
|
228
|
+
href=href_value,
|
|
229
|
+
key_path=[],
|
|
230
|
+
is_items=False,
|
|
231
|
+
idx=None,
|
|
232
|
+
is_top_level=True,
|
|
233
|
+
protocol_binding=protocol_binding
|
|
177
234
|
)
|
|
178
235
|
|
|
179
236
|
return mapping
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser/collection_helpers.py
RENAMED
|
File without changes
|
{aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser/reference_helpers.py
RENAMED
|
File without changes
|
{aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser/submodel_parser.py
RENAMED
|
File without changes
|
{aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser.egg-info/requires.txt
RENAMED
|
File without changes
|
{aas_standard_parser-0.1.6 → aas_standard_parser-0.1.7}/aas_standard_parser.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|