1616
1717#include < cstddef>
1818#include < memory>
19+ #include < set>
20+ #include < sstream>
1921#include < string>
2022#include < vector>
2123
2224#include " bridge_handle_ros_to_gz.hpp"
2325#include " bridge_handle_gz_to_ros.hpp"
26+ #include " get_mappings.hpp"
2427
2528#include < rclcpp/expand_topic_or_service_name.hpp>
2629
2730namespace ros_gz_bridge
2831{
2932
33+ namespace
34+ {
35+ std::string bridge_key (const BridgeConfig & config)
36+ {
37+ std::stringstream stream;
38+ stream << config.ros_topic_name << ' |' ;
39+ stream << config.ros_type_name << ' |' ;
40+ stream << config.gz_topic_name << ' |' ;
41+ stream << config.gz_type_name << ' |' ;
42+ stream << static_cast <int >(config.direction );
43+ return stream.str ();
44+ }
45+
46+ BridgeDirection parse_bridge_direction (const std::string & direction)
47+ {
48+ if (direction == " BIDIRECTIONAL" ) {
49+ return BridgeDirection::BIDIRECTIONAL ;
50+ }
51+ if (direction == " GZ_TO_ROS" ) {
52+ return BridgeDirection::GZ_TO_ROS ;
53+ }
54+ if (direction == " ROS_TO_GZ" ) {
55+ return BridgeDirection::ROS_TO_GZ ;
56+ }
57+ return BridgeDirection::NONE ;
58+ }
59+ } // namespace
60+
3061RosGzBridge::RosGzBridge (const rclcpp::NodeOptions & options)
3162: rclcpp::Node(" ros_gz_bridge" , options)
3263{
@@ -38,6 +69,8 @@ RosGzBridge::RosGzBridge(const rclcpp::NodeOptions & options)
3869 this ->declare_parameter <bool >(" expand_gz_topic_names" , false );
3970 this ->declare_parameter <bool >(" override_timestamps_with_wall_time" , false );
4071 this ->declare_parameter <std::string>(" override_frame_id" , " " );
72+ this ->declare_parameter <bool >(" create_dynamic_bridges" , false );
73+ this ->declare_parameter <std::string>(" dynamic_bridge_direction" , " GZ_TO_ROS" );
4174 this ->declare_parameter (" bridge_names" , std::vector<std::string>());
4275 const auto names = this ->get_parameter (" bridge_names" ).as_string_array ();
4376
@@ -120,7 +153,7 @@ RosGzBridge::RosGzBridge(const rclcpp::NodeOptions & options)
120153
121154void RosGzBridge::spin ()
122155{
123- if (handles_. empty () ) {
156+ if (!configured_bridges_created_ ) {
124157 std::string config_file;
125158 this ->get_parameter (" config_file" , config_file);
126159 bool expand_names;
@@ -158,16 +191,8 @@ void RosGzBridge::spin()
158191 const auto prefix = " bridges." + name + " ." ;
159192 if (!this ->get_parameter (prefix + " ros_topic_name" ).as_string ().empty ()) {
160193 const auto directionStr = this ->get_parameter (prefix + " direction" ).as_string ();
161- BridgeDirection direction {BridgeDirection::NONE };
162- if (directionStr == " NONE" ) {
163- direction = BridgeDirection::NONE ;
164- } else if (directionStr == " BIDIRECTIONAL" ) {
165- direction = BridgeDirection::BIDIRECTIONAL ;
166- } else if (directionStr == " GZ_TO_ROS" ) {
167- direction = BridgeDirection::GZ_TO_ROS ;
168- } else if (directionStr == " ROS_TO_GZ" ) {
169- direction = BridgeDirection::ROS_TO_GZ ;
170- } else {
194+ const auto direction = parse_bridge_direction (directionStr);
195+ if (direction == BridgeDirection::NONE && directionStr != " NONE" ) {
171196 RCLCPP_ERROR (
172197 this ->get_logger (),
173198 " Bridge %s defines unknown direction %s." ,
@@ -234,12 +259,79 @@ void RosGzBridge::spin()
234259 this ->get_parameter (prefix + " service_name" ).as_string ());
235260 }
236261 }
262+ configured_bridges_created_ = true ;
237263 }
264+
265+ bool create_dynamic_bridges = false ;
266+ this ->get_parameter (" create_dynamic_bridges" , create_dynamic_bridges);
267+ if (create_dynamic_bridges) {
268+ this ->add_dynamic_bridges ();
269+ }
270+
238271 for (auto & bridge : handles_) {
239272 bridge->Spin ();
240273 }
241274}
242275
276+ void RosGzBridge::add_dynamic_bridges ()
277+ {
278+ std::vector<std::string> gz_topics;
279+ gz_node_->TopicList (gz_topics);
280+
281+ bool lazy;
282+ this ->get_parameter (" lazy" , lazy);
283+
284+ const auto direction_str = this ->get_parameter (" dynamic_bridge_direction" ).as_string ();
285+ const auto direction = parse_bridge_direction (direction_str);
286+ if (direction == BridgeDirection::NONE ) {
287+ RCLCPP_WARN (
288+ this ->get_logger (),
289+ " Ignoring dynamic bridge discovery with invalid direction [%s]." ,
290+ direction_str.c_str ());
291+ return ;
292+ }
293+
294+ for (const auto & gz_topic : gz_topics) {
295+ std::vector<gz::transport::MessagePublisher> publishers;
296+ std::vector<gz::transport::MessagePublisher> subscribers;
297+ if (!gz_node_->TopicInfo (gz_topic, publishers, subscribers)) {
298+ continue ;
299+ }
300+
301+ std::set<std::string> gz_type_names;
302+ for (const auto & publisher : publishers) {
303+ const auto gz_type_name = publisher.MsgTypeName ();
304+ if (!gz_type_name.empty ()) {
305+ gz_type_names.insert (gz_type_name);
306+ }
307+ }
308+
309+ if (gz_type_names.size () > 1 ) {
310+ RCLCPP_WARN (
311+ this ->get_logger (),
312+ " Skipping dynamic bridge for topic [%s] with multiple Gazebo types." ,
313+ gz_topic.c_str ());
314+ continue ;
315+ }
316+
317+ for (const auto & gz_type_name : gz_type_names) {
318+ std::string ros_type_name;
319+ if (!get_gz_to_ros_mapping (gz_type_name, ros_type_name)) {
320+ continue ;
321+ }
322+
323+ BridgeConfig config;
324+ config.ros_type_name = ros_type_name;
325+ config.ros_topic_name = gz_topic;
326+ config.gz_type_name = gz_type_name;
327+ config.gz_topic_name = gz_topic;
328+ config.direction = direction;
329+ config.is_lazy = lazy;
330+ this ->add_bridge (config);
331+ }
332+ }
333+ }
334+
243335void RosGzBridge::add_bridge (const BridgeConfig & input_config)
244336{
245337 // Resolve the laziness: if the caller left is_lazy as nullopt, inherit the
@@ -266,6 +358,14 @@ void RosGzBridge::add_bridge(const BridgeConfig & input_config)
266358 gz_to_ros = true ;
267359 }
268360
361+ if (!gz_to_ros && !ros_to_gz) {
362+ return ;
363+ }
364+
365+ if (!bridge_topics_.insert (bridge_key (config)).second ) {
366+ return ;
367+ }
368+
269369 try {
270370 if (gz_to_ros) {
271371 RCLCPP_INFO (
0 commit comments