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  import argparse 
 35  import os 
 36  import roslib.network 
 37  import rospy 
 38  import socket 
 39  import sys 
 40  import threading 
 41   
 42  from master_discovery_fkie.common import get_hostname 
 43  from node_manager_fkie.common import get_ros_home, masteruri_from_ros 
 44  from node_manager_fkie.file_watcher import FileWatcher 
 45  from node_manager_fkie.history import History 
 46  from node_manager_fkie.master_view_proxy import LaunchArgsSelectionRequest 
 47  from node_manager_fkie.name_resolution import NameResolution 
 48  from node_manager_fkie.progress_queue import InteractionNeededError 
 49  from node_manager_fkie.screen_handler import ScreenHandler, ScreenSelectionRequest 
 50  from node_manager_fkie.settings import Settings 
 51  from node_manager_fkie.ssh_handler import SSHhandler, AuthenticationRequest 
 52  from node_manager_fkie.start_handler import StartException, AdvRunCfg 
 53  from node_manager_fkie.start_handler import StartHandler, BinarySelectionRequest 
 54   
 55   
 56  PKG_NAME = 'node_manager_fkie' 
 57   
 58  __author__ = "Alexander Tiderko (Alexander.Tiderko@fkie.fraunhofer.de)" 
 59  __copyright__ = "Copyright (c) 2012 Alexander Tiderko, Fraunhofer FKIE/US" 
 60  __license__ = "BSD" 
 61  __version__ = "0.7.2"  # git describe --tags --dirty --always 
 62  __date__ = "2017-01-27"  # git log -1 --date=iso 
 63   
 64  # PYTHONVER = (2, 7, 1) 
 65  # if sys.version_info < PYTHONVER: 
 66  #  print 'For full scope of operation this application requires python version > %s, current: %s' % (str(PYTHONVER), sys.version_info) 
 67   
 68   
 69  HOSTS_CACHE = dict() 
 70  ''' 
 71  the cache directory to store the results of tests for local hosts. 
 72  @see: L{is_local()} 
 73  ''' 
 74   
 75  _LOCK = threading.RLock() 
 76   
 77  _MAIN_FORM = None 
 78  _SETTINGS = None 
 79  _SSH_HANDLER = None 
 80  _SCREEN_HANDLER = None 
 81  _START_HANDLER = None 
 82  _NAME_RESOLUTION = None 
 83  _HISTORY = None 
 84  _FILE_WATCHER = None 
 85  _FILE_WATCHER_PARAM = None 
 86  _QAPP = None 
 87   
 88   
89 -def settings():
90 ''' 91 @return: The global settings 92 @rtype: L{Settings} 93 ''' 94 return _SETTINGS
95 96
97 -def ssh():
98 ''' 99 @return: The SSH handler to handle the SSH connections 100 @rtype: L{SSHhandler} 101 ''' 102 return _SSH_HANDLER
103 104
105 -def screen():
106 ''' 107 @return: The screen handler to the screens. 108 @rtype: L{ScreenHandler} 109 @see: U{http://linuxwiki.de/screen} 110 ''' 111 return _SCREEN_HANDLER
112 113
114 -def starter():
115 ''' 116 @return: The start handler to handle the start of new ROS nodes on local or 117 remote machines. 118 @rtype: L{StartHandler} 119 ''' 120 return _START_HANDLER
121 122
123 -def nameres():
124 ''' 125 @return: The name resolution object translate the the name to the host or 126 ROS master URI. 127 @rtype: L{NameResolution} 128 ''' 129 return _NAME_RESOLUTION
130 131
132 -def history():
133 ''' 134 @return: The history of entered parameter. 135 @rtype: L{History} 136 ''' 137 return _HISTORY
138 139
140 -def filewatcher():
141 ''' 142 @return: The file watcher object with all loaded configuration files. 143 @rtype: L{FileWatcher} 144 ''' 145 return _FILE_WATCHER
146 147
148 -def file_watcher_param():
149 ''' 150 @return: The file watcher object with all configuration files referenced by 151 parameter value. 152 @rtype: L{FileWatcher} 153 ''' 154 return _FILE_WATCHER_PARAM
155 156
157 -def get_ros_hostname(url):
158 ''' 159 Returns the host name used in a url, if it is a name. If it is an IP an 160 empty string will be returned. 161 162 @return: host or '' if url is an IP or invalid 163 164 @rtype: C{str} 165 ''' 166 return NameResolution.get_ros_hostname(url)
167 168
169 -def is_local(hostname, wait=False):
170 ''' 171 Test whether the given host name is the name of the local host or not. 172 @param hostname: the name or IP of the host 173 @type hostname: C{str} 174 @return: C{True} if the hostname is local or None 175 @rtype: C{bool} 176 @raise Exception: on errors while resolving host 177 ''' 178 if hostname is None: 179 return True 180 with _LOCK: 181 if hostname in HOSTS_CACHE: 182 if isinstance(HOSTS_CACHE[hostname], threading.Thread): 183 return False 184 return HOSTS_CACHE[hostname] 185 try: 186 socket.inet_aton(hostname) 187 local_addresses = ['localhost'] + roslib.network.get_local_addresses() 188 # check 127/8 and local addresses 189 result = hostname.startswith('127.') or hostname in local_addresses 190 with _LOCK: 191 HOSTS_CACHE[hostname] = result 192 return result 193 except socket.error: 194 # the hostname must be resolved => do it in a thread 195 if wait: 196 result = __is_local(hostname) 197 return result 198 else: 199 thread = threading.Thread(target=__is_local, args=((hostname,))) 200 thread.daemon = True 201 with _LOCK: 202 HOSTS_CACHE[hostname] = thread 203 thread.start() 204 return False
205 206
207 -def __is_local(hostname):
208 ''' 209 Test the hostname whether it is local or not. Uses socket.gethostbyname(). 210 ''' 211 try: 212 machine_addr = socket.gethostbyname(hostname) 213 except socket.gaierror: 214 with _LOCK: 215 HOSTS_CACHE[hostname] = False 216 return False 217 local_addresses = ['localhost'] + roslib.network.get_local_addresses() 218 # check 127/8 and local addresses 219 result = machine_addr.startswith('127.') or machine_addr in local_addresses 220 with _LOCK: 221 HOSTS_CACHE[hostname] = result 222 return result
223 224
225 -def finish(*arg):
226 ''' 227 Callback called on exit of the ros node. 228 ''' 229 # close all ssh sessions 230 if _SSH_HANDLER is not None: 231 _SSH_HANDLER.close() 232 # save the launch history 233 if _HISTORY is not None: 234 try: 235 _HISTORY.storeAll() 236 except Exception as err: 237 print >> sys.stderr, "Error while store history: %s" % err 238 from node_manager_fkie.main_window import MainWindow 239 # stop all threads in the main window 240 if isinstance(_MAIN_FORM, MainWindow): 241 _MAIN_FORM.finish() 242 if _QAPP is not None: 243 _QAPP.exit()
244 245
246 -def set_terminal_name(name):
247 ''' 248 Change the terminal name. 249 @param name: New name of the terminal 250 @type name: C{str} 251 ''' 252 sys.stdout.write("\x1b]2;%s\x07" % name)
253 254
255 -def set_process_name(name):
256 ''' 257 Change the process name. 258 @param name: New process name 259 @type name: C{str} 260 ''' 261 try: 262 from ctypes import cdll, byref, create_string_buffer 263 libc = cdll.LoadLibrary('libc.so.6') 264 buff = create_string_buffer(len(name) + 1) 265 buff.value = name 266 libc.prctl(15, byref(buff), 0, 0, 0) 267 except: 268 pass
269 270
271 -def init_settings():
272 global _SETTINGS 273 _SETTINGS = Settings()
274 275
276 -def init_globals(masteruri):
277 ''' 278 :return: True if the masteruri referred to localhost 279 :rtype: bool 280 ''' 281 # initialize the global handler 282 global _SSH_HANDLER 283 global _SCREEN_HANDLER 284 global _START_HANDLER 285 global _NAME_RESOLUTION 286 global _HISTORY 287 global _FILE_WATCHER 288 global _FILE_WATCHER_PARAM 289 _SSH_HANDLER = SSHhandler() 290 _SCREEN_HANDLER = ScreenHandler() 291 _START_HANDLER = StartHandler() 292 _NAME_RESOLUTION = NameResolution() 293 _HISTORY = History() 294 _FILE_WATCHER = FileWatcher() 295 _FILE_WATCHER_PARAM = FileWatcher() 296 297 # test where the roscore is running (local or remote) 298 __is_local('localhost') # fill cache 299 return __is_local(get_hostname(masteruri)) # fill cache
300 301
302 -def init_arg_parser():
303 parser = argparse.ArgumentParser() 304 parser.add_argument("--version", action="version", version="%s %s" % ("%(prog)s", __version__)) 305 parser.add_argument("-f", "--file", nargs=1, help="loads the given file as default on start") 306 parser.add_argument("-m", "--muri", nargs=1, default='', help="starts ROS master with given URI, usefull on hosts " 307 "with multiple interfaces. ROS_HOSTNAME will be set " 308 "to the host of this URI, but only if it is not an IP.") 309 310 group = parser.add_argument_group('echo') 311 group.add_argument("--echo", nargs=2, help="starts an echo dialog instead of node manager", metavar=('name', 'type')) 312 group.add_argument("--hz", action="store_true", help="shows only the Hz value instead of topic content in echo dialog") 313 group.add_argument("--ssh", action="store_true", help="connects via SSH") 314 315 return parser
316 317
318 -def init_echo_dialog(prog_name, masteruri, topic_name, topic_type, hz=False, use_ssh=False):
319 ''' 320 Intialize the environment to start an echo window. 321 ''' 322 # start ROS-Master, if not currently running 323 # StartHandler._prepareROSMaster(masteruri) 324 name = '%s_echo' % prog_name 325 rospy.init_node(name, anonymous=True, log_level=rospy.INFO) 326 set_terminal_name(name) 327 set_process_name(name) 328 from node_manager_fkie.echo_dialog import EchoDialog 329 global _SSH_HANDLER 330 _SSH_HANDLER = SSHhandler() 331 return EchoDialog(topic_name, topic_type, hz, masteruri, use_ssh=use_ssh)
332 333
334 -def init_main_window(prog_name, masteruri, launch_files=[]):
335 ''' 336 Intialize the environment to start Node Manager. 337 ''' 338 # start ROS-Master, if not currently running 339 StartHandler._prepareROSMaster(masteruri) 340 # setup the loglevel 341 try: 342 log_level = getattr(rospy, rospy.get_param('/%s/log_level' % prog_name, "INFO")) 343 except Exception as err: 344 print "Error while set the log level: %s\n->INFO level will be used!" % err 345 log_level = rospy.INFO 346 rospy.init_node(prog_name, anonymous=False, log_level=log_level) 347 set_terminal_name(prog_name) 348 set_process_name(prog_name) 349 from node_manager_fkie.main_window import MainWindow 350 local_master = init_globals(masteruri) 351 return MainWindow(launch_files, not local_master, launch_files)
352 353 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 354 # %%%%%%%%%%%%% MAIN %%%%%%%% 355 # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 356 357
358 -def main(name):
359 ''' 360 Start the NodeManager or EchoDialog. 361 :param name: the name propagated to the rospy.init_node() 362 :type name: str 363 ''' 364 try: 365 from python_qt_binding.QtGui import QApplication 366 except: 367 try: 368 from python_qt_binding.QtWidgets import QApplication 369 except: 370 print >> sys.stderr, "please install 'python_qt_binding' package!!" 371 sys.exit(-1) 372 373 init_settings() 374 parser = init_arg_parser() 375 args = rospy.myargv(argv=sys.argv) 376 parsed_args = parser.parse_args(args[1:]) 377 if parsed_args.muri: 378 masteruri = parsed_args.muri[0] 379 hostname = NameResolution.get_ros_hostname(masteruri) 380 os.environ['ROS_MASTER_URI'] = masteruri 381 if hostname: 382 os.environ['ROS_HOSTNAME'] = hostname 383 masteruri = settings().masteruri() 384 # Initialize Qt 385 global _QAPP 386 _QAPP = QApplication(sys.argv) 387 388 # decide to show main or echo dialog 389 global _MAIN_FORM 390 try: 391 if parsed_args.echo: 392 _MAIN_FORM = init_echo_dialog(name, masteruri, parsed_args.echo[0], 393 parsed_args.echo[1], parsed_args.hz, 394 parsed_args.ssh) 395 else: 396 _MAIN_FORM = init_main_window(name, masteruri, parsed_args.file) 397 except Exception as err: 398 sys.exit("%s" % err) 399 400 exit_code = 0 401 # resize and show the qt window 402 if not rospy.is_shutdown(): 403 # change path for access to the images of descriptions 404 os.chdir(settings().PACKAGE_DIR) 405 # _MAIN_FORM.resize(1024, 720) 406 screen_size = QApplication.desktop().availableGeometry() 407 if (_MAIN_FORM.size().width() >= screen_size.width() or 408 _MAIN_FORM.size().height() >= screen_size.height() - 24): 409 _MAIN_FORM.showMaximized() 410 else: 411 _MAIN_FORM.show() 412 exit_code = -1 413 rospy.on_shutdown(finish) 414 exit_code = _QAPP.exec_() 415 return exit_code
416