23 #ifndef GOBYMOOSAPP20100726H 24 #define GOBYMOOSAPP20100726H 26 #include "goby/moos/moos_header.h" 27 #include "goby/moos/moos_translator.h" 28 #include "goby/util/as.h" 30 #include <boost/algorithm/string.hpp> 31 #include <boost/bind.hpp> 32 #include <boost/date_time/posix_time/posix_time.hpp> 33 #include <boost/filesystem.hpp> 34 #include <boost/function.hpp> 35 #include <boost/signals2.hpp> 38 #include "dynamic_moos_vars.h" 39 #include "goby/common/configuration_reader.h" 40 #include "goby/common/exception.h" 41 #include "goby/common/logger.h" 42 #include "goby/moos/protobuf/goby_moos_app.pb.h" 43 #include "goby/version.h" 50 template <
typename App>
int run(
int argc,
char* argv[]);
52 template <
typename ProtobufMessage>
53 inline void protobuf_inbox(
const CMOOSMsg& msg,
54 boost::function<
void(
const ProtobufMessage& msg)> handler)
56 ProtobufMessage pb_msg;
68 bool Iterate() {
return true; }
69 bool OnStartUp() {
return true; }
70 bool OnConnectToServer() {
return true; }
71 bool OnNewMail(MOOSMSG_LIST& NewMail) {
return true; }
72 void RegisterVariables() {}
85 typedef boost::function<void(const CMOOSMsg& msg)> InboxFunc;
87 template <
typename ProtobufConfig>
89 : start_time_(MOOSTime()), configuration_read_(
false), cout_cleared_(
false),
90 connected_(
false), started_up_(
false), ignore_stale_(
true),
91 dynamic_moos_vars_enabled_(
true)
95 read_configuration(cfg);
98 common_cfg_ = cfg->common();
99 configuration_read_ =
true;
101 process_configuration();
103 glog.is(goby::common::logger::DEBUG2) &&
glog << cfg->DebugString() << std::endl;
108 template <
typename ProtobufMessage>
109 void publish_pb(
const std::string& key,
const ProtobufMessage& msg)
111 std::string serialized;
112 bool is_binary = serialize_for_moos(&serialized, msg);
113 CMOOSMsg moos_msg = goby::moos::MOOSTranslator::make_moos_msg(
114 key, serialized, is_binary, goby::moos::moos_technique,
115 msg.GetDescriptor()->full_name());
121 if (connected_ && started_up_)
122 MOOSAppType::m_Comms.Post(msg);
124 msg_buffer_.push_back(msg);
127 void publish(
const std::string& key,
const std::string& value)
129 CMOOSMsg msg(MOOS_NOTIFY, key, value);
133 void publish(
const std::string& key,
double value)
135 CMOOSMsg msg(MOOS_NOTIFY, key, value);
140 double start_time()
const {
return start_time_; }
142 void subscribe(
const std::string& var, InboxFunc handler = InboxFunc(),
int blackout = 0);
144 template <
typename V,
typename A1>
145 void subscribe(
const std::string& var,
void (V::*mem_func)(A1), V* obj,
int blackout = 0)
147 subscribe(var, boost::bind(mem_func, obj, _1), blackout);
151 void subscribe(
const std::string& var_pattern,
const std::string& app_pattern,
152 InboxFunc handler = InboxFunc(),
int blackout = 0);
154 template <
typename V,
typename A1>
155 void subscribe(
const std::string& var_pattern,
const std::string& app_pattern,
156 void (V::*mem_func)(A1), V* obj,
int blackout = 0)
158 subscribe(var_pattern, app_pattern, boost::bind(mem_func, obj, _1), blackout);
161 template <
typename V,
typename ProtobufMessage>
162 void subscribe_pb(
const std::string& var,
void (V::*mem_func)(
const ProtobufMessage&), V* obj,
165 subscribe_pb<ProtobufMessage>(var, boost::bind(mem_func, obj, _1), blackout);
168 template <
typename ProtobufMessage>
169 void subscribe_pb(
const std::string& var,
170 boost::function<
void(
const ProtobufMessage& msg)> handler,
int blackout = 0)
172 subscribe(var, boost::bind(&goby::moos::protobuf_inbox<ProtobufMessage>, _1, handler),
176 void register_timer(
int period_seconds, boost::function<
void()> handler)
179 now *= period_seconds;
181 SynchronousLoop new_loop;
182 new_loop.unix_next = now + period_seconds;
183 new_loop.period_seconds = period_seconds;
184 new_loop.handler = handler;
185 synchronous_loops_.push_back(new_loop);
188 template <
typename V>
void register_timer(
int period_seconds,
void (V::*mem_func)(), V* obj)
190 register_timer(period_seconds, boost::bind(mem_func, obj));
193 template <
typename App>
friend int ::goby::moos::run(
int argc,
char* argv[]);
195 virtual void loop() = 0;
197 bool ignore_stale() {
return ignore_stale_; }
198 void set_ignore_stale(
bool b) { ignore_stale_ = b; }
200 bool dynamic_moos_vars_enabled() {
return dynamic_moos_vars_enabled_; }
201 void set_dynamic_moos_vars_enabled(
bool b) { dynamic_moos_vars_enabled_ = b; }
203 std::pair<std::string, goby::moos::protobuf::TranslatorEntry::ParserSerializerTechnique>
204 parse_type_technique(
const std::string& type_and_technique)
206 std::string protobuf_type;
207 goby::moos::protobuf::TranslatorEntry::ParserSerializerTechnique technique;
208 if (!type_and_technique.empty())
210 std::string::size_type colon_pos = type_and_technique.find(
':');
212 if (colon_pos != std::string::npos)
214 protobuf_type = type_and_technique.substr(0, colon_pos);
215 std::string str_technique = type_and_technique.substr(colon_pos + 1);
217 if (!goby::moos::protobuf::TranslatorEntry::ParserSerializerTechnique_Parse(
218 str_technique, &technique))
219 throw(std::runtime_error(
"Invalid technique string"));
223 throw std::runtime_error(
"Missing colon (:)");
225 return std::make_pair(protobuf_type, technique);
229 throw std::runtime_error(
"Empty technique string");
237 bool OnConnectToServer();
238 bool OnDisconnectFromServer();
239 bool OnNewMail(MOOSMSG_LIST& NewMail);
240 void try_subscribing();
241 void do_subscriptions();
246 void process_configuration();
253 bool configuration_read_;
261 std::map<std::string, boost::shared_ptr<boost::signals2::signal<void(const CMOOSMsg& msg)> > >
264 std::map<std::pair<std::string, std::string>,
265 boost::shared_ptr<boost::signals2::signal<void(const CMOOSMsg& msg)> > >
266 wildcard_mail_handlers_;
273 std::deque<CMOOSMsg> msg_buffer_;
276 std::deque<std::pair<std::string, int> > pending_subscriptions_;
277 std::deque<std::pair<std::string, int> > existing_subscriptions_;
280 std::deque<std::pair<std::pair<std::string, std::string>,
int> >
281 wildcard_pending_subscriptions_;
282 std::deque<std::pair<std::pair<std::string, std::string>,
int> >
283 wildcard_existing_subscriptions_;
285 struct SynchronousLoop
289 boost::function<void()> handler;
292 std::vector<SynchronousLoop> synchronous_loops_;
298 bool dynamic_moos_vars_enabled_;
302 static std::string mission_file_;
303 static std::string application_name_;
309 template <
typename ProtobufConfig>
324 MOOSAppType::Iterate();
326 if (!configuration_read_)
334 cout_cleared_ =
true;
337 while (!msg_buffer_.empty() && (connected_ && started_up_))
339 goby::glog.is(goby::common::logger::DEBUG3) &&
340 goby::glog <<
"writing from buffer: " << msg_buffer_.front().GetKey() <<
": " 341 << msg_buffer_.front().GetAsString() << std::endl;
343 MOOSAppType::m_Comms.Post(msg_buffer_.front());
344 msg_buffer_.pop_front();
349 if (synchronous_loops_.size())
352 for (
typename std::vector<SynchronousLoop>::iterator it = synchronous_loops_.begin(),
353 end = synchronous_loops_.end();
356 SynchronousLoop& loop = *it;
357 if (loop.unix_next <= now)
360 loop.unix_next += loop.period_seconds;
363 if (loop.unix_next < now)
364 loop.unix_next = now + loop.period_seconds;
368 if (loop.unix_next > (now + 2 * loop.period_seconds))
369 loop.unix_next = now + loop.period_seconds;
379 MOOSAppType::OnNewMail(NewMail);
381 for (MOOSMSG_LIST::const_iterator it = NewMail.begin(), end = NewMail.end(); it != end; ++it)
384 goby::glog.is(goby::common::logger::DEBUG3) &&
385 goby::glog <<
"Received mail: " << msg.GetKey() <<
", time: " << std::setprecision(15)
386 << msg.GetTime() << std::endl;
390 if (dynamic_moos_vars_enabled_)
391 dynamic_vars().update_moos_vars(msg);
393 if (msg.GetTime() < start_time_ && ignore_stale_)
396 goby::glog <<
"ignoring normal mail from " << msg.GetKey()
397 <<
" from before we started (dynamics still updated)" << std::endl;
399 else if (mail_handlers_.count(msg.GetKey()))
400 (*mail_handlers_[msg.GetKey()])(msg);
403 std::pair<std::string, std::string>,
404 boost::shared_ptr<boost::signals2::signal<
void(
const CMOOSMsg&msg)> > >::iterator
405 it = wildcard_mail_handlers_.begin(),
406 end = wildcard_mail_handlers_.end();
409 if (MOOSWildCmp(it->first.first, msg.GetKey()) &&
410 MOOSWildCmp(it->first.second, msg.GetSource()))
411 (*(it->second))(msg);
420 std::cout << MOOSAppType::m_MissionReader.GetAppName() <<
", disconnected from server." 423 pending_subscriptions_.insert(pending_subscriptions_.end(), existing_subscriptions_.begin(),
424 existing_subscriptions_.end());
425 existing_subscriptions_.clear();
426 wildcard_pending_subscriptions_.insert(wildcard_pending_subscriptions_.end(),
427 wildcard_existing_subscriptions_.begin(),
428 wildcard_existing_subscriptions_.end());
429 wildcard_existing_subscriptions_.clear();
435 std::cout << MOOSAppType::m_MissionReader.GetAppName() <<
", connected to server." << std::endl;
439 for (google::protobuf::RepeatedPtrField<GobyMOOSAppConfig::Initializer>::const_iterator
440 it = common_cfg_.initializer().begin(),
441 end = common_cfg_.initializer().end();
445 if (ini.has_global_cfg_var())
448 if (MOOSAppType::m_MissionReader.GetValue(ini.global_cfg_var(), result))
450 if (ini.type() == GobyMOOSAppConfig::Initializer::INI_DOUBLE)
451 publish(ini.moos_var(), goby::util::as<double>(result));
452 else if (ini.type() == GobyMOOSAppConfig::Initializer::INI_STRING)
453 publish(ini.moos_var(), ini.trim() ? boost::trim_copy(result) : result);
458 if (ini.type() == GobyMOOSAppConfig::Initializer::INI_DOUBLE)
459 publish(ini.moos_var(), ini.dval());
460 else if (ini.type() == GobyMOOSAppConfig::Initializer::INI_STRING)
461 publish(ini.moos_var(), ini.trim() ? boost::trim_copy(ini.sval()) : ini.sval());
470 MOOSAppType::OnStartUp();
472 std::cout << MOOSAppType::m_MissionReader.GetAppName() <<
", starting ..." << std::endl;
473 CMOOSApp::SetCommsFreq(common_cfg_.comm_tick());
474 CMOOSApp::SetAppFreq(common_cfg_.app_tick());
480 template <
class MOOSAppType>
484 goby::glog.is(goby::common::logger::VERBOSE) &&
485 goby::glog <<
"subscribing for MOOS variable: " << var <<
" @ " << blackout << std::endl;
487 pending_subscriptions_.push_back(std::make_pair(var, blackout));
490 if (!mail_handlers_[var])
491 mail_handlers_[var].reset(
new boost::signals2::signal<
void(
const CMOOSMsg& msg)>);
494 mail_handlers_[var]->connect(handler);
497 template <
class MOOSAppType>
499 const std::string& app_pattern, InboxFunc handler,
502 goby::glog.is(goby::common::logger::VERBOSE) &&
503 goby::glog <<
"wildcard subscribing for MOOS variable pattern: " << var_pattern
504 <<
", app pattern: " << app_pattern <<
" @ " << blackout << std::endl;
506 std::pair<std::string, std::string> key = std::make_pair(var_pattern, app_pattern);
507 wildcard_pending_subscriptions_.push_back(std::make_pair(key, blackout));
510 if (!wildcard_mail_handlers_.count(key))
511 wildcard_mail_handlers_.insert(std::make_pair(
512 key, boost::shared_ptr<boost::signals2::signal<
void(
const CMOOSMsg& msg)> >(
513 new boost::signals2::signal<
void(
const CMOOSMsg& msg)>)));
516 wildcard_mail_handlers_[key]->connect(handler);
521 if (connected_ && started_up_)
527 MOOSAppType::RegisterVariables();
529 while (!pending_subscriptions_.empty())
532 if (MOOSAppType::m_Comms.Register(pending_subscriptions_.front().first,
533 pending_subscriptions_.front().second))
535 goby::glog.is(goby::common::logger::VERBOSE) &&
536 goby::glog <<
"subscribed for: " << pending_subscriptions_.front().first
542 goby::glog <<
"failed to subscribe for: " << pending_subscriptions_.front().first
545 existing_subscriptions_.push_back(pending_subscriptions_.front());
546 pending_subscriptions_.pop_front();
549 while (!wildcard_pending_subscriptions_.empty())
552 if (MOOSAppType::m_Comms.Register(wildcard_pending_subscriptions_.front().first.first,
553 wildcard_pending_subscriptions_.front().first.second,
554 wildcard_pending_subscriptions_.front().second))
556 goby::glog.is(goby::common::logger::VERBOSE) &&
558 << wildcard_pending_subscriptions_.front().first.first <<
":" 559 << wildcard_pending_subscriptions_.front().first.second << std::endl;
565 << wildcard_pending_subscriptions_.front().first.first <<
":" 566 << wildcard_pending_subscriptions_.front().first.second << std::endl;
569 wildcard_existing_subscriptions_.push_back(wildcard_pending_subscriptions_.front());
570 wildcard_pending_subscriptions_.pop_front();
574 template <
class MOOSAppType>
576 CMOOSFileReader& moos_file_reader)
579 const google::protobuf::Descriptor* desc = msg->GetDescriptor();
580 const google::protobuf::Reflection* refl = msg->GetReflection();
582 for (
int i = 0, n = desc->field_count(); i < n; ++i)
584 const google::protobuf::FieldDescriptor* field_desc = desc->field(i);
585 if (field_desc->is_repeated())
588 std::string moos_global = field_desc->options().GetExtension(goby::field).moos_global();
590 switch (field_desc->cpp_type())
592 case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE:
594 bool message_was_empty = !refl->HasField(*msg, field_desc);
596 fetch_moos_globals(refl->MutableMessage(msg, field_desc), moos_file_reader);
597 if (set_globals == 0 && message_was_empty)
598 refl->ClearField(msg, field_desc);
603 case google::protobuf::FieldDescriptor::CPPTYPE_INT32:
606 if (moos_file_reader.GetValue(moos_global, result))
608 refl->SetInt32(msg, field_desc, result);
615 case google::protobuf::FieldDescriptor::CPPTYPE_INT64:
618 if (moos_file_reader.GetValue(moos_global, result))
620 refl->SetInt64(msg, field_desc, result);
626 case google::protobuf::FieldDescriptor::CPPTYPE_UINT32:
629 if (moos_file_reader.GetValue(moos_global, result))
631 refl->SetUInt32(msg, field_desc, result);
638 case google::protobuf::FieldDescriptor::CPPTYPE_UINT64:
641 if (moos_file_reader.GetValue(moos_global, result))
643 refl->SetUInt64(msg, field_desc, result);
649 case google::protobuf::FieldDescriptor::CPPTYPE_BOOL:
652 if (moos_file_reader.GetValue(moos_global, result))
654 refl->SetBool(msg, field_desc, result);
660 case google::protobuf::FieldDescriptor::CPPTYPE_STRING:
663 if (moos_file_reader.GetValue(moos_global, result))
665 refl->SetString(msg, field_desc, result);
672 case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT:
675 if (moos_file_reader.GetValue(moos_global, result))
677 refl->SetFloat(msg, field_desc, result);
684 case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE:
687 if (moos_file_reader.GetValue(moos_global, result))
689 refl->SetDouble(msg, field_desc, result);
695 case google::protobuf::FieldDescriptor::CPPTYPE_ENUM:
698 if (moos_file_reader.GetValue(moos_global, result))
700 const google::protobuf::EnumValueDescriptor* enum_desc =
701 refl->GetEnum(*msg, field_desc)->type()->FindValueByName(result);
703 throw(std::runtime_error(std::string(
"invalid enumeration " + result +
704 " for field " + field_desc->name())));
706 refl->SetEnum(msg, field_desc, enum_desc);
716 template <
class MOOSAppType>
719 boost::filesystem::path launch_path(argv_[0]);
721 #if BOOST_FILESYSTEM_VERSION == 3 722 application_name_ = launch_path.filename().string();
724 application_name_ = launch_path.filename();
731 boost::program_options::options_description od_all;
732 boost::program_options::variables_map var_map;
735 boost::program_options::options_description od_cli_only(
"Given on command line only");
736 od_cli_only.add_options()(
"help,h",
"writes this help message")(
737 "moos_file,c", boost::program_options::value<std::string>(&mission_file_),
738 "path to .moos file")(
"moos_name,a",
739 boost::program_options::value<std::string>(&application_name_),
740 "name to register with MOOS")(
741 "example_config,e",
"writes an example .moos ProcessConfig block")(
742 "version,V",
"writes the current version");
744 boost::program_options::options_description od_both(
745 "Typically given in the .moos file, but may be specified on the command line");
747 goby::common::ConfigReader::get_protobuf_program_options(od_both, cfg->GetDescriptor());
749 od_all.add(od_cli_only);
751 boost::program_options::positional_options_description p;
752 p.add(
"moos_file", 1);
753 p.add(
"moos_name", 2);
755 boost::program_options::store(boost::program_options::command_line_parser(argc_, argv_)
761 boost::program_options::notify(var_map);
763 if (var_map.count(
"help"))
769 else if (var_map.count(
"example_config"))
771 std::cout <<
"ProcessConfig = " << application_name_ <<
"\n{";
772 goby::common::ConfigReader::get_example_cfg_file(cfg, &std::cout,
" ");
773 std::cout <<
"}" << std::endl;
776 else if (var_map.count(
"version"))
778 std::cout << goby::version_message() << std::endl;
785 std::string protobuf_text;
787 fin.open(mission_file_.c_str());
791 bool in_process_config =
false;
792 while (getline(fin, line))
794 std::string no_blanks_line = boost::algorithm::erase_all_copy(line,
" ");
795 if (boost::algorithm::iequals(no_blanks_line,
"PROCESSCONFIG=" + application_name_))
797 in_process_config =
true;
799 else if (in_process_config &&
800 !boost::algorithm::ifind_first(line,
"PROCESSCONFIG").empty())
805 if (in_process_config)
806 protobuf_text += line +
"\n";
809 if (!in_process_config)
812 goby::glog <<
"no ProcessConfig block for " << application_name_ << std::endl;
816 protobuf_text.erase(0, protobuf_text.find_first_of(
'{') + 1);
819 protobuf_text.erase(protobuf_text.find_last_of(
'}'));
822 boost::algorithm::replace_all(protobuf_text,
"//",
"#");
824 google::protobuf::TextFormat::Parser parser;
826 parser.RecordErrorsTo(&error_collector);
827 parser.AllowPartialMessage(
true);
828 parser.ParseFromString(protobuf_text, cfg);
829 if (error_collector.has_errors() || error_collector.has_warnings())
832 goby::glog <<
"fatal configuration errors (see above)" << std::endl;
838 << mission_file_ << std::endl;
843 CMOOSFileReader moos_file_reader;
844 moos_file_reader.SetFile(mission_file_);
845 fetch_moos_globals(cfg, moos_file_reader);
848 typedef std::pair<std::string, boost::program_options::variable_value> P;
849 BOOST_FOREACH (
const P& p, var_map)
852 if (!p.second.defaulted())
853 goby::common::ConfigReader::set_protobuf_program_option(var_map, *cfg, p.first,
858 if (!cfg->IsInitialized())
860 std::vector<std::string> errors;
861 cfg->FindInitializationErrors(&errors);
863 std::stringstream err_msg;
864 err_msg <<
"Configuration is missing required parameters: \n";
865 BOOST_FOREACH (
const std::string& s, errors)
866 err_msg << goby::common::esc_red << s <<
"\n" << goby::common::esc_nocolor;
868 err_msg <<
"Make sure you specified a proper .moos file";
875 std::cerr << od_all <<
"\n";
877 std::cerr <<
"Problem parsing command-line configuration: \n" << e.what() <<
"\n";
889 if (common_cfg_.show_gui())
894 if (common_cfg_.log())
896 if (!common_cfg_.has_log_path())
899 goby::glog <<
"logging all terminal output to default directory (" 900 << common_cfg_.log_path() <<
")." 901 <<
"set log_path for another path " << std::endl;
904 if (!common_cfg_.log_path().empty())
907 std::string file_name_base = boost::replace_all_copy(application_name_,
"/",
"_") +
908 "_" + common_cfg_.community();
910 std::string file_name =
911 file_name_base +
"_" + to_iso_string(second_clock::universal_time()) +
".txt";
913 std::string file_symlink = file_name_base +
"_latest.txt";
915 goby::glog.is(goby::common::logger::VERBOSE) &&
916 goby::glog <<
"logging output to file: " << file_name << std::endl;
918 fout_.open(std::string(common_cfg_.log_path() +
"/" + file_name).c_str());
921 remove(std::string(common_cfg_.log_path() +
"/" + file_symlink).c_str());
922 symlink(file_name.c_str(),
923 std::string(common_cfg_.log_path() +
"/" + file_symlink).c_str());
926 if (!fout_.is_open())
928 fout_.open(std::string(
"./" + file_name).c_str());
931 <<
"logging to current directory because given directory is unwritable!" 935 if (!fout_.is_open())
938 goby::glog <<
"cannot write to current directory, so cannot log." << std::endl;
945 if (common_cfg_.has_moos_parser_technique())
946 goby::moos::moos_technique = common_cfg_.moos_parser_technique();
947 else if (common_cfg_.has_use_binary_protobuf())
948 goby::moos::moos_technique =
949 common_cfg_.use_binary_protobuf()
950 ? goby::moos::protobuf::TranslatorEntry::TECHNIQUE_PREFIXED_PROTOBUF_NATIVE_ENCODED
951 : goby::moos::protobuf::TranslatorEntry::TECHNIQUE_PREFIXED_PROTOBUF_TEXT_FORMAT;
953 if (common_cfg_.time_warp_multiplier() != 1)
956 goby::common::goby_time_warp_factor = common_cfg_.time_warp_multiplier();
958 start_time_ *= common_cfg_.time_warp_multiplier();
964 template <
typename App>
int goby::moos::run(
int argc,
char* argv[])
971 App* app = App::get_instance();
972 app->Run(App::application_name_.c_str(), App::mission_file_.c_str());
979 catch (std::exception& e)
void set_name(const std::string &s)
Set the name of the application that the logger is serving.
void parse_for_moos(const std::string &in, google::protobuf::Message *msg)
Parses the string in to Google Protocol Buffers message msg. All errors are written to the goby::util...
indicates a problem with the runtime command line or .cfg file configuration (or –help was given) ...
Helpers for MOOS applications for serializing and parsed Google Protocol buffers messages.
int run(int argc, char *argv[], Config *cfg)
Run a Goby application derived from MinimalApplicationBase. blocks caller until MinimalApplicationBas...
double goby_time< double >()
Returns current UTC time as seconds and fractional seconds since 1970-01-01 00:00:00.
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...