Goby3  3.1.5a
2024.05.23
interface.h
Go to the documentation of this file.
1 // Copyright 2011-2024:
2 // GobySoft, LLC (2013-)
3 // Massachusetts Institute of Technology (2007-2014)
4 // Community contributors (see AUTHORS file)
5 // File authors:
6 // Toby Schneider <toby@gobysoft.org>
7 //
8 //
9 // This file is part of the Goby Underwater Autonomy Project Libraries
10 // ("The Goby Libraries").
11 //
12 // The Goby Libraries are free software: you can redistribute them and/or modify
13 // them under the terms of the GNU Lesser General Public License as published by
14 // the Free Software Foundation, either version 2.1 of the License, or
15 // (at your option) any later version.
16 //
17 // The Goby Libraries are distributed in the hope that they will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU Lesser General Public License for more details.
21 //
22 // You should have received a copy of the GNU Lesser General Public License
23 // along with Goby. If not, see <http://www.gnu.org/licenses/>.
24 
25 #ifndef GOBY_MIDDLEWARE_APPLICATION_INTERFACE_H
26 #define GOBY_MIDDLEWARE_APPLICATION_INTERFACE_H
27 
28 #include <chrono>
29 #include <csignal>
30 #include <iostream>
31 #include <sys/types.h>
32 #include <unistd.h>
33 
34 #include <boost/format.hpp>
35 
36 #include "goby/exception.h"
40 #include "goby/time.h"
41 #include "goby/util/debug_logger.h"
42 #include "goby/util/geodesy.h"
43 
44 namespace goby
45 {
52 template <typename App>
54 
61 template <typename App,
62  typename Configurator = middleware::ProtobufConfigurator<typename App::ConfigType>>
63 int run(int argc, char* argv[])
64 {
65  return run<App>(Configurator(argc, argv));
66 }
67 
68 namespace middleware
69 {
71 template <typename Config> class Application
72 {
73  public:
74  Application();
75  virtual ~Application()
76  {
77  goby::glog.is_debug2() && goby::glog << "Application: destructing cleanly" << std::endl;
78  }
79 
80  using ConfigType = Config;
81 
82  protected:
84  virtual void pre_initialize(){};
85 
87  virtual void initialize(){};
88 
90  virtual void post_initialize(){};
91 
93  virtual void run() = 0;
94 
96  virtual void pre_finalize(){};
97 
99  virtual void finalize(){};
100 
102  virtual void post_finalize(){};
103 
107  void quit(int return_value = 0)
108  {
109  alive_ = false;
110  return_value_ = return_value;
111  }
112 
114  const Config& app_cfg() { return *app_cfg_; }
115 
118  {
119  if (geodesy_)
120  return *geodesy_;
121  else
122  throw(goby::Exception("No lat_origin and lon_origin defined for requested UTMGeodesy"));
123  }
124 
126  bool has_geodesy() { return geodesy_ ? true : false; }
127 
128  std::string app_name() { return app3_base_configuration_->name(); }
129 
130  protected:
132 
133  private:
134  template <typename App>
135  friend int ::goby::run(
137  // main loop that exits on quit(); returns the desired return value
138  int __run();
139 
140  void configure_logger();
141  void configure_glog_file();
142  void check_rotate_glog_file();
143 
144  private:
145  // sets configuration (before Application construction)
146  static std::unique_ptr<Config> app_cfg_;
147  static std::unique_ptr<protobuf::AppConfig> app3_base_configuration_;
148 
149  bool alive_;
150  int return_value_;
151 
152  // static here allows fout_ to live until program exit to log glog output
153  static std::unique_ptr<std::ofstream> fout_;
154 
155  goby::time::SteadyClock::time_point next_log_rotate_time_;
156 
157  std::unique_ptr<goby::util::UTMGeodesy> geodesy_;
158 };
159 } // namespace middleware
160 
161 } // namespace goby
162 
163 template <typename Config>
164 std::unique_ptr<std::ofstream> goby::middleware::Application<Config>::fout_;
165 
166 template <typename Config> std::unique_ptr<Config> goby::middleware::Application<Config>::app_cfg_;
167 
168 template <typename Config>
169 std::unique_ptr<goby::middleware::protobuf::AppConfig>
171 
172 template <typename Config> goby::middleware::Application<Config>::Application() : alive_(true)
173 {
174  using goby::glog;
175 
176  configure_logger();
177  if (app3_base_configuration_->has_geodesy())
178  configure_geodesy({app3_base_configuration_->geodesy().lat_origin_with_units(),
179  app3_base_configuration_->geodesy().lon_origin_with_units()});
180 
181  if (!app3_base_configuration_->IsInitialized())
182  throw(middleware::ConfigException("Invalid base configuration"));
183 
184  glog.is_debug2() && glog << "Application: constructed with PID: " << getpid() << std::endl;
185  glog.is_debug1() && glog << "App name is " << app3_base_configuration_->name() << std::endl;
186  glog.is_debug2() && glog << "Configuration is: " << app_cfg_->DebugString() << std::endl;
187 }
188 
189 template <typename Config> void goby::middleware::Application<Config>::configure_logger()
190 {
191  using goby::glog;
192 
193  // set up the logger
194  glog.set_name(app3_base_configuration_->name());
196  app3_base_configuration_->glog_config().tty_verbosity()),
197  &std::cout);
198 
199  if (app3_base_configuration_->glog_config().show_gui())
200  glog.enable_gui();
201 
202  if (app3_base_configuration_->glog_config().has_file_log())
203  configure_glog_file();
204 
205  if (app3_base_configuration_->glog_config().show_dccl_log())
207 }
208 
210 {
211  using goby::glog;
212  if (app3_base_configuration_->glog_config().has_file_log() &&
213  app3_base_configuration_->glog_config().file_log().has_log_rotate_sec() &&
214  goby::time::SteadyClock::now() > next_log_rotate_time_)
215  {
216  glog.remove_stream(fout_.get());
217  configure_glog_file();
218  }
219 }
220 
221 template <typename Config> void goby::middleware::Application<Config>::configure_glog_file()
222 {
223  const auto& file_log = app3_base_configuration_->glog_config().file_log();
224  std::string file_format_str;
225 
226  std::string file_name_format = file_log.file_name();
227 
228  if (!file_log.has_file_name() && file_log.omit().file_timestamp())
229  file_name_format = "%2%.txt";
230 
231  if (file_log.has_file_dir() && !file_log.file_dir().empty())
232  {
233  auto file_dir = file_log.file_dir();
234  if (file_dir.back() != '/')
235  file_dir += "/";
236  file_format_str = file_dir + file_name_format;
237  }
238  else
239  {
240  file_format_str = file_name_format;
241  }
242 
243  boost::format file_format(file_format_str);
244 
245  if (!file_log.omit().file_timestamp())
246  {
247  if (file_format_str.find("%1") == std::string::npos)
248  glog.is_die() &&
249  glog << "file_name string must contain \"%1%\" which is expanded to the current "
250  "application start time (e.g. 20190201T184925), unless omit.file_timestamp "
251  "== true. Erroneous file_name is: "
252  << file_format_str << std::endl;
253  }
254 
255  file_format.exceptions(boost::io::all_error_bits ^
256  (boost::io::too_many_args_bit | boost::io::too_few_args_bit));
257 
258  std::string file_name =
259  (file_format % goby::time::file_str() % app3_base_configuration_->name()).str();
260  glog.is_verbose() && glog << "logging output to file: " << file_name << std::endl;
261 
262  fout_.reset(new std::ofstream(file_name.c_str()));
263 
264  if (!fout_->is_open())
265  glog.is_die() && glog << "cannot write glog output to requested file: " << file_name
266  << std::endl;
267 
268  if (!file_log.omit().latest_symlink())
269  {
270  std::string file_symlink =
271  (file_format % "latest" % app3_base_configuration_->name()).str();
272  remove(file_symlink.c_str());
273  int result = symlink(realpath(file_name.c_str(), NULL), file_symlink.c_str());
274  if (result != 0)
275  glog.is_warn() &&
276  glog << "Cannot create symlink to latest file. Continuing onwards anyway"
277  << std::endl;
278  }
279 
280  glog.add_stream(file_log.verbosity(), fout_.get());
281 
282  if (file_log.has_log_rotate_sec())
283  next_log_rotate_time_ =
284  goby::time::SteadyClock::now() + std::chrono::seconds(file_log.log_rotate_sec());
285 }
286 
287 template <typename Config>
290 {
291  geodesy_.reset(new goby::util::UTMGeodesy(datum));
292 }
293 
294 template <typename Config> int goby::middleware::Application<Config>::__run()
295 {
296  // block SIGWINCH (change window size) in all threads
297  sigset_t signal_mask;
298  sigemptyset(&signal_mask);
299  sigaddset(&signal_mask, SIGWINCH);
300  pthread_sigmask(SIG_BLOCK, &signal_mask, NULL);
301 
302  this->pre_initialize();
303  this->initialize();
304  this->post_initialize();
305  // continue to run while we are alive (quit() has not been called)
306  while (alive_)
307  {
308  this->run();
309  this->check_rotate_glog_file();
310  }
311  this->pre_finalize();
312  this->finalize();
313  this->post_finalize();
314  return return_value_;
315 }
316 
317 template <typename App>
319 {
320  int return_value = 0;
321  try
322  {
323  try
324  {
325  cfgtor.validate();
326  }
328  {
329  cfgtor.handle_config_error(e);
330  return 1;
331  }
332 
333  // simply print the configuration and exit
334  if (cfgtor.app_configuration().debug_cfg())
335  {
336  std::cout << cfgtor.str() << std::endl;
337  exit(EXIT_SUCCESS);
338  }
339 
340  // set configuration
341  App::app_cfg_.reset(new typename App::ConfigType(cfgtor.cfg()));
342  App::app3_base_configuration_.reset(
344 
345  // set up simulation time
346  if (App::app3_base_configuration_->simulation().time().use_sim_time())
347  {
350  App::app3_base_configuration_->simulation().time().warp_factor();
351  if (App::app3_base_configuration_->simulation().time().has_reference_microtime())
353  std::chrono::system_clock::time_point(std::chrono::microseconds(
354  App::app3_base_configuration_->simulation().time().reference_microtime()));
355  }
356 
357  // instantiate the application (with the configuration already set)
358  App app;
359  return_value = app.__run();
360  }
361  catch (std::exception& e)
362  {
363  // some other exception
364  std::cerr << "Application:: uncaught exception: " << e.what() << std::endl;
365  throw;
366  }
367 
368  goby::glog.is_debug2() && goby::glog << "goby::run: exiting cleanly with code: " << return_value
369  << std::endl;
370  return return_value;
371 }
372 
373 #endif
goby::middleware::protobuf::AppConfig
Definition: app_config.pb.h:767
goby::middleware::Application::has_geodesy
bool has_geodesy()
Returns if the geodesy tool is configured with a datum.
Definition: interface.h:126
goby
The global namespace for the Goby project.
Definition: acomms_constants.h:33
goby::util::FlexOstream::is_warn
bool is_warn()
Definition: flex_ostream.h:82
goby::middleware::ConfiguratorInterface::handle_config_error
virtual void handle_config_error(middleware::ConfigException &e) const
Override to customize how ConfigException errors are handled.
Definition: configurator.h:54
goby::middleware::Application::app_name
std::string app_name()
Definition: interface.h:128
goby::run
int run(const goby::middleware::ConfiguratorInterface< typename App::ConfigType > &cfgtor)
Run a Goby application using the provided Configurator.
Definition: interface.h:318
goby::middleware::Application::ConfigType
Config ConfigType
Definition: interface.h:80
goby::util::FlexOstream::enable_gui
void enable_gui()
Definition: flex_ostream.h:73
goby::util::e
constexpr T e
Definition: constants.h:35
goby::time::SteadyClock::now
static time_point now() noexcept
Returns the current steady time unless SimulatorSettings::using_sim_time == true in which case a simu...
Definition: steady_clock.h:49
goby::middleware::Application::~Application
virtual ~Application()
Definition: interface.h:75
goby::time::str
std::string str(TimeType value=SystemClock::now< TimeType >())
Returns the provided time (or current time if omitted) as a human-readable string.
Definition: convert.h:191
goby::middleware::ConfiguratorInterface::str
virtual std::string str() const =0
Override to output the configuration object as a string.
goby::util::FlexOstream::is_debug2
bool is_debug2()
Definition: flex_ostream.h:85
goby::middleware::ConfigException
indicates a problem with the runtime command line or .cfg file configuration (or –help was given)
Definition: configuration_reader.h:56
goby::util::FlexOstream::add_stream
void add_stream(logger::Verbosity verbosity=logger::VERBOSE, std::ostream *os=nullptr)
Attach a stream object (e.g. std::cout, std::ofstream, ...) to the logger with desired verbosity.
Definition: flex_ostream.h:89
goby::middleware::Application::Application
Application()
Definition: interface.h:172
goby::middleware::Application::post_initialize
virtual void post_initialize()
Called just after initialize.
Definition: interface.h:90
goby::middleware::Application::post_finalize
virtual void post_finalize()
Called just after finalize.
Definition: interface.h:102
goby::util::FlexOstream::is_debug1
bool is_debug1()
Definition: flex_ostream.h:84
goby::util::FlexOstream::remove_stream
void remove_stream(std::ostream *os=nullptr)
Definition: flex_ostream.h:103
goby::time::SteadyClock::time_point
std::chrono::time_point< SteadyClock > time_point
Definition: steady_clock.h:45
goby::middleware::Application::run
virtual void run()=0
Runs continuously until quit() is called.
goby::util::FlexOstream::is_verbose
bool is_verbose()
Definition: flex_ostream.h:83
time.h
goby::middleware::Application::configure_geodesy
void configure_geodesy(goby::util::UTMGeodesy::LatLonPoint datum)
Definition: interface.h:288
goby::time::file_str
std::string file_str(TimeType value=SystemClock::now< TimeType >())
Returns the provided time (or current time if omitted) as an ISO string suitable for file names (no s...
Definition: convert.h:204
goby::middleware::Application::pre_finalize
virtual void pre_finalize()
Called just before finalize.
Definition: interface.h:96
goby::time::SimulatorSettings::reference_time
static std::chrono::system_clock::time_point reference_time
Reference time when calculating SystemClock::now(). If this is unset, the default is 1 January of the...
Definition: simulation.h:42
goby::middleware::ConfiguratorInterface::cfg
const Config & cfg() const
The configuration object produced from the command line parameters.
Definition: configurator.h:42
goby::run
int run(int argc, char *argv[])
Shorthand for goby::run for Configurators that have a constructor that simply takes argc,...
Definition: interface.h:63
goby::middleware::Application::finalize
virtual void finalize()
Perform any final cleanup actions just before the destructor is called.
Definition: interface.h:99
goby::util::UTMGeodesy
Definition: geodesy.h:36
goby::middleware::ConfiguratorInterface::app_configuration
virtual const protobuf::AppConfig & app_configuration() const
Subset of the configuration used to configure the Application itself.
Definition: configurator.h:46
goby::util::logger::Verbosity
Verbosity
Definition: flex_ostreambuf.h:70
goby::middleware::detail::DCCLSerializerParserHelperBase::setup_dlog
static void setup_dlog()
Enable dlog output to glog using same verbosity settings as glog.
goby::middleware::Application::app_cfg
const Config & app_cfg()
Accesses configuration object passed at launch.
Definition: interface.h:114
goby::time::SimulatorSettings::using_sim_time
static bool using_sim_time
Enables simulation time if true (if false, none of the remaining parameters are used)
Definition: simulation.h:38
goby::time::SimulatorSettings::warp_factor
static int warp_factor
Warp factor to speed up (or slow time) the time values returned by SteadyClock::now() and SystemClock...
Definition: simulation.h:40
goby::middleware::ConfiguratorInterface::validate
virtual void validate() const
Override to validate the configuration.
Definition: configurator.h:51
goby::util::FlexOstream::is_die
bool is_die()
Definition: flex_ostream.h:81
debug_logger.h
goby::middleware::Application::initialize
virtual void initialize()
Perform any initialize tasks that couldn't be done in the constructor.
Definition: interface.h:87
goby::middleware::Application::geodesy
const util::UTMGeodesy & geodesy()
Accesses the geodetic conversion tool if lat_origin and lon_origin were provided.
Definition: interface.h:117
geodesy.h
boost::units::si::time
unit< time_dimension, si::system > time
Definition: time.hpp:22
goby::util::UTMGeodesy::LatLonPoint
Definition: geodesy.h:39
goby::middleware::Application::quit
void quit(int return_value=0)
Requests a clean exit.
Definition: interface.h:107
goby::Exception
simple exception class for goby applications
Definition: exception.h:34
configurator.h
exception.h
dccl_serializer_parser.h
goby::glog
util::FlexOstream glog
Access the Goby logger through this object.
goby::middleware::ConfiguratorInterface
Defines the interface to a "configurator", a class that can read command line parameters (argc,...
Definition: configurator.h:38
app_config.pb.h
goby::util::FlexOstream::set_name
void set_name(const std::string &s)
Set the name of the application that the logger is serving.
Definition: flex_ostream.h:67
goby::middleware::Application::pre_initialize
virtual void pre_initialize()
Called just before initialize.
Definition: interface.h:84
goby::middleware::Application
Base class for Goby applications. Generally you will want to use SingleThreadApplication or MultiThre...
Definition: interface.h:71
goby::middleware::protobuf::AppConfig::debug_cfg
bool debug_cfg() const
Definition: app_config.pb.h:1729