VirtualBox

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

Last change on this file since 103131 was 103131, checked in by vboxsync, 14 months ago

FE/Qt: bugref:10501. Moving cloud machine metric data related progress tasks to common area.

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

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