Package rocon_utilities :: Module launch'
[frames] | no frames]

Source Code for Module rocon_utilities.launch'

  1  #!/usr/bin/env python 
  2  # 
  3  # License: BSD 
  4  #   https://raw.github.com/robotics-in-concert/rocon_multimaster/master/rocon_utilities/LICENSE 
  5  # 
  6   
  7  ############################################################################## 
  8  # Imports 
  9  ############################################################################## 
 10   
 11  import os 
 12  import argparse 
 13  import subprocess 
 14  import signal 
 15  import sys 
 16  from time import sleep 
 17  import roslaunch 
 18   
 19  # Local imports 
 20  from .system import which, wait_pid 
 21  import rocon_utilities.console as console 
 22  import xml.etree.ElementTree as ElementTree 
 23  import ros_utilities 
 24   
 25  ############################################################################## 
 26  # Global variables 
 27  ############################################################################## 
 28   
 29  processes = [] 
 30  roslaunch_pids = [] 
 31   
 32  ############################################################################## 
 33  # Methods 
 34  ############################################################################## 
 35   
 36   
37 -def preexec():
38 ''' 39 Don't forward signals. 40 41 http://stackoverflow.com/questions/3791398/how-to-stop-python-from-propagating-signals-to-subprocesses 42 ''' 43 os.setpgrp() # setpgid(0,0)
44 45
46 -def get_roslaunch_pid(parent_pid):
47 ''' 48 Get the pid of the roslaunch process running in the terminal 49 specified by the parent pid. 50 ''' 51 ps_command = subprocess.Popen("ps -o pid -o comm --ppid %d --noheaders" % parent_pid, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 52 ps_output = ps_command.stdout.read() 53 retcode = ps_command.wait() 54 pids = [] 55 if retcode == 0: 56 for pair in ps_output.split("\n")[:-1]: 57 [pid, command] = pair.lstrip(' ').split(" ") 58 if command == 'roslaunch': 59 pids.append(int(pid)) 60 else: 61 # Presume this roslaunch was killed by ctrl-c or terminated already. 62 # Am not worrying about classifying between the above presumption and real errors for now 63 pass 64 return pids
65 66
67 -def signal_handler(sig, frame):
68 global processes 69 global roslaunch_pids 70 for p in processes: 71 roslaunch_pids.extend(get_roslaunch_pid(p.pid)) 72 # kill roslaunch's 73 for pid in roslaunch_pids: 74 try: 75 os.kill(pid, signal.SIGHUP) 76 except OSError: 77 continue 78 for pid in roslaunch_pids: 79 console.pretty_println("Terminating roslaunch [pid: %d]" % pid, console.bold) 80 wait_pid(pid) 81 #console.pretty_println("Terminated roslaunch [pid: %d]" % pid, console.bold) 82 sleep(1) 83 # now kill konsoles 84 for p in processes: 85 p.terminate()
86 87
88 -def parse_rocon_launcher(rocon_launcher, default_roslaunch_options):
89 ''' 90 Parses an rocon multi-launcher (xml file). 91 92 @param rocon_launcher : xml file in rocon_launch format 93 @param default_roslaunch_options : options to pass to roslaunch (usually "--screen") 94 @return launchers : list with launcher parameters as dictionary elements of the list. 95 96 @raise IOError : if it can't find any of the individual launchers on the filesystem. 97 ''' 98 tree = ElementTree.parse(rocon_launcher) 99 root = tree.getroot() 100 # should check for root concert tag 101 launchers = [] 102 ports = [] 103 default_port = 11311 104 for launch in root.findall('launch'): 105 parameters = {} 106 parameters['options'] = default_roslaunch_options 107 parameters['package'] = launch.get('package') 108 parameters['name'] = launch.get('name') 109 parameters['path'] = ros_utilities.find_resource(parameters['package'], parameters['name']) # raises an IO error if there is a problem. 110 parameters['port'] = launch.get('port', str(default_port)) 111 if parameters['port'] == str(default_port): 112 default_port += 1 113 if parameters['port'] in ports: 114 parameters['options'] = parameters['options'] + " " + "--wait" 115 else: 116 ports.append(parameters['port']) 117 launchers.append(parameters) 118 return launchers
119 120
121 -def parse_arguments():
122 parser = argparse.ArgumentParser(description="Rocon's multiple master launcher.") 123 terminal_group = parser.add_mutually_exclusive_group() 124 terminal_group.add_argument('-k', '--konsole', default=False, action='store_true', help='spawn individual ros systems via multiple konsole terminals') 125 terminal_group.add_argument('-g', '--gnome', default=False, action='store_true', help='spawn individual ros systems via multiple gnome terminals') 126 parser.add_argument('--screen', action='store_true', help='run each roslaunch with the --screen option') 127 # Force package, launcher pairs, I like this better than roslaunch style which is a bit vague 128 parser.add_argument('package', nargs='?', default='', help='name of the package in which to find the concert launcher') 129 parser.add_argument('launcher', nargs=1, help='name of the concert launch configuration (xml) file') 130 #parser.add_argument('launchers', nargs='+', help='package and concert launch configuration (xml) file configurations, roslaunch style') 131 args = parser.parse_args() 132 return args
133 134
135 -def choose_terminal(gnome_flag, konsole_flag):
136 ''' 137 Use ubuntu's x-terminal-emulator to choose the shell, or over-ride if it there is a flag. 138 ''' 139 if konsole_flag: 140 if not which('konsole'): 141 console.error("Cannot find 'konsole' [hint: try --gnome for gnome-terminal instead]") 142 sys.exit(1) 143 return 'konsole' 144 elif gnome_flag: 145 if not which('gnome-terminal'): 146 console.error("Cannot find 'gnome' [hint: try --konsole for konsole instead]") 147 sys.exit(1) 148 return 'gnome-terminal' 149 else: 150 if not which('x-terminal-emulator'): 151 console.error("Cannot find 'x-terminal-emulator' [hint: try --gnome or --konsole instead]") 152 sys.exit(1) 153 p = subprocess.Popen([which('update-alternatives'), '--query', 'x-terminal-emulator'], stdout=subprocess.PIPE) 154 terminal = None 155 for line in p.stdout: 156 if line.startswith("Value:"): 157 terminal = os.path.basename(line.split()[1]) 158 break 159 if terminal not in ["gnome-terminal", "gnome-terminal.wrapper", "konsole"]: 160 console.warning("You are using an esoteric unsupported terminal [%s]" % terminal) 161 if which('konsole'): 162 terminal = 'konsole' 163 console.warning(" --> falling back to 'konsole'") 164 elif which('gnome-terminal'): 165 console.warning(" --> falling back to 'gnome-terminal'") 166 terminal = 'gnome-terminal' 167 else: 168 console.error("Unsupported terminal set for 'x-terminal-emulator' [%s][hint: try --gnome or --konsole instead]" % terminal) 169 sys.exit(1) 170 return terminal
171 172
173 -def main():
174 global processes 175 global roslaunch_pids 176 signal.signal(signal.SIGINT, signal_handler) 177 args = parse_arguments() 178 if not which('konsole') and not which('gnome-terminal')and not which('x-terminal-emulator'): 179 console.error("Cannot find a suitable terminal [x-terminal-emulator, konsole, gnome-termional]") 180 sys.exit(1) 181 terminal = choose_terminal(args.gnome, args.konsole) 182 183 if args.package == '': 184 rocon_launcher = roslaunch.rlutil.resolve_launch_arguments(args.launcher)[0] 185 else: 186 rocon_launcher = roslaunch.rlutil.resolve_launch_arguments([args.package] + args.launcher)[0] 187 if args.screen: 188 roslaunch_options = "--screen" 189 else: 190 roslaunch_options = "" 191 launchers = parse_rocon_launcher(rocon_launcher, roslaunch_options) 192 for launcher in launchers: 193 console.pretty_println("Launching [%s, %s] on port %s" % (launcher['package'], launcher['name'], launcher['port']), console.bold) 194 if terminal == 'konsole': 195 p = subprocess.Popen([terminal, '--nofork', '--hold', '-e', "/bin/bash", "-c", "roslaunch %s --port %s %s %s" % 196 (launcher['options'], launcher['port'], launcher['package'], launcher['name'])], preexec_fn=preexec) 197 elif terminal == 'gnome-terminal.wrapper' or terminal == 'gnome-terminal': 198 # --disable-factory inherits the current environment, bit wierd. 199 p = subprocess.Popen(['gnome-terminal', '--disable-factory', '-e', "/bin/bash", "-e", "roslaunch %s --port %s %s %s" % 200 (launcher['options'], launcher['port'], launcher['package'], launcher['name'])], preexec_fn=preexec) 201 processes.append(p) 202 signal.pause()
203