Updated German translation
[dasher.git] / Src / DasherCore / UserLog.cpp
blobfd4707180f2213347ebf8608132a7cb99c2f72d6
1 #include "../Common/Common.h"
3 #include "UserLog.h"
4 #include <fstream>
5 #include <cstring>
7 #ifdef _WIN32
8 #include <sys/timeb.h>
9 #else
10 #include <sys/time.h>
11 #endif
13 // Track memory leaks on Windows to the line that new'd the memory
14 #ifdef _WIN32
15 #ifdef _DEBUG_MEMLEAKS
16 #define DEBUG_NEW new( _NORMAL_BLOCK, THIS_FILE, __LINE__ )
17 #define new DEBUG_NEW
18 #undef THIS_FILE
19 static char THIS_FILE[] = __FILE__;
20 #endif
21 #endif
23 using namespace Dasher;
24 using namespace Dasher::Settings;
26 static UserLogParamMask s_UserLogParamMaskTable [] = {
27 {SP_ALPHABET_ID, userLogParamOutputToSimple},
28 {SP_COLOUR_ID, userLogParamOutputToSimple},
29 {LP_MAX_BITRATE, userLogParamOutputToSimple |
30 userLogParamTrackMultiple |
31 userLogParamTrackInTrial |
32 userLogParamForceInTrial |
33 userLogParamShortInCycle},
34 {BP_CONTROL_MODE, userLogParamOutputToSimple},
35 {LP_UNIFORM, userLogParamOutputToSimple},
36 {LP_YSCALE, userLogParamOutputToSimple},
37 {LP_LANGUAGE_MODEL_ID, userLogParamOutputToSimple},
38 {LP_LM_MAX_ORDER, userLogParamOutputToSimple},
39 {LP_LM_EXCLUSION, userLogParamOutputToSimple},
40 {LP_LM_UPDATE_EXCLUSION, userLogParamOutputToSimple},
41 {LP_LM_ALPHA, userLogParamOutputToSimple},
42 {LP_LM_BETA, userLogParamOutputToSimple},
43 {LP_LM_MIXTURE, userLogParamOutputToSimple},
44 {LP_LM_WORD_ALPHA, userLogParamOutputToSimple},
45 {-1, -1} // Flag value that should always be at the end
48 CUserLog::CUserLog(CSettingsUser *pCreateFrom,
49 Observable<const CEditEvent *> *pObsv, int iLogTypeMask)
50 : CUserLogBase(pObsv), CSettingsUserObserver(pCreateFrom) {
51 //CFunctionLogger f1("CUserLog::CUserLog", g_pLogger);
53 InitMemberVars();
55 m_iLevelMask = iLogTypeMask;
57 InitUsingMask(iLogTypeMask);
59 if ((m_bSimple) && (m_pSimpleLogger != NULL))
60 m_pSimpleLogger->Log("start, %s", logDEBUG, GetVersionInfo().c_str());
62 SetOuputFilename();
63 m_pApplicationSpan = new CTimeSpan("Application", true);
65 if (m_pApplicationSpan == NULL)
66 g_pLogger->Log("CUserLog::CUserLog, failed to create m_pApplicationSpan!", logNORMAL);
68 // TODO: for the load test harness, we apparently need to create the object directly
69 // without a settings store (which will break CSettingsObserver, etc.); and then,
70 // don't call the following:
71 AddInitialParam();
74 CUserLog::~CUserLog()
76 //CFunctionLogger f1("CUserLog::~CUserLog", g_pLogger);
78 if ((m_bSimple) && (m_pSimpleLogger != NULL))
79 m_pSimpleLogger->Log("stop", logDEBUG);
81 if (m_pApplicationSpan != NULL)
83 delete m_pApplicationSpan;
84 m_pApplicationSpan = NULL;
87 for (unsigned int i = 0; i < m_vpTrials.size(); i++)
89 CUserLogTrial* pTrial = (CUserLogTrial*) m_vpTrials[i];
91 if (pTrial != NULL)
93 delete pTrial;
94 pTrial = NULL;
98 for (unsigned int i = 0; i < m_vParams.size(); i++)
100 CUserLogParam* pParam = (CUserLogParam*) m_vParams[i];
102 if (pParam != NULL)
104 delete pParam;
105 pParam = NULL;
109 if (m_pSimpleLogger != NULL)
111 delete m_pSimpleLogger;
112 m_pSimpleLogger = NULL;
115 if (m_pCycleTimer != NULL)
117 delete m_pCycleTimer;
118 m_pCycleTimer = NULL;
122 // Do initialization of member variables based on the user log level mask
123 void CUserLog::InitUsingMask(int iLogLevelMask)
125 //CFunctionLogger f1("CUserLog::InitUsingMask", g_pLogger);
127 m_bInitIsDone = false;
129 if (iLogLevelMask & userLogSimple)
131 // First we check to see if the file exists, if it does not
132 // then we want to force all parameter values to be sent to
133 // the log file even before InitIsDone() is called.
134 FILE* fp = fopen(USER_LOG_SIMPLE_FILENAME.c_str(), "r");
135 if (fp == NULL)
137 m_bInitIsDone = true;
139 else
141 fclose(fp);
142 fp = NULL;
145 m_bSimple = true;
147 if (m_pSimpleLogger == NULL)
148 m_pSimpleLogger = new CFileLogger(USER_LOG_SIMPLE_FILENAME, logDEBUG, logTimeStamp | logDateStamp);
151 if (iLogLevelMask & userLogDetailed)
152 m_bDetailed = true;
156 // Called when we want to output the log file (usually on exit of dasher)
157 void CUserLog::OutputFile()
159 //CFunctionLogger f1("CUserLog::OutputFile", g_pLogger);
161 if (m_bDetailed)
163 // Let the last pTrial object know we are done with it, this lets it do
164 // any final calculations.
165 if (m_vpTrials.size() > 0)
167 CUserLogTrial* pTrial = m_vpTrials[m_vpTrials.size() - 1];
169 if (pTrial != NULL)
170 pTrial->Done();
173 // Output our data to an XML file before we destruct
174 WriteXML();
178 void CUserLog::StartWriting()
180 //CFunctionLogger f1("CUserLog::StartWriting", g_pLogger);
182 if (m_bSimple)
184 // The canvas size changes multiple times as a user resizes it. We just want to write
185 // one short log entry for the final position the next time they start writing.
186 if ((m_bNeedToWriteCanvas) && (m_pSimpleLogger != NULL))
188 m_pSimpleLogger->Log("canvas:\t%d\t%d\t%d\t%d",
189 logDEBUG,
190 m_sCanvasCoordinates.top,
191 m_sCanvasCoordinates.left,
192 m_sCanvasCoordinates.bottom,
193 m_sCanvasCoordinates.right);
194 m_bNeedToWriteCanvas = false;
197 // We log what happened between StartWriting() and StopWriting()
198 // so clear out any previous history.
199 ResetCycle();
202 if (m_bDetailed)
204 CUserLogTrial* pTrial = GetCurrentTrial();
206 // This could be the first use in this pTrial, create a new one if needed
207 if (pTrial == NULL)
208 pTrial = AddTrial();
210 if (pTrial != NULL)
211 pTrial->StartWriting();
212 else
213 g_pLogger->Log("CUserLog::StartWriting, failed to create new pTrial!", logNORMAL);
216 m_bIsWriting = true;
219 // This version should be called at the end of navigation with the dNats
220 // value under the current mouse position. This would be more accurate
221 // then the last value from a mouse event since some time may have
222 // elapsed.
223 void CUserLog::StopWriting(float dNats)
225 //CFunctionLogger f1("CUserLog::StopWriting", g_pLogger);
227 if (m_bIsWriting)
229 m_dCycleNats = (double) dNats;
230 StopWriting();
234 void CUserLog::StopWriting()
236 //CFunctionLogger f1("CUserLog::StopWriting", g_pLogger);
238 if (m_bIsWriting)
240 m_bIsWriting = false;
242 // In simple logging mode, we'll output the stats for this navigation cycle
243 if ((m_bSimple) && (m_pSimpleLogger != NULL))
244 m_pSimpleLogger->Log("%s", logDEBUG, GetStartStopCycleStats().c_str());
246 if (m_bDetailed)
248 CUserLogTrial* pTrial = GetCurrentTrial();
250 if (pTrial == NULL)
252 g_pLogger->Log("CUserLog::StopWriting, pTrial was NULL!", logNORMAL);
253 return;
256 pTrial->StopWriting(GetCycleBits());
261 void CUserLog::AddSymbols(Dasher::VECTOR_SYMBOL_PROB* vpNewSymbols, eUserLogEventType iEvent)
263 //CFunctionLogger f1("CUserLog::AddSymbols", g_pLogger);
265 if (!m_bIsWriting)
267 // StartWriting() wasn't called, so we'll do it implicitly now
268 g_pLogger->Log("CUserLog::AddSymbols, StartWriting() not called?", logDEBUG);
269 StartWriting();
272 if (vpNewSymbols == NULL)
274 g_pLogger->Log("CUserLog::AddSymbols, vpNewSymbols was NULL!", logNORMAL);
275 return;
278 if (m_bSimple)
280 // Also store a copy in a vector that gets cleared
281 // time StartWriting() is called.
282 m_vCycleHistory.insert(m_vCycleHistory.end(), vpNewSymbols->begin(), vpNewSymbols->end());
285 if (m_bDetailed)
287 CUserLogTrial* pTrial = GetCurrentTrial();
289 // We should have a pTrial object since StartWriting() should have been called before us
290 if (pTrial == NULL)
292 g_pLogger->Log("CUserLog::AddSymbols, pTrial was NULL!", logNORMAL);
293 return;
296 pTrial->AddSymbols(vpNewSymbols, iEvent);
300 void CUserLog::DeleteSymbols(int iNumToDelete, eUserLogEventType iEvent)
302 //CFunctionLogger f1("CUserLog::DeleteSymbols", g_pLogger);
304 if (iNumToDelete <= 0)
305 return;
307 if (!m_bIsWriting)
309 // StartWriting() wasn't called, so we'll do it implicitly now
310 g_pLogger->Log("CUserLog::DeleteSymbols, StartWriting() not called?", logDEBUG);
311 StartWriting();
314 if (m_bSimple)
316 m_iCycleNumDeletes += iNumToDelete;
318 // Be careful not to pop more things than we have (this will hork the
319 // memory up on linux but not windows).
320 int iActualNumToDelete = min((int) m_vCycleHistory.size(), iNumToDelete);
321 for (int i = 0; i < iActualNumToDelete; i++)
322 m_vCycleHistory.pop_back();
325 if (m_bDetailed)
327 CUserLogTrial* pTrial = GetCurrentTrial();
329 // We should have a pTrial object since StartWriting() should have been called before us
330 if (pTrial == NULL)
332 g_pLogger->Log("CUserLog::DeleteSymbols, pTrial was NULL!", logNORMAL);
333 return;
336 pTrial->DeleteSymbols(iNumToDelete, iEvent);
340 void CUserLog::NewTrial()
342 //CFunctionLogger f1("CUserLog::NewTrial", g_pLogger);
344 if (m_bIsWriting)
346 // We should have called StopWriting(), but we'll do it here implicitly
347 g_pLogger->Log("CUserLog::NewTrial, StopWriting() not called?", logDEBUG);
348 StopWriting();
351 // For safety we can dump the XML to file after each pTrial is done. This
352 // might be a good idea for long user pTrial sessions just in case Dasher
353 // were to do something completely crazy like crash.
354 if (USER_LOG_DUMP_AFTER_TRIAL)
355 WriteXML();
357 if (m_bDetailed)
359 CUserLogTrial* pTrial = GetCurrentTrial();
361 if (pTrial == NULL)
363 // Not an error, they may just hit new doc before anything else at start
364 return;
367 if (pTrial->HasWritingOccured())
369 // Create a new pTrial if the existing one has already been used
370 pTrial = AddTrial();
375 // Overloaded version that converts a double to a string
376 void CUserLog::AddParam(const string& strName, double dValue, int iOptionMask)
378 sprintf(m_szTempBuffer, "%0.4f", dValue);
379 AddParam(strName, m_szTempBuffer, iOptionMask);
382 // Overloaded version that converts a int to a string
383 void CUserLog::AddParam(const string& strName, int iValue, int iOptionMask)
385 sprintf(m_szTempBuffer, "%d", iValue);
386 AddParam(strName, m_szTempBuffer, iOptionMask);
389 // Adds a general parameter to our XML. This lets various Dasher components
390 // record what they are set at. Optionally can be set to track multiple
391 // values for the same parameter or to always output a line to the simple
392 // log file when the parameter is set.
393 void CUserLog::AddParam(const string& strName, const string& strValue, int iOptionMask)
395 //CFunctionLogger f1("CUserLog::AddParam", g_pLogger);
397 bool bOutputToSimple = false;
398 bool bTrackMultiple = false;
399 bool bTrackInTrial = false;
400 bool bForceInTrial = false;
401 bool bShortInCycle = false;
403 if (iOptionMask & userLogParamOutputToSimple)
404 bOutputToSimple = true;
405 if (iOptionMask & userLogParamTrackMultiple)
406 bTrackMultiple = true;
407 if (iOptionMask & userLogParamTrackInTrial)
408 bTrackInTrial = true;
409 if (iOptionMask & userLogParamForceInTrial)
410 bForceInTrial = true;
411 if (iOptionMask & userLogParamShortInCycle)
412 bShortInCycle = true;
414 // See if we want to immediately output this name/value pair to
415 // the running simple log file. If we are tracking the parameter
416 // in the short cycle stats line, then don't output here.
417 if ((bOutputToSimple) &&
418 (m_bSimple) &&
419 (m_pSimpleLogger != NULL) &&
420 (m_bInitIsDone) &&
421 (!bShortInCycle))
423 m_pSimpleLogger->Log("%s = %s", logDEBUG, strName.c_str(), strValue.c_str());
426 // See if this matches an existing parameter value that we may want to
427 // overwrite. But only if we aren't suppose to keep track of multiple changes.
428 if (!bTrackMultiple)
430 for (unsigned int i = 0; i < m_vParams.size(); i++)
432 CUserLogParam* pParam = (CUserLogParam*) m_vParams[i];
434 if (pParam != NULL)
436 if (pParam->strName.compare(strName) == 0)
438 pParam->strValue = strValue;
439 return;
445 // We need to add a new param
446 CUserLogParam* pNewParam = new CUserLogParam;
448 if (pNewParam == NULL)
450 g_pLogger->Log("CUserLog::AddParam, failed to create CUserLogParam object!", logNORMAL);
451 return;
454 pNewParam->strName = strName;
455 pNewParam->strValue = strValue;
456 pNewParam->strTimeStamp = "";
457 pNewParam->options = iOptionMask;
459 // Parameters that can have multiple values logged will also log when they were changed
460 if (bTrackMultiple)
461 pNewParam->strTimeStamp = CTimeSpan::GetTimeStamp();
463 m_vParams.push_back(pNewParam);
465 if ((bTrackInTrial) && (m_bDetailed))
467 // See if we need to pass the parameter onto the current pTrial object
468 CUserLogTrial* pTrial = GetCurrentTrial();
470 if (pTrial != NULL)
471 pTrial->AddParam(strName, strValue, iOptionMask);
476 // Adds a new point in our tracking of mouse locations
477 void CUserLog::AddMouseLocation(int iX, int iY, float dNats)
479 //CFunctionLogger f1("CUserLog::AddMouseLocation", g_pLogger);
481 // Check to see if it is time to actually push a mouse location update
482 if (UpdateMouseLocation())
484 if (m_bDetailed)
486 CUserLogTrial* pTrial = GetCurrentTrial();
488 if (pTrial == NULL)
490 // Only track during an actual pTrial
491 return;
494 // Only record mouse locations during navigation
495 if (pTrial->IsWriting())
496 pTrial->AddMouseLocation(iX, iY, dNats);
499 // Keep track of the dNats for the current mouse position
500 if (m_bIsWriting)
501 m_dCycleNats = dNats;
505 // Adds the size of the current window
506 void CUserLog::AddWindowSize(int iTop, int iLeft, int iBottom, int iRight)
508 //CFunctionLogger f1("CUserLog::AddWindowSize", g_pLogger);
510 m_sWindowCoordinates.top = iTop;
511 m_sWindowCoordinates.left = iLeft;
512 m_sWindowCoordinates.bottom = iBottom;
513 m_sWindowCoordinates.right = iRight;
515 if (m_bDetailed)
517 CUserLogTrial* pTrial = GetCurrentTrial();
519 if (pTrial == NULL)
521 // Only track during an actual pTrial
522 return;
525 pTrial->AddWindowSize(iTop, iLeft, iBottom, iRight);
529 // Adds the size of the current canvas, this should be called when our
530 // window is initially created and whenever somebody mucks with the
531 // size.
532 void CUserLog::AddCanvasSize(int iTop, int iLeft, int iBottom, int iRight)
534 //CFunctionLogger f1("CUserLog::AddCanvasSize", g_pLogger);
536 // Only log to simple log object if the coordinates are different from
537 // what we had prior to now.
538 if ((m_bSimple) &&
539 (m_pSimpleLogger != NULL) &&
540 ((m_sCanvasCoordinates.top != iTop) ||
541 (m_sCanvasCoordinates.left != iLeft) ||
542 (m_sCanvasCoordinates.bottom != iBottom) ||
543 (m_sCanvasCoordinates.right != iRight)))
544 m_bNeedToWriteCanvas = true;
546 m_sCanvasCoordinates.top = iTop;
547 m_sCanvasCoordinates.left = iLeft;
548 m_sCanvasCoordinates.bottom = iBottom;
549 m_sCanvasCoordinates.right = iRight;
551 if (m_bDetailed)
553 CUserLogTrial* pTrial = GetCurrentTrial();
555 if (pTrial == NULL)
557 // Only track during an actual pTrial
558 return;
561 pTrial->AddCanvasSize(iTop, iLeft, iBottom, iRight);
565 // We may want to use a noramlized version of mouse coordinates, this way it is
566 // invariant to changes in the window size before, during, or after navigation.
567 // The caller must send us both the x, y coordinates and the current window size.
568 void CUserLog::AddMouseLocationNormalized(int iX, int iY, bool bStoreIntegerRep, float dNats)
570 //CFunctionLogger f1("CUserLog::AddMouseLocationNormalized", g_pLogger);
572 // Check to see if it is time to actually push a mouse location update
573 if (UpdateMouseLocation())
575 if ((m_sCanvasCoordinates.bottom == 0) &&
576 (m_sCanvasCoordinates.left == 0) &&
577 (m_sCanvasCoordinates.right == 0) &&
578 (m_sCanvasCoordinates.top == 0))
580 g_pLogger->Log("CUserLog::AddMouseLocationNormalized, called before AddCanvasSize()?", logNORMAL);
581 return;
584 ComputeSimpleMousePos(iX, iY);
586 if (m_bDetailed)
588 CUserLogTrial* pTrial = GetCurrentTrial();
590 if (pTrial == NULL)
592 // Only track during an actual pTrial
593 return;
596 // Only record mouse locations during navigation
597 if (pTrial->IsWriting())
598 pTrial->AddMouseLocationNormalized(iX, iY, bStoreIntegerRep, dNats);
601 // Keep track of the dNats for the current mouse position
602 if (m_bIsWriting)
603 m_dCycleNats = dNats;
607 // For simple logging, we don't want to log the same parameters settings
608 // in the file every single time Dasher starts up. So we require that
609 // this method be called once the initial loading of parameters is
610 // complete. This way only changes during a session are logged (we can
611 // also force logging of the parameter setting when the log file is
612 // created by setting m_bInitIsDone to true in the constructor).
613 void CUserLog::InitIsDone()
615 //CFunctionLogger f1("CUserLog::InitIsDone", g_pLogger);
617 m_bInitIsDone = true;
620 // Sets our output filename based on the current date and time.
621 // Or if a parameter is passed in, use that as the output name.
622 void CUserLog::SetOuputFilename(const string& strFilename)
624 //CFunctionLogger f1("CUserLog::SetOuputFilename", g_pLogger);
626 if (strFilename.length() > 0)
628 m_strFilename = strFilename;
630 else
632 m_strFilename = USER_LOG_DETAILED_PREFIX;
633 char* szTimeLine = NULL;
634 time_t t;
636 t = time(NULL);
637 szTimeLine = ctime(&t);
639 if ((szTimeLine != NULL) && (strlen(szTimeLine) > 18))
641 for (int i = 4; i < 19; i++)
643 if (szTimeLine[i] == ' ')
644 m_strFilename += "_";
645 else if (szTimeLine[i] != ':')
646 m_strFilename += szTimeLine[i];
650 m_strFilename += ".xml";
653 // Make sure we store a fully qualified form, to prevent movent
654 // if the working directory changes
655 m_strFilename = CFileLogger::GetFullFilenamePath(m_strFilename);
658 // Find out what level mask this object was created with
659 int CUserLog::GetLogLevelMask()
661 //CFunctionLogger f1("CUserLog::GetLogLevelMask", g_pLogger);
663 return m_iLevelMask;
666 void CUserLog::KeyDown(int iId, int iType, int iEffect) {
667 CUserLogTrial* pTrial = GetCurrentTrial();
669 if(pTrial)
670 pTrial->AddKeyDown(iId, iType, iEffect);
673 // This gets called whenever parameters get changed that we are tracking
674 void CUserLog::HandleEvent(int iParameter)
676 int i = 0;
678 // Go through each of the parameters in our lookup table from UserLogParam.h.
679 // If the key matches the notification event, then we want to push the
680 // parameter change to the logging object.
681 while (s_UserLogParamMaskTable[i].key != -1)
683 if (s_UserLogParamMaskTable[i].key == iParameter)
685 int iOptionMask = s_UserLogParamMaskTable[i].mask;
687 UpdateParam(iParameter, iOptionMask);
688 return;
691 i++;
693 } // end while (s_UserLogParamMaskTable[i].key != -1)
696 ////////////////////////////////////////// private methods ////////////////////////////////////////////////
698 // Just inits all our member variables, called by the constructors
699 void CUserLog::InitMemberVars()
701 //CFunctionLogger f1("CUserLog::InitMemberVars", g_pLogger);
703 m_strFilename = "";
704 m_pApplicationSpan = NULL;
705 m_dLastMouseUpdate = 0.0;
706 m_bSimple = false;
707 m_bDetailed = false;
708 m_pSimpleLogger = NULL;
709 m_bIsWriting = false;
710 m_bInitIsDone = false;
711 m_bNeedToWriteCanvas = false;
713 m_pCycleTimer = NULL;
714 m_iCycleNumDeletes = 0;
715 m_iCycleMouseCount = 0;
716 m_dCycleMouseNormXSum = 0.0;
717 m_dCycleMouseNormYSum = 0.0;
718 m_dCycleNats = 0.0;
720 m_sCanvasCoordinates.bottom = 0;
721 m_sCanvasCoordinates.top = 0;
722 m_sCanvasCoordinates.right = 0;
723 m_sCanvasCoordinates.left = 0;
725 m_sWindowCoordinates.bottom = 0;
726 m_sWindowCoordinates.top = 0;
727 m_sWindowCoordinates.right = 0;
728 m_sWindowCoordinates.left = 0;
730 // We want to use a fully qualified path so that we always
731 // look in the same spot, regardless of if the working
732 // directory has moved during runtime.
733 m_strCurrentTrialFilename = CFileLogger::GetFullFilenamePath(USER_LOG_CURRENT_TRIAL_FILENAME);
737 // Write this objects XML out
738 bool CUserLog::WriteXML()
740 fstream fout(m_strFilename.c_str(), ios::trunc | ios::out);
741 fout << GetXML();
742 fout.close();
744 return true;
747 // Serializes our data to XML
748 string CUserLog::GetXML()
750 //CFunctionLogger f1("CUserLog::GetXML", g_pLogger);
752 string strResult = "";
753 strResult.reserve(USER_LOG_DEFAULT_SIZE_TRIAL_XML * (m_vpTrials.size() + 1));
755 strResult += "<?xml version=\"1.0\"?>\n";
757 strResult += "<UserLog>\n";
758 if (m_pApplicationSpan != NULL)
759 strResult += m_pApplicationSpan->GetXML("\t");
761 strResult += "\t<Params>\n";
762 strResult += GetParamsXML();
763 strResult += "\t</Params>\n";
765 strResult += "\t<Trials>\n";
766 for (unsigned int i = 0; i < m_vpTrials.size(); i++)
768 CUserLogTrial* pTrial = (CUserLogTrial*) m_vpTrials[i];
770 // Only log trials that actually had some writing in it
771 if ((pTrial != NULL) && (pTrial->HasWritingOccured()))
773 strResult += pTrial->GetXML("\t\t");
776 strResult += "\t</Trials>\n";
778 strResult += "</UserLog>\n";
780 return strResult;
783 // Returns pointer to the current user pTrial, NULL if we don't have one yet
784 CUserLogTrial* CUserLog::GetCurrentTrial()
786 //CFunctionLogger f1("CUserLog::GetCurrentTrial", g_pLogger);
788 if (m_vpTrials.size() <= 0)
789 return NULL;
790 return m_vpTrials[m_vpTrials.size() - 1];
793 // Creates a new pTrial, adds to our vector and returns the pointer
794 CUserLogTrial* CUserLog::AddTrial()
796 //CFunctionLogger f1("CUserLog::AddTrial", g_pLogger);
798 // Let the last pTrial object know we are done with it
799 if (m_vpTrials.size() > 0)
801 CUserLogTrial* pTrial = m_vpTrials[m_vpTrials.size() - 1];
803 if (pTrial != NULL)
804 pTrial->Done();
807 CUserLogTrial* pTrial = new CUserLogTrial(m_strCurrentTrialFilename);
808 if (pTrial != NULL)
810 m_vpTrials.push_back(pTrial);
811 PrepareNewTrial();
813 else
814 g_pLogger->Log("CUserLog::AddTrial, failed to create CUserLogTrialSpeech!", logNORMAL);
816 return pTrial;
819 // See if the specified number of milliseconds has elapsed since the last mouse location update
820 bool CUserLog::UpdateMouseLocation()
822 //CFunctionLogger f1("CUserLog::UpdateMouseLocation", g_pLogger);
824 #ifdef _WIN32
825 struct timeb sTimeBuffer;
826 #else
827 struct timeval sTimeBuffer;
828 struct timezone sTimezoneBuffer;
829 #endif
831 #ifdef _WIN32
832 ftime(&sTimeBuffer);
833 double dTime = (sTimeBuffer.time * 1000.0) + sTimeBuffer.millitm;
834 #else
835 gettimeofday(&sTimeBuffer, &sTimezoneBuffer);
836 double dTime = (sTimeBuffer.tv_sec * 1000.0) + (int)(sTimeBuffer.tv_usec / 1000);
837 #endif
840 if ((dTime - m_dLastMouseUpdate) > LOG_MOUSE_EVERY_MS)
842 m_dLastMouseUpdate = dTime;
843 return true;
845 return false;
848 // Calculate how many bits entered in the last Start/Stop cycle
849 double CUserLog::GetCycleBits()
851 //CFunctionLogger f1("CUserLog::GetCycleBits", g_pLogger);
853 return m_dCycleNats / log(2.0);
856 // For lightweight logging, we want a string that represents the critical
857 // stats for what happened between start and stop
858 string CUserLog::GetStartStopCycleStats()
860 //CFunctionLogger f1("CUserLog::GetStartStopCycleStats", g_pLogger);
862 string strResult = "";
864 double dNormX = 0.0;
865 double dNormY = 0.0;
866 if (m_iCycleMouseCount > 0)
868 dNormX = m_dCycleMouseNormXSum / (double) m_iCycleMouseCount,
869 dNormY = m_dCycleMouseNormYSum / (double) m_iCycleMouseCount;
872 if (m_pCycleTimer == NULL)
874 g_pLogger->Log("CUserLog::GetStartStopCycleStats, cycle timer was NULL!", logNORMAL);
875 return "";
878 // Tab delimited fields are:
879 // elapsed time, symbols written, bits written, symbols deleted,
880 // avg normalized x mouse coordinate, avg normalized y mouse
881 // coordinate, (any parameters marked to be put in cycle stats)
883 // tsbdxym stands for: time symbols bits deletes x y maxbitrate
884 sprintf(m_szTempBuffer,
885 "tsbdxym:\t%0.3f\t%zu\t%0.6f\t%d\t%0.3f\t%0.3f%s",
886 m_pCycleTimer->GetElapsed(),
887 m_vCycleHistory.size(),
888 GetCycleBits(),
889 m_iCycleNumDeletes,
890 dNormX,
891 dNormY,
892 GetCycleParamStats().c_str());
893 strResult = m_szTempBuffer;
895 return strResult;
898 // Helper that computes update of the simple logging's mouse
899 // position tracking member variables.
900 void CUserLog::ComputeSimpleMousePos(int iX, int iY)
902 //CFunctionLogger f1("CUserLog::ComputeSimpleMousePos", g_pLogger);
904 if ((m_bSimple) && (m_bIsWriting))
906 // We keep a running sum of normalized X, Y coordinates
907 // for use in the simple log file.
908 m_dCycleMouseNormXSum += CUserLocation::ComputeNormalizedX(iX,
909 m_sCanvasCoordinates.left,
910 m_sCanvasCoordinates.right);
912 m_dCycleMouseNormYSum += CUserLocation::ComputeNormalizedY(iY,
913 m_sCanvasCoordinates.top,
914 m_sCanvasCoordinates.bottom);
915 m_iCycleMouseCount++;
919 // Resets member variables that track a cycle for simple logging
920 void CUserLog::ResetCycle()
922 //CFunctionLogger f1("CUserLog::ResetCycle", g_pLogger);
924 m_vCycleHistory.clear();
925 m_iCycleNumDeletes = 0;
926 m_iCycleMouseCount = 0;
927 m_dCycleMouseNormXSum = 0.0;
928 m_dCycleMouseNormYSum = 0.0;
930 if (m_pCycleTimer != NULL)
932 delete m_pCycleTimer;
933 m_pCycleTimer = NULL;
936 m_pCycleTimer = new CSimpleTimer();
939 // Gets the XML that goes in the <Params> tag, but not the tags themselves.
940 string CUserLog::GetParamsXML()
942 //CFunctionLogger f1("CUserLog::GetParamsXML", g_pLogger);
944 string strResult = "";
946 // Make parameters with the same name appear near each other in the results
947 sort(m_vParams.begin(), m_vParams.end(), CUserLogParam::ComparePtr);
949 for (unsigned int i = 0; i < m_vParams.size(); i++)
951 CUserLogParam* pParam = (CUserLogParam*) m_vParams[i];
953 strResult += CUserLogTrial::GetParamXML(pParam, "\t\t");
956 return strResult;
959 // Prepares a new pTrial for use. Passes on the current canvas and window
960 // size so normalized mouse coordinates can be calculated. Also
961 // parameters can be marked to force them into the Trial object. Looks for
962 // these and push into the current Trial object.
963 void CUserLog::PrepareNewTrial()
965 //CFunctionLogger f1("CUserLog::PrepareNewTrial", g_pLogger);
967 CUserLogTrial* pTrial = GetCurrentTrial();
969 if (pTrial != NULL)
971 // We want to force the current value of any parameters that we marked
972 // with the userLogParamForceInTrial option when created. We can
973 // do this by going backwards through the parameter vector and only
974 // pushing through the first value of a given parameter name.
975 VECTOR_STRING vFound;
976 if (m_vParams.size() > 0)
978 for (VECTOR_USER_LOG_PARAM_PTR_REV_ITER iter = m_vParams.rbegin(); iter != m_vParams.rend(); ++iter)
980 if (((*iter) != NULL) && ((*iter)->options & userLogParamForceInTrial))
982 // Make sure we haven't output this one already
983 VECTOR_STRING_ITER strIter;
984 strIter = find(vFound.begin(), vFound.end(), (*iter)->strName);
985 if (strIter == vFound.end())
987 pTrial->AddParam((*iter)->strName, (*iter)->strValue, (*iter)->options);
988 vFound.push_back((*iter)->strName);
994 // Make sure the pTrial has the current canvas and window coordinate sizes
995 pTrial->AddCanvasSize(m_sCanvasCoordinates.top,
996 m_sCanvasCoordinates.left,
997 m_sCanvasCoordinates.bottom,
998 m_sCanvasCoordinates.right);
1000 pTrial->AddWindowSize(m_sWindowCoordinates.top,
1001 m_sWindowCoordinates.left,
1002 m_sWindowCoordinates.bottom,
1003 m_sWindowCoordinates.right);
1006 else
1007 g_pLogger->Log("CUserLog::PrepareNewTrial, failed to create CUserLogTrial", logNORMAL);
1010 // Parameters can be marked to always end them at the cycle stats in short logging.
1011 // We'll look through our parameters and return a tab delimited list of their
1012 // values.
1013 string CUserLog::GetCycleParamStats()
1015 //CFunctionLogger f1("CUserLog::GetCycleParamStats", g_pLogger);
1017 string strResult = "";
1018 VECTOR_STRING vFound;
1020 if (m_vParams.size() <= 0)
1021 return strResult;
1023 // We may have more than one parameter that needs to be added and we want
1024 // the stats line to be invariant to the order in which AddParam() was
1025 // called. So we'll sort by param name and then by time stamp (for
1026 // parameters with multiple values).
1027 sort(m_vParams.begin(), m_vParams.end(), CUserLogParam::ComparePtr);
1029 // Find the last instance of any parameter marked as needed to be on
1030 // the cycle stats line.
1031 for (VECTOR_USER_LOG_PARAM_PTR_REV_ITER iter = m_vParams.rbegin(); iter != m_vParams.rend(); ++iter)
1033 if (((*iter) != NULL) && ((*iter)->options & userLogParamShortInCycle))
1035 // Make sure we haven't output this one already
1036 VECTOR_STRING_ITER strIter;
1037 strIter = find(vFound.begin(), vFound.end(), (*iter)->strName);
1038 if (strIter == vFound.end())
1040 strResult += "\t";
1041 strResult += (*iter)->strValue;
1042 vFound.push_back((*iter)->strName);
1047 return strResult;
1050 // Return a string with the operating system and product version
1051 string CUserLog::GetVersionInfo()
1053 //CFunctionLogger f1("CUserLog::GetVersionInfo", g_pLogger);
1055 string strResult = "";
1056 #ifdef _WIN32
1057 strResult += "win ";
1059 // TBD: getting version from resource is quite tricky and requires linking in
1060 // a whole library to do. Maybe we can just #DEFINE the product version?
1061 #else
1062 strResult += "not win ";
1063 #endif
1065 return strResult;
1068 // Forces all the parameters we are tracking to be intially set, used when the
1069 // object is first starting up.
1070 void CUserLog::AddInitialParam()
1072 int i = 0;
1073 while (s_UserLogParamMaskTable[i].key != -1)
1075 UpdateParam(s_UserLogParamMaskTable[i].key, s_UserLogParamMaskTable[i].mask);
1076 i++;
1080 // Helper method that takes a parameter ID a la Parameters.h and
1081 // looks up its type, name and value and pushes into our object
1082 // using the specified options mask.
1083 void CUserLog::UpdateParam(int iParameter, int iOptionMask)
1085 string strParamName = GetParameterName(iParameter);
1087 // What method we call depends on the type of the parameter
1088 switch (GetParameterType(iParameter))
1090 case (ParamBool):
1092 // Convert bool to a integer
1093 int iValue = 0;
1094 if (GetBoolParameter(iParameter))
1095 iValue = 1;
1096 AddParam(strParamName, iValue, iOptionMask);
1097 return;
1098 break;
1100 case (ParamLong):
1102 AddParam(strParamName, (int) GetLongParameter(iParameter), iOptionMask);
1103 return;
1104 break;
1106 case (ParamString):
1108 AddParam(strParamName, GetStringParameter(iParameter), iOptionMask);
1109 return;
1110 break;
1112 default:
1114 g_pLogger->Log("CUserLog::UpdateParam, matched parameter %d but unknown type %d", logNORMAL, iParameter, GetParameterType(iParameter));
1115 break;
1121 ///////////////////////////////////////////////////////////////////////////////////////
1122 // Below here are methods that are just used in the standalone tool that reads in
1123 // UserLog XML files and does cool things to them.
1124 // TODO these are broken by settings rewrite. Fix???
1126 // Load the object from an XML file
1127 CUserLog::CUserLog(string strXMLFilename)
1128 : CUserLogBase(NULL), CSettingsUserObserver(NULL) {
1129 //CFunctionLogger f1("CUserLog::CUserLog(XML)", g_pLogger);
1131 InitMemberVars();
1133 // We are representing detailed logging when we create from XML
1134 m_bDetailed = true;
1136 VECTOR_STRING vectorTrials;
1138 // First split up various parts of the XML
1139 string strXML = XMLUtil::LoadFile(strXMLFilename);
1140 string strApp = XMLUtil::GetElementString("Application", strXML, true);
1141 string strParams = XMLUtil::GetElementString("Params", strXML, true);
1142 string strTrials = XMLUtil::GetElementString("Trials", strXML, true);
1143 vectorTrials = XMLUtil::GetElementStrings("Trial", strTrials, true);
1145 m_pApplicationSpan = new CTimeSpan("Application", strApp);
1146 m_vParams = CUserLogTrial::ParseParamsXML(strParams);
1148 // Now construct each of the Trial objects based on its section of XML
1149 for (VECTOR_STRING_ITER iter = vectorTrials.begin(); iter < vectorTrials.end(); iter++)
1151 CUserLogTrial* pTrial = new CUserLogTrial(*iter, 0);
1153 if (pTrial != NULL)
1154 m_vpTrials.push_back(pTrial);
1159 // Returns a vector that contains vectors of strings which each
1160 // contain a tab delimited list of mouse coordinates for each
1161 // navigation cycle.
1162 VECTOR_VECTOR_STRING CUserLog::GetTabMouseXY(bool bReturnNormalized)
1164 //CFunctionLogger f1("CUserLog::GetTabMouseXY", g_pLogger);
1166 VECTOR_VECTOR_STRING vResult;
1168 for (VECTOR_USER_LOG_TRIAL_PTR_ITER iter = m_vpTrials.begin();
1169 iter < m_vpTrials.end();
1170 iter++)
1172 if (*iter != NULL)
1174 VECTOR_STRING vectorTrial = (*iter)->GetTabMouseXY(bReturnNormalized);
1175 vResult.push_back(vectorTrial);
1179 return vResult;
1182 // Returns a vector that contains a vector of density grids.
1183 VECTOR_VECTOR_DENSITY_GRIDS CUserLog::GetMouseDensity(int iGridSize)
1185 //CFunctionLogger f1("CUserLog::GetMouseDensity", g_pLogger);
1187 VECTOR_VECTOR_DENSITY_GRIDS vResult;
1188 for (VECTOR_USER_LOG_TRIAL_PTR_ITER iter = m_vpTrials.begin();
1189 iter < m_vpTrials.end();
1190 iter++)
1192 if (*iter != NULL)
1194 VECTOR_DENSITY_GRIDS vTrial = (*iter)->GetMouseDensity(iGridSize);
1195 vResult.push_back(vTrial);
1199 return vResult;