/*
Original copyright 2018 Benjamin Vedder benjamin@vedder.se and the VESC Tool project ( https://github.com/vedderb/vesc_tool )
Forked to:
Copyright 2018 Danny Bokma github@diebie.nl (https://github.com/DieBieEngineering/DieBieMS-Tool)
Now forked to:
Copyright 2019 - 2020 Kevin Dionne kevin.dionne@ennoid.me (https://github.com/EnnoidMe/ENNOID-BMS-Tool)
This file is part of ENNOID-BMS Tool.
ENNOID-BMS Tool 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.
ENNOID-BMS Tool 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 .
*/
#include "pagertdata.h"
PageRtData::PageRtData(QWidget *parent) :
QWidget(parent),
ui(new Ui::PageRtData)
{
ui->setupUi(this);
// remove disabled widgets
for (;;)
{
int i;
for(i=0; i!=ui->tabWidget->count(); ++i)
if ( ! ui->tabWidget->widget(i)->isEnabled() )
break;
if ( i==ui->tabWidget->count() ) // nothing was found
break; // exit
ui->tabWidget->removeTab(i);
}
layout()->setContentsMargins(0, 0, 0, 0);
mDieBieMS = 0;
mTimer = new QTimer(this);
mTimer->start(20);
mSecondCounter = 0.0;
mLastUpdateTime = 0;
mUpdateValPlot = false;
ui->ivLCGraph->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
ui->cellGraph->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
ui->cellLimitsGraph->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
ui->tempGraph->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
// LC IVGraph
int graphIndex = 0;
ui->ivLCGraph->addGraph();
ui->ivLCGraph->graph(graphIndex)->setPen(QPen(Qt::red));
ui->ivLCGraph->graph(graphIndex)->setName("Pack Voltage");
graphIndex++;
ui->ivLCGraph->addGraph();
ui->ivLCGraph->graph(graphIndex)->setPen(QPen(Qt::darkGreen));
ui->ivLCGraph->graph(graphIndex)->setName("Load Voltage");
graphIndex++;
ui->ivLCGraph->addGraph(ui->ivLCGraph->xAxis, ui->ivLCGraph->yAxis2);
ui->ivLCGraph->graph(graphIndex)->setPen(QPen(Qt::green));
ui->ivLCGraph->graph(graphIndex)->setName("Current");
graphIndex++;
/* HC IVGraph
graphIndex = 0;
ui->ivHCGraph->addGraph();
ui->ivHCGraph->graph(graphIndex)->setPen(QPen(Qt::blue));
ui->ivHCGraph->graph(graphIndex)->setName("HC Voltage");
graphIndex++;
ui->ivHCGraph->addGraph(ui->ivHCGraph->xAxis, ui->ivHCGraph->yAxis2);
ui->ivHCGraph->graph(graphIndex)->setPen(QPen(Qt::magenta));
ui->ivHCGraph->graph(graphIndex)->setName("HC Current");
graphIndex++;
*/
// Cell voltage graph
#if 0
graphIndex = 0;
ui->cellGraph->addGraph();
ui->cellGraph->graph(graphIndex)->setPen(QPen(Qt::green));
ui->cellGraph->graph(graphIndex)->setName("Cell high");
graphIndex++;
ui->cellGraph->addGraph();
ui->cellGraph->graph(graphIndex)->setPen(QPen(Qt::blue));
ui->cellGraph->graph(graphIndex)->setName("Cell average");
graphIndex++;
ui->cellGraph->addGraph();
ui->cellGraph->graph(graphIndex)->setPen(QPen(Qt::red));
ui->cellGraph->graph(graphIndex)->setName("Cell low");
graphIndex++;
#endif
// For cellGraph use configuration similar to
// ui->cellBarGraph, i.e. hard coded 12 cells & 2.5-4.5volts
const enum Qt::GlobalColor colors[12]={
Qt::black, Qt::blue, Qt::red, Qt::green, Qt::cyan, Qt::magenta,
Qt::darkGray, Qt::darkBlue, Qt::darkRed, Qt::darkGreen, Qt::darkCyan, Qt::darkMagenta
};
for(int i=0; i!=12; ++i)
{
QCPGraph *const pg = ui->cellGraph->addGraph();
pg->setPen(QPen( colors[i] ));
pg->setName(QString(tr("Cell ")+QString::number(i)));
}
// Temperature graph
graphIndex = 0;
ui->tempGraph->addGraph();
ui->tempGraph->graph(graphIndex)->setPen(QPen(Qt::blue));
ui->tempGraph->graph(graphIndex)->setName("BMS high");
graphIndex++;
ui->tempGraph->addGraph();
ui->tempGraph->graph(graphIndex)->setPen(QPen(Qt::darkBlue));
ui->tempGraph->graph(graphIndex)->setName("BMS Average");
graphIndex++;
ui->tempGraph->addGraph();
ui->tempGraph->graph(graphIndex)->setPen(QPen(Qt::darkYellow));
ui->tempGraph->graph(graphIndex)->setName("BMS Low");
graphIndex++;
ui->tempGraph->addGraph();
ui->tempGraph->graph(graphIndex)->setPen(QPen(Qt::green));
ui->tempGraph->graph(graphIndex)->setName("Battery high");
graphIndex++;
ui->tempGraph->addGraph();
ui->tempGraph->graph(graphIndex)->setPen(QPen(Qt::darkGreen));
ui->tempGraph->graph(graphIndex)->setName("Battery Average");
graphIndex++;
ui->tempGraph->addGraph();
ui->tempGraph->graph(graphIndex)->setPen(QPen(Qt::darkRed));
ui->tempGraph->graph(graphIndex)->setName("Battery Low");
graphIndex++;
QFont legendFont = font();
legendFont.setPointSize(9);
//LC Graph
ui->ivLCGraph->legend->setVisible(true);
ui->ivLCGraph->legend->setFont(legendFont);
ui->ivLCGraph->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignRight|Qt::AlignBottom);
ui->ivLCGraph->legend->setBrush(QBrush(QColor(255,255,255,230)));
ui->ivLCGraph->xAxis->setLabel("Seconds (s)");
ui->ivLCGraph->yAxis->setLabel("Voltage (V)");
ui->ivLCGraph->yAxis2->setLabel("Current (A)");
ui->ivLCGraph->yAxis->setRange(0, 60);
ui->ivLCGraph->yAxis2->setRange(-5, 5);
ui->ivLCGraph->yAxis2->setVisible(true);
//Cell voltage Graph
ui->cellGraph->legend->setVisible(true);
ui->cellGraph->legend->setFont(legendFont);
ui->cellGraph->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignRight|Qt::AlignBottom);
ui->cellGraph->legend->setBrush(QBrush(QColor(255,255,255,230)));
ui->cellGraph->xAxis->setLabel("Seconds (s)");
ui->cellGraph->yAxis->setLabel("Voltage (V)");
ui->cellGraph->yAxis->setRange(0, 4.2);
//Temperature Graph
ui->tempGraph->legend->setVisible(true);
ui->tempGraph->legend->setFont(legendFont);
ui->tempGraph->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignRight|Qt::AlignBottom);
ui->tempGraph->legend->setBrush(QBrush(QColor(255,255,255,230)));
ui->tempGraph->xAxis->setLabel("Seconds (s)");
ui->tempGraph->yAxis->setLabel("Temperature (\u00B0C)");
ui->tempGraph->yAxis->setRange(0, 60);
// Cell bar graph
group = new QCPBarsGroup(ui->cellBarGraph);
barsNormal = new QCPBars(ui->cellBarGraph->xAxis, ui->cellBarGraph->yAxis);
barsBalance = new QCPBars(ui->cellBarGraph->xAxis, ui->cellBarGraph->yAxis);
barsNormal->setBrush(QColor(0, 255, 0, 50));
barsNormal->setPen(QColor(0, 211, 56));
barsNormal->setWidth(0.9);
barsNormal->setBarsGroup(group);
barsBalance->setBrush(QColor(0, 0, 255, 50));
barsBalance->setPen(QColor(0, 211, 56));
barsBalance->setWidth(0.9);
barsBalance->setBarsGroup(group);
barsBalance->moveAbove(barsNormal);
ui->cellBarGraph->xAxis->setRange(1, 12);
ui->cellBarGraph->yAxis->setRange(2.5, 4.15);
ui->cellBarGraph->yAxis->setLabel("Voltage (V)");
ui->cellBarGraph->xAxis->setTickLabelRotation(85);
ui->cellBarGraph->xAxis->setSubTicks(false);
ui->cellBarGraph->xAxis->setTickLength(0, 5);
// Aux bar graph
group2 = new QCPBarsGroup(ui->auxBarGraph);
barsTemperature = new QCPBars(ui->auxBarGraph->xAxis, ui->auxBarGraph->yAxis);
barsTemperature->setBrush(QColor(0, 255, 0, 50));
barsTemperature->setPen(QColor(0, 211, 56));
barsTemperature->setWidth(0.9);
barsTemperature->setBarsGroup(group2);
ui->auxBarGraph->xAxis->setRange(1, 9);
ui->auxBarGraph->yAxis->setRange(-40, 75);
ui->auxBarGraph->yAxis->setLabel("Temperature (°C)");
ui->auxBarGraph->xAxis->setTickLabelRotation(85);
ui->auxBarGraph->xAxis->setSubTicks(false);
ui->auxBarGraph->xAxis->setTickLength(0, 5);
// Expansion bar graph
group3 = new QCPBarsGroup(ui->expBarGraph);
ExpBarsTemperature = new QCPBars(ui->expBarGraph->xAxis, ui->expBarGraph->yAxis);
ExpBarsTemperature->setBrush(QColor(0, 255, 0, 50));
ExpBarsTemperature->setPen(QColor(0, 211, 56));
ExpBarsTemperature->setWidth(0.9);
ExpBarsTemperature->setBarsGroup(group3);
ui->expBarGraph->xAxis->setRange(1, 8);
ui->expBarGraph->yAxis->setRange(-40, 75);
ui->expBarGraph->yAxis->setLabel("Temperature (°C)");
ui->expBarGraph->xAxis->setTickLabelRotation(85);
ui->expBarGraph->xAxis->setSubTicks(false);
ui->expBarGraph->xAxis->setTickLength(0, 5);
// cell limits graph
{
auto *widget = ui->cellLimitsGraph;
widget->legend->setVisible(true);
widget->legend->setFont(legendFont);
widget->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignRight|Qt::AlignBottom);
widget->legend->setBrush(QBrush(QColor(255,255,255,230)));
widget->xAxis->setLabel("Seconds (s)");
widget->yAxis->setLabel("Voltage (V)");
widget->yAxis->setRange(0, 5.0);
widget->addGraph();
widget->graph(0)->setPen(QPen(Qt::green));
widget->graph(0)->setName("Cell high");
widget->addGraph();
widget->graph(1)->setPen(QPen(Qt::blue));
widget->graph(1)->setName("Cell average");
widget->addGraph();
widget->graph(2)->setPen(QPen(Qt::red));
widget->graph(2)->setName("Cell low");
}
connect(mTimer, SIGNAL(timeout()),this, SLOT(timerSlot()));
}
PageRtData::~PageRtData()
{
delete ui;
}
BMSInterface *PageRtData::bms() const
{
return mDieBieMS;
}
void PageRtData::setDieBieMS(BMSInterface *dieBieMS)
{
mDieBieMS = dieBieMS;
if (mDieBieMS) {
connect(mDieBieMS->commands(), SIGNAL(valuesReceived(BMS_VALUES)),this, SLOT(valuesReceived(BMS_VALUES)));
connect(mDieBieMS->commands(), SIGNAL(cellsReceived(int,QVector)),this, SLOT(cellsReceived(int,QVector)));
connect(mDieBieMS->commands(), SIGNAL(auxReceived(int,QVector)),this, SLOT(auxReceived(int,QVector)));
connect(mDieBieMS->commands(), SIGNAL(expTempReceived(int,QVector)),this, SLOT(expTempReceived(int,QVector)));
}
}
void PageRtData::timerSlot()
{
if (mUpdateValPlot) {
int dataSize = mPackVoltage.size();
QVector xAxis(dataSize);
for (int i = 0;i < mSeconds.size();i++) {
xAxis[i] = mSeconds[i];
}
// Current and duty-plot
int graphIndex = 0;
ui->ivLCGraph->graph(graphIndex++)->setData(xAxis, mPackVoltage);
ui->ivLCGraph->graph(graphIndex++)->setData(xAxis, mLCLoadVoltage);
ui->ivLCGraph->graph(graphIndex++)->setData(xAxis, mLCLoadCurrent);
#if 0
graphIndex = 0;
ui->cellGraph->graph(graphIndex++)->setData(xAxis, mCellVHigh);
ui->cellGraph->graph(graphIndex++)->setData(xAxis, mCellVAverage);
ui->cellGraph->graph(graphIndex++)->setData(xAxis, mCellVLow);
#endif
graphIndex = 0;
ui->cellLimitsGraph->graph(graphIndex++)->setData(xAxis, mCellVHigh);
ui->cellLimitsGraph->graph(graphIndex++)->setData(xAxis, mCellVAverage);
ui->cellLimitsGraph->graph(graphIndex++)->setData(xAxis, mCellVLow);
graphIndex = 0;
ui->tempGraph->graph(graphIndex++)->setData(xAxis, mTempBMSHigh);
ui->tempGraph->graph(graphIndex++)->setData(xAxis, mTempBMSAverage);
ui->tempGraph->graph(graphIndex++)->setData(xAxis, mTempBMSLow);
ui->tempGraph->graph(graphIndex++)->setData(xAxis, mTempBattHigh);
ui->tempGraph->graph(graphIndex++)->setData(xAxis, mTempBattAverage);
ui->tempGraph->graph(graphIndex++)->setData(xAxis, mTempBattLow);
if (ui->autoscaleButton->isChecked()) {
ui->ivLCGraph->rescaleAxes();
#if 0 // do not autoscale since we're displaying cell voltages
ui->cellGraph->rescaleAxes();
#endif
// only scale X axis
ui->cellGraph->xAxis->rescale();
ui->cellLimitsGraph->xAxis->rescale();
ui->tempGraph->rescaleAxes();
}
ui->ivLCGraph->replot();
ui->cellGraph->replot();
ui->cellLimitsGraph->replot();
ui->tempGraph->replot();
ui->cellBarGraph->replot();
ui->auxBarGraph->replot();
ui->expBarGraph->replot();
mUpdateValPlot = false;
}
}
void PageRtData::valuesReceived(BMS_VALUES values)
{
ui->rtText->setValues(values);
const int maxS = 500;
appendDoubleAndTrunc(&mPackVoltage, values.packVoltage, maxS);
appendDoubleAndTrunc(&mLCLoadVoltage, values.loadLCVoltage, maxS);
appendDoubleAndTrunc(&mLCLoadCurrent, values.loadLCCurrent, maxS);
appendDoubleAndTrunc(&mHCLoadVoltage, values.loadHCVoltage, maxS);
appendDoubleAndTrunc(&mHCLoadCurrent, values.loadHCCurrent, maxS);
appendDoubleAndTrunc(&mChargerVoltage, values.chargerVoltage, maxS);
appendDoubleAndTrunc(&mAuxVoltage, values.auxVoltage, maxS);
appendDoubleAndTrunc(&mAuxCurrent, values.auxCurrent, maxS);
appendDoubleAndTrunc(&mCellVHigh, values.cVHigh, maxS);
appendDoubleAndTrunc(&mCellVAverage, values.cVAverage, maxS);
appendDoubleAndTrunc(&mCellVLow, values.cVLow, maxS);
appendDoubleAndTrunc(&mTempBMSHigh, values.tempBMSHigh, maxS);
appendDoubleAndTrunc(&mTempBMSAverage, values.tempBMSAverage, maxS);
appendDoubleAndTrunc(&mTempBMSLow, values.tempBMSLow, maxS);
appendDoubleAndTrunc(&mTempBattHigh, values.tempBattHigh, maxS);
appendDoubleAndTrunc(&mTempBattAverage, values.tempBattAverage, maxS);
appendDoubleAndTrunc(&mTempBattLow, values.tempBattLow, maxS);
appendDoubleAndTrunc(&mHumidity, values.humidity, maxS);
qint64 tNow = QDateTime::currentMSecsSinceEpoch();
double elapsed = (double)(tNow - mLastUpdateTime) / 1000.0;
if (elapsed > 1.0) {
elapsed = 1.0;
}
mSecondCounter += elapsed;
appendDoubleAndTrunc(&mSeconds, mSecondCounter, maxS);
mLastUpdateTime = tNow;
mUpdateValPlot = true;
}
void PageRtData::cellsReceived(int cellCount, QVector cellVoltageArray){
QVector dataxNew;
dataxNew.clear();
QVector datayNormal;
datayNormal.clear();
QVector datayBalance;
datayBalance.clear();
QVector labels;
int indexPointer;
double cellHardUnder = mDieBieMS->bmsConfig()->getParamDouble("cellHardUnderVoltage");
double cellHardOver = mDieBieMS->bmsConfig()->getParamDouble("cellHardOverVoltage");
for(indexPointer = 0; indexPointer < cellCount; indexPointer++){
dataxNew.append(indexPointer + 1);
if(cellVoltageArray[indexPointer] < 0.0){
datayNormal.append(0.0);
datayBalance.append(fabs(cellVoltageArray[indexPointer]));
}else{
datayNormal.append(fabs(cellVoltageArray[indexPointer]));
datayBalance.append(0.0);
}
QString voltageString = QStringLiteral("%1V (C").arg(fabs(cellVoltageArray[indexPointer]), 0, 'f',3);
labels.append(voltageString + QString::number(indexPointer+1) + ")");
}
QSharedPointer textTicker(new QCPAxisTickerText);
textTicker->addTicks(dataxNew, labels);
int idx = 0;
double plotx = mSeconds.empty() ? 0 : mSeconds.last();
for(auto v : cellVoltageArray)
{
if ( auto *pg = ui->cellGraph->graph(idx) )
{
auto ploty = fabs(v);
pg->addData(plotx, ploty);
}
++idx;
}
ui->cellBarGraph->xAxis->setTicker(textTicker);
ui->cellBarGraph->xAxis->setRange(0.5, indexPointer + 0.5);
ui->cellBarGraph->yAxis->setRange(cellHardUnder, cellHardOver);
barsNormal->setData(dataxNew, datayNormal);
barsBalance->setData(dataxNew, datayBalance);
}
void PageRtData::auxReceived(int auxCount, QVector auxVoltageArray){
QVector dataxNew;
dataxNew.clear();
QVector datayNormal;
datayNormal.clear();
QVector labels;
int indexPointer;
for(indexPointer = 0; indexPointer < auxCount; indexPointer++){
dataxNew.append(indexPointer + 1);
if(auxVoltageArray[indexPointer] < -50.0){
datayNormal.append(0.0);
}else{
datayNormal.append(auxVoltageArray[indexPointer]);
}
QString voltageString = QStringLiteral("%1°C (TH").arg(auxVoltageArray[indexPointer], 0, 'f',3);
labels.append(voltageString + QString::number(indexPointer+1) + ")");
}
QSharedPointer textTicker(new QCPAxisTickerText);
textTicker->addTicks(dataxNew, labels);
ui->auxBarGraph->xAxis->setTicker(textTicker);
ui->auxBarGraph->xAxis->setRange(0.5, indexPointer + 0.5);
ui->auxBarGraph->yAxis->setRange(-40, 75);
barsTemperature->setData(dataxNew, datayNormal);
}
void PageRtData::expTempReceived(int expTempCount, QVector expTempVoltageArray){
QVector dataxNew;
dataxNew.clear();
QVector datayNormal;
datayNormal.clear();
QVector labels;
int indexPointer;
for(indexPointer = 0; indexPointer < expTempCount; indexPointer++){
dataxNew.append(indexPointer + 1);
if(expTempVoltageArray[indexPointer] < -50.0){
datayNormal.append(0.0);
}else{
datayNormal.append(expTempVoltageArray[indexPointer]);
}
QString voltageString = QStringLiteral("%1°C (T").arg(expTempVoltageArray[indexPointer], 0, 'f',3);
labels.append(voltageString + QString::number(indexPointer) + ")");
}
QSharedPointer textTicker(new QCPAxisTickerText);
textTicker->addTicks(dataxNew, labels);
ui->expBarGraph->xAxis->setTicker(textTicker);
ui->expBarGraph->xAxis->setRange(0.5, indexPointer + 0.5);
ui->expBarGraph->yAxis->setRange(-40, 75);
ExpBarsTemperature->setData(dataxNew, datayNormal);
}
void PageRtData::appendDoubleAndTrunc(QVector *vec, double num, int maxSize)
{
vec->append(num);
if(vec->size() > maxSize) {
vec->remove(0, vec->size() - maxSize);
}
}
void PageRtData::updateZoom()
{
Qt::Orientations plotOrientations = (Qt::Orientations)
((ui->zoomHButton->isChecked() ? Qt::Horizontal : 0) |
(ui->zoomVButton->isChecked() ? Qt::Vertical : 0));
ui->ivLCGraph->axisRect()->setRangeZoom(plotOrientations);
ui->cellGraph->axisRect()->setRangeZoom(plotOrientations);
ui->cellLimitsGraph->axisRect()->setRangeZoom(plotOrientations);
ui->tempGraph->axisRect()->setRangeZoom(plotOrientations);
}
void PageRtData::on_zoomHButton_toggled(bool checked)
{
(void)checked;
updateZoom();
}
void PageRtData::on_zoomVButton_toggled(bool checked)
{
(void)checked;
updateZoom();
}
void PageRtData::on_rescaleButton_clicked()
{
ui->ivLCGraph->rescaleAxes();
ui->cellGraph->rescaleAxes();
ui->cellLimitsGraph->rescaleAxes();
ui->tempGraph->rescaleAxes();
ui->ivLCGraph->replot();
ui->cellGraph->replot();
ui->cellLimitsGraph->replot();
ui->tempGraph->replot();
}
void PageRtData::on_tempShowBMSBox_toggled(bool checked)
{
ui->tempGraph->graph(0)->setVisible(checked);
ui->tempGraph->graph(1)->setVisible(checked);
}
void PageRtData::on_tempShowBatteryBox_toggled(bool checked)
{
ui->tempGraph->graph(2)->setVisible(checked);
ui->tempGraph->graph(3)->setVisible(checked);
}
/*
void PageRtData::on_csvChooseDirButton_clicked()
{
ui->csvFileEdit->setText(QFileDialog::getExistingDirectory(this,
"Choose CSV output directory"));
}
void PageRtData::on_csvEnableLogBox_clicked(bool checked)
{
if (checked) {
if (mDieBieMS) {
mDieBieMS->openRtLogFile(ui->csvFileEdit->text());
}
} else {
mDieBieMS->closeRtLogFile();
}
}
void PageRtData::on_csvHelpButton_clicked()
{
HelpDialog::showHelp(this, mDieBieMS->infoConfig(), "help_rt_logging");
}
*/