VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/activity/vmactivity/UIVMActivityMonitor.cpp

Last change on this file was 106061, checked in by vboxsync, 2 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 87.2 KB
Line 
1/* $Id: UIVMActivityMonitor.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIVMActivityMonitor class implementation.
4 */
5
6/*
7 * Copyright (C) 2016-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QApplication>
30#include <QColorDialog>
31#include <QDateTime>
32#include <QLabel>
33#include <QMenu>
34#include <QPainter>
35#include <QGridLayout>
36#include <QScrollArea>
37#include <QStyle>
38#include <QToolTip>
39#include <QXmlStreamReader>
40#include <QTimer>
41
42/* GUI includes: */
43#include "QIFileDialog.h"
44#include "UIActionPool.h"
45#include "UICommon.h"
46#include "UIConverter.h"
47#include "UIExtraDataManager.h"
48#include "UIGlobalSession.h"
49#include "UIIconPool.h"
50#include "UILocalMachineStuff.h"
51#include "UITranslator.h"
52#include "UITranslationEventListener.h"
53#include "UIVMActivityMonitor.h"
54#include "UIVirtualBoxEventHandler.h"
55
56/* COM includes: */
57#include "CConsole.h"
58#include "CGuest.h"
59#include "CPerformanceCollector.h"
60#include "CPerformanceMetric.h"
61#include <iprt/path.h>
62
63/* External includes: */
64#include <math.h>
65
66/** The time in seconds between metric inquries done to API. */
67const ULONG g_iPeriod = 1;
68
69/** This is passed to IPerformanceCollector during its setup. When 1 that means IPerformanceCollector object does a data cache of size 1. */
70const int g_iMetricSetupCount = 1;
71const int g_iDecimalCount = 2;
72const int g_iBackgroundTint = 104;
73const quint64 uInvalidValueSentinel = ~0U;
74
75
76/*********************************************************************************************************************************
77* UIChart definition. *
78*********************************************************************************************************************************/
79
80class UIChart : public QWidget
81{
82
83 Q_OBJECT;
84
85signals:
86
87 void sigExportMetricsToFile();
88
89public:
90
91 UIChart(QWidget *pParent, UIMetric *pMetric, UIActionPool *pActionPool, int iMaximumQueueSize);
92 void setFontSize(int iFontSize);
93 int fontSize() const;
94 const QStringList &textList() const;
95
96 bool isPieChartAllowed() const;
97 void setIsPieChartAllowed(bool fWithPieChart);
98
99 bool usePieChart() const;
100 void setShowPieChart(bool fShowPieChart);
101
102 bool useGradientLineColor() const;
103 void setUseGradientLineColor(bool fUseGradintLineColor);
104
105 bool useAreaChart() const;
106 void setUseAreaChart(bool fUseAreaChart);
107
108 bool isAreaChartAllowed() const;
109 void setIsAreaChartAllowed(bool fIsAreaChartAllowed);
110
111 QColor dataSeriesColor(int iDataSeriesIndex, int iDark = 0);
112 void setDataSeriesColor(int iDataSeriesIndex, const QColor &color);
113
114 QString XAxisLabel();
115 void setXAxisLabel(const QString &strLabel);
116
117 bool isAvailable() const;
118 void setIsAvailable(bool fIsAvailable);
119
120protected:
121
122 virtual void resizeEvent(QResizeEvent *pEvent) RT_OVERRIDE;
123 virtual void mouseMoveEvent(QMouseEvent *pEvent) RT_OVERRIDE;
124 virtual void paintEvent(QPaintEvent *pEvent) RT_OVERRIDE;
125 virtual QSize minimumSizeHint() const RT_OVERRIDE;
126 virtual QSize sizeHint() const RT_OVERRIDE;
127 virtual bool event(QEvent *pEvent) RT_OVERRIDE;
128
129private slots:
130
131 void sltCreateContextMenu(const QPoint &point);
132 void sltResetMetric();
133 void sltSetShowPieChart(bool fShowPieChart);
134 void sltSetUseAreaChart(bool fUseAreaChart);
135 void sltRetranslateUI();
136
137private:
138
139 /** @name Drawing helper functions.
140 * @{ */
141 void drawXAxisLabels(QPainter &painter, int iXSubAxisCount);
142 void drawPieChart(QPainter &painter, quint64 iMaximum, int iDataIndex, const QRectF &chartRect, bool fWithBorder = true);
143 void drawCombinedPieCharts(QPainter &painter, quint64 iMaximum);
144 QString YAxisValueLabel(quint64 iValue) const;
145 /** Drawing an overlay rectangle over the charts to indicate that they are disabled. */
146 void drawDisabledChartRectangle(QPainter &painter);
147 QConicalGradient conicalGradientForDataSeries(const QRectF &rectangle, int iDataIndex);
148 /** @} */
149 int maxDataSize() const;
150 QString toolTipText() const;
151 bool isUnderMouse() const;
152 void updateIndexUnderCursor(const QPoint &point);
153 void updateToolTip();
154
155 UIMetric *m_pMetric;
156 QSize m_size;
157 QFont m_axisFont;
158 int m_iMarginLeft;
159 int m_iMarginRight;
160 int m_iMarginTop;
161 int m_iMarginBottom;
162 int m_iOverlayAlpha;
163 QRect m_lineChartRect;
164 int m_iPieChartRadius;
165 int m_iPieChartSpacing;
166 float m_fPixelPerDataPoint;
167 /** is set to -1 if mouse cursor is not over a data point*/
168 int m_iDataIndexUnderCursor;
169 /** For some chart it is not possible to have a pie chart, Then We dont present the
170 * option to show it to user. see m_fIsPieChartAllowed. */
171 bool m_fIsPieChartAllowed;
172 /** m_fShowPieChart is considered only if m_fIsPieChartAllowed is true. */
173 bool m_fShowPieChart;
174 bool m_fUseGradientLineColor;
175 /** When it is true we draw an area graph where data series drawn on top of each other.
176 * We draw first data0 then data 1 on top. Makes sense where the summation of data is guaranteed not to exceed some max. */
177 bool m_fUseAreaChart;
178 /** False if the chart is not useable for some reason. For example it depends guest additions and they are not installed. */
179 bool m_fIsAvailable;
180 /** For some charts it does not make sense to have an area chart. */
181 bool m_fIsAreaChartAllowed;
182 QColor m_dataSeriesColor[DATA_SERIES_SIZE];
183 QString m_strXAxisLabel;
184 QString m_strGAWarning;
185 QString m_strResetActionLabel;
186 QString m_strPieChartToggleActionLabel;
187 QString m_strAreaChartToggleActionLabel;
188 QString m_strSelectChartColor0;
189 QString m_strSelectChartColor1;
190 bool m_fDrawCurenValueIndicators;
191 /** The width of the right margin in characters. */
192 int m_iRightMarginCharWidth;
193 int m_iMaximumQueueSize;
194 QLabel *m_pMouseOverLabel;
195 UIActionPool *m_pActionPool;
196};
197
198/*********************************************************************************************************************************
199* UIChart implementation. *
200*********************************************************************************************************************************/
201
202UIChart::UIChart(QWidget *pParent, UIMetric *pMetric, UIActionPool *pActionPool, int iMaximumQueueSize)
203 : QWidget(pParent)
204 , m_pMetric(pMetric)
205 , m_size(QSize(50, 50))
206 , m_iOverlayAlpha(80)
207 , m_fPixelPerDataPoint(0.f)
208 , m_iDataIndexUnderCursor(-1)
209 , m_fIsPieChartAllowed(false)
210 , m_fShowPieChart(true)
211 , m_fUseGradientLineColor(false)
212 , m_fUseAreaChart(true)
213 , m_fIsAvailable(true)
214 , m_fIsAreaChartAllowed(false)
215 , m_fDrawCurenValueIndicators(true)
216 , m_iRightMarginCharWidth(10)
217 , m_iMaximumQueueSize(iMaximumQueueSize)
218 , m_pMouseOverLabel(0)
219 , m_pActionPool(pActionPool)
220{
221 QPalette tempPal = palette();
222 tempPal.setColor(QPalette::Window, tempPal.color(QPalette::Window).lighter(g_iBackgroundTint));
223 setPalette(tempPal);
224 setAutoFillBackground(true);
225
226 setToolTipDuration(-1);
227 m_axisFont = font();
228 m_axisFont.setPixelSize(14);
229 setContextMenuPolicy(Qt::CustomContextMenu);
230 setMouseTracking(true);
231 connect(this, &UIChart::customContextMenuRequested,
232 this, &UIChart::sltCreateContextMenu);
233
234 m_iMarginLeft = 3 * QFontMetricsF(m_axisFont).averageCharWidth();
235 m_iMarginRight = m_iRightMarginCharWidth * QFontMetricsF(m_axisFont).averageCharWidth();
236 m_iMarginTop = QFontMetrics(m_axisFont).height();
237 m_iMarginBottom = QFontMetrics(m_axisFont).height();
238
239 float fAppIconSize = qApp->style()->pixelMetric(QStyle::PM_LargeIconSize);
240 m_size = QSize(14 * fAppIconSize, 3.5 * fAppIconSize);
241 m_iPieChartSpacing = 2;
242 m_iPieChartRadius = m_size.height() - (m_iMarginTop + m_iMarginBottom + 2 * m_iPieChartSpacing);
243
244 m_pMouseOverLabel = new QLabel(this);
245 m_pMouseOverLabel->hide();
246 m_pMouseOverLabel->setFrameStyle(QFrame::Box);
247
248 m_pMouseOverLabel->setAutoFillBackground(true);
249 m_pMouseOverLabel->setMargin(0.1 * QStyle::PM_HeaderMargin);
250 sltRetranslateUI();
251 connect(&translationEventListener(), &UITranslationEventListener::sigRetranslateUI,
252 this, &UIChart::sltRetranslateUI);
253}
254
255bool UIChart::isPieChartAllowed() const
256{
257 return m_fIsPieChartAllowed;
258}
259
260void UIChart::setIsPieChartAllowed(bool fWithPieChart)
261{
262 if (m_fIsPieChartAllowed == fWithPieChart)
263 return;
264 m_fIsPieChartAllowed = fWithPieChart;
265 update();
266}
267
268bool UIChart::usePieChart() const
269{
270 return m_fShowPieChart;
271}
272
273void UIChart::setShowPieChart(bool fDrawChart)
274{
275 if (m_fShowPieChart == fDrawChart)
276 return;
277 m_fShowPieChart = fDrawChart;
278 update();
279}
280
281bool UIChart::useGradientLineColor() const
282{
283 return m_fUseGradientLineColor;
284}
285
286void UIChart::setUseGradientLineColor(bool fUseGradintLineColor)
287{
288 if (m_fUseGradientLineColor == fUseGradintLineColor)
289 return;
290 m_fUseGradientLineColor = fUseGradintLineColor;
291 update();
292}
293
294bool UIChart::useAreaChart() const
295{
296 return m_fUseAreaChart;
297}
298
299void UIChart::setUseAreaChart(bool fUseAreaChart)
300{
301 if (m_fUseAreaChart == fUseAreaChart)
302 return;
303 m_fUseAreaChart = fUseAreaChart;
304 update();
305}
306
307bool UIChart::isAreaChartAllowed() const
308{
309 return m_fIsAreaChartAllowed;
310}
311
312void UIChart::setIsAreaChartAllowed(bool fIsAreaChartAllowed)
313{
314 m_fIsAreaChartAllowed = fIsAreaChartAllowed;
315}
316
317QColor UIChart::dataSeriesColor(int iDataSeriesIndex, int iDark /* = 0 */)
318{
319 if (iDataSeriesIndex >= DATA_SERIES_SIZE)
320 return QColor();
321 return QColor(qMax(m_dataSeriesColor[iDataSeriesIndex].red() - iDark, 0),
322 qMax(m_dataSeriesColor[iDataSeriesIndex].green() - iDark, 0),
323 qMax(m_dataSeriesColor[iDataSeriesIndex].blue() - iDark, 0),
324 m_dataSeriesColor[iDataSeriesIndex].alpha());
325}
326
327void UIChart::setDataSeriesColor(int iDataSeriesIndex, const QColor &color)
328{
329 if (iDataSeriesIndex >= DATA_SERIES_SIZE)
330 return;
331 if (m_dataSeriesColor[iDataSeriesIndex] == color)
332 return;
333 m_dataSeriesColor[iDataSeriesIndex] = color;
334 update();
335}
336
337QString UIChart::XAxisLabel()
338{
339 return m_strXAxisLabel;
340}
341
342void UIChart::setXAxisLabel(const QString &strLabel)
343{
344 m_strXAxisLabel = strLabel;
345}
346
347bool UIChart::isAvailable() const
348{
349 return m_fIsAvailable;
350}
351
352void UIChart::setIsAvailable(bool fIsAvailable)
353{
354 if (m_fIsAvailable == fIsAvailable)
355 return;
356 m_fIsAvailable = fIsAvailable;
357 update();
358}
359
360QSize UIChart::minimumSizeHint() const
361{
362 return m_size;
363}
364
365QSize UIChart::sizeHint() const
366{
367 return m_size;
368}
369
370void UIChart::sltRetranslateUI()
371{
372 m_strGAWarning = QApplication::translate("UIVMInformationDialog", "This metric requires guest additions to work.");
373 m_strResetActionLabel = QApplication::translate("UIVMInformationDialog", "Reset");
374 m_strPieChartToggleActionLabel = QApplication::translate("UIVMInformationDialog", "Show Pie Chart");
375 m_strAreaChartToggleActionLabel = QApplication::translate("UIVMInformationDialog", "Draw Area Chart");
376 m_strSelectChartColor0 = QApplication::translate("UIVMInformationDialog", "Change 1st Dataseries' Color");
377 m_strSelectChartColor1 = QApplication::translate("UIVMInformationDialog", "Change 2nd Dataseries' Color");
378 update();
379}
380
381bool UIChart::event(QEvent *pEvent)
382{
383 if (pEvent->type() == QEvent::Leave)
384 {
385 if (m_pMouseOverLabel)
386 m_pMouseOverLabel->setVisible(false);
387 m_iDataIndexUnderCursor = -1;
388 }
389 else if (pEvent->type() == QEvent::ToolTip)
390 updateToolTip();
391
392 return QWidget::event(pEvent);
393}
394
395void UIChart::resizeEvent(QResizeEvent *pEvent)
396{
397 int iWidth = width() - m_iMarginLeft - m_iMarginRight;
398 if (m_iMaximumQueueSize > 0)
399 m_fPixelPerDataPoint = iWidth / (float)m_iMaximumQueueSize;
400 QWidget::resizeEvent(pEvent);
401}
402
403void UIChart::updateIndexUnderCursor(const QPoint &point)
404{
405 if (!isUnderMouse())
406 {
407 m_iDataIndexUnderCursor = -1;
408 return;
409 }
410 const int iX = width() - point.x() - m_iMarginRight;
411
412 int iDataSize = maxDataSize();
413 m_iDataIndexUnderCursor = -1;
414
415 if (iDataSize > 0 && m_lineChartRect.contains(point))
416 {
417 m_iDataIndexUnderCursor = m_iMaximumQueueSize - (int)((iX) / m_fPixelPerDataPoint) - 1;
418 m_iDataIndexUnderCursor = m_iDataIndexUnderCursor - (m_iMaximumQueueSize - iDataSize);
419 }
420}
421
422void UIChart::updateToolTip()
423{
424 updateIndexUnderCursor(mapFromGlobal(QCursor::pos()));
425 if (m_pMouseOverLabel)
426 {
427 if (m_iDataIndexUnderCursor < 0)
428 m_pMouseOverLabel->setVisible(false);
429 else
430 {
431
432 QString strToolTip = toolTipText();
433 QPoint pos = mapFromGlobal(QCursor::pos());
434
435 if (!strToolTip.isEmpty())
436 {
437 m_pMouseOverLabel->setText(strToolTip);
438 m_pMouseOverLabel->move(QPoint(pos.x(), pos.y() - m_pMouseOverLabel->height()));
439 m_pMouseOverLabel->setVisible(true);
440 m_pMouseOverLabel->adjustSize();
441 }
442 else
443 m_pMouseOverLabel->setVisible(false);
444 }
445 }
446}
447void UIChart::mouseMoveEvent(QMouseEvent *pEvent)
448{
449 QPoint eventPosition(pEvent->position().x(), pEvent->position().y());
450 updateIndexUnderCursor(eventPosition);
451 update();
452 QWidget::mouseMoveEvent(pEvent);
453}
454
455void UIChart::paintEvent(QPaintEvent *pEvent)
456{
457 if (isUnderMouse())
458 updateToolTip();
459
460 Q_UNUSED(pEvent);
461 if (!m_pMetric || m_iMaximumQueueSize <= 1)
462 return;
463
464 QPainter painter(this);
465 painter.setFont(m_axisFont);
466 painter.setRenderHint(QPainter::Antialiasing);
467
468 /* Draw a rectanglar grid over which we will draw the line graphs: */
469 QPoint chartTopLeft(m_iMarginLeft, m_iMarginTop);
470 QSize chartSize(width() - (m_iMarginLeft + m_iMarginRight), height() - (m_iMarginTop + m_iMarginBottom));
471
472 m_lineChartRect = QRect(chartTopLeft, chartSize);
473 QColor mainAxisColor(120, 120, 120);
474 QColor subAxisColor(200, 200, 200);
475 /* Draw the main axes: */
476 painter.setPen(mainAxisColor);
477 painter.drawRect(m_lineChartRect);
478
479 /* draw Y subaxes: */
480 painter.setPen(subAxisColor);
481 int iYSubAxisCount = 3;
482 for (int i = 0; i < iYSubAxisCount; ++i)
483 {
484 float fSubAxisY = m_iMarginTop + (i + 1) * m_lineChartRect.height() / (float) (iYSubAxisCount + 1);
485 painter.drawLine(m_lineChartRect.left(), fSubAxisY,
486 m_lineChartRect.right(), fSubAxisY);
487 }
488
489 /* draw X subaxes: */
490 int iXSubAxisCount = 5;
491 for (int i = 0; i < iXSubAxisCount; ++i)
492 {
493 float fSubAxisX = m_lineChartRect.left() + (i + 1) * m_lineChartRect.width() / (float) (iXSubAxisCount + 1);
494 painter.drawLine(fSubAxisX, m_lineChartRect.top(), fSubAxisX, m_lineChartRect.bottom());
495 }
496
497 /* Draw XAxis tick labels: */
498 painter.setPen(mainAxisColor);
499 drawXAxisLabels(painter, iXSubAxisCount);
500
501 if (!isEnabled())
502 return;
503
504 /* Draw a half-transparent rectangle over the whole widget to indicate the it is not available: */
505 if (!isAvailable())
506 {
507 drawDisabledChartRectangle(painter);
508 return;
509 }
510
511 quint64 iMaximum = m_pMetric->maximum();
512 QFontMetrics fontMetrics(painter.font());
513 int iFontHeight = fontMetrics.height();
514
515 /* Draw the data lines: */
516 float fBarWidth = m_lineChartRect.width() / (float) (m_iMaximumQueueSize - 1);
517 float fH = iMaximum == 0 ? 0 : m_lineChartRect.height() / (float)iMaximum;
518 const float fPenWidth = 1.5f;
519 const float fPointSize = 3.5f;
520 for (int k = 0; k < DATA_SERIES_SIZE; ++k)
521 {
522 if (m_fUseGradientLineColor)
523 {
524 QLinearGradient gradient(0, 0, 0, m_lineChartRect.height());
525 gradient.setColorAt(0, Qt::black);
526 gradient.setColorAt(1, m_dataSeriesColor[k]);
527 painter.setPen(QPen(gradient, fPenWidth));
528 }
529 const QQueue<quint64> *data = m_pMetric->data(k);
530 if (!m_fUseGradientLineColor)
531 painter.setPen(QPen(m_dataSeriesColor[k], fPenWidth));
532 if (m_fUseAreaChart && m_fIsAreaChartAllowed)
533 {
534 QVector<QPointF> points;
535 /* Create two polygons. Polygon of data series 0 is on top of the one of 1st data series: */
536 /* We add the points to vector in counter clockwise fashion: */
537 if (k == 0)
538 {
539 for (int i = 0; i < data->size(); ++i)
540 {
541 float fX = (width() - m_iMarginRight) - ((data->size() - i - 1) * fBarWidth);
542 float fHeight = fH * data->at(i);
543 if (m_pMetric->data(1) && m_pMetric->data(1)->size() > i)
544 fHeight += fH * m_pMetric->data(1)->at(i);
545 points << QPointF(fX, height() - (fHeight + m_iMarginBottom));
546 }
547 for (int i = data->size() - 1; i >= 0; --i)
548 {
549 float fX = (width() - m_iMarginRight) - ((data->size() - i - 1) * fBarWidth);
550 float fHeight = 0;
551 if (m_pMetric->data(1) && m_pMetric->data(1)->size() > i)
552 fHeight = fH * m_pMetric->data(1)->at(i);
553 points << QPointF(fX, height() - (fHeight + m_iMarginBottom));
554 }
555 }
556 else if (k == 1)
557 {
558 for (int i = 0; i < data->size(); ++i)
559 {
560 float fHeight = fH * data->at(i);
561 float fX = (width() - m_iMarginRight) - ((data->size() - i - 1) * fBarWidth);
562 if (i == 0)
563 points << QPointF(fX, height() - m_iMarginBottom);
564 points << QPointF(fX, height() - (fHeight + m_iMarginBottom));
565 if (i == data->size() - 1)
566 points << QPointF(fX, height() - + m_iMarginBottom);
567 }
568 }
569 painter.setOpacity(0.6);
570 painter.setPen(Qt::NoPen);
571 painter.setBrush(m_dataSeriesColor[k]);
572 painter.drawPolygon(points, Qt::WindingFill);
573 painter.setOpacity(1.0);
574 }
575 else
576 {
577 /* Draw lines between data points: */
578 for (int i = 0; i < data->size() - 1; ++i)
579 {
580 int j = i + 1;
581 if (data->at(i) == uInvalidValueSentinel || data->at(j) == uInvalidValueSentinel)
582 continue;
583
584 float fHeight = fH * data->at(i);
585 float fX = (width() - m_iMarginRight) - ((data->size() - i - 1) * fBarWidth);
586 float fHeight2 = fH * data->at(j);
587 float fX2 = (width() - m_iMarginRight) - ((data->size() - j - 1) * fBarWidth);
588 QLineF bar(fX, height() - (fHeight + m_iMarginBottom), fX2, height() - (fHeight2 + m_iMarginBottom));
589 painter.drawLine(bar);
590 }
591 /* Draw a point at each data point: */
592 painter.setPen(QPen(m_dataSeriesColor[k], fPointSize));
593 for (int i = 0; i < data->size(); ++i)
594 {
595 if (data->at(i) == uInvalidValueSentinel)
596 continue;
597 float fHeight = fH * data->at(i);
598 float fX = (width() - m_iMarginRight) - ((data->size() - i - 1) * fBarWidth);
599 painter.drawPoint(fX, height() - (fHeight + m_iMarginBottom));
600 }
601 }
602 /* Draw a horizontal and vertical line on data point under the mouse cursor
603 * and draw the value on the left hand side of the chart: */
604 if (m_fDrawCurenValueIndicators && m_iDataIndexUnderCursor >= 0 && m_iDataIndexUnderCursor < data->size())
605 {
606
607 painter.setPen(QPen(m_dataSeriesColor[k], 0.5));
608 painter.setPen(mainAxisColor);
609 float fX = (width() - m_iMarginRight) - ((data->size() - m_iDataIndexUnderCursor - 1) * fBarWidth);
610 painter.drawLine(fX, m_iMarginTop, fX, height() - m_iMarginBottom);
611 }
612 }
613
614 /* Draw YAxis tick labels: */
615 painter.setPen(mainAxisColor);
616 /* This skips 0 and starts at the 2nd Y axis tick to label: */
617 for (int i = iYSubAxisCount; i >= 0; --i)
618 {
619 /* Draw the bottom most label and skip others when data maximum is 0: */
620 if (iMaximum == 0 && i <= iYSubAxisCount)
621 break;
622 int iTextY = 0.5 * iFontHeight + m_iMarginTop + i * m_lineChartRect.height() / (float) (iYSubAxisCount + 1);
623 quint64 iValue = (iYSubAxisCount + 1 - i) * (iMaximum / (float) (iYSubAxisCount + 1));
624 QString strValue = YAxisValueLabel(iValue);
625 /* Leave space of one character between the text and chart rectangle: */
626 painter.drawText(width() - (m_iRightMarginCharWidth - 1) * QFontMetricsF(m_axisFont).averageCharWidth(), iTextY, strValue);
627 }
628
629 if (iMaximum != 0 && m_fIsPieChartAllowed && m_fShowPieChart)
630 drawCombinedPieCharts(painter, iMaximum);
631}
632
633QString UIChart::YAxisValueLabel(quint64 iValue) const
634{
635 if (iValue == uInvalidValueSentinel)
636 return QString();
637 if (m_pMetric->unit().compare("%", Qt::CaseInsensitive) == 0)
638 return QString::number(iValue).append("%");
639 if (m_pMetric->unit().compare("kb", Qt::CaseInsensitive) == 0)
640 return UITranslator::formatSize(_1K * iValue, g_iDecimalCount);
641 if ( m_pMetric->unit().compare("b", Qt::CaseInsensitive) == 0
642 || m_pMetric->unit().compare("b/s", Qt::CaseInsensitive) == 0)
643 return UITranslator::formatSize(iValue, g_iDecimalCount);
644 if (m_pMetric->unit().compare("times", Qt::CaseInsensitive) == 0)
645 return UITranslator::addMetricSuffixToNumber(iValue);
646 if (m_pMetric->unit().compare("gb", Qt::CaseInsensitive) == 0)
647 return QString::number(iValue).append(" GB");
648 return QString();
649}
650
651
652void UIChart::drawXAxisLabels(QPainter &painter, int iXSubAxisCount)
653{
654 QFont painterFont = painter.font();
655 QFontMetrics fontMetrics(painter.font());
656 int iFontHeight = fontMetrics.height();
657
658 int iTotalSeconds = g_iPeriod * m_iMaximumQueueSize;
659 for (int i = 0; i < iXSubAxisCount + 2; ++i)
660 {
661 int iTimeIndex = i * iTotalSeconds / (float)(iXSubAxisCount + 1);
662
663 QString strAxisText;
664 if (m_pMetric && m_pMetric->hasDataLabels())
665 {
666 const QQueue<QString> *labels = m_pMetric->labels();
667 int iDataIndex = qMin(labels->size() - 1, iTimeIndex - (m_iMaximumQueueSize - maxDataSize()));
668 if (iDataIndex >= 0)
669 strAxisText = labels->at(iDataIndex);
670 }
671 else
672 strAxisText = QString::number(iTotalSeconds - iTimeIndex);
673 const int iTextWidth = fontMetrics.horizontalAdvance(strAxisText);
674 const int iTextX = m_lineChartRect.left() + i * m_lineChartRect.width() / (float) (iXSubAxisCount + 1);
675 if (i == 0)
676 {
677 if (!m_pMetric || !m_pMetric->hasDataLabels())
678 strAxisText += " " + m_strXAxisLabel;
679 painter.drawText(iTextX, m_lineChartRect.bottom() + iFontHeight, strAxisText);
680 }
681 else
682 painter.drawText(iTextX - 0.5 * iTextWidth, m_lineChartRect.bottom() + iFontHeight, strAxisText);
683 }
684}
685
686void UIChart::drawPieChart(QPainter &painter, quint64 iMaximum, int iDataIndex,
687 const QRectF &chartRect, bool fWithBorder /* = false */)
688{
689 if (!m_pMetric)
690 return;
691
692 const QQueue<quint64> *data = m_pMetric->data(iDataIndex);
693 if (!data || data->isEmpty())
694 return;
695
696 /* Draw a whole non-filled circle: */
697 if (fWithBorder)
698 {
699 painter.setPen(QPen(QColor(100, 100, 100, m_iOverlayAlpha), 1));
700 painter.drawArc(chartRect, 0, 3600 * 16);
701 painter.setPen(Qt::NoPen);
702 }
703
704 /* Draw a white filled circle and that the arc for data: */
705 QPainterPath background = UIMonitorCommon::wholeArc(chartRect);
706 painter.setPen(Qt::NoPen);
707 painter.setBrush(QColor(255, 255, 255, m_iOverlayAlpha));
708 painter.drawPath(background);
709
710 float fAngle = 360.f * data->back() / (float)iMaximum;
711
712 QPainterPath dataPath;
713 dataPath.moveTo(chartRect.center());
714 dataPath.arcTo(chartRect, 90.f/*startAngle*/,
715 -1.f * fAngle /*sweepLength*/);
716 painter.setBrush(conicalGradientForDataSeries(chartRect, iDataIndex));
717 painter.drawPath(dataPath);
718}
719
720QConicalGradient UIChart::conicalGradientForDataSeries(const QRectF &rectangle, int iDataIndex)
721{
722 QConicalGradient gradient;
723 gradient.setCenter(rectangle.center());
724 gradient.setAngle(90);
725 gradient.setColorAt(0, QColor(0, 0, 0, m_iOverlayAlpha));
726 QColor pieColor(m_dataSeriesColor[iDataIndex]);
727 pieColor.setAlpha(m_iOverlayAlpha);
728 gradient.setColorAt(1, pieColor);
729 return gradient;
730}
731
732int UIChart::maxDataSize() const
733{
734 int iSize = 0;
735 for (int k = 0; k < DATA_SERIES_SIZE; ++k)
736 {
737 if (m_pMetric->data(k))
738 iSize = qMax(iSize, m_pMetric->data(k)->size());
739 }
740 return iSize;
741}
742
743QString UIChart::toolTipText() const
744{
745 if (m_iDataIndexUnderCursor < 0)
746 return QString();
747
748 if (!m_pMetric->data(0) || m_pMetric->data(0)->isEmpty())
749 return QString();
750 QString strToolTip;
751 QString strData0;
752 if (m_iDataIndexUnderCursor < m_pMetric->data(0)->size())
753 strData0 = YAxisValueLabel(m_pMetric->data(0)->at(m_iDataIndexUnderCursor));
754 QString strData1;
755 if (m_iDataIndexUnderCursor < m_pMetric->data(1)->size())
756 strData1 = YAxisValueLabel(m_pMetric->data(1)->at(m_iDataIndexUnderCursor));
757 if (!strData0.isEmpty() && !strData1.isEmpty())
758 {
759 strToolTip = QString("<font color=\"%1\">%2</font> / <font color=\"%3\">%4</font>")
760 .arg(m_dataSeriesColor[0].name(QColor::HexRgb)).arg(strData0)
761 .arg(m_dataSeriesColor[1].name(QColor::HexRgb)).arg(strData1);
762 }
763 else if (!strData0.isEmpty())
764 {
765 strToolTip = QString("<font color=\"%1\">%2</font>")
766 .arg(m_dataSeriesColor[0].name(QColor::HexRgb)).arg(strData0);
767 }
768 else if (!strData1.isEmpty())
769 {
770 strToolTip = QString("<font color=\"%1\">%2</font>")
771 .arg(m_dataSeriesColor[1].name(QColor::HexRgb)).arg(strData1);
772 }
773 return strToolTip;
774}
775
776bool UIChart::isUnderMouse() const
777{
778 return rect().contains(mapFromGlobal(QCursor::pos()));
779}
780
781void UIChart::drawCombinedPieCharts(QPainter &painter, quint64 iMaximum)
782{
783 if (!m_pMetric)
784 return;
785
786 QRectF chartRect(QPointF(m_iPieChartSpacing + m_iMarginLeft, m_iPieChartSpacing + m_iMarginTop),
787 QSizeF(m_iPieChartRadius, m_iPieChartRadius));
788
789 bool fData0 = m_pMetric->data(0) && !m_pMetric->data(0)->isEmpty();
790 bool fData1 = m_pMetric->data(0) && !m_pMetric->data(1)->isEmpty();
791
792 if (fData0 && fData1)
793 {
794 /* Draw a doughnut chart where data series are stacked on to of each other: */
795 if (m_pMetric->data(0) && !m_pMetric->data(0)->isEmpty() &&
796 m_pMetric->data(1) && !m_pMetric->data(1)->isEmpty())
797 UIMonitorCommon::drawCombinedDoughnutChart(m_pMetric->data(1)->back(), dataSeriesColor(1, 0),
798 m_pMetric->data(0)->back(), dataSeriesColor(0, 0),
799 painter, iMaximum, chartRect,
800 UIMonitorCommon::getScaledRect(chartRect, 0.5f, 0.5f), m_iOverlayAlpha);
801 }
802 else if (fData0 && !fData1)
803 drawPieChart(painter, iMaximum, 0 /* iDataIndex */, chartRect);
804 else if (!fData0 && fData1)
805 drawPieChart(painter, iMaximum, 1 /* iDataIndex */, chartRect);
806}
807
808void UIChart::drawDisabledChartRectangle(QPainter &painter)
809{
810 painter.setPen(Qt::NoPen);
811 painter.setBrush(QColor(255, 255, 255, 150));
812 painter.drawRect(m_lineChartRect);
813 painter.setPen(QColor(20, 20, 20, 180));
814 QFont font = painter.font();
815 int iFontSize = 64;
816 do {
817 font.setPixelSize(iFontSize);
818 --iFontSize;
819 } while (QFontMetrics(font).horizontalAdvance(m_strGAWarning) >= 0.8 * m_lineChartRect.width());
820 font.setBold(true);
821 painter.setFont(font);
822 painter.drawText(m_lineChartRect, m_strGAWarning);
823}
824
825void UIChart::sltCreateContextMenu(const QPoint &point)
826{
827 QMenu menu;
828 QAction *pExportAction =
829 menu.addAction(QApplication::translate("UIVMInformationDialog", "Export"));
830 pExportAction->setIcon(UIIconPool::iconSet(":/performance_monitor_export_16px.png"));
831 connect(pExportAction, &QAction::triggered, this, &UIChart::sigExportMetricsToFile);
832 if (uiCommon().uiType() == UIType_RuntimeUI)
833 menu.addAction(m_pActionPool->action(UIActionIndex_M_Activity_T_Preferences));
834 menu.addSeparator();
835 QAction *pResetAction = menu.addAction(m_strResetActionLabel);
836 connect(pResetAction, &QAction::triggered, this, &UIChart::sltResetMetric);
837 if (m_fIsPieChartAllowed)
838 {
839 QAction *pPieChartToggle = menu.addAction(m_strPieChartToggleActionLabel);
840 pPieChartToggle->setCheckable(true);
841 pPieChartToggle->setChecked(m_fShowPieChart);
842 connect(pPieChartToggle, &QAction::toggled, this, &UIChart::sltSetShowPieChart);
843 }
844 if (m_fIsAreaChartAllowed)
845 {
846 QAction *pAreaChartToggle = menu.addAction(m_strAreaChartToggleActionLabel);
847 pAreaChartToggle->setCheckable(true);
848 pAreaChartToggle->setChecked(m_fUseAreaChart);
849 connect(pAreaChartToggle, &QAction::toggled, this, &UIChart::sltSetUseAreaChart);
850 }
851 menu.exec(mapToGlobal(point));
852}
853
854void UIChart::sltResetMetric()
855{
856 if (m_pMetric)
857 m_pMetric->reset();
858}
859
860void UIChart::sltSetShowPieChart(bool fShowPieChart)
861{
862 setShowPieChart(fShowPieChart);
863}
864
865void UIChart::sltSetUseAreaChart(bool fUseAreaChart)
866{
867 setUseAreaChart(fUseAreaChart);
868}
869
870
871/*********************************************************************************************************************************
872* UIMetric implementation. *
873*********************************************************************************************************************************/
874
875UIMetric::UIMetric(const QString &strUnit, int iMaximumQueueSize)
876 : m_strUnit(strUnit)
877 , m_iMaximum(0)
878 , m_fRequiresGuestAdditions(false)
879 , m_fIsInitialized(false)
880 , m_fAutoUpdateMaximum(false)
881 , m_iMaximumQueueSize(iMaximumQueueSize)
882{
883 m_iTotal[0] = 0;
884 m_iTotal[1] = 0;
885}
886
887UIMetric::UIMetric()
888 : m_iMaximum(0)
889 , m_fRequiresGuestAdditions(false)
890 , m_fIsInitialized(false)
891 , m_fAutoUpdateMaximum(false)
892 , m_iMaximumQueueSize(0)
893{
894 m_iTotal[0] = 0;
895 m_iTotal[1] = 0;
896}
897
898void UIMetric::setMaximum(quint64 iMaximum)
899{
900 m_iMaximum = iMaximum;
901}
902
903quint64 UIMetric::maximum() const
904{
905 return m_iMaximum;
906}
907
908void UIMetric::setUnit(QString strUnit)
909{
910 m_strUnit = strUnit;
911
912}
913const QString &UIMetric::unit() const
914{
915 return m_strUnit;
916}
917
918void UIMetric::addData(int iDataSeriesIndex, quint64 iData)
919{
920 if (iDataSeriesIndex >= DATA_SERIES_SIZE)
921 return;
922
923 m_data[iDataSeriesIndex].enqueue(iData);
924
925 /* dequeue if needed and update the maximum value: */
926 if (m_data[iDataSeriesIndex].size() > m_iMaximumQueueSize)
927 m_data[iDataSeriesIndex].dequeue();
928
929 updateMax();
930}
931
932void UIMetric::updateMax()
933{
934 if (!m_fAutoUpdateMaximum)
935 return;
936 m_iMaximum = 0;
937 for (int k = 0; k < DATA_SERIES_SIZE; ++k)
938 {
939 for (int i = 0; i < m_data[k].size(); ++i)
940 {
941 if (m_data[k].at(i) != uInvalidValueSentinel)
942 m_iMaximum = qMax(m_iMaximum, m_data[k].at(i));
943 }
944 }
945}
946
947void UIMetric::addData(int iDataSeriesIndex, quint64 iData, const QString &strLabel)
948{
949 if (iDataSeriesIndex >= DATA_SERIES_SIZE)
950 return;
951
952 addData(iDataSeriesIndex, iData);
953 if (iDataSeriesIndex == 0)
954 {
955 m_labels.enqueue(strLabel);
956 if (m_labels.size() > m_iMaximumQueueSize)
957 m_labels.dequeue();
958 }
959}
960
961const QQueue<quint64> *UIMetric::data(int iDataSeriesIndex) const
962{
963 if (iDataSeriesIndex >= DATA_SERIES_SIZE)
964 return 0;
965 return &m_data[iDataSeriesIndex];
966}
967
968const QQueue<QString> *UIMetric::labels() const
969{
970 return &m_labels;
971}
972
973bool UIMetric::hasDataLabels() const
974{
975 return !m_labels.isEmpty();
976}
977
978int UIMetric::dataSize(int iDataSeriesIndex) const
979{
980 if (iDataSeriesIndex >= DATA_SERIES_SIZE)
981 return 0;
982 return m_data[iDataSeriesIndex].size();
983}
984
985void UIMetric::setDataSeriesName(int iDataSeriesIndex, const QString &strName)
986{
987 if (iDataSeriesIndex >= DATA_SERIES_SIZE)
988 return;
989 m_strDataSeriesName[iDataSeriesIndex] = strName;
990}
991
992QString UIMetric::dataSeriesName(int iDataSeriesIndex) const
993{
994 if (iDataSeriesIndex >= DATA_SERIES_SIZE)
995 return QString();
996 return m_strDataSeriesName[iDataSeriesIndex];
997}
998
999void UIMetric::setTotal(int iDataSeriesIndex, quint64 iTotal)
1000{
1001 if (iDataSeriesIndex >= DATA_SERIES_SIZE)
1002 return;
1003 m_iTotal[iDataSeriesIndex] = iTotal;
1004}
1005
1006quint64 UIMetric::total(int iDataSeriesIndex) const
1007{
1008 if (iDataSeriesIndex >= DATA_SERIES_SIZE)
1009 return 0;
1010 return m_iTotal[iDataSeriesIndex];
1011}
1012
1013bool UIMetric::requiresGuestAdditions() const
1014{
1015 return m_fRequiresGuestAdditions;
1016}
1017
1018void UIMetric::setRequiresGuestAdditions(bool fRequiresGAs)
1019{
1020 m_fRequiresGuestAdditions = fRequiresGAs;
1021}
1022
1023bool UIMetric::isInitialized() const
1024{
1025 return m_fIsInitialized;
1026}
1027
1028void UIMetric::setIsInitialized(bool fIsInitialized)
1029{
1030 m_fIsInitialized = fIsInitialized;
1031}
1032
1033void UIMetric::reset()
1034{
1035 m_fIsInitialized = false;
1036 for (int i = 0; i < DATA_SERIES_SIZE; ++i)
1037 {
1038 m_iTotal[i] = 0;
1039 m_data[i].clear();
1040 }
1041 m_iMaximum = 0;
1042}
1043
1044void UIMetric::toFile(QTextStream &stream) const
1045{
1046 stream << "Unit: " << m_strUnit << "\n";
1047 stream << "Maximum: " << m_iMaximum << "\n";
1048 for (int i = 0; i < 2; ++i)
1049 {
1050 if (!m_data[i].isEmpty())
1051 {
1052 stream << "Data Series: " << m_strDataSeriesName[i] << "\n";
1053 foreach (const quint64& data, m_data[i])
1054 stream << data << " ";
1055 stream << "\n";
1056 }
1057 }
1058 stream << "\n";
1059}
1060
1061void UIMetric::setAutoUpdateMaximum(bool fAuto)
1062{
1063 m_fAutoUpdateMaximum = fAuto;
1064}
1065
1066bool UIMetric::autoUpdateMaximum() const
1067{
1068 return m_fAutoUpdateMaximum;
1069}
1070
1071/*********************************************************************************************************************************
1072* UIVMActivityMonitor implementation. *
1073*********************************************************************************************************************************/
1074
1075UIVMActivityMonitor::UIVMActivityMonitor(EmbedTo enmEmbedding, QWidget *pParent, UIActionPool *pActionPool, int iMaximumQueueSize)
1076 : QWidget(pParent)
1077 , m_pContainerLayout(0)
1078 , m_pTimer(0)
1079 , m_iTimeStep(0)
1080 , m_iMaximumQueueSize(iMaximumQueueSize)
1081 , m_pActionPool(pActionPool)
1082 , m_pMainLayout(0)
1083 , m_enmEmbedding(enmEmbedding)
1084{
1085 uiCommon().setHelpKeyword(this, "vm-activity-session-information");
1086 setContextMenuPolicy(Qt::CustomContextMenu);
1087 connect(this, &UIVMActivityMonitor::customContextMenuRequested,
1088 this, &UIVMActivityMonitor::sltCreateContextMenu);
1089}
1090
1091void UIVMActivityMonitor::sltRetranslateUI()
1092{
1093 /* Translate the chart info labels: */
1094 m_iMaximumLabelLength = 0;
1095 m_strCPUInfoLabelTitle = QApplication::translate("UIVMInformationDialog", "CPU Load");
1096 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strCPUInfoLabelTitle.length());
1097 m_strCPUInfoLabelGuest = QApplication::translate("UIVMInformationDialog", "Guest Load");
1098 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strCPUInfoLabelGuest.length());
1099 m_strCPUInfoLabelVMM = QApplication::translate("UIVMInformationDialog", "VMM Load");
1100 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strCPUInfoLabelVMM.length());
1101 m_strRAMInfoLabelTitle = QApplication::translate("UIVMInformationDialog", "RAM Usage");
1102 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strRAMInfoLabelTitle.length());
1103 m_strRAMInfoLabelTotal = QApplication::translate("UIVMInformationDialog", "Total");
1104 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strRAMInfoLabelTotal.length());
1105 m_strRAMInfoLabelFree = QApplication::translate("UIVMInformationDialog", "Free");
1106 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strRAMInfoLabelFree.length());
1107 m_strRAMInfoLabelUsed = QApplication::translate("UIVMInformationDialog", "Used");
1108 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strRAMInfoLabelUsed.length());
1109 m_strNetworkInfoLabelReceived = QApplication::translate("UIVMInformationDialog", "Receive Rate");
1110 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strNetworkInfoLabelReceived.length());
1111 m_strNetworkInfoLabelTransmitted = QApplication::translate("UIVMInformationDialog", "Transmit Rate");
1112 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strNetworkInfoLabelTransmitted.length());
1113 m_strNetworkInfoLabelReceivedTotal = QApplication::translate("UIVMInformationDialog", "Total Received");
1114 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strNetworkInfoLabelReceivedTotal.length());
1115 m_strNetworkInfoLabelTransmittedTotal = QApplication::translate("UIVMInformationDialog", "Total Transmitted");
1116 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strNetworkInfoLabelReceivedTotal.length());
1117 m_strDiskIOInfoLabelTitle = QApplication::translate("UIVMInformationDialog", "Disk IO");
1118 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strDiskIOInfoLabelTitle.length());
1119 m_strDiskIOInfoLabelWritten = QApplication::translate("UIVMInformationDialog", "Write Rate");
1120 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strDiskIOInfoLabelWritten.length());
1121 m_strDiskIOInfoLabelRead = QApplication::translate("UIVMInformationDialog", "Read Rate");
1122 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strDiskIOInfoLabelRead.length());
1123 m_strDiskIOInfoLabelWrittenTotal = QApplication::translate("UIVMInformationDialog", "Total Written");
1124 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strDiskIOInfoLabelWrittenTotal.length());
1125 m_strDiskIOInfoLabelReadTotal = QApplication::translate("UIVMInformationDialog", "Total Read");
1126 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strDiskIOInfoLabelReadTotal.length());
1127}
1128
1129void UIVMActivityMonitor::prepareWidgets()
1130{
1131 m_pMainLayout = new QVBoxLayout(this);
1132 if (!m_pMainLayout)
1133 return;
1134
1135 m_pMainLayout->setContentsMargins(0, 0, 0, 0);
1136#ifdef VBOX_WS_MAC
1137 m_pMainLayout->setSpacing(10);
1138#else
1139 m_pMainLayout->setSpacing(qApp->style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) / 2);
1140#endif
1141
1142 m_pTimer = new QTimer(this);
1143 if (m_pTimer)
1144 connect(m_pTimer, &QTimer::timeout, this, &UIVMActivityMonitor::sltTimeout);
1145
1146 QScrollArea *pScrollArea = new QScrollArea(this);
1147 m_pMainLayout->addWidget(pScrollArea);
1148
1149 QWidget *pContainerWidget = new QWidget(pScrollArea);
1150 m_pContainerLayout = new QGridLayout(pContainerWidget);
1151 pContainerWidget->setLayout(m_pContainerLayout);
1152 m_pContainerLayout->setSpacing(10);
1153 pContainerWidget->show();
1154 pScrollArea->setWidget(pContainerWidget);
1155 pScrollArea->setWidgetResizable(true);
1156}
1157
1158void UIVMActivityMonitor::sltTimeout()
1159{
1160 obtainDataAndUpdate();
1161}
1162
1163void UIVMActivityMonitor::sltExportMetricsToFile()
1164{
1165 QString strStartFileName = QString("%1/%2_%3").
1166 arg(defaultMachineFolder()).
1167 arg(machineName()).
1168 arg(QDateTime::currentDateTime().toString("dd-MM-yyyy_hh-mm-ss"));
1169 QString strFileName = QIFileDialog::getSaveFileName(strStartFileName,"",this,
1170 QApplication::translate("UIVMInformationDialog",
1171 "Export activity data of the machine \"%1\"")
1172 .arg(machineName()));
1173 QFile dataFile(strFileName);
1174
1175 if (dataFile.open(QFile::WriteOnly | QFile::Truncate))
1176 {
1177 QTextStream stream(&dataFile);
1178 for (QMap<Metric_Type, UIMetric>::const_iterator iterator = m_metrics.begin(); iterator != m_metrics.end(); ++iterator)
1179 iterator.value().toFile(stream);
1180 dataFile.close();
1181 }
1182}
1183
1184void UIVMActivityMonitor::sltCreateContextMenu(const QPoint &point)
1185{
1186 QMenu menu;
1187 QAction *pExportAction =
1188 menu.addAction(QApplication::translate("UIVMInformationDialog", "Export"));
1189 pExportAction->setIcon(UIIconPool::iconSet(":/performance_monitor_export_16px.png"));
1190 connect(pExportAction, &QAction::triggered, this, &UIVMActivityMonitor::sltExportMetricsToFile);
1191 if (uiCommon().uiType() == UIType_RuntimeUI)
1192 menu.addAction(m_pActionPool->action(UIActionIndex_M_Activity_T_Preferences));
1193 menu.exec(mapToGlobal(point));
1194}
1195
1196void UIVMActivityMonitor::prepareActions()
1197{
1198}
1199
1200void UIVMActivityMonitor::resetRAMInfoLabel()
1201{
1202 if (m_infoLabels.contains(Metric_Type_RAM) && m_infoLabels[Metric_Type_RAM])
1203 {
1204 QString strInfo = QString("<b>%1</b><br/>%2: %3<br/>%4: %5<br/>%6: %7").
1205 arg(m_strRAMInfoLabelTitle).arg(m_strRAMInfoLabelTotal).arg("--")
1206 .arg(m_strRAMInfoLabelFree).arg("--")
1207 .arg(m_strRAMInfoLabelUsed).arg("--");
1208 m_infoLabels[Metric_Type_RAM]->setText(strInfo);
1209 }
1210}
1211
1212QString UIVMActivityMonitor::dataColorString(Metric_Type enmType, int iDataIndex)
1213{
1214 if (!m_charts.contains(enmType))
1215 return QColor(Qt::black).name(QColor::HexRgb);
1216 UIChart *pChart = m_charts[enmType];
1217 if (!pChart)
1218 return QColor(Qt::black).name(QColor::HexRgb);
1219 return pChart->dataSeriesColor(iDataIndex).name(QColor::HexRgb);
1220}
1221
1222void UIVMActivityMonitor::setInfoLabelWidth()
1223{
1224 /* Compute the maximum label string length and set it as a fixed width to labels to prevent always changing widths: */
1225 /* Add m_iDecimalCount plus 4 characters for the number and 3 for unit string: */
1226 m_iMaximumLabelLength += (g_iDecimalCount + 7);
1227 if (!m_infoLabels.isEmpty())
1228 {
1229 QLabel *pLabel = m_infoLabels.begin().value();
1230 if (pLabel)
1231 {
1232 const QFontMetrics labelFontMetric(pLabel->font());
1233 const int iWidth = m_iMaximumLabelLength * labelFontMetric.horizontalAdvance('X');
1234 foreach (QLabel *pInfoLabel, m_infoLabels)
1235 pInfoLabel->setFixedWidth(iWidth);
1236 }
1237 }
1238}
1239
1240void UIVMActivityMonitor::setDataSeriesColor(int iIndex, const QColor &color)
1241{
1242 if (iIndex < 0 || iIndex >= DATA_SERIES_SIZE)
1243 return;
1244 m_dataSeriesColor[iIndex] = color;
1245
1246 foreach (UIChart *pChart, m_charts)
1247 if (pChart)
1248 pChart->setDataSeriesColor(iIndex, color);
1249}
1250
1251/*********************************************************************************************************************************
1252* UIVMActivityMonitorLocal definition. *
1253*********************************************************************************************************************************/
1254
1255UIVMActivityMonitorLocal::UIVMActivityMonitorLocal(EmbedTo enmEmbedding, QWidget *pParent,
1256 const CMachine &machine, UIActionPool *pActionPool)
1257 :UIVMActivityMonitor(enmEmbedding, pParent, pActionPool, 120 /* iMaximumQueueSize */)
1258 , m_fGuestAdditionsAvailable(false)
1259 , m_fCOMPerformanceCollectorConfigured(false)
1260{
1261 prepareMetrics();
1262 prepareWidgets();
1263 sltRetranslateUI();
1264 prepareActions();
1265 connect(gVBoxEvents, &UIVirtualBoxEventHandler::sigMachineStateChange, this, &UIVMActivityMonitorLocal::sltMachineStateChange);
1266 connect(&uiCommon(), &UICommon::sigAskToDetachCOM, this, &UIVMActivityMonitorLocal::sltClearCOMData);
1267 connect(&translationEventListener(), &UITranslationEventListener::sigRetranslateUI, this, &UIVMActivityMonitorLocal::sltRetranslateUI);
1268 setMachine(machine);
1269
1270 /* Configure charts: */
1271 if (m_charts.contains(Metric_Type_CPU) && m_charts[Metric_Type_CPU])
1272 {
1273 m_charts[Metric_Type_CPU]->setIsPieChartAllowed(true);
1274 m_charts[Metric_Type_CPU]->setIsAreaChartAllowed(true);
1275 }
1276}
1277
1278void UIVMActivityMonitorLocal::start()
1279{
1280 if (m_comMachine.isNull() || m_comMachine.GetState() != KMachineState_Running)
1281 return;
1282
1283 m_fGuestAdditionsAvailable = guestAdditionsAvailable("6.1");
1284 enableDisableGuestAdditionDependedWidgets(m_fGuestAdditionsAvailable);
1285 if (m_pTimer)
1286 m_pTimer->start(1000 * g_iPeriod);
1287}
1288
1289UIVMActivityMonitorLocal::~UIVMActivityMonitorLocal()
1290{
1291 sltClearCOMData();
1292}
1293
1294QUuid UIVMActivityMonitorLocal::machineId() const
1295{
1296 if (m_comMachine.isNull())
1297 return QUuid();
1298 return m_comMachine.GetId();
1299}
1300
1301void UIVMActivityMonitorLocal::sltRetranslateUI()
1302{
1303 UIVMActivityMonitor::sltRetranslateUI();
1304
1305 foreach (UIChart *pChart, m_charts)
1306 pChart->setXAxisLabel(QApplication::translate("UIVMInformationDialog", "Sec.", "short from seconds"));
1307
1308 m_strVMExitInfoLabelTitle = QApplication::translate("UIVMInformationDialog", "VM Exits");
1309 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strVMExitInfoLabelTitle.length());
1310 m_strVMExitLabelCurrent = QApplication::translate("UIVMInformationDialog", "Current");
1311 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strVMExitLabelCurrent.length());
1312 m_strVMExitLabelTotal = QApplication::translate("UIVMInformationDialog", "Total");
1313 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strVMExitLabelTotal.length());
1314 m_strNetworkInfoLabelTitle = QApplication::translate("UIVMInformationDialog", "Network Rate");
1315 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strNetworkInfoLabelTitle.length());
1316 setInfoLabelWidth();
1317}
1318
1319void UIVMActivityMonitorLocal::setMachine(const CMachine &comMachine)
1320{
1321 reset();
1322 if (comMachine.isNull())
1323 return;
1324
1325 if (!m_comSession.isNull())
1326 m_comSession.UnlockMachine();
1327
1328 m_comMachine = comMachine;
1329
1330 if (m_comMachine.GetState() == KMachineState_Running)
1331 {
1332 setEnabled(true);
1333 openSession();
1334 start();
1335 }
1336}
1337
1338QString UIVMActivityMonitorLocal::machineName() const
1339{
1340 if (m_comMachine.isNull())
1341 return QString();
1342 return m_comMachine.GetName();
1343}
1344
1345void UIVMActivityMonitorLocal::openSession()
1346{
1347 if (!m_comSession.isNull())
1348 return;
1349 m_comSession = UILocalMachineStuff::openSession(m_comMachine.GetId(), KLockType_Shared);
1350 AssertReturnVoid(!m_comSession.isNull());
1351
1352 CConsole comConsole = m_comSession.GetConsole();
1353 AssertReturnVoid(!comConsole.isNull());
1354 m_comGuest = comConsole.GetGuest();
1355
1356 m_comMachineDebugger = comConsole.GetDebugger();
1357}
1358
1359void UIVMActivityMonitorLocal::obtainDataAndUpdate()
1360{
1361 ++m_iTimeStep;
1362
1363 if (m_metrics.contains(Metric_Type_RAM) && !m_performanceCollector.isNull())
1364 {
1365 quint64 iTotalRAM = 0;
1366 quint64 iFreeRAM = 0;
1367 UIMonitorCommon::getRAMLoad(m_performanceCollector, m_nameList, m_objectList, iTotalRAM, iFreeRAM);
1368 updateRAMGraphsAndMetric(iTotalRAM, iFreeRAM);
1369 }
1370
1371 /* Update the CPU load chart with values we get from IMachineDebugger::getCPULoad(..): */
1372 if (m_metrics.contains(Metric_Type_CPU))
1373 {
1374 ULONG aPctExecuting;
1375 ULONG aPctHalted;
1376 ULONG aPctOther;
1377 m_comMachineDebugger.GetCPULoad(0x7fffffff, aPctExecuting, aPctHalted, aPctOther);
1378 updateCPUChart(aPctExecuting, aPctOther);
1379 }
1380
1381 /* Update the network load chart with values we find under /Public/NetAdapter/: */
1382 {
1383 quint64 cbNetworkTotalReceived = 0;
1384 quint64 cbNetworkTotalTransmitted = 0;
1385 UIMonitorCommon::getNetworkLoad(m_comMachineDebugger, cbNetworkTotalReceived, cbNetworkTotalTransmitted);
1386 updateNetworkChart(cbNetworkTotalReceived, cbNetworkTotalTransmitted);
1387 }
1388
1389 /* Update the Disk I/O chart with values we find under /Public/Storage/?/Port?/Bytes*: */
1390 {
1391 quint64 cbDiskIOTotalWritten = 0;
1392 quint64 cbDiskIOTotalRead = 0;
1393 UIMonitorCommon::getDiskLoad(m_comMachineDebugger, cbDiskIOTotalWritten, cbDiskIOTotalRead);
1394 updateDiskIOChart(cbDiskIOTotalWritten, cbDiskIOTotalRead);
1395 }
1396
1397 /* Update the VM exit chart with values we find as /PROF/CPU?/EM/RecordedExits: */
1398 {
1399 quint64 cTotalVMExits = 0;
1400 UIMonitorCommon::getVMMExitCount(m_comMachineDebugger, cTotalVMExits);
1401 updateVMExitMetric(cTotalVMExits);
1402 }
1403}
1404
1405void UIVMActivityMonitorLocal::sltMachineStateChange(const QUuid &uId)
1406{
1407 if (m_comMachine.isNull())
1408 return;
1409 if (m_comMachine.GetId() != uId)
1410 return;
1411 if (m_comMachine.GetState() == KMachineState_Running)
1412 {
1413 setEnabled(true);
1414 openSession();
1415 start();
1416 }
1417 else if (m_comMachine.GetState() == KMachineState_Paused)
1418 {
1419 /* If we are already active then stop: */
1420 if (!m_comSession.isNull() && m_pTimer && m_pTimer->isActive())
1421 m_pTimer->stop();
1422 }
1423 else
1424 reset();
1425}
1426
1427QString UIVMActivityMonitorLocal::defaultMachineFolder() const
1428{
1429 if (m_comMachine.isOk())
1430 return m_comMachine.GetLogFolder();
1431 else
1432 return QString();
1433}
1434
1435void UIVMActivityMonitorLocal::guestAdditionsStateChange()
1436{
1437 bool fGuestAdditionsAvailable = guestAdditionsAvailable("6.1");
1438 if (m_fGuestAdditionsAvailable == fGuestAdditionsAvailable)
1439 return;
1440 m_fGuestAdditionsAvailable = fGuestAdditionsAvailable;
1441 enableDisableGuestAdditionDependedWidgets(m_fGuestAdditionsAvailable);
1442}
1443
1444void UIVMActivityMonitorLocal::sltClearCOMData()
1445{
1446 if (!m_comSession.isNull())
1447 {
1448 m_comSession.UnlockMachine();
1449 m_comSession.detach();
1450 }
1451}
1452
1453void UIVMActivityMonitorLocal::reset()
1454{
1455 m_fGuestAdditionsAvailable = false;
1456 setEnabled(false);
1457
1458 if (m_pTimer)
1459 m_pTimer->stop();
1460 /* reset the metrics. this will delete their data cache: */
1461 for (QMap<Metric_Type, UIMetric>::iterator iterator = m_metrics.begin();
1462 iterator != m_metrics.end(); ++iterator)
1463 iterator.value().reset();
1464 /* force update on the charts to draw now emptied metrics' data: */
1465 for (QMap<Metric_Type, UIChart*>::iterator iterator = m_charts.begin();
1466 iterator != m_charts.end(); ++iterator)
1467 iterator.value()->update();
1468 /* Reset the info labels: */
1469 resetCPUInfoLabel();
1470 resetRAMInfoLabel();
1471 resetNetworkInfoLabel();
1472 resetDiskIOInfoLabel();
1473 resetVMExitInfoLabel();
1474 update();
1475 sltClearCOMData();
1476}
1477
1478void UIVMActivityMonitorLocal::prepareWidgets()
1479{
1480 UIVMActivityMonitor::prepareWidgets();
1481
1482 QVector<Metric_Type> chartOrder;
1483 chartOrder << Metric_Type_CPU << Metric_Type_RAM <<
1484 Metric_Type_Network_InOut << Metric_Type_Disk_InOut;
1485#ifdef DEBUG
1486 chartOrder << Metric_Type_VM_Exits;
1487#else
1488 if (gEDataManager->VMActivityMonitorShowVMExits())
1489 chartOrder << Metric_Type_VM_Exits;
1490#endif
1491 int iRow = 0;
1492 foreach (Metric_Type enmType, chartOrder)
1493 {
1494 if (!m_metrics.contains(enmType))
1495 continue;
1496
1497 QHBoxLayout *pChartLayout = new QHBoxLayout;
1498 pChartLayout->setSpacing(0);
1499
1500 QLabel *pLabel = new QLabel(this);
1501
1502 QPalette tempPal = pLabel->palette();
1503 tempPal.setColor(QPalette::Window, tempPal.color(QPalette::Window).lighter(g_iBackgroundTint));
1504 pLabel->setPalette(tempPal);
1505 pLabel->setAutoFillBackground(true);
1506
1507 pLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
1508 pChartLayout->addWidget(pLabel);
1509 m_infoLabels.insert(enmType, pLabel);
1510
1511 UIChart *pChart = new UIChart(this, &(m_metrics[enmType]), m_pActionPool, m_iMaximumQueueSize);
1512 connect(pChart, &UIChart::sigExportMetricsToFile,
1513 this, &UIVMActivityMonitor::sltExportMetricsToFile);
1514 m_charts.insert(enmType, pChart);
1515 pChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
1516 pChartLayout->addWidget(pChart);
1517 m_pContainerLayout->addLayout(pChartLayout, iRow, 0, 1, 2);
1518 ++iRow;
1519 }
1520
1521 QWidget *bottomSpacerWidget = new QWidget(this);
1522 bottomSpacerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
1523 bottomSpacerWidget->setVisible(true);
1524 m_pContainerLayout->addWidget(bottomSpacerWidget, iRow, 0, 1, 2);
1525}
1526
1527void UIVMActivityMonitorLocal::configureCOMPerformanceCollector()
1528{
1529 /* Only once: */
1530 if (m_fCOMPerformanceCollectorConfigured)
1531 return;
1532
1533 m_nameList << "Guest/RAM/Usage*";
1534 m_objectList = QVector<CUnknown>(m_nameList.size(), CUnknown());
1535
1536 m_performanceCollector = gpGlobalSession->virtualBox().GetPerformanceCollector();
1537 if (m_performanceCollector.isNull())
1538 return;
1539
1540 m_performanceCollector.SetupMetrics(m_nameList, m_objectList, g_iPeriod, g_iMetricSetupCount);
1541 {
1542 QVector<CPerformanceMetric> metrics = m_performanceCollector.GetMetrics(m_nameList, m_objectList);
1543 for (int i = 0; i < metrics.size(); ++i)
1544 {
1545 QString strName(metrics[i].GetMetricName());
1546 if (!strName.contains(':'))
1547 {
1548 if (strName.contains("RAM", Qt::CaseInsensitive) && strName.contains("Free", Qt::CaseInsensitive))
1549 {
1550 if (m_metrics.contains(Metric_Type_RAM))
1551 m_metrics[Metric_Type_RAM].setUnit(metrics[i].GetUnit());
1552 }
1553 }
1554 }
1555 }
1556 m_fCOMPerformanceCollectorConfigured = true;
1557}
1558
1559void UIVMActivityMonitorLocal::prepareMetrics()
1560{
1561 /* CPU Metric: */
1562 UIMetric cpuMetric("%", m_iMaximumQueueSize);
1563 cpuMetric.setDataSeriesName(0, "Guest Load");
1564 cpuMetric.setDataSeriesName(1, "VMM Load");
1565 m_metrics.insert(Metric_Type_CPU, cpuMetric);
1566
1567 /* RAM Metric: */
1568 UIMetric ramMetric(""/*metrics[i].GetUnit()*/, m_iMaximumQueueSize);
1569 ramMetric.setDataSeriesName(0, "Free");
1570 ramMetric.setDataSeriesName(1, "Used");
1571 ramMetric.setRequiresGuestAdditions(true);
1572 m_metrics.insert(Metric_Type_RAM, ramMetric);
1573
1574
1575 /* Network metric: */
1576 UIMetric networkMetric("B", m_iMaximumQueueSize);
1577 networkMetric.setDataSeriesName(0, "Receive Rate");
1578 networkMetric.setDataSeriesName(1, "Transmit Rate");
1579 networkMetric.setAutoUpdateMaximum(true);
1580 m_metrics.insert(Metric_Type_Network_InOut, networkMetric);
1581
1582 /* Disk IO metric */
1583 UIMetric diskIOMetric("B", m_iMaximumQueueSize);
1584 diskIOMetric.setDataSeriesName(0, "Write Rate");
1585 diskIOMetric.setDataSeriesName(1, "Read Rate");
1586 diskIOMetric.setAutoUpdateMaximum(true);
1587 m_metrics.insert(Metric_Type_Disk_InOut, diskIOMetric);
1588
1589 /* VM exits metric */
1590 UIMetric VMExitsMetric("times", m_iMaximumQueueSize);
1591 VMExitsMetric.setAutoUpdateMaximum(true);
1592 m_metrics.insert(Metric_Type_VM_Exits, VMExitsMetric);
1593}
1594
1595bool UIVMActivityMonitorLocal::guestAdditionsAvailable(const char *pszMinimumVersion)
1596{
1597 if (m_comGuest.isNull() || !pszMinimumVersion)
1598 return false;
1599
1600 /* Guest control stuff is in userland: */
1601 if (!m_comGuest.GetAdditionsStatus(KAdditionsRunLevelType_Userland))
1602 return false;
1603
1604 if (!m_comGuest.isOk())
1605 return false;
1606
1607 /* Check the related GA facility: */
1608 LONG64 iLastUpdatedIgnored;
1609 if (m_comGuest.GetFacilityStatus(KAdditionsFacilityType_VBoxService, iLastUpdatedIgnored) != KAdditionsFacilityStatus_Active)
1610 return false;
1611
1612 if (!m_comGuest.isOk())
1613 return false;
1614
1615 QString strGAVersion = m_comGuest.GetAdditionsVersion();
1616 if (m_comGuest.isOk())
1617 return (RTStrVersionCompare(strGAVersion.toUtf8().constData(), pszMinimumVersion) >= 0);
1618
1619 return false;
1620}
1621
1622void UIVMActivityMonitorLocal::enableDisableGuestAdditionDependedWidgets(bool fEnable)
1623{
1624 /* Configure performace monitor: */
1625 if (fEnable)
1626 configureCOMPerformanceCollector();
1627 for (QMap<Metric_Type, UIMetric>::const_iterator iterator = m_metrics.begin();
1628 iterator != m_metrics.end(); ++iterator)
1629 {
1630 if (!iterator.value().requiresGuestAdditions())
1631 continue;
1632 if (m_charts.contains(iterator.key()) && m_charts[iterator.key()])
1633 m_charts[iterator.key()]->setIsAvailable(fEnable);
1634 if (m_infoLabels.contains(iterator.key()) && m_infoLabels[iterator.key()])
1635 {
1636 m_infoLabels[iterator.key()]->setEnabled(fEnable);
1637 m_infoLabels[iterator.key()]->update();
1638 }
1639 }
1640}
1641
1642void UIVMActivityMonitorLocal::updateVMExitMetric(quint64 uTotalVMExits)
1643{
1644 if (uTotalVMExits <= 0)
1645 return;
1646
1647 UIMetric &VMExitMetric = m_metrics[Metric_Type_VM_Exits];
1648 quint64 iRate = uTotalVMExits - VMExitMetric.total(0);
1649 VMExitMetric.setTotal(0, uTotalVMExits);
1650 /* Do not set data and maximum if the metric has not been initialized since we need to initialize totals "(t-1)" first: */
1651 if (!VMExitMetric.isInitialized())
1652 {
1653 VMExitMetric.setIsInitialized(true);
1654 return;
1655 }
1656 VMExitMetric.addData(0, iRate);
1657 if (m_infoLabels.contains(Metric_Type_VM_Exits) && m_infoLabels[Metric_Type_VM_Exits])
1658 {
1659 QString strInfo;
1660 strInfo = QString("<b>%1</b><br/><font color=\"%2\">%3: %4 %5</font><br/>%6: %7 %8")
1661 .arg(m_strVMExitInfoLabelTitle)
1662 .arg(dataColorString(Metric_Type_VM_Exits, 0)).arg(m_strVMExitLabelCurrent).arg(UITranslator::addMetricSuffixToNumber(iRate)).arg(VMExitMetric.unit())
1663 .arg(m_strVMExitLabelTotal).arg(UITranslator::addMetricSuffixToNumber(uTotalVMExits)).arg(VMExitMetric.unit());
1664 m_infoLabels[Metric_Type_VM_Exits]->setText(strInfo);
1665 }
1666 if (m_charts.contains(Metric_Type_VM_Exits))
1667 m_charts[Metric_Type_VM_Exits]->update();
1668}
1669
1670void UIVMActivityMonitorLocal::updateCPUChart(quint64 iExecutingPercentage, ULONG iOtherPercentage)
1671{
1672 UIMetric &CPUMetric = m_metrics[Metric_Type_CPU];
1673 CPUMetric.addData(0, iExecutingPercentage);
1674 CPUMetric.addData(1, iOtherPercentage);
1675 CPUMetric.setMaximum(100);
1676 if (m_infoLabels.contains(Metric_Type_CPU) && m_infoLabels[Metric_Type_CPU])
1677 {
1678 QString strInfo;
1679
1680 strInfo = QString("<b>%1</b></b><br/><font color=\"%2\">%3: %4%5</font><br/><font color=\"%6\">%7: %8%9</font>")
1681 .arg(m_strCPUInfoLabelTitle)
1682 .arg(dataColorString(Metric_Type_CPU, 0))
1683 .arg(m_strCPUInfoLabelGuest).arg(QString::number(iExecutingPercentage)).arg(CPUMetric.unit())
1684 .arg(dataColorString(Metric_Type_CPU, 1))
1685 .arg(m_strCPUInfoLabelVMM).arg(QString::number(iOtherPercentage)).arg(CPUMetric.unit());
1686 m_infoLabels[Metric_Type_CPU]->setText(strInfo);
1687 }
1688
1689 if (m_charts.contains(Metric_Type_CPU))
1690 m_charts[Metric_Type_CPU]->update();
1691}
1692
1693void UIVMActivityMonitorLocal::updateRAMGraphsAndMetric(quint64 iTotalRAM, quint64 iFreeRAM)
1694{
1695 UIMetric &RAMMetric = m_metrics[Metric_Type_RAM];
1696 RAMMetric.setMaximum(iTotalRAM);
1697 RAMMetric.addData(0, iTotalRAM - iFreeRAM);
1698 if (m_infoLabels.contains(Metric_Type_RAM) && m_infoLabels[Metric_Type_RAM])
1699 {
1700 QString strInfo;
1701 strInfo = QString("<b>%1</b><br/>%2: %3<br/><font color=\"%4\">%5: %6</font><br/><font color=\"%7\">%8: %9</font>")
1702 .arg(m_strRAMInfoLabelTitle)
1703 .arg(m_strRAMInfoLabelTotal).arg(UITranslator::formatSize(_1K * iTotalRAM, g_iDecimalCount))
1704 .arg(dataColorString(Metric_Type_RAM, 1)).arg(m_strRAMInfoLabelFree).arg(UITranslator::formatSize(_1K * (iFreeRAM), g_iDecimalCount))
1705 .arg(dataColorString(Metric_Type_RAM, 0)).arg(m_strRAMInfoLabelUsed).arg(UITranslator::formatSize(_1K * (iTotalRAM - iFreeRAM), g_iDecimalCount));
1706 m_infoLabels[Metric_Type_RAM]->setText(strInfo);
1707 }
1708 if (m_charts.contains(Metric_Type_RAM))
1709 m_charts[Metric_Type_RAM]->update();
1710}
1711
1712void UIVMActivityMonitorLocal::updateNetworkChart(quint64 uReceiveTotal, quint64 uTransmitTotal)
1713{
1714 UIMetric &NetMetric = m_metrics[Metric_Type_Network_InOut];
1715
1716 quint64 uReceiveRate = uReceiveTotal - NetMetric.total(0);
1717 quint64 uTransmitRate = uTransmitTotal - NetMetric.total(1);
1718
1719 NetMetric.setTotal(0, uReceiveTotal);
1720 NetMetric.setTotal(1, uTransmitTotal);
1721
1722 if (!NetMetric.isInitialized())
1723 {
1724 NetMetric.setIsInitialized(true);
1725 return;
1726 }
1727
1728 NetMetric.addData(0, uReceiveRate);
1729 NetMetric.addData(1, uTransmitRate);
1730
1731 if (m_infoLabels.contains(Metric_Type_Network_InOut) && m_infoLabels[Metric_Type_Network_InOut])
1732 {
1733 QString strInfo;
1734 strInfo = QString("<b>%1</b></b><br/><font color=\"%2\">%3: %4<br/>%5: %6</font><br/><font color=\"%7\">%8: %9<br/>%10: %11</font>")
1735 .arg(m_strNetworkInfoLabelTitle)
1736 .arg(dataColorString(Metric_Type_Network_InOut, 0)).arg(m_strNetworkInfoLabelReceived).arg(UITranslator::formatSize(uReceiveRate, g_iDecimalCount))
1737 .arg(m_strNetworkInfoLabelReceivedTotal).arg(UITranslator::formatSize(uReceiveTotal, g_iDecimalCount))
1738 .arg(dataColorString(Metric_Type_Network_InOut, 1)).arg(m_strNetworkInfoLabelTransmitted).arg(UITranslator::formatSize(uTransmitRate, g_iDecimalCount))
1739 .arg(m_strNetworkInfoLabelTransmittedTotal).arg(UITranslator::formatSize(uTransmitTotal, g_iDecimalCount));
1740 m_infoLabels[Metric_Type_Network_InOut]->setText(strInfo);
1741 }
1742 if (m_charts.contains(Metric_Type_Network_InOut))
1743 m_charts[Metric_Type_Network_InOut]->update();
1744}
1745
1746void UIVMActivityMonitorLocal::updateDiskIOChart(quint64 uDiskIOTotalWritten, quint64 uDiskIOTotalRead)
1747{
1748 UIMetric &diskMetric = m_metrics[Metric_Type_Disk_InOut];
1749
1750 quint64 uWriteRate = uDiskIOTotalWritten - diskMetric.total(0);
1751 quint64 uReadRate = uDiskIOTotalRead - diskMetric.total(1);
1752
1753 diskMetric.setTotal(0, uDiskIOTotalWritten);
1754 diskMetric.setTotal(1, uDiskIOTotalRead);
1755
1756 /* Do not set data and maximum if the metric has not been initialized since we need to initialize totals "(t-1)" first: */
1757 if (!diskMetric.isInitialized()){
1758 diskMetric.setIsInitialized(true);
1759 return;
1760 }
1761 diskMetric.addData(0, uWriteRate);
1762 diskMetric.addData(1, uReadRate);
1763
1764 if (m_infoLabels.contains(Metric_Type_Disk_InOut) && m_infoLabels[Metric_Type_Disk_InOut])
1765 {
1766 QString strInfo = QString("<b>%1</b></b><br/><font color=\"%2\">%3: %4<br/>%5: %6</font><br/><font color=\"%7\">%8: %9<br/>%10: %11</font>")
1767 .arg(m_strDiskIOInfoLabelTitle)
1768 .arg(dataColorString(Metric_Type_Disk_InOut, 0)).arg(m_strDiskIOInfoLabelWritten).arg(UITranslator::formatSize(uWriteRate, g_iDecimalCount))
1769 .arg(m_strDiskIOInfoLabelWrittenTotal).arg(UITranslator::formatSize((quint64)uDiskIOTotalWritten, g_iDecimalCount))
1770 .arg(dataColorString(Metric_Type_Disk_InOut, 1)).arg(m_strDiskIOInfoLabelRead).arg(UITranslator::formatSize(uReadRate, g_iDecimalCount))
1771 .arg(m_strDiskIOInfoLabelReadTotal).arg(UITranslator::formatSize((quint64)uDiskIOTotalRead, g_iDecimalCount));
1772 m_infoLabels[Metric_Type_Disk_InOut]->setText(strInfo);
1773 }
1774 if (m_charts.contains(Metric_Type_Disk_InOut))
1775 m_charts[Metric_Type_Disk_InOut]->update();
1776}
1777
1778void UIVMActivityMonitorLocal::resetVMExitInfoLabel()
1779{
1780 if (m_infoLabels.contains(Metric_Type_VM_Exits) && m_infoLabels[Metric_Type_VM_Exits])
1781 {
1782 QString strInfo;
1783 strInfo = QString("<b>%1</b></b><br/>%2: %3<br/>%4: %5")
1784 .arg(m_strVMExitInfoLabelTitle)
1785 .arg(m_strVMExitLabelCurrent).arg("--")
1786 .arg(m_strVMExitLabelTotal).arg("--");
1787
1788 m_infoLabels[Metric_Type_VM_Exits]->setText(strInfo);
1789 }
1790}
1791
1792void UIVMActivityMonitorLocal::resetCPUInfoLabel()
1793{
1794 if (m_infoLabels.contains(Metric_Type_CPU) && m_infoLabels[Metric_Type_CPU])
1795 {
1796 QString strInfo =QString("<b>%1</b></b><br/>%2: %3<br/>%4: %5")
1797 .arg(m_strCPUInfoLabelTitle)
1798 .arg(m_strCPUInfoLabelGuest).arg("--")
1799 .arg(m_strCPUInfoLabelVMM).arg("--");
1800 m_infoLabels[Metric_Type_CPU]->setText(strInfo);
1801 }
1802}
1803
1804void UIVMActivityMonitorLocal::resetNetworkInfoLabel()
1805{
1806 if (m_infoLabels.contains(Metric_Type_Network_InOut) && m_infoLabels[Metric_Type_Network_InOut])
1807 {
1808 QString strInfo = QString("<b>%1</b></b><br/>%2: %3<br/>%4 %5<br/>%6: %7<br/>%8 %9")
1809 .arg(m_strNetworkInfoLabelTitle)
1810 .arg(m_strNetworkInfoLabelReceived).arg("--")
1811 .arg(m_strNetworkInfoLabelReceivedTotal).arg("--")
1812 .arg(m_strNetworkInfoLabelTransmitted).arg("--")
1813 .arg(m_strNetworkInfoLabelTransmittedTotal).arg("--");
1814 m_infoLabels[Metric_Type_Network_InOut]->setText(strInfo);
1815 }
1816}
1817
1818void UIVMActivityMonitorLocal::resetDiskIOInfoLabel()
1819{
1820 if (m_infoLabels.contains(Metric_Type_Disk_InOut) && m_infoLabels[Metric_Type_Disk_InOut])
1821 {
1822 QString strInfo = QString("<b>%1</b></b><br/>%2: %3<br/>%4 %5<br/>%6: %7<br/>%8 %9")
1823 .arg(m_strDiskIOInfoLabelTitle)
1824 .arg(m_strDiskIOInfoLabelWritten).arg("--")
1825 .arg(m_strDiskIOInfoLabelWrittenTotal).arg("--")
1826 .arg(m_strDiskIOInfoLabelRead).arg("--")
1827 .arg(m_strDiskIOInfoLabelReadTotal).arg("--");
1828 m_infoLabels[Metric_Type_Disk_InOut]->setText(strInfo);
1829 }
1830}
1831
1832/*********************************************************************************************************************************
1833* UIVMActivityMonitorCloud definition. *
1834*********************************************************************************************************************************/
1835
1836UIVMActivityMonitorCloud::UIVMActivityMonitorCloud(EmbedTo enmEmbedding, QWidget *pParent,
1837 const CCloudMachine &machine, UIActionPool *pActionPool)
1838 :UIVMActivityMonitor(enmEmbedding, pParent, pActionPool, 60 /* iMaximumQueueSize */)
1839 , m_pMachineStateUpdateTimer(0)
1840 , m_enmMachineState(KCloudMachineState_Invalid)
1841{
1842 m_metricTypeDict[KMetricType_CpuUtilization] = Metric_Type_CPU;
1843 m_metricTypeDict[KMetricType_MemoryUtilization] = Metric_Type_RAM;
1844 m_metricTypeDict[KMetricType_DiskBytesRead] = Metric_Type_Disk_Out;
1845 m_metricTypeDict[KMetricType_DiskBytesWritten] = Metric_Type_Disk_In;
1846 m_metricTypeDict[KMetricType_NetworksBytesIn] = Metric_Type_Network_In;
1847 m_metricTypeDict[KMetricType_NetworksBytesOut] = Metric_Type_Network_Out;
1848
1849 setMachine(machine);
1850 m_uTotalRAM = UIMonitorCommon::determineTotalRAMAmount(m_comMachine);
1851
1852 m_pMachineStateUpdateTimer = new QTimer(this);
1853 if (m_pMachineStateUpdateTimer)
1854 connect(m_pMachineStateUpdateTimer, &QTimer::timeout, this, &UIVMActivityMonitorCloud::sltMachineStateUpdateTimeout);
1855
1856 prepareMetrics();
1857 prepareWidgets();
1858 sltRetranslateUI();
1859 prepareActions();
1860 resetCPUInfoLabel();
1861 resetNetworkInInfoLabel();
1862 resetNetworkOutInfoLabel();
1863 resetDiskIOWrittenInfoLabel();
1864 resetDiskIOReadInfoLabel();
1865 resetRAMInfoLabel();
1866 connect(&translationEventListener(), &UITranslationEventListener::sigRetranslateUI, this, &UIVMActivityMonitorCloud::sltRetranslateUI);
1867
1868 /* Start the timer: */
1869 start();
1870}
1871
1872void UIVMActivityMonitorCloud::setMachine(const CCloudMachine &comMachine)
1873{
1874 m_comMachine = comMachine;
1875 if (!m_comMachine.isOk())
1876 return;
1877 setEnabled(m_comMachine.GetState() == KCloudMachineState_Running);
1878}
1879
1880void UIVMActivityMonitorCloud::sltMachineStateUpdateTimeout()
1881{
1882 if (!m_comMachine.isOk())
1883 return;
1884
1885 KCloudMachineState enmNewState = m_comMachine.GetState();
1886 /* No changes. Noting to do: */
1887 if (m_enmMachineState == enmNewState)
1888 return;
1889
1890 if (m_ReadListProgressTask)
1891 {
1892 disconnect(m_ReadListProgressTask, &UIProgressTaskReadCloudMachineMetricList::sigMetricListReceived,
1893 this, &UIVMActivityMonitorCloud::sltMetricNameListingComplete);
1894 delete m_ReadListProgressTask;
1895 }
1896
1897 if (enmNewState == KCloudMachineState_Running)
1898 {
1899 m_ReadListProgressTask = new UIProgressTaskReadCloudMachineMetricList(this, m_comMachine);
1900 if (m_ReadListProgressTask)
1901 {
1902 connect(m_ReadListProgressTask, &UIProgressTaskReadCloudMachineMetricList::sigMetricListReceived,
1903 this, &UIVMActivityMonitorCloud::sltMetricNameListingComplete);
1904 m_ReadListProgressTask->start();
1905 }
1906 setEnabled(true);
1907 /* Every minute: */
1908 if (m_pTimer)
1909 m_pTimer->start(1000 * 60);
1910 }
1911 else
1912 {
1913 reset();
1914 if (m_pTimer)
1915 m_pTimer->stop();
1916 }
1917 m_enmMachineState = enmNewState;
1918}
1919
1920void UIVMActivityMonitorCloud::sltMetricNameListingComplete(QVector<QString> metricNameList)
1921{
1922 m_availableMetricTypes.clear();
1923 foreach (const QString &strName, metricNameList)
1924 m_availableMetricTypes << gpConverter->fromInternalString<KMetricType>(strName);
1925
1926 if (!m_availableMetricTypes.isEmpty())
1927 start();
1928
1929 sender()->deleteLater();
1930 obtainDataAndUpdate();
1931}
1932
1933void UIVMActivityMonitorCloud::sltMetricDataReceived(KMetricType enmMetricType,
1934 const QVector<QString> &data, const QVector<QString> &timeStamps)
1935{
1936 if (data.size() != timeStamps.size())
1937 return;
1938 /* Hack alert!! I am told that time series' interval is `guaranteed` to be 1 min. although it is clearly
1939 * parametrized in OCI API. I would much prefer to have some way of deermining the said interval via our API
1940 * but it looks like Christmas is over: */
1941 const int iInterval = 60;
1942 QVector<QString> newTimeStamps;
1943 QVector<quint64> newData;
1944 for (int i = 0; i < timeStamps.size() - 1; ++i)
1945 {
1946 if (timeStamps[i].isEmpty())
1947 continue;
1948 QTime time = QDateTime::fromString(timeStamps[i], Qt::RFC2822Date).time();
1949 if (!time.isValid())
1950 continue;
1951 newTimeStamps << time.toString("hh:mm");
1952 /* It looks like in some cases OCI sends us negative values: */
1953 if (data[i].toFloat() < 0)
1954 newData << 0U;
1955 else
1956 newData << (quint64)data[i].toFloat();
1957
1958 QTime nextTime = QDateTime::fromString(timeStamps[i + 1], Qt::RFC2822Date).time();
1959 while(time.secsTo(nextTime) > iInterval)
1960 {
1961 time = time.addSecs(iInterval);
1962 newTimeStamps << time.toString("hh:mm");
1963 newData << uInvalidValueSentinel;
1964 }
1965 }
1966 if (!data.isEmpty())
1967 {
1968 if (!timeStamps.last().isEmpty())
1969 newTimeStamps << QDateTime::fromString(timeStamps.last(), Qt::RFC2822Date).time().toString("hh:mm");
1970 newData << (quint64)data.last().toFloat();
1971 }
1972 AssertReturnVoid(newData.size() == newTimeStamps.size());
1973
1974 if (enmMetricType == KMetricType_NetworksBytesIn)
1975 m_metrics[Metric_Type_Network_In].reset();
1976 else if (enmMetricType == KMetricType_NetworksBytesOut)
1977 m_metrics[Metric_Type_Network_Out].reset();
1978 else if (enmMetricType == KMetricType_DiskBytesRead)
1979 m_metrics[Metric_Type_Disk_Out].reset();
1980 else if (enmMetricType == KMetricType_DiskBytesWritten)
1981 m_metrics[Metric_Type_Disk_In].reset();
1982 else if (enmMetricType == KMetricType_CpuUtilization)
1983 m_metrics[Metric_Type_CPU].reset();
1984 else if (enmMetricType == KMetricType_MemoryUtilization)
1985 m_metrics[Metric_Type_RAM].reset();
1986
1987
1988 for (int i = 0; i < newData.size(); ++i)
1989 {
1990 if (enmMetricType == KMetricType_CpuUtilization)
1991 updateCPUChart(newData[i], newTimeStamps[i]);
1992 else if (enmMetricType == KMetricType_NetworksBytesOut)
1993 updateNetworkOutChart(newData[i], newTimeStamps[i]);
1994 else if (enmMetricType == KMetricType_NetworksBytesIn)
1995 updateNetworkInChart(newData[i], newTimeStamps[i]);
1996 else if (enmMetricType == KMetricType_DiskBytesRead)
1997 updateDiskIOReadChart(newData[i], newTimeStamps[i]);
1998 else if (enmMetricType == KMetricType_DiskBytesWritten)
1999 updateDiskIOWrittenChart(newData[i], newTimeStamps[i]);
2000 else if (enmMetricType == KMetricType_MemoryUtilization)
2001 {
2002 if (m_uTotalRAM != 0)
2003 {
2004 /* calculate used RAM amount in kb: */
2005 if (newData[i] != uInvalidValueSentinel)
2006 {
2007 quint64 iUsedRAM = newData[i] * (m_uTotalRAM / 100.f);
2008 updateRAMChart(iUsedRAM, newTimeStamps[i]);
2009 }
2010 else
2011 updateRAMChart(newData[i], newTimeStamps[i]);
2012 }
2013 }
2014 }
2015 sender()->deleteLater();
2016}
2017
2018QUuid UIVMActivityMonitorCloud::machineId() const
2019{
2020 if (m_comMachine.isOk())
2021 return m_comMachine.GetId();
2022 return QUuid();
2023}
2024
2025QString UIVMActivityMonitorCloud::machineName() const
2026{
2027 if (m_comMachine.isOk())
2028 return m_comMachine.GetName();
2029 return QString();
2030}
2031
2032void UIVMActivityMonitorCloud::sltRetranslateUI()
2033{
2034 UIVMActivityMonitor::sltRetranslateUI();
2035 foreach (UIChart *pChart, m_charts)
2036 pChart->setXAxisLabel(QApplication::translate("UIVMInformationDialog", "Min.", "short from minutes"));
2037
2038 m_strNetworkInInfoLabelTitle = QApplication::translate("UIVMInformationDialog", "Network");
2039 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strNetworkInInfoLabelTitle.length());
2040
2041 m_strNetworkOutInfoLabelTitle = QApplication::translate("UIVMInformationDialog", "Network");
2042 m_iMaximumLabelLength = qMax(m_iMaximumLabelLength, m_strNetworkOutInfoLabelTitle.length());
2043
2044 setInfoLabelWidth();
2045}
2046
2047void UIVMActivityMonitorCloud::obtainDataAndUpdate()
2048{
2049 foreach (const KMetricType &enmMetricType, m_availableMetricTypes)
2050 {
2051 UIMetric metric;
2052 int iDataSeriesIndex = 0;
2053 if (!findMetric(enmMetricType, metric, iDataSeriesIndex))
2054 continue;
2055 /* Be a paranoid: */
2056 if (iDataSeriesIndex >= DATA_SERIES_SIZE)
2057 continue;
2058#if 0
2059 int iDataSize = 1;
2060 if (metric.dataSize(iDataSeriesIndex) == 0)
2061 iDataSize = 60;
2062#endif
2063 /* Request the whole time series (all 60 values) at each iteration to detect time points with no
2064 * data (due to stop and restart). We sanitize the data when we receive it and mark time points
2065 * with no data with sentinel value: */
2066 int iDataSize = 60;
2067 UIProgressTaskReadCloudMachineMetricData *pTask = new UIProgressTaskReadCloudMachineMetricData(this, m_comMachine,
2068 enmMetricType, iDataSize);
2069 connect(pTask, &UIProgressTaskReadCloudMachineMetricData::sigMetricDataReceived,
2070 this, &UIVMActivityMonitorCloud::sltMetricDataReceived);
2071 pTask->start();
2072 }
2073}
2074
2075QString UIVMActivityMonitorCloud::defaultMachineFolder() const
2076{
2077 char szPath[RTPATH_MAX];
2078 int rc = RTPathUserDocuments(szPath, sizeof(szPath));
2079 if (RT_SUCCESS(rc))
2080 return QString(szPath);
2081 return gpGlobalSession->virtualBox().GetHomeFolder();
2082}
2083
2084void UIVMActivityMonitorCloud::reset()
2085{
2086 setEnabled(false);
2087
2088 if (m_pTimer)
2089 m_pTimer->stop();
2090 /* reset the metrics. this will delete their data cache: */
2091 for (QMap<Metric_Type, UIMetric>::iterator iterator = m_metrics.begin();
2092 iterator != m_metrics.end(); ++iterator)
2093 iterator.value().reset();
2094 /* force update on the charts to draw now emptied metrics' data: */
2095 for (QMap<Metric_Type, UIChart*>::iterator iterator = m_charts.begin();
2096 iterator != m_charts.end(); ++iterator)
2097 iterator.value()->update();
2098 /* Reset the info labels: */
2099 resetCPUInfoLabel();
2100 resetRAMInfoLabel();
2101 resetNetworkInInfoLabel();
2102 resetNetworkOutInfoLabel();
2103 resetDiskIOWrittenInfoLabel();
2104 resetDiskIOReadInfoLabel();
2105
2106 update();
2107 //sltClearCOMData();
2108}
2109
2110void UIVMActivityMonitorCloud::start()
2111{
2112 sltMachineStateUpdateTimeout();
2113 if (m_pMachineStateUpdateTimer)
2114 m_pMachineStateUpdateTimer->start(1000 * 10);
2115}
2116
2117void UIVMActivityMonitorCloud::updateCPUChart(quint64 iLoadPercentage, const QString &strLabel)
2118{
2119 UIMetric &CPUMetric = m_metrics[Metric_Type_CPU];
2120 CPUMetric.addData(0, iLoadPercentage, strLabel);
2121 CPUMetric.setMaximum(100);
2122 if (m_infoLabels.contains(Metric_Type_CPU) && m_infoLabels[Metric_Type_CPU])
2123 {
2124 QString strInfo;
2125
2126 strInfo = QString("<b>%1</b></b><br/><font color=\"%2\">%3: %4%5</font>")
2127 .arg(m_strCPUInfoLabelTitle)
2128 .arg(dataColorString(Metric_Type_CPU, 0))
2129 .arg(m_strCPUInfoLabelGuest).arg(QString::number(iLoadPercentage)).arg(CPUMetric.unit());
2130
2131 m_infoLabels[Metric_Type_CPU]->setText(strInfo);
2132 }
2133
2134 if (m_charts.contains(Metric_Type_CPU))
2135 m_charts[Metric_Type_CPU]->update();
2136}
2137
2138void UIVMActivityMonitorCloud::updateNetworkInChart(quint64 uReceiveRate, const QString &strLabel)
2139{
2140 UIMetric &networkMetric = m_metrics[Metric_Type_Network_In];
2141 networkMetric.addData(0, uReceiveRate, strLabel);
2142
2143
2144 if (m_infoLabels.contains(Metric_Type_Network_In) && m_infoLabels[Metric_Type_Network_In])
2145 {
2146 QString strInfo;
2147 strInfo = QString("<b>%1</b></b><br/><font color=\"%2\">%3: %4</font><br/>")
2148 .arg(m_strNetworkInInfoLabelTitle)
2149 .arg(dataColorString(Metric_Type_Network_In, 0)).arg(m_strNetworkInfoLabelReceived).arg(UITranslator::formatSize(uReceiveRate, g_iDecimalCount));
2150
2151 m_infoLabels[Metric_Type_Network_In]->setText(strInfo);
2152 }
2153 if (m_charts.contains(Metric_Type_Network_In))
2154 m_charts[Metric_Type_Network_In]->update();
2155}
2156
2157void UIVMActivityMonitorCloud::updateNetworkOutChart(quint64 uTransmitRate, const QString &strLabel)
2158{
2159 UIMetric &networkMetric = m_metrics[Metric_Type_Network_Out];
2160 networkMetric.addData(0, uTransmitRate, strLabel);
2161
2162 if (m_infoLabels.contains(Metric_Type_Network_Out) && m_infoLabels[Metric_Type_Network_Out])
2163 {
2164 QString strInfo;
2165 strInfo = QString("<b>%1</b></b><br/><font color=\"%5\">%6: %7<br/></font>")
2166 .arg(m_strNetworkOutInfoLabelTitle)
2167 .arg(dataColorString(Metric_Type_Network_Out, 0)).arg(m_strNetworkInfoLabelTransmitted).arg(UITranslator::formatSize(uTransmitRate, g_iDecimalCount));
2168
2169 m_infoLabels[Metric_Type_Network_Out]->setText(strInfo);
2170 }
2171 if (m_charts.contains(Metric_Type_Network_Out))
2172 m_charts[Metric_Type_Network_Out]->update();
2173}
2174
2175void UIVMActivityMonitorCloud::updateDiskIOWrittenChart(quint64 uWriteRate, const QString &strLabel)
2176{
2177 UIMetric &diskMetric = m_metrics[Metric_Type_Disk_In];
2178
2179 diskMetric.addData(0, uWriteRate, strLabel);
2180
2181
2182 if (m_infoLabels.contains(Metric_Type_Disk_In) && m_infoLabels[Metric_Type_Disk_In])
2183 {
2184 QString strInfo = QString("<b>%1</b></b><br/> <font color=\"%2\">%3: %4</font>")
2185 .arg(m_strDiskIOInfoLabelTitle)
2186 .arg(dataColorString(Metric_Type_Disk_In, 0)).arg(m_strDiskIOInfoLabelWritten).arg(UITranslator::formatSize(uWriteRate, g_iDecimalCount));
2187
2188 m_infoLabels[Metric_Type_Disk_In]->setText(strInfo);
2189 }
2190
2191 if (m_charts.contains(Metric_Type_Disk_In))
2192 m_charts[Metric_Type_Disk_In]->update();
2193}
2194
2195void UIVMActivityMonitorCloud::updateDiskIOReadChart(quint64 uReadRate, const QString &strLabel)
2196{
2197 UIMetric &diskMetric = m_metrics[Metric_Type_Disk_Out];
2198
2199 diskMetric.addData(0, uReadRate, strLabel);
2200
2201
2202 if (m_infoLabels.contains(Metric_Type_Disk_Out) && m_infoLabels[Metric_Type_Disk_Out])
2203 {
2204 QString strInfo = QString("<b>%1</b></b><br/> <font color=\"%2\">%3: %4</font>")
2205 .arg(m_strDiskIOInfoLabelTitle)
2206 .arg(dataColorString(Metric_Type_Disk_Out, 0)).arg(m_strDiskIOInfoLabelRead).arg(UITranslator::formatSize(uReadRate, g_iDecimalCount));
2207
2208 m_infoLabels[Metric_Type_Disk_Out]->setText(strInfo);
2209 }
2210
2211 if (m_charts.contains(Metric_Type_Disk_Out))
2212 m_charts[Metric_Type_Disk_Out]->update();
2213}
2214
2215
2216void UIVMActivityMonitorCloud::updateRAMChart(quint64 iUsedRAM, const QString &strLabel)
2217{
2218 UIMetric &RAMMetric = m_metrics[Metric_Type_RAM];
2219 RAMMetric.setMaximum(m_uTotalRAM);
2220 RAMMetric.addData(0, iUsedRAM, strLabel);
2221
2222 if (m_infoLabels.contains(Metric_Type_RAM) && m_infoLabels[Metric_Type_RAM])
2223 {
2224 QString strInfo;
2225 strInfo = QString("<b>%1</b><br/>%2: %3<br/><font color=\"%4\">%5: %6</font><br/><font color=\"%7\">%8: %9</font>")
2226 .arg(m_strRAMInfoLabelTitle)
2227 .arg(m_strRAMInfoLabelTotal).arg(UITranslator::formatSize(_1K * m_uTotalRAM, g_iDecimalCount))
2228 .arg(dataColorString(Metric_Type_RAM, 1)).arg(m_strRAMInfoLabelFree).arg(UITranslator::formatSize(_1K * (m_uTotalRAM - iUsedRAM), g_iDecimalCount))
2229 .arg(dataColorString(Metric_Type_RAM, 0)).arg(m_strRAMInfoLabelUsed).arg(UITranslator::formatSize(_1K * iUsedRAM, g_iDecimalCount));
2230 m_infoLabels[Metric_Type_RAM]->setText(strInfo);
2231 }
2232
2233 if (m_charts.contains(Metric_Type_RAM))
2234 m_charts[Metric_Type_RAM]->update();
2235}
2236
2237bool UIVMActivityMonitorCloud::findMetric(KMetricType enmMetricType, UIMetric &metric, int &iDataSeriesIndex) const
2238{
2239 if (!m_metricTypeDict.contains(enmMetricType))
2240 return false;
2241
2242 Metric_Type enmType = m_metricTypeDict[enmMetricType];
2243
2244 if (!m_metrics.contains(enmType))
2245 return false;
2246
2247 metric = m_metrics[enmType];
2248 iDataSeriesIndex = 0;
2249 if (enmMetricType == KMetricType_NetworksBytesOut ||
2250 enmMetricType == KMetricType_DiskBytesRead)
2251 iDataSeriesIndex = 1;
2252 return true;
2253}
2254
2255void UIVMActivityMonitorCloud::prepareMetrics()
2256{
2257 /* RAM Metric: */
2258 if (m_uTotalRAM != 0)
2259 {
2260 UIMetric ramMetric("kb", m_iMaximumQueueSize);
2261 ramMetric.setDataSeriesName(0, "Used");
2262 m_metrics.insert(Metric_Type_RAM, ramMetric);
2263 }
2264
2265 /* CPU Metric: */
2266 UIMetric cpuMetric("%", m_iMaximumQueueSize);
2267 cpuMetric.setDataSeriesName(0, "CPU Utilization");
2268 m_metrics.insert(Metric_Type_CPU, cpuMetric);
2269
2270 /* Network in metric: */
2271 UIMetric networkInMetric("B", m_iMaximumQueueSize);
2272 networkInMetric.setDataSeriesName(0, "Receive Rate");
2273 networkInMetric.setAutoUpdateMaximum(true);
2274 m_metrics.insert(Metric_Type_Network_In, networkInMetric);
2275
2276 /* Network out metric: */
2277 UIMetric networkOutMetric("B", m_iMaximumQueueSize);
2278 networkOutMetric.setDataSeriesName(0, "Transmit Rate");
2279 networkOutMetric.setAutoUpdateMaximum(true);
2280 m_metrics.insert(Metric_Type_Network_Out, networkOutMetric);
2281
2282 /* Disk write metric */
2283 UIMetric diskIOWrittenMetric("B", m_iMaximumQueueSize);
2284 diskIOWrittenMetric.setDataSeriesName(0, "Write Rate");
2285 diskIOWrittenMetric.setAutoUpdateMaximum(true);
2286 m_metrics.insert(Metric_Type_Disk_In, diskIOWrittenMetric);
2287
2288 /* Disk read metric */
2289 UIMetric diskIOReadMetric("B", m_iMaximumQueueSize);
2290 diskIOReadMetric.setDataSeriesName(0, "Read Rate");
2291 diskIOReadMetric.setAutoUpdateMaximum(true);
2292 m_metrics.insert(Metric_Type_Disk_Out, diskIOReadMetric);
2293
2294}
2295
2296void UIVMActivityMonitorCloud::prepareWidgets()
2297{
2298 UIVMActivityMonitor::prepareWidgets();
2299
2300 QVector<Metric_Type> chartOrder;
2301 chartOrder << Metric_Type_CPU << Metric_Type_RAM <<
2302 Metric_Type_Network_In << Metric_Type_Network_Out << Metric_Type_Disk_In << Metric_Type_Disk_Out;
2303 int iRow = 0;
2304 foreach (Metric_Type enmType, chartOrder)
2305 {
2306 if (!m_metrics.contains(enmType))
2307 continue;
2308
2309 QHBoxLayout *pChartLayout = new QHBoxLayout;
2310 pChartLayout->setSpacing(0);
2311
2312 QLabel *pLabel = new QLabel(this);
2313
2314 QPalette tempPal = pLabel->palette();
2315 tempPal.setColor(QPalette::Window, tempPal.color(QPalette::Window).lighter(g_iBackgroundTint));
2316 pLabel->setPalette(tempPal);
2317 pLabel->setAutoFillBackground(true);
2318
2319 pLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
2320 pChartLayout->addWidget(pLabel);
2321 m_infoLabels.insert(enmType, pLabel);
2322
2323 UIChart *pChart = new UIChart(this, &(m_metrics[enmType]), m_pActionPool, m_iMaximumQueueSize);
2324 connect(pChart, &UIChart::sigExportMetricsToFile,
2325 this, &UIVMActivityMonitor::sltExportMetricsToFile);
2326 m_charts.insert(enmType, pChart);
2327 pChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
2328 pChartLayout->addWidget(pChart);
2329 m_pContainerLayout->addLayout(pChartLayout, iRow, 0, 1, 2);
2330 ++iRow;
2331 }
2332
2333 // if (m_charts.contains(Metric_Type_Network_Out) && m_charts[Metric_Type_Network_Out])
2334 // m_charts[Metric_Type_Network_Out]->setDataSeriesColor(0, QColor(0, 0, 200, 255));
2335
2336 // if (m_charts.contains(Metric_Type_Disk_Out) && m_charts[Metric_Type_Disk_Out])
2337 // m_charts[Metric_Type_Disk_Out]->setDataSeriesColor(0, QColor(0, 0, 200, 255));
2338
2339 QWidget *bottomSpacerWidget = new QWidget(this);
2340 bottomSpacerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
2341 bottomSpacerWidget->setVisible(true);
2342 m_pContainerLayout->addWidget(bottomSpacerWidget, iRow, 0, 1, 2);
2343 m_charts[Metric_Type_CPU]->setShowPieChart(false);
2344}
2345
2346void UIVMActivityMonitorCloud::resetCPUInfoLabel()
2347{
2348 if (m_infoLabels.contains(Metric_Type_CPU) && m_infoLabels[Metric_Type_CPU])
2349 {
2350 QString strInfo;
2351
2352 strInfo = QString("<b>%1</b></b><br/><font>%2: %3</font>")
2353 .arg(m_strCPUInfoLabelTitle)
2354 .arg(m_strCPUInfoLabelGuest).arg("---");
2355
2356 m_infoLabels[Metric_Type_CPU]->setText(strInfo);
2357 }
2358}
2359
2360void UIVMActivityMonitorCloud::resetNetworkInInfoLabel()
2361{
2362 if (m_infoLabels.contains(Metric_Type_Network_In) && m_infoLabels[Metric_Type_Network_In])
2363 {
2364 QString strInfo = QString("<b>%1</b></b><br/>%2: %3")
2365 .arg(m_strNetworkInInfoLabelTitle)
2366 .arg(m_strNetworkInfoLabelReceived).arg("--");
2367
2368 m_infoLabels[Metric_Type_Network_In]->setText(strInfo);
2369 }
2370}
2371
2372void UIVMActivityMonitorCloud::resetNetworkOutInfoLabel()
2373{
2374 if (m_infoLabels.contains(Metric_Type_Network_Out) && m_infoLabels[Metric_Type_Network_Out])
2375 {
2376 QString strInfo = QString("<b>%1</b></b><br/>%2: %3")
2377 .arg(m_strNetworkOutInfoLabelTitle)
2378 .arg(m_strNetworkInfoLabelTransmitted).arg("--");
2379
2380 m_infoLabels[Metric_Type_Network_Out]->setText(strInfo);
2381 }
2382}
2383
2384void UIVMActivityMonitorCloud::resetDiskIOWrittenInfoLabel()
2385{
2386 if (m_infoLabels.contains(Metric_Type_Disk_In) && m_infoLabels[Metric_Type_Disk_In])
2387 {
2388 QString strInfo = QString("<b>%1</b></b><br/>%2: %3")
2389 .arg(m_strDiskIOInfoLabelTitle)
2390 .arg(m_strDiskIOInfoLabelWritten).arg("--");
2391 m_infoLabels[Metric_Type_Disk_In]->setText(strInfo);
2392 }
2393}
2394
2395void UIVMActivityMonitorCloud::resetDiskIOReadInfoLabel()
2396{
2397 if (m_infoLabels.contains(Metric_Type_Disk_Out) && m_infoLabels[Metric_Type_Disk_Out])
2398 {
2399 QString strInfo = QString("<b>%1</b></b><br/>%2: %3")
2400 .arg(m_strDiskIOInfoLabelTitle)
2401 .arg(m_strDiskIOInfoLabelRead).arg("--");
2402 m_infoLabels[Metric_Type_Disk_Out]->setText(strInfo);
2403 }
2404}
2405
2406/* static */
2407QString UIVMActivityMonitorCloud::formatCloudTimeStamp(const QString &strInput)
2408{
2409 if (strInput.isEmpty())
2410 return QString();
2411 QDateTime dateTime = QDateTime::fromString(strInput, Qt::RFC2822Date);
2412
2413 if (!dateTime.isValid())
2414 return QString();
2415
2416 return dateTime.time().toString("HH:mm");
2417}
2418
2419#include "UIVMActivityMonitor.moc"
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette