Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
92b5beb
Implement action recording and display info about recorded action
Barry-Xu-2018 Mar 14, 2025
1bc1f9a
Address review comment of first round
Barry-Xu-2018 Mar 26, 2025
6c437c0
Address review comments from fujitatomoya
Barry-Xu-2018 Mar 26, 2025
7a29fcc
genstub
Barry-Xu-2018 Mar 27, 2025
cb175c2
Optimize some parts of the code
Barry-Xu-2018 Mar 27, 2025
0e23198
Fix an issue with the display when there is no service data
Barry-Xu-2018 Mar 28, 2025
a4f2190
Address review comments
Barry-Xu-2018 Mar 28, 2025
a56f295
Change a variable name to make the code clearer and more readable
Barry-Xu-2018 Mar 29, 2025
fb1286d
Address review comments
Barry-Xu-2018 Mar 29, 2025
e4fd9ef
Simplify if-else block with a shorter name service_info
morlov-apexai Mar 29, 2025
63723f5
Throw exception by default in the get_action_type_for_info(topic_type)
morlov-apexai Mar 29, 2025
8fcfbde
Remove type conversion in sequential_writer
morlov-apexai Mar 29, 2025
99103f8
Calculate actions status and feedback statistics in read_service_and_…
morlov-apexai Mar 29, 2025
c85ed3c
Address some other small style changes and nitpicks
morlov-apexai Mar 29, 2025
5c31b04
Add default section with exception to sorting functions
MichaelOrlov Mar 29, 2025
8e69169
Use structural bindings in the test_action_utils.cpp
MichaelOrlov Mar 29, 2025
6bb2be1
Add Doxygen comments to the action_utils.hpp
MichaelOrlov Mar 29, 2025
e196b67
Avoid using ContainsRegex on Windows to handle the '|' character
Barry-Xu-2018 Apr 1, 2025
bdc6e52
Implement rosbag2 action play
Barry-Xu-2018 Mar 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ Options:
Space-delimited list of topics to play.
* `--services`:
Space-delimited list of services to play.
* `--actions`:
Space-delimited list of actions to play.
* `-e,--regex`:
Play only topics and services matches with regular expression.
* `-x,--exclude-regex`:
Expand All @@ -175,6 +177,8 @@ Options:
Space-delimited list of topics not to play.
* `--exclude-services`:
Space-delimited list of services not to play.
* `--exclude-actions`:
Space-delimited list of actions not to play.
* `--message-order {received,sent}`:
The reference to use for bag message chronological ordering.
Choices: reception timestamp (`received`), publication timestamp (`sent`).
Expand Down Expand Up @@ -282,12 +286,15 @@ output_bags:
topic_types: []
all_services: false
services: []
all_actions: false
actions: []
rmw_serialization_format: "" # defaults to using the format of the input topic
regex: ""
exclude_regex: ""
exclude_topics: []
exclude_topic_types: []
exclude_services: []
exclude_actions: []
compression_mode: ""
compression_format: ""
compression_queue_size: 1
Expand Down
6 changes: 6 additions & 0 deletions ros2bag/ros2bag/verb/burst.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ def add_arguments(self, parser, cli_name): # noqa: D102
'--services', type=str, default=[], nargs='+',
help='services to replay, separated by space. At least one service needs to be '
"specified. If this parameter isn\'t specified, all services will be replayed.")
parser.add_argument(
'--actions', type=str, default=[], nargs='+',
help='actions to replay, separated by space. At least one action needs to be '
"specified. If this parameter isn\'t specified, all actions will be replayed.")
parser.add_argument(
'--qos-profile-overrides-path', type=FileType('r'),
help='Path to a yaml file defining overrides of the QoS profile for specific topics.')
Expand Down Expand Up @@ -97,6 +101,8 @@ def main(self, *, args): # noqa: D102
play_options.topics_to_filter = args.topics
# Convert service name to service event topic name
play_options.services_to_filter = convert_service_to_service_event_topic(args.services)
# Covert action name to action related topic names
play_options.actions_to_filter = args.actions
play_options.topic_qos_profile_overrides = qos_profile_overrides
play_options.loop = False
play_options.topic_remapping_options = topic_remapping
Expand Down
3 changes: 2 additions & 1 deletion ros2bag/ros2bag/verb/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ def add_arguments(self, parser, cli_name): # noqa: D102
)
parser.add_argument(
'-v', '--verbose', action='store_true',
help='Display request/response information for services'
help='Display verbose information about request/response services, actions and'
' print messages size contribution on per topic bases.'
)
available_sorting_methods = Info().get_sorting_methods()
parser.add_argument(
Expand Down
28 changes: 24 additions & 4 deletions ros2bag/ros2bag/verb/play.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,24 @@ def add_arguments(self, parser, cli_name): # noqa: D102
parser.add_argument(
'--services', type=str, default=[], metavar='service', nargs='+',
help='Space-delimited list of services to play.')
parser.add_argument(
'--actions', type=str, default=[], metavar='action', nargs='+',
help='Space-delimited list of actions to play.')
parser.add_argument(
'-e', '--regex', default='',
help='Play only topics and services matches with regular expression.')
help='Play only topics, services and actions matches with regular expression.')
parser.add_argument(
'-x', '--exclude-regex', default='',
help='regular expressions to exclude topics and services from replay.')
help='regular expressions to exclude topics, services and actions from replay.')
parser.add_argument(
'--exclude-topics', type=str, default=[], metavar='topic', nargs='+',
help='Space-delimited list of topics not to play.')
parser.add_argument(
'--exclude-services', type=str, default=[], metavar='service', nargs='+',
help='Space-delimited list of services not to play.')
parser.add_argument(
'--exclude-actions', type=str, default=[], metavar='action', nargs='+',
help='Space-delimited list of actions not to play.')
parser.add_argument(
'--qos-profile-overrides-path', type=FileType('r'),
help='Path to a yaml file defining overrides of the QoS profile for specific topics.')
Expand Down Expand Up @@ -162,12 +168,20 @@ def add_arguments(self, parser, cli_name): # noqa: D102
parser.add_argument(
'--publish-service-requests', action='store_true', default=False,
help='Publish recorded service requests instead of recorded service events')
parser.add_argument(
'--send-actions-as-client', action='store_true', default=False,
help='Send the send_goal request, cancel_goal request, and get_result request '
'respectively based on the recorded send_goal, cancel_goal, and get_result '
'event messages. Note that the messages from action\'s "status topic" and '
'"feedback topic" will not be sent because they are expected to be sent from '
'the action server side.')
parser.add_argument(
'--service-requests-source', default='service_introspection',
choices=['service_introspection', 'client_introspection'],
help='Determine the source of the service requests to be replayed. This option only '
'makes sense if the "--publish-service-requests" option is set. By default,'
' the service requests replaying from recorded service introspection message.')
'makes sense if the "--publish-service-requests" or "--send-actions-as-client" '
'option is set. By default, the service requests replaying from recorded '
'service introspection message.')
parser.add_argument(
'--message-order', default='received',
choices=['received', 'sent'],
Expand Down Expand Up @@ -271,13 +285,18 @@ def main(self, *, args): # noqa: D102
# Convert service name to service event topic name
play_options.services_to_filter = convert_service_to_service_event_topic(args.services)

play_options.actions_to_filter = args.actions

play_options.regex_to_filter = args.regex

play_options.exclude_regex_to_filter = args.exclude_regex

play_options.exclude_service_events_to_filter = \
convert_service_to_service_event_topic(args.exclude_services)

play_options.exclude_actions_to_filter = \
args.exclude_actions if args.exclude_actions else []

play_options.topic_qos_profile_overrides = qos_profile_overrides
play_options.loop = args.loop
play_options.topic_remapping_options = topic_remapping
Expand All @@ -299,6 +318,7 @@ def main(self, *, args): # noqa: D102
play_options.wait_acked_timeout = args.wait_for_all_acked
play_options.disable_loan_message = args.disable_loan_message
play_options.publish_service_requests = args.publish_service_requests
play_options.send_actions_as_client = args.send_actions_as_client
if not args.service_requests_source or \
args.service_requests_source == 'service_introspection':
play_options.service_requests_source = ServiceRequestsSource.SERVICE_INTROSPECTION
Expand Down
46 changes: 36 additions & 10 deletions ros2bag/ros2bag/verb/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,27 +70,34 @@ def add_recorder_arguments(parser: ArgumentParser) -> None:
parser.add_argument(
'--services', type=str, metavar='ServiceName', nargs='+',
help='Space-delimited list of services to record.')
parser.add_argument(
'--actions', type=str, metavar='ActionName', nargs='+',
help='Space-delimited list of actions to record.')
parser.add_argument(
'--topic-types', nargs='+', default=[], metavar='TopicType',
help='Space-delimited list of topic types to record.')
parser.add_argument(
'-a', '--all', action='store_true',
help='Record all topics and services (Exclude hidden topic).')
help='Record all topics, services and actions (Exclude hidden topic).')
parser.add_argument(
'--all-topics', action='store_true',
help='Record all topics (Exclude hidden topic).')
parser.add_argument(
'--all-services', action='store_true',
help='Record all services via service event topics.')
parser.add_argument(
'--all-actions', action='store_true',
help='Record all actions via internal topics and service event topics.')
parser.add_argument(
'-e', '--regex', default='',
help='Record only topics and services containing provided regular expression. '
'Note: --all, --all-topics or --all-services will override --regex.')
'Note: --all, --all-topics, --all-services or --all-actions will override --regex.')
parser.add_argument(
'--exclude-regex', default='',
help='Exclude topics and services containing provided regular expression. '
'Works on top of '
'--all, --all-topics, --all-services, --topics, --services or --regex.')
'--all, --all-topics, --all-services, --all-actions, --topics, --services, --actions '
'or --regex.')
parser.add_argument(
'--exclude-topic-types', type=str, default=[], metavar='ExcludeTopicTypes', nargs='+',
help='Space-delimited list of topic types not being recorded. '
Expand All @@ -103,6 +110,10 @@ def add_recorder_arguments(parser: ArgumentParser) -> None:
'--exclude-services', type=str, metavar='ServiceName', nargs='+',
help='Space-delimited list of services not being recorded. '
'Works on top of --all, --all-services, --services or --regex.')
parser.add_argument(
'--exclude-actions', type=str, metavar='ActionName', nargs='+',
help='Space-delimited list of actions not being recorded. '
'Works on top of --all, --all-actions, --actions or --regex.')

# Discovery behavior
parser.add_argument(
Expand Down Expand Up @@ -217,10 +228,11 @@ def add_recorder_arguments(parser: ArgumentParser) -> None:


def check_necessary_argument(args):
# At least one options out of --all, --all-topics, --all-services, --services, --topics,
# --topic-types or --regex must be used
if not (args.all or args.all_topics or args.all_services or
# At least one options out of --all, --all-topics, --all-services, --all-actions, --services,
# --actions --topics, --topic-types or --regex must be used
if not (args.all or args.all_topics or args.all_services or args.all_actions or
(args.services and len(args.services) > 0) or
(args.actions and len(args.actions) > 0) or
(args.topics and len(args.topics) > 0) or
(args.topic_types and len(args.topic_types) > 0) or args.regex):
return False
Expand All @@ -239,9 +251,9 @@ def validate_parsed_arguments(args, uri) -> str:

if args.exclude_regex and not \
(args.all or args.all_topics or args.topic_types or args.all_services or
args.regex):
args.all_actions or args.regex):
return print_error('--exclude-regex argument requires either --all, '
'--all-topics, --topic-types, --all-services or --regex')
'--all-topics, --topic-types, --all-services, --all-actions or --regex')

if args.exclude_topics and not \
(args.all or args.all_topics or args.topic_types or args.regex):
Expand All @@ -257,14 +269,22 @@ def validate_parsed_arguments(args, uri) -> str:
return print_error('--exclude-services argument requires either --all, --all-services '
'or --regex')

if args.exclude_actions and not (args.all or args.all_actions or args.regex):
return print_error('--exclude-actions argument requires either --all, --all-actions '
'or --regex')

if (args.all or args.all_services) and args.services:
print(print_warn('--all or --all-services will override --services'))

if (args.all or args.all_topics) and args.topics:
print(print_warn('--all or --all-topics will override --topics'))

if (args.all or args.all_topics or args.all_services) and args.regex:
print(print_warn('--all, --all-topics or --all-services will override --regex'))
if (args.all or args.all_actions) and args.actions:
print(print_warn('--all or --all-actions will override --actions'))

if (args.all or args.all_topics or args.all_services or args.all_actions) and args.regex:
print(print_warn('--all, --all-topics --all-services or --all-actions will override '
'--regex'))

if os.path.isdir(uri):
return print_error("Output folder '{}' already exists.".format(uri))
Expand All @@ -281,6 +301,8 @@ def validate_parsed_arguments(args, uri) -> str:
if args.compression_queue_size < 0:
return print_error('Compression queue size must be at least 0.')

return None


class RecordVerb(VerbExtension):
"""Record ROS data to a bag."""
Expand Down Expand Up @@ -330,11 +352,14 @@ def main(self, *, args): # noqa: D102
record_options = RecordOptions()
record_options.all_topics = args.all_topics or args.all
record_options.all_services = args.all_services or args.all
record_options.all_actions = args.all_actions or args.all
record_options.is_discovery_disabled = args.no_discovery
record_options.topics = args.topics
record_options.topic_types = args.topic_types
# Convert service name to service event topic name
record_options.services = convert_service_to_service_event_topic(args.services)
record_options.actions = args.actions if args.actions else []

record_options.exclude_topic_types = args.exclude_topic_types
record_options.rmw_serialization_format = args.serialization_format
record_options.topic_polling_interval = datetime.timedelta(
Expand All @@ -344,6 +369,7 @@ def main(self, *, args): # noqa: D102
record_options.exclude_topics = args.exclude_topics if args.exclude_topics else []
record_options.exclude_service_events = \
convert_service_to_service_event_topic(args.exclude_services)
record_options.exclude_actions = args.exclude_actions if args.exclude_actions else []
record_options.node_prefix = NODE_NAME_PREFIX
record_options.compression_mode = args.compression_mode
record_options.compression_format = args.compression_format
Expand Down
Loading
Loading