diff --git a/app/models/basic_event_element.py b/app/models/basic_event_element.py index 6f2b773..c81e758 100644 --- a/app/models/basic_event_element.py +++ b/app/models/basic_event_element.py @@ -18,8 +18,11 @@ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +import rdflib from pydantic import BaseModel +from rdflib import RDF +from app.models.aas_namespace import AASNameSpace from app.models.event_element import EventElement from app.models.model_type import ModelType from app.models.reference import Reference @@ -37,16 +40,17 @@ from app.models.range import Range from app.models.reference_element import ReferenceElement from app.models.relationship_element import RelationshipElement +from app.models.submodel_element import SubmodelElement class Direction(Enum): - input = "input" - output = "output" + Input = "input" + Output = "output" class StateOfEvent(Enum): - off = "off" - on = "on" + Off = "off" + On = "on" class BasicEventElement(EventElement): @@ -64,3 +68,138 @@ class BasicEventElement(EventElement): minInterval: Optional[constr()] = None maxInterval: Optional[constr()] = None modelType: Literal["BasicEventElement"] = ModelType.BasicEventElement.value + + def to_rdf( + self, + graph: rdflib.Graph = None, + parent_node: rdflib.IdentifiedNode = None, + prefix_uri: str = "", + base_uri: str = "", + ) -> (rdflib.Graph, rdflib.IdentifiedNode): + created_graph, created_node = super().to_rdf(graph, parent_node, prefix_uri, base_uri) + created_graph.add((created_node, RDF.type, AASNameSpace.AAS["BasicEventElement"])) + + _, created_observed_node = self.observed.to_rdf(created_graph, created_node) + created_graph.add((created_node, AASNameSpace.AAS["BasicEventElement/observed"], created_observed_node)) + created_graph.add( + ( + created_node, + AASNameSpace.AAS["BasicEventElement/direction"], + AASNameSpace.AAS[f"Direction/{self.direction.name}"], + ) + ) + created_graph.add( + ( + created_node, + AASNameSpace.AAS["BasicEventElement/state"], + AASNameSpace.AAS[f"StateOfEvent/{self.state.name}"], + ) + ) + if self.messageTopic: + _, created_message_broker_node = self.messageBroker.to_rdf(created_graph, created_node) + created_graph.add( + (created_node, AASNameSpace.AAS["BasicEventElement/messageBroker"], created_message_broker_node) + ) + if self.messageBroker: + created_graph.add( + (created_node, AASNameSpace.AAS["BasicEventElement/messageTopic"], rdflib.Literal(self.messageTopic)) + ) + if self.lastUpdate: + created_graph.add( + (created_node, AASNameSpace.AAS["BasicEventElement/lastUpdate"], rdflib.Literal(self.lastUpdate)) + ) + if self.minInterval: + created_graph.add( + (created_node, AASNameSpace.AAS["BasicEventElement/minInterval"], rdflib.Literal(self.minInterval)) + ) + if self.maxInterval: + created_graph.add( + (created_node, AASNameSpace.AAS["BasicEventElement/maxInterval"], rdflib.Literal(self.maxInterval)) + ) + return created_graph, created_node + + @staticmethod + def from_rdf(graph: rdflib.Graph, subject: rdflib.IdentifiedNode) -> "BasicEventElement": + observed_value = None + observed_ref: rdflib.URIRef = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["BasicEventElement/observed"]), + None, + ) + if observed_ref: + observed_value = Reference.from_rdf(graph, observed_ref) + + direction_value = None + direction_ref: rdflib.URIRef = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["BasicEventElement/direction"]), + None, + ) + if direction_ref: + direction_value = Direction[direction_ref[direction_ref.rfind("/") + 1 :]] + + state_value = None + state_ref: rdflib.URIRef = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["BasicEventElement/state"]), + None, + ) + if state_ref: + state_value = StateOfEvent[state_ref[state_ref.rfind("/") + 1 :]] + + message_topic_value = None + message_topic_ref: rdflib.Literal = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["BasicEventElement/messageTopic"]), + None, + ) + if message_topic_ref: + message_topic_value = message_topic_ref.value + + message_broker_value = None + message_broker_ref: rdflib.URIRef = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["BasicEventElement/messageBroker"]), + None, + ) + if message_broker_ref: + message_broker_value = Reference.from_rdf(graph, message_broker_ref) + + last_update_value = None + last_update_ref: rdflib.Literal = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["BasicEventElement/lastUpdate"]), + None, + ) + if last_update_ref: + last_update_value = last_update_ref.value + min_interval_value = None + min_interval_ref: rdflib.Literal = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["BasicEventElement/minInterval"]), + None, + ) + if min_interval_ref: + min_interval_value = min_interval_ref.value + + max_interval_value = None + max_interval_ref: rdflib.Literal = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["BasicEventElement/maxInterval"]), + None, + ) + if max_interval_ref: + max_interval_value = max_interval_ref.value + + submodel_element = SubmodelElement.from_rdf(graph, subject) + return BasicEventElement( + observed=observed_value, + direction=direction_value, + state=state_value, + messageTopic=message_topic_value, + messageBroker=message_broker_value, + lastUpdate=last_update_value, + minInterval=min_interval_value, + maxInterval=max_interval_value, + qualifiers=submodel_element.qualifiers, + category=submodel_element.category, + idShort=submodel_element.idShort, + displayName=submodel_element.displayName, + description=submodel_element.description, + extensions=submodel_element.extensions, + semanticId=submodel_element.semanticId, + supplementalSemanticIds=submodel_element.supplementalSemanticIds, + embeddedDataSpecifications=submodel_element.embeddedDataSpecifications, + ) diff --git a/app/models/entity.py b/app/models/entity.py index 292b8bf..39d894a 100644 --- a/app/models/entity.py +++ b/app/models/entity.py @@ -18,6 +18,10 @@ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +import rdflib +from rdflib import RDF + +from app.models.aas_namespace import AASNameSpace from app.models.model_type import ModelType from app.models.specific_asset_id import SpecificAssetId from app.models.submodel_element import SubmodelElement @@ -35,8 +39,7 @@ class EntityType(Enum): class Entity(SubmodelElement): - # TODO: recheck - statements: List["SubmodelElementChoice"] = Field(None, min_length=0, discriminator="modelType") + statements: Optional[List["SubmodelElementChoice"]] = Field(None, min_length=0) entityType: EntityType globalAssetId: Optional[ constr( @@ -44,5 +47,99 @@ class Entity(SubmodelElement): max_length=2000, ) ] = None - specificAssetIds: Optional[List[SpecificAssetId]] = Field(None, min_length=1) + specificAssetIds: Optional[List[SpecificAssetId]] = Field(None, min_length=0) modelType: Literal["Entity"] = ModelType.Entity.value + + def to_rdf( + self, + graph: rdflib.Graph = None, + parent_node: rdflib.IdentifiedNode = None, + prefix_uri: str = "", + base_uri: str = "", + ) -> (rdflib.Graph, rdflib.IdentifiedNode): + created_graph, created_node = super().to_rdf(graph, parent_node, prefix_uri, base_uri) + created_graph.add((created_node, RDF.type, AASNameSpace.AAS["Entity"])) + + created_graph.add( + ( + created_node, + AASNameSpace.AAS["Entity/entityType"], + AASNameSpace.AAS[f"EntityType/{self.entityType.name}"], + ) + ) + if self.statements: + for idx, statement in enumerate(self.statements): + _, created_sub_node = statement.to_rdf( + graph, created_node, prefix_uri=prefix_uri + str(created_node) + "." + ) + graph.add((created_sub_node, AASNameSpace.AAS["index"], rdflib.Literal(idx))) + graph.add((created_node, AASNameSpace.AAS["Entity/statements"], created_sub_node)) + if self.globalAssetId: + created_graph.add( + ( + created_node, + AASNameSpace.AAS["Entity/globalAssetId"], + rdflib.Literal(self.globalAssetId), + ) + ) + + if self.specificAssetIds: + for idx, specific_asset_id in enumerate(self.specificAssetIds): + _, created_sub_node = specific_asset_id.to_rdf( + graph, created_node, prefix_uri=prefix_uri + str(created_node) + "." + ) + graph.add((created_sub_node, AASNameSpace.AAS["index"], rdflib.Literal(idx))) + graph.add((created_node, AASNameSpace.AAS["Entity/specificAssetIds"], created_sub_node)) + return created_graph, created_node + + @staticmethod + def from_rdf(graph: rdflib.Graph, subject: rdflib.IdentifiedNode) -> "Entity": + statements_value = [] + from app.models.util import from_unknown_rdf + + for statement_uriref in graph.objects(subject=subject, predicate=AASNameSpace.AAS["Entity/statements"]): + element = from_unknown_rdf(graph, statement_uriref) + statements_value.append(element) + + if len(statements_value) == 0: + statements_value = None + + entity_type_value = None + entity_type_ref: rdflib.URIRef = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["Entity/entityType"]), + None, + ) + if entity_type_ref: + entity_type_value = EntityType[entity_type_ref[entity_type_ref.rfind("/") + 1 :]] + + global_asset_id_value = None + global_asset_id_ref: rdflib.Literal = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["Entity/globalAssetId"]), + None, + ) + if global_asset_id_ref: + global_asset_id_value = global_asset_id_ref.value + + specificAssetIds_value = [] + for statement_uriref in graph.objects(subject=subject, predicate=AASNameSpace.AAS["Entity/specificAssetIds"]): + element = SpecificAssetId.from_rdf(graph, statement_uriref) + specificAssetIds_value.append(element) + if len(specificAssetIds_value) == 0: + specificAssetIds_value = None + submodel_element = SubmodelElement.from_rdf(graph, subject) + + return Entity( + statements=statements_value, + entityType=entity_type_value, + globalAssetId=global_asset_id_value, + specificAssetIds=specificAssetIds_value, + qualifiers=submodel_element.qualifiers, + category=submodel_element.category, + idShort=submodel_element.idShort, + displayName=submodel_element.displayName, + description=submodel_element.description, + extensions=submodel_element.extensions, + semanticId=submodel_element.semanticId, + supplementalSemanticIds=submodel_element.supplementalSemanticIds, + embeddedDataSpecifications=submodel_element.embeddedDataSpecifications, + ) diff --git a/app/models/operation.py b/app/models/operation.py index 8f86224..76fa05d 100644 --- a/app/models/operation.py +++ b/app/models/operation.py @@ -18,7 +18,12 @@ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +import rdflib +from rdflib import RDF + +from app.models.aas_namespace import AASNameSpace from app.models.model_type import ModelType +from app.models.rdfiable import RDFiable from app.models.specific_asset_id import SpecificAssetId from app.models.submodel_element import SubmodelElement @@ -30,12 +35,110 @@ # TODO: recheck -class OperationVariable(BaseModel): +class OperationVariable(BaseModel, RDFiable): value: "SubmodelElementChoice" + def to_rdf( + self, + graph: rdflib.Graph = None, + parent_node: rdflib.IdentifiedNode = None, + prefix_uri: str = "", + base_uri: str = "", + ) -> (rdflib.Graph, rdflib.IdentifiedNode): + if graph == None: + graph = rdflib.Graph() + graph.bind("aas", AASNameSpace.AAS) + + node = rdflib.BNode() + graph.add((node, RDF.type, AASNameSpace.AAS["OperationVariable"])) + _, created_node = self.value.to_rdf(graph, node) + graph.add((node, AASNameSpace.AAS["OperationVariable/value"], created_node)) + return graph, node + + @staticmethod + def from_rdf(graph: rdflib.Graph, subject: rdflib.IdentifiedNode) -> "OperationVariable": + value_value = None + from app.models.util import from_unknown_rdf + + value_value_ref: rdflib.URIRef = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["OperationVariable/value"]), + None, + ) + if value_value_ref: + value_value = from_unknown_rdf(graph, value_value_ref) + return OperationVariable(value=value_value) + class Operation(SubmodelElement): - inputVariables: Optional[List[OperationVariable]] = Field(None, min_length=1) - outputVariables: Optional[List[OperationVariable]] = Field(None, min_length=1) - inoutputVariables: Optional[List[OperationVariable]] = Field(None, min_length=1) + inputVariables: Optional[List[OperationVariable]] = Field(None, min_length=0) + outputVariables: Optional[List[OperationVariable]] = Field(None, min_length=0) + inoutputVariables: Optional[List[OperationVariable]] = Field(None, min_length=0) modelType: Literal["Operation"] = ModelType.Operation.value + + def to_rdf( + self, + graph: rdflib.Graph = None, + parent_node: rdflib.IdentifiedNode = None, + prefix_uri: str = "", + base_uri: str = "", + ) -> (rdflib.Graph, rdflib.IdentifiedNode): + created_graph, created_node = super().to_rdf(graph, parent_node, prefix_uri, base_uri) + + created_graph.add((created_node, RDF.type, AASNameSpace.AAS["Operation"])) + if self.inputVariables: + for idx, input_variable in enumerate(self.inputVariables): + _, created_sub_node = input_variable.to_rdf(created_graph, created_node) + created_graph.add((created_sub_node, AASNameSpace.AAS["index"], rdflib.Literal(idx))) + created_graph.add((created_node, AASNameSpace.AAS["Operation/inputVariables"], created_sub_node)) + + if self.outputVariables: + for idx, input_variable in enumerate(self.outputVariables): + _, created_sub_node = input_variable.to_rdf(created_graph, created_node) + created_graph.add((created_sub_node, AASNameSpace.AAS["index"], rdflib.Literal(idx))) + created_graph.add((created_node, AASNameSpace.AAS["Operation/outputVariables"], created_sub_node)) + if self.inoutputVariables: + for idx, input_variable in enumerate(self.inoutputVariables): + _, created_sub_node = input_variable.to_rdf(created_graph, created_node) + created_graph.add((created_sub_node, AASNameSpace.AAS["index"], rdflib.Literal(idx))) + created_graph.add((created_node, AASNameSpace.AAS["Operation/inoutputVariables"], created_sub_node)) + + return created_graph, created_node + + @staticmethod + def from_rdf(graph: rdflib.Graph, subject: rdflib.IdentifiedNode) -> "Operation": + input_variables_value = [] + for variable_ref in graph.objects(subject=subject, predicate=AASNameSpace.AAS["Operation/inputVariables"]): + element = OperationVariable.from_rdf(graph, variable_ref) + input_variables_value.append(element) + if len(input_variables_value) == 0: + input_variables_value = None + + output_variables_value = [] + for variable_ref in graph.objects(subject=subject, predicate=AASNameSpace.AAS["Operation/outputVariables"]): + element = OperationVariable.from_rdf(graph, variable_ref) + output_variables_value.append(element) + if len(output_variables_value) == 0: + output_variables_value = None + + inoutput_variables_value = [] + for variable_ref in graph.objects(subject=subject, predicate=AASNameSpace.AAS["Operation/inoutputVariables"]): + element = OperationVariable.from_rdf(graph, variable_ref) + inoutput_variables_value.append(element) + if len(inoutput_variables_value) == 0: + inoutput_variables_value = None + submodel_element = SubmodelElement.from_rdf(graph, subject) + + return Operation( + inputVariables=input_variables_value, + outputVariables=output_variables_value, + inoutputVariables=inoutput_variables_value, + qualifiers=submodel_element.qualifiers, + category=submodel_element.category, + idShort=submodel_element.idShort, + displayName=submodel_element.displayName, + description=submodel_element.description, + extensions=submodel_element.extensions, + semanticId=submodel_element.semanticId, + supplementalSemanticIds=submodel_element.supplementalSemanticIds, + embeddedDataSpecifications=submodel_element.embeddedDataSpecifications, + ) diff --git a/app/models/reference_element.py b/app/models/reference_element.py index 9e0effb..e7583c2 100644 --- a/app/models/reference_element.py +++ b/app/models/reference_element.py @@ -24,7 +24,9 @@ import rdflib from pydantic import BaseModel, Field, constr +from rdflib import RDF +from app.models.aas_namespace import AASNameSpace from app.models.data_element import DataElement from app.models.data_type_def_xsd import DataTypeDefXsd from app.models.lang_string_text_type import LangStringTextType @@ -37,15 +39,29 @@ class ReferenceElement(DataElement): value: Optional[Reference] = None modelType: Literal["ReferenceElement"] = ModelType.ReferenceElement.value + def to_rdf( + self, + graph: rdflib.Graph = None, + parent_node: rdflib.IdentifiedNode = None, + prefix_uri: str = "", + base_uri: str = "", + ) -> (rdflib.Graph, rdflib.IdentifiedNode): + created_graph, created_node = super().to_rdf(graph, parent_node, prefix_uri, base_uri) + created_graph.add((created_node, RDF.type, AASNameSpace.AAS["ReferenceElement"])) + if self.value: + _, created_sub_node = self.value.to_rdf(created_graph, created_node) + created_graph.add((created_node, AASNameSpace.AAS["ReferenceElement/value"], created_sub_node)) + return created_graph, created_node + @staticmethod def from_rdf(graph: rdflib.Graph, subject: rdflib.IdentifiedNode) -> "ReferenceElement": value_value = None - value_value_ref: rdflib.Literal = next( + value_ref: rdflib.URIRef = next( graph.objects(subject=subject, predicate=AASNameSpace.AAS["ReferenceElement/value"]), None, ) - if value_value_ref != None: - value_value = value_value_ref.value + if value_ref: + value_value = Reference.from_rdf(graph, value_ref) submodel_element = SubmodelElement.from_rdf(graph, subject) return ReferenceElement( value=value_value, diff --git a/app/models/relationship_element.py b/app/models/relationship_element.py index 58cfa8a..e7946d3 100644 --- a/app/models/relationship_element.py +++ b/app/models/relationship_element.py @@ -20,9 +20,41 @@ # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from typing import Literal +import rdflib +from rdflib import RDF + +from app.models.aas_namespace import AASNameSpace from app.models.model_type import ModelType from app.models.relationship_element_abstract import RelationshipElementAbstract class RelationshipElement(RelationshipElementAbstract): modelType: Literal["RelationshipElement"] = ModelType.RelationshipElement.value + + def to_rdf( + self, + graph: rdflib.Graph = None, + parent_node: rdflib.IdentifiedNode = None, + prefix_uri: str = "", + base_uri: str = "", + ) -> (rdflib.Graph, rdflib.IdentifiedNode): + created_graph, created_node = super().to_rdf(graph, parent_node, prefix_uri, base_uri) + created_graph.add((created_node, RDF.type, AASNameSpace.AAS["RelationshipElement"])) + return created_graph, created_node + + @staticmethod + def from_rdf(graph: rdflib.Graph, subject: rdflib.IdentifiedNode) -> "RelationshipElement": + element = RelationshipElementAbstract.from_rdf(graph, subject) + return RelationshipElement( + first=element.first, + second=element.second, + qualifiers=element.qualifiers, + category=element.category, + idShort=element.idShort, + displayName=element.displayName, + description=element.description, + extensions=element.extensions, + semanticId=element.semanticId, + supplementalSemanticIds=element.supplementalSemanticIds, + embeddedDataSpecifications=element.embeddedDataSpecifications, + ) diff --git a/app/models/submodel.py b/app/models/submodel.py index ee06d53..b8bf2c2 100644 --- a/app/models/submodel.py +++ b/app/models/submodel.py @@ -104,7 +104,7 @@ def to_rdf( return graph, node @staticmethod - def from_rdf(graph: rdflib.Graph, subject: rdflib.IdentifiedNode): + def from_rdf(graph: rdflib.Graph, subject: rdflib.IdentifiedNode) -> "Submodel": # HasSemantics has_semantics = HasSemantics.from_rdf(graph, subject) # Identifiable diff --git a/app/models/submodel_element_collection.py b/app/models/submodel_element_collection.py index ba58696..c21b646 100644 --- a/app/models/submodel_element_collection.py +++ b/app/models/submodel_element_collection.py @@ -55,7 +55,9 @@ def to_rdf( if self.value: for idx, submodel_element in enumerate(self.value): # headache - _, created_sub_node = submodel_element.to_rdf(graph, created_node, prefix_uri=str(created_node) + ".") + _, created_sub_node = submodel_element.to_rdf( + graph, created_node, prefix_uri=prefix_uri + str(created_node) + "." + ) graph.add((created_sub_node, AASNameSpace.AAS["index"], rdflib.Literal(idx))) graph.add((created_node, AASNameSpace.AAS["SubmodelElementCollection/value"], created_sub_node)) return created_graph, created_node diff --git a/app/models/submodel_element_list.py b/app/models/submodel_element_list.py index 550f5f9..809f337 100644 --- a/app/models/submodel_element_list.py +++ b/app/models/submodel_element_list.py @@ -18,6 +18,10 @@ # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +import rdflib +from rdflib import RDF + +from app.models.aas_namespace import AASNameSpace from app.models.data_type_def_xsd import DataTypeDefXsd from app.models.model_type import ModelType from app.models.reference import Reference @@ -56,5 +60,123 @@ class SubmodelElementList(SubmodelElement): semanticIdListElement: Optional[Reference] = None typeValueListElement: AasSubmodelElements valueTypeListElement: Optional[DataTypeDefXsd] = None - value: List["SubmodelElementChoice"] = Field(None, min_length=0, discriminator="modelType") + value: Optional[List["SubmodelElementChoice"]] = Field(None, min_length=0) modelType: Literal["SubmodelElementList"] = ModelType.SubmodelElementList.value + + def to_rdf( + self, + graph: rdflib.Graph = None, + parent_node: rdflib.IdentifiedNode = None, + prefix_uri: str = "", + base_uri: str = "", + ) -> (rdflib.Graph, rdflib.IdentifiedNode): + created_graph, created_node = super().to_rdf(graph, parent_node, prefix_uri, base_uri) + created_graph.add((created_node, RDF.type, AASNameSpace.AAS["SubmodelElementList"])) + if self.orderRelevant: + created_graph.add( + ( + created_node, + AASNameSpace.AAS["SubmodelElementList/orderRelevant"], + rdflib.Literal(self.orderRelevant, datatype=rdflib.XSD.boolean), + ) + ) + if self.semanticIdListElement: + _, created_sub_node = self.semanticIdListElement.to_rdf(graph, created_node) + created_graph.add( + ( + created_node, + AASNameSpace.AAS["SubmodelElementList/semanticIdListElement"], + created_sub_node, + ) + ) + created_graph.add( + ( + created_node, + AASNameSpace.AAS["SubmodelElementList/typeValueListElement"], + AASNameSpace.AAS[f"AasSubmodelElements/{self.typeValueListElement.name}"], + ) + ) + if self.valueTypeListElement: + created_graph.add( + ( + created_node, + AASNameSpace.AAS["SubmodelElementList/valueTypeListElement"], + AASNameSpace.AAS[f"DataTypeDefXsd/{self.valueTypeListElement.name}"], + ) + ) + if self.value: + for idx, element_value in enumerate(self.value): + _, created_sub_node = element_value.to_rdf( + graph, created_node, prefix_uri=prefix_uri + str(created_node) + "." + ) + graph.add((created_sub_node, AASNameSpace.AAS["index"], rdflib.Literal(idx))) + graph.add((created_node, AASNameSpace.AAS["SubmodelElementList/value"], created_sub_node)) + + return created_graph, created_node + + @staticmethod + def from_rdf(graph: rdflib.Graph, subject: rdflib.IdentifiedNode) -> "SubmodelElementList": + submodel_element = SubmodelElement.from_rdf(graph, subject) + order_relevant_value = None + order_relevant_ref: rdflib.Literal = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["SubmodelElementList/orderRelevant"]), + None, + ) + if order_relevant_ref: + order_relevant_value = order_relevant_ref.value + + semantic_id_list_element_value = None + semantic_id_list_element_ref: rdflib.Literal = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["SubmodelElementList/semanticIdListElement"]), + None, + ) + if semantic_id_list_element_ref: + semantic_id_list_element_value = Reference.from_rdf(graph, semantic_id_list_element_ref) + type_value_list_element_value = None + type_value_list_element_ref: rdflib.URIRef = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["SubmodelElementList/typeValueListElement"]), + None, + ) + if type_value_list_element_ref: + type_value_list_element_value = AasSubmodelElements[ + type_value_list_element_ref[type_value_list_element_ref.rfind("/") + 1 :] + ] + + value_type_list_element_value = None + value_type_list_element_ref: rdflib.URIRef = next( + graph.objects(subject=subject, predicate=AASNameSpace.AAS["SubmodelElementList/valueTypeListElement"]), + None, + ) + if value_type_list_element_ref: + value_type_list_element_value = DataTypeDefXsd[ + value_type_list_element_ref[value_type_list_element_ref.rfind("/") + 1 :] + ] + + value_value = [] + from app.models.util import from_unknown_rdf + + for submodel_element_uriref in graph.objects( + subject=subject, predicate=AASNameSpace.AAS["SubmodelElementList/value"] + ): + element = from_unknown_rdf(graph, submodel_element_uriref) + value_value.append(element) + + if len(value_value) == 0: + value_value = None + + return SubmodelElementList( + orderRelevant=order_relevant_value, + semanticIdListElement=semantic_id_list_element_value, + typeValueListElement=type_value_list_element_value, + valueTypeListElement=value_type_list_element_value, + value=value_value, + qualifiers=submodel_element.qualifiers, + category=submodel_element.category, + idShort=submodel_element.idShort, + displayName=submodel_element.displayName, + description=submodel_element.description, + extensions=submodel_element.extensions, + semanticId=submodel_element.semanticId, + supplementalSemanticIds=submodel_element.supplementalSemanticIds, + embeddedDataSpecifications=submodel_element.embeddedDataSpecifications, + ) diff --git a/app/models/util.py b/app/models/util.py index 8054c61..a730ff2 100644 --- a/app/models/util.py +++ b/app/models/util.py @@ -24,18 +24,25 @@ from app.models.aas_namespace import AASNameSpace from app.models.annotated_relationship_element import AnnotatedRelationshipElement +from app.models.basic_event_element import BasicEventElement from app.models.blob import Blob from app.models.capability import Capability +from app.models.entity import Entity from app.models.file import File from app.models.multi_language_property import MultiLanguageProperty +from app.models.operation import Operation from app.models.property import Property from app.models.range import Range +from app.models.reference_element import ReferenceElement +from app.models.relationship_element import RelationshipElement from app.models.submodel_element import SubmodelElement from app.models.submodel_element_collection import SubmodelElementCollection +from app.models.submodel_element_list import SubmodelElementList def from_unknown_rdf(graph: rdflib.Graph, subject: rdflib.IdentifiedNode) -> SubmodelElement: # use modeltype as discriminator + # This is my worst code ever :) type_ref: rdflib.URIRef = next( graph.objects(subject=subject, predicate=RDF.type), None, @@ -43,9 +50,9 @@ def from_unknown_rdf(graph: rdflib.Graph, subject: rdflib.IdentifiedNode) -> Sub if type_ref == AASNameSpace.AAS["AnnotatedRelationshipElement"]: return AnnotatedRelationshipElement.from_rdf(graph, subject) if type_ref == AASNameSpace.AAS["RelationshipElement"]: - raise NotImplementedError() + return RelationshipElement.from_rdf(graph, subject) if type_ref == AASNameSpace.AAS["BasicEventElement"]: - raise NotImplementedError() + return BasicEventElement.from_rdf(graph, subject) if type_ref == AASNameSpace.AAS["Blob"]: return Blob.from_rdf(graph, subject) if type_ref == AASNameSpace.AAS["File"]: @@ -57,14 +64,14 @@ def from_unknown_rdf(graph: rdflib.Graph, subject: rdflib.IdentifiedNode) -> Sub if type_ref == AASNameSpace.AAS["Range"]: return Range.from_rdf(graph, subject) if type_ref == AASNameSpace.AAS["ReferenceElement"]: - raise NotImplementedError() + return ReferenceElement.from_rdf(graph, subject) if type_ref == AASNameSpace.AAS["SubmodelElementCollection"]: return SubmodelElementCollection.from_rdf(graph, subject) if type_ref == AASNameSpace.AAS["SubmodelElementList"]: - raise NotImplementedError() + return SubmodelElementList.from_rdf(graph, subject) if type_ref == AASNameSpace.AAS["Entity"]: - raise NotImplementedError() + return Entity.from_rdf(graph, subject) if type_ref == AASNameSpace.AAS["Capability"]: return Capability.from_rdf(graph, subject) if type_ref == AASNameSpace.AAS["Operation"]: - raise NotImplementedError() + return Operation.from_rdf(graph, subject) diff --git a/tests/serialization/shell_serialization_test.py b/tests/serialization/shell_serialization_test.py index 07004e9..bdda32b 100644 --- a/tests/serialization/shell_serialization_test.py +++ b/tests/serialization/shell_serialization_test.py @@ -366,16 +366,15 @@ def test_any_submodel_element_minimal_to_rdf(): "Blob", "Range", "ReferenceElement", - # "RelationshipElement", - # "Operation", - # "Entity", - # "BasicEventElement", - # "SubmodelElementList", + "RelationshipElement", + "Entity", + "SubmodelElementList", + "BasicEventElement", + "Operation", ]: payload_json = json.loads(get_testdata_json(model, "minimal"))["submodels"][0] payload = Submodel(**payload_json) graph, created_node = payload.to_rdf() - print(graph.serialize(format="turtle_custom")) re_created = Submodel.from_rdf(graph, created_node) assert re_created == payload @@ -391,17 +390,15 @@ def test_any_submodel_element_maximal_to_rdf(): "Blob", "Range", "ReferenceElement", - # "RelationshipElement", - # "Operation", - # "Entity", - # "BasicEventElement", - - # "SubmodelElementList", + "RelationshipElement", + "Entity", + "SubmodelElementList", + "BasicEventElement", + "Operation", ]: payload_json = json.loads(get_testdata_json(model, "maximal"))["submodels"][0] payload = Submodel(**payload_json) graph, created_node = payload.to_rdf() - print(graph.serialize(format="turtle_custom")) re_created = Submodel.from_rdf(graph, created_node) assert re_created == payload