/***************************************************************************
* GApplication.cpp - GammaLib application base class *
* ----------------------------------------------------------------------- *
* copyright (C) 2010-2016 by Juergen Knoedlseder *
* ----------------------------------------------------------------------- *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
* *
***************************************************************************/
/**
* @file GApplication.cpp
* @brief GammaLib application base class
* @author Juergen Knoedlseder
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include "GApplication.hpp"
#include "GTools.hpp"
/* __ Method name definitions ____________________________________________ */
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
const int header_width = 80; //!< Header width
/* __ Debug definitions __________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
GApplication::GApplication(void)
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Application constructor
*
* @param[in] name Application name.
* @param[in] version Application version.
*
* Constructs an application from an application @p name and @p version. The
* application parameters will be loaded from the parameter file. No log
* file will be opened.
*
* This constructor should be used for Python scripts.
***************************************************************************/
GApplication::GApplication(const std::string& name, const std::string& version)
{
// Initialise members
init_members();
// Set application name and version
m_name = name;
m_version = version;
// Set default parfile and logfile name
m_parfile = name+".par";
m_logfile = name+".log";
// Initialise application parameters
m_pars.load(par_filename());
// Set log filename and chattiness
set_log_filename();
set_log_chatter();
// Return
return;
}
/***********************************************************************//**
* @brief Application constructor
*
* @param[in] name Application name.
* @param[in] version Application version.
* @param[in] argc Number of command line arguments
* @param[in] argv Command line arguments
*
* Constructs an application from an application @p name, @p version and a
* number @p argc of command line arguments @p argv. The application
* parameters will be loaded from the parameter file and the log file will
* be opened.
*
* This constructor should be used for C++ applications.
***************************************************************************/
GApplication::GApplication(const std::string& name, const std::string& version,
int argc, char *argv[])
{
// Initialise members
init_members();
// Set application name and version
m_name = name;
m_version = version;
// Set default parfile and logfile name
m_parfile = name+".par";
m_logfile = name+".log";
// Save arguments as vector of strings
for (int i = 0; i < argc; ++i) {
m_args.push_back(gammalib::strip_whitespace(argv[i]));
}
// Catch --help option before doing anything else. The actual action
// needs to be done by the client, but we skip the loading of the
// parameter file and the opening of the logger if the help option
// was specified.
m_need_help = false;
if (m_args.size() >= 2) {
if (m_args[1] == "--help") {
m_need_help = true;
}
}
// If no --help option has been specified then proceed with loading
// the parameter file and opening the logger
if (!m_need_help) {
// Initialise application parameters
m_pars.load(par_filename(), m_args);
// Set log filename and chattiness
set_log_filename();
set_log_chatter();
// Initialise the application logger
logFileOpen();
} // endif: no --help option specified
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] app Application.
***************************************************************************/
GApplication::GApplication(const GApplication& app)
{
// Initialise members
init_members();
// Copy members
copy_members(app);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
GApplication::~GApplication(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] app Application.
* @return Application.
***************************************************************************/
GApplication& GApplication::operator=(const GApplication& app)
{
// Execute only if object is not identical
if (this != &app) {
// Free members
free_members();
// Initialise private members for clean destruction
init_members();
// Copy members
copy_members(app);
} // endif: object was not identical
// Return
return *this;
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Clear application
***************************************************************************/
void GApplication::clear(void)
{
// Free members
free_members();
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Clone application
*
* @return Pointer to deep copy of application.
***************************************************************************/
GApplication* GApplication::clone(void) const
{
// Clone application
return new GApplication(*this);
}
/***********************************************************************//**
* @brief Return application elapsed time in calendar seconds
*
* @return Application elapsed time in calendar seconds.
***************************************************************************/
double GApplication::telapse(void) const
{
// Get actual time
std::time_t acttime;
std::time(&acttime);
// Compute elapsed time
double telapse = difftime(acttime, m_tstart);
// Return elapsed time
return telapse;
}
/***********************************************************************//**
* @brief Return application elapsed time in CPU seconds
*
* @return Application elapsed time in CPU seconds.
***************************************************************************/
double GApplication::celapse(void) const
{
// Compute elapsed CPU clock time
double celapse = ((double) (clock() - m_cstart)) / CLOCKS_PER_SEC;
// Return elapsed time
return celapse;
}
/***********************************************************************//**
* @brief Open log file
*
* @param[in] clobber Overwrite (true) or append (false) to existing file
* (defaults to true)
*
* Opens application log file and sets the logger chattiness dependent on
* the chatter parameter of the application.
***************************************************************************/
void GApplication::logFileOpen(const bool& clobber)
{
// Open logger only if filename is non-empty
if (!log_filename().empty()) {
// Initialise the application logger
log.open(log_filename(), clobber);
}
// Set logger chattiness
set_log_chatter();
// Return
return;
}
/***********************************************************************//**
* @brief Close log file
*
* Logs trailer and close application log file.
***************************************************************************/
void GApplication::logFileClose(void)
{
// Put trailer in log file
log_trailer();
// Close log file
log.close();
// Return
return;
}
/***********************************************************************//**
* @brief Signal terse logging
*
* @return True if at least terse logging is requested.
*
* The terse level is used for the most crucial application information that
* should be logged in all cases (chatter >= 1).
***************************************************************************/
bool GApplication::logTerse(void) const
{
// Get chatter level (circumvent const correctness)
int chatter = const_cast(&m_pars["chatter"])->integer();
// Return terse logging condition
return (chatter > 0);
}
/***********************************************************************//**
* @brief Signal normal logging
*
* @return True if at least normal logging is requested.
*
* The normal level is used for standard application information that should
* be logged in normal operations (chatter >= 2).
***************************************************************************/
bool GApplication::logNormal(void) const
{
// Get chatter level (circumvent const correctness)
int chatter = const_cast(&m_pars["chatter"])->integer();
// Return normal logging condition
return (chatter > 1);
}
/***********************************************************************//**
* @brief Signal explicit logging
*
* @return True if at least explicit logging is requested.
*
* The explicit level is used for detailed application information that
* should be logged in detailed studies (chatter >= 3).
***************************************************************************/
bool GApplication::logExplicit(void) const
{
// Get chatter level (circumvent const correctness)
int chatter = const_cast(&m_pars["chatter"])->integer();
// Return explicit logging condition
return (chatter > 2);
}
/***********************************************************************//**
* @brief Signal verbose logging
*
* @return True if verbose logging is requested.
*
* The verbose level is used for full application information (chatter >= 4).
***************************************************************************/
bool GApplication::logVerbose(void) const
{
// Get chatter level (circumvent const correctness)
int chatter = const_cast(&m_pars["chatter"])->integer();
// Return verbose logging condition
return (chatter > 3);
}
/***********************************************************************//**
* @brief Signal debug logging
*
* @return True if the debugging mode has been selected.
*
* The debug level is used for application debugging.
***************************************************************************/
bool GApplication::logDebug(void) const
{
// Get debug condition (circumvent const correctness)
bool debug = const_cast(&m_pars["debug"])->boolean();
// Return debug condition
return (debug);
}
/***********************************************************************//**
* @brief Return clobber
*
* @return True if the clobber parameter is true.
*
* The clobber indicates if existing files should be overwritten.
***************************************************************************/
bool GApplication::clobber(void) const
{
// Get clobber condition (circumvent const correctness)
bool clobber = const_cast(&m_pars["clobber"])->boolean();
// Return clobber condition
return (clobber);
}
/***********************************************************************//**
* @brief Write application header in log file
*
* Dump the application header into the log file. The header is composed of
* a fixed width block delimined by "*" characters that contains information
* about the application name and version.
***************************************************************************/
void GApplication::log_header(void)
{
// Reset any indentation
log.indent(0);
// Dump header
log << gammalib::fill("*", header_width) << std::endl;
log << "*" << gammalib::centre(m_name, header_width-2) << "*" << std::endl;
log << "* " << gammalib::fill("-", header_width-4) << " *" << std::endl;
log << "* Version: " << gammalib::left(m_version, header_width-12) << "*" << std::endl;
log << gammalib::fill("*", header_width) << std::endl;
// Return
return;
}
/***********************************************************************//**
* @brief Write application trailer in log file
*
* The application trailer gives the total number of elapsed calendar and
* CPU seconds.
***************************************************************************/
void GApplication::log_trailer(void)
{
// Reset any indentation
log.indent(0);
// Dump trailer
log << "Application \"" << m_name << "\" terminated after ";
log << telapse() << " wall clock seconds, consuming ";
log << celapse() << " seconds of CPU time.";
if (telapse() < 0.1) {
log << " This was rather quick!";
}
if (telapse() > 86400.0) {
log << " Hope it was worth waiting ...";
}
log << std::endl;
// Return
return;
}
/***********************************************************************//**
* @brief Write string in log file
*
* @param[in] chatter Minimum required chattiness
* @param[in] string String
*
* Writes a string into the log file if chattiness is at least @p chatter.
***************************************************************************/
void GApplication::log_string(const GChatter& chatter,
const std::string& string)
{
// Get chattiness of application
GChatter chattiness = static_cast((&m_pars["chatter"])->integer());
// Only write message if chattiness is at least equal to the minimum
// required chattiness
if (chattiness >= chatter) {
log << string;
}
// Return
return;
}
/***********************************************************************//**
* @brief Write parameter value in log file
*
* @param[in] chatter Minimum required chattiness
* @param[in] name Parameter name string
* @param[in] value Value string
*
* Writes a parameter value into the log file if chattiness is at least
* @p chatter.
***************************************************************************/
void GApplication::log_value(const GChatter& chatter,
const std::string& name,
const std::string& value)
{
// Get chattiness of application
GChatter chattiness = static_cast((&m_pars["chatter"])->integer());
// Only write message if chattiness is at least equal to the minimum
// required chattiness
if (chattiness >= chatter) {
log << gammalib::parformat(name);
log << value << std::endl;
}
// Return
return;
}
/***********************************************************************//**
* @brief Write header 1 in log file
*
* @param[in] chatter Minimum required chattiness
* @param[in] header Header string
*
* Writes a header of level 1 into the log file if chattiness is at least
* @p chatter.
***************************************************************************/
void GApplication::log_header1(const GChatter& chatter,
const std::string& header)
{
// Get chattiness of application
GChatter chattiness = static_cast((&m_pars["chatter"])->integer());
// Only write message if chattiness is at least equal to the minimum
// required chattiness
if (chattiness >= chatter) {
log << std::endl;
log.header1(header);
}
// Return
return;
}
/***********************************************************************//**
* @brief Write header 2 in log file
*
* @param[in] chatter Minimum required chattiness
* @param[in] header Header string
*
* Writes a header of level 2 into the log file if chattiness is at least
* @p chatter.
***************************************************************************/
void GApplication::log_header2(const GChatter& chatter,
const std::string& header)
{
// Get chattiness of application
GChatter chattiness = static_cast((&m_pars["chatter"])->integer());
// Only write message if chattiness is at least equal to the minimum
// required chattiness
if (chattiness >= chatter) {
log << std::endl;
log.header2(header);
}
// Return
return;
}
/***********************************************************************//**
* @brief Write header 3 in log file
*
* @param[in] chatter Minimum required chattiness
* @param[in] header Header string
*
* Writes a header of level 3 into the log file if chattiness is at least
* @p chatter.
***************************************************************************/
void GApplication::log_header3(const GChatter& chatter,
const std::string& header)
{
// Get chattiness of application
GChatter chattiness = static_cast((&m_pars["chatter"])->integer());
// Only write message if chattiness is at least equal to the minimum
// required chattiness
if (chattiness >= chatter) {
log << std::endl;
log.header3(header);
}
// Return
return;
}
/***********************************************************************//**
* @brief Write application parameters in log file
*
* @param[in] chatter Minimum required chattiness
*
* Writes all application parameters in the log file. For parameters that
* have not yet been queried the method does not write the current value
* but signals [not queried].
***************************************************************************/
void GApplication::log_parameters(const GChatter& chatter)
{
// Get chattiness of application
GChatter chattiness = static_cast((&m_pars["chatter"])->integer());
// Only write parameters if the chattiness is at least equal to the
// minimum required chattiness
if (chattiness >= chatter) {
// Write header
log.header1("Parameters");
// Write parameters in logger
for (int i = 0; i < m_pars.size(); ++i) {
// Set parameter name
std::string name = " " + m_pars.m_pars[i].name() + " ";
name = name + gammalib::fill(".", 28-name.length()) + ": ";
// Write parameter
if (m_pars.m_pars[i].is_query()) {
log << name << "[not queried]" << std::endl;
}
else {
log << name << m_pars.m_pars[i].m_value << std::endl;
}
} // endfor: looped over all parameters
} // endif: chattiness satisfied minimum required level
// Return
return;
}
/***********************************************************************//**
* @brief Print application
*
* @param[in] chatter Chattiness.
* @return String containing application information.
***************************************************************************/
std::string GApplication::print(const GChatter& chatter) const
{
// Initialise result string
std::string result;
// Continue only if chatter is not silent
if (chatter != SILENT) {
// Append header
result.append("=== GApplication ===");
// Append application name, version and arguments
result.append("\n"+gammalib::parformat("Name")+name());
result.append("\n"+gammalib::parformat("Version")+version());
for (int i = 0; i < m_args.size(); ++i) {
if (i == 0) {
result.append("\n"+gammalib::parformat("Command")+m_args[i]);
}
else if (i == 1) {
result.append("\n"+gammalib::parformat("Arguments")+m_args[i]);
}
else {
result.append("\n"+gammalib::parformat(" ")+m_args[i]);
}
}
// Append parameters
for (int i = 0; i < m_pars.size(); ++i) {
result.append("\n"+gammalib::parformat(m_pars.m_pars[i].name()) +
m_pars.m_pars[i].m_value);
}
} // endif: chatter was not silent
// Return result
return result;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void GApplication::init_members(void)
{
// Initialise public members
log.clear();
// Initialise protected members
m_name.clear();
m_version.clear();
m_parfile.clear();
m_logfile.clear();
m_args.clear();
m_pars.clear();
m_need_help = false;
// Save the execution calendar start time
std::time(&m_tstart);
// Save the execution start clock
m_cstart = std::clock();
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] app Application.
***************************************************************************/
void GApplication::copy_members(const GApplication& app)
{
// Copy public attributes
log = app.log;
// Copy protected attributes
m_name = app.m_name;
m_version = app.m_version;
m_parfile = app.m_parfile;
m_logfile = app.m_logfile;
m_args = app.m_args;
m_tstart = app.m_tstart;
m_cstart = app.m_cstart;
m_pars = app.m_pars;
m_need_help = app.m_need_help;
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void GApplication::free_members(void)
{
// Save application parameters
m_pars.save(par_filename());
// Put trailer in log file
log_trailer();
// Close log file
log.close();
// Return
return;
}
/***********************************************************************//**
* @brief Set chattiness of logger
***************************************************************************/
void GApplication::set_log_chatter(void)
{
// Get chattiness from application parameter
int chatter = m_pars["chatter"].integer();
// Make sure that chatter is within valid range
if (chatter < 0) {
chatter = 0;
}
else if (chatter > 4) {
chatter = 4;
}
// Set logger chatter
log.chatter((GChatter)chatter);
// Return
return;
}
/***********************************************************************//**
* @brief Set log filename from "logfile" parameter (if it exists)
***************************************************************************/
void GApplication::set_log_filename(void)
{
// Continue only if logfile parameter exists
if (m_pars.contains("logfile")) {
// Get log filename from application parameter
m_logfile = m_pars["logfile"].filename();
}
// Return
return;
}