Absturz nach mehreren Stunden mit Qwt!

Alles rund um die Programmierung mit Qt
Antworten
zx7r
Beiträge: 13
Registriert: 26. Mai 2010 20:16
Wohnort: Berlin

Absturz nach mehreren Stunden mit Qwt!

Beitrag von zx7r »

Hallo an alle!
Ich habe echt ein mieses Problem und hoffe, dass ihr mir helfen könntet... aaaalso;-)
Ich habe in mein aktuelles Qt-Projekts ein QwtPlot mit QwtPlotCurve eingebunden (Beide Klassen natürlich überschrieben). Ich steuer mit meinem Programm Geräte an, die Messdaten zurückgeben. Es sind drei verschiedene Messwerte pro Gerät. 8 Geräte sind angeschlossen. Pro Messwert wird in einer PlotCurve ein Datensatz aus Messwert und Zeitstempel angefügt. Also es kommen schon einige Datensätze zusammen. Nun habe ich ein Problem, dass mein Programm immer nach 6 bis 7 Stunden abschmiert! Es kommt das übliche Visual-C++ Fenster mit dem Runtime-Error. Anschließend schaue ich in den Debugteil im Creator und es ist folgendes zu sehen:

Code: Alles auswählen

Warning: QPainter::end: Painter ended with 3 saved states
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Gegooglet habe ich schon alle möglichen ähnlichen Fehler. Allerdings bin ich nicht wirklich fündig geworden. Den Code werde ich einmal posten:

Measurementdata.h

Code: Alles auswählen

#ifndef MEASUREMENTDATA_H
#define MEASUREMENTDATA_H

#include "eurotherm.h"
#include "eurothermexception.h"
#include "myqwtplotcurve.h"
#include <qwt_plot.h>
#include <qwt_legend.h>
#include <QList>
#include <QTime>
#include <QDateTime>
#include <QFile>
#include <QDir>

#define DELIMITER QChar((int) 9)

class MeasurementData : public QwtPlot
{
public:
    MeasurementData(QWidget *parent);

    void    init();                                                 // initialisiert den Plot (Kurven, Legende, ...)
    void    setEurothermList(QList<Eurotherm*> tEuroList);          // übergibt die Liste aller Eurotherms

    void    startMeasurement();                                     // startet die Messung
    QString measure();                                              // fragt die Messwerte ab und stellt sie dar
    void    stopMeasurement();                                      // stoppt die Messung

    void    initTempFile();                                         // temporär gespeicherte Datei initialisieren
    void    saveTempData();                                         // speichert Daten in TempDatei
    bool    saveMeasurement(QString &fileName);                     // speichert alle Messwerte als CSV-Datei

    void    hideCurve(QwtPlotItem *tPlotItem);                      // blendet eine Kurve aus
    void    showCurve(QwtPlotItem *tPlotItem);                      // blendet eine Kurve ein
    void    hideAout(bool);                                         // blendet die Kurve Aout aus (true) bzw ein (false)
    void    hideWsp(bool);                                          // blendet die Kurve Wsp aus (true) bzw ein (false)
    void    hidePv(bool);                                           // blendet die Kurve Pv aus (true) bzw ein (false)

private:
    QList<Eurotherm*>       euroList;
    QList<MyQwtPlotCurve*>  wspList;
    QList<MyQwtPlotCurve*>  aoutList;
    QList<MyQwtPlotCurve*>  pvList;
    QTime                   measurementTime;
    QList<QPen>             penList;                                // Liste mit verschiedenen Farben
    int                     latestSample;                           // merkt sich die Anzahl der gespeicherten Datensätze
    QFile                   *tempFile;
    int                     savesCount;                             // merkt sich die Anzahl der Speichervorgänge
    bool                    isRunning;                              // true, wenn Messung gestartet wurde

};

#endif // MEASUREMENTDATA_H
MeasurementData.cpp

Code: Alles auswählen

#include "measurementdata.h"
#include <QLabel>

MeasurementData::MeasurementData(QWidget *parent) :
        euroList(),
        wspList(),
        aoutList(),
        pvList(),
        measurementTime(0,0,0),
        penList(),
        latestSample(0),
        tempFile(new QFile()),
        savesCount(1),
        isRunning(false)
{
    penList.append(QPen(Qt::red, 3));
    penList.append(QPen(Qt::red, 3, Qt::DotLine));
    penList.append(QPen(Qt::blue, 3));
    penList.append(QPen(Qt::blue, 3, Qt::DotLine));
    penList.append(QPen(Qt::yellow, 3));
    penList.append(QPen(Qt::yellow, 3, Qt::DotLine));
    penList.append(QPen(Qt::black, 3));
    penList.append(QPen(Qt::black, 3, Qt::DotLine));
    penList.append(QPen(Qt::green, 3));
    penList.append(QPen(Qt::green, 3, Qt::DotLine));
    penList.append(QPen(Qt::magenta, 3));
    penList.append(QPen(Qt::magenta, 3, Qt::DotLine));
    penList.append(QPen(Qt::white, 3));
    penList.append(QPen(Qt::white, 3, Qt::DotLine));
    penList.append(QPen(Qt::darkGray, 3));
    penList.append(QPen(Qt::darkGray, 3, Qt::DotLine));
}

// initialisiert den this (Kurven, Legende, ...)
void MeasurementData::init()
{
    wspList.clear();
    aoutList.clear();
    pvList.clear();
    this->clear();

    this->initTempFile();
    this->setTitle("Messung");
    this->insertLegend(new QwtLegend());
    this->legend()->setItemMode(QwtLegend::CheckableItem);
    this->setAxisTitle(xBottom, "t in sec");
    this->setAxisTitle(yLeft, "WSP");
    QString name;
    // für jeden Eurotherm eine Kurve erstellen
    for(int i=0; i<euroList.size(); i++)
    {
        name.clear();
        name.append(euroList.at(i)->getAssay());
        name.append("-");
        name.append(euroList.at(i)->getType());
        // Kurve erstellt und zur Liste hinzugefügt
        wspList.append(new MyQwtPlotCurve(name));
        aoutList.append(new MyQwtPlotCurve(name));
        pvList.append(new MyQwtPlotCurve(name));
        wspList.at(i)->attach(this);
        wspList.at(i)->setPen(penList.at(i));
        aoutList.at(i)->setPen(penList.at(i));
        pvList.at(i)->setPen(penList.at(i));
    }
    this->replot();
}

// fragt die Messwerte ab und stellt sie dar
QString MeasurementData::measure()
{
    QString err = "";
    // 4Test start
//    for(int i=0; i<euroList.size(); i++)
//    {
//        pvList.at(i)->deleteAll();
//        aoutList.at(i)->deleteAll();
//        wspList.at(i)->deleteAll();
//    }
//    qDebug() << QTime::currentTime().toString("hh:mm::ss");
    // Test ende

    // falls immer mehr als eine Minute verstrichen ist,
    // speichere Daten zwischen
    if(this->measurementTime.elapsed() >= (60*1000*savesCount))
        this->saveTempData();

    float   value = -1;
    for(int i=0; i< euroList.size(); i++)
    {
        try{
            value = euroList.at(i)->getWSP();
            wspList.at(i)->addSample((double) measurementTime.elapsed()/1000, (double) value);
        } catch(EurothermException e)
        {
            // Im Fall eines Fehlers wird eine -1 gespeichert
            wspList.at(i)->addSample((double) measurementTime.elapsed()/1000, (double) -1);
            err = e.getErrorString();
            err.append("\n");
        }
        try{
            value = euroList.at(i)->getAout();
            aoutList.at(i)->addSample((double) measurementTime.elapsed()/1000, (double) value);
        } catch(EurothermException e)
        {
            // Im Fall eines Fehlers wird eine -1 gespeichert
            aoutList.at(i)->addSample((double) measurementTime.elapsed()/1000, (double) -1);
            err.append(e.getErrorString());
            err.append("\n");
        }
        try{
            value = euroList.at(i)->getPV();
            pvList.at(i)->addSample((double) measurementTime.elapsed()/1000, (double) value);
        } catch(EurothermException e)
        {
            // Im Fall eines Fehlers wird eine -1 gespeichert
            pvList.at(i)->addSample((double) measurementTime.elapsed()/1000, (double) -1);
            err.append(e.getErrorString());
            err.append("\n");
        }
    }
    this->replot();
    return err;
}

// übergibt die Liste aller Eurotherms
void MeasurementData::setEurothermList(QList<Eurotherm*> tEuroList)
{
    this->euroList.clear();
    this->euroList = tEuroList;
    this->init();
}

// startet die Messung
void MeasurementData::startMeasurement()
{
    if(!isRunning)
    {
        this->measurementTime.restart();
        isRunning = true;
    }
}

// stoppt die Messung
void MeasurementData::stopMeasurement()
{
    this->isRunning = false;
}

// temporär gespeicherte Datei initialisieren
void MeasurementData::initTempFile()
{
    delete tempFile;
    QDir dir;
    // Falls der Ordner "Messwerte" nicht existiert, soll er erstellt werden
    if(!dir.exists("Messwerte"))
    {
        dir.mkdir("Messwerte");
    }

    QString     row, tString;
    QByteArray  tArray;

    tempFile = new QFile("Messwerte/" + QDateTime::currentDateTime().toString("yyyy_MM_dd_hh_mm_ss")+".csv~");
    tempFile->open(QFile::WriteOnly | QFile::Text);

    // Header zusammenbauen
    row.append(tr("########## start Header ##########") + tr("\n") +
               tr("# In-situ PVD Templog v1") + tr("\n") +
               tr("# Date") + DELIMITER + QDate::currentDate().toString("yyyy/MM/dd") + tr("\n") +
               tr("# Time") + DELIMITER + QTime::currentTime().toString("hh:mm:ss.ms") + tr("\n") +
               tr("##########  end Header  ##########") + tr("\n\n"));

    row.append(tr("Uhrzeit") +
               DELIMITER +
               tr("Zeit in sec") +
               DELIMITER);

    // erste Zeile zusammenbauen
    for(int i=0;i<euroList.size();i++)
    {
        // Zeit;CU-HL;CU-EC; ...
        tString.append(DELIMITER +
                       euroList.at(i)->getAssay() +
                       tr("-") +
                       euroList.at(i)->getType() +
                       DELIMITER +
                       tr(" ") +
                       DELIMITER);
        row.append(tString);
        tString.clear();
    }

    // Zeilenumbruch
    row.append(tr("\n")+
               DELIMITER);

    for(int i=0;i<euroList.size();i++)
        row.append(DELIMITER +
                   tr("WSP") +
                   DELIMITER +
                   tr("Aout") +
                   DELIMITER +
                   tr("PV"));

    // Zeilenumbruch
    row.append("\n");

    tArray.append(row);
    tempFile->write(tArray.data());

    tempFile->close();
}

// speichert Daten in TempDatei
void MeasurementData::saveTempData()
{
    QString row;
    QByteArray tArray;
    this->savesCount++;

    tempFile->open(QFile::Append | QFile::Text);

    for(;latestSample<wspList.at(0)->getSampleCount();latestSample++)
    {
        // Uhrzeit
        row.append(wspList.at(0)->getTime(latestSample) +
                   DELIMITER);
        // Zeit
        row.append(QString::number(wspList.at(0)->x(latestSample)) +
                   DELIMITER);
        for(int i=0;i<wspList.size();i++)
        {
            // Messwerte einer Zeile
            row.append(QString::number(wspList.at(i)->y(latestSample))+
                       DELIMITER);
            // Messwerte einer Zeile
            row.append(QString::number(aoutList.at(i)->y(latestSample)) +
                       DELIMITER);
            // Messwerte einer Zeile
            row.append(QString::number(pvList.at(i)->y(latestSample)) +
                       DELIMITER);
        }
        // Zeilenumbruch
        row.append("\n");
        tArray.append(row);
        tempFile->write(tArray.data());

        tArray.clear();
        row.clear();
    }

    tempFile->close();
}

// speichert alle Messwerte als CSV-Datei
bool MeasurementData::saveMeasurement(QString &fileName)
{
    this->saveTempData();

    // Überschreiben, falls Datei schon vorhanden
    if(QFile::exists(fileName))
        QFile::remove(fileName);

    return this->tempFile->copy(fileName);
}

// blendet eine Kurve aus
void MeasurementData::hideCurve(QwtPlotItem *tPlotItem)
{
    tPlotItem->hide();
}

// blendet eine Kurve ein
void MeasurementData::showCurve(QwtPlotItem *tPlotItem)
{
    tPlotItem->show();
}

// blendet die Kurve Aout aus (true) bzw ein (false)
void MeasurementData::hideAout(bool checked)
{
    if(!checked)
    {
        foreach(MyQwtPlotCurve *my, aoutList)
            my->detach();
    } else
    {
        foreach(MyQwtPlotCurve *my, aoutList)
        {
            my->attach(this);
            my->show();
        }
        this->setAxisTitle(yLeft, "Aout");
    }
}

// blendet die Kurve Wsp aus (true) bzw ein (false)
void MeasurementData::hideWsp(bool checked)
{
    if(!checked)
    {
        foreach(MyQwtPlotCurve *my, wspList)
            my->detach();
    } else
    {
        foreach(MyQwtPlotCurve *my, wspList)
        {
            my->attach(this);
            my->show();
        }
        this->setAxisTitle(yLeft, "WSP");
    }
}

// blendet die Kurve Pv aus (true) bzw ein (false)
void MeasurementData::hidePv(bool checked)
{
    if(!checked)
    {
        foreach(MyQwtPlotCurve *my, pvList)
            my->detach();
    } else
    {
        foreach(MyQwtPlotCurve *my, pvList)
        {
            my->attach(this);
            my->show();
        }
        this->setAxisTitle(yLeft, "PV");
    }
}
MyQwtPlotCurve.h

Code: Alles auswählen

#ifndef MYQWTPLOTCURVE_H
#define MYQWTPLOTCURVE_H
#include <qwt_plot_curve.h>
#include <QTime>

class MyQwtPlotCurve : public QwtPlotCurve
{
public:
    explicit MyQwtPlotCurve(QString &tName);

    void            addSample(double x, double y);                          // fügt einen Messwert zur Kurve hinzu
    int             getSampleCount();                                       // gibt die Anzahl der Messwerte zurück
    inline  QString getName()           const {return this->name;}          // gibt den Namen der Kurve zurück
    inline  QString getTime(int index)  const {return timeList.at(index);}  // gibt die Uhrzeit zurück, die an der Stelle index steht
    void            deleteAll();                                            // Test

private:
    QString             name;
    QVector<double>     v_x;
    QVector<double>     v_y;
    QStringList         timeList;

};

#endif // MYQWTPLOTCURVE_H
MyQwtPlotCurve.cpp

Code: Alles auswählen

#include "myqwtplotcurve.h"

MyQwtPlotCurve::MyQwtPlotCurve(QString &tName) :
    QwtPlotCurve(tName),
    v_x(),
    v_y(),
    timeList()
{
    this->setRenderHint(QwtPlotItem::RenderAntialiased);
}

// fügt einen Messwert zur Kurve hinzu
void MyQwtPlotCurve::addSample(double x, double y)
{
    this->timeList.append(QTime::currentTime().toString("hh:mm:ss"));
    this->v_x.append(x);
    this->v_y.append(y);

    this->setData(v_x, v_y);
}

// gibt die Anzahl der Messwerte zurück
int MyQwtPlotCurve::getSampleCount()
{
    return this->v_x.size();
}

// TEst
void MyQwtPlotCurve::deleteAll()
{
    this->v_x.clear();
    this->v_y.clear();
}
So. ich denke nicht, dass bei einer messung pro minute zu viele Datensätze anstehen, oder was denkt ihr? Rechnung: (Messwert+Zeitstempel[beides double])*3verschiedene Messwerte*8Geräte*60s*60m*6h ~= 8,3Mb.

Vielen Dank für alles, was weiterhilft!!!
ich möchte die 64-bit Farbpalette als Buntstiftsammlung
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: Absturz nach mehreren Stunden mit Qwt!

Beitrag von franzf »

Du hast Listen mit Pointern. Die Listen leerst du immer mit clear() ohne die einzelnen Items mit delete zu zerstören -> Memory Leak. Klar dass irgendwann der Speicher voll ist.
Außerdem: Exceptions besser per const Referenz catchen, denn Kopien können wieder Exceptions auslösen (z.B. so ein "bad_alloc", wie du es jetzt schon bekommen hast).
zx7r
Beiträge: 13
Registriert: 26. Mai 2010 20:16
Wohnort: Berlin

Re: Absturz nach mehreren Stunden mit Qwt!

Beitrag von zx7r »

Also ich wollte eigentlich die Listen nie löschen, weil die Messwerte während der Laufzeit ja auf dem QwtPlot angezeigt werden sollen. Ansonsten hast du natürlich recht, dass ich den Speicherplatz wieder frei geben muss, daran hab ich gar nicht gedacht :( Ich habe bei dem Problem aber eher an etwas gedacht, dass der QwtPlot mit dem Zeichnen aller Datensätze nicht hinterherkommt? Ist das möglich wenn es so viele Messwerte sind?

Ist es eigentlich okay den QVector mit double's zu füllen oder sollte man lieber Zeiger auf die double's nehmen?
ich möchte die 64-bit Farbpalette als Buntstiftsammlung
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Re: Absturz nach mehreren Stunden mit Qwt!

Beitrag von franzf »

zx7r hat geschrieben:Ist es eigentlich okay den QVector mit double's zu füllen oder sollte man lieber Zeiger auf die double's nehmen?
Es besteht ein großer Unterschied zwischen Zeigern und bloßen Objekten. Zeiger zeigen immer auf ein Objekt. Wenn du die Objekte hinter den Zeigern austauschen willst, dann geht das nur mit Zeigern, nicht mit Values. Ansonsten würde ich immer versuchen, auf Zeiger zu verzichten.
* Zeiger muss man selber löschen. Tut man das nicht, bekommt man ein Memory Leak - wird richtig problematisch, wenn man das oft macht.
* Zeiger müssen dereferenziert werden. Dafür gehen ein paar zusätzliche CPU-Zyklen drauf. Passiert das oft, bekommst du vllt. sogar ein messabares Performance-Loch.
* Speicher anfordern dauert bei Zeigern deutlich länger. Der Allokator muss für jedes zu erzeugende Objekt nach freiem Speicher suchen, was hier die meiste Zeit in Anspruch nimmt. Und wieder: werden oft und viele Einzelne Objekte im Freispeicher angelegt, kann das in performancekritischen Abschnitten zu Problemen führen.

Also: Wenns bisher ohne Zeiger funktioniert bleib dabei.
zx7r
Beiträge: 13
Registriert: 26. Mai 2010 20:16
Wohnort: Berlin

Re: Absturz nach mehreren Stunden mit Qwt!

Beitrag von zx7r »

Alles klar, vielen Dank! Ich dachte aber, dass es bei "großen" Objekten (die also viel Speicherplatz benötigen) sinnvoller wäre einen Zeiger zu nutzen. Denn das Kopieren eines kompletten Objektes nimmt wahrscheinlich viel mehr rechenzeit in anspruch als nur der zeiger. aber es kommt wahrscheinlich darauf an, wie oft man kopieren will etc.

Weiß denn jemand was die meldung vom QPainter bedeutet, also das mit den "3 states"? vielleicht liegt dort die fehlerursache?!

Grüße!
ich möchte die 64-bit Farbpalette als Buntstiftsammlung
Uwe
Beiträge: 176
Registriert: 9. Oktober 2005 13:37
Wohnort: München

Re: Absturz nach mehreren Stunden mit Qwt!

Beitrag von Uwe »

zx7r hat geschrieben:Weiß denn jemand was die meldung vom QPainter bedeutet, also das mit den "3 states"?
Das ist eine Folge der Exception - die Ursache Deines Absturzes ist mit Sicherheit, dass Dir der Speicher ausgeht.

Uwe
zx7r
Beiträge: 13
Registriert: 26. Mai 2010 20:16
Wohnort: Berlin

Re: Absturz nach mehreren Stunden mit Qwt!

Beitrag von zx7r »

Hallöchen!

So. ich habe jetzt nochmal nachgeschaut und bin doch etwas ins grübeln gekommen. ich denke ich mache bei der benutzung der klasse "qwtplotcurve" etwas falsch! im moment erbe ich von der klasse "qwtplotcurve". die neue klasse heißt ganz einfallsreich "myqwtplotcurve";) nun habe ich denk ich mal verständnisprobleme wenn es um die methode "setData(const QVector< double > &xData, const QVector< double > &yData)" geht. in "myqwtplotcurve" habe ich zwei membervektoren: einmal v_x und v_y. neue datensätze hänge ich immer an diesen vectoren ran und mache dann den aufruf der basisklasse "setData(v_x, v_y)". Ist das so okay oder ist die lösung(wie ich denke) eher grottenschlecht und führt zu speicherproblemen? in der qwt-dokumentation konnte ich das nicht genau entnehmen wie ich das nun performanter gestalten soll. bin auch nicht der englischexperte:(


Vielen Dank mal wieder!
Gruß Oliver
ich möchte die 64-bit Farbpalette als Buntstiftsammlung
Uwe
Beiträge: 176
Registriert: 9. Oktober 2005 13:37
Wohnort: München

Re: Absturz nach mehreren Stunden mit Qwt!

Beitrag von Uwe »

zx7r hat geschrieben:Ist das so okay oder ist die lösung(wie ich denke) eher grottenschlecht und führt zu speicherproblemen?
Qt Container sind "implicitely shared" d.h. die tatsächliche Daten werden kopiert, wenn sich eine Instanz verändert. Wenn Du Deine lokalen Vektoren erweiterst ensteht daher temporär eine Kopie - nach dem setData hast Du dann wieder nur noch eine Instanz.

Falls Du die Kopie vermeiden willst kannst Du Deine Daten über ein abgeleitetes QwtSeriesData ( QwtData mit Qwt 5.x ) Objekt mit der Kurve verknüpfen oder noch einfacher so:

Code: Alles auswählen

YourCurve::addPoint( const QPointF& pos )
{
    setData( QVector< double >(), QVector< double >() );
    v_x += pos.x();
    v_y += pos.y();
    setData( v_x, v_y );
}
Prinzipiell würdest v_x/v_y aber gar nicht benötigen, da Du Dir die übergebenen Vektoren von der Kurve geben lassen kannst.

Ich denke in Deinem Fall geht es aber eher um ein Memory Leak !

Uwe
Antworten