Skip to content
Snippets Groups Projects
Commit f4011aae authored by Giacomo Strangolino's avatar Giacomo Strangolino
Browse files

Project blmhistory imported from last version in CVS

parents
No related branches found
No related tags found
No related merge requests found
Showing with 1118 additions and 0 deletions
images/zoom-best-fit.png

1.08 KiB

images/zoom-in.png

1.12 KiB

images/zoom-original.png

1.1 KiB

images/zoom-out.png

1.05 KiB

jive 0 → 100644
This diff is collapsed.
#ifndef BLMHISTORY_H
#define BLMHISTORY_H
#include <QMainWindow>
#include <QHash>
#include <tvariant.h>
class Label;
class DbThread;
class Image;
class Painter;
namespace Ui {
class MainWindow;
}
class BlmHistory : public QMainWindow
{
Q_OBJECT
friend class WindowsSaveRestore;
public:
explicit BlmHistory(QWidget *parent = 0);
~BlmHistory();
enum Type { None, Bergoz, Photodiode };
public slots:
void getData();
void setLiveMode(bool live);
void setImageDynamicRange(bool dr);
void setMinExpLog();
void setEmptyValue();
signals:
void progress(int step);
void magnifierEnabled(bool);
private slots:
void blmNamesReady(const TVariant& sl);
void onProgressUpdate(int step);
void sourceIsArchived(const QString &, int i, int j);
void dbDataAvailable();
void changePalette(bool);
void updateLiveData();
void updateCountdownTimer();
void configTabChanged(int tabno);
void exportData() const;
void updateCustomRange();
void enableCustomRange(bool enab);
void setMaxSpinBoxValue(int);
void setMaxValSliderValue(double);
void resetMaxAndMinDoubleSpinboxes();
protected:
void closeEvent(QCloseEvent *);
private:
Ui::MainWindow *ui;
Label *mImageLabel;
Type mType;
DbThread *mDbThread;
int mTotSteps, mCurrentStep;
QHash<QString, QStringList> mBlmNamesHash;
/* used to store blm names in the right order */
QVector<QStringList> mBlmNames;
};
#endif // BLMHISTORY_H
This diff is collapsed.
#ifndef BLMHISTORY_CONSTANTS_H
#define BLMHISTORY_CONSTANTS_H
#define IMAGE_W 1440
#define EMPTY_VAL 255
/* expected seconds between two subsequent timestamps in the data */
#define DIFFTIME_SECS 60
#define DELTA_SECS_TOLERANCE 30
#define BLM_MIN_VALUE (double) -4.2
#define BLM_MAX_VALUE (double) 1061.0
#endif // CONSTANTS_H
#include "dbdatacache.h"
#include <QtDebug>
#include <macros.h>
#include <QStringList>
DbDataCache::DbDataCache()
{
}
void DbDataCache::invalidate()
{
mAttDataHash.clear();
mAttIdHash.clear();
}
/** \brief This method must be called before calculateStartStopDateTime
* in order to update the currently start and stop timestamps used by the caller.
*/
void DbDataCache::saveStartStopDateTime(const QDateTime& sdat, const QDateTime stodt)
{
mStartDt = sdat;
mStopDt = stodt;
}
int DbDataCache::maxDataLen() const
{
int max = 0;
QTime t;
t.start();
foreach(QList<HdbData> d, mAttDataHash.values())
{
if(d.size() > max)
max = d.size();
}
qDebug() << __FUNCTION__ << "maximum data len calculated in " << t.elapsed() << "ms";
return max;
}
/** \brief inserts the dataList associated with attName into the data hash,
* according to the operation type.
*
* @param attName: the name of the attribute to cache
* @param dataList the list of data associated with attName
* @param operation the operation type to perform when data is inserted
*
* \par Note
* If the operation is Disjoined and a new insert operation has to be started,
* the caller must first invalidate the cache
*/
void DbDataCache::insert(const QString& attName,
const QList<HdbData> &dataList,
OperationType operation)
{
int dataLen = dataList.size();
if(!mAttDataHash.contains(attName)) /* insert it */
mAttDataHash.insert(attName, dataList);
else if(dataLen > 0)
{
/* the att data hash already contains the attName key */
QList<HdbData> &alreadyExistingList = mAttDataHash[attName];
int i;
QDateTime firstExistingTimestamp;
QDateTime lastExistingTimestamp, timestamp;
switch(operation)
{
case Disjoined:
/* data hash must have been cleared by the caller before the insertion
* begins. So we should have fallen into the case
* if(!mAttDataHash.contains(attName)) above
*/
perr("DbDataCache: Disjoined operation on a hash already containing \"%s\""
": have you cleared the cache before inserting?",
qstoc(attName));
break;
case ExtendLeft:
// qDebug() << __FUNCTION__ << operation << "prepending" << dataList.size() << "to" <<
// alreadyExistingList.size() << " adjacent tstamps" <<
// alreadyExistingList.first().timestamp << dataList.last().timestamp;
/* data list is the new data completely preceding the data we already have in
* the hash: prepend the new data to the old data
*/
alreadyExistingList = dataList + alreadyExistingList;
break;
case ExtendRight:
if(dataList.size())
{
// qDebug() << __FUNCTION__ << operation << "appending" << dataList.size() << "to" <<
// alreadyExistingList.size() << " adjacent tstamps" <<
// alreadyExistingList.last().timestamp << dataList.first().timestamp;
alreadyExistingList = alreadyExistingList + dataList;
}
break;
case ExtendLeftRight:
firstExistingTimestamp = alreadyExistingList.first().timestamp;
lastExistingTimestamp = alreadyExistingList.last().timestamp;
/* fill in the left part: insert at the i-th position the i-th value
* of dataList which timestamp is less than the already existing timestamp
*/
i = 0;
timestamp = dataList[i].timestamp;
// qDebug() << __FUNCTION__ << "joining first ts" << firstExistingTimestamp << "with first new " << timestamp;
while(timestamp < firstExistingTimestamp && i < dataLen - 1)
{
alreadyExistingList.insert(i, dataList[i]);
i++;
timestamp = dataList[i].timestamp;
}
/* right part.
* i will point to the first location inside dataList where a timestamp greater than
* lastExistingTimestamp is expected
*/
if(i < dataLen)
{
// qDebug() << __FUNCTION__ << "joining last ts" << lastExistingTimestamp << "with new " << timestamp;
while(i < dataLen)
{
if(dataList[i].timestamp > lastExistingTimestamp)
alreadyExistingList.append(dataList[i]);
// else
// qDebug() << __FUNCTION__ << "DbDataCache: data at index %d has timestamp <= " << lastExistingTimestamp;
i++;
}
}
else
printf("DbDataCache: no new data after last existing timestamp\n");
break;
case None:
default:
break;
}
}
}
QList<QDateTime> DbDataCache::calculateStartStopDateTime(const QDateTime &start,
const QDateTime& stop,
OperationType &operation)
{
QList<QDateTime> dtCouples;
/* first time */
if(mAttDataHash.isEmpty() ||
!mStartDt.isValid() ||
!mStopDt.isValid()
|| stop <= mStartDt
|| start >= mStopDt)
{
/* start----------stop mStartDt++++++mStopDt
* or
* mStartDt++++++mStopDt start------------stop
*/
qDebug() << __FUNCTION__ << "[0] invalidating cache";
operation = Disjoined;
dtCouples << start << stop;
}
else if(start < mStartDt && stop > mStopDt)
{
/* start-------mStartDt++++++++++++mStopDt------stop */
qDebug() << __FUNCTION__ << "[1] extending interval";
operation = ExtendLeftRight;
dtCouples << start << mStartDt << stop << mStopDt;
}
else if(start == mStartDt && stop == mStopDt)
{
operation = None;
}
else if(start <= mStartDt && stop <= mStopDt)
{
/* start------mStartDt+++++++stop++++++mStopDt
*/
if(start != mStartDt)
dtCouples << start << mStartDt;
operation = ExtendLeft;
/* remove data from stop to mStopDt */
qDebug() << start << mStartDt << stop << mStopDt;
foreach(QString blmatt, mAttDataHash.keys())
{
QList<HdbData> &dlist = mAttDataHash[blmatt];
/* remove from the back until the i-th timestamp is greater than stop
* We keep data with timestamp == stop
*/
while(dlist.size() > 0 && dlist.last().timestamp > stop)
{
qDebug() << __FUNCTION__ << "popping " << dlist.last().timestamp << "cuz > " << stop;
dlist.pop_back();
}
qDebug() << __FUNCTION__ << "[2] removed elements from" << blmatt <<
"last timestamp kept" << dlist.last().timestamp << "requested interval" << start << stop;
}
}
else if(start >= mStartDt && stop >= mStopDt)
{
/* mStartDt+++++++start++++++mStopDt---------stop */
if(stop != mStopDt)
dtCouples << mStopDt << stop;
operation = ExtendRight;
/* remove data with timestamp < start */
foreach(QString blmatt, mAttDataHash.keys())
{
QList<HdbData> &dlist = mAttDataHash[blmatt];
while(dlist.size() > 0 && dlist.at(0).timestamp < start)
dlist.pop_front();
if(dlist.size() > 0)
qDebug() << __FUNCTION__ << "[3] removed elements from" << blmatt <<
"first timestamp kept" << dlist.first().timestamp << "requested interval" << start << stop;
}
}
else if(start >= mStartDt && stop <= mStopDt)
{
operation = None;
/* mStartDt++++++start++++++++++++++++++++++stop+++++++++++mStopDt */
/* no need to make query: will return empty couple but need to remove unused data
*/
foreach(QString blmatt, mAttDataHash.keys())
{
QList<HdbData> &dlist = mAttDataHash[blmatt];
/* remove from the back until the i-th timestamp is greater than stop
* We keep data with timestamp == stop
*/
while(dlist.size() > 0 && dlist.last().timestamp > stop) /* leave timestamp == stop */
dlist.pop_back();
qDebug() << __FUNCTION__ << "[4a] removed elements from" << blmatt <<
"first timestamp kept" << dlist.first().timestamp << "requested interval" << start << stop;
while(dlist.size() > 0 && dlist.first().timestamp < start) /* leave timestamp == start */
dlist.pop_front();
qDebug() << __FUNCTION__ << "[4b] removed elements from" << blmatt <<
"first timestamp kept" << dlist.first().timestamp << "requested interval" << start << stop;
}
}
return dtCouples;
}
#ifndef DBDATACACHE_H
#define DBDATACACHE_H
#include <QHash>
#include <QList>
# include "hdbdata.h"
class DbDataCache
{
friend class DbThread;
private:
enum OperationType { Disjoined, ExtendLeft, ExtendRight, None, ExtendLeftRight };
DbDataCache();
void invalidate();
void insert(const QString& attName, const QList<HdbData> &dataList, OperationType operation);
QList<QDateTime> calculateStartStopDateTime(const QDateTime &start,
const QDateTime &stop,
OperationType &operation);
void saveStartStopDateTime(const QDateTime& sdat, const QDateTime stodt);
int maxDataLen() const;
QHash<QString, QList<HdbData> > mAttDataHash;
QHash<QString, int> mAttIdHash;
QDateTime mStartDt, mStopDt;
};
#endif // DBDATACACHE_H
#include "dbthread.h"
#include "dbdatacache.h"
#include "hdbdata.h"
#include "hdbdataaligner.h"
#include <macros.h>
#include <QtDebug>
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlError>
#include <QtSql/QSqlQuery>
#include <QReadWriteLock>
#include <QMutexLocker>
DbThread::DbThread(QObject *parent,
const QString& dbUser,
const QString& dbPass,
const QString& dbHost,
const QString& schema,
const int dbPort
, bool hdbpp) :
QThread(parent)
{
m_dbHost = dbHost;
m_dbPassword = dbPass;
m_dbUser = dbUser;
m_dbPort = dbPort;
m_hdbPP = hdbpp;
m_dbSchema = schema;
mDbDataCache = new DbDataCache();
mMaximumValue = mMinimumValue = 0.0;
}
void DbThread::getData(const QStringList &devices,
const QString&attName,
const QDateTime& startDt,
const QDateTime& stopDt)
{
if(attName != mAttName)
mDbDataCache->invalidate();
mDeviceList = devices;
mAttName = attName;
mStartDt = startDt;
mStopDt = stopDt;
this->start();
}
QDateTime DbThread::startDateTime() const
{
return mStartDt;
}
QDateTime DbThread::stopDateTime() const
{
return mStopDt;
}
QHash<QString, QList<HdbData> > DbThread::attDataHash()
{
QReadLocker rlocker(&mRWLock);
return mDbDataCache->mAttDataHash;
}
void DbThread::setAbortFlag()
{
QWriteLocker rwLocker(&mAbortRWLock);
mAbort = true;
}
int DbThread::maxDataLen()
{
QReadLocker rlocker(&mRWLock);
return mStartDt.secsTo(mStopDt) / 60 + 1;
}
void DbThread::run()
{
QString whereTimeQuery;
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL");
db.setHostName(m_dbHost);
db.setPassword(m_dbPassword);
db.setUserName(m_dbUser);
db.setDatabaseName(m_dbSchema);
db.setPort(m_dbPort);
QString attconf_tbl, att_name, att_id, time, value;
m_hdbPP ? attconf_tbl = "att_conf" : attconf_tbl = "adt";
m_hdbPP ? att_name = "att_name" : att_name = "full_name";
m_hdbPP ? att_id = "att_conf_id" : att_id = "ID";
m_hdbPP ? time = "data_time" : time = "time";
m_hdbPP ? value = "value_r" : value = "value";
mAbort = false;
/* auto reconnect */
db.setConnectOptions("MYSQL_OPT_RECONNECT=1");
m_dbError = !db.open();
if(m_dbError)
{
m_lastErrorMessage = QString("HDbProxy: failed to connect to database with host \"%1\", port %2, schema \"%3\""
"user \"%4\" password \"%5\"").
arg(db.hostName()).arg(db.port()).
arg(db.databaseName()).arg(db.userName()).
arg(db.password());
emit databaseError("DbProxy: db open failed", m_lastErrorMessage);
perr("database open error: %s", qstoc(m_lastErrorMessage));
}
else
{
QString attNameLowerCase = mAttName.toLower();
if(mDeviceList.isEmpty())
{
m_lastErrorMessage = QString("HDbProxy: devices list is empty");
emit databaseError("Error: empty device list", m_lastErrorMessage);
perr("m_switchDatabase: %s", qstoc(m_lastErrorMessage));
return;
}
QWriteLocker writeLocker(&mRWLock);
int progressPercent = 0;
int previousProgressPercent = 0;
int step = 0;
int totSteps = 10;
DbDataCache::OperationType operationType;
QStringList dateTimeIntervalListAsString;
QList<QDateTime> dateTimeIntervalList =
mDbDataCache->calculateStartStopDateTime(mStartDt, mStopDt, operationType);
/* save current start stop date time */
mDbDataCache->saveStartStopDateTime(mStartDt, mStopDt);
/* need to invalidate cache? We have to do it
*/
if(operationType == DbDataCache::Disjoined)
mDbDataCache->invalidate();
foreach(QDateTime dt, dateTimeIntervalList)
dateTimeIntervalListAsString << dt.toString("yyyy-MM-dd hh:mm");
if(mDbDataCache->mAttIdHash.isEmpty())
{
mMissingDevices.clear();
pinfo("DbThread: querying database for full_name/ID associations");
QString part;
for(int i = 0; i < mDeviceList.size() - 1; i++)
part += "'%" + mDeviceList.at(i) + "/" + attNameLowerCase + "' or " + att_name + " like ";
part += "'%" + mDeviceList.last() + "/" + attNameLowerCase + "'";
QString queryStr = QString("SELECT %1,%2 FROM %3 where %1 like ")
.arg(att_name).arg(att_id).arg(attconf_tbl) + part;
QSqlQuery q(queryStr);
printf("\e[1;33mquery for ID: \"%s\" -- SIZE %d\e[0m\n", qstoc(queryStr), q.size());
qDebug() << __FUNCTION__ << "QUERY " << queryStr;
if(q.lastError().isValid())
perr("error in query \"%s\": \"\e[0;31m%s\"\e[0m", qstoc(queryStr), qstoc(q.lastError().text()));
while(q.next())
{
/* remove tango://tango.host.bla.bla and keep only domain/family/member/attribute */
QString s = q.value(0).toString().section('/', -4, -1).toLower();
mDbDataCache->mAttIdHash.insert(s, q.value(1).toInt());
printf("\e[0;35m + inserting in cache attribute \"%s\"\e[0m\n", qstoc(s));
}
/* check for missing attributes */
foreach(QString dev, mDeviceList)
{
QString n = dev + "/" + attNameLowerCase;
if(!mDbDataCache->mAttIdHash.keys().contains(n))
{
perr("DbThread: missing attribute \"%s\" in database", qstoc(n));
mMissingDevices << dev;
}
}
if(q.lastError().isValid())
perr("id query: error %s\n", qstoc(q.lastError().text()));
}
else
pinfo("DbThread: reusing cached full_name/ID couples.");
emit progress(5);
totSteps = mDbDataCache->mAttIdHash.size();
/* compose SQL query now
* >= and <= only on Disjoined operation.
* Otherwise, when merging, > and < is enough because the cache will keep the
* extremes of the interval.
*/
if(dateTimeIntervalListAsString.size() == 2 && operationType == DbDataCache::Disjoined)
{
whereTimeQuery = QString(" WHERE %1 >= '%2' AND %1 <= '%3'").arg(time)
.arg(dateTimeIntervalListAsString.first()).arg(dateTimeIntervalListAsString.last());
}
else if(dateTimeIntervalListAsString.size() == 2 && operationType == DbDataCache::ExtendLeft)
{
whereTimeQuery = QString(" WHERE %1 >= '%2' AND %1 < '%3'").arg(time)
.arg(dateTimeIntervalListAsString.first()).arg(dateTimeIntervalListAsString.last());
}
else if(dateTimeIntervalListAsString.size() == 2 && operationType == DbDataCache::ExtendRight)
{
whereTimeQuery = QString(" WHERE %1 > '%2' AND %1 <= '%3'")
.arg(dateTimeIntervalListAsString.first()).arg(dateTimeIntervalListAsString.last());
}
else if(dateTimeIntervalListAsString.size() == 4)
{
whereTimeQuery = QString(" WHERE (%1 > '%2' AND %1 < '%3') "
" OR (%1 > '%4' AND %1 < '%5')").arg(time)
.arg(dateTimeIntervalListAsString.at(0))
.arg(dateTimeIntervalListAsString.at(1))
.arg(dateTimeIntervalListAsString.at(2))
.arg(dateTimeIntervalListAsString.at(3));
}
if(!whereTimeQuery.isEmpty() && dateTimeIntervalListAsString.last() > dateTimeIntervalListAsString.first())
{
QTime t;
t.start();
foreach(QString fullName, mDbDataCache->mAttIdHash.keys())
{
int attId = mDbDataCache->mAttIdHash.value(fullName);
QList<HdbData> hdbDataList;
mAbortRWLock.lockForRead();
/* check if we have to stop querying the database */
if(mAbort)
{
emit progress(100);
emit databaseError("DbThread", "Aborted by user");
mDbDataCache->invalidate();
db.close();
mAbortRWLock.unlock();
break;
}
else
{
QString data_tablename;
m_hdbPP ? data_tablename = "att_scalar_devdouble_ro" :
data_tablename = QString("att_%1").arg(attId, 5, 10, QChar('0'));
QString qu = QString("SELECT %1,%2 FROM %3").arg(time).arg(value).arg(data_tablename);
qu += whereTimeQuery;
if(m_hdbPP)
qu += " AND att_conf_id=" + QString::number(attId);
pinfo("executing \"%s\"", qstoc(qu));
/* use the 'where' query composed above according to one or two date time intervals
* needed
*/
QSqlQuery valuesQuery(qu);
if(valuesQuery.lastError().isValid())
perr("data query: error %s\n", qstoc(valuesQuery.lastError().text()));
while(valuesQuery.next())
{
QDateTime timestamp = QDateTime::fromString(valuesQuery.value(0).toString(), "yyyy-MM-ddThh:mm:ss");
QTime t = timestamp.time();
t.setHMS(t.hour(), t.minute(), 0); /* round the value to the minute (discard seconds) */
timestamp.setTime(t); /* update date time with a value rounded to the minute */
hdbDataList.push_back(HdbData(timestamp, valuesQuery.value(1).toDouble()));
}
/* data cache insert is optimized according to the operation type to do:
* prepend data to the beginning (left), append (right) or extend (left and right)
*/
mDbDataCache->insert(fullName, hdbDataList, operationType);
// printf("- inserted %d data for \"%s\" \n", hdbDataList.size(), qstoc(fullName));
step++;
progressPercent = 100.0 * step / totSteps;
/* progressPercent - previousProgressPercent > 9 to avoid progress signal duplicates */
if(progressPercent % 10 == 0 && progressPercent - previousProgressPercent > 9)
{
emit progress(progressPercent);
previousProgressPercent = progressPercent;
}
}
mAbortRWLock.unlock();
}
qDebug() << step << "* queries completed in " << t.elapsed() <<"ms, intervals " <<
dateTimeIntervalListAsString;
}
else
emit progress(100);
db.close();
/* align data into attDataHash according to expected timestamps, one datum each
* minute.
* mDbDataCache->mAttDataHash is passed as a reference and so it is modified
* by the data aligner.
* HdbDataAligner calculates maximum and minimum values.
*/
HdbDataAligner dataAligner;
dataAligner.alignToTimestamps(mDbDataCache->mAttDataHash, mStartDt, mStopDt);
mMaximumValue = dataAligner.getMaximumValue();
mMinimumValue = dataAligner.getMinimumValue();
mMinimumPositive = dataAligner.getMinimumPositive();
qDebug() << __FUNCTION__ << "max " << mMaximumValue << "min" << mMinimumValue << " min pos "
<< mMinimumPositive;
}
}
#ifndef DBTHREAD_H
#define DBTHREAD_H
#include <QThread>
#include <QDateTime>
#include <QStringList>
#include <QHash>
#include <QReadWriteLock>
#include "hdbdata.h"
class DbDataCache;
class DbThread : public QThread
{
Q_OBJECT
public:
explicit DbThread(QObject *parent,
const QString& dbUser,
const QString& dbPass,
const QString& dbHost,
const QString &schema,
const int dbPort,
bool hdbpp);
QString lastError() const { return m_lastErrorMessage; }
bool dbError() const { return m_dbError; }
QHash<QString, QList<HdbData> > attDataHash();
int maxDataLen();
double maximumValue() const { return mMaximumValue; }
double minimumValue() const { return mMinimumValue; }
double minimumPositive() const { return mMinimumPositive; }
QDateTime startDateTime() const;
QDateTime stopDateTime() const;
/** \brief returns the list of the devices that are not present into the
* hdb database
*
* @return the device name list of the tango devices that are not logged into
* the hdb, in the form domain/family/member
*/
QStringList missingDevices() const { return mMissingDevices; }
protected:
virtual void run();
signals:
void databaseError(const QString& origin, const QString& error);
void progress(int step);
public slots:
void getData(const QStringList &devices, const
QString&attName,
const QDateTime& startDt,
const QDateTime& stopDt);
void setAbortFlag();
private:
QStringList mDeviceList, mMissingDevices;
QString mAttName;
QString m_lastErrorMessage;
QDateTime mStartDt, mStopDt;
DbDataCache *mDbDataCache;
QString m_dbHost, m_dbPassword, m_dbUser, m_dbName, m_dbSchema;
int m_dbPort;
bool m_dbError, mAbort, m_hdbPP;
double mMaximumValue, mMinimumValue, mMinimumPositive;
QReadWriteLock mRWLock, mAbortRWLock;
};
#endif // DBTHREAD_H
#include "hdbdata.h"
HdbData::HdbData(const QDateTime &tstamp, const double &val)
{
timestamp = tstamp;
value = val;
}
HdbData::HdbData()
{
}
#ifndef HDBDATA_H
#define HDBDATA_H
#include <QString>
#include <QDateTime>
#include <QList>
class HdbData
{
public:
HdbData();
HdbData(const QDateTime &tstamp, const double &value);
QDateTime timestamp;
double value;
};
#endif // HDBDATA_H
#include "blmhistory_constants.h"
#include "hdbdataaligner.h"
#include <math.h>
#include <QDateTime>
#include <QtDebug>
HdbDataAligner::HdbDataAligner()
{
mMaximum = -100;
mMinimum = mMinimumPositive = 1000;
}
void HdbDataAligner::alignToTimestamps(QHash<QString, QList<HdbData> > &attDataHash,
const QDateTime &startDt,
const QDateTime &stopDt)
{
QDateTime lastDateTime = stopDt; /* last expected timestamp */
QDateTime ithDateTime; /* will store the timestamp of the data inside the check cycle below */
QDateTime expectedDateTime; /* will keep track of the expected date time through the cycle below */
int expectedDataSize = startDt.secsTo(stopDt) / 60 + 1;
mMaximum = -100;
mMinimum = 1000;
mMinimumPositive = 1000;
double nanval = nan("NaN");
int deltaSecs, i, cnt;
/* find the maximum value
*/
qDebug() << __FUNCTION__ << "addTadaHash keys" << attDataHash.keys();
foreach(QString blmattnam, attDataHash.keys())
{
const QList<HdbData>& hdbDataList = attDataHash[blmattnam];
qDebug() << __FUNCTION__ << "hdbDataList size" << hdbDataList.size();
/* get last timestamp, which is expected at the last location of
* each dataList
*/
foreach(HdbData d, hdbDataList)
{
if(d.value > mMaximum)
mMaximum = d.value;
if(d.value < mMinimum)
mMinimum = d.value;
if(d.value > 0 && d.value < mMinimumPositive)
mMinimumPositive = d.value;
qDebug() << __FUNCTION__ <<blmattnam << d.value << mMaximum << mMinimum;
}
}
qDebug() << __FUNCTION__ << "last timestamp is"
<< lastDateTime << "expected data size" << expectedDataSize;
/* check all the timestamps inside hdb data list.
* If data is missing at some time, fill the empty space with an invalid
* HdbData
*/
foreach(QString blmattnam, attDataHash.keys())
{
/* at the beginning, at the last location we expect the most recent timestamp */
expectedDateTime = lastDateTime;
QList<HdbData>& hdbDataList = attDataHash[blmattnam];
i = hdbDataList.size() - 1;
cnt = 0;
if(i < 0)
qDebug() << __FUNCTION__ << "inflating " << blmattnam << " from size" << hdbDataList.size() << "to size" << expectedDataSize;
/* a. inflate empty data list with invalid HdbData */
while(i < 0 && cnt < expectedDataSize) /* empty hdbDataList: no data at all */
{
hdbDataList.insert(0, HdbData(expectedDateTime, nanval));
expectedDateTime = expectedDateTime.addSecs(-DIFFTIME_SECS);
cnt++;
}
if(i < 0) /* must reinitialize data used in while below */
{
qDebug() << __FUNCTION__ << " inflated to " << hdbDataList.size();
cnt = 0;
expectedDateTime = lastDateTime;
}
/* b. expand the list of HdbData where necessary.
*/
/* do not insert a number of elements that would extend the list beyond
* maxDataLen
*/
while(i >= 0 && cnt < expectedDataSize)
{
ithDateTime = hdbDataList.at(i).timestamp;
/*
* calculate the distance, in seconds, between the expected timestamp
* and the actual timestamp.
* We will save data only if the two timestamps are separated by at most
* one minute
*/
deltaSecs = ithDateTime.secsTo(expectedDateTime);
if(deltaSecs > DELTA_SECS_TOLERANCE)
{
// qDebug() << __FUNCTION__ << "previous data size for" << blmattnam <<
// attDataHash.value(blmattnam).size();
/*
* insert an invalid HdbData at pos i + 1
* hdbDataList is a reference, so we modify it in place,
* inside the original hash.
*/
hdbDataList.insert(i + 1, HdbData(expectedDateTime, nanval));
// qDebug() << __FUNCTION__ << "filling " << blmattnam << "pos" << i+1 <<
// "expected was" << expectedDateTime << "ith was" << ithDateTime
// << "size is now" << attDataHash.value(blmattnam).size();
}
else /* look for previous element */
i--;
cnt++;
/*
* next expected date time (next loop) is one minute before
*/
expectedDateTime = expectedDateTime.addSecs(-DIFFTIME_SECS);
}
/* align the first part of the vector */
i = expectedDataSize - hdbDataList.size();
if(i > 0)
qDebug() << __FUNCTION__ << "--> must push_front" << i << "nan values";
while(i > 0)
{
hdbDataList.push_front(HdbData(expectedDateTime, nanval));
expectedDateTime = expectedDateTime.addSecs(-DIFFTIME_SECS);
i--;
}
}
}
#ifndef HDBDATAALIGNER_H
#define HDBDATAALIGNER_H
#include "db/hdbdata.h"
#include <QHash>
#include <QString>
#include <QList>
class HdbDataAligner
{
public:
HdbDataAligner();
void alignToTimestamps(QHash<QString, QList<HdbData> > &attDataHash,
const QDateTime& startDt,
const QDateTime &stopDt);
double getMaximumValue() const { return mMaximum; }
double getMinimumValue() const { return mMinimum; }
double getMinimumPositive() const { return mMinimumPositive; }
private:
double mMaximum, mMinimum, mMinimumPositive;
};
#endif // HDBDATAALIGNER_H
#include "exportdata.h"
#include <QFile>
#include <QFileDialog>
#include <QMessageBox>
#include <QTextStream>
#include <QDesktopServices>
#include <QUrl>
#include <QtDebug>
ExportData::ExportData()
{
}
bool ExportData::save(const QHash<QString, QList<HdbData> > &attDataHash,
const QStringList &orderedDevices,
const QString& attName)
{
bool ret;
mError = "";
QString fileName = QFileDialog::getSaveFileName(0, "Select a file to open", QDir::homePath(), "*.csv");
if(!fileName.endsWith(".csv"))
fileName.append(".csv");
mFilePath = fileName;
QFile f(fileName);
if (!f.open(QIODevice::WriteOnly | QIODevice::Text))
{
mError = f.errorString();
ret = false;
}
else
{
QTextStream out(&f);
qDebug() << "saving on " << fileName << orderedDevices << "size hash" << attDataHash.size();
/* write header */
out << "tango point,blm device,attribute,timestamp,date/time,value\n";
foreach(QString dev, orderedDevices)
{
QString key = dev + "/" + attName;
QList<HdbData> hdbDataList = attDataHash.value(key);
qDebug() << key << hdbDataList.size();
foreach(HdbData hdbd, hdbDataList)
out << key << "," << dev << "," << attName << ","
<< hdbd.timestamp.toTime_t() << ","
<< hdbd.timestamp.toString() << ","
<< QString::number(hdbd.value) << "\n";
}
f.close();
ret = true;
}
return ret;
}
void ExportData::dialogOpenFile()
{
int result = QMessageBox::question(0, "Would you like to open the file?", "Save successful.\n"
"Would you like to open "
" the file now?", QMessageBox::Open, QMessageBox::No);
switch(result)
{
case QMessageBox::Open:
QDesktopServices::openUrl(QUrl("file://" + mFilePath));
break;
default:
break;
}
}
#ifndef EXPORTDATA_H
#define EXPORTDATA_H
#include <QHash>
#include "db/hdbdata.h"
class ExportData
{
public:
ExportData();
bool save(const QHash<QString, QList<HdbData> > &attDataHash,
const QStringList& orderedDevices, const QString &attName);
void dialogOpenFile();
QString error() const { return mError; }
QString filePath() const { return mFilePath; }
private:
QString mError,mFilePath ;
};
#endif // EXPORTDATA_H
#include "guihelper.h"
#include <QWidget>
#include <QApplication>
#include <QTabWidget>
#include <QPushButton>
GuiHelper::GuiHelper(QWidget *w)
{
mWidget = w;
}
void GuiHelper::blockUI(BlockType t)
{
QList<QWidget *> widgets;
widgets = mWidget->findChild<QTabWidget *>("tabWidgetConfig")->widget(0)->findChildren<QWidget *>();
switch(t)
{
case LiveMode:
foreach(QWidget *w, widgets)
if(w->objectName() != "cbLive" &&
w->objectName() != "gbDataSelect" && w->objectName() != "cbAttName" &&
w->objectName() != "lCountdown")
w->setDisabled(true);
mWidget->findChild<QTabWidget *>("tabWidgetConfig")->widget(0)->findChild<QPushButton *>("pbAbort")->setVisible(true);
break;
case GetDataButton:
qApp->setOverrideCursor(QCursor(Qt::WaitCursor));
foreach(QWidget *w, widgets)
if(w->objectName() != "pbAbort")
w->setDisabled(true);
mWidget->findChild<QTabWidget *>("tabWidgetConfig")->widget(0)->findChild<QPushButton *>("pbAbort")->setVisible(true);
break;
default:
break;
}
}
void GuiHelper::unblock()
{
qApp->restoreOverrideCursor();
QList<QWidget *> widgets;
widgets = mWidget->findChild<QTabWidget *>("tabWidgetConfig")->widget(0)->findChildren<QWidget *>();
foreach(QWidget *w, widgets)
w->setDisabled(false);
mWidget->findChild<QTabWidget *>("tabWidgetConfig")->widget(0)->findChild<QPushButton *>("pbAbort")->setVisible(false);
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment