Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Readable and Writable storage #18

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
1427d7f
Fix build and uncrustify
botteroa-si Aug 2, 2018
ef2f8db
Add readable and writable storage interfaces
botteroa-si Aug 2, 2018
a924d48
Delete first storage interface proposal and adapt storage factory to …
botteroa-si Aug 2, 2018
099d89c
Modify test to work with new storage interfaces
botteroa-si Aug 2, 2018
95e6307
Prevent pluginlib from using boost
Martin-Idel-SI Aug 6, 2018
f757f39
Introduce better logging using rcutils in rosbag_storage
botteroa-si Aug 8, 2018
2a8b1fb
Add visibility control for Windows in rosbag_storage
botteroa-si Aug 7, 2018
7fd2593
Rename visibility_control.h to visibility_control.hpp
botteroa-si Aug 7, 2018
be92a5d
Fix package.xml in rosbag2_storage
botteroa-si Aug 8, 2018
9e70eb5
Cleanup CMakeLists in rosbag2_storage
Martin-Idel-SI Aug 7, 2018
695d5dd
Adapt read() and write() methods signature
botteroa-si Aug 2, 2018
800cb4a
Use void * instead of char * in rosbag_storage
botteroa-si Aug 8, 2018
cd129c4
Add storage facade for plugins which are both readable and writable
Martin-Idel-SI Aug 8, 2018
88977b1
Extract bag_info struct to own file
Martin-Idel-SI Aug 8, 2018
d9e33e3
Change storage interface to have read/write access
Martin-Idel-SI Aug 9, 2018
8410c7e
Add visibility to template specializations
Martin-Idel-SI Aug 10, 2018
4b806f4
Remove no longer necessary File install from CMakeLists.txt
botteroa-si Aug 10, 2018
e3e8fe9
Refactor storage_factory_impl.hpp
botteroa-si Aug 10, 2018
6f89010
Minor refactoring
botteroa-si Aug 10, 2018
bc1044e
Fix Windows warning
botteroa-si Aug 10, 2018
8eede3b
Add COLCON_IGNORE files to irrelevant projects
botteroa-si Aug 10, 2018
ef63977
Simpler class hierarchy without WritableStorage
Martin-Idel-SI Aug 13, 2018
568e1d9
Use exceptions instead of bool returns everywhere in interface
Martin-Idel-SI Aug 13, 2018
262f598
Change rosbag2_storage interface
Martin-Idel-SI Aug 13, 2018
177600f
Adapt sqlite3 plugin to new interface and extract rosbag2 part to own…
botteroa-si Aug 2, 2018
6564ac5
Update plugin_description.xml and write() method
botteroa-si Aug 8, 2018
d3f6f3f
Remove Sqlite dependencies from rosbag2 tests
botteroa-si Aug 7, 2018
7fb8bb8
Add tests to rosbag2_storage_default_plugins
botteroa-si Aug 7, 2018
9491cc3
Adapt interface and introduce better logging
botteroa-si Aug 8, 2018
81fb249
Adapt copyright and use copyright linter
Martin-Idel-SI Aug 9, 2018
a861461
Add plugin development documentation
Martin-Idel-SI Aug 6, 2018
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
55 changes: 55 additions & 0 deletions docs/plugin_development.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Writing storage plugins

There are different interfaces for storage plugins depending on your need: The general `ReadWriteStorage` and the more specific `ReadableStorage`.

## Writing a general plugin

Assume you write a plugin `MyStorage` which can both save messages and read messages.
Its header file could be `my_storage.hpp` and it would derive from `rosbag2_storage::ReadWriteStorage`.
While implementing the interface provided by `rosbag2_storage::ReadWriteStorage`, make sure that all resources such as file handles or database connections are closed or destroyed in the destructor, no additional `close` call should be necessary.

In order to find the plugin at runtime, it needs to be exported to the pluginlib.
Add the following lines to `my_storage.cpp`:

```
#include "pluginlib/class_list_macros.hpp"
PLUGINLIB_EXPORT_CLASS(MyStorage, rosbag2_storage::ReadWriteStorage)
```

Furthermore, we need some meta-information in the form of a `plugin_description.xml` file.
Here, it contains

```
<library path="my_storage_lib">
<class name="my_storage" type="MyStorage" base_class_type="rosbag2_storage::ReadWriteStorage">
</class>
</library>
```
Here, `my_storage_lib` is the name of the library while `my_storage` is an identifier used by the pluginlib to load it.

In addition, in the `CMakeLists.txt` plugins and `plugin_description` file need to be added to the index to be found at runtime:

`pluginlib_export_plugin_description_file(rosbag2_storage plugin_description.xml)`

The first argument `rosbag2_storage` denotes the library we add our plugin to, while the second argument is the path to the plugin description file.

## Writing a plugin for reading only

When writing plugins to only provide functionality for reading, derive from `rosbag2_storage::ReadableStorage`.

If the read-only plugin is called `my_readonly_storage` in a library `my_storage_lib`, it will be registered using

```
#include "pluginlib/class_list_macros.hpp"
PLUGINLIB_EXPORT_CLASS(MyReadonlyStorage, rosbag2_storage::ReadableStorage)
```
with the plugin description
```
<library path="my_storage_lib">
<class name="my_readonly_storage" type="MyReadonlyStorage" base_class_type="rosbag2_storage::ReadableStorage">
</class>
</library>
```
and the usual pluginlib export in the CMakeLists:

`pluginlib_export_plugin_description_file(rosbag2_storage plugin_description.xml)`
File renamed without changes.
59 changes: 48 additions & 11 deletions rosbag2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,61 @@ if(NOT CMAKE_CXX_STANDARD)
endif()

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
add_compile_options(-Wall -Wextra -Wpedantic -Werror)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
# uncomment the following section in order to fill in
# further dependencies manually.
# find_package(<dependency> REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rcutils REQUIRED)
find_package(std_msgs REQUIRED)
find_package(rosbag2_storage REQUIRED)

add_library(librosbag
src/rosbag2/rosbag2.cpp)

ament_target_dependencies(librosbag rclcpp rcutils std_msgs rosbag2_storage)
target_include_directories(librosbag
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)

add_executable(${PROJECT_NAME}_record src/rosbag2/demo_record.cpp)
target_link_libraries(${PROJECT_NAME}_record librosbag)

add_executable(${PROJECT_NAME}_play src/rosbag2/demo_play.cpp)
target_link_libraries(${PROJECT_NAME}_play librosbag)

install(
DIRECTORY include/
DESTINATION include)

install(
TARGETS librosbag
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib)

if(BUILD_TESTING)
find_package(ament_cmake_gtest REQUIRED)
find_package(ament_cmake_gmock REQUIRED)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# remove the line when a copyright and license is present in all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# remove the line when this package is a git repo
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()

ament_add_gmock(rosbag2_write_integration_test
test/rosbag2/rosbag2_write_integration_test.cpp
test/rosbag2/rosbag2_test_fixture.hpp
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
if(TARGET rosbag2_write_integration_test)
target_link_libraries(rosbag2_write_integration_test librosbag)
endif()

ament_add_gmock(rosbag2_read_integration_test
test/rosbag2/rosbag2_read_integration_test.cpp
test/rosbag2/rosbag2_test_fixture.hpp
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
if(TARGET rosbag2_read_integration_test)
target_link_libraries(rosbag2_read_integration_test librosbag)
endif()
endif()

ament_package()
36 changes: 36 additions & 0 deletions rosbag2/include/rosbag2/rosbag2.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2018, Bosch Software Innovations GmbH.
//
// 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__ROSBAG2_HPP_
#define ROSBAG2__ROSBAG2_HPP_

#include <functional>
#include <string>

namespace rosbag2
{

class Rosbag2
{
public:
void record(
const std::string & file_name,
const std::string & topic_name,
std::function<void(void)> after_write_action = nullptr);
void play(const std::string & file_name, const std::string & topic_name);
};

} // namespace rosbag2

#endif // ROSBAG2__ROSBAG2_HPP_
6 changes: 6 additions & 0 deletions rosbag2/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@

<buildtool_depend>ament_cmake</buildtool_depend>

<build_depend>rosbag2_storage</build_depend>
<build_depend>rcutils</build_depend>

<exec_depend>rosbag2_storage</exec_depend>
<exec_depend>rcutils</exec_depend>

<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

Expand Down
29 changes: 29 additions & 0 deletions rosbag2/src/rosbag2/demo_play.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2018, Bosch Software Innovations GmbH.
//
// 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.

#include "rclcpp/rclcpp.hpp"

#include "rosbag2/rosbag2.hpp"

int main(int argc, const char ** argv)
{
rclcpp::init(argc, argv);

rosbag2::Rosbag2 rosbag2;
rosbag2.play("test.bag", "string_topic");

rclcpp::shutdown();

return 0;
}
37 changes: 37 additions & 0 deletions rosbag2/src/rosbag2/demo_record.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2018, Bosch Software Innovations GmbH.
//
// 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.

#include <cstdio>
#include <string>

#include "rclcpp/rclcpp.hpp"

#include "rosbag2/rosbag2.hpp"

int main(int argc, const char ** argv)
{
// TODO(anhosi): allow output file to be specified by cli argument and do proper checking if
// file already exists
std::string filename("test.bag");
std::remove(filename.c_str());

rclcpp::init(argc, argv);

rosbag2::Rosbag2 rosbag2;
rosbag2.record(filename, "string_topic");

rclcpp::shutdown();

return 0;
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,42 @@
/*
* Copyright (c) 2018, Bosch Software Innovations GmbH.
*
* 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.
*/
// Copyright 2018, Bosch Software Innovations GmbH.
//
// 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.

#include "rosbag2/rosbag2.hpp"

#include <memory>
#include <string>

#include "rclcpp/rclcpp.hpp"
#include "rcutils/logging_macros.h"
#include "std_msgs/msg/string.hpp"

#include "storage/storage_factory.hpp"
#include "storage/sqlite/sqlite_storage.hpp"
#include "rosbag2_storage/storage_factory.hpp"

namespace rosbag2
{

const char * ROS_PACKAGE_NAME = "rosbag2";

void Rosbag2::record(
const std::string & file_name,
const std::string & topic_name,
std::function<void(void)> after_write_action)
{
StorageFactory factory;
std::unique_ptr<WritableStorage> storage = factory.get_for_writing(file_name);
rosbag2_storage::StorageFactory factory;
auto storage =
factory.get_read_write_storage("sqlite3", file_name);
storage->create_topic();

if (storage) {
auto node = std::make_shared<rclcpp::Node>("rosbag_node");
Expand All @@ -46,22 +49,21 @@ void Rosbag2::record(
}
});

// TODO(anhosi): use proper logging from rcutils
std::cout << "Waiting for messages..." << std::endl;
RCUTILS_LOG_INFO_NAMED(ROS_PACKAGE_NAME, "Waiting for messages...");
rclcpp::spin(node);
}
}

void Rosbag2::play(const std::string & file_name, const std::string & topic_name)
{
StorageFactory factory;
std::unique_ptr<ReadableStorage> storage = factory.get_for_reading(file_name);
rosbag2_storage::StorageFactory factory;
auto storage = factory.get_read_only_storage("sqlite3", file_name);

if (storage) {
auto messages = storage->get_messages();
auto node = std::make_shared<rclcpp::Node>("rosbag_publisher_node");
auto publisher = node->create_publisher<std_msgs::msg::String>(topic_name);
for (const auto & message : messages) {
while (storage->has_next()) {
std::string message = storage->read_next();
auto string_msg = std_msgs::msg::String();
string_msg.data = message;
// without the sleep_for() many messages are lost.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
/*
* Copyright (c) 2018, Bosch Software Innovations GmbH.
*
* 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.
*/
// Copyright 2018, Bosch Software Innovations GmbH.
//
// 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.

#include <gmock/gmock.h>

Expand Down
Loading