app.py
Go to the documentation of this file.
00001 # Software License Agreement (BSD License)
00002 #
00003 # Copyright (c) 2011, Willow Garage, Inc.
00004 # All rights reserved.
00005 #
00006 # Redistribution and use in source and binary forms, with or without
00007 # modification, are permitted provided that the following conditions
00008 # are met:
00009 #
00010 #  * Redistributions of source code must retain the above copyright
00011 #    notice, this list of conditions and the following disclaimer.
00012 #  * Redistributions in binary form must reproduce the above
00013 #    copyright notice, this list of conditions and the following
00014 #    disclaimer in the documentation and/or other materials provided
00015 #    with the distribution.
00016 #  * Neither the name of Willow Garage, Inc. nor the names of its
00017 #    contributors may be used to endorse or promote products derived
00018 #    from this software without specific prior written permission.
00019 #
00020 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00021 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00022 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00023 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00024 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00025 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00026 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00027 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00028 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00030 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00031 # POSSIBILITY OF SUCH DAMAGE.
00032 #
00033 # Revision $Id: app.py 14667 2011-08-12 23:55:04Z pratkanis $
00034 
00035 # author: leibs, kwc
00036 
00037 import os
00038 import errno
00039 import yaml
00040 
00041 import roslib.names
00042 from rospkg import ResourceNotFound
00043 from .exceptions import AppException, InvalidAppException, NotFoundException, InternalAppException
00044 
00045 class Interface(object):
00046     def __init__(self, subscribed_topics, published_topics):
00047         self.subscribed_topics = subscribed_topics
00048         self.published_topics = published_topics
00049 
00050     def __eq__(self, other):
00051         if not isinstance(other, Interface):
00052             return False
00053         return self.subscribed_topics == other.subscribed_topics and \
00054                self.published_topics == other.published_topics
00055                
00056 class Client(object):
00057     __slots__ = ['client_type', 'manager_data', 'app_data']
00058     def __init__(self, client_type, manager_data, app_data):
00059         self.client_type = client_type
00060         self.manager_data = manager_data
00061         self.app_data = app_data
00062         
00063     def as_dict(self):
00064         return {'client_type': self.client_type, 'manager_data': self.manager_data, 'app_data': self.app_data}
00065     
00066     def __eq__(self, other):
00067         if not isinstance(other, Client):
00068             return False
00069         return self.client_type == other.client_type and \
00070                self.manager_data == other.manager_data and \
00071                self.app_data == other.app_data
00072 
00073     def __repr__(self):
00074         return yaml.dump(self.as_dict())
00075                
00076 class AppDefinition(object):
00077     __slots__ = ['name', 'display_name', 'description', 'platform',
00078                  'launch', 'interface', 'clients', 'icon']
00079     def __init__(self, name, display_name, description, platform,
00080                  launch, interface, clients, icon=None):
00081         self.name = name
00082         self.display_name = display_name
00083         self.description = description
00084         self.platform=platform
00085         self.launch = launch
00086         self.interface = interface
00087         self.clients = clients
00088         self.icon = icon
00089 
00090     def __repr__(self):
00091         d = {}
00092         for s in self.__slots__:
00093             if s == 'clients':
00094                 d[s] = [c.as_dict() for c in self.clients]
00095             else:
00096                 d[s] = getattr(self, s)
00097         return yaml.dump(d)
00098     #    return "name: %s\ndisplay: %s\ndescription: %s\nplatform: %s\nlaunch: %s\ninterface: %s\nclients: %s"%(self.name, self.display_name, self.description, self.platform, self.launch, self.interface, self.clients)
00099     
00100     def __eq__(self, other):
00101         if not isinstance(other, AppDefinition):
00102             return False
00103         return self.name == other.name and \
00104                self.display_name == other.display_name and \
00105                self.description == other.description and \
00106                self.platform == other.platform and \
00107                self.launch == other.launch and \
00108                self.interface == other.interface and \
00109                self.clients == other.clients and \
00110                self.icon == other.icon
00111                
00112 def find_resource(resource):
00113     """
00114     @return: filepath of resource.  Does not validate if filepath actually exists.
00115     
00116     @raise ValueError: if resource is not a valid resource name.
00117     @raise rospkg.ResourceNotFound: if package referred
00118         to in resource name cannot be found.
00119     @raise NotFoundException: if resource does not exist.
00120     """
00121     p, a = roslib.names.package_resource_name(resource)
00122     if not p:
00123         raise ValueError("Resource is missing package name: %s"%(resource))
00124     matches = roslib.packages.find_resource(p, a)
00125     # TODO: convert ValueError to better type for better error messages
00126     if len(matches) == 1:
00127         return matches[0]
00128     elif not matches:
00129         raise NotFoundException("No resource [%s]"%(resource))        
00130     else:
00131         raise ValueError("Multiple resources named [%s]"%(resource))        
00132 
00133 def load_Interface_from_file(filename):
00134     """
00135     @raise IOError: I/O error reading file (e.g. does not exist)
00136     @raise InvalidAppException: if app file is invalid
00137     """
00138     with open(filename,'r') as f:
00139         y = yaml.load(f.read())
00140         y = y or {} #coerce to dict
00141         try:
00142             subscribed_topics = y.get('subscribed_topics', {})
00143             published_topics = y.get('published_topics', {})
00144         except KeyError:
00145             raise InvalidAppException("Malformed interface, missing keys")
00146     return Interface(published_topics=published_topics, subscribed_topics=subscribed_topics)
00147 
00148 def _AppDefinition_load_icon_entry(app_data, appfile="UNKNOWN"):
00149     """
00150     @raise InvalidAppExcetion: if app definition is invalid.
00151     """
00152     # load/validate launch entry
00153     try:
00154         icon_resource = app_data.get('icon', '')
00155         if icon_resource == '':
00156             return None
00157         icon_filename = find_resource(icon_resource)
00158         if not icon_filename or not os.path.exists(icon_filename):
00159             return None
00160         return icon_filename
00161     except ValueError as e:
00162         raise InvalidAppException("Malformed appfile [%s]: bad icon entry: %s"%(appfile, e))
00163     except NotFoundException:
00164         # TODO: make this a soft fail?
00165         raise InvalidAppException("App file [%s] refers to icon that cannot be found"%(appfile))
00166     except ResourceNotFound as e:
00167         raise InvalidAppException("App file [%s] refers to package that is not installed: %s"%(appfile, str(e)))
00168 
00169 def _AppDefinition_load_launch_entry(app_data, appfile="UNKNOWN"):
00170     """
00171     @raise InvalidAppExcetion: if app definition is invalid.
00172     """
00173     # load/validate launch entry
00174     try:
00175         launch = find_resource(app_data['launch'])
00176         if not os.path.exists(launch):
00177             raise InvalidAppException("Malformed appfile [%s]: refers to launch that does not exist."%(appfile))
00178         return launch
00179     except ValueError as e:
00180         raise InvalidAppException("Malformed appfile [%s]: bad launch entry: %s"%(appfile, e))
00181     except NotFoundException:
00182         raise InvalidAppException("App file [%s] refers to launch that is not installed"%(appfile))
00183     except ResourceNotFound as e:
00184         raise InvalidAppException("App file [%s] refers to package that is not installed: %s"%(appfile, str(e)))
00185 
00186 def _AppDefinition_load_interface_entry(app_data, appfile="UNKNOWN"):
00187     """
00188     @raise InvalidAppExcetion: if app definition is invalid.
00189     """
00190     # load/validate interface entry
00191     try:
00192         return load_Interface_from_file(find_resource(app_data['interface']))
00193     except IOError as e:
00194         if e.errno == errno.ENOENT:
00195             raise InvalidAppException("Malformed appfile [%s]: refers to interface file that does not exist"%(appfile))
00196         else:
00197             raise InvalidAppException("Error with appfile [%s]: cannot read interface file"%(appfile))
00198     except ValueError:
00199         raise InvalidAppException("Malformed appfile [%s]: bad interface entry"%(appfile))
00200     except ResourceNotFound as e:
00201         raise InvalidAppException("App file [%s] refers to package that is not installed: %s"%(appfile, str(e)))
00202     
00203 def _AppDefinition_load_clients_entry(app_data, appfile="UNKNOWN"):
00204     """
00205     @raise InvalidAppExcetion: if app definition is invalid.
00206     """
00207     clients_data = app_data.get('clients', [])
00208     clients = []
00209     for c in clients_data:
00210         for reqd in ['type', 'manager']:
00211             if not reqd in c:
00212                 raise InvalidAppException("Malformed appfile [%s], missing required key [%s]"%(appfile, reqd))
00213         client_type = c['type']
00214         manager_data = c['manager']
00215         if not type(manager_data) == dict:
00216             raise InvalidAppException("Malformed appfile [%s]: manager data must be a map"%(appfile))
00217 
00218         app_data = c.get('app', {})
00219         if not type(app_data) == dict:
00220             raise InvalidAppException("Malformed appfile [%s]: app data must be a map"%(appfile))
00221 
00222         clients.append(Client(client_type, manager_data, app_data))
00223     return clients
00224 
00225 def load_AppDefinition_from_file(appfile, appname):
00226     """
00227     @raise InvalidAppExcetion: if app definition is invalid.
00228     @raise IOError: I/O error reading appfile (e.g. file does not exist).
00229     """
00230     with open(appfile,'r') as f:
00231         app_data = yaml.load(f.read())
00232         for reqd in ['launch', 'interface', 'platform']:
00233             if not reqd in app_data:
00234                 raise InvalidAppException("Malformed appfile [%s], missing required key [%s]"%(appfile, reqd))
00235 
00236         display_name = app_data.get('display', appname)
00237         description = app_data.get('description', '')        
00238         platform = app_data['platform']
00239 
00240 
00241         launch = _AppDefinition_load_launch_entry(app_data, appfile)
00242         interface = _AppDefinition_load_interface_entry(app_data, appfile)
00243         clients = _AppDefinition_load_clients_entry(app_data, appfile)
00244         icon = _AppDefinition_load_icon_entry(app_data, appfile)
00245 
00246     return AppDefinition(appname, display_name, description, platform,
00247                          launch, interface, clients, icon)
00248     
00249 def load_AppDefinition_by_name(appname):
00250     """
00251     @raise InvalidAppExcetion: if app definition is invalid.
00252     @raise NotFoundExcetion: if app definition is not installed.
00253     @raise ValueError: if appname is invalid.
00254     """
00255     if not appname:
00256         raise ValueError("app name is empty")
00257 
00258     try:
00259         appfile = find_resource(appname + '.app')
00260     except ResourceNotFound as e:
00261         raise NotFoundException("Cannot locate app file for %s: package is not installed."%(appname))
00262 
00263     try:
00264         return load_AppDefinition_from_file(appfile, appname)
00265     except IOError as e:
00266         if e.errno == errno.ENOENT:
00267             raise NotFoundException("Cannot locate app file for %s."%(appname))
00268         else:
00269             raise InternalAppException("I/O error loading AppDefinition file: %s."%(e.errno))


turtlebot_app_manager
Author(s): Jeremy Leibs, Ken Conley
autogenerated on Mon Oct 6 2014 08:00:31