- Changes in logging and debug output
[public/netxms.git] / src / console / win32 / Graph.cpp
1 // Graph.cpp : implementation file
2 //
3
4 #include "stdafx.h"
5 #include "nxcon.h"
6 #include "Graph.h"
7 #include <math.h>
8
9 #define ROW_DATA(row, dt) ((dt == DCI_DT_STRING) ? strtod(row->value.szString, NULL) : \
10 (((dt == DCI_DT_INT) || (dt == DCI_DT_UINT)) ? row->value.dwInt32 : \
11 (((dt == DCI_DT_INT64) || (dt == DCI_DT_UINT64)) ? row->value.qwInt64 : \
12 ((dt == DCI_DT_FLOAT) ? row->value.dFloat : 0) \
13 ) \
14 ) \
15 )
16
17 #ifdef _DEBUG
18 #define new DEBUG_NEW
19 #undef THIS_FILE
20 static char THIS_FILE[] = __FILE__;
21 #endif
22
23 /////////////////////////////////////////////////////////////////////////////
24 // CGraph
25
26 CGraph::CGraph()
27 {
28 m_iGridSize = 40;
29 m_dMaxValue = 100;
30 m_bAutoScale = TRUE;
31 m_bShowGrid = TRUE;
32 m_dwNumItems = 0;
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);*/
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);
43 m_rgbLabelBkColor = RGB(255, 255, 170);
44 m_rgbLabelTextColor = RGB(85, 0, 0);
45 memset(m_pData, 0, sizeof(NXC_DCI_DATA *) * MAX_GRAPH_ITEMS);
46 m_bIsActive = FALSE;
47 memset(&m_rectInfo, 0, sizeof(RECT));
48 }
49
50 CGraph::~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
60 BEGIN_MESSAGE_MAP(CGraph, CWnd)
61 //{{AFX_MSG_MAP(CGraph)
62 ON_WM_PAINT()
63 ON_WM_MOUSEMOVE()
64 ON_WM_SETFOCUS()
65 ON_WM_KILLFOCUS()
66 //}}AFX_MSG_MAP
67 ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
68 END_MESSAGE_MAP()
69
70
71 /////////////////////////////////////////////////////////////////////////////
72 // CGraph message handlers
73
74
75 BOOL 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
85 BOOL 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
103 void 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;
111 CBrush brush;
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
127 // Fill background
128 brush.CreateSolidBrush(m_rgbBkColor);
129 dc.FillRect(&rect, &brush);
130
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
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
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;
215 rgn.CreateRectRgn(m_rectGraph.left, m_rectGraph.top, m_rectGraph.right, m_rectGraph.bottom);
216 dc.SelectClipRgn(&rgn);
217 for(i = 0; i < MAX_GRAPH_ITEMS; i++)
218 if (m_pData[i] != NULL)
219 DrawLineGraph(dc, m_pData[i], m_rgbLineColors[i]);
220 dc.SelectClipRgn(NULL);
221 rgn.DeleteObject();
222
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
268 void 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
279 void 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
294 void CGraph::DrawLineGraph(CDC &dc, NXC_DCI_DATA *pData, COLORREF rgbColor)
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
305 pen.CreatePen(PS_SOLID, 2, rgbColor);
306 pOldPen = dc.SelectObject(&pen);
307
308 // Calculate scale factor for values
309 dScale = (double)(m_rectGraph.bottom - m_rectGraph.top -
310 (m_rectGraph.bottom - m_rectGraph.top) % m_iGridSize) / m_dCurrMaxValue;
311
312 // Move to first position
313 pRow = pData->pRows;
314 dc.MoveTo(m_rectGraph.right,
315 (int)(m_rectGraph.bottom - (double)ROW_DATA(pRow, pData->wDataType) * dScale - 1));
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
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));
323 inc_ptr(pRow, pData->wRowSize, NXC_DCI_ROW);
324 }
325
326 dc.SelectObject(pOldPen);
327 }
328
329
330 //
331 // Set mouse tracking parameters
332 //
333
334 void 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
350 int 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
366 void 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
382 void 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
394 void 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
407 void 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 }