From cc48280e006967b1ab97ab04c74b9c181352fcf8 Mon Sep 17 00:00:00 2001
From: mehtank <mehtank>
Date: Thu, 29 Apr 2021 00:51:18 -0700
Subject: [PATCH] Generate component compositional hierarchy tree

---
 rocolib/__init__.py         |   2 +-
 rocolib/library/__init__.py | 119 +++++++++++++-----------------------
 rocolib/utils/filter.py     |  39 ------------
 3 files changed, 43 insertions(+), 117 deletions(-)
 delete mode 100644 rocolib/utils/filter.py

diff --git a/rocolib/__init__.py b/rocolib/__init__.py
index a54d298..db495d5 100644
--- a/rocolib/__init__.py
+++ b/rocolib/__init__.py
@@ -9,7 +9,7 @@ from os.path import realpath
 from os.path import relpath
 
 
-__version__ = "0.2.1"
+__version__ = "0.2.2"
 __author__ = 'UCLA LEMUR'
 __credits__ = 'The Laboratory for Embedded Machines and Ubiquitous Robots'
 
diff --git a/rocolib/library/__init__.py b/rocolib/library/__init__.py
index 5e3fbc3..73f6613 100644
--- a/rocolib/library/__init__.py
+++ b/rocolib/library/__init__.py
@@ -14,32 +14,51 @@ ROCOLIB_LIBRARY = dirname(realpath(__file__))
 
 pyComponents = [ basename(f)[:-3] for f in glob(ROCOLIB_LIBRARY + "/[!_]*.py")]
 yamlComponents = [ basename(f)[:-5] for f in glob(ROCOLIB_LIBRARY + "/*.yaml")]
-allComponents = list(set(pyComponents + yamlComponents))
+allComponents = set(pyComponents + yamlComponents)
+
+def getSubcomponents(c):
+    try:
+        return set((x['classname'] for x in load_yaml(c).get('subcomponents', dict()).values()))
+    except FileNotFoundError:
+        return set()
+
+def getComponentTree():
+    tree = []
+    ac = set(allComponents)
+    while ac:
+        leaves = []
+        for c in ac:
+            subcomponents = getSubcomponents(c)
+            if not subcomponents.intersection(ac):
+                leaves.append(c)
+        tree.append(sorted(leaves))
+        ac -= set(leaves)
+    return tree
 
 def getComponent(c, **kwargs):
-  '''
-  Here we are  doing Dynamic instantiation from string name of a class in dynamically imported module
-  Parameter c (str): component name e.g. 'Stool'
-  '''
-  if c in pyComponents:
-    # Load "module.submodule.MyClass"
-    obj = getattr(importlib.import_module("rocolib.library." + c), c)
-    # Instantiate the class (pass arguments to the constructor, if needed)
-    my_obj = obj()
-  elif c in yamlComponents:
-    my_obj = Component(f"{ROCOLIB_LIBRARY}/{c}.yaml")
-  else:
-    raise ValueError(f"Component {c} not found in library")
-
-  for k, v in kwargs.items():
-    if k == 'name':
-      my_obj.setName(v)
+    '''
+    Here we are doing Dynamic instantiation from string name of a class in dynamically imported module
+    Parameter c (str): component name e.g. 'Stool'
+    '''
+    if c in pyComponents:
+        # Load "module.submodule.MyClass"
+        obj = getattr(importlib.import_module("rocolib.library." + c), c)
+        # Instantiate the class (pass arguments to the constructor, if needed)
+        my_obj = obj()
+    elif c in yamlComponents:
+        my_obj = Component(f"{ROCOLIB_LIBRARY}/{c}.yaml")
     else:
-      my_obj.setParameter(k, v)
-  if 'name' not in kwargs:
-    my_obj.setName(c)
+        raise ValueError(f"Component {c} not found in library")
+
+    for k, v in kwargs.items():
+        if k == 'name':
+            my_obj.setName(v)
+        else:
+            my_obj.setParameter(k, v)
+    if 'name' not in kwargs:
+        my_obj.setName(c)
 
-  return my_obj
+    return my_obj
 
 def rebuild(built=None):
     if built is None:
@@ -71,7 +90,7 @@ def rebuildComponent(c, built=None, throw=True):
         for sc in subcomponents:
             rebuildComponent(sc, built)
 
-        # XXX TOOD: Test to make sure we don't call this script and then infinitely recurse!
+        # XXX TODO: Test to make sure we don't call this script and then infinitely recurse!
         log.debug(f"Calling os.system: % python {ROCOLIB_LIBRARY}/{src}")
         if system(f"python {ROCOLIB_LIBRARY}/{src}"):
             success = False
@@ -94,57 +113,3 @@ def getComponentPaths(c):
         if src:
             paths["builder"] = rocopath(join(ROCOLIB_LIBRARY, src))
     return paths
-
-
-# tag : [[required ports], [forbidden ports]]
-tagDefinitions = {
-  'sensor': [["DataOutputPort"],[]],
-  'actuator': [["DataInputPort"],[]],
-  'mechanical': [["EdgePort"],[]],
-  'device': [["MountPort"],[]],
-  'UI': [[],["MountPort", "EdgePort"]]
-}
-
-def tag(ports):
-  tags = {}
-  portset = set(ports.keys())
-  for tag, (must, cant) in tagDefinitions.items():
-    if set(must).issubset(portset) and not len(set(cant).intersection(portset)):
-      tags[tag] = [port for ptype in must for port in ports[ptype] ]
-  return tags
-
-_taggedComponents = {}
-def getTags(x):
-  if x in _taggedComponents:
-    return _taggedComponents[x]
-
-  try:
-    c = getComponent(x)
-  except:
-    return None
-
-  if isinstance(c, Component):
-    interfaces = list(c.interfaces.keys())
-    ports = {}
-    for iname in interfaces:
-      i = c.getInterface(iname)
-      iclass = i.__class__.__name__
-      try:
-        ports[iclass].append(iname)
-      except KeyError:
-        ports[iclass] = [iname]
-    _taggedComponents[x] = tag(ports)
-    return tag(ports)
-  return None
-
-def taggedComponents(components = None):
-  if components == None:
-    components = allComponents
-  for x in components:
-    if getTags(x):
-      yield x, getTags(x)
-
-def filterComponents(tagList, components = None):
-  for x, tags in taggedComponents(components):
-    if set(tagList).issubset(set(tags.keys())):
-      yield x, [port for tag in tagList for port in tags[tag] ]
diff --git a/rocolib/utils/filter.py b/rocolib/utils/filter.py
deleted file mode 100644
index bfc90ea..0000000
--- a/rocolib/utils/filter.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from rocolib.library import filterComponents
-
-print("~~~")
-print("Actuators")
-print("~~~")
-'''
-print "All:"
-for c in filterComponents(["actuator"]): 
-  print "-", c
-'''
-print("Mechanical actuators:")
-for c in filterComponents(["actuator", "mechanical"]): 
-  print("-", c)
-print("Physical interface devices:")
-for c in filterComponents(["actuator", "device"]): 
-  print("-", c)
-print("Virtual UI widgets:")
-for c in filterComponents(["actuator", "UI"]): 
-  print("-", c)
-
-print()
-
-print("~~~")
-print("Sensors")
-print("~~~")
-'''
-print "All:"
-for c in filterComponents(["sensor"]): 
-  print "-", c
-print "Mechanical feedback sensors:"
-for c in filterComponents(["sensor", "mechanical"]): 
-  print "-", c
-'''
-print("Environmental sensing devices:")
-for c in filterComponents(["sensor", "device"]): 
-  print("-", c)
-print("Virtual UI widgets:")
-for c in filterComponents(["sensor", "UI"]): 
-  print("-", c)
-- 
GitLab