Skip to content

Commit 4e78813

Browse files
authored
Update spawner to accept controllers list and start them in sequence (ros-controls#1139)
1 parent 971ff39 commit 4e78813

File tree

3 files changed

+221
-102
lines changed

3 files changed

+221
-102
lines changed

controller_manager/controller_manager/spawner.py

Lines changed: 124 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ def is_controller_loaded(node, controller_manager, controller_name):
135135
def main(args=None):
136136
rclpy.init(args=args, signal_handler_options=SignalHandlerOptions.NO)
137137
parser = argparse.ArgumentParser()
138-
parser.add_argument("controller_name", help="Name of the controller")
138+
parser.add_argument("controller_names", help="List of controllers", nargs="+")
139139
parser.add_argument(
140140
"-c",
141141
"--controller-manager",
@@ -184,10 +184,17 @@ def main(args=None):
184184
default=10,
185185
type=int,
186186
)
187+
parser.add_argument(
188+
"--activate-as-group",
189+
help="Activates all the parsed controllers list together instead of one by one."
190+
" Useful for activating all chainable controllers altogether",
191+
action="store_true",
192+
required=False,
193+
)
187194

188195
command_line_args = rclpy.utilities.remove_ros_args(args=sys.argv)[1:]
189196
args = parser.parse_args(command_line_args)
190-
controller_name = args.controller_name
197+
controller_names = args.controller_names
191198
controller_manager_name = args.controller_manager
192199
controller_namespace = args.namespace
193200
param_file = args.param_file
@@ -197,11 +204,7 @@ def main(args=None):
197204
if param_file and not os.path.isfile(param_file):
198205
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), param_file)
199206

200-
prefixed_controller_name = controller_name
201-
if controller_namespace:
202-
prefixed_controller_name = controller_namespace + "/" + controller_name
203-
204-
node = Node("spawner_" + controller_name)
207+
node = Node("spawner_" + controller_names[0])
205208

206209
if not controller_manager_name.startswith("/"):
207210
spawner_namespace = node.get_namespace()
@@ -219,115 +222,142 @@ def main(args=None):
219222
)
220223
return 1
221224

222-
if is_controller_loaded(node, controller_manager_name, prefixed_controller_name):
223-
node.get_logger().warn(
224-
bcolors.WARNING
225-
+ "Controller already loaded, skipping load_controller"
226-
+ bcolors.ENDC
227-
)
228-
else:
229-
if controller_type:
230-
parameter = Parameter()
231-
parameter.name = prefixed_controller_name + ".type"
232-
parameter.value = get_parameter_value(string_value=controller_type)
225+
for controller_name in controller_names:
226+
prefixed_controller_name = controller_name
227+
if controller_namespace:
228+
prefixed_controller_name = controller_namespace + "/" + controller_name
233229

234-
response = call_set_parameters(
235-
node=node, node_name=controller_manager_name, parameters=[parameter]
230+
if is_controller_loaded(node, controller_manager_name, prefixed_controller_name):
231+
node.get_logger().warn(
232+
bcolors.WARNING
233+
+ "Controller already loaded, skipping load_controller"
234+
+ bcolors.ENDC
236235
)
237-
assert len(response.results) == 1
238-
result = response.results[0]
239-
if result.successful:
240-
node.get_logger().info(
241-
bcolors.OKCYAN
242-
+ 'Set controller type to "'
243-
+ controller_type
244-
+ '" for '
245-
+ bcolors.BOLD
246-
+ prefixed_controller_name
247-
+ bcolors.ENDC
236+
else:
237+
if controller_type:
238+
parameter = Parameter()
239+
parameter.name = prefixed_controller_name + ".type"
240+
parameter.value = get_parameter_value(string_value=controller_type)
241+
242+
response = call_set_parameters(
243+
node=node, node_name=controller_manager_name, parameters=[parameter]
248244
)
249-
else:
245+
assert len(response.results) == 1
246+
result = response.results[0]
247+
if result.successful:
248+
node.get_logger().info(
249+
bcolors.OKCYAN
250+
+ 'Set controller type to "'
251+
+ controller_type
252+
+ '" for '
253+
+ bcolors.BOLD
254+
+ prefixed_controller_name
255+
+ bcolors.ENDC
256+
)
257+
else:
258+
node.get_logger().fatal(
259+
bcolors.FAIL
260+
+ 'Could not set controller type to "'
261+
+ controller_type
262+
+ '" for '
263+
+ bcolors.BOLD
264+
+ prefixed_controller_name
265+
+ bcolors.ENDC
266+
)
267+
return 1
268+
269+
ret = load_controller(node, controller_manager_name, controller_name)
270+
if not ret.ok:
250271
node.get_logger().fatal(
251272
bcolors.FAIL
252-
+ 'Could not set controller type to "'
253-
+ controller_type
254-
+ '" for '
273+
+ "Failed loading controller "
255274
+ bcolors.BOLD
256275
+ prefixed_controller_name
257276
+ bcolors.ENDC
258277
)
259278
return 1
260-
261-
ret = load_controller(node, controller_manager_name, controller_name)
262-
if not ret.ok:
263-
node.get_logger().fatal(
264-
bcolors.FAIL
265-
+ "Failed loading controller "
279+
node.get_logger().info(
280+
bcolors.OKBLUE
281+
+ "Loaded "
266282
+ bcolors.BOLD
267283
+ prefixed_controller_name
268284
+ bcolors.ENDC
269285
)
270-
return 1
271-
node.get_logger().info(
272-
bcolors.OKBLUE + "Loaded " + bcolors.BOLD + prefixed_controller_name + bcolors.ENDC
273-
)
274286

275-
if param_file:
276-
# load_parameter_file writes to stdout/stderr. Here we capture that and use node logging instead
277-
with redirect_stdout(io.StringIO()) as f_stdout, redirect_stderr(
278-
io.StringIO()
279-
) as f_stderr:
280-
load_parameter_file(
281-
node=node,
282-
node_name=prefixed_controller_name,
283-
parameter_file=param_file,
284-
use_wildcard=True,
287+
if param_file:
288+
# load_parameter_file writes to stdout/stderr. Here we capture that and use node logging instead
289+
with redirect_stdout(io.StringIO()) as f_stdout, redirect_stderr(
290+
io.StringIO()
291+
) as f_stderr:
292+
load_parameter_file(
293+
node=node,
294+
node_name=prefixed_controller_name,
295+
parameter_file=param_file,
296+
use_wildcard=True,
297+
)
298+
if f_stdout.getvalue():
299+
node.get_logger().info(bcolors.OKCYAN + f_stdout.getvalue() + bcolors.ENDC)
300+
if f_stderr.getvalue():
301+
node.get_logger().error(bcolors.FAIL + f_stderr.getvalue() + bcolors.ENDC)
302+
node.get_logger().info(
303+
bcolors.OKCYAN
304+
+ 'Loaded parameters file "'
305+
+ param_file
306+
+ '" for '
307+
+ bcolors.BOLD
308+
+ prefixed_controller_name
309+
+ bcolors.ENDC
285310
)
286-
if f_stdout.getvalue():
287-
node.get_logger().info(bcolors.OKCYAN + f_stdout.getvalue() + bcolors.ENDC)
288-
if f_stderr.getvalue():
289-
node.get_logger().error(bcolors.FAIL + f_stderr.getvalue() + bcolors.ENDC)
290-
node.get_logger().info(
291-
bcolors.OKCYAN
292-
+ 'Loaded parameters file "'
293-
+ param_file
294-
+ '" for '
295-
+ bcolors.BOLD
296-
+ prefixed_controller_name
297-
+ bcolors.ENDC
298-
)
299-
# TODO(destogl): use return value when upstream return value is merged
300-
# ret =
301-
# if ret.returncode != 0:
302-
# Error message printed by ros2 param
303-
# return ret.returncode
304-
node.get_logger().info("Loaded " + param_file + " into " + prefixed_controller_name)
305-
306-
if not args.load_only:
307-
ret = configure_controller(node, controller_manager_name, controller_name)
308-
if not ret.ok:
309-
node.get_logger().error(
310-
bcolors.FAIL + "Failed to configure controller" + bcolors.ENDC
311+
# TODO(destogl): use return value when upstream return value is merged
312+
# ret =
313+
# if ret.returncode != 0:
314+
# Error message printed by ros2 param
315+
# return ret.returncode
316+
node.get_logger().info(
317+
"Loaded " + param_file + " into " + prefixed_controller_name
311318
)
312-
return 1
313319

314-
if not args.inactive:
315-
ret = switch_controllers(
316-
node, controller_manager_name, [], [controller_name], True, True, 5.0
317-
)
320+
if not args.load_only:
321+
ret = configure_controller(node, controller_manager_name, controller_name)
318322
if not ret.ok:
319323
node.get_logger().error(
320-
bcolors.FAIL + "Failed to activate controller" + bcolors.ENDC
324+
bcolors.FAIL + "Failed to configure controller" + bcolors.ENDC
321325
)
322326
return 1
323327

324-
node.get_logger().info(
325-
bcolors.OKGREEN
326-
+ "Configured and activated "
327-
+ bcolors.BOLD
328-
+ prefixed_controller_name
329-
+ bcolors.ENDC
328+
if not args.inactive and not args.activate_as_group:
329+
ret = switch_controllers(
330+
node, controller_manager_name, [], [controller_name], True, True, 5.0
331+
)
332+
if not ret.ok:
333+
node.get_logger().error(
334+
bcolors.FAIL + "Failed to activate controller" + bcolors.ENDC
335+
)
336+
return 1
337+
338+
node.get_logger().info(
339+
bcolors.OKGREEN
340+
+ "Configured and activated "
341+
+ bcolors.BOLD
342+
+ prefixed_controller_name
343+
+ bcolors.ENDC
344+
)
345+
346+
if not args.inactive and args.activate_as_group:
347+
ret = switch_controllers(
348+
node, controller_manager_name, [], controller_names, True, True, 5.0
349+
)
350+
if not ret.ok:
351+
node.get_logger().error(
352+
bcolors.FAIL + "Failed to activate the parsed controllers list" + bcolors.ENDC
330353
)
354+
return 1
355+
356+
node.get_logger().info(
357+
bcolors.OKGREEN
358+
+ "Configured and activated all the parsed controllers list!"
359+
+ bcolors.ENDC
360+
)
331361

332362
if not args.unload_on_kill:
333363
return 0
@@ -339,8 +369,9 @@ def main(args=None):
339369
except KeyboardInterrupt:
340370
if not args.inactive:
341371
node.get_logger().info("Interrupt captured, deactivating and unloading controller")
372+
# TODO(saikishor) we might have an issue in future, if any of these controllers is in chained mode
342373
ret = switch_controllers(
343-
node, controller_manager_name, [controller_name], [], True, True, 5.0
374+
node, controller_manager_name, controller_names, [], True, True, 5.0
344375
)
345376
if not ret.ok:
346377
node.get_logger().error(

controller_manager/controller_manager/unspawner.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
def main(args=None):
2828
rclpy.init(args=args)
2929
parser = argparse.ArgumentParser()
30-
parser.add_argument("controller_name", help="Name of the controller")
30+
parser.add_argument("controller_names", help="Name of the controller", nargs="+")
3131
parser.add_argument(
3232
"-c",
3333
"--controller-manager",
@@ -38,22 +38,23 @@ def main(args=None):
3838

3939
command_line_args = rclpy.utilities.remove_ros_args(args=sys.argv)[1:]
4040
args = parser.parse_args(command_line_args)
41-
controller_name = args.controller_name
41+
controller_names = args.controller_names
4242
controller_manager_name = args.controller_manager
4343

44-
node = Node("unspawner_" + controller_name)
44+
node = Node("unspawner_" + controller_names[0])
4545
try:
4646
# Ignore returncode, because message is already printed and we'll try to unload anyway
4747
ret = switch_controllers(
48-
node, controller_manager_name, [controller_name], [], True, True, 5.0
48+
node, controller_manager_name, controller_names, [], True, True, 5.0
4949
)
5050
node.get_logger().info("Deactivated controller")
5151

52-
ret = unload_controller(node, controller_manager_name, controller_name)
53-
if not ret.ok:
54-
node.get_logger().info("Failed to unload controller")
55-
return 1
56-
node.get_logger().info("Unloaded controller")
52+
for controller_name in controller_names:
53+
ret = unload_controller(node, controller_manager_name, controller_name)
54+
if not ret.ok:
55+
node.get_logger().info("Failed to unload controller")
56+
return 1
57+
node.get_logger().info("Unloaded controller")
5758

5859
return 0
5960
finally:

0 commit comments

Comments
 (0)