- Changes in logging and debug output
[public/netxms.git] / src / console / win32 / Graph.cpp
CommitLineData
23e62c09
VK
1// Graph.cpp : implementation file
2//
3
4#include "stdafx.h"
5#include "nxcon.h"
6#include "Graph.h"
7#include <math.h>
8
42d7ed00 9#define ROW_DATA(row, dt) ((dt == DCI_DT_STRING) ? strtod(row->value.szString, NULL) : \
b1dd534d
VK
10 (((dt == DCI_DT_INT) || (dt == DCI_DT_UINT)) ? row->value.dwInt32 : \
11 (((dt == DCI_DT_INT64) || (dt == DCI_DT_UINT64)) ? row->value.qwInt64 : \
42d7ed00 12 ((dt == DCI_DT_FLOAT) ? row->value.dFloat : 0) \
23e62c09
VK
13 ) \
14 ) \
15 )
16
17#ifdef _DEBUG
18#define new DEBUG_NEW
19#undef THIS_FILE
20static char THIS_FILE[] = __FILE__;
21#endif
22
23/////////////////////////////////////////////////////////////////////////////
24// CGraph
25
26CGraph::CGraph()
27{
28 m_iGridSize = 40;
29 m_dMaxValue = 100;
30 m_bAutoScale = TRUE;
31 m_bShowGrid = TRUE;
32 m_dwNumItems = 0;
806ada5f
VK
33 /*m_rgbBkColor = RGB(200,200,200);
34 m_rgbGridColor = RGB(64, 64, 64);
35 m_rgbAxisColor = RGB(0, 0, 0);
36 m_rgbTextColor = RGB(0, 0, 0);
37 m_rgbLineColors[0] = RGB(0, 127, 0);*/
23e62c09
VK
38 m_rgbBkColor = RGB(0,0,0);
39 m_rgbGridColor = RGB(64, 64, 64);
40 m_rgbAxisColor = RGB(127, 127, 127);
41 m_rgbTextColor = RGB(255, 255, 255);
42 m_rgbLineColors[0] = RGB(0, 255, 0);
0890540a
VK
43 m_rgbLabelBkColor = RGB(255, 255, 170);
44 m_rgbLabelTextColor = RGB(85, 0, 0);
23e62c09 45 memset(m_pData, 0, sizeof(NXC_DCI_DATA *) * MAX_GRAPH_ITEMS);
0890540a
VK
46 m_bIsActive = FALSE;
47 memset(&m_rectInfo, 0, sizeof(RECT));
23e62c09
VK
48}
49
50CGraph::~CGraph()
51{
52 DWORD i;
53
54 for(i = 0; i < MAX_GRAPH_ITEMS; i++)
55 if (m_pData[i] != NULL)
56 NXCDestroyDCIData(m_pData[i]);
57}
58
59
60BEGIN_MESSAGE_MAP(CGraph, CWnd)
61 //{{AFX_MSG_MAP(CGraph)
62 ON_WM_PAINT()
0890540a
VK
63 ON_WM_MOUSEMOVE()
64 ON_WM_SETFOCUS()
65 ON_WM_KILLFOCUS()
23e62c09 66 //}}AFX_MSG_MAP
0890540a 67 ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
23e62c09
VK
68END_MESSAGE_MAP()
69
70
71/////////////////////////////////////////////////////////////////////////////
72// CGraph message handlers
73
74
75BOOL CGraph::Create(DWORD dwStyle, const RECT &rect, CWnd *pwndParent, int nId)
76{
77 return CWnd::Create(NULL, "", dwStyle, rect, pwndParent, nId);
78}
79
80
81//
82// Overloaded PreCreateWindow()
83//
84
85BOOL CGraph::PreCreateWindow(CREATESTRUCT& cs)
86{
87 if (!CWnd::PreCreateWindow(cs))
88 return FALSE;
89
90 cs.dwExStyle |= WS_EX_CLIENTEDGE;
91 cs.style &= ~WS_BORDER;
92 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,
93 ::LoadCursor(NULL, IDC_ARROW), ::CreateSolidBrush(RGB(0, 0, 0)), NULL);
94
95 return TRUE;
96}
97
98
99//
100// WM_PAINT message handler
101//
102
103void CGraph::OnPaint()
104{
105 CPaintDC sdc(this); // original device context for painting
106 CDC dc; // In-memory dc
107 CBitmap bitmap; // Bitmap for in-memory drawing
108 CBitmap *pOldBitmap;
109 CPen pen, *pOldPen;
110 CFont font, *pOldFont;
806ada5f 111 CBrush brush;
23e62c09
VK
112 RECT rect;
113 CSize textSize;
114 DWORD i, dwTimeStamp;
115 int iLeftMargin, iBottomMargin, iRightMargin = 5, iTopMargin = 5;
116 int x, y, iTimeLen, iStep, iGraphLen;
117 double dStep, dMark;
118 char szBuffer[256];
119
120 GetClientRect(&rect);
121
122 dc.CreateCompatibleDC(&sdc);
123 bitmap.CreateCompatibleBitmap(&sdc, rect.right, rect.bottom);
124 pOldBitmap = dc.SelectObject(&bitmap);
125 dc.SetBkColor(m_rgbBkColor);
126
806ada5f
VK
127 // Fill background
128 brush.CreateSolidBrush(m_rgbBkColor);
129 dc.FillRect(&rect, &brush);
130
23e62c09
VK
131 // Setup text parameters
132 font.CreateFont(-MulDiv(7, GetDeviceCaps(GetDC()->m_hDC, LOGPIXELSY), 72),
133 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
134 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY,
135 VARIABLE_PITCH | FF_DONTCARE, "Verdana");
136 pOldFont = dc.SelectObject(&font);
137 dc.SetTextColor(m_rgbTextColor);
138
139 // Calculate text size and left margin
140 textSize = dc.GetTextExtent("00000.000");
141 iTimeLen = dc.GetTextExtent("00:00:00").cx;
142 iLeftMargin = textSize.cx + 10;
143 iBottomMargin = textSize.cy + 8;
144
145 // Draw grid
146 if (m_bShowGrid)
147 {
148 pen.CreatePen(PS_ALTERNATE | PS_COSMETIC, 0, m_rgbGridColor);
149 pOldPen = dc.SelectObject(&pen);
150 for(x = iLeftMargin + m_iGridSize; x < rect.right - iRightMargin; x += m_iGridSize)
151 {
152 dc.MoveTo(x, rect.bottom - iBottomMargin);
153 dc.LineTo(x, iTopMargin);
154 }
155 for(y = rect.bottom - iBottomMargin - m_iGridSize; y > iTopMargin; y -= m_iGridSize)
156 {
157 dc.MoveTo(iLeftMargin, y);
158 dc.LineTo(rect.right - iRightMargin, y);
159 }
160 dc.SelectObject(pOldPen);
161 pen.DeleteObject();
162 }
163
164 // Draw all parameters
165 ///////////////////////////////////
166
167 // Calculate data rectangle
0890540a
VK
168 memcpy(&m_rectGraph, &rect, sizeof(RECT));
169 m_rectGraph.left += iLeftMargin;
170 m_rectGraph.top += iTopMargin;
171 m_rectGraph.right -= iRightMargin;
172 m_rectGraph.bottom -= iBottomMargin;
173 iGraphLen = m_rectGraph.right - m_rectGraph.left + 1; // Actual data area length in pixels
23e62c09
VK
174 m_dSecondsPerPixel = (double)(m_dwTimeTo - m_dwTimeFrom) / (double)iGraphLen;
175
176 // Calculate max graph value
177 if (m_bAutoScale)
178 {
179 for(i = 0, m_dCurrMaxValue = 0; i < MAX_GRAPH_ITEMS; i++)
180 if (m_pData[i] != NULL)
181 {
182 NXC_DCI_ROW *pRow;
183 double dCurrValue;
184 DWORD j;
185
186 pRow = m_pData[i]->pRows;
187 for(j = 0; (j < m_pData[i]->dwNumRows) && (pRow->dwTimeStamp >= m_dwTimeFrom); j++)
188 {
189 dCurrValue = (double)ROW_DATA(pRow, m_pData[i]->wDataType);
190 if (dCurrValue > m_dCurrMaxValue)
191 m_dCurrMaxValue = dCurrValue;
192 inc_ptr(pRow, m_pData[i]->wRowSize, NXC_DCI_ROW);
193 }
194 }
195
196 if (m_dCurrMaxValue == 0)
197 m_dCurrMaxValue = 1;
198
199 // Round max value
200 for(double d = 0.0001; d < 1000000; d *= 10)
201 if ((m_dCurrMaxValue >= d) && (m_dCurrMaxValue <= d * 10))
202 {
203 m_dCurrMaxValue -= fmod(m_dCurrMaxValue, d);
204 m_dCurrMaxValue += d;
205 break;
206 }
207 }
208 else
209 {
210 m_dCurrMaxValue = m_dMaxValue;
211 }
212
213 // Draw each parameter
214 CRgn rgn;
0890540a 215 rgn.CreateRectRgn(m_rectGraph.left, m_rectGraph.top, m_rectGraph.right, m_rectGraph.bottom);
23e62c09
VK
216 dc.SelectClipRgn(&rgn);
217 for(i = 0; i < MAX_GRAPH_ITEMS; i++)
218 if (m_pData[i] != NULL)
0890540a 219 DrawLineGraph(dc, m_pData[i], m_rgbLineColors[i]);
23e62c09
VK
220 dc.SelectClipRgn(NULL);
221 rgn.DeleteObject();
222
23e62c09
VK
223 // Paint ordinates
224 pen.CreatePen(PS_SOLID, 3, m_rgbAxisColor);
225 pOldPen = dc.SelectObject(&pen);
226 dc.MoveTo(iLeftMargin, rect.bottom - iBottomMargin);
227 dc.LineTo(iLeftMargin, iTopMargin);
228 dc.MoveTo(iLeftMargin, rect.bottom - iBottomMargin);
229 dc.LineTo(rect.right - iRightMargin, rect.bottom - iBottomMargin);
230 dc.SelectObject(pOldPen);
231 pen.DeleteObject();
232
233 // Display ordinate marks
234 dStep = m_dCurrMaxValue / ((rect.bottom - iBottomMargin - iTopMargin) / m_iGridSize);
235 for(y = rect.bottom - iBottomMargin - textSize.cy / 2, dMark = 0; y > iTopMargin; y -= m_iGridSize * 2, dMark += dStep * 2)
236 {
237 sprintf(szBuffer, "%5.3f", dMark);
238 CSize cz = dc.GetTextExtent(szBuffer);
239 dc.TextOut(iLeftMargin - cz.cx - 5, y, szBuffer);
240 }
241
242 // Display absciss marks
243 y = rect.bottom - iBottomMargin + 3;
244 iStep = iTimeLen / m_iGridSize + 1; // How many grid lines we should skip
245 for(x = iLeftMargin; x < rect.right - iRightMargin; x += m_iGridSize * iStep)
246 {
247 dwTimeStamp = m_dwTimeFrom + (DWORD)((double)(x - iLeftMargin) * m_dSecondsPerPixel);
248 FormatTimeStamp(dwTimeStamp, szBuffer, TS_LONG_TIME);
249 dc.TextOut(x, y, szBuffer);
250 }
251
252 // Move drawing from in-memory DC to screen
253 sdc.BitBlt(0, 0, rect.right, rect.bottom, &dc, 0, 0, SRCCOPY);
254
255 // Cleanup
256 dc.SelectObject(pOldFont);
257 dc.SelectObject(pOldBitmap);
258 bitmap.DeleteObject();
259 font.DeleteObject();
260 dc.DeleteDC();
261}
262
263
264//
265// Set time frame this graph covers
266//
267
268void CGraph::SetTimeFrame(DWORD dwTimeFrom, DWORD dwTimeTo)
269{
270 m_dwTimeFrom = dwTimeFrom;
271 m_dwTimeTo = dwTimeTo;
272}
273
274
275//
276// Set data for specific item
277//
278
279void CGraph::SetData(DWORD dwIndex, NXC_DCI_DATA *pData)
280{
281 if (dwIndex < MAX_GRAPH_ITEMS)
282 {
283 if (m_pData[dwIndex] != NULL)
284 NXCDestroyDCIData(m_pData[dwIndex]);
285 m_pData[dwIndex] = pData;
286 }
287}
288
289
290//
291// Draw single line
292//
293
0890540a 294void CGraph::DrawLineGraph(CDC &dc, NXC_DCI_DATA *pData, COLORREF rgbColor)
23e62c09
VK
295{
296 DWORD i;
297 int x;
298 CPen pen, *pOldPen;
299 NXC_DCI_ROW *pRow;
300 double dScale;
301
302 if (pData->dwNumRows < 2)
303 return; // Nothing to draw
304
806ada5f 305 pen.CreatePen(PS_SOLID, 2, rgbColor);
23e62c09
VK
306 pOldPen = dc.SelectObject(&pen);
307
308 // Calculate scale factor for values
0890540a
VK
309 dScale = (double)(m_rectGraph.bottom - m_rectGraph.top -
310 (m_rectGraph.bottom - m_rectGraph.top) % m_iGridSize) / m_dCurrMaxValue;
23e62c09
VK
311
312 // Move to first position
313 pRow = pData->pRows;
0890540a
VK
314 dc.MoveTo(m_rectGraph.right,
315 (int)(m_rectGraph.bottom - (double)ROW_DATA(pRow, pData->wDataType) * dScale - 1));
23e62c09
VK
316 inc_ptr(pRow, pData->wRowSize, NXC_DCI_ROW);
317
318 for(i = 1; (i < pData->dwNumRows) && (pRow->dwTimeStamp >= m_dwTimeFrom); i++)
319 {
320 // Calculate timestamp position on graph
0890540a
VK
321 x = m_rectGraph.right - (int)((double)(m_dwTimeTo - pRow->dwTimeStamp) / m_dSecondsPerPixel);
322 dc.LineTo(x, (int)(m_rectGraph.bottom - (double)ROW_DATA(pRow, pData->wDataType) * dScale - 1));
23e62c09
VK
323 inc_ptr(pRow, pData->wRowSize, NXC_DCI_ROW);
324 }
325
326 dc.SelectObject(pOldPen);
327}
0890540a
VK
328
329
330//
331// Set mouse tracking parameters
332//
333
334void CGraph::SetMouseTracking()
335{
336 TRACKMOUSEEVENT tme;
337
338 tme.cbSize = sizeof(TRACKMOUSEEVENT);
339 tme.dwFlags = TME_HOVER;
340 tme.hwndTrack = m_hWnd;
341 tme.dwHoverTime = 1000;
342 _TrackMouseEvent(&tme);
343}
344
345
346//
347// WM_MOUSEHOVER message handler
348//
349
350int CGraph::OnMouseHover(WPARAM wParam, LPARAM lParam)
351{
352 POINTS pt;
353
354 pt = MAKEPOINTS(lParam);
355 m_ptLastHoverPoint = CPoint(pt.x, pt.y);
356 if (PtInRect(&m_rectGraph, m_ptLastHoverPoint))
357 DrawInfoRect(pt);
358 return 0;
359}
360
361
362//
363// WM_MOUSEMOVE message handler
364//
365
366void CGraph::OnMouseMove(UINT nFlags, CPoint point)
367{
368 CWnd::OnMouseMove(nFlags, point);
369 if ((m_bIsActive) && (m_ptLastHoverPoint != point))
370 {
371 SetMouseTracking();
372 InvalidateRect(&m_rectInfo, FALSE);
373 memset(&m_rectInfo, 0, sizeof(RECT));
374 }
375}
376
377
378//
379// WM_SETFOCUS message handler
380//
381
382void CGraph::OnSetFocus(CWnd* pOldWnd)
383{
384 CWnd::OnSetFocus(pOldWnd);
385 m_bIsActive = TRUE;
386 SetMouseTracking();
387}
388
389
390//
391// WM_KILLFOCUS message handler
392//
393
394void CGraph::OnKillFocus(CWnd* pNewWnd)
395{
396 CWnd::OnKillFocus(pNewWnd);
397 m_bIsActive = FALSE;
398 InvalidateRect(&m_rectInfo, FALSE);
399 memset(&m_rectInfo, 0, sizeof(RECT));
400}
401
402
403//
404// Draw information rectangle at mouse coordinates
405//
406
407void CGraph::DrawInfoRect(POINTS pt)
408{
409 CDC *pDC;
410 CSize size;
411 char szBuffer[256], szTime[32];
412 DWORD dwTimeStamp;
413 double dValue;
414
415 // Prepare text for output
416 dwTimeStamp = m_dwTimeFrom + (DWORD)((pt.x - m_rectGraph.left) * m_dSecondsPerPixel);
417 dValue = m_dCurrMaxValue / (m_rectGraph.bottom - m_rectGraph.top -
418 (m_rectGraph.bottom - m_rectGraph.top) % m_iGridSize) *
419 (m_rectGraph.bottom - pt.y);
420 sprintf(szBuffer, "%s %5.3f", FormatTimeStamp(dwTimeStamp, szTime, TS_LONG_TIME), dValue);
421
422 // Prepare for drawing
423 pDC = GetDC();
424 pDC->SetTextColor(m_rgbLabelTextColor);
425 pDC->SetBkColor(m_rgbLabelBkColor);
426
427 // Calculate text size and draw it
428 size = pDC->GetTextExtent(szBuffer, strlen(szBuffer));
429 pDC->TextOut(pt.x, pt.y - size.cy - 1, szBuffer, strlen(szBuffer));
430
431 // Fill rectangle to be invalidated
432 m_rectInfo.left = pt.x;
433 m_rectInfo.top = pt.y - size.cy - 1;
434 m_rectInfo.right = pt.x + size.cx + 1;
435 m_rectInfo.bottom = pt.y;
436
437 // Cleanup
438 ReleaseDC(pDC);
439}