diff --git a/ros2action/ros2action/verb/info.py b/ros2action/ros2action/verb/info.py index ab137a865..989a69cb4 100644 --- a/ros2action/ros2action/verb/info.py +++ b/ros2action/ros2action/verb/info.py @@ -19,6 +19,25 @@ from ros2cli.node.strategy import NodeStrategy +def _split_fully_qualified_name(fqn): + if not fqn.startswith('/'): + return '/', fqn + idx = fqn.rfind('/') + if idx == 0: + return '/', fqn[1:] + return fqn[:idx], fqn[idx + 1:] + + +def _format_action_endpoint_block(node_fqn, types, endpoint_label): + node_namespace, node_name = _split_fully_qualified_name(node_fqn) + return ( + f'Node name: {node_name}\n' + f'Node namespace: {node_namespace}\n' + f'Action type: {", ".join(types)}\n' + f'Endpoint type: {endpoint_label}' + ) + + class InfoVerb(VerbExtension): """Print information about an action.""" @@ -33,6 +52,12 @@ def add_arguments(self, parser, cli_name): parser.add_argument( '-c', '--count', action='store_true', help='Only display the number of action clients and action servers') + parser.add_argument( + '--verbose', '-v', action='store_true', + help='Prints detailed information for each action client and action server, ' + 'including the node name, node namespace, action type, and endpoint type ' + '(CLIENT or SERVER). QoS profiles and type hashes are not exposed for ' + 'action endpoints.') def main(self, *, args): with NodeStrategy(args) as node: @@ -44,20 +69,38 @@ def main(self, *, args): except (ValueError, rclpy.exceptions.InvalidTopicNameException) as e: raise RuntimeError(e) - print('Action: {}'.format(args.action_name)) - print('Action clients: {}'.format(len(action_clients))) + line_end = '\n\n' if args.verbose else '\n' + + print('Action: {}'.format(args.action_name), end=line_end) + + print('Action clients: {}'.format(len(action_clients)), end=line_end) if not args.count: - for client_name, client_types in action_clients: - if args.show_types: - types_formatted = ', '.join(client_types) - print(f' {client_name} [{types_formatted}]') - else: - print(f' {client_name}') - print('Action servers: {}'.format(len(action_servers))) + if args.verbose: + for node_fqn, client_types in action_clients: + print( + _format_action_endpoint_block(node_fqn, client_types, 'CLIENT'), + end=line_end, + ) + else: + for client_name, client_types in action_clients: + if args.show_types: + types_formatted = ', '.join(client_types) + print(f' {client_name} [{types_formatted}]') + else: + print(f' {client_name}') + + print('Action servers: {}'.format(len(action_servers)), end=line_end) if not args.count: - for server_name, server_types in action_servers: - if args.show_types: - types_formatted = ', '.join(server_types) - print(f' {server_name} [{types_formatted}]') - else: - print(f' {server_name}') + if args.verbose: + for node_fqn, server_types in action_servers: + print( + _format_action_endpoint_block(node_fqn, server_types, 'SERVER'), + end=line_end, + ) + else: + for server_name, server_types in action_servers: + if args.show_types: + types_formatted = ', '.join(server_types) + print(f' {server_name} [{types_formatted}]') + else: + print(f' {server_name}') diff --git a/ros2action/test/test_cli.py b/ros2action/test/test_cli.py index a44cdd5c3..e8d1888c8 100644 --- a/ros2action/test/test_cli.py +++ b/ros2action/test/test_cli.py @@ -213,6 +213,44 @@ def test_fibonacci_info_count(self): strict=False ) + @launch_testing.markers.retry_on_failure(times=5, delay=1) + def test_fibonacci_info_verbose(self): + with self.launch_action_command( + arguments=['info', '-v', '/test/fibonacci']) as action_command: + assert action_command.wait_for_shutdown(timeout=10) + assert action_command.exit_code == launch_testing.asserts.EXIT_OK + assert launch_testing.tools.expect_output( + expected_lines=[ + 'Action: /test/fibonacci', + 'Action clients: 0', + 'Action servers: 1', + 'Node name: fibonacci_action_server', + 'Node namespace: /test', + 'Action type: test_msgs/action/Fibonacci', + 'Endpoint type: SERVER', + ], + text=action_command.output, + strict=False + ) + + @launch_testing.markers.retry_on_failure(times=5, delay=1) + def test_fibonacci_info_verbose_count(self): + with self.launch_action_command( + arguments=['info', '-v', '-c', '/test/fibonacci']) as action_command: + assert action_command.wait_for_shutdown(timeout=10) + assert action_command.exit_code == launch_testing.asserts.EXIT_OK + assert launch_testing.tools.expect_output( + expected_lines=[ + 'Action: /test/fibonacci', + 'Action clients: 0', + 'Action servers: 1', + ], + text=action_command.output, + strict=False + ) + assert 'Node name:' not in action_command.output + assert 'Endpoint type:' not in action_command.output + @launch_testing.markers.retry_on_failure(times=5, delay=1) def test_list(self): with self.launch_action_command(arguments=['list']) as action_command: