/*****************************************************************************
*
* 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 1992 - 1998 Microsoft Corporation
*
*
****************************************************************************/
//==========================================================================//
// Includes //
//==========================================================================//
#include <stdio.h> // for sprintf
#include "perfmon.h" // main perfmon declarations
#include "grafdata.h" // external declarations for this file
#include <float.h> // 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 ;
}
}
// 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 ;
pGraph->gMaxValues = DEFAULT_MAX_VALUES;
pGraph->pptDataPoints =
(PPOINT) MemoryAllocate (sizeof (POINT) * pGraph->gMaxValues) ;
pGraph->pDataTime =
(SYSTEMTIME *) MemoryAllocate (sizeof(SYSTEMTIME) * pGraph->gMaxValues) ;
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)
{
pLineEquivalent->Visual = pLine->Visual ;
pLineEquivalent->iScaleIndex = pLine->iScaleIndex ;
pLineEquivalent->eScale = pLine->eScale ;
tempPen = pLineEquivalent->hPen ;
pLineEquivalent->hPen = pLine->hPen ;
pLine->hPen = tempPen ;
return FALSE ;
}
else
{
if (!pGraph->pLineFirst && !PlayingBackLog())
{
SetGraphTimer (pGraph) ;
}
if (!SystemAdd (&pGraph->pSystemFirst, pLine->lnSystemName))
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) ;
for (i = pGraph->gMaxValues, pTempPts = pLine->lnValues ;
i > 0 ;
i-- )
{
*pTempPts++ = (FLOAT) 0.0 ;
}
if (PlayingBackLog ())
{
pLine->aiLogIndexes =
(int *) MemoryAllocate (sizeof (LONG) * pGraph->gMaxValues) ;
}
// 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) ;
}
FLOAT Counter_Queuelen(PLINESTRUCT pLine)
{
return((FLOAT)0.0);
// pLine;
}
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) ;
// 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) ;
}
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) ;
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 ;
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