├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── include └── record_ros │ └── record.h ├── launch └── record_ros.launch ├── package.xml ├── src ├── record.cpp └── record_node.cpp └── srv └── String_cmd.srv /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | build/* 11 | CMakeLists.txt.* 12 | CMakeLists.txt.user 13 | CMakeFiles/ 14 | *~ 15 | 16 | # Packages # 17 | ############ 18 | # it's better to unpack these files and commit the raw source 19 | # git has its own built in compression methods 20 | *.7z 21 | *.dmg 22 | *.gz 23 | *.iso 24 | *.jar 25 | *.rar 26 | *.tar 27 | *.zip 28 | 29 | # Logs and databases # 30 | ###################### 31 | *.log 32 | *.sql 33 | *.sqlite 34 | 35 | # OS generated files # 36 | ###################### 37 | .DS_Store 38 | .DS_Store? 39 | ._* 40 | .Spotlight-V100 41 | .Trashes 42 | ehthumbs.db 43 | Thumbs.db 44 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Generic .travis.yml file for running continuous integration on Travis-CI with 2 | # any ROS package. 3 | # 4 | # This installs ROS on a clean Travis-CI virtual machine, creates a ROS 5 | # workspace, resolves all listed dependencies, and sets environment variables 6 | # (setup.bash). Then, it compiles the entire ROS workspace (ensuring there are 7 | # no compilation errors), and runs all the tests. If any of the compilation/test 8 | # phases fail, the build is marked as a failure. 9 | # 10 | # We handle two types of package dependencies: 11 | # - packages (ros and otherwise) available through apt-get. These are installed 12 | # using rosdep, based on the information in the ROS package.xml. 13 | # - dependencies that must be checked out from source. These are handled by 14 | # 'wstool', and should be listed in a file named dependencies.rosinstall. 15 | # 16 | # There are two variables you may want to change: 17 | # - ROS_DISTRO (default is indigo). Note that packages must be available for 18 | # ubuntu 14.04 trusty. 19 | # - ROSINSTALL_FILE (default is dependencies.rosinstall inside the repo 20 | # root). This should list all necessary repositories in wstool format (see 21 | # the ros wiki). If the file does not exists then nothing happens. 22 | # 23 | # See the README.md for more information. 24 | # 25 | # Author: Felix Duvallet 26 | 27 | # NOTE: The build lifecycle on Travis.ci is something like this: 28 | # before_install 29 | # install 30 | # before_script 31 | # script 32 | # after_success or after_failure 33 | # after_script 34 | # OPTIONAL before_deploy 35 | # OPTIONAL deploy 36 | # OPTIONAL after_deploy 37 | 38 | ################################################################################ 39 | 40 | # Use ubuntu trusty (14.04) with sudo privileges. 41 | dist: trusty 42 | sudo: required 43 | language: 44 | - generic 45 | cache: 46 | - apt 47 | 48 | # Configuration variables. All variables are global now, but this can be used to 49 | # trigger a build matrix for different ROS distributions if desired. 50 | env: 51 | global: 52 | - ROS_DISTRO=indigo 53 | - ROS_CI_DESKTOP="`lsb_release -cs`" # e.g. [precise|trusty|...] 54 | - CI_SOURCE_PATH=$(pwd) 55 | - ROSINSTALL_FILE=$CI_SOURCE_PATH/dependencies.rosinstall 56 | - CATKIN_OPTIONS=$CI_SOURCE_PATH/catkin.options 57 | - ROS_PARALLEL_JOBS='-j8 -l6' 58 | 59 | ################################################################################ 60 | 61 | # Install system dependencies, namely a very barebones ROS setup. 62 | before_install: 63 | - sudo sh -c "echo \"deb http://packages.ros.org/ros/ubuntu $ROS_CI_DESKTOP main\" > /etc/apt/sources.list.d/ros-latest.list" 64 | - wget http://packages.ros.org/ros.key -O - | sudo apt-key add - 65 | - sudo apt-get update -qq 66 | - sudo apt-get install -y python-catkin-pkg python-rosdep python-wstool ros-$ROS_DISTRO-catkin 67 | - source /opt/ros/$ROS_DISTRO/setup.bash 68 | # Prepare rosdep to install dependencies. 69 | - sudo rosdep init 70 | - rosdep update 71 | 72 | # Create a catkin workspace with the package under integration. 73 | install: 74 | - mkdir -p ~/catkin_ws/src 75 | - cd ~/catkin_ws/src 76 | - catkin_init_workspace 77 | # Create the devel/setup.bash (run catkin_make with an empty workspace) and 78 | # source it to set the path variables. 79 | - cd ~/catkin_ws 80 | - catkin_make 81 | - source devel/setup.bash 82 | # Add the package under integration to the workspace using a symlink. 83 | - cd ~/catkin_ws/src 84 | - ln -s $CI_SOURCE_PATH . 85 | 86 | # Install all dependencies, using wstool and rosdep. 87 | # wstool looks for a ROSINSTALL_FILE defined in the environment variables. 88 | before_script: 89 | # source dependencies: install using wstool. 90 | - cd ~/catkin_ws/src 91 | - wstool init 92 | - if [[ -f $ROSINSTALL_FILE ]] ; then wstool merge $ROSINSTALL_FILE ; fi 93 | - wstool up 94 | # package depdencies: install using rosdep. 95 | - cd ~/catkin_ws 96 | - rosdep install -y --from-paths src --ignore-src --rosdistro $ROS_DISTRO 97 | 98 | # Compile and test. If the CATKIN_OPTIONS file exists, use it as an argument to 99 | # catkin_make. 100 | script: 101 | - cd ~/catkin_ws 102 | - catkin_make $( [ -f $CATKIN_OPTIONS ] && cat $CATKIN_OPTIONS ) 103 | # Testing: Use both run_tests (to see the output) and test (to error out). 104 | - catkin_make run_tests # This always returns 0, but looks pretty. 105 | - catkin_make test # This will return non-zero if a test fails. 106 | 107 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(record_ros) 3 | 4 | ## Find catkin macros and libraries 5 | ## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz) 6 | ## is used, also find other catkin packages 7 | find_package(catkin REQUIRED roscpp message_generation rosbag std_msgs) 8 | find_package(Boost REQUIRED COMPONENTS regex ) 9 | 10 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -O3 -std=c++11 -mtune=native -fPIC -Wall") ## Optimize 11 | 12 | add_service_files( 13 | FILES 14 | String_cmd.srv 15 | ) 16 | 17 | generate_messages( 18 | DEPENDENCIES 19 | std_msgs 20 | ) 21 | 22 | catkin_package( 23 | INCLUDE_DIRS include 24 | LIBRARIES ${PROJECT_NAME} 25 | CATKIN_DEPENDS roscpp message_runtime std_msgs 26 | # DEPENDS system_lib 27 | ) 28 | 29 | 30 | ########### 31 | ## Build ## 32 | ########### 33 | 34 | set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) 35 | set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) 36 | 37 | 38 | set(HEADER_FILES ${INCLUDE_DIR}/record_ros/record.h ) 39 | 40 | set(SRC_FILES ${SRC_DIR}/record.cpp 41 | ${SRC_DIR}/record_node.cpp 42 | ) 43 | 44 | include_directories( 45 | ${catkin_INCLUDE_DIRS} 46 | ${INCLUDE_DIR} 47 | ${INCLUDE_STATS} 48 | ${Boost_INCLUDE_DIRS} 49 | ) 50 | 51 | add_executable(${PROJECT_NAME} ${SRC_FILES} ${HEADER_FILES}) 52 | target_link_libraries(${PROJECT_NAME} ${catkin_LIBRARIES} ${Boost_REGEX_LIBRARY}) 53 | 54 | 55 | add_dependencies(${PROJECT_NAME} ${catkin_EXPORTED_TARGETS} ${PROJECT_NAME}_gencpp ${PROJECT_NAME}_generate_messages_cpp) 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 LASA Laboratory, EPFL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # record_ros 2 | [![Build Status](https://travis-ci.org/epfl-lasa/record_ros.svg?branch=master)](https://travis-ci.org/epfl-lasa/record_ros/branches) 3 | 4 | A callback wrapper for rosbag record, where a ros service allows a user to start recording a set of topics and 5 | also stop. 6 | 7 | # Launch file 8 | 9 | Three arguments have to be over-written in your own launch file; 10 | 11 | * **path_save**: system dependent path to folder where all the rosbags will be put: /home/user/rosbag_record 12 | * **topic**: set of space sperated topics: topic1 topic2 topic3 ... 13 | * **file_name**: the name prefix which will be put before the rosbag time stamp (optional) 14 | 15 | # Quick example 16 | 17 | TODO 18 | -------------------------------------------------------------------------------- /include/record_ros/record.h: -------------------------------------------------------------------------------- 1 | #ifndef RECORD_ROS_RECORD_H_ 2 | #define RECORD_ROS_RECORD_H_ 3 | 4 | #include "record_ros/String_cmd.h" 5 | #include 6 | #include 7 | 8 | class Record : public rosbag::Recorder{ 9 | 10 | public: 11 | 12 | Record(ros::NodeHandle& nh,rosbag::RecorderOptions const& options); 13 | 14 | void wait_for_callback(); 15 | 16 | private: 17 | 18 | bool string_command(record_ros::String_cmd::Request& req, record_ros::String_cmd::Response& res); 19 | 20 | private: 21 | 22 | bool b_record; 23 | ros::ServiceServer service_srv; 24 | ros::Subscriber topic_cmd; 25 | 26 | }; 27 | 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /launch/record_ros.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | record_ros 4 | 0.0.0 5 | The record_ros package 6 | 7 | 8 | 9 | 10 | guillaume 11 | 12 | 13 | 14 | 15 | 16 | TODO 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | catkin 43 | 44 | roscpp 45 | roscpp 46 | 47 | message_generation 48 | message_generation 49 | 50 | message_runtime 51 | message_runtime 52 | 53 | rosbag 54 | rosbag 55 | 56 | std_msgs 57 | std_msgs 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/record.cpp: -------------------------------------------------------------------------------- 1 | #include "record_ros/record.h" 2 | #include 3 | #include 4 | 5 | Record::Record(ros::NodeHandle &nh,rosbag::RecorderOptions const& options): 6 | rosbag::Recorder(options) 7 | { 8 | service_srv = nh.advertiseService("cmd",&Record::string_command,this); 9 | b_record = false; 10 | 11 | } 12 | 13 | void Record::wait_for_callback(){ 14 | ros::Rate r(100); // 60 hz 15 | while (!b_record && ros::ok()) 16 | { 17 | ros::spinOnce(); 18 | r.sleep(); 19 | } 20 | } 21 | 22 | bool Record::string_command(record_ros::String_cmd::Request& req, record_ros::String_cmd::Response& res){ 23 | std::string cmd = req.cmd; 24 | ROS_INFO("Record callback"); 25 | if(cmd == "record"){ 26 | if(b_record){ 27 | ros::shutdown(); 28 | res.res = "stopping recorder"; 29 | }else{ 30 | b_record = true; 31 | res.res = "starting recorder"; 32 | } 33 | return true; 34 | }else if(cmd == "stop"){ 35 | ros::shutdown(); 36 | res.res = "stopping recorder"; 37 | return true; 38 | }else{ 39 | res.res = "No such command[" + cmd + "] in [Record::string_command]"; 40 | ROS_WARN_STREAM(res.res); 41 | return false; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/record_node.cpp: -------------------------------------------------------------------------------- 1 | #include "record_ros/record.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | std::string normalize(std::string path) 8 | { 9 | if(path[0] == '~') 10 | { 11 | char* home = getenv("HOME"); 12 | return home + path.substr(1); 13 | } 14 | else 15 | { 16 | return path; 17 | } 18 | 19 | } 20 | 21 | int main(int argc, char** argv) 22 | { 23 | 24 | ros::init(argc, argv,"record_node"); 25 | ros::NodeHandle nh; 26 | 27 | std::string topic, path_save, file_name; 28 | if (!nh.getParam("/record/topic", topic)) 29 | { 30 | ROS_WARN("no topic param found"); 31 | } 32 | if (!nh.getParam("/record/path_save", path_save)) 33 | { 34 | ROS_WARN("no path_save param found"); 35 | }else{ 36 | path_save = normalize(path_save); 37 | boost::filesystem::path config_folder(path_save); 38 | if(!(boost::filesystem::exists(config_folder))) 39 | { 40 | ROS_WARN_STREAM("TARGET directory to save bag not found, make sure this path [" << path_save << "] exists"); 41 | } 42 | } 43 | if (!nh.getParam("/record/file_name", file_name)) 44 | { 45 | ROS_WARN("file_name param found"); 46 | } 47 | 48 | 49 | // split topics with space (use regrex) 50 | ROS_INFO("Starting record node"); 51 | ROS_INFO_STREAM("topics: " << topic); 52 | ROS_INFO_STREAM("name: " << path_save); 53 | ROS_INFO_STREAM("file_name: " << file_name); 54 | 55 | // Extract all topics white space seperated 56 | rosbag::RecorderOptions options; 57 | 58 | options.prefix = path_save + "/" + file_name; 59 | 60 | if(topic.empty()) 61 | { 62 | options.record_all = true; 63 | } 64 | else 65 | { 66 | boost::regex rgx("\\s+"); 67 | boost::sregex_token_iterator iter(topic.begin(), topic.end(),rgx,-1); 68 | boost::sregex_token_iterator end; 69 | std::vector topics; 70 | for ( ; iter != end; ++iter){ 71 | topics.push_back(*iter); 72 | } 73 | 74 | options.topics = topics; 75 | } 76 | 77 | Record* record = new Record(nh,options); 78 | //ROS_INFO_STREAM("RECORD_NODE: Waiting for callback()!"); 79 | record->wait_for_callback(); 80 | //ROS_INFO_STREAM("RECORD_NODE: START!"); 81 | int ret = record->run(); 82 | 83 | 84 | return ret; 85 | } 86 | -------------------------------------------------------------------------------- /srv/String_cmd.srv: -------------------------------------------------------------------------------- 1 | string cmd 2 | --- 3 | string res 4 | --------------------------------------------------------------------------------