00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
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
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
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 {}
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
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
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
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
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))