Skip to content

Commit

Permalink
api/server: Add aas registry server and submodel registry server to s…
Browse files Browse the repository at this point in the history
…erver. sdk/basyx/object_store: add filter function
  • Loading branch information
somsonson committed Jan 14, 2025
1 parent 675f788 commit b9a9668
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 4 deletions.
38 changes: 38 additions & 0 deletions api/server/routes/aas_registry_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from typing import Any

from aas_core3.types import Identifiable
from fastapi import APIRouter, Request, HTTPException

from basyx import ObjectStore
from services.aas_registry_server_service import AasRegistryServerService


class AasRegistryRouter:
def __init__(self, global_obj_store: ObjectStore[Identifiable]):
self.router = APIRouter()
self.obj_store = global_obj_store
self.service = AasRegistryServerService(global_obj_store)
self._setup_routes()

def _setup_routes(self):
@self.router.get("/")
async def GetAllAssetAdministrationShellDescriptors() -> Any:
return self.service.GetAllAssetAdministrationShellDescriptors()

@self.router.get("/{aas_descriptor_id}")
async def GetAssetAdministrationShellDescriptorById(aas_descriptor_id: str) -> Any:
return self.service.GetAssetAdministrationShellDescriptorById(aas_descriptor_id)

@self.router.post("/")
async def PostAssetAdministrationShellDescriptor(request: Request) -> Any:
body = await request.json()
return self.service.PostAssetAdministrationShellDescriptor(body)

@self.router.put("/")
async def PutAssetAdministrationShellDescriptorById(request: Request) -> Any:
body = await request.json()
return self.service.PutAssetAdministrationShellDescriptorById(body)

@self.router.delete("/{aas_descriptor_id}")
async def DeleteAssetAdministrationShellDescriptorById(aas_descriptor_id: str) -> Any:
return self.service.DeleteAssetAdministrationShellDescriptorById(aas_descriptor_id)
38 changes: 38 additions & 0 deletions api/server/routes/submodel_registry_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from typing import Any

from aas_core3.types import Identifiable
from fastapi import APIRouter, Request, HTTPException

from basyx import ObjectStore
from services.submodel_registry_server_service import SubmodelRegistryServerService


class SubmodelRegistryRouter:
def __init__(self, global_obj_store: ObjectStore[Identifiable]):
self.router = APIRouter()
self.obj_store = global_obj_store
self.service = SubmodelRegistryServerService(global_obj_store)
self._setup_routes()

def _setup_routes(self):
@self.router.get("/")
async def GetAllSubmodelDescriptors() -> Any:
return self.service.GetAllSubmodelDescriptors()

@self.router.get("/{submodel_id}")
async def GetSubmodelDescriptorById(submodel_id: str) -> Any:
return self.service.GetSubmodelDescriptorById(submodel_id)

@self.router.post("/")
async def PostSubmodelDescriptor(request: Request) -> Any:
body = await request.json()
return self.service.PostSubmodelDescriptor(body)

@self.router.put("/")
async def PutSubmodelDescriptorById(request: Request) -> Any:
body = await request.json()
return self.service.PutSubmodelDescriptorById(body)

@self.router.delete("/{submodel_id}")
async def DeleteSubmodelDescriptorById(submodel_id: str) -> Any:
return self.service.DeleteSubmodelDescriptorById(submodel_id)
6 changes: 5 additions & 1 deletion api/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import uvicorn

# Import routers
from routes import submodel, aasx_file_server
from routes import submodel, aasx_file_server, aas_registry_server, submodel_registry_server

from basyx import object_store

Expand All @@ -13,11 +13,15 @@

submodel_router = submodel.SubmodelRouter(central_object_store)
aasx_file_router = aasx_file_server.AasxFileServerRouter(central_object_store)
aas_registry_router = aas_registry_server.AasRegistryRouter(central_object_store)
submodel_registry_router = submodel_registry_server.SubmodelRegistryRouter(central_object_store)

# Register router
# TODO: This can be done dynamically based on startup params
app.include_router(submodel_router.router, prefix=prefix + "/submodels")
app.include_router(aasx_file_router.router, prefix=prefix + "/aasx")
app.include_router(aas_registry_router.router, prefix=prefix + "/aas_registry")
app.include_router(submodel_registry_router.router, prefix=prefix + "/submodel_registry")

# Start the server if this file is executed directly
if __name__ == "__main__":
Expand Down
74 changes: 74 additions & 0 deletions api/server/services/aas_registry_server_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from basyx import ObjectStore
from aas_core3.types import AssetAdministrationShell, ConceptDescription
from aas_core3 import jsonization
from fastapi import HTTPException
from typing import Any, MutableMapping
from aas_core3 import jsonization


class AasRegistryServerService:
def __init__(self, global_object_store: ObjectStore):
self.obj_store = global_object_store

def GetAllAssetAdministrationShellDescriptors(self) -> list[str]:
all_descriptors = self.obj_store.filter_identifiables_by_instance(ConceptDescription)
print(all_descriptors.__dict__)
print(all_descriptors)
print("test")
aas_descriptors_store = ObjectStore()
for descriptor in all_descriptors:
reference_list = descriptor.is_case_of
print(reference_list)
for element in reference_list:
reference_ids = element.keys
for reference_id in reference_ids:
try:
identifiable = self.obj_store.get_identifiable(reference_id)
except KeyError as e:
# TODO: Provide a stacktrace
# Wenn anders in Spezifikation, Stacktrace in server log
raise HTTPException(status_code=400, detail=str(e))

if isinstance(identifiable, AssetAdministrationShell):
try:
aas_descriptors_store.add(descriptor)
except KeyError as e:
pass
return [jsonization.to_jsonable(descriptor) for descriptor in aas_descriptors_store]

def GetAssetAdministrationShellDescriptorById(self, descriptor_id) \
-> list[bool | int | float | str | list[Any] | MutableMapping[str, Any]]:
aas_descriptor = self.obj_store.get_identifiable(descriptor_id)
assert isinstance(aas_descriptor, ConceptDescription)
return jsonization.to_jsonable(aas_descriptor)

def PostAssetAdministrationShellDescriptor(self, json):
aas_descriptor = jsonization.concept_description_from_jsonable(json)
try:
self.obj_store.add(aas_descriptor)
except KeyError as e:
# TODO: Provide a stacktrace
# Wenn anders in Spezifikation, Stacktrace in server log
raise HTTPException(status_code=400, detail=str(e))
return {"message": "AAS Descriptor processed"}

def PutAssetAdministrationShellDescriptorById(self, json):
aas_descriptor = jsonization.asset_administration_shell_from_jsonable(json)
try:
self.obj_store.delete(aas_descriptor.id) # should there be an exception if there is no aasx_package to
# update?
self.obj_store.add(aas_descriptor)
except KeyError as e:
# TODO: Provide a stacktrace
# Wenn anders in Spezifikation, Stacktrace in server log
raise HTTPException(status_code=400, detail=str(e))
return {"message": "AASX package updated"}

def DeleteAssetAdministrationShellDescriptorById(self, descriptor_id):
try:
self.obj_store.delete(descriptor_id) # should there be an exception if there is no aasx_package to delete?
except KeyError as e:
# TODO: Provide a stacktrace
# Wenn anders in Spezifikation, Stacktrace in server log
raise HTTPException(status_code=400, detail=str(e))
return {"message": "AASX descriptor deleted"}
5 changes: 3 additions & 2 deletions api/server/services/aasx_flie_server_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ def PostAASXPackage(self, json):
def PutAASXByPackageId(self, json):
aasx_package = jsonization.asset_administration_shell_from_jsonable(json)
try:
self.obj_store.delete(aasx_package.id) # should there be an exception if there is no aasx_package to update?
self.obj_store.delete(aasx_package.id) # should there be an exception if there is no aasx_package to
# update?
self.obj_store.add(aasx_package)
except KeyError as e:
# TODO: Provide a stacktrace
Expand All @@ -41,7 +42,7 @@ def PutAASXByPackageId(self, json):

def DeleteAASXByPackageId(self, package_id):
try:
self.obj_store.delete(package_id) # should there be an exception if there is no aasx_package to update?
self.obj_store.delete(package_id) # should there be an exception if there is no aasx_package to delete?
except KeyError as e:
# TODO: Provide a stacktrace
# Wenn anders in Spezifikation, Stacktrace in server log
Expand Down
113 changes: 113 additions & 0 deletions api/server/services/submodel_registry_server_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
from basyx import ObjectStore
from aas_core3.types import AssetAdministrationShell, Submodel
from aas_core3 import jsonization
from fastapi import HTTPException
from typing import Any, MutableMapping
from aas_core3.types import AssetAdministrationShell, ConceptDescription


class SubmodelRegistryServerService:
def __init__(self, global_object_store: ObjectStore):
self.obj_store = global_object_store

def GetAllSubmodelDescriptors(self) -> list[str]:
#print(self.obj_store.__dict__)

all_descriptors = self.obj_store.filter_identifiables_by_instance(ConceptDescription)
#print(all_descriptors.__dict__)
print(all_descriptors)
#print("test")
submodel_descriptors_store = ObjectStore()
for descriptor in all_descriptors:
reference_list = descriptor.is_case_of
print(reference_list)
for element in reference_list:
reference_ids = element.keys
print(reference_ids)
for reference_id in reference_ids:
try:
identifiable = self.obj_store.get_identifiable(reference_id.value)
except KeyError as e:
# TODO: Provide a stacktrace
# Wenn anders in Spezifikation, Stacktrace in server log
identifiable = []
if isinstance(identifiable, Submodel):
try:
submodel_descriptors_store.add(descriptor)
except KeyError as e:
pass
return [jsonization.to_jsonable(descriptor) for descriptor in submodel_descriptors_store]

def GetSubmodelDescriptorById(self, descriptor_id) \
-> list[bool | int | float | str | list[Any] | MutableMapping[str, Any]]:
try:
aas_descriptor = self.obj_store.get_identifiable(descriptor_id)
except KeyError as e:
# TODO: Provide a stacktrace
# Wenn anders in Spezifikation, Stacktrace in server log
raise HTTPException(status_code=400, detail=str(e))
assert isinstance(aas_descriptor, ConceptDescription)
return jsonization.to_jsonable(aas_descriptor)

def PostSubmodelDescriptor(self, json):
submodel_descriptor = jsonization.concept_description_from_jsonable(json)

# Check if all referenced submodels exist in the obeject_store

for reference in submodel_descriptor.is_case_of:
reference_ids = reference.keys
for reference_id in reference_ids:
try:
self.obj_store.get_identifiable(reference_id.value)
except KeyError as e:
# TODO: Provide a stacktrace
# Wenn anders in Spezifikation, Stacktrace in server log
raise HTTPException(status_code=400, detail= "A referenced submodel of the concept description "
"with the following id does not exist in the "
"object_store:" + str(e))

try:
self.obj_store.add(submodel_descriptor)
except KeyError as e:
# TODO: Provide a stacktrace
# Wenn anders in Spezifikation, Stacktrace in server log
raise HTTPException(status_code=400, detail="A referenced submodel of the concept description "
"with the following id does not exist in the "
"object_store:" + str(e))
return {"message": "Submodel descriptor processed"}

def PutSubmodelDescriptorById(self, json):
submodel_descriptor = jsonization.concept_description_from_jsonable(json)

# Check if all referenced submodels exist in the obeject_store

for reference in submodel_descriptor.is_case_of:
reference_ids = reference.keys
for reference_id in reference_ids:
try:
self.obj_store.get_identifiable(reference_id.value)
except KeyError as e:
# TODO: Provide a stacktrace
# Wenn anders in Spezifikation, Stacktrace in server log
raise HTTPException(status_code=400, detail= "A referenced submodel of the concept description "
"with the following id does not exist in the "
"object_store:" + str(e))

try:
self.obj_store.delete(submodel_descriptor.id) # should there be an exception if there is no aasx_package to
# update?
self.obj_store.add(submodel_descriptor)
except KeyError as e:
# TODO: Provide a stacktrace
# Wenn anders in Spezifikation, Stacktrace in server log
raise HTTPException(status_code=400, detail=str(e))
return {"message": "AASX package updated"}

def DeleteSubmodelDescriptorById(self, descriptor_id):
try:
self.obj_store.delete(descriptor_id) # should there be an exception if there is no aasx_package to delete?
except KeyError as e:
# TODO: Provide a stacktrace
# Wenn anders in Spezifikation, Stacktrace in server log
raise HTTPException(status_code=400, detail=str(e))
return {"message": "Submodel descriptor deleted"}
15 changes: 14 additions & 1 deletion sdk/basyx/object_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"""

import abc
from typing import MutableSet, Iterator, Generic, TypeVar, Dict, List, Optional, Iterable
from typing import MutableSet, Iterator, Generic, TypeVar, Dict, List, Optional, Iterable, Type

from aas_core3.types import Identifiable, Referable, Class

Expand Down Expand Up @@ -190,6 +190,19 @@ def get_parent_referable(self, id_short: str) -> Referable:
return element
raise KeyError("there is no parent Identifiable for id_short {}".format(id_short))

def filter_identifiables_by_instance(self, instance: Type) -> list[Type]:
"""
Get all identifiables of the specified type.
:param instance: The Type to filter by. For example, we can filter by "aas_core3.types.ConceptDescription"
:return: The list of the filtered identifiables
"""
filtered_identifiables = []
for identifiable in self._backend.values():
if isinstance(identifiable, instance):
filtered_identifiables.append(identifiable)
return filtered_identifiables

def __contains__(self, x: object) -> bool:
if isinstance(x, str):
return x in self._backend
Expand Down

0 comments on commit b9a9668

Please sign in to comment.