00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "Config.h"
00011 #include <iostream>
00012 #include "../../Architecture/TinyXML/tinyxml.h"
00013 #include "ros/ros.h"
00014
00015 #include <fstream>
00016
00017 #define ROOTNODE_NAME "MergedConfig"
00018
00019 Config* Config::m_inst=0;
00020
00021 Mutex Config::m_AccessMutex = Mutex ();
00022
00023 using namespace std;
00024
00025
00026 Config::Config()
00027 {
00028 m_doc=0;
00029 }
00030
00031 Config::~Config()
00032 {
00033 delete m_doc;
00034 }
00035
00036 Config::Config(const Config& c2)
00037 :
00038 m_fileName(c2.m_fileName),
00039 m_activeProfile(c2.m_activeProfile),
00040 m_moduleList(c2.m_moduleList),
00041 m_values(c2.m_values)
00042 {
00043 if (c2.m_doc)
00044 m_doc=new TiXmlDocument(*c2.m_doc);
00045 else
00046 m_doc=0;
00047 }
00048
00049 Config& Config::operator=(const Config& c2)
00050 {
00051 m_fileName=c2.m_fileName;
00052 m_moduleList=c2.m_moduleList;
00053 m_activeProfile=c2.m_activeProfile;
00054 m_values=c2.m_values;
00055 if (c2.m_doc)
00056 m_doc=new TiXmlDocument(*c2.m_doc);
00057 else
00058 m_doc=0;
00059 return *this;
00060 }
00061
00062 Config* Config::getInstance()
00063 {
00064 m_AccessMutex.lock ();
00065 if ( !m_inst )
00066 {
00067 ROS_WARN_STREAM( "No instance was set! Trying to load 'default' profile from file 'Config.xml'.." );
00068 Config* newConf=new Config();
00069 vector<string> profiles;
00070 profiles.push_back("default");
00071 newConf->loadFromFile("Config.xml",profiles);
00072 setInstance( newConf );
00073 }
00074 m_AccessMutex.unlock ();
00075 return m_inst;
00076 }
00077
00078
00079 void Config::loadConfig(const std::vector<std::string> files, const std::vector<std::string> profiles, const std::string& prepend_path)
00080 {
00081 ofstream outStream;
00082 std::string mergedFile = prepend_path + "/config/merged.xml";
00083 outStream.open(mergedFile.c_str());
00084 outStream << "<?xml version=\"1.0\" standalone=\"no\" ?>\n\n";
00085 outStream << "<MergedConfig>\n\n";
00086
00087 vector< string > fileNames;
00088 fileNames.push_back(prepend_path + "/config/default.xml");
00089
00090
00091 std::vector<std::string>::iterator it;
00092 it = fileNames.end();
00093 fileNames.insert (it, files.begin(), files.end());
00094
00095 for ( unsigned i=0; i<fileNames.size(); i++ )
00096 {
00097 ROS_INFO_STREAM( "Loading " + fileNames[i] );
00098 string line;
00099 ifstream configFile (fileNames[i].c_str());
00100 if (configFile.is_open()) {
00101 while ( configFile.good() )
00102 {
00103 getline (configFile,line);
00104 outStream << line << endl;
00105 }
00106 configFile.close();
00107 }
00108 }
00109
00110 outStream << "\n\n</MergedConfig>";
00111 outStream.close();
00112
00113 vector<std::string> profilesToLoad;
00114 profilesToLoad.push_back("default");
00115
00116
00117 it = profilesToLoad.end();
00118 profilesToLoad.insert (it, profiles.begin(), profiles.end());
00119
00120 Config* newConf=new Config();
00121 if (!newConf->loadFromFile( prepend_path + "/config/merged.xml", profilesToLoad ))
00122 {
00123 ROS_ERROR_STREAM("Could not load config file!");
00124 exit ( -126 );
00125 }
00126 Config::setInstance(newConf);
00127 }
00128
00129
00130 int Config::getInt ( const std::string& id )
00131 {
00132 return getInstance()->get<int>( id );
00133 }
00134
00135 bool Config::getBool ( const std::string& id )
00136 {
00137 return getInstance()->get<int>( id );
00138 }
00139
00140 std::string Config::getString ( const std::string& id )
00141 {
00142 return getInstance()->get<const char*>( id );
00143 }
00144
00145 float Config::getFloat ( const std::string& id )
00146 {
00147 return getInstance()->get<float>( id );
00148 }
00149
00150 bool Config::getFloatMinMax ( const std::string& id, std::pair<float,float>& minmax )
00151 {
00152 return getInstance()->getMinMax<float>( id, minmax );
00153 }
00154
00155 bool Config::getIntMinMax ( const std::string& id, std::pair<int,int>& minmax )
00156 {
00157 return getInstance()->getMinMax<int>( id, minmax );
00158 }
00159
00160
00161 void Config::clear()
00162 {
00163 m_AccessMutex.lock ();
00164 m_values.clear();
00165 m_AccessMutex.unlock ();
00166 }
00167
00168 bool Config::parseValue ( TiXmlElement* el,Config::ConfigEntry& newEntry, std::string& name ) const
00169 {
00170 if ( !el )
00171 return false;
00172
00173 bool error=false;
00174
00175 const char* pName=el->Attribute ( "name" );
00176 if ( !pName )
00177 return false;
00178
00179 name=pName;
00180
00181 if ( name.size() <1 )
00182 return false;
00183
00184
00185 if ( name.c_str() [0]=='i' || name.c_str() [0]=='b' )
00186 {
00187 int tmp=0;
00188 error&= ( el->QueryIntAttribute ( "value",&tmp ) !=TIXML_SUCCESS );
00189 newEntry.setInt ( tmp );
00190
00191 tmp=0;
00192 error&= ( el->QueryIntAttribute ( "min", &tmp ) !=TIXML_SUCCESS );
00193 newEntry.setIntMin ( tmp );
00194
00195 tmp=0;
00196 error&= ( el->QueryIntAttribute ( "max", &tmp ) !=TIXML_SUCCESS );
00197 newEntry.setIntMax ( tmp );
00198 }
00199 else if ( name.c_str() [0]=='f' )
00200 {
00201 float tmp=0;
00202 error&= ( el->QueryFloatAttribute ( "value", &tmp ) !=TIXML_SUCCESS );
00203 newEntry.setFloat ( tmp );
00204
00205 tmp=0;
00206 error&= ( el->QueryFloatAttribute ( "min", &tmp ) !=TIXML_SUCCESS );
00207 newEntry.setFloatMin ( tmp );
00208
00209 tmp=0;
00210 error&= ( el->QueryFloatAttribute ( "max", &tmp ) !=TIXML_SUCCESS );
00211 newEntry.setFloatMax ( tmp );
00212 }
00213 else if ( name.c_str() [0]=='s' )
00214 {
00215 const char* val=el->Attribute ( "value" );
00216 if ( val )
00217 {
00218 newEntry.setString ( val );
00219 }
00220 else
00221 error=true;
00222 }
00223 else
00224 error=true;
00225
00226 if ( error )
00227 return false;
00228
00229
00230
00231 return true;
00232 }
00233
00234 bool Config::parseValues ( const std::string& level, TiXmlNode* root )
00235 {
00236 if ( !root )
00237 return false;
00238
00239 for ( TiXmlNode* value=root->FirstChildElement();value!=NULL;value=value->NextSibling() )
00240 {
00241 TiXmlElement* el=value->ToElement();
00242 if (!el) continue;
00243 if ( !strcasecmp ( el->Value(),"Modules" ) ) continue;
00244 if ( !strcasecmp ( el->Value(),"value" ) )
00245 {
00246 ConfigEntry dest;
00247 std::string name;
00248 if (parseValue ( el,dest ,name))
00249 {
00250 m_values[level+"."+name]=dest;
00251 }
00252 }
00253 else
00254 {
00255 if ( !level.empty() )
00256 parseValues ( level+"."+el->Value(),value );
00257 else
00258 parseValues ( el->Value(),value );
00259 }
00260
00261 }
00262
00263 return true;
00264 }
00265
00266 void Config::addModule( std::string name )
00267 {
00268 bool found=false;
00269 for (unsigned i=0; i<m_moduleList.size(); i++ )
00270 {
00271 if (m_moduleList[i] == name)
00272 {
00273 found=true;
00274 break;
00275 }
00276 }
00277 if ( !found )
00278 {
00279 ROS_DEBUG_STREAM( "Adding module "+name );
00280 m_moduleList.push_back( name );
00281 } else {
00282 ROS_WARN_STREAM( "Duplicate module entry '"+name+"' was ignored." );
00283 }
00284 }
00285
00286 void Config::parseModules(TiXmlNode* root)
00287 {
00288 if (!root)
00289 return;
00290
00291 for ( TiXmlNode* value=root->FirstChildElement();value!=NULL;value=value->NextSibling() )
00292 {
00293 if (!value->ToElement()) continue;
00294 if (!strcasecmp(value->ToElement()->Value(),"module"))
00295 {
00296 if (value->ToElement()->Attribute("name"))
00297 {
00298 std::string name=value->ToElement()->Attribute("name");
00299 addModule( name );
00300 }
00301 }
00302 }
00303
00304 }
00305
00306 bool Config::parseProfile ( TiXmlNode* root )
00307 {
00308
00309 if ( root->ToElement()->Attribute("parents") )
00310 {
00311 string parents = root->ToElement()->Attribute("parents");
00312
00313 std::vector<std::string> parentList = explode( parents, "," );
00314
00315 for ( unsigned i=0; i < parentList.size(); i++ )
00316 {
00317 string parentProfileName = parentList[i];
00318 TiXmlNode* docRoot=m_doc->FirstChild ( ROOTNODE_NAME );
00319 TiXmlNode* parentProfileNode=docRoot->FirstChild ( parentProfileName );
00320
00321 if ( m_loadedProfileNames.find( parentProfileName) != m_loadedProfileNames.end() )
00322 {
00323 ROS_ERROR_STREAM( "Cyclic profile dependecy detected! Main profile: "+m_activeProfile );
00324 ROS_INFO_STREAM( "Profile " << parentProfileName << " already loaded" );
00325 return true;
00326 }
00327
00328 if (!parentProfileNode) {
00329 ROS_ERROR_STREAM( "Parent profile node '" + parentProfileName + "' not found");
00330 return false;
00331 }
00332
00333 m_loadedProfileNames.insert( parentProfileName );
00334 parseProfile( parentProfileNode );
00335 }
00336
00337 }
00338
00339 ROS_INFO_STREAM( "Parsing profile "+string(root->ToElement()->Value()) );
00340
00341 TiXmlNode* modules=root->FirstChild ( "Modules" );
00342 if (modules) {
00343 parseModules(modules);
00344 }
00345
00346 if ( !parseValues( "", root) ) {
00347 ROS_ERROR_STREAM( "Could not parse values from " + root->ValueStr());
00348 return false;
00349 }
00350 return true;
00351 }
00352
00353
00354 bool Config::parseDocument(const std::string& fileName, const std::vector<std::string>& profiles )
00355 {
00356 m_fileName=fileName;
00357
00358 TiXmlNode* root=m_doc->FirstChild ( ROOTNODE_NAME );
00359 if ( !root ) {
00360 ROS_ERROR_STREAM( "Failed to load " + fileName + ": Root node not found: " << ROOTNODE_NAME);
00361 return false;
00362 }
00363
00364
00365 TiXmlNode* child = 0;
00366 ostringstream stream;
00367 stream.setf ( ios::left, ios::adjustfield );
00368 stream.width(35);
00369 stream << "Profile";
00370 stream << "Parents";
00371 stream << endl << "---------------------------------------------------------";
00372 while( (child = root->IterateChildren( child )) )
00373 {
00374 if ( child->Type() == TiXmlNode::ELEMENT )
00375 {
00376 string parentName=" ";
00377 if ( child->ToElement()->Attribute("parents") )
00378 {
00379 parentName=child->ToElement()->Attribute("parents");
00380 }
00381 stream << endl;
00382 stream.width(35);
00383 stream << child->ValueStr();
00384 stream << parentName;
00385 }
00386 }
00387 m_allProfileNames=stream.str();
00388
00389 stream.str("");
00390
00391 for ( unsigned p=0; p<profiles.size(); p++ )
00392 {
00393 string profileName = profiles[p];
00394
00395 if ( p != 0 )
00396 {
00397 stream << "_";
00398 }
00399 stream << profileName;
00400
00401 TiXmlNode* profileNode=root->FirstChild ( profileName );
00402 if (!profileNode) {
00403 ROS_ERROR_STREAM( "Failed to load " + fileName + ": Profile node '" + profileName + "' not found\n\n"+m_allProfileNames);
00404 return false;
00405 }
00406
00407 m_loadedProfileNames.insert( profileName );
00408 if (!parseProfile( profileNode )) {
00409 ROS_ERROR_STREAM( "Failed to load " + fileName + ": Could not parse profile '"+profileName+"'\n\n"+m_allProfileNames);
00410 return false;
00411 }
00412 }
00413
00414 m_activeProfile = stream.str();
00415
00416 return true;
00417 }
00418
00419
00420 bool Config::loadFromFile ( const std::string& fileName, const std::vector<std::string>& profiles )
00421 {
00422 delete m_doc;
00423 m_doc=new TiXmlDocument ( fileName.c_str() );
00424 bool loadOkay = m_doc->LoadFile();
00425 if ( !loadOkay )
00426 {
00427 ROS_ERROR_STREAM( "Failed to load " + fileName + ": "+ m_doc->ErrorDesc());
00428 return false;
00429 }
00430 return parseDocument(fileName,profiles);
00431
00432 }
00433
00434 void Config::saveValue ( const std::string& name, const ConfigEntry& entry, TiXmlElement* allProfValues,TiXmlElement* values ) const
00435 {
00436 assert(values);
00437 std::string valuename;
00438
00439 std::string::size_type start=0;
00440 std::string::size_type pos;
00441 TiXmlElement* curAllProf=allProfValues;
00442 bool doInsert=false;
00443 while ( 1 )
00444 {
00445 pos = name.find ( '.' ,start);
00446 if ( pos == std::string::npos )
00447 {
00448 valuename=name.substr ( start );
00449 curAllProf=curAllProf->FirstChildElement();
00450 while (curAllProf)
00451 {
00452 const char* nameattr=curAllProf->Attribute("name");
00453 if (!nameattr) continue;
00454 if (!strcmp(curAllProf->Attribute("name"),valuename.c_str()))
00455 break;
00456 curAllProf=curAllProf->NextSiblingElement();
00457 }
00458 if (!curAllProf)
00459 {
00460 doInsert=true;
00461 }
00462 break;
00463 }
00464 else
00465 {
00466 TiXmlElement* childAll=curAllProf->FirstChildElement ( name.substr ( start,pos-start ).c_str() );
00467 if ( !childAll )
00468 {
00469 doInsert=true;
00470 break;
00471 }
00472 curAllProf=childAll;
00473 start=pos+1;
00474 }
00475 }
00476 TiXmlElement * cxn = new TiXmlElement ( "value" );
00477
00478
00479 cxn->SetAttribute ( "name", valuename.c_str() );
00480 if ( entry.getType() ==ConfigEntry::TYPE_INT )
00481 {
00482 char tmp[256];
00483 sprintf ( tmp,"%d",entry.getInt() );
00484 cxn->SetAttribute ( "value",tmp );
00485
00486 if ( entry.getIntMax() !=entry.getIntMin() )
00487 {
00488 sprintf ( tmp,"%d",entry.getIntMin() );
00489 cxn->SetAttribute ( "min",tmp );
00490 sprintf ( tmp,"%d",entry.getIntMax() );
00491 cxn->SetAttribute ( "max",tmp );
00492 }
00493 }
00494 else if ( entry.getType() ==ConfigEntry::TYPE_FLOAT )
00495 {
00496 char tmp[256];
00497 sprintf ( tmp,"%1.2f",entry.getFloat() );
00498 cxn->SetAttribute ( "value",tmp );
00499
00500 if ( entry.getFloatMax() !=entry.getFloatMin() )
00501 {
00502 sprintf ( tmp,"%1.2f",entry.getFloatMin() );
00503 cxn->SetAttribute ( "min",tmp );
00504 sprintf ( tmp,"%1.2f",entry.getFloatMax() );
00505 cxn->SetAttribute ( "max",tmp );
00506 }
00507 }
00508 else if ( entry.getType() ==ConfigEntry::TYPE_STRING )
00509 {
00510 cxn->SetAttribute ( "value",entry.getString() );
00511 }
00512 else
00513 assert ( 0 );
00514
00515
00516 if (!doInsert)
00517 {
00518 TiXmlElement* val=curAllProf;
00519 if (val)
00520 {
00521 ConfigEntry ent;
00522 std::string dummy;
00523 if (parseValue(val,ent,dummy))
00524 {
00525 if (ent==entry)
00526 {
00527 delete cxn;
00528 return;
00529 }
00530 }
00531
00532 }
00533 }
00534
00535 start=0;
00536 TiXmlElement* cur=values;
00537 while ( 1 )
00538 {
00539 pos = name.find ( '.' ,start);
00540 if ( pos == std::string::npos )
00541 {
00542 valuename=name.substr ( start );
00543 break;
00544 }
00545 else
00546 {
00547 TiXmlElement* child=cur->FirstChildElement ( name.substr ( start,pos-start ).c_str() );
00548 if ( !child )
00549 {
00550 child=new TiXmlElement ( name.substr ( start,pos-start ).c_str() );
00551 cur->LinkEndChild ( child );
00552 }
00553 cur=child;
00554
00555 start=pos+1;
00556
00557 }
00558 }
00559
00560
00561 cur->LinkEndChild ( cxn );
00562
00563 }
00564
00565 std::vector<std::string> Config::getAllVars() const
00566 {
00567 std::vector<std::string> ret;
00568 for ( std::map<std::string,ConfigEntry>::const_iterator it=m_values.begin();it!=m_values.end();++it )
00569 {
00570 ret.push_back(it->first);
00571 }
00572 return ret;
00573 }
00574
00575 const std::vector<std::string>& Config::getModuleList() const
00576 {
00577 return m_moduleList;
00578 }
00579
00580
00581
00582 std::vector<std::string> Config::explode (const std::string& text, const std::string& separators, bool keepSeparators)
00583 {
00584 size_t n = text.length ();
00585 size_t start = text.find_first_not_of (separators);
00586 std::vector<std::string> words;
00587
00588 while (start < n)
00589 {
00590 size_t stop = text.find_first_of (separators, start);
00591 if (stop > n) stop = n;
00592 if ( keepSeparators )
00593 {
00594 words.push_back (text.substr (start, stop-start+1));
00595 }
00596 else
00597 {
00598 words.push_back (text.substr (start, stop-start));
00599 }
00600 start = text.find_first_not_of (separators, stop+1);
00601 };
00602 return words;
00603 }
00604