TestApp.cpp

Parent Previous Next

///////////////////////////////////////////////////////////////////////////////////////////////////

// TestApp.cpp

///////////////////////////////////////////////////////////////////////////////////////////////////


#include "stdafx.h"

#include "testapp.h"


#include <iostream>

#include <limits>

#include <signal.h>

#include <boost/filesystem.hpp> // Dictionaries manipulation


#include <codecvt>

#include <fcntl.h>

#include <io.h>

#include <tchar.h>


///////////////////////////////////////////////////////////////////////////////////////////////////


// Global pointer to DreamSolver (used for signal handling)  

static IDreamSolverPtr pTheOnlySolverInThisProcess = nullptr;


//-------------------------------------------------------------------------------------------------

 int _tmain(int argc, _TCHAR* argv[])

//-------------------------------------------------------------------------------------------------

{

 // Return value

 HRESULT hRes = S_OK;


 // Initialize COM subsystem

 hRes = CoInitialize(NULL);

 if(SUCCEEDED(hRes))

 {

   // Display info

   wprintf(L"##################################################\n");

   wprintf(L" DREAM Suite 1.0 - Plugin Test Application\n");

   wprintf(L"##################################################\n");

   wprintf(L"This application is intended for testing of DREAM plugin modules.\n");

   int iRunTest = GetUserChoice(L"Do you want to run the test? (Yes/No = 1/0) : ", 0, 1);

   if(iRunTest == 0)

     return 0;


   // We will assume that the current directory is "Project\Plugin\Bin"

   _bstr_t bstrPluginBin = boost::filesystem::current_path().c_str();


   // Path to the project directory

   _bstr_t bstrExampleDir = bstrPluginBin;

   bstrExampleDir += L"\\..\\..";


   // Path to Evaluator DLL

   _bstr_t bstrPluginDllName = L"$DREAM(ProjectName)";

   _bstr_t bstrEvaluatorPath = bstrPluginBin;

   bstrEvaluatorPath += L"\\";

   bstrEvaluatorPath += bstrPluginDllName;

   bstrEvaluatorPath += L".dll";


   // Path to directory with Example data

   _bstr_t bstrModelData = bstrExampleDir;

   bstrModelData += L"\\Model\\Data";


   // Path to directory with Example executables

   _bstr_t bstrModelBin = bstrExampleDir;

   bstrModelBin += L"\\Model\\Bin";


   // Path to working directory

   _bstr_t bstrWorkingDir = bstrExampleDir;

   bstrWorkingDir += L"\\Temp\\Simulations\\Case1";


   // Delete old working directory (to clear old files)

   DrxDeleteDirectory(std::wstring(bstrWorkingDir));

   // Create new working directory

   DrxCreateDirectory(std::wstring(bstrWorkingDir));


   // Calc observer for communication, logging...

   CCalcObserver calcObserver(bstrWorkingDir);


   /////////////////////////////////////////////////////////////////////////////////////////////////

   // Run DREAM Solver

   /////////////////////////////////////////////////////////////////////////////////////////////////

   bool bDisplayInfoAboutCalculationResults = false;

   bool bLogInfoAvailable = false;

   try

   {

     wprintf(L"Connecting DREAM Server...\n");


     // Try to create DreamSolver in COM way

     IDreamSolverPtr pSolver(__uuidof(IDreamSolver));

     // Check if the creation of solver succeeded

     if(pSolver != nullptr)

     {

       // Setup calculation observer

       IDreamObserverPtr pIDreamCalcObserver = nullptr;

       hRes = calcObserver.QueryInterface(IID_IDreamObserver, (void**)&pIDreamCalcObserver);

       if(SUCCEEDED(hRes))

       {

         // Establish communication with solver

         pSolver->AttachObserver(pIDreamCalcObserver);

         bLogInfoAvailable = true;

       }


       // Set directories

       pSolver->SetPath(ePathProjectDir, bstrExampleDir);

       pSolver->SetPath(ePathModelData, bstrModelData);

       pSolver->SetPath(ePathModelBin, bstrModelBin);

       pSolver->SetPath(ePathPluginBin, bstrPluginBin);

       pSolver->SetPath(ePathWorkingDir, bstrWorkingDir);


       // Load plugin module

       hRes = pSolver->LoadPlugin(bstrEvaluatorPath);

       if(SUCCEEDED(hRes))

       {

         // Plugin info

         CDreamPluginInfo pluginInfo;

         pluginInfo.m_PluginType = ePluginType::ePlugin_DLL;

         pluginInfo.m_strPluginName = bstrPluginDllName.copy();

         pluginInfo.m_strModelDataDir = bstrModelData.copy();


         // DREAM calculation parameters

         CDreamCalcParams calcParamInfo;

         // Convergence diagnostics?

         calcParamInfo.m_SaveWithinChainDiagnostics = GetUserChoice(L"Do you want convergence diagnostics?  (Yes/No = 1/0) : ", 0, 1);

         // Export Markov chains?

         calcParamInfo.m_SaveMarkovChains = GetUserChoice(L"Do you want to export Markov chains?  (Yes/No = 1/0) : ", 0, 1);

         // Number of cores

         calcParamInfo.m_NumberOfCPUs = GetUserChoice(L"Number of cores <1,20>, N > 1 = parallel calculation : ", 1, 20);

         calcParamInfo.m_RunParallel = calcParamInfo.m_NumberOfCPUs > 1 ? 1 : 0;


         // Initialize the evaluator and all input data by values obtained from the evaluator

         hRes = pSolver->InitEvaluatorDef(&pluginInfo, &calcParamInfo);

         if(SUCCEEDED(hRes))

         {

           // Get pointer to evaluator

           IDreamEvaluatorPtr pEvaluator = nullptr;

           hRes = pSolver->GetEvaluator(&pEvaluator);

           if(SUCCEEDED(hRes))

           {

             // Set signal handler (Ctrl+C)

             pTheOnlySolverInThisProcess = pSolver;

             SetConsoleCtrlHandler((PHANDLER_ROUTINE)SignalHandler, TRUE);


             // Prepare the console to display calculation progress (%)  

             wprintf(L"Starting calculation...\n");

             wprintf(L"(press Ctrl+C to interrupt the calculation)\n");


             // Perform calculations

             hRes = pSolver->StartCalculation();

             if(SUCCEEDED(hRes))

             {

               wprintf(L"Calculation succeeded.\n");

             }

             else

             {

               // Check errors

               _bstr_t message;

               CDreamErrorInfo lastErrorInfo;

               if(pSolver->GetLastError(&lastErrorInfo) != S_OK && lastErrorInfo.GetErrType() == eTypeError)

                 message = L"Calculation failed with error:\n" + _bstr_t(lastErrorInfo.GetErrDescription()) + L"\n";

               else

                 message = L"Calculation failed.\n";


               wprintf(message);

             }

             bDisplayInfoAboutCalculationResults = true;

           }


           // Failed to get pointer to evaluator

           else

           {

             wprintf(L"Failed to get pointer to evaluator.\n");

           }

         }


         // Failed to initialize evaluator

         else

         {

           wprintf(L"Failed to initialize evaluator.\n");

         }

       }


       // Failed to load plugin module

       else

       {

         wprintf(L"Failed to load plugin module.\n");

       }

     }


     // Failed to create Dream solver

     else

     {

       wprintf(L"Failed to create Dream solver.\n");

     }

   }

   catch(_com_error& e)

   {

     // Dream solver creation failed

     std::wstring message = L"COM error: ";

     message += e.ErrorMessage();

     message += L"\n";

     PrintMessageUTF8(message);

   }


   // Clean-up

   if(pTheOnlySolverInThisProcess != nullptr)

   {

     // Release solver interface - it must be done here, because it fails after CoUnitialize()

     pTheOnlySolverInThisProcess = nullptr;

   }


   // Output information

   if(bDisplayInfoAboutCalculationResults)

   {

     // Output files

     wprintf(L"Output and log files are available in directory:\n" + bstrWorkingDir + L"\n");

   }


   // Error info

   if(bLogInfoAvailable)

   {

     // Number of errors and warnings returned by the solver

     int nErrors = calcObserver.GetNoOfErrors();

     int nWarnings = calcObserver.GetNoOfWarnings();

     wprintf(L"Number of errors: %d, Number of warnings: %d\n", nErrors, nWarnings);


     // Print logged messages (errors, warnings, messages and calculation time)?

     int iPrintLogInfo = GetUserChoice(L"Do you want to display log-file content? (Yes/No = 1/0) : ", 0, 1);

     if(iPrintLogInfo == 1)

     {

       wprintf(L"Log file contents:\n");

       calcObserver.PrintMessages();

     }

   }


   // Clean-up COM services

   CoUninitialize();

 }


 // Wait for ENTER

 std::wcin.ignore(1024, '\n');

 std::cout << "\nPress Enter to continue...";

 std::wcin.get();


 // Return result

 return hRes;

}


///////////////////////////////////////////////////////////////////////////////////////////////////

// CCalcObserver


//-------------------------------------------------------------------------------------------------

 CCalcObserver::CCalcObserver()

//-------------------------------------------------------------------------------------------------

 : m_cRef(1)

 , m_iCurrentStep(0)

 , m_fConvergenceInfo(0.0f)

 , m_iNoOfErrors(0)

 , m_iNoOfWarnings(0)

 ///////////////////////////////////////////////////////////////////////////////////////////////////

{

 // Path to logfile

 m_LogFile = boost::filesystem::current_path().c_str();

 m_LogFile += "\\DREAM_Out_LogObs.txt";

}


//-------------------------------------------------------------------------------------------------

 CCalcObserver::CCalcObserver(_bstr_t path)

//-------------------------------------------------------------------------------------------------

 : m_cRef(1)

 , m_iCurrentStep(0)

 , m_fConvergenceInfo(0.f)

 , m_iNoOfErrors(0)

 , m_iNoOfWarnings(0)

 ///////////////////////////////////////////////////////////////////////////////////////////////////

{

 // Path to logfile

 m_LogFile = path;

 m_LogFile += "\\DREAM_Out_LogObs.txt";

}


//-------------------------------------------------------------------------------------------------

 CCalcObserver::~CCalcObserver()

//-------------------------------------------------------------------------------------------------

{

}


//-------------------------------------------------------------------------------------------------

 HRESULT STDMETHODCALLTYPE CCalcObserver::OnLogMessage(BSTR strLogMessage)

//-------------------------------------------------------------------------------------------------

{

 // Use stream to log messages to a file

 std::wofstream fs(m_LogFile.GetBSTR(), std::ofstream::out | std::ofstream::app);

 std::locale loc(std::locale::classic(), new std::codecvt_utf8<wchar_t>);

 fs.imbue(loc);


 // Write message to stream

 fs << strLogMessage << L"\n";


 // Write

 fs.flush();

 fs.close();


 return S_OK;

}


//-------------------------------------------------------------------------------------------------

 HRESULT STDMETHODCALLTYPE CCalcObserver::OnError(IDreamErrorInfo* pErrorInfo)

//-------------------------------------------------------------------------------------------------

{

 // Check input parameter

 if(pErrorInfo == nullptr)

 {

   return E_INVALIDARG;

 }


 // Use stream to log messages to a file

 std::wofstream fs(m_LogFile.GetBSTR(), std::ofstream::out | std::ofstream::app);


 // String for message

 std::wstring message;


 if(pErrorInfo->_type == eTypeWarning)

 {

   message = L"Warning No. ";

   m_iNoOfWarnings++;

 }

 else

 {

   message = L"Error No. ";

   m_iNoOfErrors++;

 }


 // Write message to stream

 fs << message << pErrorInfo->_errorID << " - " << pErrorInfo->_errorDesc << "\n";


 // Write

 fs.flush();

 fs.close();


 return S_OK;

}


//-------------------------------------------------------------------------------------------------

 HRESULT STDMETHODCALLTYPE CCalcObserver::SetCurrentStep(float fStep)

//-------------------------------------------------------------------------------------------------

{

 m_iCurrentStep = (int)fStep;

 std::cout << "Finished: " << m_iCurrentStep << " %          " << "\r";

 return S_OK;

}


//-------------------------------------------------------------------------------------------------

 HRESULT STDMETHODCALLTYPE CCalcObserver::SetConvergenceInfo(float fInfo)

//-------------------------------------------------------------------------------------------------

{

 m_fConvergenceInfo = fInfo;

 return S_OK;

}


//-------------------------------------------------------------------------------------------------

 HRESULT STDMETHODCALLTYPE CCalcObserver::OnCalculationTerminatedByUser()

//-------------------------------------------------------------------------------------------------

{

 // Use stream to write info to a file

 std::wofstream fs(m_LogFile.GetBSTR(), std::ofstream::out | std::ofstream::app);


 // Write message to stream

 fs << "Calculation was terminated by user. Please wait..." << "\n";


 // Write

 fs.flush();

 fs.close();


 return S_OK;

}


//-------------------------------------------------------------------------------------------------

 HRESULT STDMETHODCALLTYPE CCalcObserver::CalculationTerminatedByUser()

//-------------------------------------------------------------------------------------------------

{

 // In this application, calculation is terminated directly by calling

 // pTheOnlySolverInThisProcess->BreakCalculation();

 // Terminating the calculation via observer is important when the calculation runs

 // in a worker thread and solver cannot be accessed from GUI.  

 return S_FALSE;

}


//-------------------------------------------------------------------------------------------------

 HRESULT STDMETHODCALLTYPE CCalcObserver::OnPreparingOutputData()

//-------------------------------------------------------------------------------------------------

{

 wprintf(L"Calculation finished, preparing output data...\n");

 return S_OK;

}


//-------------------------------------------------------------------------------------------------

 HRESULT STDMETHODCALLTYPE CCalcObserver::OnCalculationFinished()

//-------------------------------------------------------------------------------------------------

{

 // Use stream to write info to a file

 std::wofstream fs(m_LogFile.GetBSTR(), std::ofstream::out | std::ofstream::app);


 // Write message to stream

 fs << "Calculation finished." << "\n";


 // Write

 fs.flush();

 fs.close();


 return S_OK;

}


//-------------------------------------------------------------------------------------------------

 void CCalcObserver::PrintMessages() const

//-------------------------------------------------------------------------------------------------

{

 // Get messages from the log file

 _bstr_t ff = m_LogFile;

 std::wifstream fs(ff.GetBSTR(), std::ofstream::in | std::ofstream::app);

 std::locale loc(std::locale::classic(), new std::codecvt_utf8<wchar_t>);

 fs.imbue(loc);


 // Print all messages to std output (convert to UTF8)

 UINT oldcp = GetConsoleOutputCP();

 SetConsoleOutputCP(CP_UTF8);

 std::wstring wsLine;

 while (std::getline(fs, wsLine))

 {

   wsLine += L"\n";

   std::string utf8str = DrxWStr2Str(wsLine);

   printf(utf8str.c_str());

 }


 fs.close();

 SetConsoleOutputCP(oldcp);

}


//-------------------------------------------------------------------------------------------------

 HRESULT STDMETHODCALLTYPE CCalcObserver::QueryInterface(const IID& iid, void** ppv)

//-------------------------------------------------------------------------------------------------

{

 // Check parameters

 if(ppv == nullptr)

 {

   // Invalid argument

   return E_INVALIDARG;

 }


 // Always set pointer to nullptr

 *ppv = nullptr;


 if(InlineIsEqualGUID(iid, IID_IUnknown) || InlineIsEqualGUID(iid, IID_IDreamObserver))

 {

   // Return pointer to IDreamObserver interface

   *ppv = static_cast<IDreamObserver*>(this);

 }

 else

 {

   // Unimplemented interface

   *ppv = nullptr;

   return E_NOINTERFACE;

 }


 // Increment reference count

 reinterpret_cast<IUnknown*>(*ppv)->AddRef();

 return S_OK;

}


//-------------------------------------------------------------------------------------------------

 ULONG STDMETHODCALLTYPE CCalcObserver::AddRef()

//-------------------------------------------------------------------------------------------------

{

 // Increment reference count

 return ::InterlockedIncrement(&m_cRef);

}


//-------------------------------------------------------------------------------------------------

 ULONG STDMETHODCALLTYPE CCalcObserver::Release()

//-------------------------------------------------------------------------------------------------

{

 // Decrement reference count

 if(::InterlockedDecrement(&m_cRef) == 0)

 {

   // Object is not referenced, it is safe to delete it

   delete this;

   return 0;

 }


 // Return actual reference count

 return m_cRef;

}


///////////////////////////////////////////////////////////////////////////////////////////////////

// Help functions


//-------------------------------------------------------------------------------------------------

 BOOL SignalHandler(DWORD fdwCtrlType)

//-------------------------------------------------------------------------------------------------

// Signal handler - Ctrl+C

{

 switch (fdwCtrlType)

 {

   // Handle the CTRL-C signal

   case CTRL_C_EVENT:

   {

     if(pTheOnlySolverInThisProcess != nullptr)

     {

       pTheOnlySolverInThisProcess->BreakCalculation();

       wprintf(L"Calculation interrupted by user!\n");

       wprintf(L"Terminating process...\n");

     }

     return TRUE;

   }

   default:

     return FALSE;

 }

}


//-------------------------------------------------------------------------------------------------

 int GetUserChoice(std::wstring wsPromptLine, int iMinNo, int iMaxNo)

//-------------------------------------------------------------------------------------------------

// Get user's choice (an integer number from interval <iMinNo, iMaxNo>)  

{

 int iUserChoice = -1;

 do

 {

   // Display the prompt line

   wprintf(wsPromptLine.c_str());


   // Read user's input

   if(scanf(" %d", &iUserChoice) > 0)

   {

     // Check whether the number is correct

     if(iUserChoice >= iMinNo && iUserChoice <= iMaxNo)

       return iUserChoice;

   }


   // Failed

   wprintf(L"Incorrect input.\n");

   // Needed

   getchar();

 } while (1);


 // Wrong choice

 return -1;

}


//-------------------------------------------------------------------------------------------------

 void PrintMessageUTF8(const std::wstring sLine)

//-------------------------------------------------------------------------------------------------

{

 // Print a message converted to UTF8

 UINT oldcp = GetConsoleOutputCP();

 SetConsoleOutputCP(CP_UTF8);

 std::string utf8str = DrxWStr2Str(sLine);

 printf(utf8str.c_str());

 SetConsoleOutputCP(oldcp);

}


///////////////////////////////////////////////////////////////////////////////////////////////////