diff --git a/JSBSim.vcxproj b/JSBSim.vcxproj
index 8a588be5b..b8cfbc796 100644
--- a/JSBSim.vcxproj
+++ b/JSBSim.vcxproj
@@ -232,6 +232,7 @@
+
@@ -356,6 +357,7 @@
+
diff --git a/JSBSim.vcxproj.filters b/JSBSim.vcxproj.filters
index da1ae9b40..b274b95ce 100644
--- a/JSBSim.vcxproj.filters
+++ b/JSBSim.vcxproj.filters
@@ -335,6 +335,9 @@
Source Files
+
+ Source Files
+
@@ -694,5 +697,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/src/FGFDMExec.cpp b/src/FGFDMExec.cpp
index c2eb8e3c8..58e3e284f 100644
--- a/src/FGFDMExec.cpp
+++ b/src/FGFDMExec.cpp
@@ -64,6 +64,7 @@ INCLUDES
#include "input_output/FGScript.h"
#include "input_output/FGXMLFileRead.h"
#include "initialization/FGInitialCondition.h"
+#include "input_output/FGLog.h"
using namespace std;
@@ -82,6 +83,7 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root, std::shared_ptr fdmc
{
Frame = 0;
disperse = 0;
+ Log = make_shared();
RootDir = "";
@@ -135,7 +137,8 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root, std::shared_ptr fdmc
}
} catch (...) { // if error set to false
disperse = 0;
- std::cerr << "Could not process JSBSIM_DISPERSIONS environment variable: Assumed NO dispersions." << endl;
+ FGLogging log(Log, LogLevel::WARN);
+ log << "Could not process JSBSIM_DISPERSIONS environment variable: Assumed NO dispersions." << endl;
}
Debug(0);
@@ -144,12 +147,14 @@ FGFDMExec::FGFDMExec(FGPropertyManager* root, std::shared_ptr fdmc
Allocate();
}
catch (const string& msg) {
- cerr << endl << "Caught error: " << msg << endl;
- throw;
+ FGLogging log(Log, LogLevel::FATAL);
+ log << endl << "Caught error: " << msg << endl;
+ throw BaseException(log.str());
}
catch (const BaseException& e) {
- cout << endl << "Caught error: " << e.what() << endl;
- throw;
+ FGLogging log(Log, LogLevel::FATAL);
+ log << endl << "Caught error: " << e.what() << endl;
+ throw BaseException(log.str());
}
trim_status = false;
@@ -183,7 +188,8 @@ FGFDMExec::~FGFDMExec()
Unbind();
DeAllocate();
} catch (const string& msg ) {
- cout << "Caught error: " << msg << endl;
+ FGLogging log(Log, LogLevel::FATAL);
+ log << "Caught error: " << msg << endl;
}
if (!FDMctr) (*FDMctr)--;
@@ -657,10 +663,11 @@ bool FGFDMExec::RunIC(void)
if (debug_lvl > 0) {
MassBalance->GetMassPropertiesReport(0);
- cout << endl << fgblue << highint
- << "End of vehicle configuration loading." << endl
- << "-------------------------------------------------------------------------------"
- << reset << std::setprecision(6) << endl;
+ FGLogging log(Log, LogLevel::DEBUG);
+ log << endl << LogFormat::BLUE << LogFormat::BOLD
+ << "End of vehicle configuration loading." << endl
+ << "-------------------------------------------------------------------------------"
+ << LogFormat::RESET << std::setprecision(6) << endl;
}
for (unsigned int n=0; n < Propulsion->GetNumEngines(); ++n) {
@@ -668,7 +675,8 @@ bool FGFDMExec::RunIC(void)
try {
Propulsion->InitRunning(n);
} catch (const string& str) {
- cerr << str << endl;
+ FGLogging log(Log, LogLevel::ERROR);
+ log << str << endl;
return false;
}
}
@@ -759,23 +767,23 @@ bool FGFDMExec::LoadPlanet(const SGPath& PlanetPath, bool useAircraftPath)
// Make sure that the document is valid
if (!document) {
- stringstream s;
- s << "File: " << PlanetFileName << " could not be read.";
- cerr << s.str() << endl;
- throw BaseException(s.str());
+ FGLogging log(Log, LogLevel::ERROR);
+ log << "File: " << PlanetFileName << " could not be read." << endl;
+ throw BaseException(log.str());
}
if (document->GetName() != "planet") {
- stringstream s;
- s << "File: " << PlanetFileName << " is not a planet file.";
- cerr << s.str() << endl;
- throw BaseException(s.str());
+ FGXMLLogging log(Log, document, LogLevel::ERROR);
+ log << "File: " << PlanetFileName << " is not a planet file." << endl;
+ throw BaseException(log.str());
}
bool result = LoadPlanet(document);
- if (!result)
- cerr << endl << "Planet element has problems in file " << PlanetFileName << endl;
+ if (!result) {
+ FGXMLLogging log(Log, document, LogLevel::ERROR);
+ log << endl << "Planet element has problems in file " << PlanetFileName << endl;
+ }
return result;
}
@@ -807,7 +815,8 @@ bool FGFDMExec::LoadPlanet(Element* element)
Atmosphere->InitModel();
result = Atmosphere->Load(atm_element);
if (!result) {
- cerr << endl << "Incorrect definition of ." << endl;
+ FGLogging log(Log, LogLevel::ERROR);
+ log << endl << "Incorrect definition of ." << endl;
return result;
}
InitializeModels();
@@ -841,8 +850,9 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
modelName = model; // Set the class modelName attribute
if( AircraftPath.isNull() || EnginePath.isNull() || SystemsPath.isNull()) {
- cerr << "Error: attempted to load aircraft with undefined "
- << "aircraft, engine, and system paths" << endl;
+ FGLogging log(Log, LogLevel::ERROR);
+ log << "Error: attempted to load aircraft with undefined "
+ << "aircraft, engine, and system paths" << endl;
return false;
}
@@ -871,7 +881,8 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (element) {
result = ReadFileHeader(element);
if (!result) {
- cerr << endl << "Aircraft fileheader element has problems in file " << aircraftCfgFileName << endl;
+ FGXMLLogging log(Log, element, LogLevel::ERROR);
+ log << endl << "Aircraft fileheader element has problems in file " << aircraftCfgFileName << endl;
return result;
}
}
@@ -883,7 +894,8 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (element) {
result = LoadPlanet(element);
if (!result) {
- cerr << endl << "Planet element has problems in file " << aircraftCfgFileName << endl;
+ FGXMLLogging log(Log, element, LogLevel::ERROR);
+ log << endl << "Planet element has problems in file " << aircraftCfgFileName << endl;
return result;
}
}
@@ -893,11 +905,13 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (element) {
result = Models[eAircraft]->Load(element);
if (!result) {
- cerr << endl << "Aircraft metrics element has problems in file " << aircraftCfgFileName << endl;
+ FGXMLLogging log(Log, element, LogLevel::ERROR);
+ log << endl << "Aircraft metrics element has problems in file " << aircraftCfgFileName << endl;
return result;
}
} else {
- cerr << endl << "No metrics element was found in the aircraft config file." << endl;
+ FGLogging log(Log, LogLevel::ERROR);
+ log << endl << "No metrics element was found in the aircraft config file." << endl;
return false;
}
@@ -906,11 +920,13 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (element) {
result = Models[eMassBalance]->Load(element);
if (!result) {
- cerr << endl << "Aircraft mass_balance element has problems in file " << aircraftCfgFileName << endl;
+ FGXMLLogging log(Log, element, LogLevel::ERROR);
+ log << endl << "Aircraft mass_balance element has problems in file " << aircraftCfgFileName << endl;
return result;
}
} else {
- cerr << endl << "No mass_balance element was found in the aircraft config file." << endl;
+ FGLogging log(Log, LogLevel::ERROR);
+ log << endl << "No mass_balance element was found in the aircraft config file." << endl;
return false;
}
@@ -919,13 +935,15 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (element) {
result = Models[eGroundReactions]->Load(element);
if (!result) {
- cerr << endl << element->ReadFrom()
- << "Aircraft ground_reactions element has problems in file "
- << aircraftCfgFileName << endl;
+ FGXMLLogging log(Log, element, LogLevel::ERROR);
+ log << endl
+ << "Aircraft ground_reactions element has problems in file "
+ << aircraftCfgFileName << endl;
return result;
}
} else {
- cerr << endl << "No ground_reactions element was found in the aircraft config file." << endl;
+ FGLogging log(Log, LogLevel::ERROR);
+ log << endl << "No ground_reactions element was found in the aircraft config file." << endl;
return false;
}
@@ -934,7 +952,8 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (element) {
result = Models[eExternalReactions]->Load(element);
if (!result) {
- cerr << endl << "Aircraft external_reactions element has problems in file " << aircraftCfgFileName << endl;
+ FGXMLLogging log(Log, element, LogLevel::ERROR);
+ log << endl << "Aircraft external_reactions element has problems in file " << aircraftCfgFileName << endl;
return result;
}
}
@@ -944,7 +963,8 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (element) {
result = Models[eBuoyantForces]->Load(element);
if (!result) {
- cerr << endl << "Aircraft buoyant_forces element has problems in file " << aircraftCfgFileName << endl;
+ FGXMLLogging log(Log, element, LogLevel::ERROR);
+ log << endl << "Aircraft buoyant_forces element has problems in file " << aircraftCfgFileName << endl;
return result;
}
}
@@ -954,7 +974,8 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (element) {
result = Propulsion->Load(element);
if (!result) {
- cerr << endl << "Aircraft propulsion element has problems in file " << aircraftCfgFileName << endl;
+ FGXMLLogging log(Log, element, LogLevel::ERROR);
+ log << endl << "Aircraft propulsion element has problems in file " << aircraftCfgFileName << endl;
return result;
}
for (unsigned int i=0; i < Propulsion->GetNumEngines(); i++)
@@ -966,7 +987,8 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
while (element) {
result = Models[eSystems]->Load(element);
if (!result) {
- cerr << endl << "Aircraft system element has problems in file " << aircraftCfgFileName << endl;
+ FGXMLLogging log(Log, element, LogLevel::ERROR);
+ log << endl << "Aircraft system element has problems in file " << aircraftCfgFileName << endl;
return result;
}
element = document->FindNextElement("system");
@@ -977,7 +999,8 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (element) {
result = Models[eSystems]->Load(element);
if (!result) {
- cerr << endl << "Aircraft autopilot element has problems in file " << aircraftCfgFileName << endl;
+ FGXMLLogging log(Log, element, LogLevel::ERROR);
+ log << endl << "Aircraft autopilot element has problems in file " << aircraftCfgFileName << endl;
return result;
}
}
@@ -987,7 +1010,8 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (element) {
result = Models[eSystems]->Load(element);
if (!result) {
- cerr << endl << "Aircraft flight_control element has problems in file " << aircraftCfgFileName << endl;
+ FGXMLLogging log(Log, element, LogLevel::ERROR);
+ log << endl << "Aircraft flight_control element has problems in file " << aircraftCfgFileName << endl;
return result;
}
}
@@ -997,11 +1021,13 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (element) {
result = Models[eAerodynamics]->Load(element);
if (!result) {
- cerr << endl << "Aircraft aerodynamics element has problems in file " << aircraftCfgFileName << endl;
+ FGXMLLogging log(Log, element, LogLevel::ERROR);
+ log << endl << "Aircraft aerodynamics element has problems in file " << aircraftCfgFileName << endl;
return result;
}
} else {
- cerr << endl << "No expected aerodynamics element was found in the aircraft config file." << endl;
+ FGLogging log(Log, LogLevel::ERROR);
+ log << endl << "No expected aerodynamics element was found in the aircraft config file." << endl;
}
// Process the input element. This element is OPTIONAL, and there may be more than one.
@@ -1028,7 +1054,8 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (element) {
result = ReadChild(element);
if (!result) {
- cerr << endl << "Aircraft child element has problems in file " << aircraftCfgFileName << endl;
+ FGXMLLogging log(Log, element, LogLevel::ERROR);
+ log << endl << "Aircraft child element has problems in file " << aircraftCfgFileName << endl;
return result;
}
}
@@ -1042,9 +1069,10 @@ bool FGFDMExec::LoadModel(const string& model, bool addModelToPath)
if (IsChild) debug_lvl = saved_debug_lvl;
} else {
- cerr << fgred
- << " JSBSim failed to open the configuration file: " << aircraftCfgFileName
- << fgdef << endl;
+ FGLogging log(Log, LogLevel::ERROR);
+ log << LogFormat::RED
+ << " JSBSim failed to open the configuration file: " << aircraftCfgFileName
+ << LogFormat::DEFAULT << endl;
}
for (unsigned int i=0; i< Models.size(); i++) LoadInputs(i);
@@ -1109,46 +1137,47 @@ string FGFDMExec::QueryPropertyCatalog(const string& in, const string& end_of_li
void FGFDMExec::PrintPropertyCatalog(void)
{
- cout << endl;
- cout << " " << fgblue << highint << underon << "Property Catalog for "
- << modelName << reset << endl << endl;
+ FGLogging log(Log, LogLevel::INFO);
+ log << endl
+ << " " << LogFormat::BLUE << highint << LogFormat::UNDERLINE_ON
+ << "Property Catalog for " << modelName << LogFormat::RESET << endl << endl;
for (auto &catalogElm: PropertyCatalog)
- cout << " " << catalogElm << endl;
+ log << " " << catalogElm << endl;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void FGFDMExec::PrintSimulationConfiguration(void) const
{
- cout << endl << "Simulation Configuration" << endl << "------------------------" << endl;
- cout << MassBalance->GetName() << endl;
- cout << GroundReactions->GetName() << endl;
- cout << Aerodynamics->GetName() << endl;
- cout << Propulsion->GetName() << endl;
+ FGLogging log(Log, LogLevel::INFO);
+ log << endl << "Simulation Configuration" << endl << "------------------------" << endl;
+ log << MassBalance->GetName() << endl;
+ log << GroundReactions->GetName() << endl;
+ log << Aerodynamics->GetName() << endl;
+ log << Propulsion->GetName() << endl;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
bool FGFDMExec::ReadFileHeader(Element* el)
{
- bool result = true; // true for success
-
- if (debug_lvl == 0) return result;
+ FGLogging log(Log, LogLevel::DEBUG);
if (IsChild) {
- cout << endl <FindElement("description"))
- cout << " Description: " << el->FindElement("description")->GetDataLine() << endl;
+ log << " Description: " << el->FindElement("description")->GetDataLine() << endl;
if (el->FindElement("author"))
- cout << " Model Author: " << el->FindElement("author")->GetDataLine() << endl;
+ log << " Model Author: " << el->FindElement("author")->GetDataLine() << endl;
if (el->FindElement("filecreationdate"))
- cout << " Creation Date: " << el->FindElement("filecreationdate")->GetDataLine() << endl;
+ log << " Creation Date: " << el->FindElement("filecreationdate")->GetDataLine() << endl;
if (el->FindElement("version"))
- cout << " Version: " << el->FindElement("version")->GetDataLine() << endl;
+ log << " Version: " << el->FindElement("version")->GetDataLine() << endl;
- return result;
+ return true;
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1162,50 +1191,61 @@ bool FGFDMExec::ReadPrologue(Element* el) // el for ReadPrologue is the document
string AircraftName = el->GetAttributeValue("name");
Aircraft->SetAircraftName(AircraftName);
- if (debug_lvl & 1) cout << underon << "Reading Aircraft Configuration File"
- << underoff << ": " << highint << AircraftName << normint << endl;
+ if (debug_lvl & 1) {
+ FGLogging log(Log, LogLevel::INFO);
+ log << LogFormat::UNDERLINE_ON << "Reading Aircraft Configuration File"
+ << LogFormat::UNDERLINE_OFF << ": " << LogFormat::BOLD << AircraftName
+ << LogFormat::NORMAL << endl;
+ }
CFGVersion = el->GetAttributeValue("version");
Release = el->GetAttributeValue("release");
- if (debug_lvl & 1)
- cout << " Version: " << highint << CFGVersion
- << normint << endl;
+ if (debug_lvl & 1) {
+ FGLogging log(Log, LogLevel::INFO);
+ log << " Version: "
+ << LogFormat::BOLD << CFGVersion << LogFormat::NORMAL << endl;
+ }
if (CFGVersion != needed_cfg_version) {
- cerr << endl << fgred << "YOU HAVE AN INCOMPATIBLE CFG FILE FOR THIS AIRCRAFT."
+ FGLogging log(Log, LogLevel::ERROR);
+ log << endl << LogFormat::RED << "YOU HAVE AN INCOMPATIBLE CFG FILE FOR THIS AIRCRAFT."
" RESULTS WILL BE UNPREDICTABLE !!" << endl;
- cerr << "Current version needed is: " << needed_cfg_version << endl;
- cerr << " You have version: " << CFGVersion << endl << fgdef << endl;
+ log << "Current version needed is: " << needed_cfg_version << endl;
+ log << " You have version: " << CFGVersion << endl << LogFormat::DEFAULT << endl;
return false;
}
if (Release == "ALPHA" && (debug_lvl & 1)) {
- cout << endl << endl
- << highint << "This aircraft model is an " << fgred << Release
- << reset << highint << " release!!!" << endl << endl << reset
- << "This aircraft model may not even properly load, and probably"
- << " will not fly as expected." << endl << endl
- << fgred << highint << "Use this model for development purposes ONLY!!!"
- << normint << reset << endl << endl;
+ FGLogging log(Log, LogLevel::DEBUG);
+ log << endl << endl
+ << LogFormat::BOLD << "This aircraft model is an " << LogFormat::RED << Release
+ << LogFormat::RESET << LogFormat::BOLD << " release!!!" << endl << endl << LogFormat::RESET
+ << "This aircraft model may not even properly load, and probably"
+ << " will not fly as expected." << endl << endl
+ << LogFormat::RED << LogFormat::BOLD << "Use this model for development purposes ONLY!!!"
+ << LogFormat::NORMAL << LogFormat::RESET << endl << endl;
} else if (Release == "BETA" && (debug_lvl & 1)) {
- cout << endl << endl
- << highint << "This aircraft model is a " << fgred << Release
- << reset << highint << " release!!!" << endl << endl << reset
- << "This aircraft model probably will not fly as expected." << endl << endl
- << fgblue << highint << "Use this model for development purposes ONLY!!!"
- << normint << reset << endl << endl;
+ FGLogging log(Log, LogLevel::DEBUG);
+ log << endl << endl
+ << LogFormat::BOLD << "This aircraft model is a " << LogFormat::RED << Release
+ << LogFormat::RESET << LogFormat::BOLD << " release!!!" << endl << endl << LogFormat::RESET
+ << "This aircraft model probably will not fly as expected." << endl << endl
+ << LogFormat::BLUE << LogFormat::BOLD << "Use this model for development purposes ONLY!!!"
+ << LogFormat::NORMAL << LogFormat::RESET << endl << endl;
} else if (Release == "PRODUCTION" && (debug_lvl & 1)) {
- cout << endl << endl
- << highint << "This aircraft model is a " << fgblue << Release
- << reset << highint << " release." << endl << endl << reset;
+ FGLogging log(Log, LogLevel::DEBUG);
+ log << endl << endl
+ << LogFormat::BOLD << "This aircraft model is a " << LogFormat::BLUE << Release
+ << LogFormat::RESET << LogFormat::BOLD << " release." << endl << endl << LogFormat::RESET;
} else if (debug_lvl & 1) {
- cout << endl << endl
- << highint << "This aircraft model is an " << fgred << Release
- << reset << highint << " release!!!" << endl << endl << reset
- << "This aircraft model may not even properly load, and probably"
- << " will not fly as expected." << endl << endl
- << fgred << highint << "Use this model for development purposes ONLY!!!"
- << normint << reset << endl << endl;
+ FGLogging log(Log, LogLevel::DEBUG);
+ log << endl << endl
+ << LogFormat::BOLD << "This aircraft model is an " << LogFormat::RED << Release
+ << LogFormat::RESET << LogFormat::BOLD << " release!!!" << endl << endl << LogFormat::RESET
+ << "This aircraft model may not even properly load, and probably"
+ << " will not fly as expected." << endl << endl
+ << LogFormat::RED << LogFormat::BOLD << "Use this model for development purposes ONLY!!!"
+ << LogFormat::NORMAL << LogFormat::RESET << endl << endl;
}
return result;
@@ -1244,17 +1284,19 @@ bool FGFDMExec::ReadChild(Element* el)
if (location) {
child->Loc = location->FindElementTripletConvertTo("IN");
} else {
- const string s(" No location was found for this child object!");
- cerr << el->ReadFrom() << endl << highint << fgred
- << s << reset << endl;
- throw BaseException(s);
+ FGXMLLogging log(Log, el, LogLevel::FATAL);
+ log << "No location was found for this child object!" << endl;
+ throw BaseException(log.str());
}
Element* orientation = el->FindElement("orient");
if (orientation) {
child->Orient = orientation->FindElementTripletConvertTo("RAD");
} else if (debug_lvl > 0) {
- cerr << endl << highint << " No orientation was found for this child object! Assuming 0,0,0." << reset << endl;
+ FGLogging log(Log, LogLevel::WARN);
+ log << endl << LogFormat::BOLD
+ << " No orientation was found for this child object! Assuming 0,0,0."
+ << LogFormat::RESET << endl;
}
ChildFDMList.push_back(child);
@@ -1341,7 +1383,7 @@ void FGFDMExec::SRand(int sr)
// variable is not set, debug_lvl is set to 1 internally
// 0: This requests JSBSim not to output any messages
// whatsoever.
-// 1: This value explicity requests the normal JSBSim
+// 1: This value explicitly requests the normal JSBSim
// startup messages
// 2: This value asks for a message to be printed out when
// a class is instantiated
@@ -1356,25 +1398,28 @@ void FGFDMExec::Debug(int from)
{
if (debug_lvl <= 0) return;
+ FGLogging log(Log, LogLevel::DEBUG);
+
if (debug_lvl & 1 && IdFDM == 0) { // Standard console startup message output
if (from == 0) { // Constructor
- cout << "\n\n "
- << "JSBSim Flight Dynamics Model v" << JSBSim_version << endl;
- cout << " [JSBSim-ML v" << needed_cfg_version << "]\n\n";
- cout << "JSBSim startup beginning ...\n\n";
- if (disperse == 1) cout << "Dispersions are ON." << endl << endl;
+ log << "\n\n "
+ << "JSBSim Flight Dynamics Model v" << JSBSim_version << endl;
+ log << " [JSBSim-ML v" << needed_cfg_version << "]\n\n";
+ log << "JSBSim startup beginning ...\n\n";
+ if (disperse == 1) log << "Dispersions are ON." << endl << endl;
} else if (from == 3) {
- cout << "\n\nJSBSim startup complete\n\n";
+ log << "\n\nJSBSim startup complete\n\n";
}
}
if (debug_lvl & 2 ) { // Instantiation/Destruction notification
- if (from == 0) cout << "Instantiated: FGFDMExec" << endl;
- if (from == 1) cout << "Destroyed: FGFDMExec" << endl;
+ if (from == 0) log << "Instantiated: FGFDMExec" << endl;
+ if (from == 1) log << "Destroyed: FGFDMExec" << endl;
}
if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
if (from == 2) {
- cout << "================== Frame: " << Frame << " Time: "
- << sim_time << " dt: " << dT << endl;
+ FGLogging log(Log, LogLevel::DEBUG);
+ log << "================== Frame: " << Frame << " Time: "
+ << sim_time << " dt: " << dT << endl;
}
}
if (debug_lvl & 8 ) { // Runtime state variables
diff --git a/src/FGFDMExec.h b/src/FGFDMExec.h
index 92fcdf16d..9de7568f6 100644
--- a/src/FGFDMExec.h
+++ b/src/FGFDMExec.h
@@ -69,6 +69,7 @@ class FGInertial;
class FGInput;
class FGPropulsion;
class FGMassBalance;
+class FGLogger;
class TrimFailureException : public BaseException {
public:
@@ -479,7 +480,7 @@ class JSBSIM_API FGFDMExec : public FGJSBBase
* You must trim first to get an accurate state-space model
*/
void DoLinearization(int);
-
+
/// Disables data logging to all outputs.
void DisableOutput(void) { Output->Disable(); }
/// Enables data logging to all outputs.
@@ -508,6 +509,9 @@ class JSBSIM_API FGFDMExec : public FGJSBBase
/// Sets the debug level.
void SetDebugLevel(int level) {debug_lvl = level;}
+ void SetLogger(std::shared_ptr logger) {Log = logger;}
+ std::shared_ptr GetLogger(void) const {return Log;}
+
struct PropertyCatalogStructure {
/// Name of the property.
std::string base_string;
@@ -693,6 +697,8 @@ class JSBSIM_API FGFDMExec : public FGJSBBase
std::vector > Models;
std::map TemplateFunctions;
+ std::shared_ptr Log;
+
bool ReadFileHeader(Element*);
bool ReadChild(Element*);
bool ReadPrologue(Element*);
diff --git a/src/input_output/CMakeLists.txt b/src/input_output/CMakeLists.txt
index 4f4122de0..c238f3e6f 100644
--- a/src/input_output/CMakeLists.txt
+++ b/src/input_output/CMakeLists.txt
@@ -15,7 +15,8 @@ set(SOURCES FGGroundCallback.cpp
FGInputType.cpp
FGInputSocket.cpp
FGUDPInputSocket.cpp
- string_utilities.cpp)
+ string_utilities.cpp
+ FGLog.cpp)
set(HEADERS FGGroundCallback.h
FGPropertyManager.h
@@ -35,7 +36,8 @@ set(HEADERS FGGroundCallback.h
FGModelLoader.h
FGInputType.h
FGInputSocket.h
- FGUDPInputSocket.h)
+ FGUDPInputSocket.h
+ FGLog.h)
add_library(InputOutput OBJECT ${HEADERS} ${SOURCES})
set_target_properties(InputOutput PROPERTIES TARGET_DIRECTORY
diff --git a/src/input_output/FGLog.cpp b/src/input_output/FGLog.cpp
new file mode 100644
index 000000000..2c7912eff
--- /dev/null
+++ b/src/input_output/FGLog.cpp
@@ -0,0 +1,124 @@
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ Module: FGOutputType.cpp
+ Author: Bertrand Coconnier
+ Date started: 05/03/24
+ Purpose: Manage output of sim parameters to file or stdout
+
+ ------------- Copyright (C) 2024 Bertrand Coconnier -------------
+
+ This program is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 2 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 Lesser General Public License for more
+ details.
+
+ You should have received a copy of the GNU Lesser General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Further information about the GNU Lesser General Public License can also be
+ found on the world wide web at http://www.gnu.org.
+
+FUNCTIONAL DESCRIPTION
+--------------------------------------------------------------------------------
+This is the place where you create output routines to dump data for perusal
+later.
+
+HISTORY
+--------------------------------------------------------------------------------
+05/03/24 BC Created
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+INCLUDES
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+#include "FGLog.h"
+#include "input_output/FGXMLElement.h"
+
+namespace JSBSim {
+
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+CLASS IMPLEMENTATION
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+void FGLogging::Flush(void)
+{
+ logger->Message(buffer.str());
+ buffer.str("");
+ logger->Format(LogFormat::RESET);
+ logger->Flush();
+ buffer.clear();
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+FGLogging& FGLogging::operator<<(LogFormat format) {
+ logger->Message(buffer.str());
+ buffer.str("");
+ logger->Format(format);
+ return *this;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+FGXMLLogging::FGXMLLogging(std::shared_ptr logger, Element* el, LogLevel level)
+ : FGLogging(logger, level)
+{
+ logger->FileLocation(el->GetFileName(), el->GetLineNumber());
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGLogConsole::SetLevel(LogLevel level) {
+ FGLogger::SetLevel(level);
+ switch (level)
+ {
+ case LogLevel::BULK:
+ case LogLevel::DEBUG:
+ case LogLevel::INFO:
+ out.tie(&std::cout);
+ break;
+ default:
+ out.tie(&std::cerr);
+ break;
+ }
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGLogConsole::Format(LogFormat format) {
+ switch (format)
+ {
+ case LogFormat::RED:
+ out << FGJSBBase::fgred;
+ break;
+ case LogFormat::BLUE:
+ out << FGJSBBase::fgblue;
+ break;
+ case LogFormat::BOLD:
+ out << FGJSBBase::highint;
+ break;
+ case LogFormat::NORMAL:
+ out << FGJSBBase::normint;
+ break;
+ case LogFormat::UNDERLINE_ON:
+ out << FGJSBBase::underon;
+ break;
+ case LogFormat::UNDERLINE_OFF:
+ out << FGJSBBase::underoff;
+ break;
+ case LogFormat::DEFAULT:
+ out << FGJSBBase::fgdef;
+ break;
+ case LogFormat::RESET:
+ default:
+ out << FGJSBBase::reset;
+ break;
+ }
+}
+}
diff --git a/src/input_output/FGLog.h b/src/input_output/FGLog.h
new file mode 100644
index 000000000..757a2659b
--- /dev/null
+++ b/src/input_output/FGLog.h
@@ -0,0 +1,158 @@
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ Header: FGLog.h
+ Author: Bertrand Coconnier
+ Date started: 05/03/24
+
+ ------------- Copyright (C) 2024 Bertrand Coconnier -------------
+
+ This program is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the Free
+ Software Foundation; either version 2 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 Lesser General Public License for more
+ details.
+
+ You should have received a copy of the GNU Lesser General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Further information about the GNU Lesser General Public License can also be
+ found on the world wide web at http://www.gnu.org.
+
+HISTORY
+--------------------------------------------------------------------------------
+05/03/24 BC Created
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+SENTRY
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+#ifndef FGLOG_H
+#define FGLOG_H
+
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+INCLUDES
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+#include
+#include
+#include
+#include
+#include
+
+#include "simgear/misc/sg_path.hxx"
+#include "FGJSBBase.h"
+
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+FORWARD DECLARATIONS
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+namespace JSBSim {
+
+class Element;
+
+// The return type of std::setprecision is unspecified by the C++ standard so we
+// need some C++ magic to be able to overload the operator<< for std::setprecision
+using setprecision_t = decltype(std::setprecision(0));
+
+enum class LogLevel {
+ BULK, // For frequent messages
+ DEBUG, // Less frequent debug type messages
+ INFO, // Informatory messages
+ WARN, // Possible impending problem
+ ERROR, // Problem that can be recovered
+ FATAL // Fatal problem => an exception will be thrown
+};
+
+enum class LogFormat {
+ RESET,
+ RED,
+ BLUE,
+ CYAN,
+ GREEN,
+ DEFAULT,
+ BOLD,
+ NORMAL,
+ UNDERLINE_ON,
+ UNDERLINE_OFF
+};
+
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+CLASS DOCUMENTATION
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+CLASS DECLARATION
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+class FGLogger
+{
+public:
+ virtual ~FGLogger() {}
+ virtual void SetLevel(LogLevel level) { level = level; }
+ virtual void FileLocation(const std::string& filename, int line) {}
+ void SetMinLevel(LogLevel level) { min_level = level; }
+ virtual void Message(const std::string& message) = 0;
+ virtual void Format(LogFormat format) {}
+ virtual void Flush(void) {}
+protected:
+ LogLevel level = LogLevel::BULK;
+ LogLevel min_level = LogLevel::INFO;
+};
+
+class FGLogging
+{
+public:
+ FGLogging(std::shared_ptr logger, LogLevel level)
+ : logger(logger)
+ { logger->SetLevel(level); }
+
+ virtual ~FGLogging() { Flush(); }
+ FGLogging& operator<<(const char* message) { buffer << message ; return *this; }
+ FGLogging& operator<<(const std::string& message) { buffer << message ; return *this; }
+ FGLogging& operator<<(unsigned int value) { buffer << value; return *this; }
+ FGLogging& operator<<(std::ostream& (*manipulator)(std::ostream&)) { buffer << manipulator; return *this; }
+ FGLogging& operator<<(setprecision_t value) { buffer << value; return *this; }
+ FGLogging& operator<<(const SGPath& path) { buffer << path; return *this; }
+ FGLogging& operator<<(LogFormat format);
+ std::string str(void) const { return buffer.str(); }
+ void Flush(void);
+protected:
+ std::shared_ptr logger;
+ std::ostringstream buffer;
+};
+
+class FGXMLLogging : public FGLogging
+{
+public:
+ FGXMLLogging(std::shared_ptr logger, Element* el, LogLevel level);
+};
+
+class FGLogConsole : public FGLogger
+{
+public:
+ FGLogConsole() : out(std::cout.rdbuf()) {}
+
+ void SetLevel(LogLevel level) override;
+ void FileLocation(const std::string& filename, int line) override
+ { out << std::endl << "In file " << filename << ": line" << line << std::endl; }
+ void Format(LogFormat format) override;
+ void Flush(void) override {
+ out.flush();
+ out.clear();
+ }
+
+ void Message(const std::string& message) override {
+ // if (level < min_level) return;
+ out << message;
+ }
+
+private:
+ std::ostream out;
+};
+} // namespace JSBSim
+#endif