1
2
3
4
5
6
7
8
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
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
27
28
29 processes = []
30 roslaunch_pids = []
31
32
33
34
35
36
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()
44
45
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
62
63 pass
64 return pids
65
66
86
87
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
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'])
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
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
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
131 args = parser.parse_args()
132 return args
133
134
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
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
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