Nav2 Navigation Stack - jazzy  jazzy
ROS 2 Navigation Stack
validate_messages.hpp
1 // Copyright (c) 2024 GoesM
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 
16 #ifndef NAV2_UTIL__VALIDATE_MESSAGES_HPP_
17 #define NAV2_UTIL__VALIDATE_MESSAGES_HPP_
18 
19 #include <array>
20 #include <cmath>
21 #include <iostream>
22 
23 #include "nav_msgs/msg/occupancy_grid.hpp"
24 #include "nav_msgs/msg/odometry.hpp"
25 #include "geometry_msgs/msg/pose_with_covariance_stamped.hpp"
26 
27 
28 // @brief Validation Check
29 // Check recieved message is safe or not for the nav2-system
30 // For each msg-type known in nav2, we could check it as following:
31 // if(!validateMsg()) RCLCPP_ERROR(,"malformed msg. Rejecting.")
32 //
33 // Workflow of validateMsg():
34 // if here's a sub-msg-type in the recieved msg,
35 // the content of sub-msg would be checked as sub-msg-type
36 // then, check the whole recieved msg.
37 //
38 // Following conditions are involved in check:
39 // 1> Value Check: to avoid damaged value like like `nan`, `INF`, empty string and so on
40 // 2> Logic Check: to avoid value with bad logic,
41 // like the size of `map` should be equal to `height*width`
42 // 3> Any other needed condition could be joint here in future
43 
44 namespace nav2_util
45 {
46 
47 
48 bool validateMsg(const double & num)
49 {
50  /* @brief double/float value check
51  * if here'a need to check message validation
52  * it should be avoid to use double value like `nan`, `inf`
53  * otherwise, we regard it as an invalid message
54  */
55  if (std::isinf(num)) {return false;}
56  if (std::isnan(num)) {return false;}
57  return true;
58 }
59 
60 template<size_t N>
61 bool validateMsg(const std::array<double, N> & msg)
62 {
63  /* @brief value check for double-array
64  * like the field `covariance` used in the msg-type:
65  * geometry_msgs::msg::PoseWithCovarianceStamped
66  */
67  for (const auto & element : msg) {
68  if (!validateMsg(element)) {return false;}
69  }
70 
71  return true;
72 }
73 
74 const int NSEC_PER_SEC = 1e9; // 1 second = 1e9 nanosecond
75 bool validateMsg(const builtin_interfaces::msg::Time & msg)
76 {
77  if (msg.nanosec >= NSEC_PER_SEC) {
78  return false; // invalid nanosec-stamp
79  }
80  return true;
81 }
82 
83 bool validateMsg(const std_msgs::msg::Header & msg)
84 {
85  // check sub-type
86  if (!validateMsg(msg.stamp)) {return false;}
87 
88  /* @brief frame_id check
89  * if here'a need to check message validation
90  * it should at least have a non-empty frame_id
91  * otherwise, we regard it as an invalid message
92  */
93  if (msg.frame_id.empty()) {return false;}
94  return true;
95 }
96 
97 bool validateMsg(const geometry_msgs::msg::Point & msg)
98 {
99  // check sub-type
100  if (!validateMsg(msg.x)) {return false;}
101  if (!validateMsg(msg.y)) {return false;}
102  if (!validateMsg(msg.z)) {return false;}
103  return true;
104 }
105 
106 const double epsilon = 1e-4;
107 bool validateMsg(const geometry_msgs::msg::Quaternion & msg)
108 {
109  // check sub-type
110  if (!validateMsg(msg.x)) {return false;}
111  if (!validateMsg(msg.y)) {return false;}
112  if (!validateMsg(msg.z)) {return false;}
113  if (!validateMsg(msg.w)) {return false;}
114 
115  if (abs(msg.x * msg.x + msg.y * msg.y + msg.z * msg.z + msg.w * msg.w - 1.0) >= epsilon) {
116  return false;
117  }
118 
119  return true;
120 }
121 
122 bool validateMsg(const geometry_msgs::msg::Pose & msg)
123 {
124  // check sub-type
125  if (!validateMsg(msg.position)) {return false;}
126  if (!validateMsg(msg.orientation)) {return false;}
127  return true;
128 }
129 
130 bool validateMsg(const geometry_msgs::msg::PoseWithCovariance & msg)
131 {
132  // check sub-type
133  if (!validateMsg(msg.pose)) {return false;}
134  if (!validateMsg(msg.covariance)) {return false;}
135 
136  return true;
137 }
138 
139 bool validateMsg(const geometry_msgs::msg::PoseWithCovarianceStamped & msg)
140 {
141  // check sub-type
142  if (!validateMsg(msg.header)) {return false;}
143  if (!validateMsg(msg.pose)) {return false;}
144  if (!validateMsg(msg.pose.covariance)) {return false;}
145  return true;
146 }
147 
148 const double MIN_MAP_RESOLUTION = 1e-6;
149 
150 // Function to verify map meta information
151 bool validateMsg(const nav_msgs::msg::MapMetaData & msg)
152 {
153  // check sub-type
154  if (!validateMsg(msg.origin)) {return false;}
155  if (!validateMsg(msg.resolution)) {return false;}
156 
157  // check logic
158  if (msg.resolution <= MIN_MAP_RESOLUTION) {return false;} // check map-resolution
159 
160  // logic check
161  // 1> we don't need an empty map
162  if (msg.height == 0 || msg.width == 0) {return false;}
163  return true;
164 }
165 
166 // for msg-type like map, costmap and others as `OccupancyGrid`
167 bool validateMsg(const nav_msgs::msg::OccupancyGrid & msg)
168 {
169  // check sub-type
170  if (!validateMsg(msg.header)) {return false;}
171  // msg.data : @todo any check for it ?
172  if (!validateMsg(msg.info)) {return false;}
173 
174  if (msg.data.size() != msg.info.width * msg.info.height) {
175  return false; // check map-size
176  }
177 
178  // avoid overflow by multiplying width and height
179  if (msg.info.width > INT16_MAX || msg.info.height > INT16_MAX) {
180  return false;
181  }
182 
183  uint32_t num_cells;
184  if (__builtin_mul_overflow(msg.info.width, msg.info.height, &num_cells)) {
185  return false;
186  }
187 
188  return true;
189 }
190 
191 
192 } // namespace nav2_util
193 
194 
195 #endif // NAV2_UTIL__VALIDATE_MESSAGES_HPP_