moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
interactive_marker_display.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2008, Willow Garage, Inc.
3 * Copyright (c) 2019, Open Source Robotics Foundation, Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of the copyright holder nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30#include <map>
31#include <memory>
32#include <string>
33#include <utility>
34#include <vector>
35
36#include <rviz_common/properties/bool_property.hpp>
37#include <rviz_common/validate_floats.hpp>
38#include <rviz_common/display_context.hpp>
39
40#include <rviz_default_plugins/displays/interactive_markers/interactive_marker_namespace_property.hpp>
41
43
45{
46namespace displays
47{
48bool validateFloats(const visualization_msgs::msg::InteractiveMarker& msg)
49{
50 bool valid = true;
51 valid = valid && rviz_common::validateFloats(msg.pose);
52 valid = valid && rviz_common::validateFloats(msg.scale);
53 for (const auto& control : msg.controls)
54 {
55 valid = valid && rviz_common::validateFloats(control.orientation);
56 for (const auto& marker : control.markers)
57 {
58 valid = valid && rviz_common::validateFloats(marker.pose);
59 valid = valid && rviz_common::validateFloats(marker.scale);
60 valid = valid && rviz_common::validateFloats(marker.color);
61 valid = valid && rviz_common::validateFloats(marker.points);
62 }
63 }
64 return valid;
65}
66
68{
69 interactive_marker_namespace_property_ =
70 new InteractiveMarkerNamespaceProperty("Interactive Markers Namespace", "",
71 "Namespace of the interactive marker server to connect to.", this,
72 SLOT(namespaceChanged()));
73
74 show_descriptions_property_ =
75 new rviz_common::properties::BoolProperty("Show Descriptions", true,
76 "Whether or not to show the descriptions of each Interactive Marker.",
77 this, SLOT(updateShowDescriptions()));
78
79 show_axes_property_ = new rviz_common::properties::BoolProperty(
80 "Show Axes", false, "Whether or not to show the axes of each Interactive Marker.", this, SLOT(updateShowAxes()));
81
82 show_visual_aids_property_ = new rviz_common::properties::BoolProperty(
83 "Show Visual Aids", false, "Whether or not to show visual helpers while moving/rotating Interactive Markers.",
84 this, SLOT(updateShowVisualAids()));
85
86 enable_transparency_property_ = new rviz_common::properties::BoolProperty(
87 "Enable Transparency", true,
88 "Whether or not to allow transparency for auto-completed markers (e.g. rings and arrows).", this,
90}
91
93{
94 auto ros_node_abstraction = context_->getRosNodeAbstraction().lock();
95 if (!ros_node_abstraction)
96 {
97 return;
98 }
99 interactive_marker_namespace_property_->initialize(ros_node_abstraction);
100
101 rclcpp::NodeOptions options;
102 options.arguments({ "--ros-args", "-r",
103 "__node:=" + std::string("interactive_marker_display_") +
104 std::to_string(reinterpret_cast<std::size_t>(this)) });
105 pnode_ = rclcpp::Node::make_shared("_", "", options);
106 private_executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
107 private_executor_->add_node(pnode_);
108 // start executor on a different thread now
109 private_executor_thread_ = std::thread([this]() { private_executor_->spin(); });
110 auto frame_transformer = context_->getFrameManager()->getTransformer();
111 // rclcpp::Node::SharedPtr node = ros_node_abstraction->get_raw_node();
112 interactive_marker_client_ = std::make_unique<interactive_markers::InteractiveMarkerClient>(
113 pnode_, frame_transformer, fixed_frame_.toStdString());
114
115 interactive_marker_client_->setInitializeCallback(
116 [this](const visualization_msgs::srv::GetInteractiveMarkers::Response::SharedPtr& msg) {
117 return initializeCallback(msg);
118 });
119 interactive_marker_client_->setUpdateCallback(
120 [this](const visualization_msgs::msg::InteractiveMarkerUpdate::ConstSharedPtr& msg) {
121 return updateCallback(msg);
122 });
123 interactive_marker_client_->setResetCallback([this]() { return resetCallback(); });
124 interactive_marker_client_->setStatusCallback(
125 [this](interactive_markers::InteractiveMarkerClient::Status status, const std::string& message) {
126 return statusCallback(status, message);
127 });
128
129 subscribe();
130}
131
133{
134 subscribe();
135}
136
138{
139 unsubscribe();
140}
141
143{
144 unsubscribe();
145
146 if (interactive_marker_namespace_property_->isEmpty())
147 {
148 setStatus(rviz_common::properties::StatusProperty::Error, "Interactive Marker Client",
149 QString("Error connecting: empty namespace"));
150 return;
151 }
152
153 subscribe();
154}
155
156void InteractiveMarkerDisplay::subscribe()
157{
158 const std::string topic_namespace = interactive_marker_namespace_property_->getNamespaceStd();
159 if (isEnabled() && !topic_namespace.empty())
160 {
161 interactive_marker_client_->connect(topic_namespace);
162 }
163}
164
165void InteractiveMarkerDisplay::publishFeedback(visualization_msgs::msg::InteractiveMarkerFeedback& feedback)
166{
167 interactive_marker_client_->publishFeedback(feedback);
168}
169
170void InteractiveMarkerDisplay::onStatusUpdate(rviz_common::properties::StatusProperty::Level level,
171 const std::string& name, const std::string& text)
172{
173 setStatusStd(level, name, text);
174}
175
176void InteractiveMarkerDisplay::unsubscribe()
177{
178 if (interactive_marker_client_)
179 {
180 interactive_marker_client_->disconnect();
181 }
182 Display::reset();
183}
184
185// rviz_common 16.0.1 (Rolling) removed the deprecated (float, float) overload
186// in favor of (std::chrono::nanoseconds, std::chrono::nanoseconds); see
187// ros2/rviz#1533. Header declaration in interactive_marker_display.hpp is
188// guarded the same way; remove this branch when Kilted goes EOL.
189// For Rolling, L-turtle, and newer
190#if RCLCPP_VERSION_GTE(30, 0, 0)
191void InteractiveMarkerDisplay::update(std::chrono::nanoseconds wall_dt, std::chrono::nanoseconds ros_dt)
192// For Kilted and older
193#else
194void InteractiveMarkerDisplay::update(float wall_dt, float ros_dt)
195#endif
196{
197 (void)wall_dt;
198 (void)ros_dt;
199
200 interactive_marker_client_->update();
201
202 for (const auto& name_marker_pair : interactive_markers_map_)
203 {
204 name_marker_pair.second->update();
205 }
206}
207
208void InteractiveMarkerDisplay::updateMarkers(const std::vector<visualization_msgs::msg::InteractiveMarker>& markers)
209{
210 for (const visualization_msgs::msg::InteractiveMarker& marker : markers)
211 {
212 if (!validateFloats(marker))
213 {
214 setStatusStd(rviz_common::properties::StatusProperty::Error, marker.name, "Marker contains invalid floats!");
215 continue;
216 }
217 RVIZ_COMMON_LOG_DEBUG_STREAM("Processing interactive marker '" << marker.name << "'. " << marker.controls.size());
218
219 auto int_marker_entry = interactive_markers_map_.find(marker.name);
220
221 if (int_marker_entry == interactive_markers_map_.end())
222 {
223 int_marker_entry =
224 interactive_markers_map_
225 .insert(std::make_pair(marker.name, std::make_shared<InteractiveMarker>(getSceneNode(), context_)))
226 .first;
227 connect(int_marker_entry->second.get(), SIGNAL(userFeedback(visualization_msgs::msg::InteractiveMarkerFeedback&)),
228 this, SLOT(publishFeedback(visualization_msgs::msg::InteractiveMarkerFeedback&)));
229 connect(
230 int_marker_entry->second.get(),
231 SIGNAL(statusUpdate(rviz_common::properties::StatusProperty::Level, const std::string&, const std::string&)),
232 this,
233 SLOT(onStatusUpdate(rviz_common::properties::StatusProperty::Level, const std::string&, const std::string&)));
234 }
235
236 if (int_marker_entry->second->processMessage(marker))
237 {
238 int_marker_entry->second->setShowAxes(show_axes_property_->getBool());
239 int_marker_entry->second->setShowVisualAids(show_visual_aids_property_->getBool());
240 int_marker_entry->second->setShowDescription(show_descriptions_property_->getBool());
241 }
242 else
243 {
244 unsubscribe();
245 return;
246 }
247 }
248}
249
250void InteractiveMarkerDisplay::eraseAllMarkers()
251{
252 interactive_markers_map_.clear();
253 deleteStatusStd("Interactive Marker Client");
254}
255
256void InteractiveMarkerDisplay::eraseMarkers(const std::vector<std::string>& erases)
257{
258 for (const std::string& marker_name : erases)
259 {
260 interactive_markers_map_.erase(marker_name);
261 deleteStatusStd(marker_name);
262 }
263}
264
265void InteractiveMarkerDisplay::updatePoses(
266 const std::vector<visualization_msgs::msg::InteractiveMarkerPose>& marker_poses)
267{
268 for (const visualization_msgs::msg::InteractiveMarkerPose& marker_pose : marker_poses)
269 {
270 if (!rviz_common::validateFloats(marker_pose.pose))
271 {
272 setStatusStd(rviz_common::properties::StatusProperty::Error, marker_pose.name,
273 "Pose message contains invalid floats!");
274 return;
275 }
276
277 auto int_marker_entry = interactive_markers_map_.find(marker_pose.name);
278
279 if (int_marker_entry != interactive_markers_map_.end())
280 {
281 int_marker_entry->second->processMessage(marker_pose);
282 }
283 else
284 {
285 setStatusStd(rviz_common::properties::StatusProperty::Error, marker_pose.name,
286 "Pose received for non-existing marker '" + marker_pose.name);
287 unsubscribe();
288 return;
289 }
290 }
291}
292
293void InteractiveMarkerDisplay::initializeCallback(
294 const visualization_msgs::srv::GetInteractiveMarkers::Response::SharedPtr& msg)
295{
296 eraseAllMarkers();
297 updateMarkers(msg->markers);
298}
299
300void InteractiveMarkerDisplay::updateCallback(const visualization_msgs::msg::InteractiveMarkerUpdate::ConstSharedPtr& msg)
301{
302 updateMarkers(msg->markers);
303 updatePoses(msg->poses);
304 eraseMarkers(msg->erases);
305}
306
307void InteractiveMarkerDisplay::resetCallback()
308{
309 eraseAllMarkers();
310 deleteStatusStd("Interactive Marker Client");
311}
312
313void InteractiveMarkerDisplay::statusCallback(interactive_markers::InteractiveMarkerClient::Status status,
314 const std::string& message)
315{
316 rviz_common::properties::StatusProperty::Level rviz_level;
317 switch (status)
318 {
319 case interactive_markers::InteractiveMarkerClient::STATUS_DEBUG:
320 rviz_level = rviz_common::properties::StatusProperty::Ok;
321 break;
322 case interactive_markers::InteractiveMarkerClient::STATUS_INFO:
323 rviz_level = rviz_common::properties::StatusProperty::Ok;
324 break;
325 case interactive_markers::InteractiveMarkerClient::STATUS_WARN:
326 rviz_level = rviz_common::properties::StatusProperty::Warn;
327 break;
328 case interactive_markers::InteractiveMarkerClient::STATUS_ERROR:
329 rviz_level = rviz_common::properties::StatusProperty::Error;
330 break;
331 default:
332 RVIZ_COMMON_LOG_WARNING("Unexpected status level from interactive marker client received");
333 rviz_level = rviz_common::properties::StatusProperty::Error;
334 }
335 setStatusStd(rviz_level, "Interactive Marker Client", message);
336}
337
339{
340 if (interactive_marker_client_)
341 {
342 interactive_marker_client_->setTargetFrame(fixed_frame_.toStdString());
343 }
344 Display::reset();
345}
346
348{
349 Display::reset();
350 unsubscribe();
351 subscribe();
352}
353
355{
356 bool show = show_descriptions_property_->getBool();
357
358 for (const auto& name_marker_pair : interactive_markers_map_)
359 {
360 name_marker_pair.second->setShowDescription(show);
361 }
362}
363
365{
366 bool show = show_axes_property_->getBool();
367
368 for (const auto& name_marker_pair : interactive_markers_map_)
369 {
370 name_marker_pair.second->setShowAxes(show);
371 }
372}
373
375{
376 bool show = show_visual_aids_property_->getBool();
377
378 for (const auto& name_marker_pair : interactive_markers_map_)
379 {
380 name_marker_pair.second->setShowVisualAids(show);
381 }
382}
383
385{
386 // This is not very efficient, but works
387 unsubscribe();
388 interactive_marker_client_->setEnableAutocompleteTransparency(enable_transparency_property_->getBool());
389 subscribe();
390}
391
392} // namespace displays
393} // namespace rviz_default_plugins
394
395#include <pluginlib/class_list_macros.hpp> // NOLINT(build/include_order)
PLUGINLIB_EXPORT_CLASS(cached_ik_kinematics_plugin::CachedIKKinematicsPlugin< kdl_kinematics_plugin::KDLKinematicsPlugin >, kinematics::KinematicsBase)
void publishFeedback(visualization_msgs::msg::InteractiveMarkerFeedback &feedback)
void onStatusUpdate(rviz_common::properties::StatusProperty::Level level, const std::string &name, const std::string &text)
bool validateFloats(const visualization_msgs::msg::InteractiveMarker &msg)