23 #include "goby/common/protobuf/option_extensions.pb.h"    25 #include "configuration_reader.h"    27 #include "exception.h"    28 #include "goby/common/logger/flex_ostream.h"    29 #include "goby/common/logger/term_color.h"    30 #include "goby/version.h"    32 #include <boost/algorithm/string.hpp>    33 #include <boost/filesystem.hpp>    34 #include <google/protobuf/dynamic_message.h>    39 void goby::common::ConfigReader::read_cfg(
int argc, 
char* argv[],
    41                                           std::string* application_name,
    42                                           boost::program_options::options_description* od_all,
    43                                           boost::program_options::variables_map* var_map)
    48     boost::filesystem::path launch_path(argv[0]);
    50 #if BOOST_FILESYSTEM_VERSION == 3    51     *application_name = launch_path.filename().string();
    53     *application_name = launch_path.filename();
    58     boost::program_options::options_description od_cli_only(
"Given on command line only");
    60     std::string cfg_path_desc = 
"path to " + *application_name + 
" configuration file (typically " +
    61                                 *application_name + 
".cfg)";
    63     std::string app_name_desc =
    64         "name to use while communicating in goby (default: " + std::string(argv[0]) + 
")";
    65     od_cli_only.add_options()(
"cfg_path,c", boost::program_options::value<std::string>(&cfg_path),
    66                               cfg_path_desc.c_str())(
"help,h", 
"writes this help message")(
    67         "app_name,a", boost::program_options::value<std::string>(),
    68         app_name_desc.c_str())(
"example_config,e", 
"writes an example .pb.cfg file")(
    69         "verbose,v", boost::program_options::value<std::string>()->implicit_value(
"")->multitoken(),
    70         "output useful information to std::cout. -v is verbosity: verbose, -vv is verbosity: "    71         "debug1, -vvv is verbosity: debug2, -vvvv is verbosity: debug3")(
    72         "ncurses,n", 
"output useful information to an NCurses GUI instead of stdout. If set, this "    73                      "parameter overrides --verbose settings.")
    75         (
"version,V", 
"writes the current version");
    77     std::string od_both_desc = 
"Typically given in " + *application_name +
    78                                " configuration file, but may be specified on the command line";
    79     boost::program_options::options_description od_both(od_both_desc.c_str());
    83         get_protobuf_program_options(od_both, message->GetDescriptor());
    86     od_all->add(od_cli_only);
    88     boost::program_options::positional_options_description p;
    94         boost::program_options::store(boost::program_options::command_line_parser(argc, argv)
   100     catch (std::exception& e)
   102         throw(ConfigException(e.what()));
   105     if (var_map->count(
"help"))
   107         ConfigException e(
"");
   109         std::cerr << *od_all << 
"\n";
   112     else if (var_map->count(
"example_config"))
   114         ConfigException e(
"");
   116         get_example_cfg_file(message, &std::cout);
   119     else if (var_map->count(
"version"))
   121         ConfigException e(
"");
   123         std::cout << goby::version_message() << std::endl;
   127     if (var_map->count(
"app_name"))
   129         *application_name = (*var_map)[
"app_name"].as<std::string>();
   132     boost::program_options::notify(*var_map);
   136         if (!cfg_path.empty())
   140             fin.open(cfg_path.c_str(), std::ifstream::in);
   142                 throw(ConfigException(std::string(
"could not open '" + cfg_path +
   143                                                   "' for reading. check value of --cfg_path")));
   146             std::string protobuf_text((std::istreambuf_iterator<char>(fin)),
   147                                       std::istreambuf_iterator<char>());
   149             google::protobuf::TextFormat::Parser parser;
   154             parser.RecordErrorsTo(&error_collector);
   155             parser.AllowPartialMessage(
true);
   156             parser.ParseFromString(protobuf_text, message);
   160             if (error_collector.has_errors())
   162                 glog.is(goby::common::logger::DIE) &&
   163                     glog << 
"fatal configuration errors (see above)" << std::endl;
   168         typedef std::pair<std::string, boost::program_options::variable_value> P;
   169         BOOST_FOREACH (
const P& p, *var_map)
   172             if (!p.second.defaulted())
   173                 set_protobuf_program_option(*var_map, *message, p.first, p.second);
   177         if (!message->IsInitialized())
   179             std::vector<std::string> errors;
   180             message->FindInitializationErrors(&errors);
   182             std::stringstream err_msg;
   183             err_msg << 
"Configuration is missing required parameters: \n";
   184             BOOST_FOREACH (
const std::string& s, errors)
   185                 err_msg << common::esc_red << s << "\n" << common::esc_nocolor;
   187             err_msg << "Make sure you specified a proper `cfg_path` to the configuration file.";
   188             throw(ConfigException(err_msg.str()));
   193 void goby::common::ConfigReader::set_protobuf_program_option(
   194     const 
boost::program_options::variables_map& var_map, 
google::protobuf::
Message& message,
   195     const 
std::
string& full_name, const 
boost::program_options::variable_value& value)
   197     const google::protobuf::Descriptor* desc = message.GetDescriptor();
   198     const google::protobuf::Reflection* refl = message.GetReflection();
   200     const google::protobuf::FieldDescriptor* field_desc = desc->FindFieldByName(full_name);
   204     if (field_desc->is_repeated())
   206         switch (field_desc->cpp_type())
   208             case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
   209                 BOOST_FOREACH (std::string v, value.as<std::vector<std::string> >())
   211                     google::protobuf::TextFormat::Parser parser;
   212                     parser.AllowPartialMessage(
true);
   213                     parser.MergeFromString(v, refl->AddMessage(&message, field_desc));
   218             case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
   219                 BOOST_FOREACH (google::protobuf::int32 v,
   220                                value.as<std::vector<google::protobuf::int32> >())
   221                     refl->AddInt32(&message, field_desc, v);
   224             case 
google::protobuf::FieldDescriptor::CPPTYPE_INT64:
   227                     refl->AddInt64(&message, field_desc, v);
   230             case 
google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
   233                     refl->AddUInt32(&message, field_desc, v);
   236             case 
google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
   239                     refl->AddUInt64(&message, field_desc, v);
   242             case 
google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
   243                 BOOST_FOREACH (
bool v, value.as<
std::vector<
bool> >())
   244                     refl->AddBool(&message, field_desc, v);
   247             case 
google::protobuf::FieldDescriptor::CPPTYPE_STRING:
   248                 BOOST_FOREACH (const 
std::
string& v, value.as<
std::vector<
std::
string> >())
   249                     refl->AddString(&message, field_desc, v);
   252             case 
google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
   253                 BOOST_FOREACH (
float v, value.as<
std::vector<
float> >())
   254                     refl->AddFloat(&message, field_desc, v);
   257             case 
google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
   258                 BOOST_FOREACH (
double v, value.as<
std::vector<
double> >())
   259                     refl->AddDouble(&message, field_desc, v);
   262             case 
google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
   263                 BOOST_FOREACH (
std::
string v, value.as<
std::vector<
std::
string> >())
   265                     const google::protobuf::EnumValueDescriptor* enum_desc =
   266                         field_desc->enum_type()->FindValueByName(v);
   268                         throw(ConfigException(
   269                             std::string(
"invalid enumeration " + v + 
" for field " + full_name)));
   271                     refl->AddEnum(&message, field_desc, enum_desc);
   279         switch (field_desc->cpp_type())
   281             case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
   283                 google::protobuf::TextFormat::Parser parser;
   284                 parser.AllowPartialMessage(
true);
   285                 parser.MergeFromString(value.as<std::string>(),
   286                                        refl->MutableMessage(&message, field_desc));
   290             case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
   291                 refl->SetInt32(&message, field_desc, value.as<boost::int_least32_t>());
   294             case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
   295                 refl->SetInt64(&message, field_desc, value.as<boost::int_least64_t>());
   298             case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
   299                 refl->SetUInt32(&message, field_desc, value.as<boost::uint_least32_t>());
   302             case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
   303                 refl->SetUInt64(&message, field_desc, value.as<boost::uint_least64_t>());
   306             case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
   307                 refl->SetBool(&message, field_desc, value.as<
bool>());
   310             case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
   311                 refl->SetString(&message, field_desc, value.as<std::string>());
   314             case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
   315                 refl->SetFloat(&message, field_desc, value.as<
float>());
   318             case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
   319                 refl->SetDouble(&message, field_desc, value.as<
double>());
   322             case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
   323                 const google::protobuf::EnumValueDescriptor* enum_desc =
   324                     field_desc->enum_type()->FindValueByName(value.as<std::string>());
   326                     throw(ConfigException(std::string(
"invalid enumeration " +
   327                                                       value.as<std::string>() + 
" for field " +
   330                 refl->SetEnum(&message, field_desc, enum_desc);
   337                                                       std::ostream* stream,
   338                                                       const std::string& indent )
   340     build_description(message->GetDescriptor(), *stream, indent, 
false);
   341     *stream << std::endl;
   344 void goby::common::ConfigReader::get_protobuf_program_options(
   345     boost::program_options::options_description& po_desc, 
const google::protobuf::Descriptor* desc)
   347     for (
int i = 0, n = desc->field_count(); i < n; ++i)
   349         const google::protobuf::FieldDescriptor* field_desc = desc->field(i);
   350         const std::string& field_name = field_desc->name();
   352         std::string cli_name = field_name;
   353         std::stringstream human_desc_ss;
   354         human_desc_ss << common::esc_lt_blue
   355                       << field_desc->options().GetExtension(goby::field).description();
   356         human_desc_ss << label(field_desc);
   357         human_desc_ss << common::esc_nocolor;
   359         switch (field_desc->cpp_type())
   361             case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
   363                 build_description(field_desc->message_type(), human_desc_ss, 
"  ");
   365                 set_single_option(po_desc, field_desc, std::string(), cli_name,
   366                                   human_desc_ss.str());
   370             case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
   372                 set_single_option(po_desc, field_desc, field_desc->default_value_int32(), cli_name,
   373                                   human_desc_ss.str());
   377             case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
   379                 set_single_option(po_desc, field_desc, field_desc->default_value_int64(), cli_name,
   380                                   human_desc_ss.str());
   384             case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
   386                 set_single_option(po_desc, field_desc, field_desc->default_value_uint32(), cli_name,
   387                                   human_desc_ss.str());
   391             case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
   393                 set_single_option(po_desc, field_desc, field_desc->default_value_uint64(), cli_name,
   394                                   human_desc_ss.str());
   398             case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
   400                 set_single_option(po_desc, field_desc, field_desc->default_value_bool(), cli_name,
   401                                   human_desc_ss.str());
   405             case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
   407                 set_single_option(po_desc, field_desc, field_desc->default_value_string(), cli_name,
   408                                   human_desc_ss.str());
   412             case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
   414                 set_single_option(po_desc, field_desc, field_desc->default_value_float(), cli_name,
   415                                   human_desc_ss.str());
   419             case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
   421                 set_single_option(po_desc, field_desc, field_desc->default_value_double(), cli_name,
   422                                   human_desc_ss.str());
   426             case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
   428                 set_single_option(po_desc, field_desc, field_desc->default_value_enum()->name(),
   429                                   cli_name, human_desc_ss.str());
   436 void goby::common::ConfigReader::build_description(
const google::protobuf::Descriptor* desc,
   437                                                    std::ostream& stream,
   438                                                    const std::string& indent ,
   441     for (
int i = 0, n = desc->field_count(); i < n; ++i)
   443         const google::protobuf::FieldDescriptor* field_desc = desc->field(i);
   445         if (field_desc->options().GetExtension(goby::field).cfg().action() ==
   446             goby::GobyFieldOptions::ConfigurationOptions::NEVER)
   449         build_description_field(field_desc, stream, indent, use_color);
   452     std::vector<const google::protobuf::FieldDescriptor*> extensions;
   453     google::protobuf::DescriptorPool::generated_pool()->FindAllExtensions(desc, &extensions);
   454     for (
int i = 0, n = extensions.size(); i < n; ++i)
   456         const google::protobuf::FieldDescriptor* field_desc = extensions[i];
   458         if (field_desc->options().GetExtension(goby::field).cfg().action() ==
   459             goby::GobyFieldOptions::ConfigurationOptions::NEVER)
   462         build_description_field(field_desc, stream, indent, use_color);
   466 void goby::common::ConfigReader::build_description_field(
   467     const google::protobuf::FieldDescriptor* field_desc, std::ostream& stream,
   468     const std::string& indent, 
bool use_color)
   470     google::protobuf::DynamicMessageFactory factory;
   472         factory.GetPrototype(field_desc->containing_type());
   474     if (field_desc->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE)
   476         std::string before_description = indent;
   478         if (field_desc->is_extension())
   480             if (field_desc->extension_scope())
   481                 before_description +=
   482                     "[" + field_desc->extension_scope()->full_name() + 
"." + field_desc->name();
   484                 before_description += 
"[" + field_desc->full_name();
   488             before_description += field_desc->name();
   491         if (field_desc->is_extension())
   492             before_description += 
"]";
   494         before_description += 
" {  ";
   495         stream << 
"\n" << before_description;
   497         std::string description;
   499             description += common::esc_green;
   504             field_desc->options().GetExtension(goby::field).description() + label(field_desc);
   507             description += common::esc_nocolor;
   510             wrap_description(&description, before_description.size());
   512         stream << description;
   514         build_description(field_desc->message_type(), stream, indent + 
"  ", use_color);
   515         stream << 
"\n" << indent << 
"}";
   521         std::string before_description = indent;
   524         if (field_desc->has_default_value())
   525             google::protobuf::TextFormat::PrintFieldValueToString(*default_msg, field_desc, -1,
   529             example = field_desc->options().GetExtension(goby::field).example();
   530             if (field_desc->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_STRING)
   531                 example = 
"\"" + example + 
"\"";
   534         if (field_desc->is_extension())
   536             if (field_desc->extension_scope())
   537                 before_description +=
   538                     "[" + field_desc->extension_scope()->full_name() + 
"." + field_desc->name();
   540                 before_description += 
"[" + field_desc->full_name();
   544             before_description += field_desc->name();
   547         if (field_desc->is_extension())
   548             before_description += 
"]";
   550         before_description += 
": ";
   551         before_description += example;
   552         before_description += 
"  ";
   554         stream << before_description;
   556         std::string description;
   559             description += common::esc_green;
   563         description += field_desc->options().GetExtension(goby::field).description();
   564         if (field_desc->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_ENUM)
   567             for (
int i = 0, n = field_desc->enum_type()->value_count(); i < n; ++i)
   571                 description += field_desc->enum_type()->value(i)->name();
   577         description += label(field_desc);
   579         if (field_desc->has_default_value())
   580             description += 
" (default=" + example + 
")";
   582         if (field_desc->options().GetExtension(goby::field).has_moos_global())
   583             description += 
" (can also set MOOS global \"" +
   584                            field_desc->options().GetExtension(goby::field).moos_global() + 
"=\")";
   587             wrap_description(&description, before_description.size());
   589         stream << description;
   592             stream << common::esc_nocolor;
   596 std::string goby::common::ConfigReader::label(
const google::protobuf::FieldDescriptor* field_desc)
   598     switch (field_desc->label())
   600         case google::protobuf::FieldDescriptor::LABEL_REQUIRED: 
return " (required)";
   602         case google::protobuf::FieldDescriptor::LABEL_OPTIONAL: 
return " (optional)";
   604         case google::protobuf::FieldDescriptor::LABEL_REPEATED: 
return " (repeated)";
   610 std::string goby::common::ConfigReader::word_wrap(std::string s, 
unsigned width,
   611                                                   const std::string& delim)
   615     while (s.length() > width)
   617         std::string::size_type pos_newline = s.find(
"\n");
   618         std::string::size_type pos_delim = s.substr(0, width).find_last_of(delim);
   619         if (pos_newline < width)
   621             out += s.substr(0, pos_newline);
   622             s = s.substr(pos_newline + 1);
   624         else if (pos_delim != std::string::npos)
   626             out += s.substr(0, pos_delim + 1);
   627             s = s.substr(pos_delim + 1);
   631             out += s.substr(0, width);
   643 void goby::common::ConfigReader::wrap_description(std::string* description, 
int num_blanks)
   646         word_wrap(*description, std::max(MAX_CHAR_PER_LINE - num_blanks, (
int)MIN_CHAR), 
" ");
   648     if (MIN_CHAR > MAX_CHAR_PER_LINE - num_blanks)
   650         *description = 
"\n" + description->substr(2);
   651         num_blanks = MAX_CHAR_PER_LINE - MIN_CHAR;
   654     std::string spaces(num_blanks, 
' ');
   656     boost::replace_all(*description, 
"\n", 
"\n" + spaces);
 void set_name(const std::string &s)
Set the name of the application that the logger is serving. 
Contains functions for adding color to Terminal window streams. 
google::protobuf::uint32 uint32
an unsigned 32 bit integer 
int run(int argc, char *argv[], Config *cfg)
Run a Goby application derived from MinimalApplicationBase. blocks caller until MinimalApplicationBas...
google::protobuf::int64 int64
a signed 64 bit integer 
common::FlexOstream glog
Access the Goby logger through this object. 
The global namespace for the Goby project. 
google::protobuf::uint64 uint64
an unsigned 64 bit integer 
void add_stream(logger::Verbosity verbosity=logger::VERBOSE, std::ostream *os=0)
Attach a stream object (e.g. std::cout, std::ofstream, ...) to the logger with desired verbosity...