aas-http-client 0.2.2__py3-none-any.whl → 0.2.4__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 CHANGED
@@ -121,6 +121,8 @@ class AasHttpClient(BaseModel):
121
121
  content = response.content.decode("utf-8")
122
122
  return json.loads(content)
123
123
 
124
+ # region shells
125
+
124
126
  def post_shells(self, aas_data: dict) -> dict | None:
125
127
  """Post an Asset Administration Shell (AAS) to the REST API.
126
128
 
@@ -311,11 +313,15 @@ class AasHttpClient(BaseModel):
311
313
 
312
314
  return True
313
315
 
314
- def post_submodels(self, submodel_data: dict) -> dict:
315
- """Post a submodel to the REST API.
316
+ # endregion
316
317
 
317
- :param submodel_data: Json data of the Submodel to post
318
- :return: Response data as a dictionary or None if an error occurred
318
+ # region submodels
319
+
320
+ def post_submodels(self, submodel_data: dict) -> dict | None:
321
+ """Create a new Submodel.
322
+
323
+ :param Submodel_data: Json data of the Submodel to post
324
+ :return: Submodel data or None if an error occurred
319
325
  """
320
326
  url = f"{self.base_url}/submodels"
321
327
 
@@ -325,19 +331,19 @@ class AasHttpClient(BaseModel):
325
331
 
326
332
  if response.status_code not in (STATUS_CODE_201, STATUS_CODE_202):
327
333
  log_response_errors(response)
328
- return False
334
+ return None
329
335
 
330
336
  except requests.exceptions.RequestException as e:
331
337
  logger.error(f"Error call REST API: {e}")
332
- return False
338
+ return None
333
339
 
334
340
  content = response.content.decode("utf-8")
335
341
  return json.loads(content)
336
342
 
337
343
  def put_submodels_by_id(self, identifier: str, submodel_data: dict) -> bool:
338
- """Update a submodel by its ID in the REST API.
344
+ """Updates a existing Submodel.
339
345
 
340
- :param identifier: Identifier of the submodel to update
346
+ :param identifier: Encoded ID of the Submodel to update
341
347
  :param submodel_data: Json data of the Submodel to update
342
348
  :return: True if the update was successful, False otherwise
343
349
  """
@@ -359,9 +365,9 @@ class AasHttpClient(BaseModel):
359
365
  return True
360
366
 
361
367
  def get_submodels(self) -> list[dict] | None:
362
- """Get all submodels from the REST API.
368
+ """Returns all Submodels
363
369
 
364
- :return: Submodel objects or None if an error occurred
370
+ :return: List of Submodel data or None if an error occurred
365
371
  """
366
372
  url = f"{self.base_url}/submodels"
367
373
 
@@ -381,10 +387,10 @@ class AasHttpClient(BaseModel):
381
387
  return json.loads(content)
382
388
 
383
389
  def get_submodels_by_id(self, submodel_id: str) -> dict | None:
384
- """Get a submodel by its ID from the REST API.
390
+ """Returns a specific Submodel.
385
391
 
386
- :param submodel_id: ID of the submodel to retrieve
387
- :return: Submodel object or None if an error occurred
392
+ :param submodel_id: Encoded ID of the Submodel to retrieve
393
+ :return: Submodel data or None if an error occurred
388
394
  """
389
395
  decoded_submodel_id: str = decode_base_64(submodel_id)
390
396
  url = f"{self.base_url}/submodels/{decoded_submodel_id}"
@@ -404,7 +410,12 @@ class AasHttpClient(BaseModel):
404
410
  content = response.content.decode("utf-8")
405
411
  return json.loads(content)
406
412
 
407
- def patch_submodel_by_id(self, submodel_id: str, submodel_data: dict):
413
+ def patch_submodel_by_id(self, submodel_id: str, submodel_data: dict) -> bool:
414
+ """Updates an existing Submodel
415
+
416
+ :param submodel_id: Encoded ID of the Submodel to delete
417
+ :return: True if the patch was successful, False otherwise
418
+ """
408
419
  decoded_submodel_id: str = decode_base_64(submodel_id)
409
420
  url = f"{self.base_url}/submodels/{decoded_submodel_id}"
410
421
 
@@ -423,9 +434,9 @@ class AasHttpClient(BaseModel):
423
434
  return True
424
435
 
425
436
  def delete_submodels_by_id(self, submodel_id: str) -> bool:
426
- """Delete a submodel by its ID from the REST API.
437
+ """Deletes a existing Submodel.
427
438
 
428
- :param submodel_id: ID of the submodel to delete
439
+ :param submodel_id: Encoded ID of the Submodel to delete
429
440
  :return: True if the deletion was successful, False otherwise
430
441
  """
431
442
  decoded_submodel_id: str = decode_base_64(submodel_id)
@@ -445,6 +456,57 @@ class AasHttpClient(BaseModel):
445
456
 
446
457
  return True
447
458
 
459
+ def get_submodels_submodel_elements(self, submodel_id: str) -> list[dict] | None:
460
+ """Returns all Submodel elements including their hierarchy.
461
+
462
+ :param submodel_id: Encoded ID of the Submodel to retrieve elements from
463
+ :return: List of Submodel element data or None if an error occurred
464
+ """
465
+ decoded_submodel_id: str = decode_base_64(submodel_id)
466
+ url = f"{self.base_url}/submodels/{decoded_submodel_id}/submodel-elements"
467
+
468
+ try:
469
+ response = self._session.get(url, headers=HEADERS, timeout=self.time_out)
470
+ logger.debug(f"Call REST API url '{response.url}'")
471
+
472
+ if response.status_code != STATUS_CODE_200:
473
+ log_response_errors(response)
474
+ return None
475
+
476
+ except requests.exceptions.RequestException as e:
477
+ logger.error(f"Error call REST API: {e}")
478
+ return None
479
+
480
+ content = response.content.decode("utf-8")
481
+ return json.loads(content)
482
+
483
+ def post_submodels_submodel_elements(self, submodel_id: str, submodel_element_data: dict) -> dict | None:
484
+ """Create a new Submodel element.
485
+
486
+ :param submodel_id: Encoded ID of the Submodel to create elements for
487
+ :return: Submodel element data or None if an error occurred
488
+ """
489
+ decoded_submodel_id: str = decode_base_64(submodel_id)
490
+ url = f"{self.base_url}/submodels/{decoded_submodel_id}/submodel-elements"
491
+
492
+ try:
493
+ response = self._session.post(url, headers=HEADERS, json=submodel_element_data, timeout=self.time_out)
494
+ logger.debug(f"Call REST API url '{response.url}'")
495
+
496
+ if response.status_code != STATUS_CODE_201:
497
+ log_response_errors(response)
498
+ return None
499
+
500
+ except requests.exceptions.RequestException as e:
501
+ logger.error(f"Error call REST API: {e}")
502
+ return None
503
+
504
+ content = response.content.decode("utf-8")
505
+ return json.loads(content)
506
+
507
+ # endregion
508
+
509
+ # region client
448
510
 
449
511
  def create_client_by_url(
450
512
  base_url: str,
@@ -480,7 +542,6 @@ def create_client_by_url(
480
542
  config_string = json.dumps(config_dict, indent=4)
481
543
  return _create_client(config_string, password)
482
544
 
483
-
484
545
  def create_client_by_config(config_file: Path, password: str = "") -> AasHttpClient | None:
485
546
  """Create a AAS HTTP client from the given parameters.
486
547
 
@@ -499,7 +560,6 @@ def create_client_by_config(config_file: Path, password: str = "") -> AasHttpCli
499
560
 
500
561
  return _create_client(config_string, password)
501
562
 
502
-
503
563
  def _create_client(config_string: str, password) -> AasHttpClient | None:
504
564
  try:
505
565
  connection_settings = AasHttpClient.model_validate_json(config_string)
@@ -525,7 +585,6 @@ def _create_client(config_string: str, password) -> AasHttpClient | None:
525
585
 
526
586
  return client
527
587
 
528
-
529
588
  def _connect_to_api(client: AasHttpClient) -> bool:
530
589
  start_time = time.time()
531
590
  logger.debug(f"Try to connect to REST API '{client.base_url}' for {client.connection_time_out} seconds")
@@ -544,3 +603,5 @@ def _connect_to_api(client: AasHttpClient) -> bool:
544
603
  counter += 1
545
604
  logger.warning(f"Retrying connection (attempt: {counter})")
546
605
  time.sleep(1)
606
+
607
+ # endregion
@@ -5,45 +5,54 @@ from aas_http_client.wrapper.sdk_wrapper import SdkWrapper, create_wrapper_by_co
5
5
  from pathlib import Path
6
6
  import json
7
7
  import basyx.aas.adapter.json
8
- from basyx.aas.model import AssetAdministrationShell
8
+ import basyx.aas.model
9
+
10
+ from basyx.aas import model
9
11
 
10
12
  logger = logging.getLogger(__name__)
11
13
 
12
14
  def start():
13
15
  """Start the demo process."""
14
-
15
- aas_1 = _create_shell()
16
- aas_2 = _create_shell()
16
+ # create a submodel element
17
+ sme_short_id: str = model_builder.create_unique_short_id("poc_sme")
18
+ sme = model_builder.create_base_submodel_element_Property(sme_short_id, model.datatypes.String, "Sample Value")
17
19
 
18
- client = _create_client()
19
- java_sdk_wrapper = _create_sdk_wrapper(Path("./aas_http_client/demo/java_server_config.json"))
20
- dotnet_sdk_wrapper = _create_sdk_wrapper(Path("./aas_http_client/demo/dotnet_server_config.json"))
21
-
22
- java_sdk_wrapper.get_shells_by_id(aas_1.id)
23
- dotnet_sdk_wrapper.get_shells_by_id(aas_1.id)
20
+ # create a submodel
21
+ sm_short_id: str = model_builder.create_unique_short_id("poc_sm")
22
+ submodel = model_builder.create_base_submodel(sm_short_id)
23
+ # add submodel element to submodel
24
+ # submodel.submodel_element.add(sme)
24
25
 
25
- java_sdk_wrapper.get_submodels_by_id(aas_1.id)
26
- dotnet_sdk_wrapper.get_submodels_by_id(aas_1.id)
26
+ # create an AAS
27
+ aas_short_id: str = model_builder.create_unique_short_id("poc_aas")
28
+ aas = model_builder.create_base_ass(aas_short_id)
27
29
 
28
- exist_shells = java_sdk_wrapper.get_shells()
29
- exist_shells = dotnet_sdk_wrapper.get_shells()
30
+ # add submodel to AAS
31
+ model_builder.add_submodel_to_aas(aas, submodel)
30
32
 
31
- for shell in exist_shells:
32
- logger.warning(f"Delete shell '{shell.id}'")
33
- java_sdk_wrapper.delete_shells_by_id(shell.id)
33
+ java_sdk_wrapper = _create_sdk_wrapper(Path("./aas_http_client/demo/java_server_config.json"))
34
+ # dotnet_sdk_wrapper = _create_sdk_wrapper(Path("./aas_http_client/demo/dotnet_server_config.json"))
34
35
 
35
- java_sdk_wrapper.post_shells(aas_1)
36
+ for existing_shell in java_sdk_wrapper.get_shells():
37
+ logger.warning(f"Delete shell '{existing_shell.id}'")
38
+ java_sdk_wrapper.delete_shells_by_id(existing_shell.id)
36
39
 
40
+ for existing_submodel in java_sdk_wrapper.get_submodels():
41
+ logger.warning(f"Delete submodel '{existing_submodel.id}'")
42
+ java_sdk_wrapper.delete_submodels_by_id(existing_submodel.id)
37
43
 
38
- aas_dict_string = json.dumps(aas_2, cls=basyx.aas.adapter.json.AASToJsonEncoder)
39
- aas_dict = json.loads(aas_dict_string)
40
- client.post_shells(aas_dict)
41
-
42
- shells = client.get_shells()
44
+ java_sdk_wrapper.post_shells(aas)
45
+ java_sdk_wrapper.post_submodels(submodel)
43
46
 
44
- logger.info(f"Client created successfully. {shells}")
47
+ shell = java_sdk_wrapper.get_shells_by_id(aas.id)
48
+ submodel = java_sdk_wrapper.get_submodels_by_id(submodel.id)
45
49
 
46
- def _create_shell() -> AssetAdministrationShell:
50
+ java_sdk_wrapper.post_submodels_submodel_elements(submodel.id, sme)
51
+
52
+ submodel = java_sdk_wrapper.get_submodels_by_id(submodel.id)
53
+
54
+
55
+ def _create_shell() -> basyx.aas.model.AssetAdministrationShell:
47
56
  # create an AAS
48
57
  aas_short_id: str = model_builder.create_unique_short_id("poc_aas")
49
58
  aas = model_builder.create_base_ass(aas_short_id)
@@ -57,11 +66,11 @@ def _create_shell() -> AssetAdministrationShell:
57
66
 
58
67
  return aas
59
68
 
60
- def _create_client() -> AasHttpClient:
69
+ def _create_client(config: Path) -> AasHttpClient:
61
70
  """Create client for java servers."""
62
71
 
63
72
  try:
64
- file = Path("./aas_http_client/demo/java_server_config.json")
73
+ file = config
65
74
  client = create_client_by_config(file, password="")
66
75
  except Exception as e:
67
76
  logger.error(f"Failed to create client for {file}: {e}")
@@ -5,15 +5,8 @@ Provides some helper methods for easier work with basyx sdk data model
5
5
 
6
6
  import uuid
7
7
 
8
- from basyx.aas.model import (
9
- AssetAdministrationShell,
10
- AssetInformation,
11
- AssetKind,
12
- ModelReference,
13
- MultiLanguageTextType,
14
- Submodel,
15
- )
16
-
8
+ from basyx.aas import model
9
+ from typing import Any
17
10
 
18
11
  def create_unique_short_id(id_short: str) -> str:
19
12
  """Generate a unique identifier string by appending a UUID to the provided ID short.
@@ -23,8 +16,29 @@ def create_unique_short_id(id_short: str) -> str:
23
16
  """
24
17
  return f"{id_short}_{str(uuid.uuid4()).replace('-', '_')}"
25
18
 
19
+ def create_base_submodel_element_Property(id_short: str, type: model.datatypes, value: Any, display_name: str = "", description: str = "") -> model.Property:
20
+ """Create a basic Property Submodel Element.
21
+ """
22
+ sme = model.Property(
23
+ id_short=id_short,
24
+ value_type=type,
25
+ value=value)
26
+
27
+ if not description:
28
+ description = f"This is the submodel element with ID short '{id_short}'"
29
+
30
+ description_text = {"en": f"{description}"}
31
+ sme.description = model.MultiLanguageTextType(description_text)
32
+
33
+ if not display_name:
34
+ display_name = "POC Submodel Element"
35
+
36
+ display_name_text = {"en": f"{display_name}"}
37
+ sme.display_name = model.MultiLanguageTextType(display_name_text)
38
+
39
+ return sme
26
40
 
27
- def create_base_submodel(id_short: str, namespace: str = "basyx_python_aas_server", display_name: str = "", description: str = "") -> Submodel:
41
+ def create_base_submodel(id_short: str, namespace: str = "basyx_python_aas_server", display_name: str = "", description: str = "") -> model.Submodel:
28
42
  """Create a basic Submodel.
29
43
 
30
44
  :param id_short: ID short of the Submodel
@@ -37,27 +51,27 @@ def create_base_submodel(id_short: str, namespace: str = "basyx_python_aas_serve
37
51
  identifier = f"{namespace}/{id_short}"
38
52
  else:
39
53
  identifier = id_short
40
- sm = Submodel(identifier)
54
+ sm = model.Submodel(identifier)
41
55
  sm.id_short = id_short
42
56
 
43
57
  if not description:
44
58
  description = f"This is the submodel with ID short '{id_short}'"
45
59
 
46
60
  description_text = {"en": f"{description}"}
47
- sm.description = MultiLanguageTextType(description_text)
61
+ sm.description = model.MultiLanguageTextType(description_text)
48
62
 
49
63
  if not display_name:
50
64
  display_name = "POC AAS"
51
65
 
52
66
  display_name_text = {"en": f"{display_name}"}
53
- sm.display_name = MultiLanguageTextType(display_name_text)
67
+ sm.display_name = model.MultiLanguageTextType(display_name_text)
54
68
 
55
69
  return sm
56
70
 
57
71
 
58
72
  def create_base_ass(
59
73
  id_short: str, namespace: str = "basyx_python_aas_server", display_name: str = "", description: str = ""
60
- ) -> AssetAdministrationShell:
74
+ ) -> model.AssetAdministrationShell:
61
75
  """Create a basic AAS.
62
76
 
63
77
  :param id_short: ID short of the AAS
@@ -68,35 +82,35 @@ def create_base_ass(
68
82
  """
69
83
  asset_info = create_base_asset_information(id_short, namespace)
70
84
 
71
- aas = AssetAdministrationShell(id_=asset_info.global_asset_id, asset_information=asset_info)
85
+ aas = model.AssetAdministrationShell(id_=asset_info.global_asset_id, asset_information=asset_info)
72
86
  aas.id_short = id_short
73
87
 
74
88
  if not description:
75
89
  description = f"This is the asset administration shell with ID short '{id_short}'"
76
90
 
77
91
  description_text = {"en": f"{description}"}
78
- aas.description = MultiLanguageTextType(description_text)
92
+ aas.description = model.MultiLanguageTextType(description_text)
79
93
 
80
94
  if not display_name:
81
95
  display_name = "POC AAS"
82
96
 
83
97
  display_name_text = {"en": f"{display_name}"}
84
- aas.display_name = MultiLanguageTextType(display_name_text)
98
+ aas.display_name = model.MultiLanguageTextType(display_name_text)
85
99
 
86
100
  return aas
87
101
 
88
102
 
89
- def add_submodel_to_aas(aas: AssetAdministrationShell, submodel: Submodel) -> None:
103
+ def add_submodel_to_aas(aas: model.AssetAdministrationShell, submodel: model.Submodel) -> None:
90
104
  """Add a given Submodel correctly to a provided AssetAdministrationShell.
91
105
 
92
106
  :param aas: provided AssetAdministrationShell to which the Submodel should be added
93
107
  :param submodel: given Submodel to add
94
108
  """
95
109
  # aas.submodel.add(submodel)
96
- aas.submodel.add(ModelReference.from_referable(submodel))
110
+ aas.submodel.add(model.ModelReference.from_referable(submodel))
97
111
 
98
112
 
99
- def create_base_asset_information(id_short: str, namespace: str = "basyx_python_aas_server") -> AssetInformation:
113
+ def create_base_asset_information(id_short: str, namespace: str = "basyx_python_aas_server") -> model.AssetInformation:
100
114
  """Return a basic AssetInformation instance.
101
115
 
102
116
  :param id_short: short ID of the AssetInformation
@@ -107,13 +121,13 @@ def create_base_asset_information(id_short: str, namespace: str = "basyx_python_
107
121
  identifier = f"{namespace}/{id_short}"
108
122
  else:
109
123
  identifier = id_short
110
- return AssetInformation(AssetKind.INSTANCE, identifier)
124
+ return model.AssetInformation(model.AssetKind.INSTANCE, identifier)
111
125
 
112
126
 
113
- def create_reference(id: str) -> ModelReference:
127
+ def create_reference(id: str) -> model.ModelReference:
114
128
  """Create a ModelReference.
115
129
 
116
130
  :param id: ID of the Submodel to reference
117
131
  :return: ModelReference instance
118
132
  """
119
- return ModelReference.from_referable(Submodel(id))
133
+ return model.ModelReference.from_referable(model.Submodel(id))
@@ -7,24 +7,27 @@ from typing import Any
7
7
 
8
8
  import basyx.aas.adapter.json
9
9
 
10
- from basyx.aas.model import AssetAdministrationShell, Reference, Submodel, ModelReference
10
+ from basyx.aas import model
11
11
  from aas_http_client.client import AasHttpClient, _create_client
12
12
  logger = logging.getLogger(__name__)
13
13
 
14
14
  class SdkWrapper():
15
- """Represents a SdkWrapper to communicate with a REST API."""
16
- _client: AasHttpClient = None
15
+ """Represents a wrapper for the BaSyx Python SDK to communicate with a REST API."""
16
+ _client: AasHttpClient = None
17
17
 
18
- def post_shells(self, aas: AssetAdministrationShell) -> dict | None:
18
+ # region shells
19
+
20
+ def post_shells(self, aas: model.AssetAdministrationShell) -> model.AssetAdministrationShell | None:
19
21
  """Post an Asset Administration Shell (AAS) to the REST API.
20
22
 
21
23
  :param aas: Asset Administration Shell to post
22
24
  :return: Response data as a dictionary or None if an error occurred
23
25
  """
24
26
  aas_data = _to_dict(aas)
25
- return self._client.post_shells(aas_data)
27
+ content: dict = self._client.post_shells(aas_data)
28
+ return _to_object(content)
26
29
 
27
- def put_shells(self, identifier: str, aas: AssetAdministrationShell) -> bool:
30
+ def put_shells(self, identifier: str, aas: model.AssetAdministrationShell) -> bool:
28
31
  """Update an Asset Administration Shell (AAS) by its ID in the REST API.
29
32
 
30
33
  :param identifier: Identifier of the AAS to update
@@ -34,7 +37,7 @@ class SdkWrapper():
34
37
  aas_data = _to_dict(aas)
35
38
  return self._client.put_shells(identifier, aas_data)
36
39
 
37
- def put_shells_submodels_by_id(self, aas_id: str, submodel_id: str, submodel: Submodel) -> bool:
40
+ def put_shells_submodels_by_id(self, aas_id: str, submodel_id: str, submodel: model.Submodel) -> bool:
38
41
  """Update a submodel by its ID for a specific Asset Administration Shell (AAS).
39
42
 
40
43
  :param aas_id: ID of the AAS to update the submodel for
@@ -44,7 +47,7 @@ class SdkWrapper():
44
47
  sm_data = _to_dict(submodel)
45
48
  return self._client.put_shells_submodels_by_id(aas_id, submodel_id, sm_data)
46
49
 
47
- def get_shells(self) -> list[AssetAdministrationShell] | None:
50
+ def get_shells(self) -> list[model.AssetAdministrationShell] | None:
48
51
  """Get all Asset Administration Shells (AAS) from the REST API.
49
52
 
50
53
  :return: AAS objects or None if an error occurred
@@ -59,7 +62,7 @@ class SdkWrapper():
59
62
  logger.warning("No shells found on server.")
60
63
  return []
61
64
 
62
- aas_list: list[AssetAdministrationShell] = []
65
+ aas_list: list[model.AssetAdministrationShell] = []
63
66
 
64
67
  for result in results:
65
68
  if not isinstance(result, dict):
@@ -73,7 +76,7 @@ class SdkWrapper():
73
76
 
74
77
  return aas_list
75
78
 
76
- def get_shells_by_id(self, aas_id: str) -> AssetAdministrationShell | None:
79
+ def get_shells_by_id(self, aas_id: str) -> model.AssetAdministrationShell | None:
77
80
  """Get an Asset Administration Shell (AAS) by its ID from the REST API.
78
81
 
79
82
  :param aas_id: ID of the AAS to retrieve
@@ -87,15 +90,15 @@ class SdkWrapper():
87
90
 
88
91
  return _to_object(content)
89
92
 
90
- def get_shells_reference_by_id(self, aas_id: str) -> Reference | None:
93
+ def get_shells_reference_by_id(self, aas_id: str) -> model.Reference | None:
91
94
  #workaround because serialization not working
92
95
  aas = self.get_shells_by_id(aas_id)
93
- return ModelReference.from_referable(aas)
96
+ return model.ModelReference.from_referable(aas)
94
97
 
95
98
  # content: dict = self._client.get_shells_reference_by_id(aas_id)
96
99
  # return json.loads(content, cls=basyx.aas.adapter.json.AASFromJsonDecoder)
97
100
 
98
- def get_shells_submodels_by_id(self, aas_id: str, submodel_id: str) -> Submodel | None:
101
+ def get_shells_submodels_by_id(self, aas_id: str, submodel_id: str) -> model.Submodel | None:
99
102
  """Get a submodel by its ID for a specific Asset Administration Shell (AAS).
100
103
 
101
104
  :param aas_id: ID of the AAS to retrieve the submodel from
@@ -113,16 +116,21 @@ class SdkWrapper():
113
116
  """
114
117
  return self._client.delete_shells_by_id(aas_id)
115
118
 
116
- def post_submodels(self, submodel: Submodel) -> bool:
119
+ # endregion
120
+
121
+ # region submodels
122
+
123
+ def post_submodels(self, submodel: model.Submodel) -> model.Submodel | None:
117
124
  """Post a submodel to the REST API.
118
125
 
119
126
  :param submodel: submodel data as a dictionary
120
127
  :return: Response data as a dictionary or None if an error occurred
121
128
  """
122
129
  sm_data = _to_dict(submodel)
123
- return self._client.post_submodels(sm_data)
130
+ content: dict = self._client.post_submodels(sm_data)
131
+ return _to_object(content)
124
132
 
125
- def put_submodels_by_id(self, identifier: str, submodel: Submodel) -> bool:
133
+ def put_submodels_by_id(self, identifier: str, submodel: model.Submodel) -> bool:
126
134
  """Update a submodel by its ID in the REST API.
127
135
 
128
136
  :param identifier: Identifier of the submodel to update
@@ -132,7 +140,7 @@ class SdkWrapper():
132
140
  sm_data = _to_dict(submodel)
133
141
  return self._client.put_submodels_by_id(identifier, sm_data)
134
142
 
135
- def get_submodels(self) -> list[Submodel] | None:
143
+ def get_submodels(self) -> list[model.Submodel] | None:
136
144
  """Get all submodels from the REST API.
137
145
 
138
146
  :return: Submodel objects or None if an error occurred
@@ -147,7 +155,7 @@ class SdkWrapper():
147
155
  logger.warning("No submodels found on server.")
148
156
  return []
149
157
 
150
- submodels: list[Submodel] = []
158
+ submodels: list[model.Submodel] = []
151
159
 
152
160
  for result in results:
153
161
  if not isinstance(result, dict):
@@ -161,7 +169,7 @@ class SdkWrapper():
161
169
 
162
170
  return submodels
163
171
 
164
- def get_submodels_by_id(self, submodel_id: str) -> Submodel | None:
172
+ def get_submodels_by_id(self, submodel_id: str) -> model.Submodel | None:
165
173
  """Get a submodel by its ID from the REST API.
166
174
 
167
175
  :param submodel_id: ID of the submodel to retrieve
@@ -175,7 +183,7 @@ class SdkWrapper():
175
183
 
176
184
  return _to_object(content)
177
185
 
178
- def patch_submodel_by_id(self, submodel_id: str, submodel: Submodel):
186
+ def patch_submodel_by_id(self, submodel_id: str, submodel: model.Submodel):
179
187
  sm_data = _to_dict(submodel)
180
188
  return self._client.patch_submodel_by_id(submodel_id, sm_data)
181
189
 
@@ -187,6 +195,52 @@ class SdkWrapper():
187
195
  """
188
196
  return self._client.delete_submodels_by_id(submodel_id)
189
197
 
198
+ def get_submodels_submodel_elements(self, submodel_id: str, ) -> list[model.SubmodelElement] | None:
199
+ """Returns all Submodel elements including their hierarchy.
200
+ !!! Serialization to model.SubmodelElement currently not possible
201
+
202
+ :param submodel_id: Encoded ID of the Submodel to retrieve elements from
203
+ :return: List of Submodel elements or None if an error occurred
204
+ """
205
+ content = self._client.get_submodels_submodel_elements(submodel_id)
206
+
207
+ if not content:
208
+ return []
209
+
210
+ results: list = content.get("result", [])
211
+ if not results:
212
+ logger.warning("No submodels found on server.")
213
+ return []
214
+
215
+ submodel_elements: list[model.SubmodelElement] = []
216
+
217
+ for result in results:
218
+ if not isinstance(result, dict):
219
+ logger.error(f"Invalid submodel data: {result}")
220
+ return None
221
+
222
+ submodel_element = _to_object(result)
223
+
224
+ if submodel_element:
225
+ submodel_elements.append(submodel_element)
226
+
227
+ return submodel_elements
228
+
229
+ def post_submodels_submodel_elements(self, submodel_id: str, submodel_element: model.SubmodelElement) -> model.SubmodelElement | None:
230
+ """Create a new submodel element.
231
+ !!! Serialization to model.SubmodelElements currently not possible
232
+
233
+ :param submodel_id: Encoded ID of the submodel to create elements for
234
+ :param submodel_element: Submodel element to create
235
+ :return: List of submodel element objects or None if an error occurred
236
+ """
237
+ sme_data = _to_dict(submodel_element)
238
+ content: dict = self._client.post_submodels_submodel_elements(submodel_id, sme_data)
239
+ return _to_object(content)
240
+
241
+
242
+ # endregion
243
+
190
244
  def _to_object(content: dict) -> Any | None:
191
245
  try:
192
246
  dict_string = json.dumps(content)
@@ -205,6 +259,8 @@ def _to_dict(object: Any) -> dict | None:
205
259
  logger.error(f"In object: {object}")
206
260
  return None
207
261
 
262
+ # region wrapper
263
+
208
264
  def create_wrapper_by_url(
209
265
  base_url: str,
210
266
  username: str = "",
@@ -215,7 +271,7 @@ def create_wrapper_by_url(
215
271
  connection_time_out: int = 60,
216
272
  ssl_verify: str = True, # noqa: FBT002
217
273
  ) -> SdkWrapper | None:
218
- """Create a BaSyx server interface client from the given parameters.
274
+ """Create a wrapper for the BaSyx Python SDK from the given parameters.
219
275
 
220
276
  :param base_url: base URL of the BaSyx server, e.g. "http://basyx_python_server:80/"_
221
277
  :param username: username for the BaSyx server interface client, defaults to ""_
@@ -225,9 +281,9 @@ def create_wrapper_by_url(
225
281
  :param time_out: timeout for the API calls, defaults to 200
226
282
  :param connection_time_out: timeout for the connection to the API, defaults to 60
227
283
  :param ssl_verify: whether to verify SSL certificates, defaults to True
228
- :return: An instance of HttpClient initialized with the provided parameters.
284
+ :return: An instance of SdkWrapper initialized with the provided parameters.
229
285
  """
230
- logger.info(f"Create BaSyx server interface client from URL '{base_url}'")
286
+ logger.info(f"Create BaSyx Python SDK wrapper from URL '{base_url}'")
231
287
  config_dict: dict[str, str] = {}
232
288
  config_dict["base_url"] = base_url
233
289
  config_dict["username"] = username
@@ -247,14 +303,12 @@ def create_wrapper_by_url(
247
303
  wrapper._client = client
248
304
  return wrapper
249
305
 
250
-
251
-
252
- def create_wrapper_by_config(config_file: Path, password: str = "") -> AasHttpClient | None:
253
- """Create a BaSyx server interface client from the given parameters.
306
+ def create_wrapper_by_config(config_file: Path, password: str = "") -> SdkWrapper | None:
307
+ """Create a wrapper for the BaSyx Python SDK from the given parameters.
254
308
 
255
309
  :param config_file: Path to the configuration file containing the BaSyx server connection settings.
256
310
  :param password: password for the BaSyx server interface client, defaults to ""_
257
- :return: An instance of HttpClient initialized with the provided parameters.
311
+ :return: An instance of SdkWrapper initialized with the provided parameters.
258
312
  """
259
313
  logger.info(f"Create BaSyx Python SDK wrapper from configuration file '{config_file}'")
260
314
  if not config_file.exists():
@@ -271,4 +325,6 @@ def create_wrapper_by_config(config_file: Path, password: str = "") -> AasHttpCl
271
325
  return None
272
326
 
273
327
  wrapper._client = client
274
- return wrapper
328
+ return wrapper
329
+
330
+ # endregion
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aas-http-client
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Generic python HTTP client for communication with various types of AAS servers
5
5
  Author-email: Daniel Klein <daniel.klein@em.ag>
6
6
  License: # :em engineering methods AG Software License
@@ -127,17 +127,27 @@ Dynamic: license-file
127
127
  [![CI](https://github.com/fluid40/aas-http-client/actions/workflows/CI.yml/badge.svg?branch=main&cache-bust=1)](https://github.com/fluid40/aas-http-client/actions)
128
128
  [![PyPI version](https://img.shields.io/pypi/v/aas-http-client.svg)](https://pypi.org/project/aas-http-client/)
129
129
 
130
- This is a generic HTTP client that can communicate with various types of AAS and submodel repository servers. The client uses Python dictionaries for input and output parameters.
131
- Additionally, wrappers are provided that work with various AAS frameworks and use the HTTP client as middleware.
130
+ This is a generic HTTP client that can communicate with various types of AAS and submodel repository servers. It uses Python dictionaries for input and output parameters of functions. It supports the most common endpoints for the [specified AAS server endpoint](https://industrialdigitaltwin.io/aas-specifications/IDTA-01002/v3.1.1/specification/interfaces.html). The client is compatible with various types of AAS repository server.
131
+ The client should be compatible with various types of AAS repository server.
132
132
 
133
- Currently, wrappers are available for the following frameworks:
134
- - BaSyx Python SDK
133
+ Tested servers include:
134
+ - [Eclipse BaSyx .Net SDK server](https://github.com/eclipse-basyx/basyx-dotnet)
135
+ - [Eclipse BaSyx .Net SDK server (Fluid4.0 Fork)](https://github.com/fluid40/basyx-dotnet)
136
+ - [Eclipse BaSyx Java SDK server](https://github.com/eclipse-basyx/basyx-java-sdk)
137
+ - [Eclipse BaSyx Python SDK server](https://github.com/eclipse-basyx/basyx-python-sdk)
138
+ - [Eclipse AASX server](https://github.com/eclipse-aaspe)
139
+
140
+ The behavior may vary depending on the details of the implementation and compliance with the [AAS specification](https://industrialdigitaltwin.org/en/content-hub/aasspecifications). It also depends on which endpoints are provided by the server.
141
+
142
+ Additionally, wrappers are provided that work with various AAS frameworks and use the HTTP client as middleware. These wrappers use the SDK-specific data model classes for function input and output parameters.
143
+ Wrappers are currently available for the following frameworks:
144
+ - [Eclipse BaSyx Python SDK](https://github.com/eclipse-basyx/basyx-python-sdk)
135
145
 
136
146
  ## Links
137
147
 
138
148
  🚀 [Getting Started](docs/getting_started.md)
139
149
 
140
- 💻 [Developer Quickstart](docs/dev_guide.md)
150
+ 💻 [Tutorials](docs/tutorials.md)
141
151
 
142
152
  👨‍⚕️ [Troubleshooting](docs/troubleshooting.md)
143
153
 
@@ -167,12 +177,12 @@ client = create_client_by_url(
167
177
  print(client.get_shells())
168
178
  ```
169
179
 
170
- ### SDK Wrapper
180
+ ### BaSyx Python SDK Wrapper
171
181
 
172
182
  ```python
173
- from aas_http_client.wrapper.sdk_wrapper import create_wrapper_by_config
183
+ from aas_http_client.wrapper.sdk_wrapper import create_wrapper_by_url
174
184
 
175
- wrapper = create_wrapper_by_config(
185
+ wrapper = create_wrapper_by_url(
176
186
  base_url="http://myaasserver:5043/"
177
187
  )
178
188
 
@@ -0,0 +1,14 @@
1
+ aas_http_client/__init__.py,sha256=cAr1mQzWp0G0LKtkAOYzc9t95OY3jM3Aj4bKnxx0Dso,901
2
+ aas_http_client/client.py,sha256=8pgEd3xV8BDBhqQvYfdLGOVEOJI5-ZfzXhjRe5OZZ_0,22916
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=kW1f8D6cjlzRS2Ald_LYM_Qk7lc_XxEV53asjaJDhKw,3188
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=mL06SX208dMJHpiB-ol51JA9Bm3czoI8ELYTfismCq8,4633
9
+ aas_http_client/wrapper/sdk_wrapper.py,sha256=G0bqZB2AL4-E7FhQF4jDJtsRREq55knj1RmYNpLHxzI,12368
10
+ aas_http_client-0.2.4.dist-info/licenses/LICENSE,sha256=ayt4HY-Tjoe1Uvj47j6UdNq8mEufKcKFangurChIHxQ,5990
11
+ aas_http_client-0.2.4.dist-info/METADATA,sha256=D8d3xu5re1-uIXnlSfZUCjTptwwtBPbtf4kJ9AuGA4I,10301
12
+ aas_http_client-0.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
+ aas_http_client-0.2.4.dist-info/top_level.txt,sha256=vzvoz2vjeTLwpuz-Y-eEfoQ7T3byoaKshVlFMFH5NaM,16
14
+ aas_http_client-0.2.4.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- aas_http_client/__init__.py,sha256=cAr1mQzWp0G0LKtkAOYzc9t95OY3jM3Aj4bKnxx0Dso,901
2
- aas_http_client/client.py,sha256=6d3g9VNhex1JrZwIXtBTopSTw3bsf5NZBSWG5mHYuQg,20705
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=ESR0ETJdEWXHZRswcuNh9qArStFyHFWCNYxhJjLB0tA,2616
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=MClsjWDuKDMN_R5b9vEkbxn_sFQ-UhGN3DBzWkLiVQU,10314
10
- aas_http_client-0.2.2.dist-info/licenses/LICENSE,sha256=ayt4HY-Tjoe1Uvj47j6UdNq8mEufKcKFangurChIHxQ,5990
11
- aas_http_client-0.2.2.dist-info/METADATA,sha256=7NJUrDZSukbER5QnPI6CEL_oIsVeaarzy_CLQX8PFvg,9144
12
- aas_http_client-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
13
- aas_http_client-0.2.2.dist-info/top_level.txt,sha256=vzvoz2vjeTLwpuz-Y-eEfoQ7T3byoaKshVlFMFH5NaM,16
14
- aas_http_client-0.2.2.dist-info/RECORD,,