573 lines
14 KiB
C++
573 lines
14 KiB
C++
#include "xlruchartsform.h"
|
|
#include "ui_xlruchartsform.h"
|
|
|
|
#include <QChart>
|
|
#include <QLineSeries>
|
|
#include <QChartView>
|
|
|
|
#include <QGraphicsScene>
|
|
#include <QGraphicsLayout>
|
|
#include <QValueAxis>
|
|
#include <QDateTimeAxis>
|
|
#include <QDateTime>
|
|
#include <QTimerEvent>
|
|
#include <QToolTip>
|
|
#include <QMessageBox>
|
|
#include <QSpinBox>
|
|
#include <QtCharts/QLegendMarker>
|
|
|
|
#include <QDebug>
|
|
|
|
using namespace QtCharts;
|
|
|
|
|
|
class MyChartView: public QChartView
|
|
{
|
|
//Q_OBJECT
|
|
|
|
public:
|
|
void mousePressEvent(QMouseEvent* event) override
|
|
{
|
|
#if 0
|
|
QChart* _chart=chart();
|
|
|
|
//qreal x = (event->pos()).x();
|
|
//qreal y = (event->pos()).y();
|
|
|
|
//qreal xVal = _chart->mapToValue(event->pos()).x();
|
|
qreal yVal = _chart->mapToValue(event->pos()).y();
|
|
|
|
if (true) //xVal <= maxX && xVal >= minX && yVal <= maxY && yVal >= minY)
|
|
{
|
|
QToolTip::showText(mapToGlobal(event->pos()), QString("%1").arg(yVal));
|
|
}
|
|
#endif
|
|
QChartView::mousePressEvent(event);
|
|
}
|
|
};
|
|
|
|
class Sensor
|
|
{
|
|
public:
|
|
QString name;
|
|
QLineSeries* series;
|
|
MyChartView* view;
|
|
QValueAxis* axis;
|
|
|
|
Sensor():
|
|
series(0),
|
|
view(0),
|
|
axis(0)
|
|
{
|
|
}
|
|
};
|
|
|
|
class XlruChartsForm::Implementation
|
|
{
|
|
public:
|
|
|
|
QtCharts::QChart* ch;
|
|
QVector<Sensor> sensors;
|
|
QVector<QLineSeries*> s;
|
|
|
|
MyChartView* mainView;
|
|
QDateTimeAxis* commonX;
|
|
QValueAxis* temperatureY;
|
|
QValueAxis* currentY;
|
|
QValueAxis* voltageY;
|
|
|
|
QVector<MyChartView*> cViews;
|
|
|
|
int t_id;
|
|
|
|
QDateTime timeStart;
|
|
qint64 timeStartMs;
|
|
|
|
QDateTime timeNow;
|
|
qint64 timeNowMs;
|
|
qint64 lastSampleMs;
|
|
bool tgtAlive;
|
|
|
|
QMessageBox alertBox;
|
|
|
|
int max_temperature;
|
|
int max_temp_period;
|
|
bool max_temp_restart;
|
|
|
|
Implementation():
|
|
alertBox(QMessageBox::Warning, "WARNING", "Overtemperature")
|
|
{
|
|
alertBox.setWindowFlags(Qt::WindowStaysOnTopHint);
|
|
alertBox.setModal( false );
|
|
|
|
tgtAlive=false;
|
|
lastSampleMs=0;
|
|
max_temperature=0;
|
|
max_temp_period=0;
|
|
max_temp_restart=false;
|
|
}
|
|
|
|
QChart* newCharts()
|
|
{
|
|
QChart* c=new QChart;
|
|
c->setAnimationOptions(QChart::NoAnimation);
|
|
|
|
return c;
|
|
}
|
|
QLineSeries* newSeries(const QString& name, QChart* c, QValueAxis* axis)
|
|
{
|
|
QLineSeries* tmp=new QLineSeries;
|
|
s.append(tmp);
|
|
c->addSeries(tmp);
|
|
tmp->attachAxis(commonX);
|
|
tmp->attachAxis(axis);
|
|
//c->setAxisX(commonX, tmp);
|
|
//c->setAxisY(axis, tmp);
|
|
tmp->setName(name);
|
|
return tmp;
|
|
}
|
|
};
|
|
|
|
XlruChartsForm::XlruChartsForm(QWidget *parent) :
|
|
QWidget(parent),
|
|
ui(new Ui::XlruChartsForm),
|
|
p_(*new XlruChartsForm::Implementation),
|
|
m_tooltip(0)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
ui->tbEnable->setChecked(true);
|
|
|
|
p_.mainView=new MyChartView;
|
|
p_.mainView->setOptimizationFlag(QGraphicsView::DontSavePainterState, true);
|
|
p_.mainView->setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, true);
|
|
p_.mainView->setRenderHint(QPainter::Antialiasing, false);
|
|
p_.mainView->setRenderHint(QPainter::TextAntialiasing, false);
|
|
p_.mainView->setRenderHint(QPainter::SmoothPixmapTransform, false);
|
|
p_.mainView->setRubberBand( QChartView::HorizontalRubberBand );
|
|
|
|
p_.mainView->chart()->layout()->setContentsMargins(0, 0, 0, 0);
|
|
p_.mainView->chart()->setBackgroundRoundness(0);
|
|
p_.mainView->chart()->setMargins(QMargins(0, 0, 0, 0));
|
|
|
|
//p_.mainView->setToolTip("Monitoring Temperature/Current/Voltage");
|
|
|
|
//p_.mainView->chart()->setMinimumSize(50, 50);
|
|
|
|
p_.ch=p_.mainView->chart();
|
|
|
|
//theChart=this->chart();
|
|
//if (commonX==0)
|
|
{
|
|
p_.commonX= new QDateTimeAxis;
|
|
p_.commonX->setFormat("h:mm:ss");
|
|
p_.commonX->setTickCount(0);
|
|
p_.commonX->setGridLineVisible(false);
|
|
|
|
p_.timeNow=QDateTime::currentDateTime();
|
|
p_.timeNowMs=p_.timeNow.toMSecsSinceEpoch();
|
|
|
|
p_.timeStart=p_.timeNow;
|
|
p_.timeStartMs=p_.timeNowMs;
|
|
|
|
p_.commonX->setMin(p_.timeNow);
|
|
p_.commonX->setMax(p_.timeNow.addSecs(60));
|
|
|
|
p_.temperatureY=new QValueAxis;
|
|
p_.temperatureY->setGridLineVisible(false);
|
|
p_.temperatureY->setRange(10, 80);
|
|
p_.temperatureY->setVisible(true);
|
|
p_.temperatureY->setTitleText("Temp(C)");
|
|
p_.temperatureY->setTitleVisible(true);
|
|
|
|
p_.currentY=new QValueAxis;
|
|
p_.currentY->setGridLineVisible(false);
|
|
p_.currentY->setRange(0, 10);
|
|
p_.currentY->setVisible(true);
|
|
p_.currentY->setTitleText("Curr(A)");
|
|
p_.currentY->setTitleVisible(true);
|
|
|
|
p_.voltageY=new QValueAxis;
|
|
p_.voltageY->setGridLineVisible(false);
|
|
p_.voltageY->setRange(-20, 20);
|
|
p_.voltageY->setVisible(false);
|
|
p_.voltageY->setTitleText("Volt(V)");
|
|
p_.voltageY->setTitleVisible(true);
|
|
|
|
p_.mainView->chart()->addAxis(p_.commonX, Qt::AlignBottom);
|
|
p_.mainView->chart()->addAxis(p_.temperatureY, Qt::AlignRight);
|
|
p_.mainView->chart()->addAxis(p_.currentY, Qt::AlignRight);
|
|
p_.mainView->chart()->addAxis(p_.voltageY, Qt::AlignRight);
|
|
|
|
}
|
|
|
|
p_.ch->setMargins(QMargins(2, 2, 2, 2));
|
|
|
|
ui->verticalLayout->addWidget(p_.mainView);
|
|
|
|
|
|
this->temperatureAxis=p_.temperatureY;
|
|
this->currentAxis=p_.currentY;
|
|
this->voltageAxis=p_.voltageY;
|
|
|
|
p_.t_id=this->startTimer(2000);
|
|
}
|
|
|
|
void XlruChartsForm::timerEvent(QTimerEvent * e)
|
|
{
|
|
if (e->timerId()==p_.t_id)
|
|
{
|
|
p_.timeNow=QDateTime::currentDateTime();
|
|
p_.timeNowMs=p_.timeNow.toMSecsSinceEpoch();
|
|
#if 0
|
|
int i=0;
|
|
foreach (QLineSeries* s, p_.s)
|
|
{
|
|
int r=qrand();
|
|
|
|
addTemperatureSample(i, (r*(float(300)/RAND_MAX)-150));
|
|
++i;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
for(unsigned int ii=6; ii<6+4; ++ii)
|
|
{
|
|
addSample(ii, 10*ii);
|
|
}
|
|
#endif
|
|
|
|
if ((p_.timeNowMs-p_.lastSampleMs)>1000*10) //Dead?
|
|
{
|
|
p_.tgtAlive=false;
|
|
}
|
|
else
|
|
{
|
|
p_.commonX->setMax(p_.timeNow);
|
|
Qt::CheckState cs=ui->cbAutoPurge->checkState();
|
|
switch(cs)
|
|
{
|
|
default:
|
|
case Qt::Unchecked:
|
|
break;
|
|
case Qt::PartiallyChecked:
|
|
{
|
|
qint64 delta=p_.timeNowMs-p_.timeStartMs;
|
|
if (delta>(1000*60*60))
|
|
{
|
|
p_.timeStart=p_.timeNow.addMSecs(-60*30);
|
|
p_.timeStartMs=p_.timeStart.toMSecsSinceEpoch();
|
|
p_.commonX->setMin(p_.timeStart);
|
|
}
|
|
}
|
|
break;
|
|
case Qt::Checked:
|
|
{
|
|
if (!p_.tgtAlive)
|
|
{
|
|
p_.timeStart=p_.timeNow;
|
|
p_.timeStartMs=p_.timeStart.toMSecsSinceEpoch();
|
|
p_.commonX->setMin(p_.timeStart);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
p_.tgtAlive=true;
|
|
if (p_.max_temp_restart)
|
|
{
|
|
ui->lblMaxTemp->setText(QString("%1").arg(p_.max_temp_period));
|
|
p_.max_temp_period=-1000;
|
|
|
|
}
|
|
p_.max_temp_restart=false;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
int XlruChartsForm::addSensor(const QString& name, QtCharts::QValueAxis* a, int id)
|
|
{
|
|
if (id==-1)
|
|
{
|
|
id=p_.s.size();
|
|
}
|
|
if (id>=p_.s.size())
|
|
{
|
|
p_.sensors.resize(id+1);
|
|
}
|
|
else
|
|
{
|
|
//Already exist!!
|
|
return -1;
|
|
}
|
|
|
|
QLineSeries* s=p_.newSeries(name, p_.ch, a);
|
|
Sensor sensor;
|
|
sensor.series=s;
|
|
sensor.view=p_.mainView;
|
|
sensor.name=name;
|
|
sensor.axis=a;
|
|
p_.sensors[id]=sensor;
|
|
return id;
|
|
}
|
|
|
|
int XlruChartsForm::addTemperatureSensor(const QString& name, int id)
|
|
{
|
|
return addSensor(name, p_.temperatureY, id);
|
|
}
|
|
|
|
int XlruChartsForm::addCurrentSensor(const QString& name, int id)
|
|
{
|
|
return addSensor(name, p_.currentY, id);
|
|
}
|
|
|
|
int XlruChartsForm::addVoltageSensor(const QString& name, int id)
|
|
{
|
|
return addSensor(name, p_.voltageY, id);
|
|
}
|
|
|
|
#if 0
|
|
void XlruChartsForm::addSampleAxis(int id, float value, QtCharts::QValueAxis *a)
|
|
{
|
|
if ((id>=0) && (id<p_.s.size()))
|
|
{
|
|
QLineSeries* s=p_.sensors[id].series;
|
|
if (s==0)
|
|
return;
|
|
|
|
s->append(p_.timeNowMs, value);
|
|
if (a->min()>value)
|
|
{
|
|
a->setMin(value);
|
|
}
|
|
else if (a->max()<value)
|
|
{
|
|
a->setMax(value);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void XlruChartsForm::addSample(int id, float value)
|
|
{
|
|
if (!ui->tbEnable->isChecked())
|
|
return;
|
|
|
|
if ((id>=0) && (id<p_.sensors.size()))
|
|
{
|
|
QLineSeries* s=p_.sensors[id].series;
|
|
if (s==0)
|
|
return;
|
|
|
|
p_.lastSampleMs=p_.timeNowMs;
|
|
|
|
s->append(p_.timeNowMs, value);
|
|
QtCharts::QValueAxis *a=p_.sensors[id].axis;
|
|
if (a!=0)
|
|
{
|
|
if (a->min()>value)
|
|
{
|
|
a->setMin(value);
|
|
}
|
|
else if (a->max()<value)
|
|
{
|
|
a->setMax(value);
|
|
}
|
|
if (a==temperatureAxis)
|
|
{
|
|
if (value>=ui->sbTempAlarm->value())
|
|
{
|
|
if (ui->cbAlarms->isChecked())
|
|
{
|
|
QApplication::beep();
|
|
p_.alertBox.setText(QString("%1 Overtemperature = %2").arg(p_.sensors[id].name).arg(value));
|
|
p_.alertBox.show();
|
|
p_.alertBox.raise();
|
|
}
|
|
}
|
|
if (value>p_.max_temperature)
|
|
p_.max_temperature=value;
|
|
if (value>p_.max_temp_period)
|
|
{
|
|
p_.max_temp_period=value;
|
|
p_.max_temp_restart=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void XlruChartsForm::addCurrentSample(int id, float value)
|
|
{
|
|
addSample(id, value); //, p_.currentY);
|
|
//p_.currentY->setVisible(true);
|
|
}
|
|
|
|
void XlruChartsForm::addTemperatureSample(int id, float value)
|
|
{
|
|
addSample(id, value); //, p_.temperatureY);
|
|
}
|
|
|
|
void XlruChartsForm::addVoltageSample(int id, float value)
|
|
{
|
|
addSample(id, value); //, p_.currentY);
|
|
}
|
|
|
|
XlruChartsForm::~XlruChartsForm()
|
|
{
|
|
delete &p_;
|
|
delete ui;
|
|
}
|
|
|
|
void XlruChartsForm::on_toolButton_clicked()
|
|
{
|
|
//Purge
|
|
auto ts=p_.timeNow.addSecs(-60*10);
|
|
p_.timeStart=ts;
|
|
p_.timeStartMs=ts.toMSecsSinceEpoch();
|
|
p_.commonX->setMin(ts);
|
|
}
|
|
|
|
void XlruChartsForm::on_tbClear_clicked()
|
|
{
|
|
foreach (QXYSeries* s, p_.s)
|
|
{
|
|
s->clear();
|
|
}
|
|
p_.commonX->setMin(p_.commonX->max());
|
|
|
|
}
|
|
|
|
void XlruChartsForm::connectMarkers()
|
|
{
|
|
// Connect all markers to handler
|
|
foreach (QLegendMarker* marker, p_.ch->legend()->markers())
|
|
{
|
|
// Disconnect possible existing connection to avoid multiple connections
|
|
QObject::disconnect(marker, SIGNAL(clicked()), this, SLOT(handleMarkerClicked()));
|
|
QObject::connect(marker, SIGNAL(clicked()), this, SLOT(handleMarkerClicked()));
|
|
}
|
|
}
|
|
|
|
void XlruChartsForm::disconnectMarkers()
|
|
{
|
|
foreach (QLegendMarker* marker, p_.ch->legend()->markers()) {
|
|
QObject::disconnect(marker, SIGNAL(clicked()), this, SLOT(handleMarkerClicked()));
|
|
}
|
|
}
|
|
|
|
void XlruChartsForm::handleMarkerClicked()
|
|
{
|
|
QLegendMarker* marker = qobject_cast<QLegendMarker*> (sender());
|
|
Q_ASSERT(marker);
|
|
|
|
switch (marker->type())
|
|
|
|
{
|
|
case QLegendMarker::LegendMarkerTypeXY:
|
|
{
|
|
|
|
// Toggle visibility of series
|
|
marker->series()->setVisible(!marker->series()->isVisible());
|
|
|
|
// Turn legend marker back to visible, since hiding series also hides the marker
|
|
// and we don't want it to happen now.
|
|
marker->setVisible(true);
|
|
|
|
// Dim the marker, if series is not visible
|
|
qreal alpha = 1.0;
|
|
|
|
if (!marker->series()->isVisible())
|
|
{
|
|
alpha = 0.5;
|
|
}
|
|
|
|
QColor color;
|
|
QBrush brush = marker->labelBrush();
|
|
color = brush.color();
|
|
color.setAlphaF(alpha);
|
|
brush.setColor(color);
|
|
marker->setLabelBrush(brush);
|
|
|
|
brush = marker->brush();
|
|
color = brush.color();
|
|
color.setAlphaF(alpha);
|
|
brush.setColor(color);
|
|
marker->setBrush(brush);
|
|
|
|
QPen pen = marker->pen();
|
|
color = pen.color();
|
|
color.setAlphaF(alpha);
|
|
pen.setColor(color);
|
|
marker->setPen(pen);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
qDebug() << "Unknown marker type";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define USE_CALLOUT 1
|
|
|
|
void XlruChartsForm::connectTooltip()
|
|
{
|
|
// Connect all series to handler
|
|
foreach (Sensor s, p_.sensors)
|
|
{
|
|
connect(s.series, &QLineSeries::clicked, this, &XlruChartsForm::keepCallout);
|
|
connect(s.series, &QLineSeries::hovered, this, &XlruChartsForm::tooltip);
|
|
}
|
|
}
|
|
|
|
|
|
void XlruChartsForm::keepCallout()
|
|
{
|
|
#if USE_CALLOUT==1
|
|
QAbstractSeries *series = qobject_cast<QAbstractSeries *>(sender());
|
|
m_callouts.append(m_tooltip);
|
|
|
|
m_tooltip = new Callout(p_.ch, series);
|
|
m_tooltip->hide();
|
|
#endif
|
|
}
|
|
|
|
void XlruChartsForm::tooltip(QPointF point, bool state)
|
|
{
|
|
#if USE_CALLOUT==0
|
|
if (!state)
|
|
return;
|
|
|
|
QAbstractSeries *series = qobject_cast<QAbstractSeries *>(sender());
|
|
|
|
QString msg=QString("%1=%2 ").arg(series->name()).arg(point.y());
|
|
|
|
QPointF anchor = series->chart()->mapToPosition(point, series);
|
|
|
|
QToolTip::showText(mapToGlobal(QPoint(anchor.x(), anchor.y())), msg, this);
|
|
|
|
#else
|
|
QAbstractSeries *series = qobject_cast<QAbstractSeries *>(sender());
|
|
if (m_tooltip == 0){
|
|
m_tooltip = new Callout(p_.ch, series);
|
|
}
|
|
if (state)
|
|
{
|
|
m_tooltip->setSeries(series);
|
|
|
|
//qreal yVal = p_.ch->mapToValue(point, series).y();
|
|
|
|
m_tooltip->setText(QString("%1\n Y: %2 ").arg(series->name()).arg(point.y()));
|
|
m_tooltip->setAnchor(point);
|
|
m_tooltip->setZValue(11);
|
|
m_tooltip->updateGeometry();
|
|
m_tooltip->show();
|
|
} else {
|
|
m_tooltip->hide();
|
|
}
|
|
#endif
|
|
}
|