src/CalibrateiSpindel.cpp

Sat, 14 Oct 2023 16:10:14 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 14 Oct 2023 16:10:14 +0200
changeset 506
ea07f6c97a69
parent 505
7ae4d022cf8f
child 507
fa07b6c6238a
permissions
-rw-r--r--

Added Simple polynomial fitting functions written by Henry M. Forson. Added a graph that displays the old and new iSpindel calibration curve. Implemented delete row from the data.

/**
 * CalibrateiSpindel.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 "CalibrateiSpindel.h"
#include "../ui/ui_CalibrateiSpindel.h"
#include "global.h"
#include "Utils.h"
#include "polyfit.h"
#include "MainWindow.h"



CalibrateiSpindel::CalibrateiSpindel(int id, QWidget *parent) : QDialog(parent), ui(new Ui::CalibrateiSpindel)
{
    QSqlQuery query;

#ifdef DEBUG_MONITOR
    qDebug() << "CalibrateiSpindel record:" << id;
#endif

    ui->setupUi(this);
    this->recno = id;
    setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint);
    WindowTitle();

    query.prepare("SELECT node,alias,calibrate FROM mon_ispindels WHERE record = :recno");
    query.bindValue(":recno", this->recno);
    query.exec();
    if (query.next()) {

	_node = query.value("node").toString();
	_alias = query.value("alias").toString();
	ui->nameEdit->setText(_node+"/"+_alias);

	QJsonParseError parseError;
        const auto& json = query.value("calibrate").toString();

	if (!json.trimmed().isEmpty()) {
	    const auto& formattedJson = QString("%1").arg(json);
	    QJsonDocument jsonResponse = QJsonDocument::fromJson(formattedJson.toUtf8(),  &parseError);
	    if (parseError.error != QJsonParseError::NoError) {
                qWarning() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ;
            } else {

	    	QJsonObject jsonObj = jsonResponse.object();
		QJsonArray polyData = jsonObj.value("polyData").toArray();
		for (int i = 0; i < polyData.size(); i++) {
		    Old[i] = New[i] = polyData.at(i).toDouble();
		    qDebug() << i << New[i];
		}
		_data_old = QString("(%1 * x^3) + (%2 * x^2) + (%3 * x) + %4").arg(Old[0], 0, 'f', 9, '0').arg(Old[1], 0, 'f', 9, '0').arg(Old[2], 0, 'f', 9, '0').arg(Old[3], 0, 'f', 9, '0');
		ui->oldEdit->setText(_data_old);

	    	qDebug() << "calData:  " << jsonObj["calData"].toArray();
		QJsonArray calData = jsonObj.value("calData").toArray();
		qDebug() << calData;
		oldtotal = 0;
		for (int i = 0; i < calData.size(); i++) {
		    QJsonObject calObj = calData.at(i).toObject();
		    oCal[i].plato = nCal[i].plato = calObj["plato"].toDouble();
		    oCal[i].angle = nCal[i].angle = calObj["angle"].toDouble();
		    oCal[i].sg = nCal[i].sg = Utils::plato_to_sg(oCal[i].plato);
		    oldtotal++;
		}
		newtotal = oldtotal;
	    }
	}

    }

    connect(ui->dataTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_Changed(int, int)));
//    connect(parent, SIGNAL(updateiSpindel(QString)), this, SLOT(refreshiSpindel(QString)));
    emit refreshTable();
}


void CalibrateiSpindel::refreshTable()
{
    QString w;
    QWidget* pWidget;
    QHBoxLayout* pLayout;
    double  d, x[12], y[12];

    qDebug() << "refreshTable" << oldtotal << newtotal;

    /*
     * During filling the table turn off the cellChanged signal because every cell that is filled
     * triggers the cellChanged signal. The QTableWidget has no better signal to use.
     */
    this->ignoreChanges = true;

    const QStringList labels({tr("SG"), tr("°Plato"), tr("Angle"), tr("Del")});
    ui->dataTable->setColumnCount(4);
    ui->dataTable->setColumnWidth(0, 100);	/* SG		*/
    ui->dataTable->setColumnWidth(1, 100);	/* °Plato	*/
    ui->dataTable->setColumnWidth(2, 100);	/* Tilt angle	*/
    ui->dataTable->setColumnWidth(3,  55);	/* Del button	*/
    ui->dataTable->setHorizontalHeaderLabels(labels);
    ui->dataTable->verticalHeader()->hide();
    ui->dataTable->setRowCount(newtotal);

    for (int i = 0; i < 12; i++) {
	x[i] = y[i] = 0;
    }

    for (int i = 0; i < newtotal; i++) {
	qDebug() << i << nCal[i].sg << nCal[i].plato << nCal[i].angle;

	y[i] = nCal[i].plato;
	x[i] = nCal[i].angle;

	w = QString("%1").arg(nCal[i].sg, 1, 'f', 4, '0');
	QTableWidgetItem *item = new QTableWidgetItem(w);
	item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
	ui->dataTable->setItem(i, 0, item);

	w = QString("%1").arg(nCal[i].plato, 1, 'f', 3, '0');
        item = new QTableWidgetItem(w);
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->dataTable->setItem(i, 1, item);

	w = QString("%1").arg(nCal[i].angle, 1, 'f', 5, '0');
        item = new QTableWidgetItem(w);
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->dataTable->setItem(i, 2, item);

	/* Add the Delete row button */
        pWidget = new QWidget();
        QPushButton* btn_del = new QPushButton();
        btn_del->setObjectName(QString("%1").arg(i));  /* Send row with the button */
        btn_del->setText(tr("Del"));
        connect(btn_del, SIGNAL(clicked()), this, SLOT(on_deleteRow_clicked()));
        pLayout = new QHBoxLayout(pWidget);
        pLayout->addWidget(btn_del);
        pLayout->setContentsMargins(5, 0, 5, 0);
        pWidget->setLayout(pLayout);
        ui->dataTable->setCellWidget(i, 3, pWidget);
    }
    int rc = Polyfit::polyfit(newtotal, x, y, 4, New);
    qDebug() << "poly:" << rc << New[0] << New[1] << New[2] << New[3];

    _data_new = QString("(%1 * x^3) + (%2 * x^2) + (%3 * x) + %4").arg(New[0], 0, 'f', 9, '0').arg(New[1], 0, 'f', 9, '0').arg(New[2], 0, 'f', 9, '0').arg(New[3], 0, 'f', 9, '0');
    ui->newEdit->setText(_data_new);

    /*
     * Check the new formula against the old formula.
     */
    this->textIsChanged = (_data_old.compare(_data_new) == 0) ? false:true;
    qDebug() << "changed" << this->textIsChanged << _data_old.compare(_data_new);
    CalibrateiSpindel::WindowTitle();

    new_plot = new QLineSeries();
    old_plot = new QLineSeries();

    for (int i = 0; i < oldtotal; i++) {
	old_plot->append(oCal[i].angle, oCal[i].plato);
    }
    for (int i = 0; i < newtotal; i++) {
	new_plot->append(nCal[i].angle, nCal[i].plato);
    }

    old_plot->setName(tr("Old"));
    new_plot->setName(tr("New"));

    chart = new QChart();
    chart->setTitle(tr("Calibration plot"));
    chart->addSeries(old_plot);
    chart->addSeries(new_plot);

    QValueAxis *axisX = new QValueAxis;
    axisX->setRange(10, 80);
    axisX->setTickCount(8);
    axisX->setLabelFormat("%.0f");
    axisX->setTitleText(tr("Angle"));
    axisX->setLabelsFont(QFont("Helvetica", 8, QFont::Normal));
    chart->addAxis(axisX, Qt::AlignBottom);
    old_plot->attachAxis(axisX);
    new_plot->attachAxis(axisX);

    QValueAxis *axisY = new QValueAxis;
    axisY->setRange(0, 20);
    axisY->setTickCount(11);
    axisY->setLabelFormat("%.1f");
    axisY->setTitleText("Plato");
    axisY->setLabelsFont(QFont("Helvetica", 8, QFont::Normal));
    chart->addAxis(axisY, Qt::AlignLeft);
    old_plot->attachAxis(axisY);
    new_plot->attachAxis(axisY);

    ui->chartView->setRenderHint(QPainter::Antialiasing);
    ui->chartView->setChart(chart);

    this->ignoreChanges = false;
}


CalibrateiSpindel::~CalibrateiSpindel()
{
    delete ui;
}


void CalibrateiSpindel::on_quitButton_clicked()
{
    this->close();
    this->setResult(1);
}


void CalibrateiSpindel::on_saveButton_clicked()
{
}


void CalibrateiSpindel::on_deleteRow_clicked()
{
    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    int row = pb->objectName().toInt();
    qDebug() << "Delete row" << row << newtotal;

    if (newtotal < 4) {
	QMessageBox::warning(this, tr("iSpindel calibrate"), tr("You cannot delete too many rows."));
	return;
    }

    if (row == (newtotal - 1)) {
	qDebug() << "Delete last row";
	newtotal--;
    } else {
	newtotal--;
    	for (int i = row; i < newtotal; i++) {
	    nCal[i].sg = nCal[i+1].sg;
	    nCal[i].plato = nCal[i+1].plato;
	    nCal[i].angle = nCal[i+1].angle;
	    qDebug() << i << " < " << i+1;
    	}
    }

    emit refreshTable();
}


void CalibrateiSpindel::on_addButton_clicked()
{
    qDebug() << "Add row" << newtotal;
}


void CalibrateiSpindel::cell_Changed(int nRow, int nCol)
{
    QString w;

    if (this->ignoreChanges)
        return;

    qDebug() << "Cell at row " + QString::number(nRow) + " column " + QString::number(nCol) + " was changed.";

}


/*
 * Window header, mark any change with '**'
 */
void CalibrateiSpindel::WindowTitle()
{
    QString txt;

    txt = QString(tr("BMSapp - Calibrate iSpindel %1").arg(this->recno));

    if (this->textIsChanged) {
        txt.append((QString(" **")));
    }
    setWindowTitle(txt);
}

mercurial