00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "overlay_diagnostic_display.h"
00037 #include <OGRE/OgreOverlayManager.h>
00038 #include <OGRE/OgreMaterialManager.h>
00039 #include <OGRE/OgreTextureManager.h>
00040 #include <OGRE/OgreTexture.h>
00041 #include <OGRE/OgreHardwarePixelBuffer.h>
00042 #include <OGRE/OgreTechnique.h>
00043
00044 #include <rviz/uniform_string_stream.h>
00045 #include <rviz/display_context.h>
00046 #include <rviz/view_manager.h>
00047 #include <rviz/render_panel.h>
00048
00049 namespace jsk_rviz_plugins
00050 {
00051 const double overlay_diagnostic_animation_duration = 5.0;
00052 OverlayDiagnosticDisplay::OverlayDiagnosticDisplay()
00053 : Display()
00054 {
00055 ros_topic_property_ = new rviz::RosTopicProperty(
00056 "Topic", "/diagnostics_agg",
00057 ros::message_traits::datatype<diagnostic_msgs::DiagnosticArray>(),
00058 "diagnostic_msgs::DiagnosticArray topic to subscribe to.",
00059 this, SLOT( updateRosTopic() ));
00060 diagnostics_namespace_property_ = new rviz::EditableEnumProperty(
00061 "diagnostics namespace", "/",
00062 "diagnostics namespace to visualize diagnostics",
00063 this, SLOT(updateDiagnosticsNamespace()));
00064 top_property_ = new rviz::IntProperty(
00065 "top", 128,
00066 "top positoin",
00067 this, SLOT(updateTop()));
00068 left_property_ = new rviz::IntProperty(
00069 "left", 128,
00070 "left positoin",
00071 this, SLOT(updateLeft()));
00072 size_property_ = new rviz::IntProperty(
00073 "size", 128,
00074 "size of the widget",
00075 this, SLOT(updateSize()));
00076 size_property_->setMin(1);
00077 alpha_property_ = new rviz::FloatProperty(
00078 "alpha", 0.8,
00079 "alpha value",
00080 this, SLOT(updateAlpha()));
00081 alpha_property_->setMin(0.0);
00082 alpha_property_->setMax(1.0);
00083 stall_duration_property_ = new rviz::FloatProperty(
00084 "stall duration", 5.0,
00085 "seconds to be regarded as stalled",
00086 this, SLOT(updateStallDuration())
00087 );
00088 stall_duration_property_->setMin(0.0);
00089 }
00090
00091 OverlayDiagnosticDisplay::~OverlayDiagnosticDisplay()
00092 {
00093 if (overlay_) {
00094 overlay_->hide();
00095 }
00096
00097
00098 delete ros_topic_property_;
00099 delete diagnostics_namespace_property_;
00100 delete top_property_;
00101 delete left_property_;
00102 delete alpha_property_;
00103 delete size_property_;
00104 }
00105
00106 void OverlayDiagnosticDisplay::processMessage(
00107 const diagnostic_msgs::DiagnosticArray::ConstPtr& msg)
00108 {
00109 boost::mutex::scoped_lock(mutex_);
00110
00111
00112 std::set<std::string> new_namespaces;
00113 for (size_t i = 0; i < msg->status.size(); i++) {
00114 new_namespaces.insert(msg->status[i].name);
00115 }
00116
00117 std::set<std::string> difference_namespaces;
00118 std::set_difference(namespaces_.begin(), namespaces_.end(),
00119 new_namespaces.begin(), new_namespaces.end(),
00120 std::inserter(difference_namespaces,
00121 difference_namespaces.end()));
00122 if (difference_namespaces.size() != 0) {
00123 namespaces_ = new_namespaces;
00124 fillNamespaceList();
00125 }
00126 else {
00127 difference_namespaces.clear();
00128 std::set_difference(new_namespaces.begin(), new_namespaces.end(),
00129 namespaces_.begin(), namespaces_.end(),
00130 std::inserter(difference_namespaces,
00131 difference_namespaces.end()));
00132 if (difference_namespaces.size() != 0) {
00133 namespaces_ = new_namespaces;
00134 fillNamespaceList();
00135 }
00136 }
00137
00138 if (diagnostics_namespace_.length() == 0) {
00139 return;
00140 }
00141
00142 for (size_t i = 0; i < msg->status.size(); i++) {
00143 diagnostic_msgs::DiagnosticStatus status = msg->status[i];
00144 if (status.name == diagnostics_namespace_) {
00145 latest_status_
00146 = boost::make_shared<diagnostic_msgs::DiagnosticStatus>(status);
00147 latest_message_time_ = ros::WallTime::now();
00148 break;
00149 }
00150 }
00151 }
00152
00153 void OverlayDiagnosticDisplay::update(float wall_dt, float ros_dt)
00154 {
00155 boost::mutex::scoped_lock(mutex_);
00156 if (!isEnabled()) {
00157 return;
00158 }
00159 if (!overlay_) {
00160 static int count = 0;
00161 rviz::UniformStringStream ss;
00162 ss << "OverlayDiagnosticDisplayObject" << count++;
00163 overlay_.reset(new OverlayObject(ss.str()));
00164 overlay_->show();
00165 }
00166 t_ += wall_dt;
00167
00168 overlay_->updateTextureSize(size_, size_);
00169 redraw();
00170 overlay_->setDimensions(overlay_->getTextureWidth(),
00171 overlay_->getTextureHeight());
00172 overlay_->setPosition(left_, top_);
00173 t_ = fmod(t_, overlay_diagnostic_animation_duration);
00174 }
00175
00176 void OverlayDiagnosticDisplay::onEnable()
00177 {
00178 t_ = 0.0;
00179 if (overlay_) {
00180 overlay_->show();
00181 }
00182 subscribe();
00183 }
00184
00185 void OverlayDiagnosticDisplay::onDisable()
00186 {
00187 ROS_INFO("onDisable");
00188 if (overlay_) {
00189 overlay_->hide();
00190 }
00191 unsubscribe();
00192 }
00193
00194 void OverlayDiagnosticDisplay::onInitialize()
00195 {
00196
00197 ROS_DEBUG("onInitialize");
00198
00199
00200 updateDiagnosticsNamespace();
00201 updateSize();
00202 updateAlpha();
00203 updateLeft();
00204 updateTop();
00205 updateStallDuration();
00206 updateRosTopic();
00207 }
00208
00209 void OverlayDiagnosticDisplay::unsubscribe()
00210 {
00211 sub_.shutdown();
00212 }
00213
00214 void OverlayDiagnosticDisplay::subscribe()
00215 {
00216 ros::NodeHandle n;
00217 sub_ = n.subscribe(ros_topic_property_->getTopicStd(),
00218 1,
00219 &OverlayDiagnosticDisplay::processMessage,
00220 this);
00221 }
00222
00223 bool OverlayDiagnosticDisplay::isStalled()
00224 {
00225 if (latest_status_) {
00226 ros::WallDuration message_duration
00227 = ros::WallTime::now() - latest_message_time_;
00228 if (message_duration.toSec() < stall_duration_) {
00229 return false;
00230 }
00231 else {
00232 return true;
00233 }
00234 }
00235 else {
00236 return true;
00237 }
00238 }
00239
00240 std::string OverlayDiagnosticDisplay::statusText()
00241 {
00242 if (latest_status_) {
00243 if (!isStalled()) {
00244 if (latest_status_->level == diagnostic_msgs::DiagnosticStatus::OK) {
00245 return "OK";
00246 }
00247 else if (latest_status_->level == diagnostic_msgs::DiagnosticStatus::WARN) {
00248 return "WARN";
00249 }
00250 else if (latest_status_->level == diagnostic_msgs::DiagnosticStatus::ERROR) {
00251 return "ERROR";
00252 }
00253 else {
00254 return "UNKNOWN";
00255 }
00256 }
00257 else {
00258 return "UNKNOWN";
00259 }
00260 }
00261 else {
00262 return "UNKNOWN";
00263 }
00264 }
00265
00266
00267 QColor OverlayDiagnosticDisplay::foregroundColor()
00268 {
00269 QColor ok_color(25, 255, 240, alpha_ * 255.0);
00270 QColor warn_color(240, 173, 78, alpha_ * 255.0);
00271 QColor error_color(217, 83, 79, alpha_ * 255.0);
00272 QColor stall_color(151, 151, 151, alpha_ * 255.0);
00273
00274
00275 if (latest_status_) {
00276 if (!isStalled()) {
00277 if (latest_status_->level == diagnostic_msgs::DiagnosticStatus::OK) {
00278 return ok_color;
00279 }
00280 else if (latest_status_->level
00281 == diagnostic_msgs::DiagnosticStatus::WARN) {
00282 return warn_color;
00283 }
00284 else if (latest_status_->level
00285 == diagnostic_msgs::DiagnosticStatus::ERROR) {
00286 return error_color;
00287 }
00288 else {
00289 return stall_color;
00290 }
00291 }
00292 else {
00293 return stall_color;
00294 }
00295 }
00296 else {
00297 return stall_color;
00298 }
00299 }
00300
00301 QColor OverlayDiagnosticDisplay::blendColor(QColor a, QColor b, double a_rate) {
00302 QColor ret (a.red() * a_rate + b.red() * (1 - a_rate),
00303 a.green() * a_rate + b.green() * (1 - a_rate),
00304 a.blue() * a_rate + b.blue() * (1 - a_rate),
00305 a.alpha() * a_rate + b.alpha() * (1 - a_rate));
00306 return ret;
00307 }
00308
00309 double OverlayDiagnosticDisplay::drawAnimatingText(QPainter& painter,
00310 QColor fg_color,
00311 const double height,
00312 const double font_size,
00313 const std::string text)
00314 {
00315 const double r = size_ / 128.0;
00316 QFont font("Arial", font_size * r, font_size * r, false);
00317 QPen pen;
00318 QPainterPath path;
00319 pen.setWidth(1);
00320 pen.setColor(blendColor(fg_color, Qt::white, 0.5));
00321 painter.setFont(font);
00322 painter.setPen(pen);
00323 painter.setBrush(fg_color);
00324 QFontMetrics metrics(font);
00325 const int text_width = metrics.width(text.c_str());
00326 const int text_height = metrics.height();
00327 if (overlay_->getTextureWidth() > text_width) {
00328 path.addText((overlay_->getTextureWidth() - text_width) / 2.0,
00329 height,
00330 font, text.c_str());
00331 }
00332 else {
00333 double left = - fmod(t_, overlay_diagnostic_animation_duration) /
00334 overlay_diagnostic_animation_duration * text_width * 2 + text_width;
00335 path.addText(left, height, font, text.c_str());
00336 }
00337 painter.drawPath(path);
00338 return text_height;
00339 }
00340
00341 void OverlayDiagnosticDisplay::drawText(QPainter& painter, QColor fg_color,
00342 const std::string& text)
00343 {
00344 double status_size = drawAnimatingText(painter, fg_color,
00345 overlay_->getTextureHeight() / 3.0,
00346 20, text);
00347 double namespace_size = drawAnimatingText(painter, fg_color,
00348 overlay_->getTextureHeight() / 3.0 + status_size,
00349 10, diagnostics_namespace_);
00350 std::string message;
00351 if (latest_status_) {
00352 if (!isStalled()) {
00353 message = latest_status_->message;
00354 }
00355 else {
00356 message = "stalled";
00357 }
00358 }
00359 else {
00360 message = "stalled";
00361 }
00362 drawAnimatingText(painter, fg_color,
00363 overlay_->getTextureHeight() / 3.0
00364 + status_size + namespace_size,
00365 10, message);
00366 }
00367
00368 void OverlayDiagnosticDisplay::redraw()
00369 {
00370 ScopedPixelBuffer buffer = overlay_->getBuffer();
00371 QColor fg_color = foregroundColor();
00372 QColor transparent(0, 0, 0, 0.0);
00373
00374 QImage Hud = buffer.getQImage(*overlay_, transparent);
00375
00376
00377 QPainter painter( &Hud );
00378 const int line_width = 10;
00379 const int margin = 10;
00380 const int inner_line_width = 20;
00381
00382 painter.setRenderHint(QPainter::Antialiasing, true);
00383 painter.setPen(QPen(fg_color, line_width, Qt::SolidLine));
00384 painter.drawEllipse(line_width / 2, line_width / 2,
00385 overlay_->getTextureWidth() - line_width,
00386 overlay_->getTextureHeight() - line_width);
00387
00388 painter.setPen(QPen(fg_color, inner_line_width, Qt::SolidLine));
00389 const double start_angle = fmod(t_, overlay_diagnostic_animation_duration) /
00390 overlay_diagnostic_animation_duration * 360;
00391 const double draw_angle = 250;
00392 const double inner_circle_start
00393 = line_width + margin + inner_line_width / 2.0;
00394 drawText(painter, fg_color, statusText());
00395 }
00396
00397 void OverlayDiagnosticDisplay::fillNamespaceList()
00398 {
00399
00400 diagnostics_namespace_property_->clearOptions();
00401 for (std::set<std::string>::iterator it = namespaces_.begin();
00402 it != namespaces_.end();
00403 it++) {
00404 diagnostics_namespace_property_->addOptionStd(*it);
00405 }
00406 diagnostics_namespace_property_->sortOptions();
00407 }
00408
00409 void OverlayDiagnosticDisplay::updateRosTopic()
00410 {
00411 latest_status_.reset();
00412 unsubscribe();
00413 subscribe();
00414 }
00415
00416 void OverlayDiagnosticDisplay::updateDiagnosticsNamespace()
00417 {
00418 latest_status_.reset();
00419 diagnostics_namespace_ = diagnostics_namespace_property_->getStdString();
00420 }
00421
00422 void OverlayDiagnosticDisplay::updateSize()
00423 {
00424 size_ = size_property_->getInt();
00425 }
00426
00427 void OverlayDiagnosticDisplay::updateAlpha()
00428 {
00429 alpha_ = alpha_property_->getFloat();
00430 }
00431
00432 void OverlayDiagnosticDisplay::updateTop()
00433 {
00434 top_ = top_property_->getInt();
00435 }
00436
00437 void OverlayDiagnosticDisplay::updateLeft()
00438 {
00439 left_ = left_property_->getInt();
00440 }
00441
00442 void OverlayDiagnosticDisplay::updateStallDuration()
00443 {
00444 stall_duration_ = stall_duration_property_->getFloat();
00445 }
00446 }
00447
00448 #include <pluginlib/class_list_macros.h>
00449 PLUGINLIB_EXPORT_CLASS( jsk_rviz_plugins::OverlayDiagnosticDisplay, rviz::Display)