generic_analyzer.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2009, Willow Garage, Inc.
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the Willow Garage nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *********************************************************************/
00034 
00037 #include "diagnostic_aggregator/generic_analyzer.h"
00038 
00039 using namespace diagnostic_aggregator;
00040 using namespace std;
00041 
00042 PLUGINLIB_DECLARE_CLASS(diagnostic_aggregator,
00043                         GenericAnalyzer, 
00044                         diagnostic_aggregator::GenericAnalyzer, 
00045                         diagnostic_aggregator::Analyzer)
00046 
00047 
00048 GenericAnalyzer::GenericAnalyzer() { }
00049 
00050 bool GenericAnalyzer::init(const string base_path, const ros::NodeHandle &n)
00051 { 
00052   string nice_name;
00053   if (!n.getParam("path", nice_name))
00054   {
00055     ROS_ERROR("GenericAnalyzer was not given parameter \"path\". Namepspace: %s",
00056               n.getNamespace().c_str());
00057     return false;
00058   }
00059 
00060   XmlRpc::XmlRpcValue findRemove;
00061   if (n.getParam("find_and_remove_prefix", findRemove))
00062   {
00063     vector<string> output;
00064     getParamVals(findRemove, output);
00065     chaff_ = output;
00066     startswith_ = output;
00067   }
00068   
00069   XmlRpc::XmlRpcValue removes;
00070   if (n.getParam("remove_prefix", removes))
00071     getParamVals(removes, chaff_);
00072     
00073   XmlRpc::XmlRpcValue startswith;
00074   if (n.getParam("startswith", startswith))
00075     getParamVals(startswith, startswith_);
00076 
00077   XmlRpc::XmlRpcValue name_val;
00078   if (n.getParam("name", name_val))
00079     getParamVals(name_val, name_);
00080 
00081   XmlRpc::XmlRpcValue contains;
00082   if (n.getParam("contains", contains))
00083     getParamVals(contains, contains_);
00084 
00085   XmlRpc::XmlRpcValue expected;
00086   if (n.getParam("expected", expected))
00087   {
00088     getParamVals(expected, expected_);
00089     for (unsigned int i = 0; i < expected_.size(); ++i)
00090     {
00091       boost::shared_ptr<StatusItem> item(new StatusItem(expected_[i]));
00092       addItem(expected_[i], item);
00093     }
00094  }
00095  
00096   XmlRpc::XmlRpcValue regexes;
00097   if (n.getParam("regex", regexes))
00098   {
00099     vector<string> regex_strs;
00100     getParamVals(regexes, regex_strs);
00101   
00102     for (unsigned int i = 0; i < regex_strs.size(); ++i)
00103     {
00104       try
00105       {
00106         boost::regex re(regex_strs[i]);
00107         regex_.push_back(re);
00108       }
00109       catch (boost::regex_error& e)
00110       {
00111         ROS_ERROR("Attempted to make regex from %s. Caught exception, ignoring value. Exception: %s", 
00112                  regex_strs[i].c_str(), e.what());
00113       }
00114     }
00115   }
00116 
00117   if (startswith_.size() == 0 && name_.size() == 0 && 
00118       contains_.size() == 0 && expected_.size() == 0 && regex_.size() == 0)
00119   {
00120     ROS_ERROR("GenericAnalyzer was not initialized with any way of checking diagnostics. Name: %s, namespace: %s", nice_name.c_str(), n.getNamespace().c_str());
00121     return false;
00122   }
00123   
00124   double timeout;
00125   int num_items_expected;
00126   bool discard_stale;
00127   n.param("timeout", timeout, 5.0);   // Timeout for stale
00128   n.param("num_items", num_items_expected, -1); // Number of items must match this
00129   n.param("discard_stale", discard_stale, false);
00130 
00131   string my_path;
00132   if (base_path == "/")
00133     my_path = nice_name;
00134   else
00135     my_path = base_path + "/" + nice_name;
00136 
00137   if (my_path.find("/") != 0)
00138     my_path = "/" + my_path;
00139 
00140   return GenericAnalyzerBase::init(my_path, nice_name, 
00141                                    timeout, num_items_expected, discard_stale);
00142 }
00143 
00144 GenericAnalyzer::~GenericAnalyzer() { }
00145 
00146 
00147 bool GenericAnalyzer::match(const string name)
00148 {
00149   boost::cmatch what;
00150   for (unsigned int i = 0; i < regex_.size(); ++i)
00151   {
00152     if (boost::regex_match(name.c_str(), what, regex_[i]))
00153       return true;
00154   }
00155   
00156   for (unsigned int i = 0; i < expected_.size(); ++i)
00157   {
00158     if (name == expected_[i])
00159       return true;
00160   }
00161   
00162   for (unsigned int i = 0; i < name_.size(); ++i)
00163   {
00164     if (name == name_[i])
00165       return true;
00166   }
00167   
00168   for (unsigned int i = 0; i < startswith_.size(); ++i)
00169   {
00170     if (name.find(startswith_[i]) == 0)
00171       return true;
00172   }
00173   
00174   for (unsigned int i = 0; i < contains_.size(); ++i)
00175   {
00176     if (name.find(contains_[i]) != string::npos)
00177       return true;
00178   }
00179   
00180   return false;
00181 }
00182 
00183 vector<boost::shared_ptr<diagnostic_msgs::DiagnosticStatus> > GenericAnalyzer::report()
00184 {
00185   vector<boost::shared_ptr<diagnostic_msgs::DiagnosticStatus> > processed = GenericAnalyzerBase::report();
00186 
00187   // Check and make sure our expected names haven't been removed ...
00188   vector<string> expected_names_missing;
00189   bool has_name = false;
00190   bool all_stale = true;
00191   
00192   for (unsigned int i = 0; i < expected_.size(); ++i)
00193   {
00194     has_name = false;
00195     for (unsigned int j = 0; j < processed.size(); ++j)
00196     {
00197       if (!processed[j]->level == 3)
00198         all_stale = false;
00199 
00200       size_t last_slash = processed[j]->name.rfind("/");
00201       string nice_name = processed[j]->name.substr(last_slash + 1);
00202       if (nice_name == expected_[i] || nice_name == getOutputName(expected_[i]))
00203       {
00204         has_name = true;
00205         break;
00206       }
00207 
00208       // Remove chaff, check names
00209       for (unsigned int k = 0; k < chaff_.size(); ++k)
00210       {     
00211         if (nice_name == removeLeadingNameChaff(expected_[i], chaff_[k]))
00212         {
00213           has_name = true;
00214           break;
00215         }
00216       }
00217 
00218     }
00219     if (!has_name)
00220       expected_names_missing.push_back(expected_[i]);
00221   }  
00222   
00223   // Add missing names to header ...
00224   for (unsigned int i = 0; i < expected_names_missing.size(); ++i)
00225   {
00226     boost::shared_ptr<StatusItem> item(new StatusItem(expected_names_missing[i]));
00227     processed.push_back(item->toStatusMsg(path_, true));
00228   } 
00229 
00230   // Check that all processed items aren't stale
00231   for (unsigned int j = 0; j < processed.size(); ++j)
00232   {
00233     if (processed[j]->level != 3)
00234       all_stale = false;
00235   }
00236 
00237   for (unsigned int j = 0; j < processed.size(); ++j)
00238   {
00239     // Remove all leading name chaff
00240     for (unsigned int i = 0; i < chaff_.size(); ++i)
00241       processed[j]->name = removeLeadingNameChaff(processed[j]->name, chaff_[i]);
00242 
00243     // If we're missing any items, set the header status to error or stale
00244     if (expected_names_missing.size() > 0 && processed[j]->name == path_)
00245     {
00246       if (!all_stale)
00247       {
00248         processed[j]->level = 2;
00249         processed[j]->message = "Error";
00250       }
00251       else
00252       {
00253         processed[j]->level = 3;
00254         processed[j]->message = "All Stale";
00255       }
00256 
00257       // Add all missing items to header item
00258       for (unsigned int k = 0; k < expected_names_missing.size(); ++k)
00259       {
00260         diagnostic_msgs::KeyValue kv;
00261         kv.key = expected_names_missing[k];
00262         kv.value = "Missing";
00263         processed[j]->values.push_back(kv);
00264       }
00265     }
00266   }
00267   
00268   return processed;
00269 }


diagnostic_aggregator
Author(s): Kevin Watts, Brice Rebsamen
autogenerated on Fri Aug 28 2015 10:30:07