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 thread
00038 import time
00039
00040 import rosgraph.names
00041 import rospy
00042
00043 import roslaunch.parent
00044 import roslaunch.pmon
00045
00046 from .app import AppDefinition, load_AppDefinition_by_name
00047 from .exceptions import LaunchException, AppException, InvalidAppException, NotFoundException
00048 from .master_sync import MasterSync
00049 from app_manager.msg import App, AppList, StatusCodes, AppStatus, AppInstallationState, ExchangeApp
00050 from app_manager.srv import StartApp, StopApp, ListApps, ListAppsResponse, StartAppResponse, StopAppResponse, InstallApp, UninstallApp, GetInstallationState, UninstallAppResponse, InstallAppResponse, GetInstallationStateResponse, GetAppDetails, GetAppDetailsResponse
00051
00052 class AppManager(object):
00053
00054 def __init__(self, robot_name, interface_master, app_list, exchange):
00055 self._robot_name = robot_name
00056 self._interface_master = interface_master
00057 self._app_list = app_list
00058 self._current_app = self._current_app_definition = None
00059 self._exchange = exchange
00060
00061 rospy.loginfo("Starting app manager for %s"%self._robot_name)
00062
00063 self._app_interface = self.scoped_name('application')
00064
00065
00066 self._status_pub = rospy.Publisher(self.scoped_name('application/app_status'), AppStatus, latch=True)
00067 self._list_apps_pub = rospy.Publisher(self.scoped_name('app_list'), AppList, latch=True)
00068
00069 self._list_apps_srv = rospy.Service(self.scoped_name('list_apps'), ListApps, self.handle_list_apps)
00070 self._start_app_srv = rospy.Service(self.scoped_name('start_app'), StartApp, self.handle_start_app)
00071 self._stop_app_srv = rospy.Service(self.scoped_name('stop_app'), StopApp, self.handle_stop_app)
00072 if (self._exchange):
00073 self._exchange_list_apps_pub = rospy.Publisher(self.scoped_name('exchange_app_list'), AppInstallationState, latch=True)
00074 self._list_exchange_apps_srv = rospy.Service(self.scoped_name('list_exchange_apps'), GetInstallationState, self.handle_list_exchange_apps)
00075 self._get_app_details_srv = rospy.Service(self.scoped_name('get_app_details'), GetAppDetails, self.handle_get_app_details)
00076 self._install_app_srv = rospy.Service(self.scoped_name('install_app'), InstallApp, self.handle_install_app)
00077 self._uninstall_app_srv = rospy.Service(self.scoped_name('uninstall_app'), UninstallApp, self.handle_uninstall_app)
00078
00079 pub_names = [x.resolved_name for x in [self._list_apps_pub, self._status_pub, self._exchange_list_apps_pub]]
00080 service_names = [x.resolved_name for x in [self._list_apps_srv, self._start_app_srv, self._stop_app_srv, self._get_app_details_srv, self._list_exchange_apps_srv, self._install_app_srv, self._uninstall_app_srv]]
00081 else:
00082 pub_names = [x.resolved_name for x in [self._list_apps_pub, self._status_pub]]
00083 service_names = [x.resolved_name for x in [self._list_apps_srv, self._start_app_srv, self._stop_app_srv]]
00084
00085 self._api_sync = MasterSync(self._interface_master,
00086 local_service_names=service_names,
00087 local_pub_names=pub_names)
00088
00089 self._launch = None
00090 self._interface_sync = None
00091
00092 roslaunch.pmon._init_signal_handlers()
00093
00094 if (self._exchange):
00095 self._exchange.update_local()
00096
00097 self._app_list.update()
00098 self.publish_exchange_list_apps()
00099 self.publish_list_apps()
00100
00101 def shutdown(self):
00102 if self._api_sync:
00103 self._api_sync.stop()
00104 if self._launch:
00105 self._launch.shutdown()
00106 self._interface_sync.stop()
00107
00108 def _get_current_app(self):
00109 return self._current_app
00110
00111 def _set_current_app(self, app, app_definition):
00112 self._current_app = app
00113 self._current_app_definition = app_definition
00114
00115 if self._list_apps_pub:
00116 if app is not None:
00117 self._list_apps_pub.publish([app], self._app_list.get_app_list())
00118 else:
00119 self._list_apps_pub.publish([], self._app_list.get_app_list())
00120
00121 def scoped_name(self, name):
00122 return rosgraph.names.canonicalize_name('/%s/%s'%(self._robot_name, rospy.remap_name(name)))
00123
00124 def handle_get_app_details(self, req):
00125 return GetAppDetailsResponse(app=self._exchange.get_app_details(req.name))
00126
00127 def handle_list_exchange_apps(self, req):
00128 if (self._exchange == None):
00129 return None
00130 if (req.remote_update):
00131 print "UPDATE"
00132 if (not self._exchange.update()):
00133 return None
00134 i_apps = self._exchange.get_installed_apps()
00135 a_apps = self._exchange.get_available_apps()
00136 return GetInstallationStateResponse(installed_apps=i_apps, available_apps=a_apps)
00137
00138 def publish_list_apps(self):
00139 if self._current_app:
00140 self._list_apps_pub.publish([self._current_app], self._app_list.get_app_list())
00141 else:
00142 self._list_apps_pub.publish([], self._app_list.get_app_list())
00143
00144 def publish_exchange_list_apps(self):
00145 if (self._exchange == None):
00146 return
00147 i_apps = self._exchange.get_installed_apps()
00148 a_apps = self._exchange.get_available_apps()
00149 self._exchange_list_apps_pub.publish(i_apps, a_apps)
00150
00151 def handle_install_app(self, req):
00152 appname = req.name
00153 if (self._exchange.install_app(appname)):
00154 self._app_list.update()
00155 self.publish_list_apps()
00156 self.publish_exchange_list_apps()
00157 return InstallAppResponse(installed=True, message="app [%s] installed"%(appname))
00158 else:
00159 return InstallAppResponse(installed=False, message="app [%s] could not be installed"%(appname))
00160
00161 def handle_uninstall_app(self, req):
00162 appname = req.name
00163 if (self._exchange.uninstall_app(appname)):
00164 self._app_list.update()
00165 self.publish_list_apps()
00166 self.publish_exchange_list_apps()
00167 return UninstallAppResponse(uninstalled=True, message="app [%s] uninstalled"%(appname))
00168 else:
00169 return UninstallAppResponse(uninstalled=False, message="app [%s] could not be uninstalled"%(appname))
00170
00171 def handle_list_apps(self, req):
00172 rospy.loginfo("Listing apps")
00173 current = self._current_app
00174 if current:
00175 running_apps = [current]
00176 else:
00177 running_apps = []
00178 self._app_list.update()
00179 rospy.loginfo("done listing apps")
00180 return ListAppsResponse(running_apps=running_apps, available_apps=self._app_list.get_app_list())
00181
00182 def handle_start_app(self, req):
00183 rospy.loginfo("start_app: %s"%(req.name))
00184 if self._current_app:
00185 if self._current_app_definition.name == req.name:
00186 return StartAppResponse(started=True, message="app [%s] already started"%(req.name), namespace=self._app_interface)
00187 else:
00188 self.stop_app(self._current_app_definition.name)
00189
00190
00191
00192
00193 appname = req.name
00194 rospy.loginfo("Loading app: %s"%(appname))
00195 try:
00196 app = load_AppDefinition_by_name(appname)
00197 except ValueError as e:
00198 return StartAppResponse(started=False, message=str(e), error_code=StatusCodes.BAD_REQUEST)
00199 except InvalidAppException as e:
00200 return StartAppResponse(started=False, message=str(e), error_code=StatusCodes.INTERNAL_ERROR)
00201 except NotFoundException as e:
00202 return StartAppResponse(started=False, message=str(e), error_code=StatusCodes.NOT_FOUND)
00203
00204 try:
00205 self._set_current_app(App(name=appname), app)
00206
00207 rospy.loginfo("Launching: %s"%(app.launch))
00208 self._status_pub.publish(AppStatus(AppStatus.INFO, 'launching %s'%(app.display_name)))
00209
00210
00211 self._launch = roslaunch.parent.ROSLaunchParent(rospy.get_param("/run_id"),
00212 [app.launch], is_core=False,
00213 process_listeners=())
00214 self._launch._load_config()
00215
00216
00217 for N in self._launch.config.nodes:
00218 for t in app.interface.published_topics.keys():
00219 N.remap_args.append((t, self._app_interface + '/' + t))
00220 for t in app.interface.subscribed_topics.keys():
00221 N.remap_args.append((t, self._app_interface + '/' + t))
00222 self._launch.start()
00223
00224 fp = [self._app_interface + '/' + x for x in app.interface.subscribed_topics.keys()]
00225 lp = [self._app_interface + '/' + x for x in app.interface.published_topics.keys()]
00226
00227 self._interface_sync = MasterSync(self._interface_master, foreign_pub_names=fp, local_pub_names=lp)
00228
00229 thread.start_new_thread(self.app_monitor,())
00230
00231 return StartAppResponse(started=True, message="app [%s] started"%(appname), namespace=self._app_interface)
00232
00233 except Exception as e:
00234 try:
00235
00236 self._stop_current()
00237 except:
00238 pass
00239 self._status_pub.publish(AppStatus(AppStatus.INFO, 'app start failed'))
00240 rospy.logerr("app start failed")
00241 return StartAppResponse(started=False, message="internal error [%s]"%(str(e)), error_code=StatusCodes.INTERNAL_ERROR)
00242
00243 def _stop_current(self):
00244 try:
00245 self._launch.shutdown()
00246 finally:
00247 self._launch = None
00248 try:
00249 self._interface_sync.stop()
00250 finally:
00251 self._interface_sync = None
00252
00253 def handle_stop_app(self, req):
00254 rospy.loginfo("handle stop app: %s"%(req.name))
00255 return self.stop_app(req.name)
00256
00257 def app_monitor(self):
00258 while self._launch:
00259 time.sleep(0.1)
00260 launch = self._launch
00261 if launch:
00262 pm = launch.pm
00263 if pm:
00264 if pm.done:
00265 time.sleep(1.0)
00266 self.stop_app(self._current_app_definition.name)
00267 break
00268
00269
00270
00271 def stop_app(self, appname):
00272 resp = StopAppResponse(stopped=False)
00273 try:
00274 app = self._current_app_definition
00275
00276
00277 if app is not None and appname == '*':
00278 appname = app.name
00279
00280 if app is None or app.name != appname:
00281 rospy.loginfo("handle stop app: app [%s] is not running [x]"%(appname))
00282 resp.error_code = StatusCodes.NOT_RUNNING
00283 resp.message = "app %s is not running"%(appname)
00284 else:
00285 try:
00286 if self._launch:
00287 rospy.loginfo("handle stop app: stopping app [%s]"%(appname))
00288 self._status_pub.publish(AppStatus(AppStatus.INFO, 'stopping %s'%(app.display_name)))
00289 self._stop_current()
00290 rospy.loginfo("handle stop app: app [%s] stopped"%(appname))
00291 resp.stopped = True
00292 resp.message = "%s stopped"%(appname)
00293 else:
00294 rospy.loginfo("handle stop app: app [%s] is not running"%(appname))
00295 resp.message = "app [%s] is not running"%(appname)
00296 resp.error_code = StatusCodes.NOT_RUNNING
00297 finally:
00298 self._launch = None
00299 self._set_current_app(None, None)
00300
00301 except Exception as e:
00302 rospy.logerr("handle stop app: internal error %s"%(e))
00303 resp.error_code = StatusCodes.INTERNAL_ERROR
00304 resp.message = "internal error: %s"%(str(e))
00305
00306 return resp