From 041a53b75f36ee91edfbc5516748e9a29b8b7300 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 3 Feb 2023 16:34:41 +0100 Subject: [PATCH 01/24] Refactor command parser to use vector of char* Signed-off-by: LucasHaug --- src/dynamic_bridge.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/dynamic_bridge.cpp b/src/dynamic_bridge.cpp index 1e1a5522..8474c03b 100644 --- a/src/dynamic_bridge.cpp +++ b/src/dynamic_bridge.cpp @@ -57,22 +57,37 @@ struct Bridge2to1HandlesAndMessageTypes std::string ros2_type_name; }; -bool find_command_option(const std::vector & args, const std::string & option) +bool find_command_option(const std::vector & args, const std::string & option) { - return std::find(args.begin(), args.end(), option) != args.end(); + auto it = std::find_if(args.begin(), args.end(), [] (const char * & element) { + return strcmp(element, option) == 0; + }); + + return it != args.end(); } -bool get_flag_option(const std::vector & args, const std::string & option) +bool get_flag_option(std::vector & args, const std::string & option, bool & remove = false) { - auto it = std::find(args.begin(), args.end(), option); - return it != args.end(); + auto it = std::find_if(args.begin(), args.end(), [] (const char * & element) { + return strcmp(element, option) == 0; + }); + + if (it != args.end()) { + if (remove) { + args.erase(it); + } + return true; + } + + return false; +} } bool parse_command_options( int argc, char ** argv, bool & output_topic_introspection, bool & bridge_all_1to2_topics, bool & bridge_all_2to1_topics) { - std::vector args(argv, argv + argc); + std::vector args(argv, argv + argc); if (find_command_option(args, "-h") || find_command_option(args, "--help")) { std::stringstream ss; From 82bdd301ec823e28277e101a51012e92c4a0bf99 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 3 Feb 2023 16:35:29 +0100 Subject: [PATCH 02/24] Add argc argv splitter to dynamic bridge Signed-off-by: LucasHaug --- src/dynamic_bridge.cpp | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/src/dynamic_bridge.cpp b/src/dynamic_bridge.cpp index 8474c03b..358f9c10 100644 --- a/src/dynamic_bridge.cpp +++ b/src/dynamic_bridge.cpp @@ -81,10 +81,29 @@ bool get_flag_option(std::vector & args, const std::string & optio return false; } + +void split_ros1_ros2_args( + const std::vector & args, std::vector & ros1_args, + std::vector & ros2_args) +{ + // Start iterating from the second argument, since the first argument is the executable name + auto it = std::find_if(args.begin() + 1, args.end(), [] (const char * & element) { + return strcmp(element, "--ros-args") == 0; + }); + + if (it != args.end()) { + ros1_args = std::vector(args.begin(), it); + ros2_args = std::vector(it, args.end()); + ros2_args.insert(ros2_args.begin(), args.at(0)); + } else { + ros1_args = args; + ros2_args = args; + } } bool parse_command_options( - int argc, char ** argv, bool & output_topic_introspection, + int argc, char ** argv, std::vector & ros1_args, + std::vector & ros2_args, bool & output_topic_introspection, bool & bridge_all_1to2_topics, bool & bridge_all_2to1_topics) { std::vector args(argv, argv + argc); @@ -129,11 +148,13 @@ bool parse_command_options( return false; } - output_topic_introspection = get_flag_option(args, "--show-introspection"); + output_topic_introspection = get_flag_option(args, "--show-introspection", true); + + bool bridge_all_topics = get_flag_option(args, "--bridge-all-topics", true); + bridge_all_1to2_topics = bridge_all_topics || get_flag_option(args, "--bridge-all-1to2-topics", true); + bridge_all_2to1_topics = bridge_all_topics || get_flag_option(args, "--bridge-all-2to1-topics", true); - bool bridge_all_topics = get_flag_option(args, "--bridge-all-topics"); - bridge_all_1to2_topics = bridge_all_topics || get_flag_option(args, "--bridge-all-1to2-topics"); - bridge_all_2to1_topics = bridge_all_topics || get_flag_option(args, "--bridge-all-2to1-topics"); + split_ros1_ros2_args(args, ros1_args, ros2_args); return true; } @@ -477,19 +498,24 @@ int main(int argc, char * argv[]) bool output_topic_introspection; bool bridge_all_1to2_topics; bool bridge_all_2to1_topics; + + std::vector ros1_args; + std::vector ros2_args; + if (!parse_command_options( - argc, argv, output_topic_introspection, bridge_all_1to2_topics, bridge_all_2to1_topics)) + argc, argv, ros1_args, ros2_args, output_topic_introspection, + bridge_all_1to2_topics, bridge_all_2to1_topics)) { return 0; } // ROS 2 node - rclcpp::init(argc, argv); + rclcpp::init(ros2_args.size(), ros2_args.data()); auto ros2_node = rclcpp::Node::make_shared("ros_bridge"); // ROS 1 node - ros::init(argc, argv, "ros_bridge"); + ros::init(ros1_args.size(), ros1_args.data(), "ros_bridge"); ros::NodeHandle ros1_node; // mapping of available topic names to type names From 5359817c2607a4a8dd4f565469ad00b4a09b0fe4 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 3 Feb 2023 16:37:50 +0100 Subject: [PATCH 03/24] Add argc argv splitter to paramter bridge Signed-off-by: LucasHaug --- src/parameter_bridge.cpp | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/parameter_bridge.cpp b/src/parameter_bridge.cpp index a95d49a4..dfc750b7 100644 --- a/src/parameter_bridge.cpp +++ b/src/parameter_bridge.cpp @@ -229,14 +229,37 @@ rclcpp::QoS qos_from_params(XmlRpc::XmlRpcValue qos_params) return ros2_publisher_qos; } +void split_ros1_ros2_args( + const std::vector & args, std::vector & ros1_args, + std::vector & ros2_args) +{ + // Start iterating from the second argument, since the first argument is the executable name + auto it = std::find_if(args.begin() + 1, args.end(), [] (const char * & element) { + return strcmp(element, "--ros-args") == 0; + }); + + if (it != args.end()) { + ros1_args = std::vector(args.begin(), it); + ros2_args = std::vector(it, args.end()); + ros2_args.insert(ros2_args.begin(), args.at(0)); + } else { + ros1_args = args; + ros2_args = args; + } +} + int main(int argc, char * argv[]) { + std::vector ros1_args; + std::vector ros2_args; + split_ros1_ros2_args(std::vector(argv, argv + argc), ros1_args, ros2_args); + // ROS 1 node - ros::init(argc, argv, "ros_bridge"); + ros::init(ros1_args.size(), ros1_args.data(), "ros_bridge"); ros::NodeHandle ros1_node; // ROS 2 node - rclcpp::init(argc, argv); + rclcpp::init(ros2_args.size(), ros2_args.data()); auto ros2_node = rclcpp::Node::make_shared("ros_bridge"); std::list all_handles; From fd96bcf0e80b7d24cd43659df3ba49437454ed94 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 3 Feb 2023 16:46:31 +0100 Subject: [PATCH 04/24] Update github action Signed-off-by: LucasHaug --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e6aed2ef..199cf734 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,10 +4,10 @@ on: [push, pull_request] # on all pushes and PRs jobs: ros1_bridge: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - - uses: ros-tooling/setup-ros@v0.2 + - uses: ros-tooling/setup-ros@v0.5 with: required-ros-distributions: "noetic rolling" - name: Build and test ros1-bridge From 40dd1b2fd3e9fbb2b66ba5849ba2721d55c52760 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 3 Feb 2023 17:22:27 +0100 Subject: [PATCH 05/24] Fix parameter bridge argv split Signed-off-by: LucasHaug --- src/parameter_bridge.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/parameter_bridge.cpp b/src/parameter_bridge.cpp index dfc750b7..a6f1d748 100644 --- a/src/parameter_bridge.cpp +++ b/src/parameter_bridge.cpp @@ -252,7 +252,8 @@ int main(int argc, char * argv[]) { std::vector ros1_args; std::vector ros2_args; - split_ros1_ros2_args(std::vector(argv, argv + argc), ros1_args, ros2_args); + std::vector args(argv, argv + argc); + split_ros1_ros2_args(args, ros1_args, ros2_args); // ROS 1 node ros::init(ros1_args.size(), ros1_args.data(), "ros_bridge"); From 883d777f50f48a6733090697fcf2b18d5224f784 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 3 Feb 2023 17:31:03 +0100 Subject: [PATCH 06/24] Remove static argv reading Signed-off-by: LucasHaug --- src/parameter_bridge.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/parameter_bridge.cpp b/src/parameter_bridge.cpp index a6f1d748..62ff6b3f 100644 --- a/src/parameter_bridge.cpp +++ b/src/parameter_bridge.cpp @@ -282,15 +282,6 @@ int main(int argc, char * argv[]) const char * services_2_to_1_parameter_name = "services_2_to_1"; const char * service_execution_timeout_parameter_name = "ros1_bridge/parameter_bridge/service_execution_timeout"; - if (argc > 1) { - topics_parameter_name = argv[1]; - } - if (argc > 2) { - services_1_to_2_parameter_name = argv[2]; - } - if (argc > 3) { - services_2_to_1_parameter_name = argv[3]; - } // Topics XmlRpc::XmlRpcValue topics; From ce8b1787221c3866fdce3e0c2721ce518097fb49 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 3 Feb 2023 17:57:57 +0100 Subject: [PATCH 07/24] Add command parser to parameter bridge Signed-off-by: LucasHaug --- src/parameter_bridge.cpp | 111 +++++++++++++++++++++++++++++++++------ 1 file changed, 95 insertions(+), 16 deletions(-) diff --git a/src/parameter_bridge.cpp b/src/parameter_bridge.cpp index 62ff6b3f..9e8112bc 100644 --- a/src/parameter_bridge.cpp +++ b/src/parameter_bridge.cpp @@ -229,6 +229,39 @@ rclcpp::QoS qos_from_params(XmlRpc::XmlRpcValue qos_params) return ros2_publisher_qos; } +bool find_command_option(const std::vector & args, const std::string & option) +{ + auto it = std::find_if(args.begin(), args.end(), [] (const char * & element) { + return strcmp(element, option) == 0; + }); + + return it != args.end(); +} + +bool get_flag_option(std::vector & args, const std::string & option, char * & value, bool & remove = false) +{ + auto it = std::find_if(args.begin(), args.end(), [] (const char * & element) { + return strcmp(element, option) == 0; + }); + + if (it != args.end()) { + auto value_it = it++; + + if (value_it != args.end()) { + value = *value_it; + + if (remove) { + args.erase(it); + args.erase(value_it); + } + + return true; + } + } + + return false; +} + void split_ros1_ros2_args( const std::vector & args, std::vector & ros1_args, std::vector & ros2_args) @@ -248,24 +281,52 @@ void split_ros1_ros2_args( } } -int main(int argc, char * argv[]) +bool parse_command_options( + int argc, char ** argv, std::vector & ros1_args, + std::vector & ros2_args, const char * & topics_parameter_name, + const char * & services_1_to_2_parameter_name, const char * & services_2_to_1_parameter_name) { - std::vector ros1_args; - std::vector ros2_args; + topics_parameter_name = "topics"; + services_1_to_2_parameter_name = "services_1_to_2"; + services_2_to_1_parameter_name = "services_2_to_1"; + std::vector args(argv, argv + argc); - split_ros1_ros2_args(args, ros1_args, ros2_args); - // ROS 1 node - ros::init(ros1_args.size(), ros1_args.data(), "ros_bridge"); - ros::NodeHandle ros1_node; + if (find_command_option(args, "-h") || find_command_option(args, "--help")) { + std::stringstream ss; + ss << "Usage:" << std::endl; + ss << " -h, --help: This message." << std::endl; + ss << " --topics: Name of the parameter that contains the list of topics to bridge."; + ss << std::endl; + ss << " --services_1_to_2: Name of the parameter that contains the list of services to bridge from ROS 1 to ROS 2."; + ss << std::endl; + ss << " --services_2_to_1: Name of the parameter that contains the list of services to bridge from ROS 2 to ROS 1."; + ss << std::endl; + std::cout << ss.str(); + return false; + } - // ROS 2 node - rclcpp::init(ros2_args.size(), ros2_args.data()); - auto ros2_node = rclcpp::Node::make_shared("ros_bridge"); + if (get_flag_option(args, "--topics", topics_parameter_name, true)) { + printf("Using default topics parameter name: %s", topics_parameter_name); + } - std::list all_handles; - std::list service_bridges_1_to_2; - std::list service_bridges_2_to_1; + if (get_flag_option(args, "--services_1_to_2", services_1_to_2_parameter_name, true)) { + printf("Using default services_1_to_2 parameter name: %s", services_1_to_2_parameter_name); + } + + if (get_flag_option(args, "--services_2_to_1", services_2_to_1_parameter_name, true)) { + printf("Using default services_2_to_1 parameter name: %s", services_2_to_1_parameter_name); + } + + split_ros1_ros2_args(args, ros1_args, ros2_args); + + return true; +} + +int main(int argc, char * argv[]) +{ + std::vector ros1_args; + std::vector ros2_args; // bridge all topics listed in a ROS 1 parameter // the topics parameter needs to be an array @@ -273,16 +334,34 @@ int main(int argc, char * argv[]) // topic: the name of the topic to bridge (e.g. '/topic_name') // type: the type of the topic to bridge (e.g. 'pkgname/msg/MsgName') // queue_size: the queue size to use (default: 100) - const char * topics_parameter_name = "topics"; + const char * topics_parameter_name; // the services parameters need to be arrays // and each item needs to be a dictionary with the following keys; // service: the name of the service to bridge (e.g. '/service_name') // type: the type of the service to bridge (e.g. 'pkgname/srv/SrvName') - const char * services_1_to_2_parameter_name = "services_1_to_2"; - const char * services_2_to_1_parameter_name = "services_2_to_1"; + const char * services_1_to_2_parameter_name; + const char * services_2_to_1_parameter_name; const char * service_execution_timeout_parameter_name = "ros1_bridge/parameter_bridge/service_execution_timeout"; + if (!parse_command_options( + argc, argv, ros1_args, ros2_args, topics_parameter_name, + services_1_to_2_parameter_name, services_2_to_1_parameter_name)) { + return 0; + } + + // ROS 1 node + ros::init(ros1_args.size(), ros1_args.data(), "ros_bridge"); + ros::NodeHandle ros1_node; + + // ROS 2 node + rclcpp::init(ros2_args.size(), ros2_args.data()); + auto ros2_node = rclcpp::Node::make_shared("ros_bridge"); + + std::list all_handles; + std::list service_bridges_1_to_2; + std::list service_bridges_2_to_1; + // Topics XmlRpc::XmlRpcValue topics; if ( From 2911f8f0a10c65348d28969771ba34ab8fb1ebf1 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 3 Feb 2023 19:25:30 +0100 Subject: [PATCH 08/24] Fix compilation errors Signed-off-by: LucasHaug --- src/dynamic_bridge.cpp | 2 +- src/parameter_bridge.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dynamic_bridge.cpp b/src/dynamic_bridge.cpp index 358f9c10..b2c816de 100644 --- a/src/dynamic_bridge.cpp +++ b/src/dynamic_bridge.cpp @@ -66,7 +66,7 @@ bool find_command_option(const std::vector & args, const std::stri return it != args.end(); } -bool get_flag_option(std::vector & args, const std::string & option, bool & remove = false) +bool get_flag_option(std::vector & args, const std::string & option, bool remove = false) { auto it = std::find_if(args.begin(), args.end(), [] (const char * & element) { return strcmp(element, option) == 0; diff --git a/src/parameter_bridge.cpp b/src/parameter_bridge.cpp index 9e8112bc..f05dba6f 100644 --- a/src/parameter_bridge.cpp +++ b/src/parameter_bridge.cpp @@ -231,16 +231,16 @@ rclcpp::QoS qos_from_params(XmlRpc::XmlRpcValue qos_params) bool find_command_option(const std::vector & args, const std::string & option) { - auto it = std::find_if(args.begin(), args.end(), [] (const char * & element) { + auto it = std::find_if(args.begin(), args.end(), [&option] (const char * & element) { return strcmp(element, option) == 0; }); return it != args.end(); } -bool get_flag_option(std::vector & args, const std::string & option, char * & value, bool & remove = false) +bool get_flag_option(std::vector & args, const std::string & option, char * & value, bool remove = false) { - auto it = std::find_if(args.begin(), args.end(), [] (const char * & element) { + auto it = std::find_if(args.begin(), args.end(), [&option] (const char * & element) { return strcmp(element, option) == 0; }); @@ -283,8 +283,8 @@ void split_ros1_ros2_args( bool parse_command_options( int argc, char ** argv, std::vector & ros1_args, - std::vector & ros2_args, const char * & topics_parameter_name, - const char * & services_1_to_2_parameter_name, const char * & services_2_to_1_parameter_name) + std::vector & ros2_args, char * & topics_parameter_name, + char * & services_1_to_2_parameter_name, char * & services_2_to_1_parameter_name) { topics_parameter_name = "topics"; services_1_to_2_parameter_name = "services_1_to_2"; @@ -334,13 +334,13 @@ int main(int argc, char * argv[]) // topic: the name of the topic to bridge (e.g. '/topic_name') // type: the type of the topic to bridge (e.g. 'pkgname/msg/MsgName') // queue_size: the queue size to use (default: 100) - const char * topics_parameter_name; + char * topics_parameter_name; // the services parameters need to be arrays // and each item needs to be a dictionary with the following keys; // service: the name of the service to bridge (e.g. '/service_name') // type: the type of the service to bridge (e.g. 'pkgname/srv/SrvName') - const char * services_1_to_2_parameter_name; - const char * services_2_to_1_parameter_name; + char * services_1_to_2_parameter_name; + char * services_2_to_1_parameter_name; const char * service_execution_timeout_parameter_name = "ros1_bridge/parameter_bridge/service_execution_timeout"; From 8650d49f9e8cace3ba40e32053856c0ec9c52cb6 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Mon, 6 Feb 2023 09:03:19 +0000 Subject: [PATCH 09/24] Fix compilation errors Signed-off-by: LucasHaug --- src/dynamic_bridge.cpp | 17 +++++++++-------- src/parameter_bridge.cpp | 25 +++++++++++++------------ 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/dynamic_bridge.cpp b/src/dynamic_bridge.cpp index b2c816de..a0201ca6 100644 --- a/src/dynamic_bridge.cpp +++ b/src/dynamic_bridge.cpp @@ -59,8 +59,8 @@ struct Bridge2to1HandlesAndMessageTypes bool find_command_option(const std::vector & args, const std::string & option) { - auto it = std::find_if(args.begin(), args.end(), [] (const char * & element) { - return strcmp(element, option) == 0; + auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { + return strcmp(element, option.c_str()) == 0; }); return it != args.end(); @@ -68,8 +68,8 @@ bool find_command_option(const std::vector & args, const std::stri bool get_flag_option(std::vector & args, const std::string & option, bool remove = false) { - auto it = std::find_if(args.begin(), args.end(), [] (const char * & element) { - return strcmp(element, option) == 0; + auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { + return strcmp(element, option.c_str()) == 0; }); if (it != args.end()) { @@ -87,7 +87,7 @@ void split_ros1_ros2_args( std::vector & ros2_args) { // Start iterating from the second argument, since the first argument is the executable name - auto it = std::find_if(args.begin() + 1, args.end(), [] (const char * & element) { + auto it = std::find_if(args.begin() + 1, args.end(), [] (const char * const & element) { return strcmp(element, "--ros-args") == 0; }); @@ -499,8 +499,8 @@ int main(int argc, char * argv[]) bool bridge_all_1to2_topics; bool bridge_all_2to1_topics; - std::vector ros1_args; - std::vector ros2_args; + std::vector ros1_args; + std::vector ros2_args; if (!parse_command_options( argc, argv, ros1_args, ros2_args, output_topic_introspection, @@ -515,7 +515,8 @@ int main(int argc, char * argv[]) auto ros2_node = rclcpp::Node::make_shared("ros_bridge"); // ROS 1 node - ros::init(ros1_args.size(), ros1_args.data(), "ros_bridge"); + int argc_ros1 = ros1_args.size(); + ros::init(argc_ros1, const_cast(ros1_args.data()), "ros_bridge"); ros::NodeHandle ros1_node; // mapping of available topic names to type names diff --git a/src/parameter_bridge.cpp b/src/parameter_bridge.cpp index f05dba6f..fb2ce6ba 100644 --- a/src/parameter_bridge.cpp +++ b/src/parameter_bridge.cpp @@ -231,17 +231,17 @@ rclcpp::QoS qos_from_params(XmlRpc::XmlRpcValue qos_params) bool find_command_option(const std::vector & args, const std::string & option) { - auto it = std::find_if(args.begin(), args.end(), [&option] (const char * & element) { - return strcmp(element, option) == 0; + auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { + return strcmp(element, option.c_str()) == 0; }); return it != args.end(); } -bool get_flag_option(std::vector & args, const std::string & option, char * & value, bool remove = false) +bool get_flag_option(std::vector & args, const std::string & option, const char * & value, bool remove = false) { - auto it = std::find_if(args.begin(), args.end(), [&option] (const char * & element) { - return strcmp(element, option) == 0; + auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { + return strcmp(element, option.c_str()) == 0; }); if (it != args.end()) { @@ -267,7 +267,7 @@ void split_ros1_ros2_args( std::vector & ros2_args) { // Start iterating from the second argument, since the first argument is the executable name - auto it = std::find_if(args.begin() + 1, args.end(), [] (const char * & element) { + auto it = std::find_if(args.begin() + 1, args.end(), [] (const char * const & element) { return strcmp(element, "--ros-args") == 0; }); @@ -283,8 +283,8 @@ void split_ros1_ros2_args( bool parse_command_options( int argc, char ** argv, std::vector & ros1_args, - std::vector & ros2_args, char * & topics_parameter_name, - char * & services_1_to_2_parameter_name, char * & services_2_to_1_parameter_name) + std::vector & ros2_args, const char * & topics_parameter_name, + const char * & services_1_to_2_parameter_name, const char * & services_2_to_1_parameter_name) { topics_parameter_name = "topics"; services_1_to_2_parameter_name = "services_1_to_2"; @@ -334,13 +334,13 @@ int main(int argc, char * argv[]) // topic: the name of the topic to bridge (e.g. '/topic_name') // type: the type of the topic to bridge (e.g. 'pkgname/msg/MsgName') // queue_size: the queue size to use (default: 100) - char * topics_parameter_name; + const char * topics_parameter_name; // the services parameters need to be arrays // and each item needs to be a dictionary with the following keys; // service: the name of the service to bridge (e.g. '/service_name') // type: the type of the service to bridge (e.g. 'pkgname/srv/SrvName') - char * services_1_to_2_parameter_name; - char * services_2_to_1_parameter_name; + const char * services_1_to_2_parameter_name; + const char * services_2_to_1_parameter_name; const char * service_execution_timeout_parameter_name = "ros1_bridge/parameter_bridge/service_execution_timeout"; @@ -351,7 +351,8 @@ int main(int argc, char * argv[]) } // ROS 1 node - ros::init(ros1_args.size(), ros1_args.data(), "ros_bridge"); + int argc_ros1 = ros1_args.size(); + ros::init(argc_ros1, const_cast(ros1_args.data()), "ros_bridge"); ros::NodeHandle ros1_node; // ROS 2 node From f5f39a1cb6cd0ed45d340bf4402ece171f9abfd8 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Mon, 6 Feb 2023 09:57:22 +0000 Subject: [PATCH 10/24] Fix parameter bidge command parser Signed-off-by: LucasHaug --- src/parameter_bridge.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/parameter_bridge.cpp b/src/parameter_bridge.cpp index fb2ce6ba..c2c579e9 100644 --- a/src/parameter_bridge.cpp +++ b/src/parameter_bridge.cpp @@ -245,14 +245,14 @@ bool get_flag_option(std::vector & args, const std::string & optio }); if (it != args.end()) { - auto value_it = it++; + auto value_it = std::next(it); if (value_it != args.end()) { value = *value_it; if (remove) { - args.erase(it); - args.erase(value_it); + args.erase(it); // Remove option + args.erase(it); // Remove value } return true; @@ -306,16 +306,16 @@ bool parse_command_options( return false; } - if (get_flag_option(args, "--topics", topics_parameter_name, true)) { - printf("Using default topics parameter name: %s", topics_parameter_name); + if (!get_flag_option(args, "--topics", topics_parameter_name, true)) { + printf("Using default topics parameter name: %s\n", topics_parameter_name); } - if (get_flag_option(args, "--services_1_to_2", services_1_to_2_parameter_name, true)) { - printf("Using default services_1_to_2 parameter name: %s", services_1_to_2_parameter_name); + if (!get_flag_option(args, "--services-1-to-2", services_1_to_2_parameter_name, true)) { + printf("Using default services 1 to 2 parameter name: %s\n", services_1_to_2_parameter_name); } - if (get_flag_option(args, "--services_2_to_1", services_2_to_1_parameter_name, true)) { - printf("Using default services_2_to_1 parameter name: %s", services_2_to_1_parameter_name); + if (!get_flag_option(args, "--services-2-to-1", services_2_to_1_parameter_name, true)) { + printf("Using default services 2 to 1 parameter name: %s\n", services_2_to_1_parameter_name); } split_ros1_ros2_args(args, ros1_args, ros2_args); From b99c2a354969c86e20e4fb619d32ca47d293ba75 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Mon, 6 Feb 2023 10:31:40 +0000 Subject: [PATCH 11/24] Refactor to add command parser utils file Signed-off-by: LucasHaug --- CMakeLists.txt | 1 + include/ros1_bridge/command_parser_utils.hpp | 46 ++++++++++ src/command_parser_utils.cpp | 91 ++++++++++++++++++++ src/dynamic_bridge.cpp | 59 ++----------- src/parameter_bridge.cpp | 63 ++------------ 5 files changed, 152 insertions(+), 108 deletions(-) create mode 100644 include/ros1_bridge/command_parser_utils.hpp create mode 100644 src/command_parser_utils.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f878af4..0f7855a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,6 +221,7 @@ add_library(${PROJECT_NAME} SHARED "src/builtin_interfaces_factories.cpp" "src/convert_builtin_interfaces.cpp" "src/bridge.cpp" + "src/command_parser_utils.cpp" ${generated_files}) ament_target_dependencies(${PROJECT_NAME} ${prefixed_ros1_message_packages} diff --git a/include/ros1_bridge/command_parser_utils.hpp b/include/ros1_bridge/command_parser_utils.hpp new file mode 100644 index 00000000..7b26b7db --- /dev/null +++ b/include/ros1_bridge/command_parser_utils.hpp @@ -0,0 +1,46 @@ +// Copyright 2015 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 ROS1_BRIDGE__COMMAND_PARSER_UTILS_HPP_ +#define ROS1_BRIDGE__COMMAND_PARSER_UTILS_HPP_ + +#include +#include + +namespace ros1_bridge +{ + +bool find_command_option( + const std::vector & args, + const std::string & option); + +bool get_flag_option( + std::vector & args, + const std::string & option, + const char * & value, + bool remove = false); + +bool get_flag_option( + std::vector & args, + const std::string & option, + bool remove = false); + +void split_ros1_ros2_args( + const std::vector & args, + std::vector & ros1_args, + std::vector & ros2_args); + +} // namespace ros1_bridge + +#endif // ROS1_BRIDGE__COMMAND_PARSER_UTILS_HPP_ diff --git a/src/command_parser_utils.cpp b/src/command_parser_utils.cpp new file mode 100644 index 00000000..963d9bb8 --- /dev/null +++ b/src/command_parser_utils.cpp @@ -0,0 +1,91 @@ +// Copyright 2015 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 "ros1_bridge/command_parser_utils.hpp" + +#include +#include + +namespace ros1_bridge +{ + +bool find_command_option(const std::vector & args, const std::string & option) +{ + auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { + return strcmp(element, option.c_str()) == 0; + }); + + return it != args.end(); +} + +bool get_flag_option(std::vector & args, const std::string & option, const char * & value, bool remove) +{ + auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { + return strcmp(element, option.c_str()) == 0; + }); + + if (it != args.end()) { + auto value_it = std::next(it); + + if (value_it != args.end()) { + value = *value_it; + + if (remove) { + args.erase(it); // Remove option + args.erase(it); // Remove value + } + + return true; + } + } + + return false; +} + +bool get_flag_option(std::vector & args, const std::string & option, bool remove) +{ + auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { + return strcmp(element, option.c_str()) == 0; + }); + + if (it != args.end()) { + if (remove) { + args.erase(it); + } + return true; + } + + return false; +} + +void split_ros1_ros2_args( + const std::vector & args, std::vector & ros1_args, + std::vector & ros2_args) +{ + // Start iterating from the second argument, since the first argument is the executable name + auto it = std::find_if(args.begin() + 1, args.end(), [] (const char * const & element) { + return strcmp(element, "--ros-args") == 0; + }); + + if (it != args.end()) { + ros1_args = std::vector(args.begin(), it); + ros2_args = std::vector(it, args.end()); + ros2_args.insert(ros2_args.begin(), args.at(0)); + } else { + ros1_args = args; + ros2_args = args; + } +} + +} // namespace ros1_bridge diff --git a/src/dynamic_bridge.cpp b/src/dynamic_bridge.cpp index a0201ca6..e72a17bf 100644 --- a/src/dynamic_bridge.cpp +++ b/src/dynamic_bridge.cpp @@ -39,6 +39,7 @@ #include "rcpputils/scope_exit.hpp" #include "ros1_bridge/bridge.hpp" +#include "ros1_bridge/command_parser_utils.hpp" std::mutex g_bridge_mutex; @@ -57,50 +58,6 @@ struct Bridge2to1HandlesAndMessageTypes std::string ros2_type_name; }; -bool find_command_option(const std::vector & args, const std::string & option) -{ - auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { - return strcmp(element, option.c_str()) == 0; - }); - - return it != args.end(); -} - -bool get_flag_option(std::vector & args, const std::string & option, bool remove = false) -{ - auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { - return strcmp(element, option.c_str()) == 0; - }); - - if (it != args.end()) { - if (remove) { - args.erase(it); - } - return true; - } - - return false; -} - -void split_ros1_ros2_args( - const std::vector & args, std::vector & ros1_args, - std::vector & ros2_args) -{ - // Start iterating from the second argument, since the first argument is the executable name - auto it = std::find_if(args.begin() + 1, args.end(), [] (const char * const & element) { - return strcmp(element, "--ros-args") == 0; - }); - - if (it != args.end()) { - ros1_args = std::vector(args.begin(), it); - ros2_args = std::vector(it, args.end()); - ros2_args.insert(ros2_args.begin(), args.at(0)); - } else { - ros1_args = args; - ros2_args = args; - } -} - bool parse_command_options( int argc, char ** argv, std::vector & ros1_args, std::vector & ros2_args, bool & output_topic_introspection, @@ -108,7 +65,7 @@ bool parse_command_options( { std::vector args(argv, argv + argc); - if (find_command_option(args, "-h") || find_command_option(args, "--help")) { + if (ros1_bridge::find_command_option(args, "-h") || ros1_bridge::find_command_option(args, "--help")) { std::stringstream ss; ss << "Usage:" << std::endl; ss << " -h, --help: This message." << std::endl; @@ -126,7 +83,7 @@ bool parse_command_options( return false; } - if (get_flag_option(args, "--print-pairs")) { + if (ros1_bridge::get_flag_option(args, "--print-pairs")) { auto mappings_2to1 = ros1_bridge::get_all_message_mappings_2to1(); if (mappings_2to1.size() > 0) { printf("Supported ROS 2 <=> ROS 1 message type conversion pairs:\n"); @@ -148,13 +105,13 @@ bool parse_command_options( return false; } - output_topic_introspection = get_flag_option(args, "--show-introspection", true); + output_topic_introspection = ros1_bridge::get_flag_option(args, "--show-introspection", true); - bool bridge_all_topics = get_flag_option(args, "--bridge-all-topics", true); - bridge_all_1to2_topics = bridge_all_topics || get_flag_option(args, "--bridge-all-1to2-topics", true); - bridge_all_2to1_topics = bridge_all_topics || get_flag_option(args, "--bridge-all-2to1-topics", true); + bool bridge_all_topics = ros1_bridge::get_flag_option(args, "--bridge-all-topics", true); + bridge_all_1to2_topics = bridge_all_topics || ros1_bridge::get_flag_option(args, "--bridge-all-1to2-topics", true); + bridge_all_2to1_topics = bridge_all_topics || ros1_bridge::get_flag_option(args, "--bridge-all-2to1-topics", true); - split_ros1_ros2_args(args, ros1_args, ros2_args); + ros1_bridge::split_ros1_ros2_args(args, ros1_args, ros2_args); return true; } diff --git a/src/parameter_bridge.cpp b/src/parameter_bridge.cpp index c2c579e9..9152b3d7 100644 --- a/src/parameter_bridge.cpp +++ b/src/parameter_bridge.cpp @@ -31,6 +31,7 @@ #include "rclcpp/rclcpp.hpp" #include "ros1_bridge/bridge.hpp" +#include "ros1_bridge/command_parser_utils.hpp" rclcpp::QoS qos_from_params(XmlRpc::XmlRpcValue qos_params) { @@ -229,58 +230,6 @@ rclcpp::QoS qos_from_params(XmlRpc::XmlRpcValue qos_params) return ros2_publisher_qos; } -bool find_command_option(const std::vector & args, const std::string & option) -{ - auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { - return strcmp(element, option.c_str()) == 0; - }); - - return it != args.end(); -} - -bool get_flag_option(std::vector & args, const std::string & option, const char * & value, bool remove = false) -{ - auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { - return strcmp(element, option.c_str()) == 0; - }); - - if (it != args.end()) { - auto value_it = std::next(it); - - if (value_it != args.end()) { - value = *value_it; - - if (remove) { - args.erase(it); // Remove option - args.erase(it); // Remove value - } - - return true; - } - } - - return false; -} - -void split_ros1_ros2_args( - const std::vector & args, std::vector & ros1_args, - std::vector & ros2_args) -{ - // Start iterating from the second argument, since the first argument is the executable name - auto it = std::find_if(args.begin() + 1, args.end(), [] (const char * const & element) { - return strcmp(element, "--ros-args") == 0; - }); - - if (it != args.end()) { - ros1_args = std::vector(args.begin(), it); - ros2_args = std::vector(it, args.end()); - ros2_args.insert(ros2_args.begin(), args.at(0)); - } else { - ros1_args = args; - ros2_args = args; - } -} - bool parse_command_options( int argc, char ** argv, std::vector & ros1_args, std::vector & ros2_args, const char * & topics_parameter_name, @@ -292,7 +241,7 @@ bool parse_command_options( std::vector args(argv, argv + argc); - if (find_command_option(args, "-h") || find_command_option(args, "--help")) { + if (ros1_bridge::find_command_option(args, "-h") || ros1_bridge::find_command_option(args, "--help")) { std::stringstream ss; ss << "Usage:" << std::endl; ss << " -h, --help: This message." << std::endl; @@ -306,19 +255,19 @@ bool parse_command_options( return false; } - if (!get_flag_option(args, "--topics", topics_parameter_name, true)) { + if (!ros1_bridge::get_flag_option(args, "--topics", topics_parameter_name, true)) { printf("Using default topics parameter name: %s\n", topics_parameter_name); } - if (!get_flag_option(args, "--services-1-to-2", services_1_to_2_parameter_name, true)) { + if (!ros1_bridge::get_flag_option(args, "--services-1-to-2", services_1_to_2_parameter_name, true)) { printf("Using default services 1 to 2 parameter name: %s\n", services_1_to_2_parameter_name); } - if (!get_flag_option(args, "--services-2-to-1", services_2_to_1_parameter_name, true)) { + if (!ros1_bridge::get_flag_option(args, "--services-2-to-1", services_2_to_1_parameter_name, true)) { printf("Using default services 2 to 1 parameter name: %s\n", services_2_to_1_parameter_name); } - split_ros1_ros2_args(args, ros1_args, ros2_args); + ros1_bridge::split_ros1_ros2_args(args, ros1_args, ros2_args); return true; } From 2e47d5ee1c30f8a75501b1e395eb3554e0d1c18f Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Mon, 6 Feb 2023 14:26:00 +0100 Subject: [PATCH 12/24] Add github action issue workaround Signed-off-by: LucasHaug --- .github/workflows/main.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 199cf734..9875832a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,6 +7,9 @@ jobs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 + - name: Workaround for https://github.com/actions/runner-images/issues/675 + run: | + sudo sed -i 's/azure\.//' /etc/apt/sources.list - uses: ros-tooling/setup-ros@v0.5 with: required-ros-distributions: "noetic rolling" From 7bcbb2db0dca9b7a0bbb8664a65600c27152cafa Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Mon, 6 Feb 2023 15:11:41 +0100 Subject: [PATCH 13/24] Ty to run github action inside container Signed-off-by: LucasHaug --- .github/workflows/main.yml | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9875832a..9b8d8cb9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,18 +5,10 @@ on: [push, pull_request] # on all pushes and PRs jobs: ros1_bridge: runs-on: ubuntu-20.04 + container: + image: ros:rolling-ros1-bridge steps: - uses: actions/checkout@v2 - - name: Workaround for https://github.com/actions/runner-images/issues/675 - run: | - sudo sed -i 's/azure\.//' /etc/apt/sources.list - - uses: ros-tooling/setup-ros@v0.5 - with: - required-ros-distributions: "noetic rolling" - name: Build and test ros1-bridge - uses: ros-tooling/action-ros-ci@v0.2 - with: - package-name: ros1_bridge - target-ros1-distro: noetic - target-ros2-distro: rolling - + run: | + /ros_entrypoint.sh colcon build --allow-overriding ros1_bridge From 7d32936774723d5f0d752983a312618663f69975 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Wed, 8 Feb 2023 11:33:06 +0100 Subject: [PATCH 14/24] Fix ROS2 args when no ROS1 args Signed-off-by: LucasHaug --- src/command_parser_utils.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/command_parser_utils.cpp b/src/command_parser_utils.cpp index 963d9bb8..b5b6c22b 100644 --- a/src/command_parser_utils.cpp +++ b/src/command_parser_utils.cpp @@ -81,11 +81,12 @@ void split_ros1_ros2_args( if (it != args.end()) { ros1_args = std::vector(args.begin(), it); ros2_args = std::vector(it, args.end()); - ros2_args.insert(ros2_args.begin(), args.at(0)); } else { ros1_args = args; - ros2_args = args; + ros2_args = {}; } + + ros2_args.insert(ros2_args.begin(), args.at(0)); } } // namespace ros1_bridge From 9a341f22b7afd3f3bad2e5af36ea7f0380d184ac Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 10 Mar 2023 09:23:13 +0100 Subject: [PATCH 15/24] =?UTF-8?q?=E2=8F=AA=20Revert=20changes=20on=20GitHu?= =?UTF-8?q?b=20Action=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: LucasHaug --- .github/workflows/main.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9b8d8cb9..ab683cba 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,11 +4,15 @@ on: [push, pull_request] # on all pushes and PRs jobs: ros1_bridge: - runs-on: ubuntu-20.04 - container: - image: ros:rolling-ros1-bridge + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - uses: ros-tooling/setup-ros@v0.2 + with: + required-ros-distributions: "noetic rolling" - name: Build and test ros1-bridge - run: | - /ros_entrypoint.sh colcon build --allow-overriding ros1_bridge + uses: ros-tooling/action-ros-ci@v0.2 + with: + package-name: ros1_bridge + target-ros1-distro: noetic + target-ros2-distro: rolling From 3b00615bd37475c6ed5e1b465051c6438f0a4aab Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 10 Mar 2023 09:08:49 +0000 Subject: [PATCH 16/24] Change parameter bridge ROS init order Signed-off-by: LucasHaug --- src/parameter_bridge.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/parameter_bridge.cpp b/src/parameter_bridge.cpp index 9152b3d7..93395413 100644 --- a/src/parameter_bridge.cpp +++ b/src/parameter_bridge.cpp @@ -299,15 +299,15 @@ int main(int argc, char * argv[]) return 0; } + // ROS 2 node + rclcpp::init(ros2_args.size(), ros2_args.data()); + auto ros2_node = rclcpp::Node::make_shared("ros_bridge"); + // ROS 1 node int argc_ros1 = ros1_args.size(); ros::init(argc_ros1, const_cast(ros1_args.data()), "ros_bridge"); ros::NodeHandle ros1_node; - // ROS 2 node - rclcpp::init(ros2_args.size(), ros2_args.data()); - auto ros2_node = rclcpp::Node::make_shared("ros_bridge"); - std::list all_handles; std::list service_bridges_1_to_2; std::list service_bridges_2_to_1; From 38460e6587e91bd006559a1262e134603a5dbadd Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 10 Mar 2023 09:44:38 +0000 Subject: [PATCH 17/24] Rename parser functions Signed-off-by: LucasHaug --- include/ros1_bridge/command_parser_utils.hpp | 4 ++-- src/command_parser_utils.cpp | 4 ++-- src/dynamic_bridge.cpp | 10 +++++----- src/parameter_bridge.cpp | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/ros1_bridge/command_parser_utils.hpp b/include/ros1_bridge/command_parser_utils.hpp index 7b26b7db..d9c0829c 100644 --- a/include/ros1_bridge/command_parser_utils.hpp +++ b/include/ros1_bridge/command_parser_utils.hpp @@ -25,13 +25,13 @@ bool find_command_option( const std::vector & args, const std::string & option); -bool get_flag_option( +bool get_option_value( std::vector & args, const std::string & option, const char * & value, bool remove = false); -bool get_flag_option( +bool get_option_flag( std::vector & args, const std::string & option, bool remove = false); diff --git a/src/command_parser_utils.cpp b/src/command_parser_utils.cpp index b5b6c22b..14a45402 100644 --- a/src/command_parser_utils.cpp +++ b/src/command_parser_utils.cpp @@ -29,7 +29,7 @@ bool find_command_option(const std::vector & args, const std::stri return it != args.end(); } -bool get_flag_option(std::vector & args, const std::string & option, const char * & value, bool remove) +bool get_option_value(std::vector & args, const std::string & option, const char * & value, bool remove) { auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { return strcmp(element, option.c_str()) == 0; @@ -53,7 +53,7 @@ bool get_flag_option(std::vector & args, const std::string & optio return false; } -bool get_flag_option(std::vector & args, const std::string & option, bool remove) +bool get_option_flag(std::vector & args, const std::string & option, bool remove) { auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { return strcmp(element, option.c_str()) == 0; diff --git a/src/dynamic_bridge.cpp b/src/dynamic_bridge.cpp index e72a17bf..7807efdc 100644 --- a/src/dynamic_bridge.cpp +++ b/src/dynamic_bridge.cpp @@ -83,7 +83,7 @@ bool parse_command_options( return false; } - if (ros1_bridge::get_flag_option(args, "--print-pairs")) { + if (ros1_bridge::get_option_flag(args, "--print-pairs")) { auto mappings_2to1 = ros1_bridge::get_all_message_mappings_2to1(); if (mappings_2to1.size() > 0) { printf("Supported ROS 2 <=> ROS 1 message type conversion pairs:\n"); @@ -105,11 +105,11 @@ bool parse_command_options( return false; } - output_topic_introspection = ros1_bridge::get_flag_option(args, "--show-introspection", true); + output_topic_introspection = ros1_bridge::get_option_flag(args, "--show-introspection", true); - bool bridge_all_topics = ros1_bridge::get_flag_option(args, "--bridge-all-topics", true); - bridge_all_1to2_topics = bridge_all_topics || ros1_bridge::get_flag_option(args, "--bridge-all-1to2-topics", true); - bridge_all_2to1_topics = bridge_all_topics || ros1_bridge::get_flag_option(args, "--bridge-all-2to1-topics", true); + bool bridge_all_topics = ros1_bridge::get_option_flag(args, "--bridge-all-topics", true); + bridge_all_1to2_topics = bridge_all_topics || ros1_bridge::get_option_flag(args, "--bridge-all-1to2-topics", true); + bridge_all_2to1_topics = bridge_all_topics || ros1_bridge::get_option_flag(args, "--bridge-all-2to1-topics", true); ros1_bridge::split_ros1_ros2_args(args, ros1_args, ros2_args); diff --git a/src/parameter_bridge.cpp b/src/parameter_bridge.cpp index 93395413..a1e29e5f 100644 --- a/src/parameter_bridge.cpp +++ b/src/parameter_bridge.cpp @@ -255,15 +255,15 @@ bool parse_command_options( return false; } - if (!ros1_bridge::get_flag_option(args, "--topics", topics_parameter_name, true)) { + if (!ros1_bridge::get_option_value(args, "--topics", topics_parameter_name, true)) { printf("Using default topics parameter name: %s\n", topics_parameter_name); } - if (!ros1_bridge::get_flag_option(args, "--services-1-to-2", services_1_to_2_parameter_name, true)) { + if (!ros1_bridge::get_option_value(args, "--services-1-to-2", services_1_to_2_parameter_name, true)) { printf("Using default services 1 to 2 parameter name: %s\n", services_1_to_2_parameter_name); } - if (!ros1_bridge::get_flag_option(args, "--services-2-to-1", services_2_to_1_parameter_name, true)) { + if (!ros1_bridge::get_option_value(args, "--services-2-to-1", services_2_to_1_parameter_name, true)) { printf("Using default services 2 to 1 parameter name: %s\n", services_2_to_1_parameter_name); } From 1097b4fdc110bccffbc5179ff050e5d065063a0b Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 10 Mar 2023 11:04:30 +0000 Subject: [PATCH 18/24] Add get_option_values function Signed-off-by: LucasHaug --- include/ros1_bridge/command_parser_utils.hpp | 5 +++ src/command_parser_utils.cpp | 34 ++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/ros1_bridge/command_parser_utils.hpp b/include/ros1_bridge/command_parser_utils.hpp index d9c0829c..92f0c2d3 100644 --- a/include/ros1_bridge/command_parser_utils.hpp +++ b/include/ros1_bridge/command_parser_utils.hpp @@ -31,6 +31,11 @@ bool get_option_value( const char * & value, bool remove = false); +bool get_option_values( + std::vector & args, const std::string & option, + std::vector & available_options, + std::vector & values, bool remove = false); + bool get_option_flag( std::vector & args, const std::string & option, diff --git a/src/command_parser_utils.cpp b/src/command_parser_utils.cpp index 14a45402..0cc7fe17 100644 --- a/src/command_parser_utils.cpp +++ b/src/command_parser_utils.cpp @@ -53,6 +53,40 @@ bool get_option_value(std::vector & args, const std::string & opti return false; } +bool get_option_values( + std::vector & args, const std::string & option, + std::vector & available_options, + std::vector & values, bool remove) +{ + auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { + return strcmp(element, option.c_str()) == 0; + }); + + if (it != args.end()) { + auto value_it = std::next(it); + + while (value_it != args.end() and + std::none_of(available_options.begin(), available_options.end(), + [value_it](const char * available_option) { return *value_it == available_option; })) { + values.push_back(*value_it); + + if (remove) { + args.erase(value_it); + } else { + ++value_it; + } + } + + if (remove) { + args.erase(it); // Remove option + } + + return true; + } + + return false; +} + bool get_option_flag(std::vector & args, const std::string & option, bool remove) { auto it = std::find_if(args.begin(), args.end(), [&option] (const char * const & element) { From 02e1d27a8f2dcab84b2843befe330ca28cedf5f6 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 10 Mar 2023 17:40:09 +0000 Subject: [PATCH 19/24] Fix wrong help for the parameter bridge Signed-off-by: LucasHaug --- src/parameter_bridge.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parameter_bridge.cpp b/src/parameter_bridge.cpp index a1e29e5f..d7d27cb3 100644 --- a/src/parameter_bridge.cpp +++ b/src/parameter_bridge.cpp @@ -247,9 +247,9 @@ bool parse_command_options( ss << " -h, --help: This message." << std::endl; ss << " --topics: Name of the parameter that contains the list of topics to bridge."; ss << std::endl; - ss << " --services_1_to_2: Name of the parameter that contains the list of services to bridge from ROS 1 to ROS 2."; + ss << " --services-1-to-2: Name of the parameter that contains the list of services to bridge from ROS 1 to ROS 2."; ss << std::endl; - ss << " --services_2_to_1: Name of the parameter that contains the list of services to bridge from ROS 2 to ROS 1."; + ss << " --services-2-to-1: Name of the parameter that contains the list of services to bridge from ROS 2 to ROS 1."; ss << std::endl; std::cout << ss.str(); return false; From 5804cdcb9eefe5dad5622ff67bcb8a84e361b9a8 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 10 Mar 2023 17:42:57 +0000 Subject: [PATCH 20/24] Fix get_option_values parser function Signed-off-by: LucasHaug --- src/command_parser_utils.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/command_parser_utils.cpp b/src/command_parser_utils.cpp index 0cc7fe17..d3fe0d96 100644 --- a/src/command_parser_utils.cpp +++ b/src/command_parser_utils.cpp @@ -67,7 +67,9 @@ bool get_option_values( while (value_it != args.end() and std::none_of(available_options.begin(), available_options.end(), - [value_it](const char * available_option) { return *value_it == available_option; })) { + [value_it](const char * available_option) { + return strcmp(*value_it, available_option) == 0; + })) { values.push_back(*value_it); if (remove) { From d76cb81b03ea235d91eee61a1e81a90c5808c927 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 10 Mar 2023 17:43:31 +0000 Subject: [PATCH 21/24] Refactor bridges to use the get_option_values Signed-off-by: LucasHaug --- src/dynamic_bridge.cpp | 79 +++++++++++++++++++++++++++++++--------- src/parameter_bridge.cpp | 45 ++++++++++++++++++++++- 2 files changed, 106 insertions(+), 18 deletions(-) diff --git a/src/dynamic_bridge.cpp b/src/dynamic_bridge.cpp index 7807efdc..b87d1bac 100644 --- a/src/dynamic_bridge.cpp +++ b/src/dynamic_bridge.cpp @@ -65,9 +65,24 @@ bool parse_command_options( { std::vector args(argv, argv + argc); + std::vector available_options = { + "-h", "--help", + "--show-introspection", + "--print-pairs", + "--bridge-all-topics", + "--bridge-all-1to2-topics", + "--bridge-all-2to1-topics", + "--ros1-args", + "--ros2-args", + }; + if (ros1_bridge::find_command_option(args, "-h") || ros1_bridge::find_command_option(args, "--help")) { std::stringstream ss; ss << "Usage:" << std::endl; + ss << "ros2 run ros1_bridge dynamic_bridge [Bridge specific options] \\" << std::endl; + ss << " [--ros1-args [ROS1 arguments]] [--ros2-args [ROS2 arguments]]" << std::endl; + ss << std::endl; + ss << "Options:" << std::endl; ss << " -h, --help: This message." << std::endl; ss << " --show-introspection: Print output of introspection of both sides of the bridge."; ss << std::endl; @@ -79,6 +94,8 @@ bool parse_command_options( ss << "a matching subscriber." << std::endl; ss << " --bridge-all-2to1-topics: Bridge all ROS 2 topics to ROS 1, whether or not there is "; ss << "a matching subscriber." << std::endl; + ss << " --ros1-args: Arguments to pass to the ROS 1 bridge node." << std::endl; + ss << " --ros2-args: Arguments to pass to the ROS 2 bridge node." << std::endl; std::cout << ss.str(); return false; } @@ -111,7 +128,35 @@ bool parse_command_options( bridge_all_1to2_topics = bridge_all_topics || ros1_bridge::get_option_flag(args, "--bridge-all-1to2-topics", true); bridge_all_2to1_topics = bridge_all_topics || ros1_bridge::get_option_flag(args, "--bridge-all-2to1-topics", true); - ros1_bridge::split_ros1_ros2_args(args, ros1_args, ros2_args); + auto logger = rclcpp::get_logger("ros1_bridge"); + + // Get ROS1 arguments + if (ros1_bridge::get_option_values(args, "--ros1-args", available_options, ros1_args, true)) { + if (ros1_args.size() == 0) { + RCLCPP_ERROR(logger, "Error: --ros1-args specified but no arguments provided."); + return false; + } + } + + ros1_args.insert(ros1_args.begin(), args.at(0)); + + // Get ROS2 arguments + if (ros1_bridge::get_option_values(args, "--ros2-args", available_options, ros2_args, true)) { + if (ros2_args.size() == 0) { + RCLCPP_ERROR(logger, "Error: --ros2-args specified but no arguments provided."); + return false; + } + + ros2_args.insert(ros2_args.begin(), "--ros-args"); + } + + ros2_args.insert(ros2_args.begin(), args.at(0)); + + if (ros1_bridge::find_command_option(args, "--ros-args") or args.size() > 1) { + RCLCPP_WARN(logger, "Warning: passing the ROS node arguments directly to the node is deprecated, use --ros1-args and --ros2-args instead."); + + ros1_bridge::split_ros1_ros2_args(args, ros1_args, ros2_args); + } return true; } @@ -185,14 +230,14 @@ void update_bridge( bridge.ros1_type_name, topic_name, 10, bridge.ros2_type_name, topic_name, ros2_publisher_qos); } catch (std::runtime_error & e) { - fprintf( - stderr, - "failed to create 1to2 bridge for topic '%s' " - "with ROS 1 type '%s' and ROS 2 type '%s': %s\n", - topic_name.c_str(), bridge.ros1_type_name.c_str(), bridge.ros2_type_name.c_str(), e.what()); - if (std::string(e.what()).find("No template specialization") != std::string::npos) { - fprintf(stderr, "check the list of supported pairs with the `--print-pairs` option\n"); - } + // fprintf( + // stderr, + // "failed to create 1to2 bridge for topic '%s' " + // "with ROS 1 type '%s' and ROS 2 type '%s': %s\n", + // topic_name.c_str(), bridge.ros1_type_name.c_str(), bridge.ros2_type_name.c_str(), e.what()); + // if (std::string(e.what()).find("No template specialization") != std::string::npos) { + // fprintf(stderr, "check the list of supported pairs with the `--print-pairs` option\n"); + // } continue; } @@ -251,14 +296,14 @@ void update_bridge( bridge.ros2_type_name, topic_name, 10, bridge.ros1_type_name, topic_name, 10); } catch (std::runtime_error & e) { - fprintf( - stderr, - "failed to create 2to1 bridge for topic '%s' " - "with ROS 2 type '%s' and ROS 1 type '%s': %s\n", - topic_name.c_str(), bridge.ros2_type_name.c_str(), bridge.ros1_type_name.c_str(), e.what()); - if (std::string(e.what()).find("No template specialization") != std::string::npos) { - fprintf(stderr, "check the list of supported pairs with the `--print-pairs` option\n"); - } + // fprintf( + // stderr, + // "failed to create 2to1 bridge for topic '%s' " + // "with ROS 2 type '%s' and ROS 1 type '%s': %s\n", + // topic_name.c_str(), bridge.ros2_type_name.c_str(), bridge.ros1_type_name.c_str(), e.what()); + // if (std::string(e.what()).find("No template specialization") != std::string::npos) { + // fprintf(stderr, "check the list of supported pairs with the `--print-pairs` option\n"); + // } continue; } diff --git a/src/parameter_bridge.cpp b/src/parameter_bridge.cpp index d7d27cb3..b781209a 100644 --- a/src/parameter_bridge.cpp +++ b/src/parameter_bridge.cpp @@ -241,9 +241,22 @@ bool parse_command_options( std::vector args(argv, argv + argc); + std::vector available_options = { + "-h", "--help", + "--topics", + "--services-1-to-2", + "--services-2-to-1", + "--ros1-args", + "--ros2-args", + }; + if (ros1_bridge::find_command_option(args, "-h") || ros1_bridge::find_command_option(args, "--help")) { std::stringstream ss; ss << "Usage:" << std::endl; + ss << "ros2 run ros1_bridge parameter_bridge [Bridge specific options] \\" << std::endl; + ss << " [--ros1-args [ROS1 arguments]] [--ros2-args [ROS2 arguments]]" << std::endl; + ss << std::endl; + ss << "Options:" << std::endl; ss << " -h, --help: This message." << std::endl; ss << " --topics: Name of the parameter that contains the list of topics to bridge."; ss << std::endl; @@ -251,6 +264,8 @@ bool parse_command_options( ss << std::endl; ss << " --services-2-to-1: Name of the parameter that contains the list of services to bridge from ROS 2 to ROS 1."; ss << std::endl; + ss << " --ros1-args: Arguments to pass to the ROS 1 bridge node." << std::endl; + ss << " --ros2-args: Arguments to pass to the ROS 2 bridge node." << std::endl; std::cout << ss.str(); return false; } @@ -267,7 +282,35 @@ bool parse_command_options( printf("Using default services 2 to 1 parameter name: %s\n", services_2_to_1_parameter_name); } - ros1_bridge::split_ros1_ros2_args(args, ros1_args, ros2_args); + auto logger = rclcpp::get_logger("ros1_bridge"); + + // Get ROS1 arguments + if (ros1_bridge::get_option_values(args, "--ros1-args", available_options, ros1_args, true)) { + if (ros1_args.size() == 0) { + RCLCPP_ERROR(logger, "Error: --ros1-args specified but no arguments provided."); + return false; + } + } + + ros1_args.insert(ros1_args.begin(), args.at(0)); + + // Get ROS2 arguments + if (ros1_bridge::get_option_values(args, "--ros2-args", available_options, ros2_args, true)) { + if (ros2_args.size() == 0) { + RCLCPP_ERROR(logger, "Error: --ros2-args specified but no arguments provided."); + return false; + } + + ros2_args.insert(ros2_args.begin(), "--ros-args"); + } + + ros2_args.insert(ros2_args.begin(), args.at(0)); + + if (ros1_bridge::find_command_option(args, "--ros-args") or args.size() > 1) { + RCLCPP_WARN(logger, "Warning: passing the ROS node arguments directly to the node is deprecated, use --ros1-args and --ros2-args instead."); + + ros1_bridge::split_ros1_ros2_args(args, ros1_args, ros2_args); + } return true; } From daf83dbe82a83ac4945e61289743f2f47e3e8fb6 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Fri, 10 Mar 2023 17:44:08 +0000 Subject: [PATCH 22/24] Add running scetion to README Signed-off-by: LucasHaug --- README.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 59d9509f..99fc110b 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ To run the following examples you will also need these ROS 1 packages: ### Prerequisites for the examples in this file -In order to make the examples below portable between versions of ROS, we define two environment variables, `ROS1_INSTALL_PATH` and `ROS2_INSTALL_PATH`. +In order to make the examples below portable between versions of ROS, we define two environment variables, `ROS1_INSTALL_PATH` and `ROS2_INSTALL_PATH`. These are defined as the paths to the installation location of their respective ROS versions. If you installed Noetic in the default location, then the definition of `ROS1_INSTALL_PATH` will be `/opt/ros/noetic`. @@ -125,6 +125,31 @@ colcon build --symlink-install --packages-select ros1_bridge --cmake-force-confi *Note:* If you are building on a memory constrained system you might want to limit the number of parallel jobs by setting e.g. the environment variable `MAKEFLAGS=-j1`. +## Running the bridge + +There are two types of bridges that are available for general use: + +* The dynamic bridge which will watch the available ROS 1 and ROS 2 topics. + Once a *matching* topic has been detected it starts to bridge the messages on this topic. +* The parameter bridge which will just bridge specific topics with specific QoS profiles depending on a configuration provided by the user. For more details see the [Example 4](#example-4-bridge-only-selected-topics-and-services) section. + +Too see the available command line options for each type of bridge run: + +```bash +ros2 run ros1_bridge [dynamic_bridge|parameter_bridge] --help +``` + +To pass arguments to the ROS 1 node of the bridge, use the `--ros1-args` option. For example, to run the dynamic bridge changing the ROS 1 node namespace to `ros1_bridge`: + +```bash +ros2 run ros1_bridge dynamic_bridge --ros1-args __ns:=ros1_bridge +``` + +To pass arguments to the ROS 2 node of the bridge, use the `--ros2-args` option. For example, to run the dynamic bridge changing the ROS 2 node namespace to `ros2_bridge`: + +```bash +ros2 run ros1_bridge dynamic_bridge --ros2-args __ns:=ros2_bridge +``` ## Example 1: run the bridge and the example talker and listener From e4c511cbf87d4f31142d3db80d0882a175133f28 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Tue, 21 Mar 2023 15:50:04 +0000 Subject: [PATCH 23/24] Add print pairs to parameter bridge Signed-off-by: LucasHaug --- src/parameter_bridge.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/parameter_bridge.cpp b/src/parameter_bridge.cpp index b781209a..22626a8f 100644 --- a/src/parameter_bridge.cpp +++ b/src/parameter_bridge.cpp @@ -258,6 +258,8 @@ bool parse_command_options( ss << std::endl; ss << "Options:" << std::endl; ss << " -h, --help: This message." << std::endl; + ss << " --print-pairs: Print a list of the supported ROS 2 <=> ROS 1 conversion pairs."; + ss << std::endl; ss << " --topics: Name of the parameter that contains the list of topics to bridge."; ss << std::endl; ss << " --services-1-to-2: Name of the parameter that contains the list of services to bridge from ROS 1 to ROS 2."; @@ -270,6 +272,28 @@ bool parse_command_options( return false; } + if (ros1_bridge::get_option_flag(args, "--print-pairs")) { + auto mappings_2to1 = ros1_bridge::get_all_message_mappings_2to1(); + if (mappings_2to1.size() > 0) { + printf("Supported ROS 2 <=> ROS 1 message type conversion pairs:\n"); + for (auto & pair : mappings_2to1) { + printf(" - '%s' (ROS 2) <=> '%s' (ROS 1)\n", pair.first.c_str(), pair.second.c_str()); + } + } else { + printf("No message type conversion pairs supported.\n"); + } + mappings_2to1 = ros1_bridge::get_all_service_mappings_2to1(); + if (mappings_2to1.size() > 0) { + printf("Supported ROS 2 <=> ROS 1 service type conversion pairs:\n"); + for (auto & pair : mappings_2to1) { + printf(" - '%s' (ROS 2) <=> '%s' (ROS 1)\n", pair.first.c_str(), pair.second.c_str()); + } + } else { + printf("No service type conversion pairs supported.\n"); + } + return false; + } + if (!ros1_bridge::get_option_value(args, "--topics", topics_parameter_name, true)) { printf("Using default topics parameter name: %s\n", topics_parameter_name); } From d64d2d9c80dae9ae24ad43fc4fa3703d20cdded3 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Wed, 21 Jun 2023 22:46:38 +0200 Subject: [PATCH 24/24] Revert mistakenly commented code Signed-off-by: LucasHaug --- src/dynamic_bridge.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/dynamic_bridge.cpp b/src/dynamic_bridge.cpp index b87d1bac..e4c11ca0 100644 --- a/src/dynamic_bridge.cpp +++ b/src/dynamic_bridge.cpp @@ -230,14 +230,14 @@ void update_bridge( bridge.ros1_type_name, topic_name, 10, bridge.ros2_type_name, topic_name, ros2_publisher_qos); } catch (std::runtime_error & e) { - // fprintf( - // stderr, - // "failed to create 1to2 bridge for topic '%s' " - // "with ROS 1 type '%s' and ROS 2 type '%s': %s\n", - // topic_name.c_str(), bridge.ros1_type_name.c_str(), bridge.ros2_type_name.c_str(), e.what()); - // if (std::string(e.what()).find("No template specialization") != std::string::npos) { - // fprintf(stderr, "check the list of supported pairs with the `--print-pairs` option\n"); - // } + fprintf( + stderr, + "failed to create 1to2 bridge for topic '%s' " + "with ROS 1 type '%s' and ROS 2 type '%s': %s\n", + topic_name.c_str(), bridge.ros1_type_name.c_str(), bridge.ros2_type_name.c_str(), e.what()); + if (std::string(e.what()).find("No template specialization") != std::string::npos) { + fprintf(stderr, "check the list of supported pairs with the `--print-pairs` option\n"); + } continue; } @@ -296,14 +296,14 @@ void update_bridge( bridge.ros2_type_name, topic_name, 10, bridge.ros1_type_name, topic_name, 10); } catch (std::runtime_error & e) { - // fprintf( - // stderr, - // "failed to create 2to1 bridge for topic '%s' " - // "with ROS 2 type '%s' and ROS 1 type '%s': %s\n", - // topic_name.c_str(), bridge.ros2_type_name.c_str(), bridge.ros1_type_name.c_str(), e.what()); - // if (std::string(e.what()).find("No template specialization") != std::string::npos) { - // fprintf(stderr, "check the list of supported pairs with the `--print-pairs` option\n"); - // } + fprintf( + stderr, + "failed to create 2to1 bridge for topic '%s' " + "with ROS 2 type '%s' and ROS 1 type '%s': %s\n", + topic_name.c_str(), bridge.ros2_type_name.c_str(), bridge.ros1_type_name.c_str(), e.what()); + if (std::string(e.what()).find("No template specialization") != std::string::npos) { + fprintf(stderr, "check the list of supported pairs with the `--print-pairs` option\n"); + } continue; }