Skip to content

Commit

Permalink
Implement service record phase 1
Browse files Browse the repository at this point in the history
Signed-off-by: Barry Xu <[email protected]>
  • Loading branch information
Barry-Xu-2018 committed Jul 11, 2023
1 parent 698b8ee commit bd6e02b
Show file tree
Hide file tree
Showing 42 changed files with 1,349 additions and 124 deletions.
14 changes: 13 additions & 1 deletion docs/message_definition_encoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ This set of definitions with all field types recursively included can be called

## `ros2msg` encoding

This encoding consists of definitions in [.msg](https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html#message-description-specification) format, concatenated together in human-readable form with
This encoding consists of definitions in [.msg](https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html#message-description-specification) and [.srv](https://docs.ros.org/en/rolling/Concepts/Basic/About-Interfaces.html#services) format, concatenated together in human-readable form with
a delimiter.

The top-level message definition is present first, with no delimiter. All dependent .msg definitions are preceded by a two-line delimiter:
Expand All @@ -38,6 +38,18 @@ MSG: my_msgs/msg/BasicMsg
float32 my_float
```

Another example is a service message definition for `my_msgs/srv/ExampleSrv` in `ros2msg` form
```
# defines a service message that includes a field of a custom message type
my_msgs/BasicMsg request
---
my_msgs/BasicMsg response
================================================================================
MSG: my_msgs/msg/BasicMsg
# defines a message with a primitive type field
float32 my_float
```

## `ros2idl` encoding

The IDL definition of the type specified by name along with all dependent types are stored together. The IDL definitions can be stored in any order. Every definition is preceded by a two-line delimiter:
Expand Down
28 changes: 26 additions & 2 deletions ros2bag/ros2bag/verb/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,35 @@ def add_arguments(self, parser, cli_name): # noqa: D102
'-t', '--topic-name', action='store_true',
help='Only display topic names.'
)
parser.add_argument(
'-v', '--verbose', action='store_true',
help='Display request/response information for services'
)

def _is_service_event_topic(self, topic_name, topic_type) -> bool:

service_event_type_middle = '/srv/'
service_event_type_postfix = '_Event'

if (service_event_type_middle not in topic_type
or not topic_type.endswith(service_event_type_postfix)):
return False

service_event_topic_postfix = '/_service_event'
if not topic_name.endswith(service_event_topic_postfix):
return False

return True

def main(self, *, args): # noqa: D102
m = Info().read_metadata(args.bag_path, args.storage)
if args.topic_name:
for topic_info in m.topics_with_message_count:
print(topic_info.topic_metadata.name)
if not self._is_service_event_topic(topic_info.topic_metadata.name,
topic_info.topic_metadata.type):
print(topic_info.topic_metadata.name)
else:
print(m)
if args.verbose:
Info().read_metadata_and_output_service_verbose(args.bag_path, args.storage)
else:
print(m)
77 changes: 59 additions & 18 deletions ros2bag/ros2bag/verb/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,31 @@ def add_arguments(self, parser, cli_name): # noqa: D102
'topics', nargs='*', default=None, help='List of topics to record.')
parser.add_argument(
'-a', '--all', action='store_true',
help='Record all topics. Required if no explicit topic list or regex filters.')
help='Record all topics and services (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(
'-e', '--regex', default='',
help='Record only topics containing provided regular expression. '
'Overrides --all, applies on top of topics list.')
help='Record only topics and services containing provided regular expression. '
'Overrides --all, --all-topics and --all-services, applies on top of '
'topics list and service list.')
parser.add_argument(
'-x', '--exclude', default='',
'--exclude-topics', default='',
help='Exclude topics containing provided regular expression. '
'Works on top of --all, --regex, or topics list.')
'Works on top of --all, --all-topics, or --regex.')
parser.add_argument(
'--exclude-services', default='',
help='Exclude services containing provided regular expression. '
'Works on top of --all, --all-services, or --regex.')

# Enable to record service
parser.add_argument(
'--services', type=str, metavar='ServiceName', nargs='+',
help='List of services to record.')

# Discovery behavior
parser.add_argument(
Expand Down Expand Up @@ -168,19 +184,34 @@ def add_arguments(self, parser, cli_name): # noqa: D102
'Has no effect if no compression mode is chosen. Default: %(default)s.')

def main(self, *, args): # noqa: D102
# both all and topics cannot be true
if (args.all and (args.topics or args.regex)) or (args.topics and args.regex):
return print_error('Must specify only one option out of topics, --regex or --all')
# one out of "all", "topics" and "regex" must be true
if not(args.all or (args.topics and len(args.topics) > 0) or (args.regex)):
return print_error('Invalid choice: Must specify topic(s), --regex or --all')
# One options out of --all, --all-topics, --all-services, --services, topics or --regex
# must be used
if not (args.all or args.all_topics or args.all_services or
args.services or (args.topics and len(args.topics) > 0) or args.regex):
return print_error('Must specify only one option out of --all, --all-topics, '
'--all-services, --services, topics and --regex')

# Only one option out of --all, --all-services --services or --regex can be used
if (args.all and args.all_services) or \
((args.all or args.all_services) and args.regex) or \
((args.all or args.all_services or args.regex) and args.services):
return print_error('Must specify only one option out of --all, --all-services, '
'--services or --regex')

if args.topics and args.exclude:
return print_error('--exclude argument cannot be used when specifying a list '
'of topics explicitly')
# Only one option out of --all, --all-topics, topics or --regex can be used
if (args.all and args.all_topics) or \
((args.all or args.all_topics) and args.regex) or \
((args.all or args.all_topics or args.regex) and args.topics):
return print_error('Must specify only one option out of --all, --all-topics, '
'topics or --regex')

if args.exclude and not(args.regex or args.all):
return print_error('--exclude argument requires either --all or --regex')
if args.exclude_topics and not (args.regex or args.all or args.all_topics):
return print_error('--exclude-topics argument requires either --all, --all-topics '
'or --regex')

if args.exclude_services and not (args.regex or args.all or args.all_services):
return print_error('--exclude-services argument requires either --all, --all-services '
'or --regex')

uri = args.output or datetime.datetime.now().strftime('rosbag2_%Y_%m_%d-%H_%M_%S')

Expand Down Expand Up @@ -232,14 +263,15 @@ def main(self, *, args): # noqa: D102
custom_data=custom_data
)
record_options = RecordOptions()
record_options.all = args.all
record_options.all_topics = args.all_topics or args.all
record_options.is_discovery_disabled = args.no_discovery
record_options.topics = args.topics
record_options.rmw_serialization_format = args.serialization_format
record_options.topic_polling_interval = datetime.timedelta(
milliseconds=args.polling_interval)
record_options.regex = args.regex
record_options.exclude = args.exclude
record_options.exclude_topics = args.exclude_topics
record_options.exclude_services = args.exclude_services
record_options.node_prefix = NODE_NAME_PREFIX
record_options.compression_mode = args.compression_mode
record_options.compression_format = args.compression_format
Expand All @@ -251,6 +283,15 @@ def main(self, *, args): # noqa: D102
record_options.start_paused = args.start_paused
record_options.ignore_leaf_topics = args.ignore_leaf_topics
record_options.use_sim_time = args.use_sim_time
record_options.all_services = args.all_services or args.all

# Convert service name to service event topic name
services = []
if args.services and len(args.services) != 0:
for s in args.services:
name = '/' + s if s[0] != '/' else s
services.append(name + '/_service_event')
record_options.services = services

recorder = Recorder()

Expand Down
9 changes: 8 additions & 1 deletion rosbag2_cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ add_library(${PROJECT_NAME} SHARED
src/rosbag2_cpp/types/introspection_message.cpp
src/rosbag2_cpp/writer.cpp
src/rosbag2_cpp/writers/sequential_writer.cpp
src/rosbag2_cpp/reindexer.cpp)
src/rosbag2_cpp/reindexer.cpp
src/rosbag2_cpp/service_utils.cpp)

target_link_libraries(${PROJECT_NAME}
PUBLIC
Expand Down Expand Up @@ -257,6 +258,12 @@ if(BUILD_TESTING)
if(TARGET test_time_controller_clock)
target_link_libraries(test_time_controller_clock ${PROJECT_NAME})
endif()

ament_add_gmock(test_service_utils
test/rosbag2_cpp/test_service_utils.cpp)
if(TARGET test_service_utils)
target_link_libraries(test_service_utils ${PROJECT_NAME})
endif()
endif()

ament_package()
14 changes: 14 additions & 0 deletions rosbag2_cpp/include/rosbag2_cpp/info.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
#ifndef ROSBAG2_CPP__INFO_HPP_
#define ROSBAG2_CPP__INFO_HPP_

#include <memory>
#include <string>
#include <vector>

#include "rosbag2_cpp/visibility_control.hpp"

Expand All @@ -24,13 +26,25 @@
namespace rosbag2_cpp
{

typedef struct
{
std::string name;
std::string type;
std::string serialization_format;
size_t request_count;
size_t response_count;
} service_info;

class ROSBAG2_CPP_PUBLIC Info
{
public:
virtual ~Info() = default;

virtual rosbag2_storage::BagMetadata read_metadata(
const std::string & uri, const std::string & storage_id = "");

virtual std::vector<std::shared_ptr<service_info>> read_service_info(
const std::string & uri, const std::string & storage_id = "");
};

} // namespace rosbag2_cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,20 @@ class ROSBAG2_CPP_PUBLIC LocalMessageDefinitionSource final
public:
/**
* Concatenate the message definition with its dependencies into a self-contained schema.
* The format is different for MSG and IDL definitions, and is described fully in
* The format is different for MSG/SRV and IDL definitions, and is described fully in
* docs/message_definition_encoding.md
* For SRV type, root_type must include a string '/srv/'.
* Throws DefinitionNotFoundError if one or more definition files are missing for the given
* package resource name.
*/
rosbag2_storage::MessageDefinition get_full_text(const std::string & root_topic_type);
rosbag2_storage::MessageDefinition get_full_text(const std::string & root_type);

enum struct Format
{
UNKNOWN = 0,
MSG = 1,
IDL = 2,
SRV = 3,
};

explicit LocalMessageDefinitionSource() = default;
Expand Down
31 changes: 31 additions & 0 deletions rosbag2_cpp/include/rosbag2_cpp/service_utils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2023 Sony Group Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef ROSBAG2_CPP__SERVICE_UTILS_HPP_
#define ROSBAG2_CPP__SERVICE_UTILS_HPP_

#include <string>

namespace rosbag2_cpp
{
bool is_service_event_topic(const std::string & topic, const std::string & topic_type);

std::string service_event_topic_name_to_service_name(const std::string & topic_name);

std::string service_event_topic_type_to_service_type(const std::string & topic_type);

size_t get_serialization_size_for_service_metadata_event();
}

#endif // ROSBAG2_CPP__SERVICE_UTILS_HPP_
Loading

0 comments on commit bd6e02b

Please sign in to comment.