diff --git a/src/volue/mesh/_attribute.py b/src/volue/mesh/_attribute.py index 5b91780e..402421e2 100644 --- a/src/volue/mesh/_attribute.py +++ b/src/volue/mesh/_attribute.py @@ -117,6 +117,18 @@ def __init__( self.id: uuid.UUID = _from_proto_guid(proto_attribute.id) self.path: str = proto_attribute.path self.name: str = proto_attribute.name + # owner_id field is always set starting from Mesh 2.11 + # TODO: Remove the check if the owner_id field exists when we move to support Mesh >= 2.11 + self.owner_id = ( + _from_proto_guid(proto_attribute.owner_id.id) + if proto_attribute.HasField("owner_id") + else None + ) + self.owner_path = ( + proto_attribute.owner_id.path + if proto_attribute.HasField("owner_id") + else None + ) self.definition = None diff --git a/src/volue/mesh/examples/traverse_model.py b/src/volue/mesh/examples/traverse_model.py index f75baf10..36a10485 100644 --- a/src/volue/mesh/examples/traverse_model.py +++ b/src/volue/mesh/examples/traverse_model.py @@ -1,16 +1,33 @@ from volue.mesh import Connection, OwnershipRelationAttribute from volue.mesh.examples import _get_connection_info +leaves = [] -def traverse_model(session: Connection.Session, target, depth=0): + +def traverse_model_top_down(session: Connection.Session, target, depth=0): """Traverses the Mesh model recursively.""" object = session.get_object(target) print(f"{'..' * depth}{object.name}") + leaf = True for attr in object.attributes.values(): if isinstance(attr, OwnershipRelationAttribute): for child_id in attr.target_object_ids: - traverse_model(session, child_id, depth + 1) + leaf = False + traverse_model_top_down(session, child_id, depth + 1) + if leaf: + leaves.append(object) + + +def traverse_model_bottom_up(session: Connection.Session, target, model): + object = session.get_object(target) + depth = object.path.count("/") - 1 + print(f"{'..' * depth}{object.name}") + if object.owner_id == model.id: + print(model.name) + return + attribute = session.get_attribute(object.owner_id) + traverse_model_bottom_up(session, attribute.owner_id, model) def main(address, port, root_pem_certificate): @@ -20,14 +37,22 @@ def main(address, port, root_pem_certificate): with connection.create_session() as session: models = session.list_models() for model in models: - traverse_model(session, model.id) - - # Excepted output: - # Model - # ..ChildObject1 - # ....SubChildObject1 - # ....SubChildObject2 - # ..ChildObject2 + leaves.clear() + print(f"\nModel: '{model.name}'") + print("Top-bottom traversal:") + traverse_model_top_down(session, model.id) + # Excepted output: + # Model + # ..ChildObject1 + # ....SubChildObject1 + # ....SubChildObject2 + # ..ChildObject2 + print("\nBottom-top traversal:") + traverse_model_bottom_up(session, leaves[0].id, model) + # Excepted output: + # ....SubChildObject1 + # ..ChildObject1 + # Model if __name__ == "__main__": diff --git a/src/volue/mesh/proto/core/v1alpha/core.proto b/src/volue/mesh/proto/core/v1alpha/core.proto index 2b085516..dd860126 100644 --- a/src/volue/mesh/proto/core/v1alpha/core.proto +++ b/src/volue/mesh/proto/core/v1alpha/core.proto @@ -846,6 +846,8 @@ message Attribute { AttributeValueType value_type = 6; // If the `value_type` is a collection then this flag is set to `True`. bool value_type_collection = 7; + + MeshId owner_id = 8; } enum AttributeValueType { diff --git a/src/volue/mesh/tests/test_attributes.py b/src/volue/mesh/tests/test_attributes.py index 525b5a69..1d83caea 100644 --- a/src/volue/mesh/tests/test_attributes.py +++ b/src/volue/mesh/tests/test_attributes.py @@ -27,6 +27,11 @@ def verify_plant_base_attribute( assert attribute.path == path assert attribute.name == name assert attribute.id == id + assert attribute.owner_id == uuid.UUID("0000000a-0001-0000-0000-000000000000") + assert ( + attribute.owner_path + == "Model/SimpleThermalTestModel/ThermalComponent.ThermalPowerToPlantRef/SomePowerPlant1" + ) if is_definition: assert (