aas-http-client 0.1.7__py3-none-any.whl → 0.1.9__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.
Potentially problematic release.
This version of aas-http-client might be problematic. Click here for more details.
- aas_http_client/client.py +0 -4
- aas_http_client/utilities/model_builder.py +0 -1
- aas_http_client/wrapper/sdk_wrapper.py +5 -25
- aas_http_client-0.1.9.dist-info/METADATA +164 -0
- aas_http_client-0.1.9.dist-info/RECORD +14 -0
- aas_http_client-0.1.9.dist-info/licenses/LICENSE +101 -0
- aas_http_client/wrapper/python_sdk_wrapper_tmp.py +0 -641
- aas_http_client-0.1.7.dist-info/METADATA +0 -61
- aas_http_client-0.1.7.dist-info/RECORD +0 -15
- aas_http_client-0.1.7.dist-info/licenses/LICENSE +0 -21
- {aas_http_client-0.1.7.dist-info → aas_http_client-0.1.9.dist-info}/WHEEL +0 -0
- {aas_http_client-0.1.7.dist-info → aas_http_client-0.1.9.dist-info}/top_level.txt +0 -0
aas_http_client/client.py
CHANGED
|
@@ -67,7 +67,6 @@ class AasHttpClient(BaseModel):
|
|
|
67
67
|
"""Represents a AasHttpClient to communicate with a REST API."""
|
|
68
68
|
|
|
69
69
|
base_url: str = "http://javaaasserver:5060/"
|
|
70
|
-
api_base_path: str = ""
|
|
71
70
|
username: str | None = None
|
|
72
71
|
_password: str | None = PrivateAttr(default=None)
|
|
73
72
|
https_proxy: str | None = None
|
|
@@ -445,7 +444,6 @@ class AasHttpClient(BaseModel):
|
|
|
445
444
|
|
|
446
445
|
def create_client_by_url(
|
|
447
446
|
base_url: str,
|
|
448
|
-
api_base_path: str = "",
|
|
449
447
|
username: str = "",
|
|
450
448
|
password: str = "",
|
|
451
449
|
http_proxy: str = "",
|
|
@@ -469,7 +467,6 @@ def create_client_by_url(
|
|
|
469
467
|
logger.info(f"Create BaSyx server interface client from URL '{base_url}'")
|
|
470
468
|
config_dict: dict[str, str] = {}
|
|
471
469
|
config_dict["base_url"] = base_url
|
|
472
|
-
config_dict["api_base_path"] = api_base_path
|
|
473
470
|
config_dict["username"] = username
|
|
474
471
|
config_dict["http_proxy"] = http_proxy
|
|
475
472
|
config_dict["https_proxy"] = https_proxy
|
|
@@ -507,7 +504,6 @@ def _create_client(config_string: str, password) -> AasHttpClient | None:
|
|
|
507
504
|
|
|
508
505
|
logger.info(
|
|
509
506
|
f"Using server configuration: '{client.base_url}' | "
|
|
510
|
-
f"API base path: '{client.api_base_path}' | "
|
|
511
507
|
f"timeout: '{client.time_out}' | "
|
|
512
508
|
f"username: '{client.username}' | "
|
|
513
509
|
f"https_proxy: '{client.https_proxy}' | "
|
|
@@ -47,7 +47,7 @@ class SdkWrapper():
|
|
|
47
47
|
sm_data_string = json.dumps(submodel, cls=basyx.aas.adapter.json.AASToJsonEncoder)
|
|
48
48
|
sm_data = json.loads(sm_data_string)
|
|
49
49
|
|
|
50
|
-
return self._client.
|
|
50
|
+
return self._client.put_shells_submodels_by_id(aas_id, submodel_id, sm_data)
|
|
51
51
|
|
|
52
52
|
def get_shells(self) -> list[AssetAdministrationShell] | None:
|
|
53
53
|
"""Get all Asset Administration Shells (AAS) from the REST API.
|
|
@@ -91,14 +91,14 @@ class SdkWrapper():
|
|
|
91
91
|
content: dict = self._client.get_shells_reference_by_id(aas_id)
|
|
92
92
|
return json.load(content, cls=basyx.aas.adapter.json.AASFromJsonDecoder)
|
|
93
93
|
|
|
94
|
-
def
|
|
94
|
+
def get_shells_submodels_by_id(self, aas_id: str, submodel_id: str) -> Submodel | None:
|
|
95
95
|
"""Get a submodel by its ID for a specific Asset Administration Shell (AAS).
|
|
96
96
|
|
|
97
97
|
:param aas_id: ID of the AAS to retrieve the submodel from
|
|
98
98
|
:param submodel_id: ID of the submodel to retrieve
|
|
99
99
|
:return: Submodel object or None if an error occurred
|
|
100
100
|
"""
|
|
101
|
-
content: dict = self._client.
|
|
101
|
+
content: dict = self._client.get_shells_submodels_by_id(aas_id, submodel_id)
|
|
102
102
|
return json.load(content, cls=basyx.aas.adapter.json.AASFromJsonDecoder)
|
|
103
103
|
|
|
104
104
|
def delete_shells_by_id(self, aas_id: str) -> bool:
|
|
@@ -120,7 +120,7 @@ class SdkWrapper():
|
|
|
120
120
|
|
|
121
121
|
return self._client.post_submodels(sm_data)
|
|
122
122
|
|
|
123
|
-
def
|
|
123
|
+
def put_submodels_by_id(self, identifier: str, submodel: Submodel) -> bool:
|
|
124
124
|
"""Update a submodel by its ID in the REST API.
|
|
125
125
|
|
|
126
126
|
:param identifier: Identifier of the submodel to update
|
|
@@ -130,25 +130,7 @@ class SdkWrapper():
|
|
|
130
130
|
sm_data_string = json.dumps(submodel, cls=basyx.aas.adapter.json.AASToJsonEncoder)
|
|
131
131
|
sm_data = json.loads(sm_data_string)
|
|
132
132
|
|
|
133
|
-
return self._client.
|
|
134
|
-
|
|
135
|
-
def get_submodel_by_id(self, submodel_id: str) -> Submodel | None:
|
|
136
|
-
"""Get a submodel by its ID from the REST API.
|
|
137
|
-
|
|
138
|
-
:param submodel_id: ID of the submodel to retrieve
|
|
139
|
-
:return: Submodel object or None if an error occurred
|
|
140
|
-
"""
|
|
141
|
-
content = self._client.get_submodel_by_id(submodel_id)
|
|
142
|
-
|
|
143
|
-
if not content:
|
|
144
|
-
logger.warning(f"No submodel found with ID '{submodel_id}' in the REST API.")
|
|
145
|
-
return None
|
|
146
|
-
|
|
147
|
-
if not isinstance(content, dict):
|
|
148
|
-
logger.error(f"Invalid submodel data: {content}")
|
|
149
|
-
return None
|
|
150
|
-
|
|
151
|
-
return json.loads(content, cls=basyx.aas.adapter.json.AASFromJsonDecoder)
|
|
133
|
+
return self._client.put_submodels_by_id(identifier, sm_data)
|
|
152
134
|
|
|
153
135
|
def get_submodels(self) -> list[Submodel] | None:
|
|
154
136
|
"""Get all submodels from the REST API.
|
|
@@ -213,7 +195,6 @@ class SdkWrapper():
|
|
|
213
195
|
|
|
214
196
|
def create_wrapper_by_url(
|
|
215
197
|
base_url: str,
|
|
216
|
-
api_base_path: str = "",
|
|
217
198
|
username: str = "",
|
|
218
199
|
password: str = "",
|
|
219
200
|
http_proxy: str = "",
|
|
@@ -237,7 +218,6 @@ def create_wrapper_by_url(
|
|
|
237
218
|
logger.info(f"Create BaSyx server interface client from URL '{base_url}'")
|
|
238
219
|
config_dict: dict[str, str] = {}
|
|
239
220
|
config_dict["base_url"] = base_url
|
|
240
|
-
config_dict["api_base_path"] = api_base_path
|
|
241
221
|
config_dict["username"] = username
|
|
242
222
|
config_dict["http_proxy"] = http_proxy
|
|
243
223
|
config_dict["https_proxy"] = https_proxy
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aas-http-client
|
|
3
|
+
Version: 0.1.9
|
|
4
|
+
Summary: Generic HTTP client for communicating with various types of AAS servers
|
|
5
|
+
Author-email: Daniel Klein <daniel.klein@em.ag>
|
|
6
|
+
License: # :em engineering methods AG Software License
|
|
7
|
+
|
|
8
|
+
SPDX-Identifier: LicenseRef-em
|
|
9
|
+
Date: 2025
|
|
10
|
+
Version: v0.1
|
|
11
|
+
|
|
12
|
+
This license agreement (“Agreement”) is made between **:em engineering methods AG (“:em AG”)**, a company
|
|
13
|
+
incorporated under the laws of Germany, and the **customer (“Customer”)** who has purchased or obtained
|
|
14
|
+
software, scripts, source code or software components (“Software”) from :em AG.
|
|
15
|
+
|
|
16
|
+
By installing, copying, or otherwise using the Software, the Customer agrees to be bound by the terms of this
|
|
17
|
+
Agreement. If the Customer does not agree to the terms of this Agreement, the Customer must not use the
|
|
18
|
+
Software and must delete or return any copies of the Software to :em AG.
|
|
19
|
+
|
|
20
|
+
## 1. Grant of License
|
|
21
|
+
|
|
22
|
+
Subject to the terms and conditions of this Agreement, :em AG grants the Customer a non-exclusive,
|
|
23
|
+
non-transferable, revocable license to use the Software solely for the Customer’s own internal business
|
|
24
|
+
purposes on systems owned or controlled by the Customer (“Customer Systems”). The Customer may also allow
|
|
25
|
+
its service providers to use the Software on Customer Systems, provided that such service providers agree
|
|
26
|
+
to comply with the terms of this Agreement and the Customer remains responsible for their actions.
|
|
27
|
+
|
|
28
|
+
The Customer may not:
|
|
29
|
+
|
|
30
|
+
* sublicense, sell, rent, lease, lend, distribute, or otherwise transfer the Software or any rights under
|
|
31
|
+
this Agreement to any third party without the prior written consent of :em AG;
|
|
32
|
+
* modify, adapt, translate, reverse engineer, decompile, disassemble, or create derivative works based
|
|
33
|
+
on the Software or any part thereof;
|
|
34
|
+
* remove, alter, or obscure any copyright notices or other proprietary notices on the Software;
|
|
35
|
+
use the Software for any illegal, unethical, or unauthorized purpose;
|
|
36
|
+
* use the Software in a manner that infringes or violates the rights of :em AG or any third party.
|
|
37
|
+
|
|
38
|
+
## 2. Reservation of Rights
|
|
39
|
+
|
|
40
|
+
:em AG retains all right, title, and interest in and to the Software and any copies thereof.
|
|
41
|
+
The Software is protected by copyright and other intellectual property laws and treaties.
|
|
42
|
+
The Software is licensed, not sold. Nothing in this Agreement shall be construed as transferring any
|
|
43
|
+
ownership rights in the Software to the Customer.
|
|
44
|
+
|
|
45
|
+
:em AG reserves the right to use the Software for any purpose, including but not limited to providing it to
|
|
46
|
+
other customers of :em AG, changing its license terms for future versions, or discontinuing its development
|
|
47
|
+
or support.
|
|
48
|
+
|
|
49
|
+
## 3. Disclaimer of Warranty
|
|
50
|
+
|
|
51
|
+
The Software is provided “as is” without any warranty of any kind, either express or implied, including but
|
|
52
|
+
not limited to the implied warranties of merchantability, fitness for a particular purpose, or
|
|
53
|
+
non-infringement.
|
|
54
|
+
|
|
55
|
+
:em AG does not warrant that the Software will meet the Customer’s requirements, that it will operate
|
|
56
|
+
uninterrupted or error-free, that it will be compatible with any other software or hardware, or that it will
|
|
57
|
+
be free from defects or viruses.
|
|
58
|
+
|
|
59
|
+
The Customer acknowledges that it has relied on its own skill and judgment in selecting and using the Software
|
|
60
|
+
and that it is solely responsible for the results obtained from the Software.
|
|
61
|
+
|
|
62
|
+
## 4. Limitation of Liability
|
|
63
|
+
|
|
64
|
+
To the maximum extent permitted by applicable law, in no event shall :em AG be liable for any direct,
|
|
65
|
+
indirect, incidental, special, consequential, or exemplary damages arising out of or in connection with this
|
|
66
|
+
Agreement or the use or inability to use the Software, even if :em AG has been advised of the possibility of
|
|
67
|
+
such damages. In any case, :em AG’s total liability under this Agreement shall not exceed the amount paid by
|
|
68
|
+
the Customer for the Software.
|
|
69
|
+
|
|
70
|
+
## 5. Termination
|
|
71
|
+
|
|
72
|
+
This Agreement shall remain in effect until terminated by either party. The Customer may terminate this
|
|
73
|
+
Agreement at any time by ceasing to use the Software and deleting or returning all copies of the Software
|
|
74
|
+
to :em AG. :em AG may terminate this Agreement at any time if the Customer breaches any term of this
|
|
75
|
+
Agreement.
|
|
76
|
+
|
|
77
|
+
Upon termination of this Agreement for any reason, all rights and licenses granted to the Customer under
|
|
78
|
+
this Agreement shall cease and the Customer shall destroy or return all copies of the Software to :em AG.
|
|
79
|
+
|
|
80
|
+
## 6. General
|
|
81
|
+
|
|
82
|
+
This Agreement constitutes the entire agreement between the parties with respect to the subject matter hereof
|
|
83
|
+
and supersedes all prior or contemporaneous agreements or understandings, whether written or oral. This
|
|
84
|
+
Agreement may not be modified or amended except by a written instrument signed by both parties.
|
|
85
|
+
|
|
86
|
+
This Agreement shall be governed by and construed in accordance with the laws of Germany without regard to its
|
|
87
|
+
conflict of laws principles. Any dispute arising out of or relating to this Agreement shall be submitted to
|
|
88
|
+
the exclusive jurisdiction of the competent courts of Munich, Germany.
|
|
89
|
+
|
|
90
|
+
If any provision of this Agreement is held to be invalid, illegal, or unenforceable, the remaining provisions
|
|
91
|
+
shall remain in full force and effect.
|
|
92
|
+
|
|
93
|
+
The failure of either party to enforce any right or provision of this Agreement shall not constitute a waiver
|
|
94
|
+
of such right or provision.
|
|
95
|
+
|
|
96
|
+
The Customer may not assign or transfer this Agreement or any rights or obligations under this Agreement
|
|
97
|
+
without the prior written consent of :em AG. :em AG may assign or transfer this Agreement or any rights
|
|
98
|
+
or obligations under this Agreement without the consent of the Customer.
|
|
99
|
+
|
|
100
|
+
The Customer agrees to comply with all applicable laws and regulations in connection with its use of the
|
|
101
|
+
Software, including but not limited to export control laws and data protection laws.
|
|
102
|
+
|
|
103
|
+
The Customer acknowledges that it has read and understood this Agreement and agrees to be bound by its terms
|
|
104
|
+
and conditions. The Customer also agrees that this Agreement is the complete and exclusive statement of the
|
|
105
|
+
agreement between the parties and supersedes any proposal or prior agreement, oral or written, and any other
|
|
106
|
+
communications between the parties relating to the subject matter of this Agreement.
|
|
107
|
+
|
|
108
|
+
Project-URL: Homepage, https://github.com/fluid40/aas-http-client
|
|
109
|
+
Description-Content-Type: text/markdown
|
|
110
|
+
License-File: LICENSE
|
|
111
|
+
Dynamic: license-file
|
|
112
|
+
|
|
113
|
+
<!-- TODO: Go through the readme and enter the information here -->
|
|
114
|
+
|
|
115
|
+
# AAS HTTP Client
|
|
116
|
+
|
|
117
|
+
<div align="center">
|
|
118
|
+
<!-- change this to your projects logo if you have on.
|
|
119
|
+
If you don't have one it might be worth trying chatgpt dall-e to create one for you...
|
|
120
|
+
-->
|
|
121
|
+
<img src="docs/assets/fluid_logo.svg" alt="aas_http_client" width=500 />
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
[](LICENSE)
|
|
127
|
+
[](https://github.com/fluid40/aas-http-client/actions)
|
|
128
|
+
[](https://pypi.org/project/aas-http-client/)
|
|
129
|
+
|
|
130
|
+
A generic HTTP client for communicating with various types of AAS and submodel repository servers. The client works with Python dictionaries for the input and output parameters.
|
|
131
|
+
In addition, wrappers are provided that work with various AAS frameworks and use the http client as middleware.
|
|
132
|
+
|
|
133
|
+
Currently, wrappers are available for the following frameworks:
|
|
134
|
+
- BaSyx Python SDK
|
|
135
|
+
|
|
136
|
+
## Links
|
|
137
|
+
|
|
138
|
+
🚀 [Getting Started](docs/getting_started.md)
|
|
139
|
+
|
|
140
|
+
💻 [Developer Quickstart](docs/dev_guide.md)
|
|
141
|
+
|
|
142
|
+
👨⚕️ [Troubleshooting](docs/troubleshooting.md)
|
|
143
|
+
|
|
144
|
+
🤖 [Releases](http://github.com/fluid40/aas-http-client/releases)
|
|
145
|
+
|
|
146
|
+
📦 [Pypi Packages](https://pypi.org/project/aas-http-client/)
|
|
147
|
+
|
|
148
|
+
📜 [em AG Software License](LICENSE)
|
|
149
|
+
|
|
150
|
+
## ⚡ Quickstart
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
pip install aas-http-client
|
|
154
|
+
````
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from aas_http_client import create_client_by_url
|
|
158
|
+
|
|
159
|
+
client = create_client_by_url(
|
|
160
|
+
base_url="http://myaasserver:5043/"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
print(client.get_shells())
|
|
164
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
aas_http_client/__init__.py,sha256=cAr1mQzWp0G0LKtkAOYzc9t95OY3jM3Aj4bKnxx0Dso,901
|
|
2
|
+
aas_http_client/client.py,sha256=d4s90n48oJkeIiAQWR8OxJVeZGXz-heQMPGMREy3hQg,20512
|
|
3
|
+
aas_http_client/core/encoder.py,sha256=FS7P0FPakzFsGz70eRFDHQZFA_2nlKLlWIxavtnFrPg,660
|
|
4
|
+
aas_http_client/core/version_check.py,sha256=721Zs3xSRrJTYZtAxkaUWg9LLKtpU7oFM62DzQHZdE4,705
|
|
5
|
+
aas_http_client/demo/demo_process.py,sha256=-9-oQEi_B08W-POl2SVP4COodGPHN3mTw9rrPgLLpeY,2170
|
|
6
|
+
aas_http_client/demo/logging_handler.py,sha256=VJtZ4u3x_LhYZQtfNck7FuXhGFZm7gid0uDhvf9GjJ8,5596
|
|
7
|
+
aas_http_client/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
aas_http_client/utilities/model_builder.py,sha256=bL4hy3uHh1Y_uL6KmLIfo9ORiUqWNlyToJrAt8G1rpw,3877
|
|
9
|
+
aas_http_client/wrapper/sdk_wrapper.py,sha256=O61Q32zlWxsNqpb4p811GbPd2Un9VGLY_pXR5Q-0QTg,10395
|
|
10
|
+
aas_http_client-0.1.9.dist-info/licenses/LICENSE,sha256=ayt4HY-Tjoe1Uvj47j6UdNq8mEufKcKFangurChIHxQ,5990
|
|
11
|
+
aas_http_client-0.1.9.dist-info/METADATA,sha256=_SaTA38hz2wOxhfgkEhKTzIVIa-NH5k_tYIe_BlZ6HM,8823
|
|
12
|
+
aas_http_client-0.1.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
13
|
+
aas_http_client-0.1.9.dist-info/top_level.txt,sha256=vzvoz2vjeTLwpuz-Y-eEfoQ7T3byoaKshVlFMFH5NaM,16
|
|
14
|
+
aas_http_client-0.1.9.dist-info/RECORD,,
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# :em engineering methods AG Software License
|
|
2
|
+
|
|
3
|
+
SPDX-Identifier: LicenseRef-em
|
|
4
|
+
Date: 2025
|
|
5
|
+
Version: v0.1
|
|
6
|
+
|
|
7
|
+
This license agreement (“Agreement”) is made between **:em engineering methods AG (“:em AG”)**, a company
|
|
8
|
+
incorporated under the laws of Germany, and the **customer (“Customer”)** who has purchased or obtained
|
|
9
|
+
software, scripts, source code or software components (“Software”) from :em AG.
|
|
10
|
+
|
|
11
|
+
By installing, copying, or otherwise using the Software, the Customer agrees to be bound by the terms of this
|
|
12
|
+
Agreement. If the Customer does not agree to the terms of this Agreement, the Customer must not use the
|
|
13
|
+
Software and must delete or return any copies of the Software to :em AG.
|
|
14
|
+
|
|
15
|
+
## 1. Grant of License
|
|
16
|
+
|
|
17
|
+
Subject to the terms and conditions of this Agreement, :em AG grants the Customer a non-exclusive,
|
|
18
|
+
non-transferable, revocable license to use the Software solely for the Customer’s own internal business
|
|
19
|
+
purposes on systems owned or controlled by the Customer (“Customer Systems”). The Customer may also allow
|
|
20
|
+
its service providers to use the Software on Customer Systems, provided that such service providers agree
|
|
21
|
+
to comply with the terms of this Agreement and the Customer remains responsible for their actions.
|
|
22
|
+
|
|
23
|
+
The Customer may not:
|
|
24
|
+
|
|
25
|
+
* sublicense, sell, rent, lease, lend, distribute, or otherwise transfer the Software or any rights under
|
|
26
|
+
this Agreement to any third party without the prior written consent of :em AG;
|
|
27
|
+
* modify, adapt, translate, reverse engineer, decompile, disassemble, or create derivative works based
|
|
28
|
+
on the Software or any part thereof;
|
|
29
|
+
* remove, alter, or obscure any copyright notices or other proprietary notices on the Software;
|
|
30
|
+
use the Software for any illegal, unethical, or unauthorized purpose;
|
|
31
|
+
* use the Software in a manner that infringes or violates the rights of :em AG or any third party.
|
|
32
|
+
|
|
33
|
+
## 2. Reservation of Rights
|
|
34
|
+
|
|
35
|
+
:em AG retains all right, title, and interest in and to the Software and any copies thereof.
|
|
36
|
+
The Software is protected by copyright and other intellectual property laws and treaties.
|
|
37
|
+
The Software is licensed, not sold. Nothing in this Agreement shall be construed as transferring any
|
|
38
|
+
ownership rights in the Software to the Customer.
|
|
39
|
+
|
|
40
|
+
:em AG reserves the right to use the Software for any purpose, including but not limited to providing it to
|
|
41
|
+
other customers of :em AG, changing its license terms for future versions, or discontinuing its development
|
|
42
|
+
or support.
|
|
43
|
+
|
|
44
|
+
## 3. Disclaimer of Warranty
|
|
45
|
+
|
|
46
|
+
The Software is provided “as is” without any warranty of any kind, either express or implied, including but
|
|
47
|
+
not limited to the implied warranties of merchantability, fitness for a particular purpose, or
|
|
48
|
+
non-infringement.
|
|
49
|
+
|
|
50
|
+
:em AG does not warrant that the Software will meet the Customer’s requirements, that it will operate
|
|
51
|
+
uninterrupted or error-free, that it will be compatible with any other software or hardware, or that it will
|
|
52
|
+
be free from defects or viruses.
|
|
53
|
+
|
|
54
|
+
The Customer acknowledges that it has relied on its own skill and judgment in selecting and using the Software
|
|
55
|
+
and that it is solely responsible for the results obtained from the Software.
|
|
56
|
+
|
|
57
|
+
## 4. Limitation of Liability
|
|
58
|
+
|
|
59
|
+
To the maximum extent permitted by applicable law, in no event shall :em AG be liable for any direct,
|
|
60
|
+
indirect, incidental, special, consequential, or exemplary damages arising out of or in connection with this
|
|
61
|
+
Agreement or the use or inability to use the Software, even if :em AG has been advised of the possibility of
|
|
62
|
+
such damages. In any case, :em AG’s total liability under this Agreement shall not exceed the amount paid by
|
|
63
|
+
the Customer for the Software.
|
|
64
|
+
|
|
65
|
+
## 5. Termination
|
|
66
|
+
|
|
67
|
+
This Agreement shall remain in effect until terminated by either party. The Customer may terminate this
|
|
68
|
+
Agreement at any time by ceasing to use the Software and deleting or returning all copies of the Software
|
|
69
|
+
to :em AG. :em AG may terminate this Agreement at any time if the Customer breaches any term of this
|
|
70
|
+
Agreement.
|
|
71
|
+
|
|
72
|
+
Upon termination of this Agreement for any reason, all rights and licenses granted to the Customer under
|
|
73
|
+
this Agreement shall cease and the Customer shall destroy or return all copies of the Software to :em AG.
|
|
74
|
+
|
|
75
|
+
## 6. General
|
|
76
|
+
|
|
77
|
+
This Agreement constitutes the entire agreement between the parties with respect to the subject matter hereof
|
|
78
|
+
and supersedes all prior or contemporaneous agreements or understandings, whether written or oral. This
|
|
79
|
+
Agreement may not be modified or amended except by a written instrument signed by both parties.
|
|
80
|
+
|
|
81
|
+
This Agreement shall be governed by and construed in accordance with the laws of Germany without regard to its
|
|
82
|
+
conflict of laws principles. Any dispute arising out of or relating to this Agreement shall be submitted to
|
|
83
|
+
the exclusive jurisdiction of the competent courts of Munich, Germany.
|
|
84
|
+
|
|
85
|
+
If any provision of this Agreement is held to be invalid, illegal, or unenforceable, the remaining provisions
|
|
86
|
+
shall remain in full force and effect.
|
|
87
|
+
|
|
88
|
+
The failure of either party to enforce any right or provision of this Agreement shall not constitute a waiver
|
|
89
|
+
of such right or provision.
|
|
90
|
+
|
|
91
|
+
The Customer may not assign or transfer this Agreement or any rights or obligations under this Agreement
|
|
92
|
+
without the prior written consent of :em AG. :em AG may assign or transfer this Agreement or any rights
|
|
93
|
+
or obligations under this Agreement without the consent of the Customer.
|
|
94
|
+
|
|
95
|
+
The Customer agrees to comply with all applicable laws and regulations in connection with its use of the
|
|
96
|
+
Software, including but not limited to export control laws and data protection laws.
|
|
97
|
+
|
|
98
|
+
The Customer acknowledges that it has read and understood this Agreement and agrees to be bound by its terms
|
|
99
|
+
and conditions. The Customer also agrees that this Agreement is the complete and exclusive statement of the
|
|
100
|
+
agreement between the parties and supersedes any proposal or prior agreement, oral or written, and any other
|
|
101
|
+
communications between the parties relating to the subject matter of this Agreement.
|
|
@@ -1,641 +0,0 @@
|
|
|
1
|
-
"""BaSyx Server interface for REST API communication."""
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import logging
|
|
5
|
-
import re
|
|
6
|
-
import time
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
|
|
9
|
-
import basyx.aas.adapter.json
|
|
10
|
-
import basyx.aas.adapter.json.json_serialization as js
|
|
11
|
-
import requests
|
|
12
|
-
from basyx.aas.model import AssetAdministrationShell, Reference, Submodel
|
|
13
|
-
from aas_http_client.core.encoder import decode_base_64, encode_base_64
|
|
14
|
-
from pydantic import BaseModel, PrivateAttr, ValidationError
|
|
15
|
-
from requests import Session
|
|
16
|
-
from requests.auth import HTTPBasicAuth
|
|
17
|
-
from requests.models import Response
|
|
18
|
-
|
|
19
|
-
logger = logging.getLogger(__name__)
|
|
20
|
-
|
|
21
|
-
STATUS_CODE_200 = 200
|
|
22
|
-
STATUS_CODE_201 = 201
|
|
23
|
-
STATUS_CODE_202 = 202
|
|
24
|
-
STATUS_CODE_204 = 204
|
|
25
|
-
HEADERS = {"Content-Type": "application/json"}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def log_response_errors(response: Response):
|
|
29
|
-
"""Create error messages from the response and log them.
|
|
30
|
-
|
|
31
|
-
:param response: response
|
|
32
|
-
"""
|
|
33
|
-
result_error_messages: list[str] = []
|
|
34
|
-
|
|
35
|
-
try:
|
|
36
|
-
response_content_dict: dict = json.loads(response.content)
|
|
37
|
-
|
|
38
|
-
if "detail" in response_content_dict:
|
|
39
|
-
detail: dict = response_content_dict.get("detail", {})
|
|
40
|
-
if "error" in detail:
|
|
41
|
-
error: str = detail.get("error", "")
|
|
42
|
-
result_error_messages.append(f"{error}")
|
|
43
|
-
else:
|
|
44
|
-
result_error_messages.append(f"{detail}")
|
|
45
|
-
|
|
46
|
-
elif "messages" in response_content_dict or "Messages" in response_content_dict:
|
|
47
|
-
messages: list = response_content_dict.get("messages", [])
|
|
48
|
-
|
|
49
|
-
if not messages:
|
|
50
|
-
messages = response_content_dict.get("Messages", [])
|
|
51
|
-
|
|
52
|
-
for message in messages:
|
|
53
|
-
if isinstance(message, dict) and "message" in message:
|
|
54
|
-
result_error_messages.append(message["message"])
|
|
55
|
-
else:
|
|
56
|
-
result_error_messages.append(str(message))
|
|
57
|
-
elif "error" in response_content_dict:
|
|
58
|
-
result_error_messages.append(response_content_dict.get("error", ""))
|
|
59
|
-
|
|
60
|
-
except json.JSONDecodeError:
|
|
61
|
-
result_error_messages.append(response.content)
|
|
62
|
-
|
|
63
|
-
logger.error(f"Status code: {response.status_code}")
|
|
64
|
-
for result_error_message in result_error_messages:
|
|
65
|
-
logger.error(result_error_message)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class AasxServerInterface(BaseModel):
|
|
69
|
-
"""Represents a AasxServerInterface to communicate with a REST API."""
|
|
70
|
-
|
|
71
|
-
base_url: str = "http://basyx_aas_server:80/"
|
|
72
|
-
username: str | None = None
|
|
73
|
-
_password: str | None = PrivateAttr(default=None)
|
|
74
|
-
https_proxy: str | None = None
|
|
75
|
-
http_proxy: str | None = None
|
|
76
|
-
time_out: int = 200
|
|
77
|
-
connection_time_out: int = 100
|
|
78
|
-
ssl_verify: bool = True
|
|
79
|
-
_session: Session = PrivateAttr(default=None)
|
|
80
|
-
namespace: str | None = None
|
|
81
|
-
|
|
82
|
-
def initialize(self, password: str):
|
|
83
|
-
"""Initialize the AasxServerInterface with the given URL, username and password.
|
|
84
|
-
|
|
85
|
-
:param password: password
|
|
86
|
-
"""
|
|
87
|
-
self._password = password
|
|
88
|
-
|
|
89
|
-
if self.base_url.endswith("/"):
|
|
90
|
-
self.base_url = self.base_url[:-1]
|
|
91
|
-
|
|
92
|
-
self._session = requests.Session()
|
|
93
|
-
self._session.auth = HTTPBasicAuth(self.username, self._password)
|
|
94
|
-
self._session.verify = self.ssl_verify
|
|
95
|
-
|
|
96
|
-
if self.https_proxy:
|
|
97
|
-
self._session.proxies.update({"https": self.https_proxy})
|
|
98
|
-
if self.http_proxy:
|
|
99
|
-
self._session.proxies.update({"http": self.http_proxy})
|
|
100
|
-
|
|
101
|
-
if not self.namespace:
|
|
102
|
-
self.namespace = re.sub(r":\d+", "", self.base_url)
|
|
103
|
-
|
|
104
|
-
def _get_namespace(self) -> str:
|
|
105
|
-
"""Get the address of the REST API.
|
|
106
|
-
|
|
107
|
-
:return: address as a string
|
|
108
|
-
"""
|
|
109
|
-
match = re.search(r"^https?://([^:/]+)", self.base_url)
|
|
110
|
-
|
|
111
|
-
if match:
|
|
112
|
-
return match.group(1)
|
|
113
|
-
|
|
114
|
-
raise ValueError(f"Invalid URL format: {self.base_url}")
|
|
115
|
-
|
|
116
|
-
def get_root(self) -> dict | None:
|
|
117
|
-
"""Get the root of the REST API.
|
|
118
|
-
|
|
119
|
-
:return: root data as a dictionary or None if an error occurred
|
|
120
|
-
"""
|
|
121
|
-
url = f"{self.base_url}/shells"
|
|
122
|
-
|
|
123
|
-
try:
|
|
124
|
-
response = self._session.get(url, headers=HEADERS, timeout=2)
|
|
125
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
126
|
-
|
|
127
|
-
if response.status_code != STATUS_CODE_200:
|
|
128
|
-
log_response_errors(response)
|
|
129
|
-
return None
|
|
130
|
-
|
|
131
|
-
except requests.exceptions.RequestException as e:
|
|
132
|
-
logger.error(f"Error call REST API: {e}")
|
|
133
|
-
return None
|
|
134
|
-
|
|
135
|
-
return json.loads(response.content)
|
|
136
|
-
|
|
137
|
-
def post_shells(self, aas: AssetAdministrationShell) -> dict | None:
|
|
138
|
-
"""Post an Asset Administration Shell (AAS) to the REST API.
|
|
139
|
-
|
|
140
|
-
:param aas: Asset Administration Shell to post
|
|
141
|
-
:return: Response data as a dictionary or None if an error occurred
|
|
142
|
-
"""
|
|
143
|
-
aas_dict_string = json.dumps(aas, cls=basyx.aas.adapter.json.AASToJsonEncoder)
|
|
144
|
-
aas_dict = json.loads(aas_dict_string)
|
|
145
|
-
|
|
146
|
-
url = f"{self.base_url}/shells"
|
|
147
|
-
logger.debug(f"Call REST API url '{url}'")
|
|
148
|
-
|
|
149
|
-
try:
|
|
150
|
-
response = self._session.post(url, headers=HEADERS, json=aas_dict, timeout=self.time_out)
|
|
151
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
152
|
-
|
|
153
|
-
if response.status_code not in (STATUS_CODE_201, STATUS_CODE_202):
|
|
154
|
-
log_response_errors(response)
|
|
155
|
-
return None
|
|
156
|
-
|
|
157
|
-
except requests.exceptions.RequestException as e:
|
|
158
|
-
logger.error(f"Error call REST API: {e}")
|
|
159
|
-
return None
|
|
160
|
-
|
|
161
|
-
content = json.loads(response.content)
|
|
162
|
-
return dict(sorted(content.items()))
|
|
163
|
-
|
|
164
|
-
def put_shells(self, identifier: str, aas: AssetAdministrationShell) -> bool:
|
|
165
|
-
"""Update an Asset Administration Shell (AAS) by its ID in the REST API.
|
|
166
|
-
|
|
167
|
-
:param identifier: Identifier of the AAS to update
|
|
168
|
-
:param aas: Asset Administration Shell data to update
|
|
169
|
-
:return: True if the update was successful, False otherwise
|
|
170
|
-
"""
|
|
171
|
-
aas_dict_string = json.dumps(aas, cls=basyx.aas.adapter.json.AASToJsonEncoder)
|
|
172
|
-
aas_dict = json.loads(aas_dict_string)
|
|
173
|
-
|
|
174
|
-
decoded_identifier: str = decode_base_64(identifier)
|
|
175
|
-
url = f"{self.base_url}/shells/{decoded_identifier}"
|
|
176
|
-
|
|
177
|
-
try:
|
|
178
|
-
response = self._session.put(url, headers=HEADERS, json=aas_dict, timeout=self.time_out)
|
|
179
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
180
|
-
|
|
181
|
-
if response.status_code is not STATUS_CODE_204:
|
|
182
|
-
log_response_errors(response)
|
|
183
|
-
return False
|
|
184
|
-
|
|
185
|
-
except requests.exceptions.RequestException as e:
|
|
186
|
-
logger.error(f"Error call REST API: {e}")
|
|
187
|
-
return False
|
|
188
|
-
|
|
189
|
-
return True
|
|
190
|
-
|
|
191
|
-
def put_shells_submodels(self, aas_id: str, submodel_id: str, submodel: Submodel) -> bool:
|
|
192
|
-
"""Update a submodel by its ID for a specific Asset Administration Shell (AAS).
|
|
193
|
-
|
|
194
|
-
:param aas_id: ID of the AAS to update the submodel for
|
|
195
|
-
:param submodel: Submodel data to update
|
|
196
|
-
:return: True if the update was successful, False otherwise
|
|
197
|
-
"""
|
|
198
|
-
sm_dict_string = json.dumps(submodel, cls=basyx.aas.adapter.json.AASToJsonEncoder)
|
|
199
|
-
sm_dict = json.loads(sm_dict_string)
|
|
200
|
-
|
|
201
|
-
decoded_aas_id: str = decode_base_64(aas_id)
|
|
202
|
-
decoded_submodel_id: str = decode_base_64(submodel_id)
|
|
203
|
-
url = f"{self.base_url}/shells/{decoded_aas_id}/submodels/{decoded_submodel_id}"
|
|
204
|
-
|
|
205
|
-
try:
|
|
206
|
-
response = self._session.put(url, headers=HEADERS, json=sm_dict, timeout=self.time_out)
|
|
207
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
208
|
-
|
|
209
|
-
if response.status_code != STATUS_CODE_204:
|
|
210
|
-
log_response_errors(response)
|
|
211
|
-
return False
|
|
212
|
-
|
|
213
|
-
except requests.exceptions.RequestException as e:
|
|
214
|
-
logger.error(f"Error call REST API: {e}")
|
|
215
|
-
return False
|
|
216
|
-
|
|
217
|
-
return True
|
|
218
|
-
|
|
219
|
-
def get_shells(self) -> list[AssetAdministrationShell] | None:
|
|
220
|
-
"""Get all Asset Administration Shells (AAS) from the REST API.
|
|
221
|
-
|
|
222
|
-
:return: AAS objects or None if an error occurred
|
|
223
|
-
"""
|
|
224
|
-
url = f"{self.base_url}/shells"
|
|
225
|
-
|
|
226
|
-
try:
|
|
227
|
-
response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
|
|
228
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
229
|
-
|
|
230
|
-
if response.status_code != STATUS_CODE_200:
|
|
231
|
-
log_response_errors(response)
|
|
232
|
-
return None
|
|
233
|
-
|
|
234
|
-
except requests.exceptions.RequestException as e:
|
|
235
|
-
logger.error(f"Error call REST API: {e}")
|
|
236
|
-
return None
|
|
237
|
-
|
|
238
|
-
content: list = json.loads(response.content)
|
|
239
|
-
|
|
240
|
-
if not content:
|
|
241
|
-
logger.warning("No AAS found in the REST API.")
|
|
242
|
-
return []
|
|
243
|
-
|
|
244
|
-
results: list = content.get("result", [])
|
|
245
|
-
if not results:
|
|
246
|
-
logger.warning("No AAS found in the REST API results.")
|
|
247
|
-
return []
|
|
248
|
-
|
|
249
|
-
aas_list: list[AssetAdministrationShell] = []
|
|
250
|
-
|
|
251
|
-
for result in results:
|
|
252
|
-
if not isinstance(result, dict):
|
|
253
|
-
logger.error(f"Invalid AAS data: {result}")
|
|
254
|
-
return None
|
|
255
|
-
|
|
256
|
-
aas_dict_string = json.dumps(result)
|
|
257
|
-
aas = json.loads(aas_dict_string, cls=basyx.aas.adapter.json.AASFromJsonDecoder)
|
|
258
|
-
aas_list.append(aas)
|
|
259
|
-
|
|
260
|
-
return aas_list
|
|
261
|
-
|
|
262
|
-
def get_shells_by_id(self, aas_id: str) -> AssetAdministrationShell | None:
|
|
263
|
-
"""Get an Asset Administration Shell (AAS) by its ID from the REST API.
|
|
264
|
-
|
|
265
|
-
:param aas_id: ID of the AAS to retrieve
|
|
266
|
-
:return: AAS object or None if an error occurred
|
|
267
|
-
"""
|
|
268
|
-
decoded_aas_id: str = decode_base_64(aas_id)
|
|
269
|
-
url = f"{self.base_url}/shells/{decoded_aas_id}"
|
|
270
|
-
|
|
271
|
-
try:
|
|
272
|
-
response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
|
|
273
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
274
|
-
|
|
275
|
-
if response.status_code != STATUS_CODE_200:
|
|
276
|
-
log_response_errors(response)
|
|
277
|
-
return None
|
|
278
|
-
|
|
279
|
-
except requests.exceptions.RequestException as e:
|
|
280
|
-
logger.error(f"Error call REST API: {e}")
|
|
281
|
-
return None
|
|
282
|
-
|
|
283
|
-
aas_dict_string = response.content.decode("utf-8")
|
|
284
|
-
return json.loads(aas_dict_string, cls=basyx.aas.adapter.json.AASFromJsonDecoder)
|
|
285
|
-
|
|
286
|
-
def get_shells_reference_by_id(self, aas_id: str) -> Reference | None:
|
|
287
|
-
decoded_aas_id: str = decode_base_64(aas_id)
|
|
288
|
-
url = f"{self.base_url}/shells/{decoded_aas_id}/$reference"
|
|
289
|
-
|
|
290
|
-
try:
|
|
291
|
-
response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
|
|
292
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
293
|
-
|
|
294
|
-
if response.status_code != STATUS_CODE_200:
|
|
295
|
-
log_response_errors(response)
|
|
296
|
-
return None
|
|
297
|
-
|
|
298
|
-
except requests.exceptions.RequestException as e:
|
|
299
|
-
logger.error(f"Error call REST API: {e}")
|
|
300
|
-
return None
|
|
301
|
-
|
|
302
|
-
ref_dict_string = response.content.decode("utf-8")
|
|
303
|
-
return json.loads(ref_dict_string, cls=basyx.aas.adapter.json.AASFromJsonDecoder)
|
|
304
|
-
|
|
305
|
-
def get_shells_submodels(self, aas_id: str, submodel_id: str) -> Submodel | None:
|
|
306
|
-
"""Get a submodel by its ID for a specific Asset Administration Shell (AAS).
|
|
307
|
-
|
|
308
|
-
:param aas_id: ID of the AAS to retrieve the submodel from
|
|
309
|
-
:param submodel_id: ID of the submodel to retrieve
|
|
310
|
-
:return: Submodel object or None if an error occurred
|
|
311
|
-
"""
|
|
312
|
-
decoded_aas_id: str = decode_base_64(aas_id)
|
|
313
|
-
decoded_submodel_id: str = decode_base_64(submodel_id)
|
|
314
|
-
|
|
315
|
-
url = f"{self.base_url}/shells/{decoded_aas_id}/submodels/{decoded_submodel_id}"
|
|
316
|
-
|
|
317
|
-
try:
|
|
318
|
-
response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
|
|
319
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
320
|
-
|
|
321
|
-
if response.status_code != STATUS_CODE_200:
|
|
322
|
-
log_response_errors(response)
|
|
323
|
-
return None
|
|
324
|
-
|
|
325
|
-
except requests.exceptions.RequestException as e:
|
|
326
|
-
logger.error(f"Error call REST API: {e}")
|
|
327
|
-
return None
|
|
328
|
-
|
|
329
|
-
submodel_dict_string = response.content.decode("utf-8")
|
|
330
|
-
return json.loads(submodel_dict_string, cls=basyx.aas.adapter.json.AASFromJsonDecoder)
|
|
331
|
-
|
|
332
|
-
def delete_shells_by_id(self, aas_id: str) -> bool:
|
|
333
|
-
"""Get an Asset Administration Shell (AAS) by its ID from the REST API.
|
|
334
|
-
|
|
335
|
-
:param aas_id: ID of the AAS to retrieve
|
|
336
|
-
:return: True if the deletion was successful, False otherwise
|
|
337
|
-
"""
|
|
338
|
-
decoded_aas_id: str = decode_base_64(aas_id)
|
|
339
|
-
url = f"{self.base_url}/shells/{decoded_aas_id}"
|
|
340
|
-
|
|
341
|
-
try:
|
|
342
|
-
response = self._session.delete(url, headers=HEADERS, timeout=self.time_out)
|
|
343
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
344
|
-
|
|
345
|
-
if response.status_code != STATUS_CODE_204:
|
|
346
|
-
log_response_errors(response)
|
|
347
|
-
return False
|
|
348
|
-
|
|
349
|
-
except requests.exceptions.RequestException as e:
|
|
350
|
-
logger.error(f"Error call REST API: {e}")
|
|
351
|
-
return False
|
|
352
|
-
|
|
353
|
-
return True
|
|
354
|
-
|
|
355
|
-
def post_submodels(self, submodel: Submodel) -> bool:
|
|
356
|
-
"""Post a submodel to the REST API.
|
|
357
|
-
|
|
358
|
-
:param submodel: submodel data as a dictionary
|
|
359
|
-
:return: Response data as a dictionary or None if an error occurred
|
|
360
|
-
"""
|
|
361
|
-
sm_dict_string = json.dumps(submodel, cls=basyx.aas.adapter.json.AASToJsonEncoder)
|
|
362
|
-
sm_dict = json.loads(sm_dict_string)
|
|
363
|
-
|
|
364
|
-
url = f"{self.base_url}/submodels"
|
|
365
|
-
|
|
366
|
-
try:
|
|
367
|
-
response = self._session.post(url, headers=HEADERS, json=sm_dict, timeout=self.time_out)
|
|
368
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
369
|
-
|
|
370
|
-
if response.status_code not in (STATUS_CODE_201, STATUS_CODE_202):
|
|
371
|
-
log_response_errors(response)
|
|
372
|
-
return False
|
|
373
|
-
|
|
374
|
-
except requests.exceptions.RequestException as e:
|
|
375
|
-
logger.error(f"Error call REST API: {e}")
|
|
376
|
-
return False
|
|
377
|
-
|
|
378
|
-
return True
|
|
379
|
-
|
|
380
|
-
def put_submodels(self, identifier: str, submodel: Submodel) -> bool:
|
|
381
|
-
"""Update a submodel by its ID in the REST API.
|
|
382
|
-
|
|
383
|
-
:param identifier: Identifier of the submodel to update
|
|
384
|
-
:param submodel: Submodel data to update
|
|
385
|
-
:return: True if the update was successful, False otherwise
|
|
386
|
-
"""
|
|
387
|
-
sm_dict_string = json.dumps(submodel, cls=basyx.aas.adapter.json.AASToJsonEncoder)
|
|
388
|
-
sm_dict = json.loads(sm_dict_string)
|
|
389
|
-
|
|
390
|
-
decoded_identifier: str = decode_base_64(identifier)
|
|
391
|
-
url = f"{self.base_url}/submodels/{decoded_identifier}"
|
|
392
|
-
|
|
393
|
-
try:
|
|
394
|
-
response = self._session.put(url, headers=HEADERS, json=sm_dict, timeout=self.time_out)
|
|
395
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
396
|
-
|
|
397
|
-
if response.status_code != STATUS_CODE_204:
|
|
398
|
-
log_response_errors(response)
|
|
399
|
-
return False
|
|
400
|
-
|
|
401
|
-
except requests.exceptions.RequestException as e:
|
|
402
|
-
logger.error(f"Error call REST API: {e}")
|
|
403
|
-
return False
|
|
404
|
-
|
|
405
|
-
return True
|
|
406
|
-
|
|
407
|
-
def get_submodel_by_id(self, submodel_id: str) -> Submodel | None:
|
|
408
|
-
"""Get a submodel by its ID from the REST API.
|
|
409
|
-
|
|
410
|
-
:param submodel_id: ID of the submodel to retrieve
|
|
411
|
-
:return: Submodel object or None if an error occurred
|
|
412
|
-
"""
|
|
413
|
-
decoded_submodel_id: str = decode_base_64(submodel_id)
|
|
414
|
-
url = f"{self.base_url}/submodels/{decoded_submodel_id}"
|
|
415
|
-
|
|
416
|
-
try:
|
|
417
|
-
response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
|
|
418
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
419
|
-
|
|
420
|
-
if response.status_code != STATUS_CODE_200:
|
|
421
|
-
log_response_errors(response)
|
|
422
|
-
return None
|
|
423
|
-
|
|
424
|
-
except requests.exceptions.RequestException as e:
|
|
425
|
-
logger.error(f"Error call REST API: {e}")
|
|
426
|
-
return None
|
|
427
|
-
|
|
428
|
-
sm_dict_string = response.content.decode("utf-8")
|
|
429
|
-
return json.loads(sm_dict_string, cls=basyx.aas.adapter.json.AASFromJsonDecoder)
|
|
430
|
-
|
|
431
|
-
def get_submodels(self) -> list[Submodel] | None:
|
|
432
|
-
"""Get all submodels from the REST API.
|
|
433
|
-
|
|
434
|
-
:return: Submodel objects or None if an error occurred
|
|
435
|
-
"""
|
|
436
|
-
url = f"{self.base_url}/submodels"
|
|
437
|
-
|
|
438
|
-
try:
|
|
439
|
-
response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
|
|
440
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
441
|
-
|
|
442
|
-
if response.status_code != STATUS_CODE_200:
|
|
443
|
-
log_response_errors(response)
|
|
444
|
-
return None
|
|
445
|
-
|
|
446
|
-
except requests.exceptions.RequestException as e:
|
|
447
|
-
logger.error(f"Error call REST API: {e}")
|
|
448
|
-
return None
|
|
449
|
-
|
|
450
|
-
content: list = json.loads(response.content)
|
|
451
|
-
|
|
452
|
-
if not content:
|
|
453
|
-
logger.warning("No submodels found in the REST API.")
|
|
454
|
-
return []
|
|
455
|
-
|
|
456
|
-
results: list = content.get("result", [])
|
|
457
|
-
if not results:
|
|
458
|
-
logger.warning("No submodels found in the REST API results.")
|
|
459
|
-
return []
|
|
460
|
-
|
|
461
|
-
submodels: list[Submodel] = []
|
|
462
|
-
|
|
463
|
-
for result in results:
|
|
464
|
-
if not isinstance(result, dict):
|
|
465
|
-
logger.error(f"Invalid submodel data: {result}")
|
|
466
|
-
return None
|
|
467
|
-
|
|
468
|
-
sm_dict_string = json.dumps(result)
|
|
469
|
-
submodel = json.loads(sm_dict_string, cls=basyx.aas.adapter.json.AASFromJsonDecoder)
|
|
470
|
-
submodels.append(submodel)
|
|
471
|
-
|
|
472
|
-
return submodels
|
|
473
|
-
|
|
474
|
-
def get_submodels_by_id(self, submodel_id: str) -> Submodel | None:
|
|
475
|
-
"""Get a submodel by its ID from the REST API.
|
|
476
|
-
|
|
477
|
-
:param submodel_id: ID of the submodel to retrieve
|
|
478
|
-
:return: Submodel object or None if an error occurred
|
|
479
|
-
"""
|
|
480
|
-
decoded_submodel_id: str = decode_base_64(submodel_id)
|
|
481
|
-
url = f"{self.base_url}/submodels/{decoded_submodel_id}"
|
|
482
|
-
|
|
483
|
-
try:
|
|
484
|
-
response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
|
|
485
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
486
|
-
|
|
487
|
-
if response.status_code != STATUS_CODE_200:
|
|
488
|
-
log_response_errors(response)
|
|
489
|
-
return None
|
|
490
|
-
|
|
491
|
-
except requests.exceptions.RequestException as e:
|
|
492
|
-
logger.error(f"Error call REST API: {e}")
|
|
493
|
-
return None
|
|
494
|
-
|
|
495
|
-
sm_dict_string = response.content.decode("utf-8")
|
|
496
|
-
return json.loads(sm_dict_string, cls=basyx.aas.adapter.json.AASFromJsonDecoder)
|
|
497
|
-
|
|
498
|
-
def patch_submodel_by_id(self, submodel_id: str, submodel: Submodel):
|
|
499
|
-
sm_dict_string = json.dumps(submodel, cls=basyx.aas.adapter.json.AASToJsonEncoder)
|
|
500
|
-
sm_dict = json.loads(sm_dict_string)
|
|
501
|
-
|
|
502
|
-
decoded_submodel_id: str = decode_base_64(submodel_id)
|
|
503
|
-
url = f"{self.base_url}/submodels/{decoded_submodel_id}"
|
|
504
|
-
|
|
505
|
-
try:
|
|
506
|
-
response = self._session.patch(url, headers=HEADERS, json=sm_dict, timeout=self.time_out)
|
|
507
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
508
|
-
|
|
509
|
-
if response.status_code != STATUS_CODE_204:
|
|
510
|
-
log_response_errors(response)
|
|
511
|
-
return False
|
|
512
|
-
|
|
513
|
-
except requests.exceptions.RequestException as e:
|
|
514
|
-
logger.error(f"Error call REST API: {e}")
|
|
515
|
-
return False
|
|
516
|
-
|
|
517
|
-
return True
|
|
518
|
-
|
|
519
|
-
def delete_submodels_by_id(self, submodel_id: str) -> bool:
|
|
520
|
-
"""Delete a submodel by its ID from the REST API.
|
|
521
|
-
|
|
522
|
-
:param submodel_id: ID of the submodel to delete
|
|
523
|
-
:return: True if the deletion was successful, False otherwise
|
|
524
|
-
"""
|
|
525
|
-
decoded_submodel_id: str = decode_base_64(submodel_id)
|
|
526
|
-
url = f"{self.base_url}/submodels/{decoded_submodel_id}"
|
|
527
|
-
|
|
528
|
-
try:
|
|
529
|
-
response = self._session.delete(url, headers=HEADERS, timeout=self.time_out)
|
|
530
|
-
logger.debug(f"Call REST API url '{response.url}'")
|
|
531
|
-
|
|
532
|
-
if response.status_code != STATUS_CODE_204:
|
|
533
|
-
log_response_errors(response)
|
|
534
|
-
return False
|
|
535
|
-
|
|
536
|
-
except requests.exceptions.RequestException as e:
|
|
537
|
-
logger.error(f"Error call REST API: {e}")
|
|
538
|
-
return False
|
|
539
|
-
|
|
540
|
-
return True
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
def create_client_by_url(
|
|
544
|
-
base_url: str,
|
|
545
|
-
username: str = "",
|
|
546
|
-
password: str = "",
|
|
547
|
-
http_proxy: str = "",
|
|
548
|
-
https_proxy: str = "",
|
|
549
|
-
time_out: int = 200,
|
|
550
|
-
connection_time_out: int = 60,
|
|
551
|
-
ssl_verify: str = True, # noqa: FBT002
|
|
552
|
-
namespace: str = "",
|
|
553
|
-
) -> AasxServerInterface | None:
|
|
554
|
-
"""Create a BaSyx server interface client from the given parameters.
|
|
555
|
-
|
|
556
|
-
:param base_url: base URL of the BaSyx server, e.g. "http://basyx_python_server:80/"_
|
|
557
|
-
:param username: username for the BaSyx server interface client, defaults to ""_
|
|
558
|
-
:param password: password for the BaSyx server interface client, defaults to ""_
|
|
559
|
-
:param http_proxy: http proxy URL, defaults to ""_
|
|
560
|
-
:param https_proxy: https proxy URL, defaults to ""_
|
|
561
|
-
:param time_out: timeout for the API calls, defaults to 200
|
|
562
|
-
:param connection_time_out: timeout for the connection to the API, defaults to 60
|
|
563
|
-
:param ssl_verify: whether to verify SSL certificates, defaults to True
|
|
564
|
-
:return: An instance of AasxServerInterface initialized with the provided parameters.
|
|
565
|
-
"""
|
|
566
|
-
logger.info(f"Create BaSyx server interface client from URL '{base_url}'")
|
|
567
|
-
config_dict: dict[str, str] = {}
|
|
568
|
-
config_dict["base_url"] = base_url
|
|
569
|
-
config_dict["username"] = username
|
|
570
|
-
config_dict["http_proxy"] = http_proxy
|
|
571
|
-
config_dict["https_proxy"] = https_proxy
|
|
572
|
-
config_dict["time_out"] = time_out
|
|
573
|
-
config_dict["connection_time_out"] = connection_time_out
|
|
574
|
-
config_dict["ssl_verify"] = ssl_verify
|
|
575
|
-
config_dict["namespace"] = namespace
|
|
576
|
-
config_string = json.dumps(config_dict, indent=4)
|
|
577
|
-
return _create_client(config_string, password)
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
def create_client_by_config(config_file: Path, password: str = "") -> AasxServerInterface | None:
|
|
581
|
-
"""Create a BaSyx server interface client from the given parameters.
|
|
582
|
-
|
|
583
|
-
:param config_file: Path to the configuration file containing the BaSyx server connection settings.
|
|
584
|
-
:param password: password for the BaSyx server interface client, defaults to ""_
|
|
585
|
-
:return: An instance of AasxServerInterface initialized with the provided parameters.
|
|
586
|
-
"""
|
|
587
|
-
logger.info(f"Create BaSyx server interface client from config file '{config_file}'")
|
|
588
|
-
if not config_file.exists():
|
|
589
|
-
config_string = "{}"
|
|
590
|
-
logger.warning(f"Server config file '{config_file}' not found. Using default config.")
|
|
591
|
-
else:
|
|
592
|
-
config_string = config_file.read_text(encoding="utf-8")
|
|
593
|
-
logger.debug(f"Server config file '{config_file}' found.")
|
|
594
|
-
|
|
595
|
-
return _create_client(config_string, password)
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
def _create_client(config_string: str, password) -> AasxServerInterface | None:
|
|
599
|
-
try:
|
|
600
|
-
connection_settings = AasxServerInterface.model_validate_json(config_string)
|
|
601
|
-
client = AasxServerInterface(**connection_settings.model_dump())
|
|
602
|
-
except ValidationError as ve:
|
|
603
|
-
raise ValidationError(f"Invalid BaSyx server connection file: {ve}") from ve
|
|
604
|
-
|
|
605
|
-
logger.info(
|
|
606
|
-
f"Using server configuration: '{client.base_url}' | "
|
|
607
|
-
f"timeout: '{client.time_out}' | "
|
|
608
|
-
f"username: '{client.username}' | "
|
|
609
|
-
f"https_proxy: '{client.https_proxy}' | "
|
|
610
|
-
f"http_proxy: '{client.http_proxy}' | "
|
|
611
|
-
f"connection_timeout: '{client.connection_time_out}'"
|
|
612
|
-
)
|
|
613
|
-
client.initialize(password)
|
|
614
|
-
|
|
615
|
-
# test the connection to the REST API
|
|
616
|
-
connected = _connect_to_api(client)
|
|
617
|
-
|
|
618
|
-
if not connected:
|
|
619
|
-
return None
|
|
620
|
-
|
|
621
|
-
return client
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
def _connect_to_api(client: AasxServerInterface) -> bool:
|
|
625
|
-
start_time = time.time()
|
|
626
|
-
logger.debug(f"Try to connect to REST API '{client.base_url}' for {client.connection_time_out} seconds")
|
|
627
|
-
counter: int = 0
|
|
628
|
-
while True:
|
|
629
|
-
try:
|
|
630
|
-
root = client.get_root()
|
|
631
|
-
if root:
|
|
632
|
-
logger.info(f"Connected to REST API at '{client.base_url}' successfully.")
|
|
633
|
-
return True
|
|
634
|
-
except requests.exceptions.ConnectionError:
|
|
635
|
-
pass
|
|
636
|
-
if time.time() - start_time > client.connection_time_out:
|
|
637
|
-
raise TimeoutError(f"Connection to REST API timed out after {client.connection_time_out} seconds.")
|
|
638
|
-
|
|
639
|
-
counter += 1
|
|
640
|
-
logger.warning(f"Retrying connection (attempt: {counter})")
|
|
641
|
-
time.sleep(1)
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: aas-http-client
|
|
3
|
-
Version: 0.1.7
|
|
4
|
-
Summary: Generic HTTP client for communicating with various types of AAS servers
|
|
5
|
-
Author-email: Daniel Klein <daniel.klein@em.ag>
|
|
6
|
-
License: MIT License
|
|
7
|
-
|
|
8
|
-
Copyright (c) 2025 Fluid4.0
|
|
9
|
-
|
|
10
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
-
in the Software without restriction, including without limitation the rights
|
|
13
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
-
furnished to do so, subject to the following conditions:
|
|
16
|
-
|
|
17
|
-
The above copyright notice and this permission notice shall be included in all
|
|
18
|
-
copies or substantial portions of the Software.
|
|
19
|
-
|
|
20
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
-
SOFTWARE.
|
|
27
|
-
|
|
28
|
-
Project-URL: Homepage, https://github.com/fluid40/aas-http-client
|
|
29
|
-
Description-Content-Type: text/markdown
|
|
30
|
-
License-File: LICENSE
|
|
31
|
-
Dynamic: license-file
|
|
32
|
-
|
|
33
|
-
<!-- TODO: Go through the readme and enter the information here -->
|
|
34
|
-
|
|
35
|
-
# AAS HTTP Client
|
|
36
|
-
|
|
37
|
-
<div align="center">
|
|
38
|
-
<!-- change this to your projects logo if you have on.
|
|
39
|
-
If you don't have one it might be worth trying chatgpt dall-e to create one for you...
|
|
40
|
-
-->
|
|
41
|
-
<img src="docs/assets/fluid_logo.svg" alt="aas_http_client" width=500 />
|
|
42
|
-
</div>
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
[](LICENSES/LicenseRef-em.txt)
|
|
47
|
-
[](https://github.com/engineering-methods/basyx_python_poc/actions)
|
|
48
|
-
|
|
49
|
-
Proof of concept for a AAS application using the BaSyx Python SDK and Python AAS Server.
|
|
50
|
-
|
|
51
|
-
## Links
|
|
52
|
-
|
|
53
|
-
🚀 [Getting Started](docs/getting_started.md)
|
|
54
|
-
|
|
55
|
-
💻 [Developer Quickstart](docs/dev_guide.md)
|
|
56
|
-
|
|
57
|
-
👨⚕️ [Troubleshooting](docs/troubleshooting.md)
|
|
58
|
-
|
|
59
|
-
🤖 [Releases](https://github.com/engineering-methods/basyx_python_poc/releases)
|
|
60
|
-
|
|
61
|
-
📦 [Pypi Packages](https://pypi.org/project/aas-http-client/)
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
aas_http_client/__init__.py,sha256=cAr1mQzWp0G0LKtkAOYzc9t95OY3jM3Aj4bKnxx0Dso,901
|
|
2
|
-
aas_http_client/client.py,sha256=KppHWikO21T8Z_JP-bbc9DeyCsgFXQ6xTQoOfETJmcU,20672
|
|
3
|
-
aas_http_client/core/encoder.py,sha256=FS7P0FPakzFsGz70eRFDHQZFA_2nlKLlWIxavtnFrPg,660
|
|
4
|
-
aas_http_client/core/version_check.py,sha256=721Zs3xSRrJTYZtAxkaUWg9LLKtpU7oFM62DzQHZdE4,705
|
|
5
|
-
aas_http_client/demo/demo_process.py,sha256=-9-oQEi_B08W-POl2SVP4COodGPHN3mTw9rrPgLLpeY,2170
|
|
6
|
-
aas_http_client/demo/logging_handler.py,sha256=VJtZ4u3x_LhYZQtfNck7FuXhGFZm7gid0uDhvf9GjJ8,5596
|
|
7
|
-
aas_http_client/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
aas_http_client/utilities/model_builder.py,sha256=jOFTvZPDG08VgrkkRgw0SZz1e_NjVi_Kx2Ty8lqp35I,3886
|
|
9
|
-
aas_http_client/wrapper/python_sdk_wrapper_tmp.py,sha256=J1ze7TXKMiOjA5dDVNrc-GRoxQ_9r7rU8qW8C0yErG0,24206
|
|
10
|
-
aas_http_client/wrapper/sdk_wrapper.py,sha256=sUfdawn9TWNbs7YELBTuMKBhiqd_eeQwk-AmzPWgLII,11117
|
|
11
|
-
aas_http_client-0.1.7.dist-info/licenses/LICENSE,sha256=simqYMD2P9Ikm0Kh9n7VGNpaVcm2TMVVQmECYZ_xVZ8,1065
|
|
12
|
-
aas_http_client-0.1.7.dist-info/METADATA,sha256=WwFnZPlDsH_T8MLPyYvaFPM2PpX8XSl5yUUscqiuMrI,2654
|
|
13
|
-
aas_http_client-0.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
14
|
-
aas_http_client-0.1.7.dist-info/top_level.txt,sha256=vzvoz2vjeTLwpuz-Y-eEfoQ7T3byoaKshVlFMFH5NaM,16
|
|
15
|
-
aas_http_client-0.1.7.dist-info/RECORD,,
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Fluid4.0
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
File without changes
|
|
File without changes
|