app_manager.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_manager.py 14948 2011-09-07 19:25:54Z pratkanis $
00034 
00035 # author: leibs
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 .msg import App, AppList, StatusCodes, AppStatus, AppInstallationState, ExchangeApp
00050 from .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         # note: we publish into the application namespace
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             #return StartAppResponse(started=False, message="Please stop the running app before starting another app.", error_code=StatusCodes.MULTIAPP_NOT_SUPPORTED)
00190 
00191         # TODO: the app list has already loaded the App data.  We should use that instead for consistency
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             #TODO:XXX This is a roslaunch-caller-like abomination.  Should leverage a true roslaunch API when it exists.
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             #TODO: convert to method
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                 # attempt to kill any launched resources
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             # request to stop all apps.
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 


app_manager
Author(s): Jeremy Leibs, Ken Conley
autogenerated on Sat Jun 8 2019 20:23:45