///////////////////////////////////////////////////////////////////////////////////////////////////
// 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);
}
///////////////////////////////////////////////////////////////////////////////////////////////////