/***************************************************************************** * * Grafdata.c - This module handles the non-drawing functions of the graph, * such as allocating linked structures and their memory, freeing it, * unlinking, starting and stopping the timer, * setting up the first graph (CPU), and all the numeric functions for * the different counter types. * * Microsoft Confidential * Copyright (c) 1992-1993 Microsoft Corporation * * ****************************************************************************/ //==========================================================================// // Includes // //==========================================================================// #include // for sprintf #include "perfmon.h" // main perfmon declarations #include "grafdata.h" // external declarations for this file #include // for FLT_MAX constant #include "addline.h" // for AddLine, EditLine #include "counters.h" // for CounterEntry #include "graph.h" // for SizeGraphComponents #include "pmemory.h" // for MemoryXXX (mallloc-type) routines #include "perfdata.h" // for UpdateLines #include "playback.h" // for PlayingBackLog, PlaybackLines #include "legend.h" #include "system.h" // for SystemGet #include "utils.h" #include "line.h" // for LineFree #include "valuebar.h" // for StatusTimer #include "fileopen.h" // for FileGetName #include "fileutil.h" // for FileRead... #include "menuids.h" // for IDM_VIEWCHART #include "perfmops.h" // for ExportFileHeader #include "status.h" // for StatusLineReady extern BOOL SaveFileHandler(HWND hWnd,DWORD type) ; // this macro is used in doing a simple DDA (Digital Differential Analyzer) // * 10 + 5 is to make the result round up with .5 #define DDA_DISTRIBUTE(TotalTics, numOfData) \ ((TotalTics * 10 / numOfData) + 5) / 10 #define szSmallValueFormat TEXT("%10.3f") #define szLargeValueFormat TEXT("%10.0f") //==========================================================================// // Local Data // //==========================================================================// //==========================================================================// // Local Functions // //==========================================================================// /**************************************************************************** * eUpdateMinMaxAve - ****************************************************************************/ void eUpdateMinMaxAve (FLOAT eValue, PLINESTRUCT pLineStruct, INT iValidValues, INT iTotalValidPoints, INT iDataPoint, INT gMaxPoints) { INT i ; INT iDataNum = iTotalValidPoints ; FLOAT eMin, eMax, eSum, eNewValue ; eMax = eMin = eValue ; if (PlayingBackLog()) { eSum = (FLOAT) 0.0 ; } else { eSum = eValue ; } if (iValidValues == iTotalValidPoints) { for (i=0 ; i < iValidValues ; i++) { if (i == iDataPoint) { // skip the data point we are going to replace continue ; } eNewValue = pLineStruct->lnValues[i] ; eSum += eNewValue ; if (eNewValue > eMax) { eMax = eNewValue ; } if (eNewValue < eMin) { eMin = eNewValue ; } } } else { // special case when we start the new line in the middle of the chart for (i = iDataPoint, iTotalValidPoints-- ; iTotalValidPoints > 0 ; iTotalValidPoints-- ) { i-- ; if (i < 0) { // for the wrap-around case.. i = gMaxPoints - 1 ; } if (i == iDataPoint) { // skip the data point we are going to replace continue ; } eNewValue = pLineStruct->lnValues[i] ; eSum += eNewValue ; if (eNewValue > eMax) { eMax = eNewValue ; } if (eNewValue < eMin) { eMin = eNewValue ; } } } pLineStruct->lnMinValue = eMin ; pLineStruct->lnMaxValue = eMax ; if (iDataNum) { pLineStruct->lnAveValue = eSum / (FLOAT) iDataNum ; } else { pLineStruct->lnAveValue = (FLOAT) 0.0 ; } //clean up bogus (negative)values if (pLineStruct->lnMinValue < (FLOAT)0.0) { pLineStruct->lnMinValue = (FLOAT)0.0; } if (pLineStruct->lnMaxValue < (FLOAT)0.0) { pLineStruct->lnMaxValue = (FLOAT)0.0; } if (pLineStruct->lnAveValue < (FLOAT) 0.0) { pLineStruct->lnAveValue = (FLOAT) 0.0 ; } } // UpdateValueBarData is used to update the value bar data // It is called when the user switches to a diff. line from // the legned window. VOID UpdateValueBarData (PGRAPHSTRUCT pGraph) { PLINESTRUCT pCurrentGraphLine ; INT KnownValue, MaxValues, iValidValues, iDataPoint ; FLOAT eValue ; pCurrentGraphLine = CurrentGraphLine (hWndGraph) ; if (!pCurrentGraphLine || pCurrentGraphLine->bFirstTime) { // we have not collect enough samples return ; } KnownValue = pGraph->gKnownValue ; MaxValues = pGraph->gMaxValues ; // The valid values is the number of valid entries in the // data buffer. After we wrap the buffer, all the values are // valid. iValidValues = pGraph->gTimeLine.iValidValues ; // Get the index to the data point we are updating. iDataPoint = KnownValue % MaxValues ; eValue = pCurrentGraphLine->lnValues[iDataPoint] ; // get the statistical data for this line eUpdateMinMaxAve(eValue, pCurrentGraphLine, iValidValues, pCurrentGraphLine->lnValidValues, iDataPoint, MaxValues) ; } // UpdateValueBarData VOID UpdateLGData (PGRAPHSTRUCT pGraph) { PLINESTRUCT pLine ; PLINESTRUCT pCurrentGraphLine ; INT KnownValue, MaxValues, iValidValues, iDataPoint ; FLOAT eValue ; // Known Value is the where data is placed in the buffer. pGraph->gKnownValue++; KnownValue = pGraph->gKnownValue ; // Update the high water mark for valid data in the lnValues // (aka DataPoint) buffer. MaxValues = pGraph->gMaxValues ; // The valid values is the number of valid entries in the // data buffer. After we wrap the buffer, all the values are // valid. iValidValues = pGraph->gTimeLine.iValidValues ; if (iValidValues < MaxValues) iValidValues = (KnownValue % MaxValues) + 1 ; pGraph->gTimeLine.iValidValues = iValidValues ; // Get the index to the data point we are updating. iDataPoint = KnownValue % MaxValues ; // loop through lines, // If one of the lines is highlighted then do the calculations // for average, min, & max on that line. pCurrentGraphLine = CurrentGraphLine (hWndGraph) ; for (pLine=pGraph->pLineFirst; pLine; pLine=pLine->pLineNext) { // for if (pLine->bFirstTime) { // skip until we have collect enough samples to plot the first data continue ; } if (pLine->lnValidValues < MaxValues) { (pLine->lnValidValues)++ ; } // Get the new value for this line. eValue = CounterEntry (pLine) ; if (pLine == pCurrentGraphLine) { // if // get the statistical data for this line eUpdateMinMaxAve (eValue, pLine, iValidValues, pLine->lnValidValues, iDataPoint, MaxValues) ; } // if // Now put the new value into the data array pLine->lnValues[iDataPoint] = eValue ; } GetLocalTime (&(pGraph->pDataTime[iDataPoint])) ; } // UpdateLGData BOOL HandleGraphTimer (void) { PGRAPHSTRUCT pGraph; //NOTE: get a strategy for these "no-go" states if (!(pGraph = pGraphs) || !pGraphs->pSystemFirst) return(FALSE); if (!UpdateLines(&(pGraphs->pSystemFirst), pGraphs->pLineFirst)) return (TRUE) ; UpdateLGData(pGraph); return(TRUE); } VOID ClearGraphTimer(PGRAPHSTRUCT pGraph) { KillTimer(pGraph->hWnd, GRAPH_TIMER_ID); } VOID SetGraphTimer(PGRAPHSTRUCT pGraph) { SetTimer(pGraph->hWnd, GRAPH_TIMER_ID, pGraph->gInterval, NULL) ; } VOID ResetGraphTimer(PGRAPHSTRUCT pGraph) { KillTimer(pGraph->hWnd, GRAPH_TIMER_ID); SetGraphTimer(pGraph); } VOID GetGraphConfig(PGRAPHSTRUCT pGraph) { LoadRefreshSettings(pGraph); LoadLineGraphSettings(pGraph); // Init the structure pGraph->pLineFirst = NULL; //NOTE: put the rest of this in Config pGraph->gOptions.bLegendChecked = TRUE; pGraph->gOptions.bMenuChecked = TRUE; pGraph->gOptions.bLabelsChecked = TRUE; pGraph->gOptions.bVertGridChecked = FALSE; pGraph->gOptions.bHorzGridChecked = FALSE; pGraph->gOptions.bStatusBarChecked = TRUE; pGraph->gOptions.GraphVGrid = TRUE; pGraph->gOptions.GraphHGrid = TRUE; pGraph->gOptions.HistVGrid = TRUE; pGraph->gOptions.HistHGrid = TRUE; pGraph->gOptions.iGraphOrHistogram = LINE_GRAPH; // vs. BAR_GRAPH pGraph->gOptions.iVertMax = DEF_GRAPH_VMAX; return; } BOOL InsertGraph (HWND hWnd) { PGRAPHSTRUCT pGraph; pGraph = MemoryAllocate (sizeof (GRAPHSTRUCT)) ; if (!pGraph) return (FALSE) ; pGraphs = pGraph; GetGraphConfig(pGraph); pGraph->bManualRefresh = FALSE ; // there's at least one place where the [DEFAULT_MAX_VALUES] entry is // accessed when the index should stop at [DEFAULT_MAX_VALUES -1]. // rather than try to find all the places this index is used it's easier // and safer just to make the array large enough to accomodate this // "oversight" pGraph->gMaxValues = DEFAULT_MAX_VALUES; pGraph->pptDataPoints = (PPOINT) MemoryAllocate (sizeof (POINT) * (pGraph->gMaxValues + 1)) ; pGraph->pDataTime = (SYSTEMTIME *) MemoryAllocate (sizeof(SYSTEMTIME) * (pGraph->gMaxValues + 1)) ; pGraph->hWnd = hWnd ; pGraph->bModified = FALSE ; pGraph->Visual.iColorIndex = 0 ; pGraph->Visual.iWidthIndex = 0 ; pGraph->Visual.iStyleIndex = 0 ; return(TRUE) ; } void PlaybackSetGraphLines (HWND hWndChart, PLINE pLineFirst, int iDisplayTic, int iLogTic, BOOL CalcData) { PLINE pLine ; FLOAT eValue ; for (pLine = pLineFirst ; pLine ; pLine = pLine->pLineNext) { // for eValue = CounterEntry (pLine) ; pLine->lnValues[iDisplayTic] = eValue ; pLine->aiLogIndexes[iDisplayTic] = iLogTic ; // only need to do this on request. if (CalcData) { eUpdateMinMaxAve (eValue, pLine, iDisplayTic, iDisplayTic, iDisplayTic, iDisplayTic) ; } // if } // for } // PlaybackSetGraphLines BOOL ChartInsertLine (PGRAPHSTRUCT pGraph, PLINE pLine) /* Effect: Insert the line pLine into the graph pGraph and allocate space for the graph's number of values. Returns: Whether the line could be added and space allocated. See Also: LineAllocate (line.c), ChartDeleteLine. */ { // ChartInsertLine PLINE pLineEquivalent ; INT i ; FLOAT *pTempPts; HPEN tempPen ; pGraph->bModified = TRUE ; pLineEquivalent = FindEquivalentLine (pLine, pGraph->pLineFirst) ; if (pLineEquivalent) { if (bMonitorDuplicateInstances) { pLine->dwInstanceIndex = pLineEquivalent->dwInstanceIndex + 1; } else { pLineEquivalent->Visual = pLine->Visual ; pLineEquivalent->iScaleIndex = pLine->iScaleIndex ; pLineEquivalent->eScale = pLine->eScale ; tempPen = pLineEquivalent->hPen ; pLineEquivalent->hPen = pLine->hPen ; pLine->hPen = tempPen ; return FALSE ; } } if (!pGraph->pLineFirst && !PlayingBackLog()) { SetGraphTimer (pGraph) ; } if (!SystemAdd (&pGraph->pSystemFirst, pLine->lnSystemName,pGraph->hWnd)) return FALSE; LineAppend (&pGraph->pLineFirst, pLine) ; pLine->lnMinValue = FLT_MAX ; pLine->lnMaxValue = - FLT_MAX ; pLine->lnAveValue = 0.0F ; pLine->lnValidValues = 0 ; pLine->lnValues = (FLOAT *) MemoryAllocate (sizeof (FLOAT) * (pGraph->gMaxValues + 1)) ; for (i = pGraph->gMaxValues, pTempPts = pLine->lnValues ; i > 0 ; i-- ) { *pTempPts++ = (FLOAT) 0.0 ; } if (PlayingBackLog ()) { pLine->aiLogIndexes = (int *) MemoryAllocate (sizeof (LONG) * (pGraph->gMaxValues + 1)) ; } // Add the line to the legend, resize the legend window, and then // select the new line as the current legend item. Do it in this // sequence to avoid the legend scroll bar momentarily appearing and // then disappearing, since the resize will obviate the scroll bar. LegendAddItem (hWndGraphLegend, pLine) ; if (!bDelayAddAction) { SizeGraphComponents (hWndGraph) ; LegendSetSelection (hWndGraphLegend, LegendNumItems (hWndGraphLegend) - 1) ; if (PlayingBackLog ()) PlaybackChart (pGraph->hWnd) ; } return (TRUE) ; } // ChartInsertLine VOID ChartDeleteLine (PGRAPHSTRUCT pGraph, PLINESTRUCT pLine) { PLINESTRUCT npLine; pGraph->bModified = TRUE ; if (pGraph->pLineFirst == pLine) pGraph->pLineFirst = pLine->pLineNext; else { for (npLine = pGraph->pLineFirst; npLine; npLine = npLine->pLineNext) { if (npLine->pLineNext == pLine) npLine->pLineNext = pLine->pLineNext; } } if (!pGraph->pLineFirst) { ResetGraph (pGraph) ; } else { BuildValueListForSystems ( pGraph->pSystemFirst, pGraph->pLineFirst) ; } // Delete the legend entry for this line. // If the line was highlighted then this will undo the highlight. LegendDeleteItem (hWndGraphLegend, pLine) ; LineFree (pLine) ; SizeGraphComponents (hWndGraph) ; } void ClearGraphDisplay (PGRAPHSTRUCT pGraph) { PLINESTRUCT pLine; // reset the timeline data // pGraph->gKnownValue = -1 ; pGraph->gKnownValue = 0 ; pGraph->gTimeLine.iValidValues = 0 ; pGraph->gTimeLine.xLastTime = 0 ; memset (pGraph->pDataTime, 0, sizeof(SYSTEMTIME) * (pGraph->gMaxValues + 1)) ; // loop through lines, // If one of the lines is highlighted then do the calculations // for average, min, & max on that line. for (pLine=pGraph->pLineFirst; pLine; pLine=pLine->pLineNext) { // for pLine->bFirstTime = 2 ; pLine->lnMinValue = FLT_MAX ; pLine->lnMaxValue = - FLT_MAX ; pLine->lnAveValue = 0.0F ; pLine->lnValidValues = 0 ; memset (pLine->lnValues, 0, sizeof(FLOAT) * (pGraph->gMaxValues + 1)) ; } StatusTimer (hWndGraphStatus, TRUE) ; WindowInvalidate (hWndGraphDisplay) ; } void ResetGraphView (HWND hWndGraph) { PGRAPHSTRUCT pGraph ; pGraph = GraphData (hWndGraph) ; if (!pGraph) { return ; } ChangeSaveFileName (NULL, IDM_VIEWCHART) ; if (pGraph->pSystemFirst) { ResetGraph (pGraph) ; } } // ResetGraphView void ResetGraph (PGRAPHSTRUCT pGraph) { ClearGraphTimer (pGraph) ; ClearLegend (hWndGraphLegend) ; if (pGraph->pLineFirst) { FreeLines (pGraph->pLineFirst) ; pGraph->pLineFirst = NULL ; } if (pGraph->pSystemFirst) { FreeSystems (pGraph->pSystemFirst) ; pGraph->pSystemFirst = NULL ; } // pGraph->gKnownValue = -1 ; pGraph->gKnownValue = 0 ; pGraph->gTimeLine.iValidValues = 0 ; pGraph->gTimeLine.xLastTime = 0 ; // reset visual data pGraph->Visual.iColorIndex = 0 ; pGraph->Visual.iWidthIndex = 0 ; pGraph->Visual.iStyleIndex = 0 ; memset (pGraph->pDataTime, 0, sizeof(SYSTEMTIME) * (pGraph->gMaxValues + 1)) ; SizeGraphComponents (hWndGraph) ; InvalidateRect(hWndGraph, NULL, TRUE) ; } void PlaybackChart (HWND hWndChart) { // PlaybackChart int iDisplayTics ; // num visual points to display int iDisplayTic ; int iLogTic ; int iLogTicsMove ; BOOL bFirstTime = TRUE; int iLogTicsRemaining ; if (!pGraphs->pLineFirst) { // no line to playback return ; } iLogTicsRemaining = PlaybackLog.iSelectedTics ; // we only have iDisplayTics-1 points since // we have to use the first two sample points to // get the first data points. if (iLogTicsRemaining <= pGraphs->gMaxValues) { iDisplayTics = iLogTicsRemaining ; pGraphs->gTimeLine.iValidValues = iDisplayTics - 1 ; } else { iDisplayTics = pGraphs->gMaxValues ; pGraphs->gTimeLine.iValidValues = iDisplayTics ; } iDisplayTic = -1 ; iLogTic = PlaybackLog.StartIndexPos.iPosition ; while (iDisplayTics) { PlaybackLines (pGraphs->pSystemFirst, pGraphs->pLineFirst, iLogTic) ; if (bFirstTime) { bFirstTime = FALSE ; // get the second sample data to form the first data point iLogTic++ ; iLogTicsRemaining-- ; PlaybackLines (pGraphs->pSystemFirst, pGraphs->pLineFirst, iLogTic) ; } iDisplayTic++ ; PlaybackSetGraphLines (hWndChart, pGraphs->pLineFirst, iDisplayTic, iLogTic, (iDisplayTics == 1)) ; // setup DDA to get the index of the next sample point iLogTicsMove = DDA_DISTRIBUTE (iLogTicsRemaining, iDisplayTics) ; iLogTicsRemaining -= iLogTicsMove ; iLogTic += iLogTicsMove ; iDisplayTics-- ; } // while // point to the last value for valuebar display pGraphs->gKnownValue = iDisplayTic ; } // PlaybackChart #if 0 PLINESTRUCT CurrentGraphLine (HWND hWndGraph) { // CurrentGraphLine UNREFERENCED_PARAMETER (hWndGraph) ; return (LegendCurrentLine (hWndGraphLegend)) ; } #endif BOOL AddChart (HWND hWndParent) { PLINE pCurrentLine = CurrentGraphLine (hWndGraph) ; return (AddLine (hWndParent, &(pGraphs->pSystemFirst), &(pGraphs->Visual), pCurrentLine ? pCurrentLine->lnSystemName : NULL, LineTypeChart)) ; } BOOL EditChart (HWND hWndParent) { // EditChart return (EditLine (hWndParent, &(pGraphs->pSystemFirst), CurrentGraphLine (hWndGraph), LineTypeChart)) ; } void GraphAddAction () { PGRAPHSTRUCT pGraph ; pGraph = GraphData (hWndGraph) ; SizeGraphComponents (hWndGraph) ; LegendSetSelection (hWndGraphLegend, LegendNumItems (hWndGraphLegend) - 1) ; if (PlayingBackLog ()) PlaybackChart (pGraph->hWnd) ; } BOOL OpenChartVer1 (HANDLE hFile, DISKCHART *pDiskChart, PGRAPHSTRUCT pGraph) { // OpenChartVer1 bDelayAddAction = TRUE ; pGraph->Visual = pDiskChart->Visual ; pGraph->gOptions = pDiskChart->gOptions ; pGraph->gMaxValues = pDiskChart->gMaxValues ; pGraph->bManualRefresh = pDiskChart->bManualRefresh ; pGraphs->gInterval = (INT) (pGraph->gOptions.eTimeInterval * (FLOAT) 1000.0) ; ReadLines (hFile, pDiskChart->dwNumLines, &(pGraph->pSystemFirst), &(pGraph->pLineFirst), IDM_VIEWCHART) ; bDelayAddAction = FALSE ; GraphAddAction () ; return (TRUE) ; } // OpenChartVer1 BOOL OpenChart (HWND hWndGraph, HANDLE hFile, DWORD dwMajorVersion, DWORD dwMinorVersion, BOOL bChartFile) { // OpenChart PGRAPHSTRUCT pGraph ; DISKCHART DiskChart ; BOOL bSuccess = TRUE ; pGraph = pGraphs ; if (!pGraph) { bSuccess = FALSE ; goto Exit0 ; } if (!FileRead (hFile, &DiskChart, sizeof (DISKCHART))) { bSuccess = FALSE ; goto Exit0 ; } switch (dwMajorVersion) { case (1): SetHourglassCursor() ; ResetGraphView (hWndGraph) ; OpenChartVer1 (hFile, &DiskChart, pGraph) ; // change to chart view if we are opening a // chart file if (bChartFile && iPerfmonView != IDM_VIEWCHART) { SendMessage (hWndMain, WM_COMMAND, (LONG)IDM_VIEWCHART, 0L) ; } if (iPerfmonView == IDM_VIEWCHART) { SetPerfmonOptions (&DiskChart.perfmonOptions) ; } SetArrowCursor() ; break ; } // switch Exit0: if (bChartFile) { CloseHandle (hFile) ; } return (bSuccess) ; } // OpenChart BOOL SaveChart (HWND hWndGraph, HANDLE hInputFile, BOOL bGetFileName) { PGRAPHSTRUCT pGraph ; PLINE pLine ; HANDLE hFile ; DISKCHART DiskChart ; PERFFILEHEADER FileHeader ; TCHAR szFileName [256] ; BOOL newFileName = FALSE ; if (hInputFile) { // use the input file handle if it is available // this is the case for saving workspace data hFile = hInputFile ; } else { if (pChartFullFileName) { lstrcpy (szFileName, pChartFullFileName) ; } if (bGetFileName || pChartFullFileName == NULL) { // if (!pChartFullFileName) // { // StringLoad (IDS_GRAPH_FNAME, szFileName) ; // } if (!FileGetName (hWndGraph, IDS_CHARTFILE, szFileName)) { return (FALSE) ; } newFileName = TRUE ; } hFile = FileHandleCreate (szFileName) ; if (hFile && newFileName) { ChangeSaveFileName (szFileName, IDM_VIEWCHART) ; } else if (!hFile) { DlgErrorBox (hWndGraph, ERR_CANT_OPEN, szFileName) ; } } if (!hFile) return (FALSE) ; pGraph = pGraphs ; if (!pGraph) { if (!hInputFile) { CloseHandle (hFile) ; } return (FALSE) ; } if (!hInputFile) { // only need to write file header if not workspace memset (&FileHeader, 0, sizeof (FileHeader)) ; lstrcpy (FileHeader.szSignature, szPerfChartSignature) ; FileHeader.dwMajorVersion = ChartMajorVersion ; FileHeader.dwMinorVersion = ChartMinorVersion ; if (!FileWrite (hFile, &FileHeader, sizeof (PERFFILEHEADER))) { goto Exit0 ; } } DiskChart.Visual = pGraph->Visual ; DiskChart.gOptions = pGraph->gOptions ; DiskChart.gMaxValues = pGraph->gMaxValues ; DiskChart.dwNumLines = NumLines (pGraph->pLineFirst) ; DiskChart.bManualRefresh = pGraph->bManualRefresh ; DiskChart.perfmonOptions = Options ; if (!FileWrite (hFile, &DiskChart, sizeof (DISKCHART))) { goto Exit0 ; } for (pLine = pGraph->pLineFirst ; pLine ; pLine = pLine->pLineNext) { // for if (!WriteLine (pLine, hFile)) { goto Exit0 ; } } // for if (!hInputFile) { CloseHandle (hFile) ; } return (TRUE) ; Exit0: if (!hInputFile) { CloseHandle (hFile) ; // only need to report error if not workspace DlgErrorBox (hWndGraph, ERR_SETTING_FILE, szFileName) ; } return (FALSE) ; } // SaveChart #define TIME_TO_WRITE 2 BOOL ExportChartLabels (HANDLE hFile, PGRAPHSTRUCT pGraph) { TCHAR UnicodeBuff [LongTextLen] ; CHAR TempBuff [LongTextLen * 2] ; int StringLen ; PLINESTRUCT pLine; int TimeToWriteFile ; int iIndex ; LPTSTR lpItem = NULL; for (iIndex = 0 ; iIndex < 5 ; iIndex++) { // for iIndex == 0, get counter name // for iIndex == 1, get instance name // for iIndex == 2, get parent name // for iIndex == 3, get object name // for iIndex == 4, get computer name if (iIndex == 4) { // the last label field, write date/time labels strcpy (TempBuff, LineEndStr) ; StringLen = strlen (TempBuff) ; StringLoad (IDS_EXPORT_DATE, UnicodeBuff) ; ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ; strcat (TempBuff, pDelimiter) ; StringLen = strlen (TempBuff) ; StringLoad (IDS_EXPORT_TIME, UnicodeBuff) ; ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ; strcat (TempBuff, pDelimiter) ; StringLen = strlen (TempBuff) ; } else { strcpy (TempBuff, LineEndStr) ; strcat (TempBuff, pDelimiter) ; strcat (TempBuff, pDelimiter) ; StringLen = strlen (TempBuff) ; } TimeToWriteFile = 0 ; for (pLine=pGraph->pLineFirst; pLine; pLine=pLine->pLineNext) { switch (iIndex) { case 0: lpItem = (LPTSTR) pLine->lnCounterName ; break ; case 1: lpItem = (LPTSTR) pLine->lnInstanceName ; break ; case 2: lpItem = (LPTSTR) pLine->lnPINName ; break ; case 3: lpItem = (LPTSTR) pLine->lnObjectName ; break ; case 4: lpItem = (LPTSTR) pLine->lnSystemName ; break ; } if (lpItem) { ConvertUnicodeStr (&TempBuff[StringLen], lpItem) ; } else { TempBuff[StringLen] = '\0' ; } strcat (TempBuff, pDelimiter); StringLen = strlen (TempBuff) ; if (++TimeToWriteFile > TIME_TO_WRITE) { // better write the buffers before they overflow. // there are better ways to check for overflow // but this is good enough if (!FileWrite (hFile, TempBuff, StringLen)) { goto Exit0 ; } StringLen = TimeToWriteFile = 0 ; } } // for each line if (StringLen) { // write the last block of data if (!FileWrite (hFile, TempBuff, StringLen)) { goto Exit0 ; } } } // for iIndex return (TRUE) ; Exit0: return (FALSE) ; } // ExportChartLabels BOOL ExportLogChart (HANDLE hFile, PGRAPHSTRUCT pGraph) { TCHAR UnicodeBuff [LongTextLen] ; CHAR TempBuff [LongTextLen * 2] ; int StringLen ; PLINESTRUCT pLine; int TimeToWriteFile ; FLOAT eValue ; int iLogTic ; BOOL bFirstTime = TRUE ; SYSTEMTIME LogSystemTime ; LOGPOSITION LogPosition ; iLogTic = PlaybackLog.StartIndexPos.iPosition ; // we have to export every point from the log file for ( ; iLogTic <= PlaybackLog.StopIndexPos.iPosition ; iLogTic++) { PlaybackLines (pGraphs->pSystemFirst, pGraphs->pLineFirst, iLogTic) ; if (!bFirstTime) { // export the values TimeToWriteFile = 0 ; if (!LogPositionN (iLogTic, &LogPosition)) { goto Exit0 ; } LogPositionSystemTime (&LogPosition, &LogSystemTime) ; strcpy (TempBuff, LineEndStr) ; StringLen = strlen (TempBuff) ; SystemTimeDateString (&LogSystemTime, UnicodeBuff) ; ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ; strcat (TempBuff, pDelimiter) ; StringLen = strlen (TempBuff) ; SystemTimeTimeString (&LogSystemTime, UnicodeBuff, FALSE) ; ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ; strcat (TempBuff, pDelimiter) ; StringLen = strlen (TempBuff) ; for (pLine=pGraph->pLineFirst; pLine; pLine=pLine->pLineNext) { eValue = CounterEntry (pLine) ; TSPRINTF (UnicodeBuff, eValue > (FLOAT)999999.0 ? szLargeValueFormat : szSmallValueFormat, eValue) ; ConvertDecimalPoint (UnicodeBuff) ; ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ; strcat (TempBuff, pDelimiter) ; StringLen = strlen (TempBuff) ; if (++TimeToWriteFile > TIME_TO_WRITE) { if (!FileWrite (hFile, TempBuff, StringLen)) { goto Exit0 ; } StringLen = TimeToWriteFile = 0 ; TempBuff[0] = '\0' ; } } if (StringLen) { if (!FileWrite (hFile, TempBuff, StringLen)) { goto Exit0 ; } } } else { // skip the first data point since we // need 2 points to form the first value bFirstTime = FALSE ; } } return (TRUE) ; Exit0: return (FALSE) ; } // ExportLogChart BOOL ExportLineValue (HANDLE hFile, PGRAPHSTRUCT pGraph, int CurrentIndex, int iDataPoint) { TCHAR UnicodeBuff [MiscTextLen] ; CHAR TempBuff [LongTextLen] ; int StringLen ; PLINESTRUCT pLine; int MaxValues ; int TimeToWriteFile ; SYSTEMTIME *pSystemTime ; BOOL ValidValue ; pSystemTime = pGraph->pDataTime ; pSystemTime += CurrentIndex ; if (pSystemTime->wYear == 0 && pSystemTime->wYear == 0) { // ignore value that has 0 system time return (TRUE) ; } MaxValues = pGraph->gMaxValues ; strcpy (TempBuff, LineEndStr) ; StringLen = strlen (TempBuff) ; SystemTimeDateString (pSystemTime, UnicodeBuff) ; ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ; strcat (TempBuff, pDelimiter) ; StringLen = strlen (TempBuff) ; SystemTimeTimeString (pSystemTime, UnicodeBuff, FALSE) ; ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ; strcat (TempBuff, pDelimiter) ; StringLen = strlen (TempBuff) ; TimeToWriteFile = 0 ; for (pLine=pGraph->pLineFirst; pLine; pLine=pLine->pLineNext) { if (!pLine->bFirstTime) { ValidValue = FALSE ; // check if this is a valid value if (pLine->lnValidValues == MaxValues) { // this is the simple case where we have filled up // the whole buffer ValidValue = TRUE ; } else if (pLine->lnValidValues <= iDataPoint) { if (CurrentIndex <= iDataPoint && CurrentIndex > iDataPoint - pLine->lnValidValues) { ValidValue = TRUE ; } } else { if (CurrentIndex <= iDataPoint || CurrentIndex > (MaxValues - pLine->lnValidValues + iDataPoint)) { // this is the case when we start the new line in the middle // and data buffer has been wrap-around. ValidValue = TRUE ; } } // only export the data when we determine it is valid if (ValidValue) { TSPRINTF (UnicodeBuff, pLine->lnValues[CurrentIndex] > (FLOAT)999999.0 ? szLargeValueFormat : szSmallValueFormat, pLine->lnValues[CurrentIndex]) ; ConvertDecimalPoint (UnicodeBuff) ; ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ; } } strcat (TempBuff, pDelimiter) ; StringLen = strlen (TempBuff) ; if (++TimeToWriteFile > TIME_TO_WRITE) { // better write the buffers before they overflow. // there are better ways to check for overflow // but this is good enough if (!FileWrite (hFile, TempBuff, StringLen)) { goto Exit0 ; } StringLen = TimeToWriteFile = 0 ; TempBuff[0] = '\0' ; } } if (StringLen) { // write the last block of data if (!FileWrite (hFile, TempBuff, StringLen)) { goto Exit0 ; } } return (TRUE) ; Exit0: return (FALSE) ; } // ExportLineValue BOOL ExportCurrentChart (HANDLE hFile, PGRAPHSTRUCT pGraph) { int KnownValue, MaxValues, iValidValues, iDataPoint ; BOOL SimpleCase = FALSE ; int iIndex ; MaxValues = pGraph->gMaxValues ; KnownValue = pGraph->gKnownValue ; iValidValues = pGraph->gTimeLine.iValidValues ; if (iValidValues < MaxValues) { // data have not wrapped around, so the oldest time // is started at 0. SimpleCase = TRUE ; iValidValues = (KnownValue % MaxValues) + 1 ; } iDataPoint = KnownValue % MaxValues ; if (!SimpleCase) { for (iIndex = iDataPoint+1 ; iIndex < MaxValues ; iIndex++) { if (!ExportLineValue (hFile, pGraph, iIndex, iDataPoint)) { goto Exit0 ; } } } for (iIndex = 0 ; iIndex <= iDataPoint ; iIndex++) { if (!ExportLineValue (hFile, pGraph, iIndex, iDataPoint)) { goto Exit0 ; } } return (TRUE) ; Exit0: return (FALSE) ; } // ExportCurrentChart void ExportChart (void) { PGRAPHSTRUCT pGraph ; HANDLE hFile = 0 ; LPTSTR pFileName = NULL ; INT ErrCode = 0 ; if (!(pGraph = pGraphs)) { return ; } // see if there is anything to export.. if (!(pGraph->pLineFirst)) { return ; } SetHourglassCursor() ; if (ErrCode = ExportFileOpen (hWndGraph, &hFile, pGraph->gInterval, &pFileName)) { goto Exit0 ; } if (!pFileName) { // this is the case when user cancel. goto Exit0 ; } // export the column labels if (!ExportChartLabels (hFile, pGraph)) { ErrCode = ERR_EXPORT_FILE ; goto Exit0 ; } // export the lines if (PlayingBackLog()) { if (!ExportLogChart (hFile, pGraph)) { ErrCode = ERR_EXPORT_FILE ; goto Exit0 ; } } else { if (!ExportCurrentChart (hFile, pGraph)) { ErrCode = ERR_EXPORT_FILE ; goto Exit0 ; } } Exit0: SetArrowCursor() ; if (hFile) { CloseHandle (hFile) ; } if (pFileName) { if (ErrCode) { DlgErrorBox (hWndGraph, ErrCode, pFileName) ; } MemoryFree (pFileName) ; } } // ExportChart typedef struct CHARTDATAPOINTSTRUCT { int iLogIndex ; int xDispDataPoint ; } CHARTDATAPOINT, *PCHARTDATAPOINT ; void PlaybackChartDataPoint (PCHARTDATAPOINT pChartDataPoint) { // PlaybackChartDataPoint int iDisplayTics ; // num visual points to display int iDisplayTic ; int iLogTic ; int iLogTicsMove ; BOOL bFirstTime = TRUE; int iLogTicsRemaining ; int numOfData, xDispDataPoint, rectWidth, xPos ; PGRAPHSTRUCT pGraph ; pGraph = GraphData (hWndGraph) ; iLogTicsRemaining = PlaybackLog.iSelectedTics ; // we only have iDisplayTics-1 points since // we have to use the first two sample points to // get the first data points. if (iLogTicsRemaining <= pGraphs->gMaxValues) { iDisplayTics = iLogTicsRemaining ; } else { iDisplayTics = pGraphs->gMaxValues ; } iDisplayTic = -1 ; iLogTic = PlaybackLog.StartIndexPos.iPosition ; numOfData = pGraph->gMaxValues - 1 ; rectWidth = pGraph->rectData.right - pGraph->rectData.left ; xDispDataPoint = pGraph->rectData.left ; while (iDisplayTics && numOfData) { if (!bFirstTime) { iDisplayTic++ ; } else { bFirstTime = FALSE ; // get the second sample data to form the first data point iLogTic++ ; iLogTicsRemaining-- ; iDisplayTic++ ; } pChartDataPoint[iDisplayTic].iLogIndex = iLogTic ; pChartDataPoint[iDisplayTic].xDispDataPoint = xDispDataPoint ; // setup DDA to get the index of the next sample point iLogTicsMove = DDA_DISTRIBUTE (iLogTicsRemaining, iDisplayTics) ; iLogTicsRemaining -= iLogTicsMove ; iLogTic += iLogTicsMove ; xPos = DDA_DISTRIBUTE (rectWidth, numOfData) ; xDispDataPoint += xPos ; numOfData-- ; rectWidth -= xPos ; iDisplayTics-- ; } // while } // PlaybackChartDataPoint