aas-http-client 0.1.3__tar.gz → 0.1.5__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.

Potentially problematic release.


This version of aas-http-client might be problematic. Click here for more details.

Files changed (22) hide show
  1. {aas_http_client-0.1.3 → aas_http_client-0.1.5}/PKG-INFO +1 -1
  2. aas_http_client-0.1.5/aas_http_client/__init__.py +20 -0
  3. aas_http_client-0.1.5/aas_http_client/client.py +567 -0
  4. aas_http_client-0.1.5/aas_http_client/core/version_check.py +17 -0
  5. aas_http_client-0.1.5/aas_http_client/demo/demo_process.py +74 -0
  6. aas_http_client-0.1.5/aas_http_client/demo/logging_handler.py +177 -0
  7. aas_http_client-0.1.5/aas_http_client/utilities/__init__.py +0 -0
  8. aas_http_client-0.1.5/aas_http_client/utilities/model_builder.py +114 -0
  9. aas_http_client-0.1.3/aas_http_client/client.py → aas_http_client-0.1.5/aas_http_client/wrapper/python_sdk_wrapper_tmp.py +1 -1
  10. aas_http_client-0.1.5/aas_http_client/wrapper/sdk_wrapper.py +272 -0
  11. {aas_http_client-0.1.3 → aas_http_client-0.1.5}/aas_http_client.egg-info/PKG-INFO +1 -1
  12. aas_http_client-0.1.5/aas_http_client.egg-info/SOURCES.txt +18 -0
  13. {aas_http_client-0.1.3 → aas_http_client-0.1.5}/pyproject.toml +1 -1
  14. aas_http_client-0.1.5/tests/test_client.py +21 -0
  15. aas_http_client-0.1.3/aas_http_client/__init__.py +0 -13
  16. aas_http_client-0.1.3/aas_http_client.egg-info/SOURCES.txt +0 -10
  17. {aas_http_client-0.1.3 → aas_http_client-0.1.5}/LICENSE +0 -0
  18. {aas_http_client-0.1.3 → aas_http_client-0.1.5}/README.md +0 -0
  19. {aas_http_client-0.1.3 → aas_http_client-0.1.5}/aas_http_client/core/encoder.py +0 -0
  20. {aas_http_client-0.1.3 → aas_http_client-0.1.5}/aas_http_client.egg-info/dependency_links.txt +0 -0
  21. {aas_http_client-0.1.3 → aas_http_client-0.1.5}/aas_http_client.egg-info/top_level.txt +0 -0
  22. {aas_http_client-0.1.3 → aas_http_client-0.1.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aas-http-client
3
- Version: 0.1.3
3
+ Version: 0.1.5
4
4
  Summary: Generic HTTP client for communicating with various types of AAS servers
5
5
  Author-email: Daniel Klein <daniel.klein@em.ag>
6
6
  License: MIT License
@@ -0,0 +1,20 @@
1
+ from datetime import datetime
2
+ import importlib.metadata
3
+
4
+ __copyright__ = f"Copyright (C) {datetime.now().year} :em engineering methods AG. All rights reserved."
5
+ __author__ = "Daniel Klein"
6
+
7
+ try:
8
+ __version__ = importlib.metadata.version(__name__)
9
+ except importlib.metadata.PackageNotFoundError:
10
+ __version__ = "0.0.0-dev"
11
+
12
+ __project__ = "aas-http-client"
13
+ __package__ = "aas-http-client"
14
+
15
+ from aas_http_client.core.version_check import check_for_update
16
+ from aas_http_client.client import create_client_by_config, create_client_by_url, AasHttpClient
17
+
18
+ check_for_update()
19
+
20
+ __all__ = ["create_client_by_config", "create_client_by_url", "AasHttpClient"]
@@ -0,0 +1,567 @@
1
+ """Client for HTTP API communication with AAS server."""
2
+ import json
3
+ import logging
4
+ import time
5
+ from pathlib import Path
6
+
7
+ import basyx.aas.adapter.json
8
+ import basyx.aas.adapter.json.json_serialization as js
9
+ import requests
10
+ from basyx.aas.model import Reference, Submodel
11
+ from aas_http_client.core.encoder import decode_base_64
12
+ from pydantic import BaseModel, PrivateAttr, ValidationError
13
+ from requests import Session
14
+ from requests.auth import HTTPBasicAuth
15
+ from requests.models import Response
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ STATUS_CODE_200 = 200
20
+ STATUS_CODE_201 = 201
21
+ STATUS_CODE_202 = 202
22
+ STATUS_CODE_204 = 204
23
+ HEADERS = {"Content-Type": "application/json"}
24
+
25
+
26
+ def log_response_errors(response: Response):
27
+ """Create error messages from the response and log them.
28
+
29
+ :param response: response
30
+ """
31
+ result_error_messages: list[str] = []
32
+
33
+ try:
34
+ response_content_dict: dict = json.loads(response.content)
35
+
36
+ if "detail" in response_content_dict:
37
+ detail: dict = response_content_dict.get("detail", {})
38
+ if "error" in detail:
39
+ error: str = detail.get("error", "")
40
+ result_error_messages.append(f"{error}")
41
+ else:
42
+ result_error_messages.append(f"{detail}")
43
+
44
+ elif "messages" in response_content_dict or "Messages" in response_content_dict:
45
+ messages: list = response_content_dict.get("messages", [])
46
+
47
+ if not messages:
48
+ messages = response_content_dict.get("Messages", [])
49
+
50
+ for message in messages:
51
+ if isinstance(message, dict) and "message" in message:
52
+ result_error_messages.append(message["message"])
53
+ else:
54
+ result_error_messages.append(str(message))
55
+ elif "error" in response_content_dict:
56
+ result_error_messages.append(response_content_dict.get("error", ""))
57
+
58
+ except json.JSONDecodeError:
59
+ result_error_messages.append(response.content)
60
+
61
+ logger.error(f"Status code: {response.status_code}")
62
+ for result_error_message in result_error_messages:
63
+ logger.error(result_error_message)
64
+
65
+
66
+ class AasHttpClient(BaseModel):
67
+ """Represents a AasHttpClient to communicate with a REST API."""
68
+
69
+ base_url: str = "http://javaaasserver:5060/"
70
+ api_base_path: str = ""
71
+ username: str | None = None
72
+ _password: str | None = PrivateAttr(default=None)
73
+ https_proxy: str | None = None
74
+ http_proxy: str | None = None
75
+ time_out: int = 200
76
+ connection_time_out: int = 100
77
+ ssl_verify: bool = True
78
+ _session: Session = PrivateAttr(default=None)
79
+
80
+ def initialize(self, password: str):
81
+ """Initialize the AasHttpClient with the given URL, username and password.
82
+
83
+ :param password: password
84
+ """
85
+ self._password = password
86
+
87
+ if self.base_url.endswith("/"):
88
+ self.base_url = self.base_url[:-1]
89
+
90
+ self._session = requests.Session()
91
+ self._session.auth = HTTPBasicAuth(self.username, self._password)
92
+ self._session.verify = self.ssl_verify
93
+
94
+ if self.https_proxy:
95
+ self._session.proxies.update({"https": self.https_proxy})
96
+ if self.http_proxy:
97
+ self._session.proxies.update({"http": self.http_proxy})
98
+
99
+ def get_root(self) -> dict | None:
100
+ """Get the root of the REST API.
101
+
102
+ :return: root data as a dictionary or None if an error occurred
103
+ """
104
+ url = f"{self.base_url}/shells"
105
+
106
+ try:
107
+ response = self._session.get(url, headers=HEADERS, timeout=2)
108
+ logger.debug(f"Call REST API url '{response.url}'")
109
+
110
+ if response.status_code != STATUS_CODE_200:
111
+ log_response_errors(response)
112
+ return None
113
+
114
+ except requests.exceptions.RequestException as e:
115
+ logger.error(f"Error call REST API: {e}")
116
+ return None
117
+
118
+ content = response.content.decode("utf-8")
119
+ return json.loads(content)
120
+
121
+ def post_shells(self, aas_data: dict) -> dict | None:
122
+ """Post an Asset Administration Shell (AAS) to the REST API.
123
+
124
+ :param aas_data: Json data of the Asset Administration Shell to post
125
+ :return: Response data as a dictionary or None if an error occurred
126
+ """
127
+ url = f"{self.base_url}/shells"
128
+ logger.debug(f"Call REST API url '{url}'")
129
+
130
+ try:
131
+ response = self._session.post(url, headers=HEADERS, json=aas_data, timeout=self.time_out)
132
+ logger.debug(f"Call REST API url '{response.url}'")
133
+
134
+ if response.status_code not in (STATUS_CODE_201, STATUS_CODE_202):
135
+ log_response_errors(response)
136
+ return None
137
+
138
+ except requests.exceptions.RequestException as e:
139
+ logger.error(f"Error call REST API: {e}")
140
+ return None
141
+
142
+ content = response.content.decode("utf-8")
143
+ return json.loads(content)
144
+
145
+ def put_shells(self, identifier: str, aas_data: dict) -> bool:
146
+ """Update an Asset Administration Shell (AAS) by its ID in the REST API.
147
+
148
+ :param identifier: Identifier of the AAS to update
149
+ :param aas_data: Json data of the Asset Administration Shell data to update
150
+ :return: True if the update was successful, False otherwise
151
+ """
152
+ decoded_identifier: str = decode_base_64(identifier)
153
+ url = f"{self.base_url}/shells/{decoded_identifier}"
154
+
155
+ try:
156
+ response = self._session.put(url, headers=HEADERS, json=aas_data, timeout=self.time_out)
157
+ logger.debug(f"Call REST API url '{response.url}'")
158
+
159
+ if response.status_code is not STATUS_CODE_204:
160
+ log_response_errors(response)
161
+ return False
162
+
163
+ except requests.exceptions.RequestException as e:
164
+ logger.error(f"Error call REST API: {e}")
165
+ return False
166
+
167
+ return True
168
+
169
+ def put_shells_submodels(self, aas_id: str, submodel_id: str, submodel_data: dict) -> bool:
170
+ """Update a submodel by its ID for a specific Asset Administration Shell (AAS).
171
+
172
+ :param aas_id: ID of the AAS to update the submodel for
173
+ :param submodel_data: Json data to the Submodel to update
174
+ :return: True if the update was successful, False otherwise
175
+ """
176
+ decoded_aas_id: str = decode_base_64(aas_id)
177
+ decoded_submodel_id: str = decode_base_64(submodel_id)
178
+ url = f"{self.base_url}/shells/{decoded_aas_id}/submodels/{decoded_submodel_id}"
179
+
180
+ try:
181
+ response = self._session.put(url, headers=HEADERS, json=submodel_data, timeout=self.time_out)
182
+ logger.debug(f"Call REST API url '{response.url}'")
183
+
184
+ if response.status_code != STATUS_CODE_204:
185
+ log_response_errors(response)
186
+ return False
187
+
188
+ except requests.exceptions.RequestException as e:
189
+ logger.error(f"Error call REST API: {e}")
190
+ return False
191
+
192
+ return True
193
+
194
+ def get_shells(self) -> list[dict] | None:
195
+ """Get all Asset Administration Shells (AAS) from the REST API.
196
+
197
+ :return: List of paginated Asset Administration Shells data or None if an error occurred
198
+ """
199
+ url = f"{self.base_url}/shells"
200
+
201
+ try:
202
+ response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
203
+ logger.debug(f"Call REST API url '{response.url}'")
204
+
205
+ if response.status_code != STATUS_CODE_200:
206
+ log_response_errors(response)
207
+ return None
208
+
209
+ except requests.exceptions.RequestException as e:
210
+ logger.error(f"Error call REST API: {e}")
211
+ return None
212
+
213
+ content = response.content.decode("utf-8")
214
+ return json.loads(content)
215
+
216
+ def get_shells_by_id(self, aas_id: str) -> dict | None:
217
+ """Get an Asset Administration Shell (AAS) by its ID from the REST API.
218
+
219
+ :param aas_id: ID of the AAS to retrieve
220
+ :return: Asset Administration Shells data or None if an error occurred
221
+ """
222
+ decoded_aas_id: str = decode_base_64(aas_id)
223
+ url = f"{self.base_url}/shells/{decoded_aas_id}"
224
+
225
+ try:
226
+ response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
227
+ logger.debug(f"Call REST API url '{response.url}'")
228
+
229
+ if response.status_code != STATUS_CODE_200:
230
+ log_response_errors(response)
231
+ return None
232
+
233
+ except requests.exceptions.RequestException as e:
234
+ logger.error(f"Error call REST API: {e}")
235
+ return None
236
+
237
+ content = response.content.decode("utf-8")
238
+ return json.loads(content)
239
+
240
+
241
+ def get_shells_reference_by_id(self, aas_id: str) -> Reference | None:
242
+ decoded_aas_id: str = decode_base_64(aas_id)
243
+ url = f"{self.base_url}/shells/{decoded_aas_id}/$reference"
244
+
245
+ try:
246
+ response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
247
+ logger.debug(f"Call REST API url '{response.url}'")
248
+
249
+ if response.status_code != STATUS_CODE_200:
250
+ log_response_errors(response)
251
+ return None
252
+
253
+ except requests.exceptions.RequestException as e:
254
+ logger.error(f"Error call REST API: {e}")
255
+ return None
256
+
257
+ ref_dict_string = response.content.decode("utf-8")
258
+ return json.loads(ref_dict_string, cls=basyx.aas.adapter.json.AASFromJsonDecoder)
259
+
260
+ def get_shells_submodels(self, aas_id: str, submodel_id: str) -> Submodel | None:
261
+ """Get a submodel by its ID for a specific Asset Administration Shell (AAS).
262
+
263
+ :param aas_id: ID of the AAS to retrieve the submodel from
264
+ :param submodel_id: ID of the submodel to retrieve
265
+ :return: Submodel object or None if an error occurred
266
+ """
267
+ decoded_aas_id: str = decode_base_64(aas_id)
268
+ decoded_submodel_id: str = decode_base_64(submodel_id)
269
+
270
+ url = f"{self.base_url}/shells/{decoded_aas_id}/submodels/{decoded_submodel_id}"
271
+
272
+ try:
273
+ response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
274
+ logger.debug(f"Call REST API url '{response.url}'")
275
+
276
+ if response.status_code != STATUS_CODE_200:
277
+ log_response_errors(response)
278
+ return None
279
+
280
+ except requests.exceptions.RequestException as e:
281
+ logger.error(f"Error call REST API: {e}")
282
+ return None
283
+
284
+ content = response.content.decode("utf-8")
285
+ return json.loads(content)
286
+
287
+ def delete_shells_by_id(self, aas_id: str) -> bool:
288
+ """Get an Asset Administration Shell (AAS) by its ID from the REST API.
289
+
290
+ :param aas_id: ID of the AAS to retrieve
291
+ :return: True if the deletion was successful, False otherwise
292
+ """
293
+ decoded_aas_id: str = decode_base_64(aas_id)
294
+ url = f"{self.base_url}/shells/{decoded_aas_id}"
295
+
296
+ try:
297
+ response = self._session.delete(url, headers=HEADERS, timeout=self.time_out)
298
+ logger.debug(f"Call REST API url '{response.url}'")
299
+
300
+ if response.status_code != STATUS_CODE_204:
301
+ log_response_errors(response)
302
+ return False
303
+
304
+ except requests.exceptions.RequestException as e:
305
+ logger.error(f"Error call REST API: {e}")
306
+ return False
307
+
308
+ return True
309
+
310
+ def post_submodels(self, submodel_data: dict) -> bool:
311
+ """Post a submodel to the REST API.
312
+
313
+ :param submodel_data: Json data of the Submodel to post
314
+ :return: Response data as a dictionary or None if an error occurred
315
+ """
316
+ url = f"{self.base_url}/submodels"
317
+
318
+ try:
319
+ response = self._session.post(url, headers=HEADERS, json=submodel_data, timeout=self.time_out)
320
+ logger.debug(f"Call REST API url '{response.url}'")
321
+
322
+ if response.status_code not in (STATUS_CODE_201, STATUS_CODE_202):
323
+ log_response_errors(response)
324
+ return False
325
+
326
+ except requests.exceptions.RequestException as e:
327
+ logger.error(f"Error call REST API: {e}")
328
+ return False
329
+
330
+ return True
331
+
332
+ def put_submodels(self, identifier: str, submodel_data: dict) -> bool:
333
+ """Update a submodel by its ID in the REST API.
334
+
335
+ :param identifier: Identifier of the submodel to update
336
+ :param submodel_data: Json data of the Submodel to update
337
+ :return: True if the update was successful, False otherwise
338
+ """
339
+ decoded_identifier: str = decode_base_64(identifier)
340
+ url = f"{self.base_url}/submodels/{decoded_identifier}"
341
+
342
+ try:
343
+ response = self._session.put(url, headers=HEADERS, json=submodel_data, timeout=self.time_out)
344
+ logger.debug(f"Call REST API url '{response.url}'")
345
+
346
+ if response.status_code != STATUS_CODE_204:
347
+ log_response_errors(response)
348
+ return False
349
+
350
+ except requests.exceptions.RequestException as e:
351
+ logger.error(f"Error call REST API: {e}")
352
+ return False
353
+
354
+ return True
355
+
356
+ def get_submodel_by_id(self, submodel_id: str) -> dict | None:
357
+ """Get a submodel by its ID from the REST API.
358
+
359
+ :param submodel_id: ID of the submodel to retrieve
360
+ :return: Submodel object or None if an error occurred
361
+ """
362
+ decoded_submodel_id: str = decode_base_64(submodel_id)
363
+ url = f"{self.base_url}/submodels/{decoded_submodel_id}"
364
+
365
+ try:
366
+ response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
367
+ logger.debug(f"Call REST API url '{response.url}'")
368
+
369
+ if response.status_code != STATUS_CODE_200:
370
+ log_response_errors(response)
371
+ return None
372
+
373
+ except requests.exceptions.RequestException as e:
374
+ logger.error(f"Error call REST API: {e}")
375
+ return None
376
+
377
+ content = response.content.decode("utf-8")
378
+ return json.loads(content)
379
+
380
+ def get_submodels(self) -> list[dict] | None:
381
+ """Get all submodels from the REST API.
382
+
383
+ :return: Submodel objects or None if an error occurred
384
+ """
385
+ url = f"{self.base_url}/submodels"
386
+
387
+ try:
388
+ response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
389
+ logger.debug(f"Call REST API url '{response.url}'")
390
+
391
+ if response.status_code != STATUS_CODE_200:
392
+ log_response_errors(response)
393
+ return None
394
+
395
+ except requests.exceptions.RequestException as e:
396
+ logger.error(f"Error call REST API: {e}")
397
+ return None
398
+
399
+ content = response.content.decode("utf-8")
400
+ return json.loads(content)
401
+
402
+ def get_submodels_by_id(self, submodel_id: str) -> dict | None:
403
+ """Get a submodel by its ID from the REST API.
404
+
405
+ :param submodel_id: ID of the submodel to retrieve
406
+ :return: Submodel object or None if an error occurred
407
+ """
408
+ decoded_submodel_id: str = decode_base_64(submodel_id)
409
+ url = f"{self.base_url}/submodels/{decoded_submodel_id}"
410
+
411
+ try:
412
+ response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
413
+ logger.debug(f"Call REST API url '{response.url}'")
414
+
415
+ if response.status_code != STATUS_CODE_200:
416
+ log_response_errors(response)
417
+ return None
418
+
419
+ except requests.exceptions.RequestException as e:
420
+ logger.error(f"Error call REST API: {e}")
421
+ return None
422
+
423
+ content = response.content.decode("utf-8")
424
+ return json.loads(content)
425
+
426
+ def patch_submodel_by_id(self, submodel_id: str, submodel_data: dict):
427
+ decoded_submodel_id: str = decode_base_64(submodel_id)
428
+ url = f"{self.base_url}/submodels/{decoded_submodel_id}"
429
+
430
+ try:
431
+ response = self._session.patch(url, headers=HEADERS, json=submodel_data, timeout=self.time_out)
432
+ logger.debug(f"Call REST API url '{response.url}'")
433
+
434
+ if response.status_code != STATUS_CODE_204:
435
+ log_response_errors(response)
436
+ return False
437
+
438
+ except requests.exceptions.RequestException as e:
439
+ logger.error(f"Error call REST API: {e}")
440
+ return False
441
+
442
+ return True
443
+
444
+ def delete_submodels_by_id(self, submodel_id: str) -> bool:
445
+ """Delete a submodel by its ID from the REST API.
446
+
447
+ :param submodel_id: ID of the submodel to delete
448
+ :return: True if the deletion was successful, False otherwise
449
+ """
450
+ decoded_submodel_id: str = decode_base_64(submodel_id)
451
+ url = f"{self.base_url}/submodels/{decoded_submodel_id}"
452
+
453
+ try:
454
+ response = self._session.delete(url, headers=HEADERS, timeout=self.time_out)
455
+ logger.debug(f"Call REST API url '{response.url}'")
456
+
457
+ if response.status_code != STATUS_CODE_204:
458
+ log_response_errors(response)
459
+ return False
460
+
461
+ except requests.exceptions.RequestException as e:
462
+ logger.error(f"Error call REST API: {e}")
463
+ return False
464
+
465
+ return True
466
+
467
+
468
+ def create_client_by_url(
469
+ base_url: str,
470
+ api_base_path: str = "",
471
+ username: str = "",
472
+ password: str = "",
473
+ http_proxy: str = "",
474
+ https_proxy: str = "",
475
+ time_out: int = 200,
476
+ connection_time_out: int = 60,
477
+ ssl_verify: str = True, # noqa: FBT002
478
+ ) -> AasHttpClient | None:
479
+ """Create a AAS HTTP client from the given parameters.
480
+
481
+ :param base_url: base URL of the BaSyx server, e.g. "http://basyx_python_server:80/"_
482
+ :param username: username for the BaSyx server interface client, defaults to ""_
483
+ :param password: password for the BaSyx server interface client, defaults to ""_
484
+ :param http_proxy: http proxy URL, defaults to ""_
485
+ :param https_proxy: https proxy URL, defaults to ""_
486
+ :param time_out: timeout for the API calls, defaults to 200
487
+ :param connection_time_out: timeout for the connection to the API, defaults to 60
488
+ :param ssl_verify: whether to verify SSL certificates, defaults to True
489
+ :return: An instance of AasHttpClient initialized with the provided parameters.
490
+ """
491
+ logger.info(f"Create BaSyx server interface client from URL '{base_url}'")
492
+ config_dict: dict[str, str] = {}
493
+ config_dict["base_url"] = base_url
494
+ config_dict["api_base_path"] = api_base_path
495
+ config_dict["username"] = username
496
+ config_dict["http_proxy"] = http_proxy
497
+ config_dict["https_proxy"] = https_proxy
498
+ config_dict["time_out"] = time_out
499
+ config_dict["connection_time_out"] = connection_time_out
500
+ config_dict["ssl_verify"] = ssl_verify
501
+ config_string = json.dumps(config_dict, indent=4)
502
+ return _create_client(config_string, password)
503
+
504
+
505
+ def create_client_by_config(config_file: Path, password: str = "") -> AasHttpClient | None:
506
+ """Create a AAS HTTP client from the given parameters.
507
+
508
+ :param config_file: Path to the configuration file containing the BaSyx server connection settings.
509
+ :param password: password for the BaSyx server interface client, defaults to ""_
510
+ :return: An instance of HttpClient initialized with the provided parameters.
511
+ """
512
+ logger.info(f"Create BaSyx server interface client from config file '{config_file}'")
513
+ if not config_file.exists():
514
+ config_string = "{}"
515
+ logger.warning(f"Server config file '{config_file}' not found. Using default config.")
516
+ else:
517
+ config_string = config_file.read_text(encoding="utf-8")
518
+ logger.debug(f"Server config file '{config_file}' found.")
519
+
520
+ return _create_client(config_string, password)
521
+
522
+
523
+ def _create_client(config_string: str, password) -> AasHttpClient | None:
524
+ try:
525
+ connection_settings = AasHttpClient.model_validate_json(config_string)
526
+ client = AasHttpClient(**connection_settings.model_dump())
527
+ except ValidationError as ve:
528
+ raise ValidationError(f"Invalid BaSyx server connection file: {ve}") from ve
529
+
530
+ logger.info(
531
+ f"Using server configuration: '{client.base_url}' | "
532
+ f"API base path: '{client.api_base_path}' | "
533
+ f"timeout: '{client.time_out}' | "
534
+ f"username: '{client.username}' | "
535
+ f"https_proxy: '{client.https_proxy}' | "
536
+ f"http_proxy: '{client.http_proxy}' | "
537
+ f"connection_timeout: '{client.connection_time_out}'"
538
+ )
539
+ client.initialize(password)
540
+
541
+ # test the connection to the REST API
542
+ connected = _connect_to_api(client)
543
+
544
+ if not connected:
545
+ return None
546
+
547
+ return client
548
+
549
+
550
+ def _connect_to_api(client: AasHttpClient) -> bool:
551
+ start_time = time.time()
552
+ logger.debug(f"Try to connect to REST API '{client.base_url}' for {client.connection_time_out} seconds")
553
+ counter: int = 0
554
+ while True:
555
+ try:
556
+ root = client.get_root()
557
+ if root:
558
+ logger.info(f"Connected to REST API at '{client.base_url}' successfully.")
559
+ return True
560
+ except requests.exceptions.ConnectionError:
561
+ pass
562
+ if time.time() - start_time > client.connection_time_out:
563
+ raise TimeoutError(f"Connection to REST API timed out after {client.connection_time_out} seconds.")
564
+
565
+ counter += 1
566
+ logger.warning(f"Retrying connection (attempt: {counter})")
567
+ time.sleep(1)
@@ -0,0 +1,17 @@
1
+ import requests
2
+ import importlib.metadata
3
+
4
+ def check_for_update(package_name="aas-http-client"):
5
+ try:
6
+ current_version = importlib.metadata.version(package_name)
7
+ pypi_url = f"https://pypi.org/pypi/{package_name}/json"
8
+ latest_version = requests.get(pypi_url, timeout=3).json()["info"]["version"]
9
+
10
+ if current_version != latest_version:
11
+ print(
12
+ f"⚠️ A new version for package '{package_name}' is available: "
13
+ f"{latest_version} (currently installed: {current_version}). "
14
+ f"Use the following command to update the package: pip install --upgrade {package_name}"
15
+ )
16
+ except Exception:
17
+ pass
@@ -0,0 +1,74 @@
1
+ import logging
2
+ import aas_http_client.utilities.model_builder as model_builder
3
+ from aas_http_client.client import create_client_by_config, AasHttpClient
4
+ from aas_http_client.wrapper.sdk_wrapper import SdkWrapper, create_wrapper_by_config
5
+ from pathlib import Path
6
+ import json
7
+ import basyx.aas.adapter.json
8
+ from basyx.aas.model import AssetAdministrationShell
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ def start():
13
+ """Start the demo process."""
14
+
15
+ aas_1 = _create_shell()
16
+ aas_2 = _create_shell()
17
+
18
+ client = _create_client()
19
+ sdk_wrapper = _create_sdk_wrapper()
20
+
21
+ exist_shells = sdk_wrapper.get_shells()
22
+
23
+ for shell in exist_shells:
24
+ logger.warning(f"Delete shell '{shell.id}'")
25
+ sdk_wrapper.delete_shells_by_id(shell.id)
26
+
27
+ sdk_wrapper.post_shells(aas_1)
28
+
29
+
30
+ aas_dict_string = json.dumps(aas_2, cls=basyx.aas.adapter.json.AASToJsonEncoder)
31
+ aas_dict = json.loads(aas_dict_string)
32
+ client.post_shells(aas_dict)
33
+
34
+ shells = client.get_shells()
35
+
36
+ logger.info(f"Client created successfully. {shells}")
37
+
38
+ def _create_shell() -> AssetAdministrationShell:
39
+ # create an AAS
40
+ aas_short_id: str = model_builder.create_unique_short_id("poc_aas")
41
+ aas = model_builder.create_base_ass(aas_short_id)
42
+
43
+ # create a Submodel
44
+ sm_short_id: str = model_builder.create_unique_short_id("poc_sm")
45
+ submodel = model_builder.create_base_submodel(sm_short_id)
46
+
47
+ # add Submodel to AAS
48
+ model_builder.add_submodel_to_aas(aas, submodel)
49
+
50
+ return aas
51
+
52
+ def _create_client() -> AasHttpClient:
53
+ """Create client for java servers."""
54
+
55
+ try:
56
+ file = Path("./demo/server_config.json")
57
+ client = create_client_by_config(file, password="")
58
+ except Exception as e:
59
+ logger.error(f"Failed to create client for {file}: {e}")
60
+ pass
61
+
62
+ return client
63
+
64
+ def _create_sdk_wrapper() -> SdkWrapper:
65
+ """Create client for java servers."""
66
+
67
+ try:
68
+ file = Path("./demo/server_config.json")
69
+ client = create_wrapper_by_config(file, password="")
70
+ except Exception as e:
71
+ logger.error(f"Failed to create client for {file}: {e}")
72
+ pass
73
+
74
+ return client