00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 import os
00011 import yaml
00012 from roslib.packages import InvalidROSPkgException
00013 import rospy
00014 import roslaunch.parent
00015 import traceback
00016 import time
00017 import thread
00018
00019 import utils
00020 from .exceptions import AppException, InvalidAppException
00021
00022
00023
00024
00025
00026
00027 class App(object):
00028 '''
00029 Got many inspiration and imported from willow_app_manager
00030 implementation (Jihoon)
00031 '''
00032 path = None
00033 data = {}
00034
00035 def __init__(self, resource_name):
00036 self.filename = ""
00037 self._connections = {}
00038 for connection_type in ['publishers', 'subscribers', 'services']:
00039 self._connections[connection_type] = []
00040 self._load_from_resource_name(resource_name)
00041
00042 def __repr__(self):
00043 string = ""
00044 for d in self.data:
00045 string += d + " : " + str(self.data[d]) + "\n"
00046 return string
00047
00048 def _load_from_resource_name(self, name):
00049 '''
00050 Loads from a ros resource name consisting of a package/app pair.
00051
00052 @param name : unique identifier for the app, e.g. rocon_apps/chirp.
00053 @type str
00054
00055 @raise InvalidAppException if the app definition was for some reason invalid.
00056 '''
00057 if not name:
00058 raise InvalidAppException("app name was invalid [%s]" % name)
00059 self.filename = utils.find_resource(name + '.app')
00060 self._load_from_app_file(self.filename, name)
00061
00062 def _load_from_app_file(self, path, app_name):
00063 '''
00064 Open and read directly from the app definition file (.app file).
00065
00066 @param path : full path to the .app file
00067 @param app_name : unique name for the app (comes from the .app filename)
00068 '''
00069 rospy.loginfo("App Manager : loading app '%s'" % app_name)
00070 self.filename = path
00071
00072 with open(path, 'r') as f:
00073 data = {}
00074 app_data = yaml.load(f.read())
00075
00076 for reqd in ['launch', 'interface', 'platform']:
00077 if not reqd in app_data:
00078 raise AppException("Invalid appfile format [" + path + "], missing required key [" + reqd + "]")
00079
00080 data['name'] = app_name
00081 data['display'] = app_data.get('display', app_name)
00082 data['description'] = app_data.get('description', '')
00083 data['platform'] = app_data['platform']
00084 data['launch'] = self.loadFromFile(app_data['launch'], 'launch', app_name)
00085 data['interface'] = self._load_interface(self.loadFromFile(app_data['interface'], 'interface', app_name))
00086 if 'icon' not in app_data:
00087 data['icon'] = None
00088 else:
00089 data['icon'] = self.loadFromFile(app_data['icon'], 'icon', app_name)
00090 data['status'] = 'Ready'
00091
00092 self.data = data
00093
00094 def loadFromFile(self, path, log, app_name="Unknown"):
00095 try:
00096 data = utils.findResource(path)
00097 if not os.path.exists(data):
00098 raise AppException("Invalid appfile [%s]: %s file does not exist." % (app_name, log))
00099 return data
00100 except ValueError as e:
00101 raise AppException("Invalid appfile [%s]: bad %s entry: %s" % (app_name, log, e))
00102 """
00103 except NotFoundException:
00104 raise AppException("App file [%s] feres to %s that is not installed"%(app_name,log))
00105 """
00106 except InvalidROSPkgException as e:
00107 raise AppException("App file [%s] feres to %s that is not installed: %s" % (app_name, log, str(e)))
00108
00109 def _load_interface(self, data):
00110 d = {}
00111 keys = ['subscribers', 'publishers', 'services']
00112 with open(data, 'r') as f:
00113 y = yaml.load(f.read())
00114 y = y or {}
00115 try:
00116 for k in keys:
00117 raw_data = y.get(k, [])
00118
00119
00120 new_data = []
00121 for r in raw_data:
00122 if r[0] == '/':
00123 r = r[1:len(r)]
00124 new_data.append(r)
00125 d[k] = new_data
00126
00127 except KeyError:
00128 raise AppException("Invalid interface, missing keys")
00129
00130 return d
00131
00132 def start(self, robot_name, remappings=[]):
00133 data = self.data
00134 rospy.loginfo("Launching: %s" % (data['name']))
00135
00136
00137 try:
00138 prefix = robot_name
00139
00140
00141 self._launch = roslaunch.parent.ROSLaunchParent(rospy.get_param("/run_id"),
00142 [data['launch']],
00143 is_core=False,
00144 process_listeners=())
00145 self._launch._load_config()
00146
00147
00148 self._connections = {}
00149
00150
00151 remap_from_list = [remapping.remap_from for remapping in remappings]
00152 remap_to_list = [remapping.remap_to for remapping in remappings]
00153 for connection_type in ['publishers', 'subscribers', 'services']:
00154 self._connections[connection_type] = []
00155 for t in data['interface'][connection_type]:
00156 remapped_name = prefix + '/' + t
00157 indices = [i for i, x in enumerate(remap_from_list) if x == t]
00158 if indices:
00159 remapped_name = remap_to_list[indices[0]]
00160 self._connections[connection_type].append(remapped_name)
00161 for N in self._launch.config.nodes:
00162 N.remap_args.append((t, remapped_name))
00163 self._launch.start()
00164
00165 thread.start_new_thread(self.app_monitor, ())
00166 data['status'] = 'Running'
00167 return True, "Success", self._connections['subscribers'], self._connections['publishers'], self._connections['services']
00168
00169 except Exception as e:
00170 print str(e)
00171 traceback.print_stack()
00172 rospy.loginfo("Error While launching " + data['launch'])
00173 data['status'] = "Error While launching " + data['launch']
00174 return False, "Error while launching " + data['name'], [], [], []
00175
00176 def stop(self):
00177 data = self.data
00178
00179 try:
00180 if self._launch:
00181 try:
00182 self._launch.shutdown()
00183 finally:
00184 self._launch = None
00185 data['status'] = 'Ready'
00186 rospy.loginfo("App Manager : stopped app [%s]" % data['name'])
00187 except Exception as e:
00188 print str(e)
00189 rospy.loginfo("Error while stopping " + data['name'])
00190 data['status'] = 'Error'
00191 return False, "Error while stopping " + data['name'], self._connections['subscribers'], self._connections['publishers'], self._connections['services']
00192
00193 return True, "Success", self._connections['subscribers'], self._connections['publishers'], self._connections['services']
00194
00195 def app_monitor(self):
00196 while self._launch:
00197 time.sleep(0.1)
00198 launch = self._launch
00199 if launch:
00200 pm = launch.pm
00201 if pm:
00202 if pm.done:
00203 time.sleep(1.0)
00204 self.stop()
00205 break