src/ChartiSpindel.cpp

Sat, 19 Aug 2023 16:58:03 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 19 Aug 2023 16:58:03 +0200
changeset 498
c6f957fa7442
parent 494
49ac23d25f61
child 501
a01ae5ff0e96
permissions
-rw-r--r--

In fermentation plot draw the SG line with temperature compensation.

/**
 * ChartiSpindel.cpp is part of bmsapp.
 *
 * bmsapp is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * bmsapp is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "ChartiSpindel.h"
#include "callout.h"
#include "MainWindow.h"
#include "Utils.h"


ChartiSpindel::ChartiSpindel(QString code, QString name, QWidget *parent) : QDialog(parent)
{
    QSqlQuery query;
    double timestamp;
    double sg_min = 1.2, sg_max = 0.8, mg, sg;
    double temp_min = 100, temp_max = 0, temp;
    double batt_min = 4.5, batt_max = 3.0, batt;

    qDebug() << "ChartiSpindel:" << code << name;

    QDialog* dialog = new QDialog(parent);
    dialog->setWindowTitle(tr("BMSapp - iSpindel ") + "\"" + name + "\"");
    dialog->setObjectName(QString::fromUtf8("ChartiSpindel"));
    dialog->setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
    dialog->resize(1024, 600);

    QPushButton *saveButton = new QPushButton(tr("Save"));
    saveButton->setAutoDefault(false);
    QIcon icon1;
    icon1.addFile(QString::fromUtf8(":icons/silk/disk.png"), QSize(), QIcon::Normal, QIcon::Off);
    saveButton->setIcon(icon1);

    QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
    buttonBox->setOrientation(Qt::Vertical);
    buttonBox->setStandardButtons(QDialogButtonBox::Ok);
    buttonBox->addButton(saveButton,QDialogButtonBox::ActionRole);

    temperature = new QLineSeries();
    density = new QLineSeries();
    battery = new QLineSeries();

    query.prepare("SELECT * FROM log_ispindel WHERE code=:code ORDER BY datetime");
    query.bindValue(":code", code);
    query.exec();
    while (query.next()) {
        timestamp = query.value("datetime").toDateTime().toSecsSinceEpoch() * 1000;

	mg = query.value("sg").toDouble();
	temp = query.value("temperature").toDouble();
	if (ceil(temp) > temp_max)
	    temp_max = ceil(temp);
	if (floor(temp) < temp_min)
	    temp_min = floor(temp);

	/*
	 * Correct the SG plotline for temperature.
	 */
	sg = Utils::HydroCorrection(mg, temp, 20.0);
	if ((ceil(sg * 100) / 100) > sg_max)
            sg_max = ceil(sg * 100) / 100;
        if ((floor(sg * 100) / 100) < sg_min)
            sg_min = floor(sg * 100) / 100;

	batt = round(query.value("battery").toDouble() * 500) / 500;
	if ((ceil(batt * 10) / 10) > batt_max)
	    batt_max = ceil(batt * 10) / 10;
	if ((floor(batt * 10) / 10) < batt_min)
	    batt_min = floor(batt * 10) / 10;

        temperature->append(timestamp, temp);
        density->append(timestamp, sg);
        battery ->append(timestamp, batt);
    }

    temperature->setName(tr("Temperature"));
    temperature->setColor(QColorConstants::Svg::red);
    density->setName(tr("SG"));
    QPen pen(QColorConstants::Svg::navy);
    pen.setWidth(2);
    density->setPen(pen);
    battery->setName(tr("Battery"));
    battery->setColor(QColorConstants::Svg::limegreen);

    chart = new QChart();
    chart->setTitle(QString("%1 \"%2\"").arg(code).arg(name));
    chart->addSeries(battery);
    chart->addSeries(temperature);
    chart->addSeries(density);

    QDateTimeAxis *axisX = new QDateTimeAxis;
    axisX->setTickCount(10);
    axisX->setFormat("dd MMM");
    axisX->setTitleText(tr("Date"));
    axisX->setLabelsFont(QFont("Helvetica", 8, QFont::Normal));
    chart->addAxis(axisX, Qt::AlignBottom);
    battery->attachAxis(axisX);
    temperature->attachAxis(axisX);
    density->attachAxis(axisX);

    QValueAxis *axisYT = new QValueAxis;
    axisYT->setRange(temp_min, temp_max);
    axisYT->setTickCount(10);
    axisYT->setLabelFormat("%.1f");
    axisYT->setTitleText(tr("Temperature °C"));
    axisYT->setLabelsFont(QFont("Helvetica", 8, QFont::Normal));
    chart->addAxis(axisYT, Qt::AlignRight);
    temperature->attachAxis(axisYT);

    QValueAxis *axisYD = new QValueAxis;
    axisYD->setRange(sg_min, sg_max);
    axisYD->setTickCount(10);
    axisYD->setLabelFormat("%.3f");
    axisYD->setTitleText("SG");
    axisYD->setLabelsFont(QFont("Helvetica", 8, QFont::Normal));
    chart->addAxis(axisYD, Qt::AlignLeft);
    density->attachAxis(axisYD);

    QValueAxis *axisYB = new QValueAxis;
    axisYB->setRange(batt_min, batt_max);
    axisYB->setTickCount(10);
    axisYB->setLabelFormat("%.2f");
    axisYB->setTitleText(tr("Battery volt"));
    axisYB->setLabelsFont(QFont("Helvetica", 8, QFont::Normal));
    chart->addAxis(axisYB, Qt::AlignRight);
    battery->attachAxis(axisYB);

    connect(temperature, &QLineSeries::hovered, this, &ChartiSpindel::tooltip);
    connect(density, &QLineSeries::hovered, this, &ChartiSpindel::tooltip);
    connect(battery, &QLineSeries::hovered, this, &ChartiSpindel::tooltip);

    chartView = new QChartView(chart);
    chartView->setRenderHint(QPainter::Antialiasing);
    dialog->setLayout(new QHBoxLayout);
    dialog->layout()->addWidget(chartView);
    dialog->layout()->addWidget(buttonBox);

    QObject::connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
    QObject::connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
    QObject::connect(saveButton, SIGNAL(clicked()), this, SLOT(savePNG()));

    dialog->setModal(true);
    dialog->exec();
}


ChartiSpindel::~ChartiSpindel() {}


void ChartiSpindel::savePNG()
{
    QSettings settings(QSettings::IniFormat, QSettings::UserScope, "mbse", "bmsapp");
    QString dirName;

    /*
     * First check if the directory stored in the settings file exists.
     * It might be on a removable media that was last used ...
     * If so, fallback to the user's home directory.
     */
    dirName = settings.value("paths/download").toString();
    if (! QDir(dirName).exists()) {
        dirName = QDir::homePath();
    }

    QString path = QFileDialog::getSaveFileName(this, tr("Save Image"), dirName + "/ispindel.png", tr("Image (*.png)"));
    if (path.isEmpty()) {
	QMessageBox::warning(this, tr("Save File"), tr("No image file selected."));
	return;
    }

    /*
     * Update to current selected path
     */
    settings.setValue("paths/download", QFileInfo(path).absolutePath());

    QImage img((chartView->size()), QImage::Format_ARGB32);
    QPainter painter;
    painter.begin(&img);
    chartView->render(&painter);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.end();
    img.save(path);
}


void ChartiSpindel::tooltip(QPointF point, bool state)
{
    QAbstractSeries *series = qobject_cast<QAbstractSeries *>(sender());

    if (t_tooltip == 0)
	t_tooltip = new Callout(chart, series);

    if (state) {
	QDateTime timeis = QDateTime::fromMSecsSinceEpoch(point.x());
	t_tooltip->setSeries(series);
	if (series == temperature)
	    t_tooltip->setText(QString(tr("%1\nTemperature %2°C")).arg(timeis.toString("dd-MM-yyyy hh:mm")).arg(point.y(), 2, 'f', 1));
	else if (series == density)
	    t_tooltip->setText(QString(tr("%1\nDensity %2 SG")).arg(timeis.toString("dd-MM-yyyy hh:mm")).arg(point.y(), 5, 'f', 4));
	else if (series == battery) {
	    double batt = point.y() - 3.064;	/* 0% */
	    if (batt < 0)
		batt = 0;
	    batt = round(batt / 1.17875 * 1000.0) / 10;	/* 100% range */
	    if (batt > 100)
		batt = 100;
	    t_tooltip->setText(QString(tr("%1\nBattery %2 volt\nCapacity %3%")).arg(timeis.toString("dd-MM-yyyy hh:mm"))
			    .arg(point.y(), 3, 'f', 2).arg(batt, 2, 'f', 1, '0'));
	}
	t_tooltip->setAnchor(point);
	t_tooltip->setZValue(11);
	t_tooltip->updateGeometry();
	t_tooltip->show();
    } else {
	t_tooltip->hide();
    }
}

mercurial