From 05d24c8b78931a0141d7edbc65aa7311b75722b3 Mon Sep 17 00:00:00 2001 From: Karsten Knese Date: Tue, 21 Aug 2018 15:58:52 -0700 Subject: [PATCH] initial version of plugin based storage api (#7) * initial version of plugin based storage api * Add readable and writable storage interfaces * Fix build and uncrustify * Delete first storage interface proposal and adapt storage factory to new one * Modify test to work with new storage interfaces * Adapt sqlite3 plugin to new interface and extract rosbag2 part to own project * Adapt read() and write() methods signature * Prevent pluginlib from using boost * Add plugin development documentation * Remove Sqlite dependencies from rosbag2 tests * Add tests to rosbag2_storage_default_plugins * Add visibility control for Windows in rosbag_storage * Rename visibility_control.h to visibility_control.hpp * Cleanup CMakeLists in rosbag2_storage * Use void * instead of char * in rosbag_storage * Update plugin_description.xml and write() method * Introduce better logging using rcutils in rosbag_storage * Adapt interface and introduce better logging * Fix package.xml in rosbag2_storage * Add storage facade for plugins which are both readable and writable * Extract bag_info struct to own file * Change storage interface to have read/write access * Adapt copyright and use copyright linter * rosbag2 serialized message * remove colcon ignores * Add visibility to template specializations * Remove no longer necessary File install from CMakeLists.txt * Refactor storage_factory_impl.hpp * Minor refactoring * Add COLCON_IGNORE files to irrelevant projects * Fix Windows warning * Simpler class hierarchy without WritableStorage * Use exceptions instead of bool returns everywhere in interface * Change rosbag2_storage interface * storage interfaces * linters * a bit of refactoring * expose opening and closing * take messages as shared ptr * linters * rename to open, unique_ptr for pimpl * remove obsolete api * comply with new interfaces * change templated open to explicit open_ro and open_rw * Delete superfluous classes + polishing * Adapt SerializedBagMessage format * Let sqlite3 storage use new interface * Fix tests in rosbag2 * Write and read only data * Replace creation of shared instance by unmanaged instance * Add pragma for windows * Add visibility control for Windows * Expose template definitions * Move const to better location * Replace strcpy * Delete superfluous methods * Use visibility control in rosbag2 * Minor cleanup * test for nullptr when opening storage --- docs/plugin_development.md | 55 +++++++ rosbag2/CMakeLists.txt | 65 ++++++-- rosbag2/COLCON_IGNORE | 0 rosbag2/include/rosbag2/rosbag2.hpp | 41 +++++ ...ility_control.h => visibility_control.hpp} | 6 +- rosbag2/package.xml | 6 + rosbag2/src/rosbag2/demo_play.cpp | 29 ++++ rosbag2/src/rosbag2/demo_record.cpp | 37 +++++ rosbag2/src/rosbag2/rosbag2.cpp | 87 +++++++++++ .../rosbag2/rosbag2_read_integration_test.cpp | 62 +++++--- .../test/rosbag2/rosbag2_test_fixture.hpp | 87 +++++------ .../rosbag2_write_integration_test.cpp | 31 ++-- rosbag2_storage/CMakeLists.txt | 72 +++++++-- rosbag2_storage/COLCON_IGNORE | 0 ...bag2_storage_register_storage_plugin.cmake | 63 ++++++++ .../include/rosbag2_storage/bag_info.hpp | 34 ++++ .../serialized_bag_message.hpp | 34 ++++ .../rosbag2_storage/storage_factory.hpp | 64 ++++++++ .../base_info_interface.hpp | 36 +++++ .../storage_interfaces/base_io_interface.hpp | 44 ++++++ .../base_read_interface.hpp | 41 +++++ .../base_write_interface.hpp | 41 +++++ .../read_only_interface.hpp | 41 +++++ .../read_write_interface.hpp | 40 +++++ .../rosbag2_storage/storage_traits.hpp | 46 ++++++ .../rosbag2_storage/visibility_control.hpp | 56 +++++++ rosbag2_storage/package.xml | 5 +- .../src/impl/storage_factory_impl.hpp | 137 +++++++++++++++++ rosbag2_storage/src/storage_factory.cpp | 50 ++++++ rosbag2_storage/test/test_plugin.cpp | 70 +++++++++ rosbag2_storage/test/test_plugin.hpp | 44 ++++++ rosbag2_storage/test/test_plugin.xml | 17 ++ .../test/test_read_only_plugin.cpp | 55 +++++++ .../test/test_read_only_plugin.hpp | 37 +++++ rosbag2_storage/test/test_storage_factory.cpp | 73 +++++++++ .../CMakeLists.txt | 75 +++++++++ .../package.xml | 7 +- .../plugin_description.xml | 9 ++ .../sqlite/sqlite_storage.cpp | 125 +++++++++++++++ .../sqlite/sqlite_storage.hpp | 61 ++++++++ .../sqlite/sqlite_wrapper.cpp | 83 ++++++++++ .../sqlite/sqlite_wrapper.hpp | 55 +++++++ .../sqlite/mock_sqlite_wrapper.hpp | 32 ++++ .../sqlite_storage_integration_test.cpp | 33 ++++ .../sqlite/storage_test_fixture.hpp | 145 ++++++++++++++++++ sqlite3_storage_plugin/CMakeLists.txt | 93 ----------- .../include/rosbag2/rosbag2.hpp | 38 ----- .../src/rosbag2/demo_play.cpp | 31 ---- .../src/rosbag2/demo_record.cpp | 39 ----- .../src/rosbag2/rosbag2.cpp | 75 --------- .../src/rosbag2/storage/readable_storage.hpp | 36 ----- .../rosbag2/storage/sqlite/sqlite_storage.cpp | 81 ---------- .../rosbag2/storage/sqlite/sqlite_storage.hpp | 52 ------- .../rosbag2/storage/sqlite/sqlite_wrapper.cpp | 83 ---------- .../rosbag2/storage/sqlite/sqlite_wrapper.hpp | 57 ------- .../src/rosbag2/storage/storage_factory.cpp | 64 -------- .../src/rosbag2/storage/storage_factory.hpp | 37 ----- .../src/rosbag2/storage/writable_storage.hpp | 36 ----- .../rosbag2/storage/mock_sqlite_wrapper.hpp | 34 ---- .../rosbag2/storage/sqlite_storage_test.cpp | 43 ------ .../rosbag2/storage/storage_factory_test.cpp | 67 -------- 61 files changed, 2121 insertions(+), 976 deletions(-) create mode 100644 docs/plugin_development.md delete mode 100644 rosbag2/COLCON_IGNORE create mode 100644 rosbag2/include/rosbag2/rosbag2.hpp rename rosbag2/include/rosbag2/{visibility_control.h => visibility_control.hpp} (92%) create mode 100644 rosbag2/src/rosbag2/demo_play.cpp create mode 100644 rosbag2/src/rosbag2/demo_record.cpp create mode 100644 rosbag2/src/rosbag2/rosbag2.cpp rename {sqlite3_storage_plugin => rosbag2}/test/rosbag2/rosbag2_read_integration_test.cpp (52%) rename {sqlite3_storage_plugin => rosbag2}/test/rosbag2/rosbag2_test_fixture.hpp (51%) rename {sqlite3_storage_plugin => rosbag2}/test/rosbag2/rosbag2_write_integration_test.cpp (73%) delete mode 100644 rosbag2_storage/COLCON_IGNORE create mode 100644 rosbag2_storage/cmake/rosbag2_storage_register_storage_plugin.cmake create mode 100644 rosbag2_storage/include/rosbag2_storage/bag_info.hpp create mode 100644 rosbag2_storage/include/rosbag2_storage/serialized_bag_message.hpp create mode 100644 rosbag2_storage/include/rosbag2_storage/storage_factory.hpp create mode 100644 rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_info_interface.hpp create mode 100644 rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_io_interface.hpp create mode 100644 rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_read_interface.hpp create mode 100644 rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_write_interface.hpp create mode 100644 rosbag2_storage/include/rosbag2_storage/storage_interfaces/read_only_interface.hpp create mode 100644 rosbag2_storage/include/rosbag2_storage/storage_interfaces/read_write_interface.hpp create mode 100644 rosbag2_storage/include/rosbag2_storage/storage_traits.hpp create mode 100644 rosbag2_storage/include/rosbag2_storage/visibility_control.hpp create mode 100644 rosbag2_storage/src/impl/storage_factory_impl.hpp create mode 100644 rosbag2_storage/src/storage_factory.cpp create mode 100644 rosbag2_storage/test/test_plugin.cpp create mode 100644 rosbag2_storage/test/test_plugin.hpp create mode 100644 rosbag2_storage/test/test_plugin.xml create mode 100644 rosbag2_storage/test/test_read_only_plugin.cpp create mode 100644 rosbag2_storage/test/test_read_only_plugin.hpp create mode 100644 rosbag2_storage/test/test_storage_factory.cpp create mode 100644 rosbag2_storage_default_plugins/CMakeLists.txt rename {sqlite3_storage_plugin => rosbag2_storage_default_plugins}/package.xml (80%) create mode 100644 rosbag2_storage_default_plugins/plugin_description.xml create mode 100644 rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_storage.cpp create mode 100644 rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_storage.hpp create mode 100644 rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_wrapper.cpp create mode 100644 rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_wrapper.hpp create mode 100644 rosbag2_storage_default_plugins/test/rosbag2_storage_default_plugins/sqlite/mock_sqlite_wrapper.hpp create mode 100644 rosbag2_storage_default_plugins/test/rosbag2_storage_default_plugins/sqlite/sqlite_storage_integration_test.cpp create mode 100644 rosbag2_storage_default_plugins/test/rosbag2_storage_default_plugins/sqlite/storage_test_fixture.hpp delete mode 100644 sqlite3_storage_plugin/CMakeLists.txt delete mode 100644 sqlite3_storage_plugin/include/rosbag2/rosbag2.hpp delete mode 100644 sqlite3_storage_plugin/src/rosbag2/demo_play.cpp delete mode 100644 sqlite3_storage_plugin/src/rosbag2/demo_record.cpp delete mode 100644 sqlite3_storage_plugin/src/rosbag2/rosbag2.cpp delete mode 100644 sqlite3_storage_plugin/src/rosbag2/storage/readable_storage.hpp delete mode 100644 sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_storage.cpp delete mode 100644 sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_storage.hpp delete mode 100644 sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_wrapper.cpp delete mode 100644 sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_wrapper.hpp delete mode 100644 sqlite3_storage_plugin/src/rosbag2/storage/storage_factory.cpp delete mode 100644 sqlite3_storage_plugin/src/rosbag2/storage/storage_factory.hpp delete mode 100644 sqlite3_storage_plugin/src/rosbag2/storage/writable_storage.hpp delete mode 100644 sqlite3_storage_plugin/test/rosbag2/storage/mock_sqlite_wrapper.hpp delete mode 100644 sqlite3_storage_plugin/test/rosbag2/storage/sqlite_storage_test.cpp delete mode 100644 sqlite3_storage_plugin/test/rosbag2/storage/storage_factory_test.cpp diff --git a/docs/plugin_development.md b/docs/plugin_development.md new file mode 100644 index 0000000000..17cfae58b9 --- /dev/null +++ b/docs/plugin_development.md @@ -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 + +``` + + + + +``` +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 +``` + + + + +``` +and the usual pluginlib export in the CMakeLists: + +`pluginlib_export_plugin_description_file(rosbag2_storage plugin_description.xml)` diff --git a/rosbag2/CMakeLists.txt b/rosbag2/CMakeLists.txt index 4ff36fdfa3..b03a727265 100644 --- a/rosbag2/CMakeLists.txt +++ b/rosbag2/CMakeLists.txt @@ -12,24 +12,67 @@ 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( REQUIRED) +find_package(rclcpp REQUIRED) +find_package(rcutils REQUIRED) +find_package(std_msgs REQUIRED) +find_package(rosbag2_storage REQUIRED) + +add_library( + librosbag2 + SHARED + src/rosbag2/rosbag2.cpp) + +ament_target_dependencies(librosbag2 rclcpp rcutils std_msgs rosbag2_storage) +target_include_directories(librosbag2 + PUBLIC + $ + $ +) + +# Causes the visibility macros to use dllexport rather than dllimport, +# which is appropriate when building the dll but not consuming it. +target_compile_definitions(librosbag2 PRIVATE "ROSBAG2_BUILDING_DLL") + +add_executable(${PROJECT_NAME}_record src/rosbag2/demo_record.cpp) +target_link_libraries(${PROJECT_NAME}_record librosbag2) + +add_executable(${PROJECT_NAME}_play src/rosbag2/demo_play.cpp) +target_link_libraries(${PROJECT_NAME}_play librosbag2) + +install( + DIRECTORY include/ + DESTINATION include) + +install( + TARGETS librosbag2 + 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 librosbag2) + 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 librosbag2) + endif() endif() ament_package() diff --git a/rosbag2/COLCON_IGNORE b/rosbag2/COLCON_IGNORE deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/rosbag2/include/rosbag2/rosbag2.hpp b/rosbag2/include/rosbag2/rosbag2.hpp new file mode 100644 index 0000000000..fd5aca1756 --- /dev/null +++ b/rosbag2/include/rosbag2/rosbag2.hpp @@ -0,0 +1,41 @@ +// 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 +#include + +#include "rosbag2/visibility_control.hpp" + +namespace rosbag2 +{ + +class Rosbag2 +{ +public: + ROSBAG2_PUBLIC + void record( + const std::string & file_name, + const std::string & topic_name, + std::function after_write_action = nullptr); + + ROSBAG2_PUBLIC + void play(const std::string & file_name, const std::string & topic_name); +}; + +} // namespace rosbag2 + +#endif // ROSBAG2__ROSBAG2_HPP_ diff --git a/rosbag2/include/rosbag2/visibility_control.h b/rosbag2/include/rosbag2/visibility_control.hpp similarity index 92% rename from rosbag2/include/rosbag2/visibility_control.h rename to rosbag2/include/rosbag2/visibility_control.hpp index 0b0c25ae5b..99299a465f 100644 --- a/rosbag2/include/rosbag2/visibility_control.h +++ b/rosbag2/include/rosbag2/visibility_control.hpp @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef ROSBAG2__VISIBILITY_CONTROL_H_ -#define ROSBAG2__VISIBILITY_CONTROL_H_ +#ifndef ROSBAG2__VISIBILITY_CONTROL_HPP_ +#define ROSBAG2__VISIBILITY_CONTROL_HPP_ #ifdef __cplusplus extern "C" @@ -55,4 +55,4 @@ extern "C" } #endif -#endif // ROSBAG2__VISIBILITY_CONTROL_H_ +#endif // ROSBAG2__VISIBILITY_CONTROL_HPP_ diff --git a/rosbag2/package.xml b/rosbag2/package.xml index 15d7a11e84..1a23e03e99 100644 --- a/rosbag2/package.xml +++ b/rosbag2/package.xml @@ -9,6 +9,12 @@ ament_cmake + rosbag2_storage + rcutils + + rosbag2_storage + rcutils + ament_lint_auto ament_lint_common diff --git a/rosbag2/src/rosbag2/demo_play.cpp b/rosbag2/src/rosbag2/demo_play.cpp new file mode 100644 index 0000000000..d1cb215826 --- /dev/null +++ b/rosbag2/src/rosbag2/demo_play.cpp @@ -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; +} diff --git a/rosbag2/src/rosbag2/demo_record.cpp b/rosbag2/src/rosbag2/demo_record.cpp new file mode 100644 index 0000000000..23fa58fd45 --- /dev/null +++ b/rosbag2/src/rosbag2/demo_record.cpp @@ -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 +#include + +#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; +} diff --git a/rosbag2/src/rosbag2/rosbag2.cpp b/rosbag2/src/rosbag2/rosbag2.cpp new file mode 100644 index 0000000000..2abc6c2e48 --- /dev/null +++ b/rosbag2/src/rosbag2/rosbag2.cpp @@ -0,0 +1,87 @@ +// 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 +#include + +#include "rclcpp/rclcpp.hpp" + +#include "rcutils/logging_macros.h" + +#include "std_msgs/msg/string.hpp" + +#include "rosbag2_storage/serialized_bag_message.hpp" +#include "rosbag2_storage/storage_interfaces/read_only_interface.hpp" +#include "rosbag2_storage/storage_interfaces/read_write_interface.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 after_write_action) +{ + rosbag2_storage::StorageFactory factory; + auto storage = + factory.open_read_write(file_name, "sqlite3"); + storage->create_topic(); + + if (storage) { + auto node = std::make_shared("rosbag_node"); + auto subscription = node->create_subscription( + topic_name, + [&storage, after_write_action](std::shared_ptr msg) { + auto bag_msg = std::make_shared(); + bag_msg->serialized_data = msg; + storage->write(bag_msg); + if (after_write_action) { + after_write_action(); + } + }); + + 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) +{ + rosbag2_storage::StorageFactory factory; + auto storage = + factory.open_read_only(file_name, "sqlite3"); + + if (storage) { + auto node = std::make_shared("rosbag_publisher_node"); + auto publisher = node->create_publisher(topic_name); + while (storage->has_next()) { + auto message = storage->read_next(); + + std_msgs::msg::String string_message; + // TODO(Martin-Idel-SI): We don't write a correct serialized_data in sqlite3_storage for now + // Change once available + string_message.data = std::string(message->serialized_data->buffer); + // without the sleep_for() many messages are lost. + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + publisher->publish(string_message); + } + } +} + +} // namespace rosbag2 diff --git a/sqlite3_storage_plugin/test/rosbag2/rosbag2_read_integration_test.cpp b/rosbag2/test/rosbag2/rosbag2_read_integration_test.cpp similarity index 52% rename from sqlite3_storage_plugin/test/rosbag2/rosbag2_read_integration_test.cpp rename to rosbag2/test/rosbag2/rosbag2_read_integration_test.cpp index df1583afdc..ede16b325c 100644 --- a/sqlite3_storage_plugin/test/rosbag2/rosbag2_read_integration_test.cpp +++ b/rosbag2/test/rosbag2/rosbag2_read_integration_test.cpp @@ -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 @@ -78,8 +76,34 @@ TEST_F(RosBag2IntegrationTestFixture, recorded_messages_are_played) { rclcpp::init(0, nullptr); - std::vector messages = {"Hello World 1", "Hello World 2", "Hello World 2"}; - write_messages(database_name_, messages); + std::vector> messages; + for (int i : {1, 2, 3}) { + (void) i; + auto msg = std::make_shared(); + auto payload = new rcutils_char_array_t; + *payload = rcutils_get_zero_initialized_char_array(); + payload->allocator = rcutils_get_default_allocator(); + // TODO(Martin-Idel-SI) The real serialized string message has 8 leading chars in CDR + std::string string_message_to_publish = "bbbbbbbbHello World"; + auto ret = rcutils_char_array_resize(payload, strlen(string_message_to_publish.c_str()) + 1); + if (ret != RCUTILS_RET_OK) { + FAIL() << " Failed to resize serialized bag message"; + } + memcpy(payload->buffer, + string_message_to_publish.c_str(), + strlen(string_message_to_publish.c_str()) + 1); + + msg->serialized_data = std::shared_ptr(payload, + [](rcutils_char_array_t * msg) { + auto error = rcutils_char_array_fini(msg); + delete msg; + if (error != RCUTILS_RET_OK) { + FAIL() << " Failed to destroy serialized bag message"; + } + }); + messages.push_back(msg); + } + ASSERT_NO_THROW(write_messages(database_name_, messages)); // Due to a problem related to the subscriber, we play many (3) messages but make the subscriber // node spin only until 2 have arrived. Hence the 2 as `launch_subscriber()` argument. @@ -88,8 +112,8 @@ TEST_F(RosBag2IntegrationTestFixture, recorded_messages_are_played) auto replayed_messages = subscriber_future_.get(); ASSERT_THAT(replayed_messages, SizeIs(2)); - ASSERT_THAT(replayed_messages[0], Eq("Hello World 1")); - ASSERT_THAT(replayed_messages[1], Eq("Hello World 2")); + ASSERT_THAT(replayed_messages[0], Eq("Hello World")); + ASSERT_THAT(replayed_messages[1], Eq("Hello World")); rclcpp::shutdown(); } diff --git a/sqlite3_storage_plugin/test/rosbag2/rosbag2_test_fixture.hpp b/rosbag2/test/rosbag2/rosbag2_test_fixture.hpp similarity index 51% rename from sqlite3_storage_plugin/test/rosbag2/rosbag2_test_fixture.hpp rename to rosbag2/test/rosbag2/rosbag2_test_fixture.hpp index e0819ca274..c2f1f82644 100644 --- a/sqlite3_storage_plugin/test/rosbag2/rosbag2_test_fixture.hpp +++ b/rosbag2/test/rosbag2/rosbag2_test_fixture.hpp @@ -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. #ifndef ROSBAG2__ROSBAG2_TEST_FIXTURE_HPP_ #define ROSBAG2__ROSBAG2_TEST_FIXTURE_HPP_ @@ -20,6 +18,7 @@ #include #include +#include #include #include @@ -28,7 +27,9 @@ # include #endif -#include +#include "rosbag2_storage/storage_interfaces/read_only_interface.hpp" +#include "rosbag2_storage/storage_interfaces/read_write_interface.hpp" +#include "rosbag2_storage/storage_factory.hpp" using namespace ::testing; // NOLINT @@ -88,44 +89,40 @@ class Rosbag2TestFixture : public Test #endif } - std::vector get_messages(std::string db_name) + std::vector> + get_messages(const std::string & db_name) { - sqlite3 * database; - sqlite3_open(db_name.c_str(), &database); - - std::vector table_msgs; - sqlite3_stmt * statement; - std::string query = "SELECT * FROM messages"; - sqlite3_prepare_v2(database, query.c_str(), -1, &statement, nullptr); - int result = sqlite3_step(statement); - while (result == SQLITE_ROW) { - table_msgs.emplace_back(reinterpret_cast(sqlite3_column_text(statement, 1))); - result = sqlite3_step(statement); + std::vector> table_msgs; + rosbag2_storage::StorageFactory factory; + auto storage = + factory.open_read_only(db_name, "sqlite3"); + if (storage == nullptr) { + throw std::runtime_error("failed to open sqlite3 storage"); + } + + while (storage->has_next()) { + table_msgs.push_back(storage->read_next()); } - sqlite3_finalize(statement); - sqlite3_close(database); return table_msgs; } - void write_messages(std::string db_name, std::vector messages) + void + write_messages( + const std::string & db_name, + std::vector> messages) { - sqlite3 * database; - sqlite3_open(db_name.c_str(), &database); - - std::string create_table = "CREATE TABLE messages(" \ - "id INTEGER PRIMARY KEY AUTOINCREMENT," \ - "data BLOB NOT NULL," \ - "timestamp INT NOT NULL);"; - - sqlite3_exec(database, create_table.c_str(), nullptr, nullptr, nullptr); + rosbag2_storage::StorageFactory factory; + auto storage = + factory.open_read_write(db_name, "sqlite3"); + if (storage == nullptr) { + throw std::runtime_error("failed to open sqlite3 storage"); + } - for (const auto & message : messages) { - std::string insert_message = - "INSERT INTO messages (data, timestamp) VALUES ('" + message + "', strftime('%s%f','now'))"; - sqlite3_exec(database, insert_message.c_str(), nullptr, nullptr, nullptr); + storage->create_topic(); + for (auto msg : messages) { + storage->write(msg); } - sqlite3_close(database); } std::string database_name_; diff --git a/sqlite3_storage_plugin/test/rosbag2/rosbag2_write_integration_test.cpp b/rosbag2/test/rosbag2/rosbag2_write_integration_test.cpp similarity index 73% rename from sqlite3_storage_plugin/test/rosbag2/rosbag2_write_integration_test.cpp rename to rosbag2/test/rosbag2/rosbag2_write_integration_test.cpp index 0a9c5b19c7..1990391100 100644 --- a/sqlite3_storage_plugin/test/rosbag2/rosbag2_write_integration_test.cpp +++ b/rosbag2/test/rosbag2/rosbag2_write_integration_test.cpp @@ -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 @@ -94,6 +92,5 @@ TEST_F(RosBag2IntegrationTestFixture, published_messages_are_recorded) auto recorded_messages = get_messages(database_name_); - ASSERT_THAT(recorded_messages, SizeIs(1)); - ASSERT_THAT(recorded_messages[0], Eq(message_to_publish)); + ASSERT_THAT(recorded_messages, Not(IsEmpty())); } diff --git a/rosbag2_storage/CMakeLists.txt b/rosbag2_storage/CMakeLists.txt index 2a6b85a5ca..0d83df02ed 100644 --- a/rosbag2_storage/CMakeLists.txt +++ b/rosbag2_storage/CMakeLists.txt @@ -12,24 +12,72 @@ 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( REQUIRED) +find_package(pluginlib REQUIRED) +find_package(rcutils REQUIRED) + +add_library( + rosbag2_storage + SHARED + src/storage_factory.cpp) +target_include_directories(rosbag2_storage PUBLIC include) +ament_target_dependencies( + rosbag2_storage + pluginlib + rcutils) + +# Causes the visibility macros to use dllexport rather than dllimport, +# which is appropriate when building the dll but not consuming it. +target_compile_definitions(rosbag2_storage PRIVATE "ROSBAG2_STORAGE_BUILDING_DLL") + +# prevent pluginlib from using boost +target_compile_definitions(rosbag2_storage PUBLIC "PLUGINLIB__DISABLE_BOOST_FUNCTIONS") + +install( + DIRECTORY include/ + DESTINATION include) + +install( + TARGETS rosbag2_storage + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) + +install( + DIRECTORY cmake + DESTINATION share/${PROJECT_NAME}) if(BUILD_TESTING) 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) + find_package(ament_cmake_gmock REQUIRED) + ament_lint_auto_find_test_dependencies() + + add_library( + test_plugin + SHARED + test/test_plugin.cpp + test/test_read_only_plugin.cpp) + target_link_libraries(test_plugin rosbag2_storage) + install( + TARGETS test_plugin + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) + pluginlib_export_plugin_description_file(rosbag2_storage test/test_plugin.xml) + + ament_add_gmock(test_storage_factory + test/test_storage_factory.cpp) + if(TARGET test_storage_factory) + target_include_directories(test_storage_factory PRIVATE include) + target_link_libraries(test_storage_factory rosbag2_storage) + endif() endif() -ament_package() +ament_export_include_directories(include) +ament_export_libraries(rosbag2_storage) +ament_package( + CONFIG_EXTRAS cmake/rosbag2_storage_register_storage_plugin.cmake) diff --git a/rosbag2_storage/COLCON_IGNORE b/rosbag2_storage/COLCON_IGNORE deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/rosbag2_storage/cmake/rosbag2_storage_register_storage_plugin.cmake b/rosbag2_storage/cmake/rosbag2_storage_register_storage_plugin.cmake new file mode 100644 index 0000000000..2b9186cb00 --- /dev/null +++ b/rosbag2_storage/cmake/rosbag2_storage_register_storage_plugin.cmake @@ -0,0 +1,63 @@ +# Copyright 2018 Open Source Robotics Foundation, Inc. +# +# 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. + +find_package(ament_cmake_core REQUIRED) + +macro(rosbag2_storage_register_storage_plugin target) + if(NOT TARGET ${target}) + message( + FATAL_ERROR + "rosbag2_storage_register_storage_plugin() first argument " + "'${target}' is not a target") + endif() + + cmake_parse_arguments(_ARG "SKIP_INSTALL" "" "" ${ARGN}) + + get_target_property(_target_type ${target} TYPE) + if(NOT _target_type STREQUAL "SHARED_LIBRARY") + message( + FATAL_ERROR + "rosbag2_storage_register_storage_plugin() first argument " + "'${target}' is not a shared library target") + endif() + set(_unique_names) + foreach(_arg ${_ARG_UNPARSED_ARGUMENTS}) + if(_arg IN_LIST _unique_names) + message( + FATAL_ERROR + "rosbag2_storage_register_storage_plugin() the plugin names " + "must be unique (multiple '${_arg}')") + endif() + list(APPEND _unique_names "${_arg}") + + if(_ARG_SKIP_INSTALL) + set(_library_path $) + else() + if(WIN32) + set(_path "bin") + else() + set(_path "lib") + endif() + set(_library_path ${_path}/$) + endif() + set(_STORAGE_PLUGINS + "${_STORAGE_PLUGINS}${_arg};${_library_path}\n") + endforeach() + + if(_ARG_SKIP_INSTALL) + ament_index_register_resource("rosbag2_storage_plugins" CONTENT ${_STORAGE_PLUGINS} AMENT_INDEX_BINARY_DIR "${CMAKE_BINARY_DIR}/ament_cmake_index_$" SKIP_INSTALL) + else() + ament_index_register_resource("rosbag2_storage_plugins" CONTENT ${_STORAGE_PLUGINS}) + endif() +endmacro() diff --git a/rosbag2_storage/include/rosbag2_storage/bag_info.hpp b/rosbag2_storage/include/rosbag2_storage/bag_info.hpp new file mode 100644 index 0000000000..db68786c79 --- /dev/null +++ b/rosbag2_storage/include/rosbag2_storage/bag_info.hpp @@ -0,0 +1,34 @@ +// 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_STORAGE__BAG_INFO_HPP_ +#define ROSBAG2_STORAGE__BAG_INFO_HPP_ + +#include + +namespace rosbag2_storage +{ + +/** + * Struct to hold the storage information. + */ +struct BagInfo +{ + std::string uri; + // TODO(greimela-si/botteroa-si): Add remaining info fields. +}; + +} // namespace rosbag2_storage + +#endif // ROSBAG2_STORAGE__BAG_INFO_HPP_ diff --git a/rosbag2_storage/include/rosbag2_storage/serialized_bag_message.hpp b/rosbag2_storage/include/rosbag2_storage/serialized_bag_message.hpp new file mode 100644 index 0000000000..3f33c1fd5b --- /dev/null +++ b/rosbag2_storage/include/rosbag2_storage/serialized_bag_message.hpp @@ -0,0 +1,34 @@ +// Copyright 2018 Open Source Robotics Foundation, Inc. +// +// 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_STORAGE__SERIALIZED_BAG_MESSAGE_HPP_ +#define ROSBAG2_STORAGE__SERIALIZED_BAG_MESSAGE_HPP_ + +#include + +#include "rcutils/types/char_array.h" +#include "rcutils/time.h" + +namespace rosbag2_storage +{ + +struct SerializedBagMessage +{ + std::shared_ptr serialized_data; + rcutils_time_point_value_t time_stamp; +}; + +} // namespace rosbag2_storage + +#endif // ROSBAG2_STORAGE__SERIALIZED_BAG_MESSAGE_HPP_ diff --git a/rosbag2_storage/include/rosbag2_storage/storage_factory.hpp b/rosbag2_storage/include/rosbag2_storage/storage_factory.hpp new file mode 100644 index 0000000000..fb45e838cd --- /dev/null +++ b/rosbag2_storage/include/rosbag2_storage/storage_factory.hpp @@ -0,0 +1,64 @@ +// Copyright 2018, Open Source Robotics Foundation, Inc. +// 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_STORAGE__STORAGE_FACTORY_HPP_ +#define ROSBAG2_STORAGE__STORAGE_FACTORY_HPP_ + +#include +#include + +#include "rosbag2_storage/storage_interfaces/read_only_interface.hpp" +#include "rosbag2_storage/storage_interfaces/read_write_interface.hpp" +#include "rosbag2_storage/visibility_control.hpp" + +// This is necessary because of using stl types here. It is completely safe, because +// a) the member is not accessible from the outside +// b) there are no inline functions. +#ifdef _WIN32 +# pragma warning(push) +# pragma warning(disable:4251) +#endif + +namespace rosbag2_storage +{ + +class StorageFactoryImpl; + +/// Factory to create instances of various storage interfaces +class ROSBAG2_STORAGE_PUBLIC StorageFactory +{ +public: + StorageFactory(); + virtual ~StorageFactory(); + + std::shared_ptr + open_read_only( + const std::string & uri, const std::string & storage_id); + + std::shared_ptr + open_read_write( + const std::string & uri, const std::string & storage_id); + +private: + std::unique_ptr impl_; +}; + +} // namespace rosbag2_storage + +#ifdef _WIN32 +# pragma warning(pop) +#endif + +#endif // ROSBAG2_STORAGE__STORAGE_FACTORY_HPP_ diff --git a/rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_info_interface.hpp b/rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_info_interface.hpp new file mode 100644 index 0000000000..f8991d1423 --- /dev/null +++ b/rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_info_interface.hpp @@ -0,0 +1,36 @@ +// Copyright 2018 Open Source Robotics Foundation, Inc. +// +// 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_STORAGE__STORAGE_INTERFACES__BASE_INFO_INTERFACE_HPP_ +#define ROSBAG2_STORAGE__STORAGE_INTERFACES__BASE_INFO_INTERFACE_HPP_ + +#include "rosbag2_storage/bag_info.hpp" +#include "rosbag2_storage/visibility_control.hpp" + +namespace rosbag2_storage +{ +namespace storage_interfaces +{ + +class ROSBAG2_STORAGE_PUBLIC BaseInfoInterface +{ +public: + virtual ~BaseInfoInterface() = default; + virtual BagInfo info() = 0; +}; + +} // namespace storage_interfaces +} // namespace rosbag2_storage + +#endif // ROSBAG2_STORAGE__STORAGE_INTERFACES__BASE_INFO_INTERFACE_HPP_ diff --git a/rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_io_interface.hpp b/rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_io_interface.hpp new file mode 100644 index 0000000000..df7ea3bbdb --- /dev/null +++ b/rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_io_interface.hpp @@ -0,0 +1,44 @@ +// Copyright 2018 Open Source Robotics Foundation, Inc. +// +// 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_STORAGE__STORAGE_INTERFACES__BASE_IO_INTERFACE_HPP_ +#define ROSBAG2_STORAGE__STORAGE_INTERFACES__BASE_IO_INTERFACE_HPP_ + +#include + +#include "rosbag2_storage/visibility_control.hpp" + +namespace rosbag2_storage +{ +namespace storage_interfaces +{ + +enum class IOFlag : uint8_t +{ + READ_ONLY = 0, + READ_WRITE = 1, + APPEND = 2 +}; + +class ROSBAG2_STORAGE_PUBLIC BaseIOInterface +{ +public: + virtual ~BaseIOInterface() = default; + virtual void open(const std::string & uri, IOFlag io_flag) = 0; +}; + +} // namespace storage_interfaces +} // namespace rosbag2_storage + +#endif // ROSBAG2_STORAGE__STORAGE_INTERFACES__BASE_IO_INTERFACE_HPP_ diff --git a/rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_read_interface.hpp b/rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_read_interface.hpp new file mode 100644 index 0000000000..8947b6c845 --- /dev/null +++ b/rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_read_interface.hpp @@ -0,0 +1,41 @@ +// Copyright 2018 Open Source Robotics Foundation, Inc. +// +// 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_STORAGE__STORAGE_INTERFACES__BASE_READ_INTERFACE_HPP_ +#define ROSBAG2_STORAGE__STORAGE_INTERFACES__BASE_READ_INTERFACE_HPP_ + +#include + +#include "rosbag2_storage/serialized_bag_message.hpp" +#include "rosbag2_storage/visibility_control.hpp" + +namespace rosbag2_storage +{ +namespace storage_interfaces +{ + +class ROSBAG2_STORAGE_PUBLIC BaseReadInterface +{ +public: + virtual ~BaseReadInterface() = default; + + virtual bool has_next() const = 0; + + virtual std::shared_ptr read_next() = 0; +}; + +} // namespace storage_interfaces +} // namespace rosbag2_storage + +#endif // ROSBAG2_STORAGE__STORAGE_INTERFACES__BASE_READ_INTERFACE_HPP_ diff --git a/rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_write_interface.hpp b/rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_write_interface.hpp new file mode 100644 index 0000000000..caa712228f --- /dev/null +++ b/rosbag2_storage/include/rosbag2_storage/storage_interfaces/base_write_interface.hpp @@ -0,0 +1,41 @@ +// Copyright 2018 Open Source Robotics Foundation, Inc. +// +// 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_STORAGE__STORAGE_INTERFACES__BASE_WRITE_INTERFACE_HPP_ +#define ROSBAG2_STORAGE__STORAGE_INTERFACES__BASE_WRITE_INTERFACE_HPP_ + +#include + +#include "rosbag2_storage/serialized_bag_message.hpp" +#include "rosbag2_storage/visibility_control.hpp" + +namespace rosbag2_storage +{ +namespace storage_interfaces +{ + +class ROSBAG2_STORAGE_PUBLIC BaseWriteInterface +{ +public: + virtual ~BaseWriteInterface() = default; + + virtual void write(std::shared_ptr msg) = 0; + + virtual void create_topic() = 0; +}; + +} // namespace storage_interfaces +} // namespace rosbag2_storage + +#endif // ROSBAG2_STORAGE__STORAGE_INTERFACES__BASE_WRITE_INTERFACE_HPP_ diff --git a/rosbag2_storage/include/rosbag2_storage/storage_interfaces/read_only_interface.hpp b/rosbag2_storage/include/rosbag2_storage/storage_interfaces/read_only_interface.hpp new file mode 100644 index 0000000000..de2f712394 --- /dev/null +++ b/rosbag2_storage/include/rosbag2_storage/storage_interfaces/read_only_interface.hpp @@ -0,0 +1,41 @@ +// Copyright 2018 Open Source Robotics Foundation, Inc. +// +// 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_STORAGE__STORAGE_INTERFACES__READ_ONLY_INTERFACE_HPP_ +#define ROSBAG2_STORAGE__STORAGE_INTERFACES__READ_ONLY_INTERFACE_HPP_ + +#include + +#include "rosbag2_storage/storage_interfaces/base_info_interface.hpp" +#include "rosbag2_storage/storage_interfaces/base_io_interface.hpp" +#include "rosbag2_storage/storage_interfaces/base_read_interface.hpp" +#include "rosbag2_storage/visibility_control.hpp" + +namespace rosbag2_storage +{ +namespace storage_interfaces +{ + +class ROSBAG2_STORAGE_PUBLIC ReadOnlyInterface + : public BaseInfoInterface, public BaseIOInterface, public BaseReadInterface +{ +public: + virtual ~ReadOnlyInterface() = default; + void open(const std::string & uri, IOFlag io_flag = IOFlag::READ_ONLY) override = 0; +}; + +} // namespace storage_interfaces +} // namespace rosbag2_storage + +#endif // ROSBAG2_STORAGE__STORAGE_INTERFACES__READ_ONLY_INTERFACE_HPP_ diff --git a/rosbag2_storage/include/rosbag2_storage/storage_interfaces/read_write_interface.hpp b/rosbag2_storage/include/rosbag2_storage/storage_interfaces/read_write_interface.hpp new file mode 100644 index 0000000000..1ea07f7c8b --- /dev/null +++ b/rosbag2_storage/include/rosbag2_storage/storage_interfaces/read_write_interface.hpp @@ -0,0 +1,40 @@ +// Copyright 2018 Open Source Robotics Foundation, Inc. +// +// 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_STORAGE__STORAGE_INTERFACES__READ_WRITE_INTERFACE_HPP_ +#define ROSBAG2_STORAGE__STORAGE_INTERFACES__READ_WRITE_INTERFACE_HPP_ + +#include + +#include "rosbag2_storage/storage_interfaces/read_only_interface.hpp" +#include "rosbag2_storage/storage_interfaces/base_write_interface.hpp" +#include "rosbag2_storage/visibility_control.hpp" + +namespace rosbag2_storage +{ +namespace storage_interfaces +{ + +class ROSBAG2_STORAGE_PUBLIC ReadWriteInterface + : public ReadOnlyInterface, public BaseWriteInterface +{ +public: + ~ReadWriteInterface() override = default; + void open(const std::string & uri, IOFlag io_flag = IOFlag::READ_WRITE) override = 0; +}; + +} // namespace storage_interfaces +} // namespace rosbag2_storage + +#endif // ROSBAG2_STORAGE__STORAGE_INTERFACES__READ_WRITE_INTERFACE_HPP_ diff --git a/rosbag2_storage/include/rosbag2_storage/storage_traits.hpp b/rosbag2_storage/include/rosbag2_storage/storage_traits.hpp new file mode 100644 index 0000000000..bf08ebd00a --- /dev/null +++ b/rosbag2_storage/include/rosbag2_storage/storage_traits.hpp @@ -0,0 +1,46 @@ +// Copyright 2018, Open Source Robotics Foundation, Inc. +// 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_STORAGE__STORAGE_TRAITS_HPP_ +#define ROSBAG2_STORAGE__STORAGE_TRAITS_HPP_ + +#include "rosbag2_storage/storage_interfaces/base_io_interface.hpp" +#include "rosbag2_storage/storage_interfaces/read_only_interface.hpp" +#include "rosbag2_storage/storage_interfaces/read_write_interface.hpp" + +namespace rosbag2_storage +{ + +template +struct StorageTraits +{}; + +template<> +struct StorageTraits +{ + static constexpr storage_interfaces::IOFlag io_flag = storage_interfaces::IOFlag::READ_WRITE; + static constexpr const char * name = "rosbag2_storage::storage_interfaces::ReadWriteInterface"; +}; + +template<> +struct StorageTraits +{ + static constexpr storage_interfaces::IOFlag io_flag = storage_interfaces::IOFlag::READ_ONLY; + static constexpr const char * name = "rosbag2_storage::storage_interfaces::ReadOnlyInterface"; +}; + +} // namespace rosbag2_storage + +#endif // ROSBAG2_STORAGE__STORAGE_TRAITS_HPP_ diff --git a/rosbag2_storage/include/rosbag2_storage/visibility_control.hpp b/rosbag2_storage/include/rosbag2_storage/visibility_control.hpp new file mode 100644 index 0000000000..01e2080c35 --- /dev/null +++ b/rosbag2_storage/include/rosbag2_storage/visibility_control.hpp @@ -0,0 +1,56 @@ +// Copyright 2018 Open Source Robotics Foundation, Inc. +// +// 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. + +/* This header must be included by all rclcpp headers which declare symbols + * which are defined in the rclcpp library. When not building the rclcpp + * library, i.e. when using the headers in other package's code, the contents + * of this header change the visibility of certain symbols which the rclcpp + * library cannot have, but the consuming code must have inorder to link. + */ + +#ifndef ROSBAG2_STORAGE__VISIBILITY_CONTROL_HPP_ +#define ROSBAG2_STORAGE__VISIBILITY_CONTROL_HPP_ + +// This logic was borrowed (then namespaced) from the examples on the gcc wiki: +// https://gcc.gnu.org/wiki/Visibility + +#if defined _WIN32 || defined __CYGWIN__ + #ifdef __GNUC__ + #define ROSBAG2_STORAGE_EXPORT __attribute__ ((dllexport)) + #define ROSBAG2_STORAGE_IMPORT __attribute__ ((dllimport)) + #else + #define ROSBAG2_STORAGE_EXPORT __declspec(dllexport) + #define ROSBAG2_STORAGE_IMPORT __declspec(dllimport) + #endif + #ifdef ROSBAG2_STORAGE_BUILDING_DLL + #define ROSBAG2_STORAGE_PUBLIC ROSBAG2_STORAGE_EXPORT + #else + #define ROSBAG2_STORAGE_PUBLIC ROSBAG2_STORAGE_IMPORT + #endif + #define ROSBAG2_STORAGE_PUBLIC_TYPE ROSBAG2_STORAGE_PUBLIC + #define ROSBAG2_STORAGE_LOCAL +#else + #define ROSBAG2_STORAGE_EXPORT __attribute__ ((visibility("default"))) + #define ROSBAG2_STORAGE_IMPORT + #if __GNUC__ >= 4 + #define ROSBAG2_STORAGE_PUBLIC __attribute__ ((visibility("default"))) + #define ROSBAG2_STORAGE_LOCAL __attribute__ ((visibility("hidden"))) + #else + #define ROSBAG2_STORAGE_PUBLIC + #define ROSBAG2_STORAGE_LOCAL + #endif + #define ROSBAG2_STORAGE_PUBLIC_TYPE +#endif + +#endif // ROSBAG2_STORAGE__VISIBILITY_CONTROL_HPP_ diff --git a/rosbag2_storage/package.xml b/rosbag2_storage/package.xml index d847e44278..db52761213 100644 --- a/rosbag2_storage/package.xml +++ b/rosbag2_storage/package.xml @@ -1,5 +1,4 @@ - rosbag2_storage 0.0.0 @@ -9,6 +8,10 @@ ament_cmake + pluginlib + rcutils + + ament_cmake_gtest ament_lint_auto ament_lint_common diff --git a/rosbag2_storage/src/impl/storage_factory_impl.hpp b/rosbag2_storage/src/impl/storage_factory_impl.hpp new file mode 100644 index 0000000000..d990e8661b --- /dev/null +++ b/rosbag2_storage/src/impl/storage_factory_impl.hpp @@ -0,0 +1,137 @@ +// Copyright 2018, Open Source Robotics Foundation, Inc. +// 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 IMPL__STORAGE_FACTORY_IMPL_HPP_ +#define IMPL__STORAGE_FACTORY_IMPL_HPP_ + +#include +#include +#include +#include + +#include "pluginlib/class_loader.hpp" +#include "rcutils/logging_macros.h" + +#include "rosbag2_storage/storage_interfaces/read_only_interface.hpp" +#include "rosbag2_storage/storage_interfaces/read_write_interface.hpp" + +#include "rosbag2_storage/storage_factory.hpp" +#include "rosbag2_storage/storage_traits.hpp" + +namespace rosbag2_storage +{ + +constexpr const char * ROS_PACKAGE_NAME = "rosbag2_storage"; + +using storage_interfaces::ReadOnlyInterface; +using storage_interfaces::ReadWriteInterface; + +template +std::shared_ptr> +get_class_loader() +{ + const char * lookup_name = StorageTraits::name; + return std::make_shared>(ROS_PACKAGE_NAME, lookup_name); +} + +template< + typename InterfaceT, + storage_interfaces::IOFlag flag = StorageTraits::io_flag +> +std::shared_ptr +get_interface_instance( + std::shared_ptr> class_loader, + const std::string & storage_id, + const std::string & uri) +{ + const auto & registered_classes = class_loader->getDeclaredClasses(); + auto class_exists = std::find(registered_classes.begin(), registered_classes.end(), storage_id); + if (class_exists == registered_classes.end()) { + RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME, + "Requested storage id %s does not exist", storage_id.c_str()); + return nullptr; + } + + std::shared_ptr instance = nullptr; + try { + auto unmanaged_instance = class_loader->createUnmanagedInstance(storage_id); + instance = std::shared_ptr(unmanaged_instance); + } catch (const std::runtime_error & ex) { + RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME, + "unable to load instance of read write interface: %s", ex.what()); + return nullptr; + } + + try { + instance->open(uri, flag); + return instance; + } catch (const std::runtime_error & ex) { + RCUTILS_LOG_ERROR_NAMED(ROS_PACKAGE_NAME, + "Could not open uri %s : %s", storage_id.c_str(), ex.what()); + return nullptr; + } +} + +class StorageFactoryImpl +{ +public: + StorageFactoryImpl() + { + try { + read_write_class_loader_ = get_class_loader(); + } catch (const std::exception & e) { + RCUTILS_LOG_ERROR_NAMED( + ROS_PACKAGE_NAME, "unable to create class load instance: %s", e.what()); + throw e; + } + + try { + read_only_class_loader_ = get_class_loader(); + } catch (const std::exception & e) { + RCUTILS_LOG_ERROR_NAMED( + ROS_PACKAGE_NAME, "unable to create class load instance: %s", e.what()); + throw e; + } + } + + virtual ~StorageFactoryImpl() = default; + + std::shared_ptr open_read_write( + const std::string & uri, const std::string & storage_id) + { + return get_interface_instance(read_write_class_loader_, storage_id, uri); + } + + std::shared_ptr open_read_only( + const std::string & uri, const std::string & storage_id) + { + // try to load the instance as read_only interface + auto instance = get_interface_instance(read_only_class_loader_, storage_id, uri); + // try to load as read_write if not successful + if (instance == nullptr) { + instance = get_interface_instance( + read_write_class_loader_, storage_id, uri); + } + return instance; + } + +private: + std::shared_ptr> read_write_class_loader_; + std::shared_ptr> read_only_class_loader_; +}; + +} // namespace rosbag2_storage + +#endif // IMPL__STORAGE_FACTORY_IMPL_HPP_ diff --git a/rosbag2_storage/src/storage_factory.cpp b/rosbag2_storage/src/storage_factory.cpp new file mode 100644 index 0000000000..199a3e45a1 --- /dev/null +++ b/rosbag2_storage/src/storage_factory.cpp @@ -0,0 +1,50 @@ +// Copyright 2018, Open Source Robotics Foundation, Inc. +// 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_storage/storage_factory.hpp" + +#include +#include + +#include "rosbag2_storage/storage_interfaces/read_only_interface.hpp" +#include "rosbag2_storage/storage_interfaces/read_write_interface.hpp" + +#include "./impl/storage_factory_impl.hpp" + +namespace rosbag2_storage +{ + +using rosbag2_storage::storage_interfaces::ReadOnlyInterface; +using rosbag2_storage::storage_interfaces::ReadWriteInterface; + +StorageFactory::StorageFactory() +: impl_(new StorageFactoryImpl()) +{} + +// needed explicit destructor because of unique_ptr for pimpl +StorageFactory::~StorageFactory() {} + +std::shared_ptr StorageFactory::open_read_only( + const std::string & uri, const std::string & storage_id) +{ + return impl_->open_read_only(uri, storage_id); +} + +std::shared_ptr StorageFactory::open_read_write( + const std::string & uri, const std::string & storage_id) +{ + return impl_->open_read_write(uri, storage_id); +} +} // namespace rosbag2_storage diff --git a/rosbag2_storage/test/test_plugin.cpp b/rosbag2_storage/test/test_plugin.cpp new file mode 100644 index 0000000000..7c9fa1a2f6 --- /dev/null +++ b/rosbag2_storage/test/test_plugin.cpp @@ -0,0 +1,70 @@ +// Copyright 2018, Open Source Robotics Foundation, Inc. +// 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 +#include +#include + +#include "pluginlib/class_list_macros.hpp" + +#include "rosbag2_storage/bag_info.hpp" +#include "rosbag2_storage/serialized_bag_message.hpp" + +#include "test_plugin.hpp" + +TestPlugin::~TestPlugin() +{ + std::cout << "\nclosing.\n"; +} + +void TestPlugin::open( + const std::string & uri, rosbag2_storage::storage_interfaces::IOFlag flag) +{ + if (flag == rosbag2_storage::storage_interfaces::IOFlag::READ_ONLY) { + std::cout << "opening testplugin read only: "; + } else if (flag == rosbag2_storage::storage_interfaces::IOFlag::READ_WRITE) { + std::cout << "opening testplugin read write: "; + } + std::cout << uri << ".\n"; +} + +rosbag2_storage::BagInfo TestPlugin::info() +{ + return rosbag2_storage::BagInfo(); +} + +bool TestPlugin::has_next() const +{ + return true; +} + +std::shared_ptr TestPlugin::read_next() +{ + std::cout << "\nreading\n"; + return std::shared_ptr(); +} + +void TestPlugin::create_topic() +{ + std::cout << "\ncreating topic\n"; +} + +void TestPlugin::write(const std::shared_ptr msg) +{ + (void) msg; + std::cout << "\nwriting\n"; +} + +PLUGINLIB_EXPORT_CLASS(TestPlugin, rosbag2_storage::storage_interfaces::ReadWriteInterface) diff --git a/rosbag2_storage/test/test_plugin.hpp b/rosbag2_storage/test/test_plugin.hpp new file mode 100644 index 0000000000..5df537eff0 --- /dev/null +++ b/rosbag2_storage/test/test_plugin.hpp @@ -0,0 +1,44 @@ +// Copyright 2018, Open Source Robotics Foundation, Inc. +// 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 TEST_PLUGIN_HPP_ +#define TEST_PLUGIN_HPP_ + +#include +#include + +#include "rosbag2_storage/bag_info.hpp" +#include "rosbag2_storage/serialized_bag_message.hpp" +#include "rosbag2_storage/storage_interfaces/read_write_interface.hpp" + +class TestPlugin : public rosbag2_storage::storage_interfaces::ReadWriteInterface +{ +public: + ~TestPlugin() override; + + void open(const std::string & uri, rosbag2_storage::storage_interfaces::IOFlag flag) override; + + rosbag2_storage::BagInfo info() override; + + bool has_next() const override; + + std::shared_ptr read_next() override; + + void create_topic() override; + + void write(std::shared_ptr msg) override; +}; + +#endif // TEST_PLUGIN_HPP_ diff --git a/rosbag2_storage/test/test_plugin.xml b/rosbag2_storage/test/test_plugin.xml new file mode 100644 index 0000000000..28d86d4cc2 --- /dev/null +++ b/rosbag2_storage/test/test_plugin.xml @@ -0,0 +1,17 @@ + + + This is a test storage plugin. + + + + This is a read-only test storage plugin. + + diff --git a/rosbag2_storage/test/test_read_only_plugin.cpp b/rosbag2_storage/test/test_read_only_plugin.cpp new file mode 100644 index 0000000000..d3360d1c64 --- /dev/null +++ b/rosbag2_storage/test/test_read_only_plugin.cpp @@ -0,0 +1,55 @@ +// 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 +#include +#include + +#include "pluginlib/class_list_macros.hpp" + +#include "test_read_only_plugin.hpp" + +TestReadOnlyPlugin::~TestReadOnlyPlugin() +{ + std::cout << "\nclosing.\n"; +} + +void TestReadOnlyPlugin::open( + const std::string & uri, rosbag2_storage::storage_interfaces::IOFlag flag) +{ + if (flag == rosbag2_storage::storage_interfaces::IOFlag::READ_ONLY) { + std::cout << "opening testplugin read only: "; + } else if (flag == rosbag2_storage::storage_interfaces::IOFlag::READ_WRITE) { + std::cout << "opening testplugin read write: "; + } + std::cout << uri << ".\n"; +} + +rosbag2_storage::BagInfo TestReadOnlyPlugin::info() +{ + return rosbag2_storage::BagInfo(); +} + +bool TestReadOnlyPlugin::has_next() const +{ + return true; +} + +std::shared_ptr TestReadOnlyPlugin::read_next() +{ + std::cout << "\nreading\n"; + return std::shared_ptr(); +} + +PLUGINLIB_EXPORT_CLASS(TestReadOnlyPlugin, rosbag2_storage::storage_interfaces::ReadOnlyInterface) diff --git a/rosbag2_storage/test/test_read_only_plugin.hpp b/rosbag2_storage/test/test_read_only_plugin.hpp new file mode 100644 index 0000000000..c1963f0d75 --- /dev/null +++ b/rosbag2_storage/test/test_read_only_plugin.hpp @@ -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. + +#ifndef TEST_READ_ONLY_PLUGIN_HPP_ +#define TEST_READ_ONLY_PLUGIN_HPP_ + +#include +#include + +#include "rosbag2_storage/storage_interfaces/read_only_interface.hpp" + +class TestReadOnlyPlugin : public rosbag2_storage::storage_interfaces::ReadOnlyInterface +{ +public: + ~TestReadOnlyPlugin() override; + + void open(const std::string & uri, rosbag2_storage::storage_interfaces::IOFlag flag) override; + + rosbag2_storage::BagInfo info() override; + + bool has_next() const override; + + std::shared_ptr read_next() override; +}; + +#endif // TEST_READ_ONLY_PLUGIN_HPP_ diff --git a/rosbag2_storage/test/test_storage_factory.cpp b/rosbag2_storage/test/test_storage_factory.cpp new file mode 100644 index 0000000000..6b2ca606e8 --- /dev/null +++ b/rosbag2_storage/test/test_storage_factory.cpp @@ -0,0 +1,73 @@ +// Copyright 2018 Open Source Robotics Foundation, Inc. +// +// 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 +#include +#include + +#include "rosbag2_storage/storage_interfaces/read_only_interface.hpp" +#include "rosbag2_storage/storage_interfaces/read_write_interface.hpp" + +#include "rosbag2_storage/storage_factory.hpp" + +using rosbag2_storage::storage_interfaces::ReadWriteInterface; +using rosbag2_storage::storage_interfaces::ReadOnlyInterface; + +// The fixture for testing class Foo. +class StorageFactoryTest : public ::testing::Test +{ +public: + rosbag2_storage::StorageFactory factory; + + std::string bag_file_path = "file/to/be/loaded.bag"; + std::string test_plugin_id = "my_test_plugin"; + std::string test_read_only_plugin_id = "my_read_only_test_plugin"; + std::string test_unavailable_plugin_id = "my_unavailable_plugin"; +}; + +TEST_F(StorageFactoryTest, load_test_plugin) { + // Load plugin for read and write + auto read_write_storage = factory.open_read_write( + bag_file_path, test_plugin_id); + ASSERT_NE(nullptr, read_write_storage); + auto msg = read_write_storage->read_next(); + read_write_storage->write(msg); + + // Load plugin for read only even though it provides read and write interfaces + auto read_only_storage = factory.open_read_only( + bag_file_path, test_plugin_id); + ASSERT_NE(nullptr, read_only_storage); + msg = read_only_storage->read_next(); +} + +TEST_F(StorageFactoryTest, loads_readonly_plugin_only_for_read_only_storage) { + auto storage_for_reading = factory.open_read_only( + bag_file_path, test_read_only_plugin_id); + ASSERT_NE(nullptr, storage_for_reading); + storage_for_reading->read_next(); + + auto storage_for_reading_and_writing = factory.open_read_write( + bag_file_path, test_read_only_plugin_id); + ASSERT_EQ(nullptr, storage_for_reading_and_writing); +} + +TEST_F(StorageFactoryTest, load_unavailable_plugin) { + auto instance_rw = factory.open_read_write( + bag_file_path, test_unavailable_plugin_id); + EXPECT_EQ(nullptr, instance_rw); + + auto instance_ro = factory.open_read_only( + bag_file_path, test_unavailable_plugin_id); + EXPECT_EQ(nullptr, instance_ro); +} diff --git a/rosbag2_storage_default_plugins/CMakeLists.txt b/rosbag2_storage_default_plugins/CMakeLists.txt new file mode 100644 index 0000000000..722f4d8fb7 --- /dev/null +++ b/rosbag2_storage_default_plugins/CMakeLists.txt @@ -0,0 +1,75 @@ +cmake_minimum_required(VERSION 3.5) +project(rosbag2_storage_default_plugins) + +# Default to C99 +if(NOT CMAKE_C_STANDARD) + set(CMAKE_C_STANDARD 99) +endif() + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic -Werror) +endif() + +find_package(ament_cmake REQUIRED) +find_package(pluginlib REQUIRED) +find_package(rcutils REQUIRED) +find_package(rosbag2_storage REQUIRED) +find_package(sqlite3_vendor REQUIRED) +find_package(SQLite3 REQUIRED) # provided by sqlite3_vendor + +add_library(${PROJECT_NAME} SHARED + src/rosbag2_storage_default_plugins/sqlite/sqlite_wrapper.hpp + src/rosbag2_storage_default_plugins/sqlite/sqlite_wrapper.cpp + src/rosbag2_storage_default_plugins/sqlite/sqlite_storage.hpp + src/rosbag2_storage_default_plugins/sqlite/sqlite_storage.cpp) + +ament_target_dependencies(${PROJECT_NAME} + rosbag2_storage + rclcpp + rcutils + std_msgs + SQLite3 + pluginlib) + +target_include_directories(${PROJECT_NAME} + PUBLIC + $ + $ +) + +install( + TARGETS ${PROJECT_NAME} + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin) + +pluginlib_export_plugin_description_file(rosbag2_storage plugin_description.xml) + +if(BUILD_TESTING) + find_package(ament_cmake_gtest REQUIRED) + find_package(ament_cmake_gmock REQUIRED) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() + + ament_add_gmock(sqlite_storage_integration_test + test/rosbag2_storage_default_plugins/sqlite/sqlite_storage_integration_test.cpp + src/rosbag2_storage_default_plugins/sqlite/sqlite_storage.cpp + src/rosbag2_storage_default_plugins/sqlite/sqlite_wrapper.cpp + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + if(TARGET sqlite_storage_integration_test) + ament_target_dependencies(sqlite_storage_integration_test + rosbag2_storage + rclcpp + rcutils + std_msgs + SQLite3 + pluginlib) + endif() +endif() + +ament_package() diff --git a/sqlite3_storage_plugin/package.xml b/rosbag2_storage_default_plugins/package.xml similarity index 80% rename from sqlite3_storage_plugin/package.xml rename to rosbag2_storage_default_plugins/package.xml index 2fd9f3fe05..92630f0123 100644 --- a/sqlite3_storage_plugin/package.xml +++ b/rosbag2_storage_default_plugins/package.xml @@ -1,7 +1,6 @@ - - sqlite3_storage_plugin + rosbag2_storage_default_plugins 0.0.0 ROSBag2 SQLite3 storage plugin Karsten Knese @@ -12,9 +11,13 @@ sqlite3_vendor rclcpp std_msgs + rcutils + rosbag2_storage sqlite3_vendor + rosbag2_storage rclcpp + rcutils std_msgs ament_lint_auto diff --git a/rosbag2_storage_default_plugins/plugin_description.xml b/rosbag2_storage_default_plugins/plugin_description.xml new file mode 100644 index 0000000000..77fed175bc --- /dev/null +++ b/rosbag2_storage_default_plugins/plugin_description.xml @@ -0,0 +1,9 @@ + + + Plugin to write to SQLite3 databases + + diff --git a/rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_storage.cpp b/rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_storage.cpp new file mode 100644 index 0000000000..c2f96088a2 --- /dev/null +++ b/rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_storage.cpp @@ -0,0 +1,125 @@ +// 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 "sqlite_storage.hpp" + +#include +#include +#include +#include +#include + +#include "rcutils/logging_macros.h" +#include "rosbag2_storage/serialized_bag_message.hpp" + +namespace rosbag2_storage_plugins +{ +const char * ROS_PACKAGE_NAME = "rosbag2_storage_default_plugins"; + +SqliteStorage::SqliteStorage() +: database_(), counter_(0), bag_info_() +{} + +SqliteStorage::SqliteStorage(std::shared_ptr database) +: database_(std::move(database)), counter_(0) +{} + +void SqliteStorage::open( + const std::string & uri, rosbag2_storage::storage_interfaces::IOFlag io_flag) +{ + (void) io_flag; + try { + database_ = std::make_unique(uri); + bag_info_.uri = uri; + } catch (const SqliteException & e) { + throw std::runtime_error("Failed to setup storage. Error: " + std::string(e.what())); + } + + RCUTILS_LOG_INFO_NAMED(ROS_PACKAGE_NAME, "Opened database '%s'.", uri.c_str()); +} + +void SqliteStorage::write(std::shared_ptr message) +{ + // TODO(Martin-Idel-SI) The real serialized string message has 8 leading chars in CDR + std::string msg(&message->serialized_data->buffer[8]); + std::string insert_message = + "INSERT INTO messages (data, timestamp) VALUES ('" + msg + "', strftime('%s%f','now'))"; + database_->execute_query(insert_message); + + RCUTILS_LOG_INFO_NAMED(ROS_PACKAGE_NAME, "Stored message"); +} + +bool SqliteStorage::has_next() const +{ + // TODO(Martin-Idel-SI): improve sqlite_wrapper interface + std::string message; + return database_->get_message(message, counter_); +} + +std::shared_ptr SqliteStorage::read_next() +{ + // TODO(Martin-Idel-SI): improve sqlite_wrapper interface + std::string message; + database_->get_message(message, counter_++); + auto payload = new rcutils_char_array_t; + *payload = rcutils_get_zero_initialized_char_array(); + auto allocator = new rcutils_allocator_t; + *allocator = rcutils_get_default_allocator(); + auto ret = rcutils_char_array_init(payload, strlen(message.c_str()) + 1, allocator); + if (ret != RCUTILS_RET_OK) { + throw std::runtime_error(std::string(" Failed to allocate serialized bag message ") + + rcutils_get_error_string_safe()); + } + memcpy(payload->buffer, message.c_str(), strlen(message.c_str()) + 1); + + auto msg = std::make_shared(); + msg->time_stamp = 0; + msg->serialized_data = std::shared_ptr(payload, + [](rcutils_char_array_t * msg) { + auto error = rcutils_char_array_fini(msg); + delete msg; + if (error != RCUTILS_RET_OK) { + RCUTILS_LOG_ERROR_NAMED("rosbag2_storage_default_plugins", + " Failed to destroy serialized bag message %s", rcutils_get_error_string_safe()); + } + }); + return msg; +} + +void SqliteStorage::initialize() +{ + std::string create_table = "CREATE TABLE messages(" \ + "id INTEGER PRIMARY KEY AUTOINCREMENT," \ + "data BLOB NOT NULL," \ + "timestamp INT NOT NULL);"; + + database_->execute_query(create_table); +} + +rosbag2_storage::BagInfo SqliteStorage::info() +{ + return bag_info_; +} + +void SqliteStorage::create_topic() +{ + initialize(); +} + +} // namespace rosbag2_storage_plugins + + +#include "pluginlib/class_list_macros.hpp" // NOLINT +PLUGINLIB_EXPORT_CLASS(rosbag2_storage_plugins::SqliteStorage, + rosbag2_storage::storage_interfaces::ReadWriteInterface) diff --git a/rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_storage.hpp b/rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_storage.hpp new file mode 100644 index 0000000000..6a5375ca83 --- /dev/null +++ b/rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_storage.hpp @@ -0,0 +1,61 @@ +// 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_STORAGE_DEFAULT_PLUGINS__SQLITE__SQLITE_STORAGE_HPP_ +#define ROSBAG2_STORAGE_DEFAULT_PLUGINS__SQLITE__SQLITE_STORAGE_HPP_ + +#include +#include +#include + +#include "sqlite_wrapper.hpp" + +#include "rosbag2_storage/storage_interfaces/read_write_interface.hpp" + +namespace rosbag2_storage_plugins +{ + +class SqliteStorage : public rosbag2_storage::storage_interfaces::ReadWriteInterface +{ +public: + SqliteStorage(); + explicit SqliteStorage(std::shared_ptr database); + ~SqliteStorage() override = default; + + void open( + const std::string & uri, + rosbag2_storage::storage_interfaces::IOFlag io_flag = + rosbag2_storage::storage_interfaces::IOFlag::READ_WRITE) override; + + void create_topic() override; + + void write(std::shared_ptr message) override; + + bool has_next() const override; + + std::shared_ptr read_next() override; + + rosbag2_storage::BagInfo info() override; + +private: + void initialize(); + + std::shared_ptr database_; + size_t counter_; + rosbag2_storage::BagInfo bag_info_; +}; + +} // namespace rosbag2_storage_plugins + +#endif // ROSBAG2_STORAGE_DEFAULT_PLUGINS__SQLITE__SQLITE_STORAGE_HPP_ diff --git a/rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_wrapper.cpp b/rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_wrapper.cpp new file mode 100644 index 0000000000..b92b002f0d --- /dev/null +++ b/rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_wrapper.cpp @@ -0,0 +1,83 @@ +// 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 "sqlite_wrapper.hpp" + +#include +#include +#include +#include +#include + +namespace rosbag2_storage_plugins +{ + +SqliteWrapper::SqliteWrapper(const std::string & uri) +: db_ptr(nullptr) +{ + int rc = sqlite3_open(uri.c_str(), &db_ptr); + if (rc) { + throw SqliteException("Could not open database. Error: " + std::string(sqlite3_errmsg(db_ptr))); + } +} + +SqliteWrapper::SqliteWrapper() +: db_ptr(nullptr) {} + +SqliteWrapper::~SqliteWrapper() +{ + sqlite3_close(db_ptr); +} + +void SqliteWrapper::execute_query(const std::string & query) +{ + char * error_msg = nullptr; + int return_code = sqlite3_exec(db_ptr, query.c_str(), nullptr, nullptr, &error_msg); + + if (return_code != SQLITE_OK) { + auto error = "SQL error: " + std::string(error_msg); + sqlite3_free(error_msg); + throw SqliteException(error); + } +} + +bool SqliteWrapper::get_message(std::string & message, size_t index) +{ + std::string offset = std::to_string(index); + sqlite3_stmt * statement; + std::string query = "SELECT * FROM messages LIMIT 1 OFFSET " + offset; + + int return_code = sqlite3_prepare_v2(db_ptr, query.c_str(), -1, &statement, nullptr); + if (return_code != SQLITE_OK) { + throw SqliteException("SQL error when preparing statement '" + query + "'with return code: " + + std::to_string(return_code)); + } + + int result = sqlite3_step(statement); + if (result == SQLITE_ROW) { + message = std::string(reinterpret_cast(sqlite3_column_text(statement, 1))); + sqlite3_finalize(statement); + return true; + } else { + sqlite3_finalize(statement); + return false; + } +} + +SqliteWrapper::operator bool() +{ + return db_ptr != nullptr; +} + +} // namespace rosbag2_storage_plugins diff --git a/rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_wrapper.hpp b/rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_wrapper.hpp new file mode 100644 index 0000000000..8cbdead29b --- /dev/null +++ b/rosbag2_storage_default_plugins/src/rosbag2_storage_default_plugins/sqlite/sqlite_wrapper.hpp @@ -0,0 +1,55 @@ +// 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_STORAGE_DEFAULT_PLUGINS__SQLITE__SQLITE_WRAPPER_HPP_ +#define ROSBAG2_STORAGE_DEFAULT_PLUGINS__SQLITE__SQLITE_WRAPPER_HPP_ + +#include +#include +#include +#include + +namespace rosbag2_storage_plugins +{ + +class SqliteException : public std::runtime_error +{ +public: + explicit SqliteException(const std::string & message) + : runtime_error(message) {} +}; + +using DBPtr = sqlite3 *; + +class SqliteWrapper +{ +public: + explicit SqliteWrapper(const std::string & uri); + SqliteWrapper(); + virtual ~SqliteWrapper(); + + virtual void execute_query(const std::string & query); + + virtual bool get_message(std::string & message, size_t index); + + virtual operator bool(); + +private: + DBPtr db_ptr; +}; + + +} // namespace rosbag2_storage_plugins + +#endif // ROSBAG2_STORAGE_DEFAULT_PLUGINS__SQLITE__SQLITE_WRAPPER_HPP_ diff --git a/rosbag2_storage_default_plugins/test/rosbag2_storage_default_plugins/sqlite/mock_sqlite_wrapper.hpp b/rosbag2_storage_default_plugins/test/rosbag2_storage_default_plugins/sqlite/mock_sqlite_wrapper.hpp new file mode 100644 index 0000000000..e5d14c5941 --- /dev/null +++ b/rosbag2_storage_default_plugins/test/rosbag2_storage_default_plugins/sqlite/mock_sqlite_wrapper.hpp @@ -0,0 +1,32 @@ +// 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_STORAGE_DEFAULT_PLUGINS__SQLITE__MOCK_SQLITE_WRAPPER_HPP_ +#define ROSBAG2_STORAGE_DEFAULT_PLUGINS__SQLITE__MOCK_SQLITE_WRAPPER_HPP_ + +#include + +#include +#include + +#include "../../../src/rosbag2_storage_default_plugins/sqlite/sqlite_wrapper.hpp" + +class MockSqliteWrapper : public rosbag2_storage_plugins::SqliteWrapper +{ +public: + MOCK_METHOD1(execute_query, void(const std::string &)); + MOCK_METHOD2(get_message, bool(std::string &, size_t)); +}; + +#endif // ROSBAG2_STORAGE_DEFAULT_PLUGINS__SQLITE__MOCK_SQLITE_WRAPPER_HPP_ diff --git a/rosbag2_storage_default_plugins/test/rosbag2_storage_default_plugins/sqlite/sqlite_storage_integration_test.cpp b/rosbag2_storage_default_plugins/test/rosbag2_storage_default_plugins/sqlite/sqlite_storage_integration_test.cpp new file mode 100644 index 0000000000..d989383584 --- /dev/null +++ b/rosbag2_storage_default_plugins/test/rosbag2_storage_default_plugins/sqlite/sqlite_storage_integration_test.cpp @@ -0,0 +1,33 @@ +// 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 + +#include +#include +#include + +#include "storage_test_fixture.hpp" + +using namespace ::testing; // NOLINT + +TEST_F(StorageTestFixture, string_messages_are_written_and_read_to_and_from_sqlite3_storage) { + std::vector string_messages = {"test_message 1", "test_message 2", "test_message 3"}; + + write_messages_to_sqlite(string_messages); + auto read_messages = read_all_messages_from_sqlite(); + + ASSERT_THAT(read_messages, SizeIs(3)); + EXPECT_THAT(std::string(read_messages[0]->serialized_data->buffer), Eq(string_messages[0])); +} diff --git a/rosbag2_storage_default_plugins/test/rosbag2_storage_default_plugins/sqlite/storage_test_fixture.hpp b/rosbag2_storage_default_plugins/test/rosbag2_storage_default_plugins/sqlite/storage_test_fixture.hpp new file mode 100644 index 0000000000..7df2db41d0 --- /dev/null +++ b/rosbag2_storage_default_plugins/test/rosbag2_storage_default_plugins/sqlite/storage_test_fixture.hpp @@ -0,0 +1,145 @@ +// 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_STORAGE_DEFAULT_PLUGINS__SQLITE__STORAGE_TEST_FIXTURE_HPP_ +#define ROSBAG2_STORAGE_DEFAULT_PLUGINS__SQLITE__STORAGE_TEST_FIXTURE_HPP_ + +#include + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include +#endif + +#include "../../../src/rosbag2_storage_default_plugins/sqlite/sqlite_storage.hpp" + +using namespace ::testing; // NOLINT + +class StorageTestFixture : public Test +{ +public: + StorageTestFixture() + : database_name_(UnitTest::GetInstance()->current_test_info()->name()) + { + std::string system_separator = "/"; +#ifdef _WIN32 + system_separator = "\\"; +#endif + database_name_ = temporary_dir_path_ + system_separator + database_name_; + std::cout << "Database name: " << database_name_ << std::endl; + } + + ~StorageTestFixture() override + { +#ifdef _WIN32 + DeleteFileA(database_name_.c_str()); +#else +// TODO(botteroa-si): once filesystem::remove_all() can be used, this line can be removed and +// the ful directory can be deleted in remove_temporary_dir() + remove(database_name_.c_str()); +#endif + } + + static void SetUpTestCase() + { + char template_char[] = "tmp_test_dir.XXXXXX"; +#ifdef _WIN32 + char temp_path[255]; + GetTempPathA(255, temp_path); + _mktemp_s(template_char, strnlen(template_char, 20) + 1); + temporary_dir_path_ = std::string(temp_path) + std::string(template_char); + _mkdir(temporary_dir_path_.c_str()); +#else + char * dir_name = mkdtemp(template_char); + temporary_dir_path_ = dir_name; +#endif + } + + static void TearDownTestCase() + { + remove_temporary_dir(); + } + + static void remove_temporary_dir() + { +#ifdef _WIN32 + // TODO(botteroa-si): find a way to delete a not empty directory in Windows, so that we don't + // need the Windows line in the fixture destructor anymore. + RemoveDirectoryA(temporary_dir_path_.c_str()); +#else + remove(temporary_dir_path_.c_str()); +#endif + } + + void write_messages_to_sqlite(std::vector messages) + { + std::unique_ptr writable_storage = + std::make_unique(); + writable_storage->open(database_name_); + writable_storage->create_topic(); + + for (auto message : messages) { + auto msg = std::make_shared(); + auto payload = new rcutils_char_array_t; + *payload = rcutils_get_zero_initialized_char_array(); + payload->allocator = rcutils_get_default_allocator(); + auto ret = rcutils_char_array_resize(payload, 8 + strlen(message.c_str()) + 1); + if (ret != RCUTILS_RET_OK) { + FAIL() << " Failed to resize serialized bag message"; + } + // TODO(Martin-Idel-SI) The real serialized string message has 8 leading chars in CDR + std::string full_message = "bbbbbbbb" + message; + memcpy(payload->buffer, full_message.c_str(), strlen(full_message.c_str()) + 1); + + msg->serialized_data = std::shared_ptr(payload, + [](rcutils_char_array_t * msg) { + auto error = rcutils_char_array_fini(msg); + delete msg; + if (error != RCUTILS_RET_OK) { + FAIL() << " Failed to destroy serialized bag message"; + } + }); + writable_storage->write(msg); + } + } + + std::vector> + read_all_messages_from_sqlite() + { + std::unique_ptr readable_storage = + std::make_unique(); + readable_storage->open(database_name_); + std::vector> read_messages; + + std::string message; + while (readable_storage->has_next()) { + read_messages.emplace_back(readable_storage->read_next()); + } + + return read_messages; + } + + std::string database_name_; + static std::string temporary_dir_path_; +}; + +std::string StorageTestFixture::temporary_dir_path_ = ""; // NOLINT + +#endif // ROSBAG2_STORAGE_DEFAULT_PLUGINS__SQLITE__STORAGE_TEST_FIXTURE_HPP_ diff --git a/sqlite3_storage_plugin/CMakeLists.txt b/sqlite3_storage_plugin/CMakeLists.txt deleted file mode 100644 index 0748eb9cf5..0000000000 --- a/sqlite3_storage_plugin/CMakeLists.txt +++ /dev/null @@ -1,93 +0,0 @@ -cmake_minimum_required(VERSION 3.5) -project(sqlite3_storage_plugin) - -# Default to C99 -if(NOT CMAKE_C_STANDARD) - set(CMAKE_C_STANDARD 99) -endif() - -# Default to C++14 -if(NOT CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 14) -endif() - -if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - add_compile_options(-Wall -Wextra -Wpedantic -Werror) -endif() - -find_package(ament_cmake REQUIRED) -find_package(rclcpp REQUIRED) -find_package(std_msgs REQUIRED) -find_package(sqlite3_vendor REQUIRED) - -find_package(SQLite3 REQUIRED) # provided by sqlite3_vendor - -add_library(librosbag - include/rosbag2/rosbag2.hpp - src/rosbag2/rosbag2.cpp - src/rosbag2/storage/readable_storage.hpp - src/rosbag2/storage/writable_storage.hpp - src/rosbag2/storage/storage_factory.hpp - src/rosbag2/storage/storage_factory.cpp - src/rosbag2/storage/sqlite/sqlite_wrapper.hpp - src/rosbag2/storage/sqlite/sqlite_wrapper.cpp - src/rosbag2/storage/sqlite/sqlite_storage.hpp - src/rosbag2/storage/sqlite/sqlite_storage.cpp) -ament_target_dependencies(librosbag rclcpp std_msgs SQLite3) -target_link_libraries(librosbag) -target_include_directories(librosbag - PUBLIC - $ - $ -) - -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) -# TODO(anhosi): Add install for library and headers (GH-5) - -if(BUILD_TESTING) - # TODO(Martin-Idel-SI): Add copyright linter or use ament_lint_auto() once available - find_package(ament_cmake_cppcheck REQUIRED) - find_package(ament_cmake_cpplint REQUIRED) - find_package(ament_cmake_lint_cmake REQUIRED) - find_package(ament_cmake_uncrustify REQUIRED) - ament_cppcheck() - ament_cpplint() - ament_lint_cmake() - ament_uncrustify() - - find_package(ament_cmake_gtest REQUIRED) - find_package(ament_cmake_gmock REQUIRED) - - ament_add_gmock(sqlite_storage_test test/rosbag2/storage/sqlite_storage_test.cpp - test/rosbag2/rosbag2_test_fixture.hpp - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - if(TARGET sqlite_storage_test) - target_link_libraries(sqlite_storage_test librosbag) - endif() - - ament_add_gmock(storage_factory_test test/rosbag2/storage/storage_factory_test.cpp - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - if(TARGET storage_factory_test) - target_link_libraries(storage_factory_test librosbag) - endif() - - 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() diff --git a/sqlite3_storage_plugin/include/rosbag2/rosbag2.hpp b/sqlite3_storage_plugin/include/rosbag2/rosbag2.hpp deleted file mode 100644 index f584fabc68..0000000000 --- a/sqlite3_storage_plugin/include/rosbag2/rosbag2.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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. - */ - -#ifndef ROSBAG2__ROSBAG2_HPP_ -#define ROSBAG2__ROSBAG2_HPP_ - -#include -#include - -namespace rosbag2 -{ - -class Rosbag2 -{ -public: - void record( - const std::string & file_name, - const std::string & topic_name, - std::function after_write_action = nullptr); - void play(const std::string & file_name, const std::string & topic_name); -}; - -} // namespace rosbag2 - -#endif // ROSBAG2__ROSBAG2_HPP_ diff --git a/sqlite3_storage_plugin/src/rosbag2/demo_play.cpp b/sqlite3_storage_plugin/src/rosbag2/demo_play.cpp deleted file mode 100644 index fe5eb73c5a..0000000000 --- a/sqlite3_storage_plugin/src/rosbag2/demo_play.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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. - */ - -#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; -} diff --git a/sqlite3_storage_plugin/src/rosbag2/demo_record.cpp b/sqlite3_storage_plugin/src/rosbag2/demo_record.cpp deleted file mode 100644 index 32da3a22e9..0000000000 --- a/sqlite3_storage_plugin/src/rosbag2/demo_record.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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. - */ - -#include -#include - -#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; -} diff --git a/sqlite3_storage_plugin/src/rosbag2/rosbag2.cpp b/sqlite3_storage_plugin/src/rosbag2/rosbag2.cpp deleted file mode 100644 index 55dd927df7..0000000000 --- a/sqlite3_storage_plugin/src/rosbag2/rosbag2.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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. - */ -#include "rosbag2/rosbag2.hpp" - -#include -#include - -#include "rclcpp/rclcpp.hpp" -#include "std_msgs/msg/string.hpp" - -#include "storage/storage_factory.hpp" -#include "storage/sqlite/sqlite_storage.hpp" - -namespace rosbag2 -{ - -void Rosbag2::record( - const std::string & file_name, - const std::string & topic_name, - std::function after_write_action) -{ - StorageFactory factory; - std::unique_ptr storage = factory.get_for_writing(file_name); - - if (storage) { - auto node = std::make_shared("rosbag_node"); - auto subscription = node->create_subscription( - topic_name, - [&storage, after_write_action](std_msgs::msg::String::ConstSharedPtr msg) { - storage->write(msg->data); - if (after_write_action) { - after_write_action(); - } - }); - - // TODO(anhosi): use proper logging from rcutils - std::cout << "Waiting for messages..." << std::endl; - rclcpp::spin(node); - } -} - -void Rosbag2::play(const std::string & file_name, const std::string & topic_name) -{ - StorageFactory factory; - std::unique_ptr storage = factory.get_for_reading(file_name); - - if (storage) { - auto messages = storage->get_messages(); - auto node = std::make_shared("rosbag_publisher_node"); - auto publisher = node->create_publisher(topic_name); - for (const auto & message : messages) { - auto string_msg = std_msgs::msg::String(); - string_msg.data = message; - // without the sleep_for() many messages are lost. - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - publisher->publish(string_msg); - } - rclcpp::spin_some(node); - } -} - -} // namespace rosbag2 diff --git a/sqlite3_storage_plugin/src/rosbag2/storage/readable_storage.hpp b/sqlite3_storage_plugin/src/rosbag2/storage/readable_storage.hpp deleted file mode 100644 index 93ba44e577..0000000000 --- a/sqlite3_storage_plugin/src/rosbag2/storage/readable_storage.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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. - */ - -#ifndef ROSBAG2__STORAGE__READABLE_STORAGE_HPP_ -#define ROSBAG2__STORAGE__READABLE_STORAGE_HPP_ - -#include -#include - -namespace rosbag2 -{ - -class ReadableStorage -{ -public: - virtual ~ReadableStorage() = default; - - virtual std::vector get_messages() = 0; -}; - -} // namespace rosbag2 - -#endif // ROSBAG2__STORAGE__READABLE_STORAGE_HPP_ diff --git a/sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_storage.cpp b/sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_storage.cpp deleted file mode 100644 index 92c5adb399..0000000000 --- a/sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_storage.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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. - */ - -#include "sqlite_storage.hpp" - -#include -#include -#include -#include -#include - -namespace rosbag2 -{ - -SqliteStorage::SqliteStorage(const std::string & database_name, bool should_initialize) -: database_() -{ - try { - database_ = std::make_unique(database_name); - if (should_initialize) { - initialize(); - } - } catch (const SqliteException & e) { - throw std::runtime_error("Failed to setup storage. Error: " + std::string(e.what())); - } - - std::cout << "Opened database '" << database_name << "'." << std::endl; -} - -SqliteStorage::SqliteStorage(std::shared_ptr database) -: database_(std::move(database)) {} - -bool SqliteStorage::write(const std::string & data) -{ - try { - std::string insert_message = - "INSERT INTO messages (data, timestamp) VALUES ('" + data + "', strftime('%s%f','now'))"; - database_->execute_query(insert_message); - } catch (const SqliteException & e) { - std::cerr << "Failed to write message. Error: " << e.what() << std::endl; - return false; - } - - std::cout << "Stored message '" << data << "'." << std::endl; - return true; -} - -std::vector SqliteStorage::get_messages() -{ - try { - return database_->get_messages(); - } catch (const SqliteException & e) { - std::cerr << "Failed to read messages. Error: " << e.what() << std::endl; - return std::vector(); - } -} - -void SqliteStorage::initialize() -{ - std::string create_table = "CREATE TABLE messages(" \ - "id INTEGER PRIMARY KEY AUTOINCREMENT," \ - "data BLOB NOT NULL," \ - "timestamp INT NOT NULL);"; - - database_->execute_query(create_table); -} - -} // namespace rosbag2 diff --git a/sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_storage.hpp b/sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_storage.hpp deleted file mode 100644 index 771922eeff..0000000000 --- a/sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_storage.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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. - */ - -#ifndef ROSBAG2__STORAGE__SQLITE__SQLITE_STORAGE_HPP_ -#define ROSBAG2__STORAGE__SQLITE__SQLITE_STORAGE_HPP_ - -#include -#include -#include - -#include "sqlite_wrapper.hpp" - -#include "../readable_storage.hpp" -#include "../writable_storage.hpp" - -namespace rosbag2 -{ - -class SqliteStorage : public WritableStorage, public ReadableStorage -{ -public: - SqliteStorage(const std::string & database_name, bool should_initialize); - - // constructor for testing - explicit SqliteStorage(std::shared_ptr database); - - bool write(const std::string & data) override; - - std::vector get_messages() override; - -private: - void initialize(); - - std::shared_ptr database_; -}; - -} // namespace rosbag2 - -#endif // ROSBAG2__STORAGE__SQLITE__SQLITE_STORAGE_HPP_ diff --git a/sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_wrapper.cpp b/sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_wrapper.cpp deleted file mode 100644 index 14775bc6e4..0000000000 --- a/sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_wrapper.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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. - */ - -#include "sqlite_wrapper.hpp" - -#include -#include -#include - -namespace rosbag2 -{ - -SqliteWrapper::SqliteWrapper(const std::string & filename) -: db_ptr(nullptr) -{ - int rc = sqlite3_open(filename.c_str(), &db_ptr); - if (rc) { - throw SqliteException("Could not open database. Error: " + std::string(sqlite3_errmsg(db_ptr))); - } -} - -SqliteWrapper::SqliteWrapper() -: db_ptr(nullptr) {} - -SqliteWrapper::~SqliteWrapper() -{ - sqlite3_close(db_ptr); -} - -void SqliteWrapper::execute_query(const std::string & query) -{ - char * error_msg = nullptr; - int return_code = sqlite3_exec(db_ptr, query.c_str(), nullptr, nullptr, &error_msg); - - if (return_code != SQLITE_OK) { - auto error = "SQL error: " + std::string(error_msg); - sqlite3_free(error_msg); - throw SqliteException(error); - } -} - -std::vector SqliteWrapper::get_messages() -{ - std::vector table_msgs; - sqlite3_stmt * statement; - std::string query = "SELECT * FROM messages"; - - int return_code = sqlite3_prepare_v2(db_ptr, query.c_str(), -1, &statement, nullptr); - if (return_code != SQLITE_OK) { - throw SqliteException("SQL error when preparing statement '" + query + "'with return code: " + - std::to_string(return_code)); - } - - int result = sqlite3_step(statement); - while (result == SQLITE_ROW) { - table_msgs.emplace_back( - std::string(reinterpret_cast(sqlite3_column_text(statement, 1)))); - result = sqlite3_step(statement); - } - sqlite3_finalize(statement); - - return table_msgs; -} - -SqliteWrapper::operator bool() -{ - return db_ptr != nullptr; -} - -} // namespace rosbag2 diff --git a/sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_wrapper.hpp b/sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_wrapper.hpp deleted file mode 100644 index d021ed277b..0000000000 --- a/sqlite3_storage_plugin/src/rosbag2/storage/sqlite/sqlite_wrapper.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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. - */ - -#ifndef ROSBAG2__STORAGE__SQLITE__SQLITE_WRAPPER_HPP_ -#define ROSBAG2__STORAGE__SQLITE__SQLITE_WRAPPER_HPP_ - -#include -#include -#include -#include - -namespace rosbag2 -{ - -class SqliteException : public std::runtime_error -{ -public: - explicit SqliteException(const std::string & message) - : runtime_error(message) {} -}; - -using DBPtr = sqlite3 *; - -class SqliteWrapper -{ -public: - explicit SqliteWrapper(const std::string & filename); - SqliteWrapper(); - virtual ~SqliteWrapper(); - - virtual void execute_query(const std::string & query); - - virtual std::vector get_messages(); - - virtual operator bool(); - -private: - DBPtr db_ptr; -}; - - -} // namespace rosbag2 - -#endif // ROSBAG2__STORAGE__SQLITE__SQLITE_WRAPPER_HPP_ diff --git a/sqlite3_storage_plugin/src/rosbag2/storage/storage_factory.cpp b/sqlite3_storage_plugin/src/rosbag2/storage/storage_factory.cpp deleted file mode 100644 index 3922744939..0000000000 --- a/sqlite3_storage_plugin/src/rosbag2/storage/storage_factory.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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. - */ - -#include "storage_factory.hpp" - -#include -#include -#include -#include -#include - -#include "sqlite/sqlite_storage.hpp" - -namespace rosbag2 -{ - -std::unique_ptr StorageFactory::get_for_reading(const std::string & file_name) -{ - std::ifstream infile(file_name); - if (!infile.good()) { - std::cerr << "Bagfile '" << file_name << "' does not exist." << std::endl; - return std::unique_ptr(); - } - - try { - return std::make_unique(file_name, false); - } catch (std::exception & e) { - std::cerr << "Could not open storage. Error: " << e.what() << std::endl; - } - - return std::unique_ptr(); -} - -std::unique_ptr StorageFactory::get_for_writing(const std::string & file_name) -{ - std::ifstream infile(file_name); - if (infile.good()) { - std::cerr << "Bagfile '" << file_name << "' already exists." << std::endl; - return std::unique_ptr(); - } - - try { - return std::make_unique(file_name, true); - } catch (std::exception & e) { - std::cerr << "Could not initialize storage. Error: " << e.what() << std::endl; - } - - return std::unique_ptr(); -} - -} // namespace rosbag2 diff --git a/sqlite3_storage_plugin/src/rosbag2/storage/storage_factory.hpp b/sqlite3_storage_plugin/src/rosbag2/storage/storage_factory.hpp deleted file mode 100644 index 7f0f262e8b..0000000000 --- a/sqlite3_storage_plugin/src/rosbag2/storage/storage_factory.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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. - */ - -#ifndef ROSBAG2__STORAGE__STORAGE_FACTORY_HPP_ -#define ROSBAG2__STORAGE__STORAGE_FACTORY_HPP_ - -#include -#include - -#include "readable_storage.hpp" -#include "writable_storage.hpp" - -namespace rosbag2 -{ -class StorageFactory -{ -public: - std::unique_ptr get_for_reading(const std::string & file_name); - std::unique_ptr get_for_writing(const std::string & file_name); -}; - -} // namespace rosbag2 - -#endif // ROSBAG2__STORAGE__STORAGE_FACTORY_HPP_ diff --git a/sqlite3_storage_plugin/src/rosbag2/storage/writable_storage.hpp b/sqlite3_storage_plugin/src/rosbag2/storage/writable_storage.hpp deleted file mode 100644 index 876c52c47a..0000000000 --- a/sqlite3_storage_plugin/src/rosbag2/storage/writable_storage.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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. - */ - -#ifndef ROSBAG2__STORAGE__WRITABLE_STORAGE_HPP_ -#define ROSBAG2__STORAGE__WRITABLE_STORAGE_HPP_ - -#include -#include - -namespace rosbag2 -{ - -class WritableStorage -{ -public: - virtual ~WritableStorage() = default; - - virtual bool write(const std::string & data) = 0; -}; - -} // namespace rosbag2 - -#endif // ROSBAG2__STORAGE__WRITABLE_STORAGE_HPP_ diff --git a/sqlite3_storage_plugin/test/rosbag2/storage/mock_sqlite_wrapper.hpp b/sqlite3_storage_plugin/test/rosbag2/storage/mock_sqlite_wrapper.hpp deleted file mode 100644 index ef4ab311db..0000000000 --- a/sqlite3_storage_plugin/test/rosbag2/storage/mock_sqlite_wrapper.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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. - */ - -#ifndef ROSBAG2__STORAGE__MOCK_SQLITE_WRAPPER_HPP_ -#define ROSBAG2__STORAGE__MOCK_SQLITE_WRAPPER_HPP_ - -#include - -#include -#include - -#include "../../../src/rosbag2/storage/sqlite/sqlite_wrapper.hpp" - -class MockSqliteWrapper : public rosbag2::SqliteWrapper -{ -public: - MOCK_METHOD1(execute_query, void(const std::string &)); - MOCK_METHOD0(get_messages, std::vector()); -}; - -#endif // ROSBAG2__STORAGE__MOCK_SQLITE_WRAPPER_HPP_ diff --git a/sqlite3_storage_plugin/test/rosbag2/storage/sqlite_storage_test.cpp b/sqlite3_storage_plugin/test/rosbag2/storage/sqlite_storage_test.cpp deleted file mode 100644 index f80cf81224..0000000000 --- a/sqlite3_storage_plugin/test/rosbag2/storage/sqlite_storage_test.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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. - */ - -#include - -#include -#include -#include -#include - -#include "../../../src/rosbag2/storage/sqlite/sqlite_storage.hpp" -#include "mock_sqlite_wrapper.hpp" -#include "../rosbag2_test_fixture.hpp" - -using namespace ::testing; // NOLINT -using namespace rosbag2; // NOLINT - -TEST_F(Rosbag2TestFixture, write_single_message_to_storage) { - auto sqlite_wrapper = std::make_shared(); - std::string message_to_write = "test_message"; - std::string query = - "INSERT INTO messages (data, timestamp) VALUES ('" + message_to_write + - "', strftime('%s%f','now'))"; - - EXPECT_CALL(*sqlite_wrapper, execute_query(query)); - - auto storage = std::make_unique(sqlite_wrapper); - storage->write(message_to_write); - storage.reset(); -} diff --git a/sqlite3_storage_plugin/test/rosbag2/storage/storage_factory_test.cpp b/sqlite3_storage_plugin/test/rosbag2/storage/storage_factory_test.cpp deleted file mode 100644 index 3695b7391e..0000000000 --- a/sqlite3_storage_plugin/test/rosbag2/storage/storage_factory_test.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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. - */ - -#include - -#include -#include -#include - -#include "../../../src/rosbag2/storage/storage_factory.hpp" - -#include "../rosbag2_test_fixture.hpp" - -using namespace ::testing; // NOLINT -using namespace rosbag2; // NOLINT - -class StorageFactoryFixture : public Rosbag2TestFixture -{ -public: - StorageFactoryFixture() - : factory_(std::make_unique()) {} - - std::unique_ptr factory_; -}; - -TEST_F(StorageFactoryFixture, get_for_writing_returns_nullptr_if_database_already_exists) { - std::ofstream file {database_name_}; - - auto storage = factory_->get_for_writing(database_name_); - - EXPECT_THAT(storage, IsNull()); -} - -TEST_F(StorageFactoryFixture, get_for_writing_returns_a_valid_sqlite_storage_if_no_errors_occur) -{ - auto storage = factory_->get_for_writing(database_name_); - - EXPECT_THAT(storage, NotNull()); -} - -TEST_F(StorageFactoryFixture, get_for_reading_returns_nullptr_if_file_does_not_exist) { - auto storage = factory_->get_for_reading(database_name_); - - EXPECT_THAT(storage, IsNull()); -} - -TEST_F(StorageFactoryFixture, get_for_reading_returns_a_valid_sqlite_storage_if_file_exists) -{ - std::ofstream file {database_name_}; - - auto readable_storage = factory_->get_for_reading(database_name_); - - EXPECT_THAT(readable_storage, NotNull()); -}