Package node_manager_fkie
[frames] | no frames]

Source Code for Package node_manager_fkie

  1  #!/usr/bin/env python 
  2  # Software License Agreement (BSD License) 
  3  # 
  4  # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko 
  5  # All rights reserved. 
  6  # 
  7  # Redistribution and use in source and binary forms, with or without 
  8  # modification, are permitted provided that the following conditions 
  9  # are met: 
 10  # 
 11  #  * Redistributions of source code must retain the above copyright 
 12  #    notice, this list of conditions and the following disclaimer. 
 13  #  * Redistributions in binary form must reproduce the above 
 14  #    copyright notice, this list of conditions and the following 
 15  #    disclaimer in the documentation and/or other materials provided 
 16  #    with the distribution. 
 17  #  * Neither the name of Fraunhofer nor the names of its 
 18  #    contributors may be used to endorse or promote products derived 
 19  #    from this software without specific prior written permission. 
 20  # 
 21  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 22  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 23  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 24  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 25  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 26  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 27  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 28  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 29  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 30  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 31  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 32  # POSSIBILITY OF SUCH DAMAGE. 
 33   
 34  __author__ = "Alexander Tiderko (Alexander.Tiderko@fkie.fraunhofer.de)" 
 35  __copyright__ = "Copyright (c) 2012 Alexander Tiderko, Fraunhofer FKIE/US" 
 36  __license__ = "BSD" 
 37  __version__ = "0.2" 
 38  __date__ = "2012-02-01" 
 39   
 40  import os 
 41  import sys 
 42  import signal 
 43  import socket 
 44  import threading 
 45   
 46  import roslib; roslib.load_manifest('node_manager_fkie') 
 47  import rospy 
 48   
 49  #PYTHONVER = (2, 7, 1) 
 50  #if sys.version_info < PYTHONVER: 
 51  #  print 'For full scope of operation this application requires python version > %s, current: %s' % (str(PYTHONVER), sys.version_info) 
 52   
 53  from ssh_handler import SSHhandler, AuthenticationRequest 
 54  from screen_handler import ScreenHandler, ScreenSelectionRequest 
 55  from start_handler import StartHandler, StartException 
 56  from progress_queue import InteractionNeededError 
 57  from name_resolution import NameResolution 
 58  from history import History 
 59   
 60  # set the cwd to the package of the node_manager_fkie to support the images 
 61  # in HTML descriptions of the robots and capabilities 
 62   
 63  PACKAGE_DIR = ''.join([roslib.packages.get_dir_pkg(os.path.abspath(os.path.dirname(sys.argv[0])))[0], os.path.sep]) 
 64  ROBOTS_DIR = ''.join([PACKAGE_DIR, os.path.sep, 'images', os.path.sep]) 
 65   
 66  CFG_PATH = ''.join(['.node_manager', os.sep]) 
 67  '''@ivar: configuration path to store the history.''' 
 68   
 69  LESS = "/usr/bin/less -fKLnQrSU" 
 70  STARTER_SCRIPT = 'rosrun node_manager_fkie remote_nm.py' 
 71  RESPAWN_SCRIPT = 'rosrun node_manager_fkie respawn' 
 72  ''' 
 73  the script used on remote hosts to start new ROS nodes 
 74  ''' 
 75   
 76  HOSTS_CACHE = dict() 
 77  '''  
 78  the cache directory to store the results of tests for local hosts. 
 79  @see: L{is_local()} 
 80  ''' 
 81   
 82  HELP_FILE = ''.join([PACKAGE_DIR, os.path.sep, 'README.rst']) 
 83   
 84  _lock = threading.RLock() 
 85   
86 -def terminal_cmd(cmd, title):
87 ''' 88 Creates a command string to run with a terminal prefix 89 @param cmd: the list with a command and args 90 @type cmd: [str,..] 91 @param title: the title of the terminal 92 @type title: str 93 @return: command with a terminal prefix 94 @rtype: str 95 ''' 96 if os.path.isfile('/usr/bin/xterm'): 97 return str(' '.join(['/usr/bin/xterm', '-geometry 112x35', '-title', str(title), '-e', ' '.join(cmd)])) 98 elif os.path.isfile('/usr/bin/konsole'): 99 return str(' '.join(['/usr/bin/konsole', '--noclose', '-title', str(title), '-e', ' '.join(cmd)]))
100 101 main_form = None 102 _ssh_handler = None 103 _screen_handler = None 104 _start_handler = None 105 _name_resolution = None 106 _history = None 107 app = None 108
109 -def ssh():
110 ''' 111 @return: The SSH handler to handle the SSH connections 112 @rtype: L{SSHhandler} 113 ''' 114 global _ssh_handler 115 return _ssh_handler
116
117 -def screen():
118 ''' 119 @return: The screen handler to the screens. 120 @rtype: L{ScreenHandler} 121 @see: U{http://linuxwiki.de/screen} 122 ''' 123 global _screen_handler 124 return _screen_handler
125
126 -def starter():
127 ''' 128 @return: The start handler to handle the start of new ROS nodes on local or 129 remote machines. 130 @rtype: L{StartHandler} 131 ''' 132 global _start_handler 133 return _start_handler
134
135 -def nameres():
136 ''' 137 @return: The name resolution object translate the the name to the host or 138 ROS master URI. 139 @rtype: L{NameResolution} 140 ''' 141 global _name_resolution 142 return _name_resolution
143
144 -def history():
145 ''' 146 @return: The history of entered parameter. 147 @rtype: L{History} 148 ''' 149 global _history 150 return _history
151
152 -def is_local(hostname):
153 ''' 154 Test whether the given host name is the name of the local host or not. 155 @param hostname: the name or IP of the host 156 @type hostname: C{str} 157 @return: C{True} if the hostname is local or None 158 @rtype: C{bool} 159 @raise Exception: on errors while resolving host 160 ''' 161 if (hostname is None): 162 return True 163 with _lock: 164 if hostname in HOSTS_CACHE: 165 if isinstance(HOSTS_CACHE[hostname], threading.Thread): 166 return False 167 return HOSTS_CACHE[hostname] 168 169 try: 170 machine_addr = socket.inet_aton(hostname) 171 local_addresses = ['localhost'] + roslib.network.get_local_addresses() 172 # check 127/8 and local addresses 173 result = machine_addr.startswith('127.') or machine_addr in local_addresses 174 with _lock: 175 HOSTS_CACHE[hostname] = result 176 return result 177 except socket.error: 178 # the hostname must be resolved => do it in a thread 179 thread = threading.Thread(target=__is_local, args=((hostname,))) 180 thread.daemon = True 181 thread.start() 182 with _lock: 183 HOSTS_CACHE[hostname] = thread 184 return False
185
186 -def __is_local(hostname):
187 import roslib 188 try: 189 machine_addr = socket.gethostbyname(hostname) 190 except socket.gaierror: 191 import traceback 192 print traceback.format_exc() 193 with _lock: 194 HOSTS_CACHE[hostname] = False 195 return 196 local_addresses = ['localhost'] + roslib.network.get_local_addresses() 197 # check 127/8 and local addresses 198 result = machine_addr.startswith('127.') or machine_addr in local_addresses 199 with _lock: 200 HOSTS_CACHE[hostname] = result
201 202
203 -def get_ros_home():
204 ''' 205 Returns the ROS HOME depending on ROS distribution API. 206 @return: ROS HOME path 207 @rtype: C{str} 208 ''' 209 try: 210 import rospkg.distro 211 distro = rospkg.distro.current_distro_codename() 212 if distro in ['electric', 'diamondback', 'cturtle']: 213 import roslib.rosenv 214 return roslib.rosenv.get_ros_home() 215 else: 216 import rospkg 217 return rospkg.get_ros_home() 218 except: 219 # import traceback 220 # print traceback.format_exc() 221 import roslib.rosenv 222 return roslib.rosenv.get_ros_home()
223 224
225 -def masteruri_from_ros():
226 ''' 227 Returns the master URI depending on ROS distribution API. 228 @return: ROS master URI 229 @rtype: C{str} 230 ''' 231 try: 232 import rospkg.distro 233 distro = rospkg.distro.current_distro_codename() 234 if distro in ['electric', 'diamondback', 'cturtle']: 235 return roslib.rosenv.get_master_uri() 236 else: 237 import rosgraph 238 return rosgraph.rosenv.get_master_uri() 239 except: 240 return os.environ['ROS_MASTER_URI']
241
242 -def finish(*arg):
243 ''' 244 Callback called on exit of the ros node. 245 ''' 246 # close all ssh sessions 247 global _ssh_handler 248 if not _ssh_handler is None: 249 _ssh_handler.close() 250 global main_form 251 import main_window 252 if isinstance(main_form, main_window.MainWindow): 253 global _history 254 _history.storeAll() 255 main_form.finish() 256 global app 257 if not app is None: 258 app.exit()
259 260
261 -def setTerminalName(name):
262 ''' 263 Change the terminal name. 264 @param name: New name of the terminal 265 @type name: C{str} 266 ''' 267 sys.stdout.write("".join(["\x1b]2;",name,"\x07"]))
268 269
270 -def setProcessName(name):
271 ''' 272 Change the process name. 273 @param name: New process name 274 @type name: C{str} 275 ''' 276 try: 277 from ctypes import cdll, byref, create_string_buffer 278 libc = cdll.LoadLibrary('libc.so.6') 279 buff = create_string_buffer(len(name)+1) 280 buff.value = name 281 libc.prctl(15, byref(buff), 0, 0, 0) 282 except: 283 pass
284 285 286 287 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 288 #%%%%%%%%%%%%% MAIN %%%%%%%% 289 #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 290
291 -def main(name, anonymous=False):
292 global CFG_PATH 293 masteruri = masteruri_from_ros() 294 CFG_PATH = ''.join([get_ros_home(), os.sep, 'node_manager', os.sep]) 295 ''' 296 Creates and runs the ROS node. 297 ''' 298 if not os.path.isdir(CFG_PATH): 299 os.makedirs(CFG_PATH) 300 301 args = rospy.myargv(argv=sys.argv) 302 # decide to show main or echo dialog 303 if len(args) >= 4 and args[1] == '-t': 304 name = ''.join([name, '_echo']) 305 anonymous = True 306 307 try: 308 from PySide.QtGui import QApplication 309 except: 310 print >> sys.stderr, "please install 'python-pyside' package!!" 311 sys.exit(-1) 312 # start ROS-Master, if not currently running 313 StartHandler._prepareROSMaster(masteruri) 314 rospy.init_node(name, anonymous=anonymous, log_level=rospy.DEBUG) 315 setTerminalName(rospy.get_name()) 316 setProcessName(rospy.get_name()) 317 318 # Initialize Qt 319 global app 320 app = QApplication(sys.argv) 321 322 # decide to show main or echo dialog 323 import main_window, echo_dialog 324 global main_form 325 if len(args) >= 4 and args[1] == '-t': 326 show_hz_only = (len(args) > 4 and args[4] == '--hz') 327 main_form = echo_dialog.EchoDialog(args[2], args[3], show_hz_only) 328 else: 329 # initialize the global handler 330 global _ssh_handler 331 global _screen_handler 332 global _start_handler 333 global _name_resolution 334 global _history 335 _ssh_handler = SSHhandler() 336 _screen_handler = ScreenHandler() 337 _start_handler = StartHandler() 338 _name_resolution = NameResolution() 339 _history = History() 340 341 # test where the roscore is running (local or remote) 342 __is_local('localhost') ## fill cache 343 __is_local(_name_resolution.getHostname(masteruri)) ## fill cache 344 local_master = is_local(_name_resolution.getHostname(masteruri)) 345 346 #start the gui 347 main_form = main_window.MainWindow(args, not local_master) 348 349 if not rospy.is_shutdown(): 350 os.chdir(PACKAGE_DIR) # change path to be able to the images of descriptions 351 main_form.show() 352 exit_code = -1 353 rospy.on_shutdown(finish) 354 exit_code = app.exec_()
355 # finally: 356 # print "final" 357 # sys.exit(exit_code) 358 # print "ex" 359