From e282e9fee352354e02ad9929f9c074ac4fa0597c Mon Sep 17 00:00:00 2001 From: danielwilms Date: Tue, 14 Jun 2022 17:35:58 +0300 Subject: [PATCH] VSSo: VehicleComponents as instances (#171) * VSSo: VehicleComponents as instances Currently the VehicleComponents were modelled as subclasses with `rdfs:subclass`. This change introduces a variable to choose either way until it's decided. Additionally introduced documentation improvements and code cleanups. Mentioned in: https://github.com/w3c/vsso/issues/22 Signed-off-by: Daniel Wilms * Adding `vsso:Vehicle` as an instance of `vsso-core:VehicleComponent` Change reflecting discussion in: https://github.com/w3c/vsso/pull/41#issuecomment-1154768824 Signed-off-by: Daniel Wilms --- contrib/vspec2ttl/vspec2ttl.py | 143 +++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 62 deletions(-) diff --git a/contrib/vspec2ttl/vspec2ttl.py b/contrib/vspec2ttl/vspec2ttl.py index 8ac80f94..4a928c2d 100755 --- a/contrib/vspec2ttl/vspec2ttl.py +++ b/contrib/vspec2ttl/vspec2ttl.py @@ -31,8 +31,21 @@ from enum import Enum +# There are two options of handling branches: +# 1. branches are handled as _subclasses_ of vehicle components +# 2. branches are handled as _instances_ of vehicle components +# +# As it's not decided for now, we can generate both by setting this +# variable to True or False. True means that branches are handled as +# subclasses of vehicle components. False means that branches are handled +# as instances of vehicle components. + +COMPONENTS_AS_CLASSES = False + class VssoCoreConcepts (Enum): - +### +# Class to define the concepts and their names for later references +### EMPTY = "" BELONGS_TO = "belongsToVehicleComponent" HOLDS_VALUE = "vehiclePropertyValue" @@ -63,24 +76,13 @@ def uri_string(self): return f'{self.ns}{self.value}' -def usage(): - print(f""" -Usage: {sys.argv[0]} [options] vspec_file ttl_file - - where [options] are: - - -I include_dir Add include directory to search for included vspec - files. Can be used multiple timees. - - vspec_file The vehicle specification file to parse. - - ttl_file The file to output the ttl data to. -""") - sys.exit(255) - - def setup_graph(): - # create a Graph + ### + # function to define the metadata of VSSo, + # to define the used prefixes and namespaces, + # and to crete the basic rdflib graph + ### + g = Graph() ontology_description_ttl = """ @@ -92,6 +94,7 @@ def setup_graph(): @prefix vann: . @prefix vsso-core: . @prefix vsso: . + @prefix skos: . vsso: rdf:type owl:Ontology ; @@ -117,19 +120,14 @@ def setup_graph(): g.parse(data=ontology_description_ttl, format="turtle") - g.bind ("skos", SKOS) - g.bind ("rdfs", RDFS) - print("--- printing mboxes ---") - print(g.serialize(format='ttl')) return g -def setUniqueNodeName (name): - if 'Vehicle' != name: - return name.replace('Vehicle','').replace('.','') - else: - return name.replace('.','') - def setTTLName (node): + ### + # function to set a unique name of a node + # which is used in the ontology. Sets a combined name of + # the node and its parent, if the parent is not `vehicle`. + ### if node.ttl_name: return node.ttl_name if node.parent and node.parent.name != "Vehicle": @@ -140,25 +138,29 @@ def setTTLName (node): return ttl_name def print_ttl_content(file, tree): + ### + # function to create & print the content of the ontology + # in turtle format. + ### + tree_node: VSSNode name_list = [] vsso_name_list = [] duplication = {} duplication_vsso = {} datatypes = {} - enums = 0 + namespace = "https://github.com/w3c/vsso#" + graph = setup_graph() - for tree_node in PreOrderIter(tree): - data_type_str = tree_node.data_type.value if tree_node.has_data_type() else "" - unit_str = tree_node.unit.value if tree_node.has_unit() else "" - type_str = tree_node.type.value - + for tree_node in PreOrderIter(tree): #< iterate over the tree in preorder + # start with setting the unique name of the node name = setTTLName(tree_node) + + # check if the original name is already used + # and store for later analysis if tree_node.name in name_list: - # print (f"** warning: {tree_node.name}" ) - # print (f"** VSSO warning Replaced by: {name}" ) if tree_node.name in duplication.keys(): duplication [tree_node.name] += 1 else: @@ -166,11 +168,10 @@ def print_ttl_content(file, tree): else: name_list.append (tree_node.name) - + # check if combined name is unique, otherwise + # use the parents unique name and combine with the node name if name in vsso_name_list: - # print (f"** VSSO warning: {name}" ) name = setTTLName(tree_node.parent) + tree_node.name - # print (f"** VSSO warning Replaced by: {name}" ) if name in duplication_vsso.keys(): duplication_vsso [name] += 1 @@ -179,24 +180,20 @@ def print_ttl_content(file, tree): else: vsso_name_list.append (name) - namespace = "https://github.com/w3c/vsso#" - + # set the URI for the node node = URIRef(namespace + name) - # not needed, in case we use classes - # if VSSType.ATTRIBUTE == tree_node.type: - # node = URIRef(f"{VssoCoreConcepts.EMPTY.uri_string}has{name}") - + # basic metadata, independent of node type graph.add((node, RDFS.label, Literal(name,"en"))) graph.add((node, VssoCoreConcepts.VSS_CLASSIFICATION.uri, Literal(tree_node.qualified_name('.'),"en"))) graph.add((node, SKOS.definition, Literal(tree_node.description,"en"))) + # if a comment is set in the VSS node add the comment to the ontology if tree_node.comment: graph.add((node, RDFS.comment, Literal(tree_node.comment,"en"))) - parent_namespace = "https://github.com/w3c/vsso#" - # branch nodes (incl. instance handling) - if VSSType.BRANCH == tree_node.type: + # branch nodes as vsso:VehicleComponent subclasses if variable is set accordingly + if VSSType.BRANCH == tree_node.type and COMPONENTS_AS_CLASSES: if tree_node.parent: graph.add((node, RDF.type, OWL.Class)) graph.add((node, RDFS.subClassOf, VssoCoreConcepts.VEHICLE_COMP.uri)) @@ -207,46 +204,68 @@ def print_ttl_content(file, tree): if "Vehicle" == setTTLName(tree_node.parent): graph.add((b, OWL.allValuesFrom, VssoCoreConcepts.VEHICLE.uri)) else: - graph.add((b, OWL.allValuesFrom, URIRef(parent_namespace + setTTLName(tree_node.parent)))) + graph.add((b, OWL.allValuesFrom, URIRef(namespace + setTTLName(tree_node.parent)))) graph.add((node, RDFS.subClassOf, b)) print( [x.name if x.type != VSSType.BRANCH else None for x in tree_node.children]) - + # branch nodes as vsso:VehicleComponent instances if variable is set accordingly + elif VSSType.BRANCH == tree_node.type and not COMPONENTS_AS_CLASSES: + graph.add ((node, RDF.type, VssoCoreConcepts.VEHICLE_COMP.uri)) + if tree_node.parent: + graph.add((node, VssoCoreConcepts.PART_OF_VEH_COMP.uri, URIRef(namespace + setTTLName(tree_node.parent)))) + + # attributes, sensors & actuators else: if VSSType.ATTRIBUTE == tree_node.type: graph.add((node, RDF.type, OWL.Class)) graph.add((node, RDFS.subClassOf, VssoCoreConcepts.VEHICLE_STAT.uri)) - b = BNode() - graph.add((b, RDF.type, OWL.Restriction)) - graph.add((b, OWL.onProperty, VssoCoreConcepts.BELONGS_TO.uri)) - if "Vehicle" == setTTLName(tree_node.parent): - graph.add((b, OWL.allValuesFrom, VssoCoreConcepts.VEHICLE.uri)) - else: - graph.add((b, OWL.allValuesFrom, URIRef(parent_namespace + setTTLName(tree_node.parent)))) - - graph.add((node, RDFS.subClassOf, b)) + if COMPONENTS_AS_CLASSES: + b = BNode() + graph.add((b, RDF.type, OWL.Restriction)) + graph.add((b, OWL.onProperty, VssoCoreConcepts.BELONGS_TO.uri)) + if "Vehicle" == setTTLName(tree_node.parent): + graph.add((b, OWL.allValuesFrom, VssoCoreConcepts.VEHICLE.uri)) + else: + graph.add((b, OWL.allValuesFrom, URIRef(namespace + setTTLName(tree_node.parent)))) + + graph.add((node, RDFS.subClassOf, b)) else: # < vss actuators & sensors - + # check different datatypes in use if (tree_node.data_type in datatypes.keys()): datatypes[tree_node.data_type] += 1 else: datatypes[tree_node.data_type] = 1 graph.add((node, RDF.type, VssoCoreConcepts.VEHICLE_SIGNAL.uri)) + graph.add((node, VssoCoreConcepts.BELONGS_TO.uri, URIRef(namespace + setTTLName(tree_node.parent)))) if VSSType.ACTUATOR == tree_node.type: graph.add((node, RDF.type, VssoCoreConcepts.VEHICLE_ACT.uri)) - + # write the file and print the metadata. file.write(graph.serialize(format='ttl')) - + for ns, url in graph.namespaces(): + print(f"@prefix {ns}: <{url}>") print (duplication) print (duplication_vsso) print (datatypes) +def usage(): + print(f""" +Usage: {sys.argv[0]} [options] vspec_file ttl_file + where [options] are: + + -I include_dir Add include directory to search for included vspec + files. Can be used multiple timees. + + vspec_file The vehicle specification file to parse. + + ttl_file The file to output the ttl data to. +""") + sys.exit(255) if __name__ == "__main__": #