// // MODULE: COUNTER.CPP // // PURPOSE: implementation the counter classes: // CPeriodicTotals (utility class) // CAbstractCounter (abstract base class). // CCounter (simple counter) // CHourlyCounter (counter with "bins" for each hour of the day) // CDailyCounter (counter with "bins" for day of the week) // CHourlyDailyCounter (counter with "bins" for each hour of the day and each day of the week) // // PROJECT: Generic Troubleshooter DLL for Microsoft AnswerPoint // // COMPANY: Saltmine Creative, Inc. (206)-284-7511 support@saltmine.com // // AUTHOR: Joe Mabel // // ORIGINAL DATE: 7-20-1998 // // NOTES: // 1. Right as daylight savings time clicks in, there will be a few anomalies. // Since this defines "days" to be 24-hour periods, rather than calendar days, // if you have just gone from standard time to daylight time, "previous days" // before the switch will begin at 11pm the night before the relevant day; // if you have just gone from daylight time to standard time, "previous days" // before the switch will begin at 1am on the relevant day. // // Version Date By Comments //-------------------------------------------------------------------- // V3.0 7-20-98 JM Original // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "event.h" #include "SafeTime.h" #include "Counter.h" #include "CounterMgr.h" #include "baseexception.h" #include #include "CharConv.h" #include "apiwraps.h" const long k_secsPerHour = 3600; const long k_secsPerDay = k_secsPerHour * 24; const long k_secsPerWeek = k_secsPerDay * 7; ////////////////////////////////////////////////////////////////////// // CPeriodicTotals // Utility class, returned to provide an effective table of hourly/daily // counts. ////////////////////////////////////////////////////////////////////// CPeriodicTotals::CPeriodicTotals(long nPeriods) : m_nPeriods(nPeriods), m_ptime(NULL), m_pCount(NULL) { Reset(); } CPeriodicTotals::~CPeriodicTotals() { ReleaseMem(); } void CPeriodicTotals::Reset() { ReleaseMem(); m_nPeriodsSet = 0; m_iPeriod = 0; m_ptime = NULL; m_pCount = NULL; try { m_ptime = new time_t[m_nPeriods]; m_pCount = new long[m_nPeriods]; } catch (bad_alloc&) { // Set the number of periods to zero, release any allocated memory, and rethrow the exception. m_nPeriods= 0; ReleaseMem(); CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ ); CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(), SrcLoc.GetSrcFileLineStr(), _T(""), _T(""), EV_GTS_CANT_ALLOC ); throw; } } void CPeriodicTotals::ReleaseMem() { delete [] m_ptime; delete [] m_pCount; } // Set the time & Count values at the current position and increment the position bool CPeriodicTotals::SetNext(time_t time, long Count) { if (m_iPeriod >= m_nPeriods) return false; m_ptime[m_iPeriod] = time; m_pCount[m_iPeriod++] = Count; m_nPeriodsSet++; return true; } // Format a time and count suitably for HTML or other text use. // returns a reference of convenience to the same string passed in. CString & CPeriodicTotals::DisplayPeriod(long i, CString & str) const { CString strTime; { CSafeTime safe(m_ptime[i]); str = safe.StrLocalTime(); } strTime.Format(_T(" %8.8d"), m_pCount[i]); str += strTime; return str; } ////////////////////////////////////////////////////////////////////// // CHourlyTotals ////////////////////////////////////////////////////////////////////// CHourlyTotals::CHourlyTotals() : CPeriodicTotals (24+1) { } CHourlyTotals::~CHourlyTotals() { } // This is strictly for display to operator, so hard-coding English is OK // returns a reference of convenience to the same string passed in. CString CHourlyTotals::HTMLDisplay() const { CString str, strTemp; if (m_nPeriodsSet > 1) { str += _T("\n"); str += _T("\n"); str += _T("Last 24 hours: "); str += _T("\n"); for (int i=0; i<24 && i\n"); str+= _T("\n"); CPeriodicTotals::DisplayPeriod(i, strTemp); str += strTemp; str += _T("\n"); str += _T("\n"); } } if (m_nPeriodsSet >= 1) { str += _T("\n"); str += _T(" \n"); str += _T("Current hour: "); str += _T("\n"); str += _T("\n"); CPeriodicTotals::DisplayPeriod(m_nPeriodsSet-1, strTemp); str += strTemp; str += _T("\n"); str += _T("\n"); } else str = _T("
No hourly data."); return str; } ////////////////////////////////////////////////////////////////////// // CDailyTotals ////////////////////////////////////////////////////////////////////// CDailyTotals::CDailyTotals() : CPeriodicTotals (7+1) { } CDailyTotals::~CDailyTotals() { } // This is strictly for display to operator, so hard-coding English is OK // returns a reference of convenience to the same string passed in. CString CDailyTotals::HTMLDisplay() const { CString str, strTemp; if (m_nPeriodsSet > 1) { str = _T("\n"); str+= _T("\n"); str += _T("Last 7 days: "); str += _T("\n"); for (int i=0; i<7 && i\n"); str+= _T("\n"); CPeriodicTotals::DisplayPeriod(i, strTemp); str += strTemp; str += _T("\n"); str += _T("\n"); } } if (m_nPeriodsSet >= 1) { str += _T("\n"); str += _T(" \n"); str += _T("Today: "); str += _T("\n"); str += _T("\n"); CPeriodicTotals::DisplayPeriod(m_nPeriodsSet-1, strTemp); str += strTemp; str += _T("\n"); str += _T("\n"); } else str = _T("
No daily data."); return str; } ////////////////////////////////////////////////////////////////////// // CCounterLocation ////////////////////////////////////////////////////////////////////// /*static*/ LPCTSTR CCounterLocation::m_GlobalStr = _T("Global"); /*static*/ LPCTSTR CCounterLocation::m_TopicStr = _T("Topic "); /*static*/ LPCTSTR CCounterLocation::m_ThreadStr = _T("Thread "); CCounterLocation::CCounterLocation(EId id, LPCTSTR scope /*=m_GlobalStr*/) : m_Scope(scope), m_Id(id) { } CCounterLocation::~CCounterLocation() { } ////////////////////////////////////////////////////////////////////// // CAbstractCounter ////////////////////////////////////////////////////////////////////// CAbstractCounter::CAbstractCounter(EId id /*=eIdGeneric*/, CString scope /*=m_GlobalStr*/) : CCounterLocation(id, scope) { ::Get_g_CounterMgr()->AddSubstitute(*this); } CAbstractCounter::~CAbstractCounter() { ::Get_g_CounterMgr()->Remove(*this); } ////////////////////////////////////////////////////////////////////// // CCounter // a simple counter ////////////////////////////////////////////////////////////////////// CCounter::CCounter(EId id /*=eIdGeneric*/, CString scope /*=m_GlobalStr*/) : CAbstractCounter(id, scope) { Clear(); } CCounter::~CCounter() { } void CCounter::Increment() { ::InterlockedIncrement( &m_Count ); } void CCounter::Clear() { ::InterlockedExchange( &m_Count, 0); } void CCounter::Init(long count) { ::InterlockedExchange( &m_Count, count); } long CCounter::Get() const { return m_Count; } ////////////////////////////////////////////////////////////////////// // CHourlyCounter // This counter maintains bins to keep track of values on a per-hour basis. // The code that sets the values can treat this as a CAbstractCounter. // Additional public functions are available to report results. ////////////////////////////////////////////////////////////////////// CHourlyCounter::CHourlyCounter() : m_ThisHour (-1), m_ThisTime (0) { m_hMutex = ::CreateMutex(NULL, FALSE, NULL); if (!m_hMutex) { CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ ); CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(), SrcLoc.GetSrcFileLineStr(), _T("Hourly"), _T(""), EV_GTS_ERROR_MUTEX ); } Clear(); } CHourlyCounter::~CHourlyCounter() { ::CloseHandle(m_hMutex); } void CHourlyCounter::Increment() { WAIT_INFINITE( m_hMutex ); SetHour(); m_arrCount[m_ThisHour].Increment(); ::ReleaseMutex(m_hMutex); } void CHourlyCounter::Clear() { WAIT_INFINITE( m_hMutex ); for (long i = 0; i < 24; i++) m_arrCount[i].Clear(); m_nThisHourYesterday = 0; ::ReleaseMutex(m_hMutex); } void CHourlyCounter::Init(long count) { CHourlyCounter::Clear(); WAIT_INFINITE( m_hMutex ); SetHour(); m_arrCount[m_ThisHour].Init(count); ::ReleaseMutex(m_hMutex); } // return a 24-hour total prior to the present hour. // non-const because it calls SetHour() long CHourlyCounter::GetDayCount() { long DayCount = 0; WAIT_INFINITE( m_hMutex ); SetHour(); for (long i=0; i<24; i++) { if ( i != m_ThisHour ) DayCount += m_arrCount[i].Get(); DayCount += m_nThisHourYesterday; } ::ReleaseMutex(m_hMutex); return DayCount; } // non-const because it calls SetHour() void CHourlyCounter::GetHourlies(CHourlyTotals & totals) { WAIT_INFINITE( m_hMutex ); totals.Reset(); SetHour(); time_t time = m_ThisTime - (k_secsPerDay); totals.SetNext(time, m_nThisHourYesterday); long i; for (i=m_ThisHour+1; i<24; i++) { time += k_secsPerHour; totals.SetNext(time, m_arrCount[i].Get()); } for (i=0; i<=m_ThisHour; i++) { time += k_secsPerHour; totals.SetNext(time, m_arrCount[i].Get()); } ::ReleaseMutex(m_hMutex); } // Based on the present time, shifts to the appropriate bin. void CHourlyCounter::SetHour() { time_t timeNow; time_t timeStartOfHour; WAIT_INFINITE( m_hMutex ); time(&timeNow); timeStartOfHour = (timeNow / k_secsPerHour) * k_secsPerHour; if (timeStartOfHour > m_ThisTime) { // If we get here, hour changed. Typically the last action was the previous // hour, but the algorithm here does not require that. long Hour; { // minimize how long we use CSafeTime, because it means holding a mutex. CSafeTime safe(timeStartOfHour); Hour = safe.LocalTime().tm_hour; } if (timeStartOfHour - m_ThisTime > k_secsPerDay) Clear(); else { m_nThisHourYesterday = m_arrCount[Hour].Get(); if (m_ThisHour > Hour) { long i; for (i=m_ThisHour+1; i<24; i++) { m_arrCount[i].Clear(); } for (i=0; i<=Hour; i++) { m_arrCount[i].Clear(); } } else { for (long i=m_ThisHour+1; i<=Hour; i++) { m_arrCount[i].Clear(); } } } m_ThisHour = Hour; m_ThisTime = timeStartOfHour; } ::ReleaseMutex(m_hMutex); return; } ////////////////////////////////////////////////////////////////////// // CDailyCounter // This counter maintains bins to keep track of values on a per-day basis. // The code that sets the values can treat this as a CAbstractCounter. // Additional public functions are available to report results. // This could share more code with CHourlyCounter, but it would be very hard to come up // with appropriate variable and function names, so we are suffering dual maintenance. ////////////////////////////////////////////////////////////////////// CDailyCounter::CDailyCounter() : m_ThisDay (-1), m_ThisTime (0) { m_hMutex = ::CreateMutex(NULL, FALSE, NULL); if (!m_hMutex) { CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ ); CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(), SrcLoc.GetSrcFileLineStr(), _T("Daily"), _T(""), EV_GTS_ERROR_MUTEX ); } Clear(); } CDailyCounter::~CDailyCounter() { ::CloseHandle(m_hMutex); } void CDailyCounter::Increment() { WAIT_INFINITE( m_hMutex ); SetDay(); m_arrCount[m_ThisDay].Increment(); ::ReleaseMutex(m_hMutex); } void CDailyCounter::Clear() { WAIT_INFINITE( m_hMutex ); for (long i = 0; i < 7; i++) m_arrCount[i].Clear(); m_nThisDayLastWeek = 0; ::ReleaseMutex(m_hMutex); } void CDailyCounter::Init(long count) { CDailyCounter::Clear(); WAIT_INFINITE( m_hMutex ); SetDay(); m_arrCount[m_ThisDay].Init(count); ::ReleaseMutex(m_hMutex); } // return a 7-day total prior to the present day. // non-const because it calls SetDay() long CDailyCounter::GetWeekCount() { long WeekCount = 0; WAIT_INFINITE( m_hMutex ); SetDay(); for (long i=0; i<7; i++) { if ( i != m_ThisDay ) WeekCount += m_arrCount[i].Get(); WeekCount += m_nThisDayLastWeek; } ::ReleaseMutex(m_hMutex); return WeekCount; } // non-const because it calls SetDay() void CDailyCounter::GetDailies(CDailyTotals & totals) { WAIT_INFINITE( m_hMutex ); totals.Reset(); SetDay(); time_t time = m_ThisTime - (k_secsPerWeek); totals.SetNext(time, m_nThisDayLastWeek); long i; for (i=m_ThisDay+1; i<7; i++) { time += k_secsPerDay; totals.SetNext(time, m_arrCount[i].Get()); } for (i=0; i<=m_ThisDay; i++) { time += k_secsPerDay; totals.SetNext(time, m_arrCount[i].Get()); } ::ReleaseMutex(m_hMutex); } // Based on the present time, shifts to the appropriate bin. void CDailyCounter::SetDay() { time_t timeNow; time_t timeStartOfDay; WAIT_INFINITE( m_hMutex ); time(&timeNow); // Want to get start of day local time. // Can't just set timeStartOfDay = (timeNow / k_secsPerDay) * k_secsPerDay // because that would be the start of the day based on GMT! long DayOfWeek; { // minimize how long we use CSafeTime, because it means holding a mutex. CSafeTime safe(timeNow); struct tm tmStartOfDay = safe.LocalTime(); DayOfWeek = tmStartOfDay.tm_wday; tmStartOfDay.tm_sec = 0; tmStartOfDay.tm_min = 0; tmStartOfDay.tm_hour = 0; timeStartOfDay = mktime(&tmStartOfDay); } if (timeStartOfDay > m_ThisTime) { // If we get here, day changed. Typically the last action was the previous // hour, but the algorithm here does not require that. { // minimize how long we use CSafeTime, because it means holding a mutex. CSafeTime safe(timeStartOfDay); DayOfWeek = safe.LocalTime().tm_wday; } if (timeStartOfDay - m_ThisTime > k_secsPerWeek) Clear(); else { m_nThisDayLastWeek = m_arrCount[DayOfWeek].Get(); if (m_ThisDay > DayOfWeek) { long i; for (i=m_ThisDay+1; i<7; i++) { m_arrCount[i].Clear(); } for (i=0; i<=DayOfWeek; i++) { m_arrCount[i].Clear(); } } else { for (long i=m_ThisDay+1; i<=DayOfWeek; i++) { m_arrCount[i].Clear(); } } } m_ThisDay = DayOfWeek; m_ThisTime = timeStartOfDay; } ::ReleaseMutex(m_hMutex); return; } ////////////////////////////////////////////////////////////////////// // CHourlyDailyCounter ////////////////////////////////////////////////////////////////////// CHourlyDailyCounter::CHourlyDailyCounter(EId id /*=eIdGeneric*/, CString scope /*=m_GlobalStr*/) : CAbstractCounter(id, scope), m_Total(0), m_timeFirst(0), m_timeLast(0), m_timeCleared(0) { m_hMutex = ::CreateMutex(NULL, FALSE, NULL); if (!m_hMutex) { CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ ); CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(), SrcLoc.GetSrcFileLineStr(), _T("HourlyDaily"), _T(""), EV_GTS_ERROR_MUTEX ); } time(&m_timeCreated); time(&m_timeCleared); } CHourlyDailyCounter::~CHourlyDailyCounter() { ::CloseHandle(m_hMutex); } void CHourlyDailyCounter::Increment() { WAIT_INFINITE( m_hMutex ); m_hourly.Increment(); m_daily.Increment(); m_Total++; time(&m_timeLast); if (!m_timeFirst) m_timeFirst = m_timeLast; ::ReleaseMutex(m_hMutex); } void CHourlyDailyCounter::Clear() { WAIT_INFINITE( m_hMutex ); m_hourly.Clear(); m_daily.Clear(); m_Total= 0; m_timeFirst = 0; m_timeLast = 0; time(&m_timeCleared); ::ReleaseMutex(m_hMutex); } void CHourlyDailyCounter::Init(long count) { CHourlyDailyCounter::Clear(); WAIT_INFINITE( m_hMutex ); m_hourly.Init(count); m_daily.Init(count); m_Total = count; time(&m_timeLast); if (!m_timeFirst) m_timeFirst = m_timeLast; ::ReleaseMutex(m_hMutex); } // no need to lock here, because m_hourly does its own locking. long CHourlyDailyCounter::GetDayCount() { return m_hourly.GetDayCount(); } // no need to lock here, because m_hourly does its own locking. void CHourlyDailyCounter::GetHourlies(CHourlyTotals & totals) { m_hourly.GetHourlies(totals); } // no need to lock here, because m_daily does its own locking. long CHourlyDailyCounter::GetWeekCount() { return m_daily.GetWeekCount(); } // no need to lock here, because m_daily does its own locking. void CHourlyDailyCounter::GetDailies(CDailyTotals & totals) { m_daily.GetDailies(totals); } long CHourlyDailyCounter::GetTotal() const { WAIT_INFINITE( m_hMutex ); long ret = m_Total; ::ReleaseMutex(m_hMutex); return ret; }; time_t CHourlyDailyCounter::GetTimeFirst() const { WAIT_INFINITE( m_hMutex ); time_t ret = m_timeFirst; ::ReleaseMutex(m_hMutex); return ret; }; time_t CHourlyDailyCounter::GetTimeLast() const { WAIT_INFINITE( m_hMutex ); time_t ret = m_timeLast; ::ReleaseMutex(m_hMutex); return ret; }; time_t CHourlyDailyCounter::GetTimeCleared() const { WAIT_INFINITE( m_hMutex ); time_t ret = m_timeCleared; ::ReleaseMutex(m_hMutex); return ret; }; time_t CHourlyDailyCounter::GetTimeCreated() const { WAIT_INFINITE( m_hMutex ); time_t ret = m_timeCreated; ::ReleaseMutex(m_hMutex); return ret; } time_t CHourlyDailyCounter::GetTimeNow() const { // No need to lock mutex on this call. time_t ret; time(&ret); return ret; } //////////////////////////////////////////////////////////////////////////////////// // CDisplayCounter...::Display() implementation //////////////////////////////////////////////////////////////////////////////////// #define STATUS_INVALID_NUMBER_STR _T("none") #define STATUS_INVALID_TIME_STR _T("none") CString CDisplayCounterTotal::Display() { TCHAR buf[128] = {0}; _stprintf(buf, _T("%ld"), long(((CHourlyDailyCounter*)m_pAbstractCounter)->GetTotal())); return buf; } CString CDisplayCounterCurrentDateTime::Display() { return CSafeTime(((CHourlyDailyCounter*)m_pAbstractCounter)->GetTimeNow()).StrLocalTime(STATUS_INVALID_TIME_STR); } CString CDisplayCounterCreateDateTime::Display() { return CSafeTime(((CHourlyDailyCounter*)m_pAbstractCounter)->GetTimeCreated()).StrLocalTime(STATUS_INVALID_TIME_STR); } CString CDisplayCounterFirstDateTime::Display() { return CSafeTime(((CHourlyDailyCounter*)m_pAbstractCounter)->GetTimeFirst()).StrLocalTime(STATUS_INVALID_TIME_STR); } CString CDisplayCounterLastDateTime::Display() { return CSafeTime(((CHourlyDailyCounter*)m_pAbstractCounter)->GetTimeLast()).StrLocalTime(STATUS_INVALID_TIME_STR); } CString CDisplayCounterDailyHourly::Display() { CString ret; if (m_pDailyTotals) { ((CHourlyDailyCounter*)m_pAbstractCounter)->GetDailies(*m_pDailyTotals); ret += m_pDailyTotals->HTMLDisplay(); } if (m_pHourlyTotals) { ((CHourlyDailyCounter*)m_pAbstractCounter)->GetHourlies(*m_pHourlyTotals); ret += m_pHourlyTotals->HTMLDisplay(); } return ret; }