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...