SXXXXXXX_PyBusMonitor1553/cpp/GrifoScope/GrifoXLruMonitor/xlruchartsform.cpp
2025-12-17 07:59:30 +01:00

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
}