frame_registrar.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # frame_registrar.py
00003 
00004 import roslib; roslib.load_manifest('frame_registrar')
00005 import rospy
00006 import sys
00007 import os
00008 import yaml
00009 import traceback
00010 from roboframenet_msgs.msg import *
00011 from roboframenet_msgs.srv import *
00012 
00013 
00014 
00015 ###############################################################################
00016 ### Globals
00017 
00018 semantic_frames = {}   # Index: frame.name ; Element: _SemanticFrame
00019 
00020 
00021 
00022 
00023 ###############################################################################
00024 ### Internal Classes
00025 
00026 class _SemanticFrame(object):
00027     def __init__(self, name):
00028         self.name = name # string
00029         self.description = "" # string
00030         self.frame_elements = [] # listof(_FrameElement)
00031         self.children = [] # listof(listof(_RelatedFrame))
00032         # ("list of frame sequences") ^^
00033         self.callbacks = [] # listof(string)
00034 
00035 
00036     @staticmethod
00037     def import_yaml(yaml):
00038         try:
00039             sf = _SemanticFrame(yaml['name'])
00040             sf.description = yaml['description']
00041             if 'frame_elements' in yaml:
00042                 for fe in yaml['frame_elements']:
00043                     f = _FrameElement(fe['name'], fe['is_core'])
00044                     if not f.is_core and 'default' in fe:
00045                         f.default = str(fe['default'])
00046                     sf.frame_elements.append(f)
00047             if 'children' in yaml:
00048                 for child in yaml['children']:
00049                     sf.children.append(map(lambda subchild:
00050                                                _RelatedFrame.import_yaml(subchild),
00051                                            listify(child)))
00052             if 'parents' in yaml:
00053                 for parent in yaml['parents']:
00054                     parent_relframe = _RelatedFrame.import_yaml(parent)
00055                     parent_semframe = get_semantic_frame(parent_relframe.name)
00056                     # Since the relations are the same between parent and child,
00057                     #   just change the parent's name to the child's,
00058                     #   and you have the relation from the parent's perspective.
00059                     parent_relframe.name = sf.name
00060                     parent_semframe.children.append(parent_relframe)
00061             # sf.callbacks: not allowed.  (ActionServer must register
00062             # itself upon startup.)
00063         except:
00064             rospy.logerr("Error converting yaml '" +
00065                          (yaml['name'] if 'name' in yaml else "???") +
00066                          "' to _SemanticFrame: " + str((sys.exc_info())))
00067             traceback.print_tb(sys.exc_info()[2])
00068         else:
00069             return sf
00070 
00071     def get_frame_element(self, name):
00072         for fe in self.frame_elements:
00073             if fe.name == name:
00074                 return fe
00075         rospy.logerr("Frame " + str(self) + " does not have frame element '" +
00076                      name + "'!  Returning False.")
00077         return False
00078 
00079     def describe_frame(self):
00080         return self.description
00081 
00082     def has_callbacks(self):
00083         return self.callbacks # [] is False; [anything but blank] is True
00084 
00085     # filled_sem_frame is _FilledSemanticFrame
00086     # returns a FilledSemanticFrame
00087     def to_ros_EmptySemanticFrame(self):
00088         esf = EmptySemanticFrame()
00089         esf.name = self.name
00090         for fe in self.frame_elements:
00091             # See if the filled_lexical_unit has an argument for each fe.  Otherwise,
00092             #   the argument will be "".
00093             efe = EmptyFrameElement()
00094             efe.name = fe.name
00095             efe.default = fe.default
00096             efe.is_core = fe.is_core
00097             esf.frame_elements.append(efe)
00098         return esf
00099 
00100     def __str__(self):
00101         return self.to_str(self.name)
00102 
00103     @staticmethod
00104     def to_str(name):
00105         return "<_SemanticFrame '" + name + "'>"
00106 
00107 
00108 
00109 class _FilledSemanticFrame(_SemanticFrame):
00110     def __init__(self, semframe):
00111         self.name           = semframe.name
00112         self.description    = semframe.description
00113         self.frame_elements = map(lambda fe: _FilledFrameElement(fe.name, fe.is_core),
00114                                   semframe.frame_elements)
00115         self.children       = semframe.children
00116         #self.parents
00117         self.callbacks      = semframe.callbacks
00118 
00119     def fill_frame_element(self, name, arg):
00120         fe = self.get_frame_element(name)
00121         if fe:
00122             fe.argument = arg
00123             return True
00124         else:
00125             return False
00126 
00127     def describe_frame():
00128         return(self.description + '\n' +
00129                '\n'.join([(element.name + " == " + element.argument)
00130                           for element in frame_elements]) +
00131                '\n')
00132 
00133     # filled_sem_frame is _FilledSemanticFrame
00134     # returns a FilledSemanticFrame
00135     def to_ros_FilledSemanticFrame(self):
00136         fsf = FilledSemanticFrame()
00137         fsf.name = self.name
00138         fsf.action_server_topics = self.callbacks
00139         for fe in self.frame_elements:
00140             # See if the filled_lexical_unit has an argument for each fe.  Otherwise,
00141             #   the argument will be "".
00142             ffe = FilledFrameElement()
00143             ffe.name = fe.name
00144             ffe.argument = fe.argument
00145             fsf.frame_elements.append(ffe)
00146         return fsf
00147 
00148 
00149 class _FrameElement:
00150     def __init__(self, name, is_core):
00151         self.name = name # string
00152         self.is_core = is_core # boolean
00153         self.default = "" # string
00154 
00155 
00156 class _FilledFrameElement:
00157     def __init__(self, name, is_core):
00158         self.name = name # string
00159         self.is_core = is_core
00160         self.argument = "" # string
00161 
00162 
00163 class _RelatedFrame(object): # enable class methods (see http://www.rexx.com/~dkuhlman/python_101/python_101.html 2.5.2.4)
00164     def __init__(self, name):
00165         self.name = name # string
00166         self.frame_element_relations = [] # listof(_FrameElementRelation)
00167 
00168     @staticmethod
00169     def import_yaml(yaml):
00170         rf = _RelatedFrame(yaml['name'])
00171         if 'frame_element_relations' in yaml:
00172             for relation in yaml['frame_element_relations']:
00173                 # Get parent name/value
00174                 if 'parent_name' in relation:
00175                     parent = relation['parent_name']
00176                     parent_is_value = False
00177                 elif 'parent_value' in relation:
00178                     parent = relation['parent_value']
00179                     parent_is_value = True
00180                 else:
00181                     rospy.logerr("_RelatedFrame.import_yaml: No parent name or value!")
00182                 # Get child name/value
00183                 if 'child_name' in relation:
00184                     child = relation['child_name']
00185                     child_is_value = False
00186                 elif 'child_value' in relation:
00187                     child = relation['child_value']
00188                     child_is_value = True
00189                 else:
00190                     rospy.logerr("_RelatedFrame.import_yaml: No child name or value!")
00191                 rf.frame_element_relations.append(_FrameElementRelation(parent,
00192                                                                         parent_is_value,
00193                                                                         child,
00194                                                                         child_is_value))
00195         return rf
00196 
00197 
00198 
00199 class _FrameElementRelation:
00200     # A relationship between 2 frame elements
00201     def __init__(self, parent, parent_is_value, child, child_is_value):
00202         self.parent = parent # string
00203         self.parent_is_value = parent_is_value # boolean.  Parent is either a value or a name.
00204         if child_is_value:
00205             self.child = map(lambda c: c, listify(child)) # listof(string) if child_is_value
00206         else:
00207             self.child = child # string if not child_is_value
00208         self.child_is_value = child_is_value # boolean.  Child is either a value or a name.
00209 
00210 
00211 
00212 
00213 
00214 
00215 ###############################################################################
00216 ### Helpers
00217 
00218 def listify(x):
00219     return x if isinstance(x, list) else [x]
00220 
00221 # Retrives frame from the database.
00222 # Returns a _SemanticFrame, or False if not found.
00223 def get_semantic_frame(frame_name):
00224     global semantic_frames
00225     if frame_name in semantic_frames:
00226         return semantic_frames[frame_name]
00227     else:
00228         rospy.logwarn("get_semantic_frame: " +
00229                       _SemanticFrame.to_str(frame_name) +
00230                       " not found.  (This may be normal.  Or not.)")
00231         return False
00232 
00233 
00234 # Stores semantic frame in the database.
00235 # Returns True on success and False on failure.
00236 def store_semantic_frame(frame):
00237     global semantic_frames
00238     if not isinstance(frame, _SemanticFrame):
00239         rospy.logerr("store_semantic_frame: Given object (" + str(frame) +
00240                      ") is not a _SemanticFrame object!  Ignoring request.")
00241         return False
00242     else:
00243         if frame.name in semantic_frames:
00244             rospy.logwarn("store_semantic_frame: Frame with name '" +
00245                           str(frame.name) + "' previously stored in" +
00246                           " semantic_frames.  Overwriting!")
00247         semantic_frames[frame.name] = frame
00248         rospy.loginfo("Added new semantic frame " + str(frame) + ".")
00249         return True
00250 
00251 
00252 # Loads semantic frame and stores it in the database.
00253 # Returns True on success and False on failure.
00254 def load_semantic_frame_yaml(filename):
00255     try:
00256         frame_yaml = yaml.load(open(filename, 'r'))
00257     except:
00258         rospy.logerr("Error loading file '%s': %s" % (str(filename), str(sys.exc_info())))
00259         return False
00260     else:
00261         return store_semantic_frame(_SemanticFrame.import_yaml(frame_yaml))
00262 
00263 
00264 
00265 # filled_lexical_unit is FilledLexicalUnit
00266 # returns listof(listof(_FilledSemanticFrame)) (list of sequences of _FilledSemanticFrames to accomplish task)
00267 def fill_semantic_frame_sequences_from_filled_lexical_unit(filled_lexical_unit):
00268     FilledSemanticFrame_sequences = []
00269     filled_sem_frame = fill_semantic_frame_from_filled_lexical_unit(filled_lexical_unit)
00270     if filled_sem_frame:
00271         fsf_sequences = fill_semantic_frame_descendants(filled_sem_frame)
00272         FilledSemanticFrame_sequences.extend(fsf_sequences)
00273     else:
00274         rospy.logwarn("cb_GetFrames: No filled_sem_frame for filled_lexical_unit:\n" + str(filled_lexical_unit))
00275     return FilledSemanticFrame_sequences
00276 
00277 # filled_lexical_unit is FilledLexicalUnit
00278 # returns a _FilledSemanticFrame
00279 def fill_semantic_frame_from_filled_lexical_unit(filled_lexical_unit):
00280     semantic_frame = get_semantic_frame(filled_lexical_unit.frame_name)
00281     if semantic_frame:
00282         filled_semantic_frame = _FilledSemanticFrame(semantic_frame)
00283         # Fill
00284         for frame_element in filled_lexical_unit.frame_elements:
00285             # See if the filled_lexical_unit has an argument for each frame
00286             #   element.  Otherwise, the argument will be "".
00287             if not filled_semantic_frame.fill_frame_element(frame_element.name,
00288                                                             frame_element.argument):
00289                 rospy.logwarn("fill_semantic_frame_from_filled_lexical_unit:" +
00290                               " Frame element parameter '" + frame_element.name +
00291                               "' could not be filled (argument '" +
00292                               frame_element.argument + "') in frame " +
00293                               str(filled_semantic_frame) + ".")
00294         # Check all core elements have been filled
00295         for fe in filled_semantic_frame.frame_elements:
00296             if fe.is_core and not fe.argument:
00297                 rospy.logwarn("fill_semantic_frame_from_filled_lexical_unit:" +
00298                               " Core element '" + fe.name + "' has no" +
00299                               " argument! Offending filled lexical unit:\n" +
00300                               str(filled_lexical_unit))
00301                 return False
00302         # Success!
00303         return filled_semantic_frame
00304     else:
00305         rospy.logwarn("fill_semantic_frame_from_filled_lexical_unit: Requested frame " +
00306                       _SemanticFrame.to_str(filled_lexical_unit.frame_name) +
00307                       " does not exist!")
00308         return False
00309 
00310 # filled_sem_frame is _FilledSemanticFrame
00311 # returns listof(listof(_FilledSemanticFrame)), a list of possible sequences which can fulfill the
00312 #   given filled_sem_frame, including the sequence of just the filled_sem_frame itself.
00313 def fill_semantic_frame_descendants(filled_sem_frame):
00314     filled = [[filled_sem_frame]]
00315     for rf_child_list in filled_sem_frame.children:
00316         filled_child_frames = []
00317         for rf_child in rf_child_list:
00318             filled_child_frame = fill_semantic_frame_from_parent(filled_sem_frame, rf_child)
00319             if filled_child_frame:
00320                 filled_child_frames.append(filled_child_frame)
00321         # See if all elements of this sequence were successfully filled in.
00322         all_filled = (len(filled_child_frames) == len(rf_child_list))
00323         # If so, find all combinations that describe each child (including the
00324         #   child itself.
00325         if all_filled:
00326             child_combinations = [] # listof( listof(listof(_FilledSemanticFrame)) )
00327             for filled_child_frame in filled_child_frames:
00328                 child_combinations.append(fill_semantic_frame_descendants(filled_child_frame))
00329             all_combinations = map(lambda combo: reduce(lambda a,b: list_join(a,b), combo),
00330                                    all_combinations_of_lists(child_combinations))
00331             filled.extend(all_combinations)
00332     return filled
00333 
00334 # Returns a new list which is the joining of the two input lists
00335 def list_join(a,b):
00336     c = []
00337     c.extend(a)
00338     c.extend(b)
00339     return c
00340 
00341 # [[1,2],[3,4]] -> [[1,3],[1,4],[2,3],[2,4]]
00342 def all_combinations_of_lists(lists):
00343     def all_combinations_of_lists_recursion(lists, index):
00344         if len(lists) == index: return [[]]
00345         combinations = []
00346         recursed_combinations = all_combinations_of_lists_recursion(lists, index+1)
00347         for item in lists[index]:
00348             for recursed_combination in recursed_combinations:
00349                 r = [item]
00350                 r.extend(recursed_combination)
00351                 combinations.append(r)
00352         return combinations
00353     return all_combinations_of_lists_recursion(lists,0)
00354 
00355 
00356 
00357 
00358 
00359 # filled_parent is a _FilledSemanticFrame
00360 # rf_child is a _RelatedFrame
00361 # returns a _FilledSemanticFrame or False
00362 def fill_semantic_frame_from_parent(filled_parent, rf_child):
00363     # Check that child frame exists
00364     child = get_semantic_frame(rf_child.name)
00365     if not child: return False
00366     filled_child = _FilledSemanticFrame(child)
00367     # Fill in frame elements of filled_child
00368     for reln in rf_child.frame_element_relations:
00369         # The parent is either an argument from the parent frame or a new argument.
00370         parent_value = (reln.parent
00371                         if reln.parent_is_value
00372                         else filled_parent.get_frame_element(reln.parent).argument)
00373         # Check that requirements are met if the child is a value
00374         if reln.child_is_value:
00375             if not (parent_value in reln.child):
00376                 rospy.loginfo("Could not fill frame " + str(child) +
00377                               " from frame " + str(filled_parent) +
00378                               ": Parent " + ("" if reln.parent_is_value else reln.parent + " with ") +
00379                               "value " + parent_value +
00380                               " is not in list of required values " + str(reln.child) +
00381                               ".  (This is normal.)")
00382                 return False
00383         # Otherwise, try to fill in the child with its name
00384         else: # not reln.child_is_value (ie, child is a blank)
00385             # Check coreness if there's no value
00386             fe = filled_child.get_frame_element(reln.child)
00387             if (not parent_value) and fe and fe.is_core:
00388                 rospy.loginfo("Could not fill frame " + str(child) +
00389                               " from frame " + str(filled_parent) +
00390                               ": Cannot fill core element '" + reln.child +
00391                               "'.  (This is normal.)")
00392                 return False
00393             # Otherwise, try filling it in.
00394             success = filled_child.fill_frame_element(reln.child, parent_value)
00395             if not success:
00396                 print reln.child
00397                 rospy.logwarn("Aborting filling frame " + str(child) +
00398                               " from frame " + str(filled_parent) +
00399                               ": Cannot fill element '" + reln.child +
00400                               "' from element '" + reln.parent +
00401                               "'.  (This should not happen.)")
00402                 return False
00403     rospy.loginfo("Filled frame " + str(child) +
00404                   " from frame " + str(filled_parent) + ".")
00405     return filled_child
00406 
00407 
00408 
00409 
00410 ###############################################################################
00411 ### ROS Callbacks
00412 
00413 def cb_AddFrame(req):
00414     response = AddFrameResponse()
00415     response.success = load_semantic_frame_yaml(req.absolute_file_path)
00416     return response
00417 
00418 
00419 def cb_FilledLexicalUnits(pub, msg):
00420     def frame_list_has_callbacks(frame_list):
00421         if len(frame_list) == 0: return True
00422         return (len(frame_list[0].action_server_topics) > 0 and
00423                 frame_list_has_callbacks(frame_list[1:]))
00424     def frame_sequences_str(frame_sequences):
00425         return '\n'.join(map(lambda sequence:
00426                                  ('[' +
00427                                   ', '.join(map(lambda frame: frame.name,
00428                                                 sequence.frames)) +
00429                                   ']'),
00430                       frame_sequences))
00431     filled_lexical_units = msg.lexical_units
00432     rospy.loginfo("frame_registrar: Received " +
00433                   str(len(filled_lexical_units)) +
00434                   " filled lexical units:\n" +
00435                   '\n'.join(map(lambda flu:
00436                                     flu.verb + "\t->\t" + flu.frame_name,
00437                                 filled_lexical_units)) +
00438                   '\n')
00439     frame_sequences = FilledSemanticFrameListList() # A list of sequences of FilledSemanticFrame
00440     frame_sequences.frame_lists = []
00441     for flu in filled_lexical_units:
00442         flu_frame_sequences = \
00443             fill_semantic_frame_sequences_from_filled_lexical_unit(flu)
00444         # Remove sequences that don't have callbacks.
00445         for flu_frame_sequence in flu_frame_sequences:
00446             frame_sequence = FilledSemanticFrameList()
00447             frame_sequence.frames = map(lambda frame:
00448                                             frame.to_ros_FilledSemanticFrame(),
00449                                         flu_frame_sequence)
00450             frame_sequences.frame_lists.append(frame_sequence)
00451     #todo remove duplicates
00452     # Filter sequences which don't have callbacks.
00453     rospy.loginfo("frame_registrar: Converted filled lexical units to " +
00454                   str(len(frame_sequences.frame_lists)) +
00455                   " sequence(s) of semantic frames:\n" +
00456                   frame_sequences_str(frame_sequences.frame_lists) + '\n')
00457     frame_sequences.frame_lists = \
00458         filter(lambda sequence: frame_list_has_callbacks(sequence.frames),
00459                frame_sequences.frame_lists)
00460     rospy.loginfo("frame_registrar: There are " +
00461                   str(len(frame_sequences.frame_lists)) +
00462                   " sequence(s) which have callbacks:\n" +
00463                   frame_sequences_str(frame_sequences.frame_lists) + '\n')
00464     # Publish if any sequences remain
00465     if frame_sequences.frame_lists:
00466         pub.publish(frame_sequences)
00467     else:
00468         rospy.logwarn("frame_registrar: No publishable frame sequences!  " +
00469                       "(Aborting publishing.)")
00470 
00471 
00472 def cb_GetEmptyFrame(req):
00473     resp = GetEmptyFrameResponse()
00474     frame = req.frame_name
00475     resp.frame = get_semantic_frame(req.frame_name).to_ros_EmptySemanticFrame()
00476     return resp
00477 
00478 
00479 def cb_RegisterWithFrame(req):
00480     response = RegisterWithFrameResponse()
00481     frame = get_semantic_frame(req.frame_name)
00482     if frame:
00483         frame.callbacks.append(req.callback_topic)
00484         rospy.loginfo("cb_RegisterWithFrame: Registered callback with topic '" +
00485                       req.callback_topic + "' to frame " +
00486                       _SemanticFrame.to_str(req.frame_name))
00487         response.success = True
00488     else:
00489         rospy.logwarn("cb_RegisterWithFrame: Frame " + _SemanticFrame.to_str(req.frame_name) +
00490                       " is not yet available.  (Callback has topic '" + req.callback_topic +
00491                       "'.)  Ignoring request.")
00492         response.success = False
00493     return response
00494 
00495 
00496 
00497 
00498 ###############################################################################
00499 ### Main
00500 
00501 def main():
00502     rospy.init_node('frame_registrar')
00503     # Load semantic frames and lexical units
00504     # (Make sure to use os.path.abspath when requesting through a message that
00505     #   a semantic frame or lexical unit be loaded.)
00506     frame_relpath = '../frames' # relative to this binary
00507     frame_filenames = filter(lambda fn: fn[-5:] == '.yaml', os.listdir(frame_relpath))
00508     rospy.loginfo("frame_registrar: Loading semantic frames...")
00509     for frame_filename in frame_filenames:
00510         load_semantic_frame_yaml(os.path.join(frame_relpath, frame_filename))
00511     rospy.loginfo("frame_registrar: Done loading semantic frames.")
00512     # Register subscriptions and services
00513     pub = rospy.Publisher('filled_semantic_frames', FilledSemanticFrameListList)
00514     rospy.Subscriber('filled_lexical_units', FilledLexicalUnitList,  lambda msg: cb_FilledLexicalUnits(pub, msg))
00515     rospy.Service('add_frame',           AddFrame,          cb_AddFrame)
00516     rospy.Service('get_empty_frame',     GetEmptyFrame,     cb_GetEmptyFrame)
00517     rospy.Service('register_with_frame', RegisterWithFrame, cb_RegisterWithFrame)
00518     rospy.loginfo("frame_registrar: Services up.")
00519     rospy.spin()
00520 
00521 if __name__ == '__main__':
00522     main()
00523 
00524 
00525 


frame_registrar
Author(s): Brian Thomas
autogenerated on Fri Dec 6 2013 20:39:56