2121#include < cstring>
2222#include < mutex>
2323#include < unordered_map>
24+ #include < sys/stat.h>
25+ #include < yaml-cpp/yaml.h>
2426
2527#include " rosidl_typesupport_introspection_cpp/message_introspection.hpp"
2628
2729using namespace std ::chrono_literals;
2830
31+ namespace {
32+
33+ // Check if a file exists and is a regular file
34+ bool is_valid_file (const std::string& path) {
35+ struct stat st;
36+ if (stat (path.c_str (), &st) != 0 ) { return false ; }
37+ return static_cast <bool >(st.st_mode & S_IFREG);
38+ }
39+
40+ } // anonymous namespace
41+
2942GreenwaveMonitor::GreenwaveMonitor (const rclcpp::NodeOptions & options)
3043: Node(" greenwave_monitor" , options)
3144{
@@ -35,6 +48,26 @@ GreenwaveMonitor::GreenwaveMonitor(const rclcpp::NodeOptions & options)
3548 this ->declare_parameter <std::vector<std::string>>(" topics" , {" " });
3649 auto topics = this ->get_parameter (" topics" ).as_string_array ();
3750
51+ // Declare and get the type registry path parameter
52+ this ->declare_parameter <std::string>(" type_registry_path" , " /workspace/colcon_ws/src/greenwave_monitor/greenwave_monitor/config/type_registry.yml" );
53+ type_registry_path_ = this ->get_parameter (" type_registry_path" ).as_string ();
54+
55+ // Check if the type registry path is valid
56+ if (!type_registry_path_.empty () && !is_valid_file (type_registry_path_)) {
57+ RCLCPP_ERROR (
58+ this ->get_logger (), " Type registry path '%s' is not a valid file" ,
59+ type_registry_path_.c_str ());
60+ type_registry_path_.clear ();
61+ return ;
62+ }
63+
64+ if (!read_type_registry (type_registry_path_)) {
65+ RCLCPP_WARN (
66+ this ->get_logger (), " Failed to read type registry from path '%s'. "
67+ " Assuming no message types have a header. Consider providing a valid type registry." ,
68+ type_registry_path_.c_str ());
69+ }
70+
3871 message_diagnostics::MessageDiagnosticsConfig diagnostics_config;
3972 diagnostics_config.enable_all_topic_diagnostics = true ;
4073
@@ -90,6 +123,45 @@ void GreenwaveMonitor::topic_callback(
90123 message_diagnostics_[topic]->updateDiagnostics (msg_timestamp.time_since_epoch ().count ());
91124}
92125
126+ // Read YAML type registry
127+ bool GreenwaveMonitor::read_type_registry (const std::string path) {
128+ try {
129+ YAML::Node config = YAML::LoadFile (path);
130+ if (config[" has_header" ]) {
131+ // Check if 'has_header' is a sequence (list)
132+ if (config[" has_header" ].IsSequence ()) {
133+ for (const auto & type_node : config[" has_header" ]) {
134+ if (type_node.IsScalar ()) {
135+ known_header_types_[type_node.as <std::string>()] = true ;
136+ } else {
137+ RCLCPP_WARN (
138+ this ->get_logger (),
139+ " Found a non-string value in the registry: %s. Skipping." ,
140+ type_node.as <std::string>().c_str ());
141+ }
142+ }
143+ } else {
144+ RCLCPP_ERROR (
145+ this ->get_logger (),
146+ " 'has_header' key is not a sequence in the YAML file." );
147+ return false ;
148+ }
149+ } else {
150+ RCLCPP_ERROR (
151+ this ->get_logger (),
152+ " 'has_header' key is not found in the YAML file." );
153+ return false ;
154+ }
155+ } catch (const YAML::BadFile& e) {
156+ RCLCPP_ERROR (this ->get_logger (), " Error reading YAML file: %s" , e.what ());
157+ } catch (const YAML::ParserException& e) {
158+ RCLCPP_ERROR (this ->get_logger (), " Error parsing YAML string: %s" , e.what ());
159+ } catch (const std::exception& e) {
160+ RCLCPP_ERROR (this ->get_logger (), " An unexpected error occurred: %s" , e.what ());
161+ }
162+ return true ;
163+ }
164+
93165void GreenwaveMonitor::timer_callback ()
94166{
95167 RCLCPP_INFO (this ->get_logger (), " ====================================================" );
@@ -182,69 +254,13 @@ bool GreenwaveMonitor::has_header_from_type(const std::string & type_name)
182254 return type_has_header_cache[type_name];
183255 }
184256
185- // rosidl typesupport API is unstable across ROS distributions, so we use this
186- // map as a more robust way to determine if a message type has a header
187- static const std::unordered_map<std::string, bool > known_header_types = {
188- // sensor_msgs
189- {" sensor_msgs/msg/Image" , true },
190- {" sensor_msgs/msg/CompressedImage" , true },
191- {" sensor_msgs/msg/CameraInfo" , true },
192- {" sensor_msgs/msg/PointCloud2" , true },
193- {" sensor_msgs/msg/LaserScan" , true },
194- {" sensor_msgs/msg/Imu" , true },
195- {" sensor_msgs/msg/NavSatFix" , true },
196- {" sensor_msgs/msg/MagneticField" , true },
197- {" sensor_msgs/msg/FluidPressure" , true },
198- {" sensor_msgs/msg/Illuminance" , true },
199- {" sensor_msgs/msg/RelativeHumidity" , true },
200- {" sensor_msgs/msg/Temperature" , true },
201- {" sensor_msgs/msg/Range" , true },
202- {" sensor_msgs/msg/PointCloud" , true },
203-
204- // geometry_msgs
205- {" geometry_msgs/msg/PoseStamped" , true },
206- {" geometry_msgs/msg/TwistStamped" , true },
207- {" geometry_msgs/msg/AccelStamped" , true },
208- {" geometry_msgs/msg/Vector3Stamped" , true },
209- {" geometry_msgs/msg/PointStamped" , true },
210- {" geometry_msgs/msg/QuaternionStamped" , true },
211- {" geometry_msgs/msg/TransformStamped" , true },
212- {" geometry_msgs/msg/WrenchStamped" , true },
213-
214- // nav_msgs
215- {" nav_msgs/msg/OccupancyGrid" , true },
216- {" nav_msgs/msg/GridCells" , true },
217- {" nav_msgs/msg/Path" , true },
218- {" nav_msgs/msg/Odometry" , true },
219-
220- // visualization_msgs
221- {" visualization_msgs/msg/Marker" , true },
222- {" visualization_msgs/msg/MarkerArray" , true },
223- {" visualization_msgs/msg/InteractiveMarker" , true },
224-
225- // std_msgs (no headers)
226- {" std_msgs/msg/String" , false },
227- {" std_msgs/msg/Int32" , false },
228- {" std_msgs/msg/Float64" , false },
229- {" std_msgs/msg/Bool" , false },
230- {" std_msgs/msg/Empty" , false },
231- {" std_msgs/msg/Header" , false }, // Header itself doesn't have a header
232-
233- // Common message types without headers
234- {" geometry_msgs/msg/Twist" , false },
235- {" geometry_msgs/msg/Pose" , false },
236- {" geometry_msgs/msg/Point" , false },
237- {" geometry_msgs/msg/Vector3" , false },
238- {" geometry_msgs/msg/Quaternion" , false }
239- };
240-
241- auto it = known_header_types.find (type_name);
242- bool has_header = (it != known_header_types.end ()) ? it->second : false ;
257+ auto it = known_header_types_.find (type_name);
258+ bool has_header = (it != known_header_types_.end ()) ? it->second : false ;
243259
244260 type_has_header_cache[type_name] = has_header;
245261
246262 // Fallback of no header in case of unknown type, log for reference
247- if (it == known_header_types .end ()) {
263+ if (it == known_header_types_ .end ()) {
248264 RCLCPP_WARN_ONCE (
249265 this ->get_logger (),
250266 " Unknown message type '%s' - assuming no header. Consider adding to registry." ,
0 commit comments