diff --git a/plugins/datamonitorPlugin/include/datamonitorplugin/dataacquisitionmanager.hpp b/plugins/datamonitorPlugin/include/datamonitorplugin/dataacquisitionmanager.hpp index 8d7d37a639..ca16910f26 100644 --- a/plugins/datamonitorPlugin/include/datamonitorplugin/dataacquisitionmanager.hpp +++ b/plugins/datamonitorPlugin/include/datamonitorplugin/dataacquisitionmanager.hpp @@ -17,6 +17,7 @@ class SCOPY_DATAMONITORPLUGIN_EXPORT DataAcquisitionManager : public QObject public: explicit DataAcquisitionManager(QObject *parent = nullptr); + void addMonitor(DataMonitorModel *monitor); void clearMonitorsData(); QList getMonitors(); @@ -26,10 +27,11 @@ class SCOPY_DATAMONITORPLUGIN_EXPORT DataAcquisitionManager : public QObject void updateActiveMonitors(bool toggled, QString monitorName); QMap *getDataMonitorMap() const; -signals: +Q_SIGNALS: void requestUpdateActiveMonitors(); void requestRead(); void activeMonitorsUpdated(); + void monitorAdded(DataMonitorModel *monito); private: QMap *m_activeMonitorsMap; diff --git a/plugins/datamonitorPlugin/include/datamonitorplugin/datamonitormodel.hpp b/plugins/datamonitorPlugin/include/datamonitorplugin/datamonitormodel.hpp index e1bef719c6..976c229902 100644 --- a/plugins/datamonitorPlugin/include/datamonitorplugin/datamonitormodel.hpp +++ b/plugins/datamonitorPlugin/include/datamonitorplugin/datamonitormodel.hpp @@ -54,6 +54,13 @@ class SCOPY_DATAMONITORPLUGIN_EXPORT DataMonitorModel : public QObject QString getDeviceName() const; void setDeviceName(const QString &newDeviceName); + bool isDummyMonitor() const; + void setIsDummyMonitor(bool newIsDummyMonitor); + + void setYdata(const QVector &newYdata); + + void setXdata(const QVector &newXdata); + Q_SIGNALS: void valueUpdated(double time, double value); void minValueUpdated(double value); @@ -75,6 +82,8 @@ class SCOPY_DATAMONITORPLUGIN_EXPORT DataMonitorModel : public QObject IReadStrategy *readStrategy; UnitOfMeasurement *unitOfMeasure; + + bool m_isDummyMonitor = false; }; } // namespace datamonitor } // namespace scopy diff --git a/plugins/datamonitorPlugin/include/datamonitorplugin/logdatatofile.hpp b/plugins/datamonitorPlugin/include/datamonitorplugin/logdatatofile.hpp index 46f3a9ba57..c2cd9c9dd9 100644 --- a/plugins/datamonitorPlugin/include/datamonitorplugin/logdatatofile.hpp +++ b/plugins/datamonitorPlugin/include/datamonitorplugin/logdatatofile.hpp @@ -14,7 +14,7 @@ class LogDataToFile : public QObject void continuousLogData(QString path); void logData(QString path); - void loadData(QString path); + void loadData(QString path, bool override); Q_SIGNALS: void startLogData(); @@ -25,6 +25,8 @@ class LogDataToFile : public QObject private: DataAcquisitionManager *m_dataAcquisitionManager; QString *currentFileHeader; + + void addDataToCorespondingMonitor(QString monitorName, QVector *time, QVector *val); }; } // namespace datamonitor } // namespace scopy diff --git a/plugins/datamonitorPlugin/include/datamonitorplugin/monitorselectionmenu.hpp b/plugins/datamonitorPlugin/include/datamonitorplugin/monitorselectionmenu.hpp index 0c042d47ac..1c7ab1fa2d 100644 --- a/plugins/datamonitorPlugin/include/datamonitorplugin/monitorselectionmenu.hpp +++ b/plugins/datamonitorPlugin/include/datamonitorplugin/monitorselectionmenu.hpp @@ -16,6 +16,8 @@ class MonitorSelectionMenu : public QWidget public: explicit MonitorSelectionMenu(QMap *monitorList, QWidget *parent = nullptr); + void addMonitor(DataMonitorModel *monitor); + Q_SIGNALS: void monitorToggled(bool toggled, QString monitorName); void removeMonitor(); @@ -26,7 +28,6 @@ class MonitorSelectionMenu : public QWidget QMap deviceMap; void generateDeviceSection(QString device); - void addMonitor(DataMonitorModel *monitor); }; } // namespace datamonitor } // namespace scopy diff --git a/plugins/datamonitorPlugin/src/dataacquisitionmanager.cpp b/plugins/datamonitorPlugin/src/dataacquisitionmanager.cpp index 22405d790a..cc17985d4c 100644 --- a/plugins/datamonitorPlugin/src/dataacquisitionmanager.cpp +++ b/plugins/datamonitorPlugin/src/dataacquisitionmanager.cpp @@ -10,6 +10,12 @@ DataAcquisitionManager::DataAcquisitionManager(QObject *parent) m_dataMonitorMap = new QMap(); } +void DataAcquisitionManager::addMonitor(DataMonitorModel *monitor) +{ + getDataMonitorMap()->insert(monitor->getName(), monitor); + Q_EMIT monitorAdded(monitor); +} + void DataAcquisitionManager::clearMonitorsData() { foreach(QString monKey, m_activeMonitorsMap->keys()) { diff --git a/plugins/datamonitorPlugin/src/datamonitor/datamonitormodel.cpp b/plugins/datamonitorPlugin/src/datamonitor/datamonitormodel.cpp index 444fac8ade..3d7b93b3d3 100644 --- a/plugins/datamonitorPlugin/src/datamonitor/datamonitormodel.cpp +++ b/plugins/datamonitorPlugin/src/datamonitor/datamonitormodel.cpp @@ -121,6 +121,24 @@ void DataMonitorModel::setDataStorageSize() } } +bool DataMonitorModel::isDummyMonitor() const { return m_isDummyMonitor; } + +void DataMonitorModel::setIsDummyMonitor(bool newIsDummyMonitor) { m_isDummyMonitor = newIsDummyMonitor; } + +void DataMonitorModel::setYdata(const QVector &newYdata) +{ + ydata.erase(ydata.begin(), ydata.end()); + ydata.append(newYdata); + Q_EMIT dataCleared(); +} + +void DataMonitorModel::setXdata(const QVector &newXdata) +{ + xdata.erase(xdata.begin(), xdata.end()); + xdata.append(newXdata); + Q_EMIT dataCleared(); +} + void DataMonitorModel::setReadStrategy(IReadStrategy *newReadStrategy) { readStrategy = newReadStrategy; @@ -139,7 +157,12 @@ void DataMonitorModel::clearMonitorData() Q_EMIT dataCleared(); } -void DataMonitorModel::read() { readStrategy->read(); } +void DataMonitorModel::read() +{ + if(!m_isDummyMonitor) { + readStrategy->read(); + } +} double DataMonitorModel::minValue() const { return m_minValue; } diff --git a/plugins/datamonitorPlugin/src/datamonitor/dmmreadstrategy.cpp b/plugins/datamonitorPlugin/src/datamonitor/dmmreadstrategy.cpp index 0d30038c0c..16f63f378a 100644 --- a/plugins/datamonitorPlugin/src/datamonitor/dmmreadstrategy.cpp +++ b/plugins/datamonitorPlugin/src/datamonitor/dmmreadstrategy.cpp @@ -35,8 +35,6 @@ void DMMReadStrategy::read() double result = (raw + m_offset) * scale * m_umScale; qDebug() << "dmm read success "; - // double currentTime = QwtDate::toDouble(QDateTime::currentDateTime()); - auto &&timeTracker = TimeManager::GetInstance(); double currentTime = QwtDate::toDouble(timeTracker->lastReadValue()); diff --git a/plugins/datamonitorPlugin/src/datamonitorstylehelper.cpp b/plugins/datamonitorPlugin/src/datamonitorstylehelper.cpp index b23cacb2e2..946bfbf2c8 100644 --- a/plugins/datamonitorPlugin/src/datamonitorstylehelper.cpp +++ b/plugins/datamonitorPlugin/src/datamonitorstylehelper.cpp @@ -85,6 +85,7 @@ void DataMonitorStyleHelper::DataLoggingMenuStyle(DataLoggingMenu *menu) StyleHelper::BlueButton(menu->dataLoggingBrowseBtn); StyleHelper::BlueButton(menu->dataLoggingBtn); StyleHelper::BlueButton(menu->dataLoadingBtn); + StyleHelper::BlueSquareCheckbox(menu->loadDataOverride); } QString DataMonitorStyleHelper::RemoveButtonStyle() diff --git a/plugins/datamonitorPlugin/src/datamonitortool.cpp b/plugins/datamonitorPlugin/src/datamonitortool.cpp index 264b0b2b5c..ad7a43ee4c 100644 --- a/plugins/datamonitorPlugin/src/datamonitortool.cpp +++ b/plugins/datamonitorPlugin/src/datamonitortool.cpp @@ -210,6 +210,9 @@ DatamonitorTool::DatamonitorTool(DataAcquisitionManager *dataAcquisitionManager, m_monitorSelectionMenu = new MonitorSelectionMenu(dataAcquisitionManager->getDataMonitorMap()); tool->leftStack()->add("Monitors", m_monitorSelectionMenu); + connect(m_dataAcquisitionManager, &DataAcquisitionManager::monitorAdded, m_monitorSelectionMenu, + &MonitorSelectionMenu::addMonitor); + connect(m_monitorSelectionMenu, &MonitorSelectionMenu::monitorToggled, m_monitorPlot, [=, this](bool toggled, QString monitorName) { // toggle monitor active inside data acquisiton manager diff --git a/plugins/datamonitorPlugin/src/menus/dataloggingmenu.cpp b/plugins/datamonitorPlugin/src/menus/dataloggingmenu.cpp index c47823b49d..db1f94e5c4 100644 --- a/plugins/datamonitorPlugin/src/menus/dataloggingmenu.cpp +++ b/plugins/datamonitorPlugin/src/menus/dataloggingmenu.cpp @@ -37,25 +37,26 @@ DataLoggingMenu::DataLoggingMenu(QWidget *parent) dataLoadingBtn = new QPushButton("Load data", logDataSection); connect(dataLoadingBtn, &QPushButton::clicked, this, [=, this]() { updateDataLoggingStatus(ProgressBarState::BUSSY); - Q_EMIT requestDataLoading(dataLoggingFilePath->getLineEdit()->text()); + Q_EMIT requestDataLoading(dataLoggingFilePath->getLineEdit()->text(), loadDataOverride->isChecked()); }); - MenuOnOffSwitch *liveDataLogging = new MenuOnOffSwitch("Live data logging", logDataSection); - connect(liveDataLogging->onOffswitch(), &QAbstractButton::toggled, this, [=, this](bool toggled) { + liveDataLoggingButton = new MenuOnOffSwitch("Live data logging", logDataSection); + connect(liveDataLoggingButton->onOffswitch(), &QAbstractButton::toggled, this, [=, this](bool toggled) { m_liveDataLogging = toggled; dataLoggingBtn->setEnabled(!toggled); dataLoadingBtn->setEnabled(!toggled); }); - dataLoggingBtn->setEnabled(false); - dataLoadingBtn->setEnabled(false); - liveDataLogging->setEnabled(false); + loadDataOverride = new QCheckBox(logDataSection); + loadDataOverride->setText("Override data on load"); + loadDataOverride->setChecked(true); - ///// time manager - /// ??? is this the best solution + toggleButtonsEnabled(false); + + ///// time manager timeout used for requesting continuous data logging auto &&timeTracker = TimeManager::GetInstance(); connect(timeTracker, &TimeManager::timeout, this, [=, this]() { - if(liveDataLogging->onOffswitch()->isChecked()) { + if(liveDataLoggingButton->onOffswitch()->isChecked()) { Q_EMIT requestLiveDataLogging(dataLoggingFilePath->getLineEdit()->text()); } }); @@ -67,11 +68,7 @@ DataLoggingMenu::DataLoggingMenu(QWidget *parent) } else { dataLoggingFilePath->getLineEdit()->setStyleSheet("color:white"); - - dataLoggingBtn->setEnabled(true); - dataLoadingBtn->setEnabled(true); - liveDataLogging->setEnabled(true); - + toggleButtonsEnabled(true); Q_EMIT pathChanged(path); } }); @@ -79,8 +76,9 @@ DataLoggingMenu::DataLoggingMenu(QWidget *parent) logDataSection->contentLayout()->addWidget(new QLabel("Choose file")); logDataSection->contentLayout()->addWidget(dataLoggingFilePath); logDataSection->contentLayout()->addWidget(dataLoggingBrowseBtn); - logDataSection->contentLayout()->addWidget(liveDataLogging); + logDataSection->contentLayout()->addWidget(liveDataLoggingButton); logDataSection->contentLayout()->addWidget(dataLoggingBtn); + logDataSection->contentLayout()->addWidget(loadDataOverride); logDataSection->contentLayout()->addWidget(dataLoadingBtn); logDataContainer->contentLayout()->addWidget(logDataSection); @@ -106,6 +104,8 @@ void DataLoggingMenu::updateDataLoggingStatus(ProgressBarState status) bool DataLoggingMenu::liveDataLogging() const { return m_liveDataLogging; } +bool DataLoggingMenu::isLoadDataOverrideOn() { return loadDataOverride->isChecked(); } + void DataLoggingMenu::chooseFile() { QString selectedFilter; @@ -114,3 +114,11 @@ void DataLoggingMenu::chooseFile() QFileDialog::Options(QFileDialog::DontUseNativeDialog)); dataLoggingFilePath->getLineEdit()->setText(filename); } + +void DataLoggingMenu::toggleButtonsEnabled(bool en) +{ + dataLoggingBtn->setEnabled(en); + dataLoadingBtn->setEnabled(en); + liveDataLoggingButton->setEnabled(en); + loadDataOverride->setEnabled(en); +} diff --git a/plugins/datamonitorPlugin/src/menus/logdatatofile.cpp b/plugins/datamonitorPlugin/src/menus/logdatatofile.cpp index 304834d8cd..cc1621f9c0 100644 --- a/plugins/datamonitorPlugin/src/menus/logdatatofile.cpp +++ b/plugins/datamonitorPlugin/src/menus/logdatatofile.cpp @@ -7,6 +7,7 @@ #include #include #include +#include using namespace scopy; using namespace datamonitor; @@ -25,8 +26,7 @@ void LogDataToFile::continuousLogData(QString path) fileHeader += "," + monitor; } - // if a channels is added or removed we need to recreate the file - // ?? use log data for recreating path + // if a channels is added or removed we need to recreate the file using logData() if(currentFileHeader != fileHeader) { logData(path); @@ -89,50 +89,32 @@ void LogDataToFile::logData(QString path) QString tableHead = "Time"; QMap values; - // foreach(QString monitor, m_dataAcquisitionManager->getActiveMonitors()) { - // tableHead += ", " + monitor; - // auto xData = - //m_dataAcquisitionManager->getDataMonitorMap()->value(monitor)->getXdata(); - - // for(int i = 0; i < xData->length(); i++) { - - // auto val = - // m_dataAcquisitionManager->getDataMonitorMap()->value(monitor)->getValueAtTime( - // xData->at(i)); - // QDateTime auxTime = QDateTime::fromMSecsSinceEpoch(xData->at(i)); - // QString time = - //QString(auxTime.toString(DataMonitorUtils::getDateTimeFormat())); - - // if(values.contains(time)) { - // values[time] += QString(", " + QString::number(val)); - // } else { - // values.insert(time, "," + QString::number(val)); - // } - // } - // } - - QVector *timeValues = new QVector(); + + // cover all the time values recorded + // use a QSet to avoid having duplicate time values + QSet timeValues; foreach(QString monitor, m_dataAcquisitionManager->getActiveMonitors()) { tableHead += ", " + monitor; - auto xData = m_dataAcquisitionManager->getDataMonitorMap()->value(monitor)->getXdata(); - - if(xData->length() > timeValues->length()) { - timeValues = xData; + for(int i = 0; i < xData->length(); i++) { + timeValues.insert(xData->at(i)); } } - for(int i = 0; i < timeValues->length(); i++) { + QSet::const_iterator i; + for(i = timeValues.begin(); i != timeValues.end(); ++i) { - QDateTime auxTime = QDateTime::fromMSecsSinceEpoch(timeValues->at(i)); + QDateTime auxTime = QDateTime::fromMSecsSinceEpoch(*i); QString time = QString(auxTime.toString(DataMonitorUtils::getDateTimeFormat())); foreach(QString monitor, m_dataAcquisitionManager->getActiveMonitors()) { auto auxVal = m_dataAcquisitionManager->getDataMonitorMap()->value(monitor)->getValueAtTime( - timeValues->at(i)); + *i); + // verify if monitor has a value this time value if no value is found leave an empty + // space in the file QString val = ", "; if(auxVal != -Q_INFINITY) { val += QString::number(auxVal); @@ -146,10 +128,9 @@ void LogDataToFile::logData(QString path) } } + // write the data to file exportStream << tableHead << "\n"; - auto iterator = values.begin(); - while(iterator != values.end()) { exportStream << iterator.key() << iterator.value() << "\n"; iterator++; @@ -165,7 +146,7 @@ void LogDataToFile::logData(QString path) Q_EMIT logDataCompleted(); } -void LogDataToFile::loadData(QString path) +void LogDataToFile::loadData(QString path, bool override) { Q_EMIT startLoadData(); @@ -175,22 +156,58 @@ void LogDataToFile::loadData(QString path) file.open(QIODevice::ReadOnly); QTextStream in(&file); + // first line of the file contains the value "Time" and all monitors stored each monitor represents a + // column auto channels = in.readLine().split(","); + + QVector *timeVector = new QVector(); + QMap *> valueMap; + while(!in.atEnd()) { QString line = in.readLine(); - auto values = line.split(","); + QStringList values = line.split(","); + // first column is always the time value QString time = values[0]; QDateTime dateTime = QDateTime::fromString(time, DataMonitorUtils::getDateTimeFormat()); + + timeVector->push_back(QwtDate::toDouble(dateTime)); + // for(int i = 1; i < channels.length(); i++) { - // there is an extra space in each name we need to remove it QString ch = channels[i].simplified(); + // make sure there is no extra spaces in the name ch.replace(" ", ""); - if(m_dataAcquisitionManager->getDataMonitorMap()->contains(ch)) { - m_dataAcquisitionManager->getDataMonitorMap()->value(ch)->setValueAtTime( - QwtDate::toDouble(dateTime), values[i].toDouble()); + // we store data to coresponding monitor + if(values[i] != " ") { + if(!valueMap.contains(ch)) { + valueMap.insert(ch, new QVector); + } + valueMap.value(ch)->push_back(values[i].toDouble()); } } } + + // for each monitor found in the file handle it's data according to settings + + QMap *>::iterator it; + + if(override) { + for(it = valueMap.begin(); it != valueMap.end(); ++it) { + addDataToCorespondingMonitor(it.key(), timeVector, it.value()); + } + } else { + // if we don't override data we create dummy monitors for all channels from file + for(it = valueMap.begin(); it != valueMap.end(); ++it) { + DataMonitorModel *channelModel = new DataMonitorModel( + QString("Loaded:" + it.key()), StyleHelper::getColor("ScopyBlue")); + + channelModel->setShortName(it.key()); + channelModel->setXdata(*timeVector); + channelModel->setYdata(*it.value()); + channelModel->setIsDummyMonitor(true); + m_dataAcquisitionManager->addMonitor(channelModel); + } + } + } else { qDebug() << "File already opened! "; } @@ -200,3 +217,71 @@ void LogDataToFile::loadData(QString path) Q_EMIT loadDataCompleted(); } + +void LogDataToFile::addDataToCorespondingMonitor(QString monitorName, QVector *time, QVector *val) +{ + if(!m_dataAcquisitionManager->getDataMonitorMap()->contains(monitorName)) { + // TODO consider option to merge + DataMonitorModel *channelModel = + new DataMonitorModel(QString("Loaded:" + monitorName), StyleHelper::getColor("ScopyBlue")); + channelModel->setShortName(monitorName); + channelModel->setIsDummyMonitor(true); + m_dataAcquisitionManager->addMonitor(channelModel); + } + + QVector *sortedTime = new QVector(); + QVector *sortedValue = new QVector(); + + int currentTimeIdx = 0; + int newTimeIdx = 0; + + QVector *xData = m_dataAcquisitionManager->getDataMonitorMap()->value(monitorName)->getXdata(); + int xDataLength = xData->length(); + + if(xDataLength == 0) { + sortedTime = time; + sortedValue = val; + } else { + while(newTimeIdx < time->length() && currentTimeIdx < xDataLength) { + if(time->at(newTimeIdx) < xData->at(currentTimeIdx)) { + sortedTime->push_back(time->at(newTimeIdx)); + sortedValue->push_back(val->at(newTimeIdx)); + newTimeIdx++; + } else if(time->at(newTimeIdx) > xData->at(currentTimeIdx)) { + sortedTime->push_back(xData->at(currentTimeIdx)); + sortedValue->push_back(m_dataAcquisitionManager->getDataMonitorMap() + ->value(monitorName) + ->getValueAtTime(xData->at(currentTimeIdx))); + currentTimeIdx++; + } else if(time->at(newTimeIdx) == xData->at(currentTimeIdx)) { + // if the value is for same time we keep the one in the file + sortedTime->push_back(time->at(newTimeIdx)); + sortedValue->push_back(val->at(newTimeIdx)); + newTimeIdx++; + currentTimeIdx++; + } + } + + // if any vectors still have values add them + // ???needed ??? + while(newTimeIdx < time->length()) { + // add all missing items + sortedTime->push_back(time->at(newTimeIdx)); + sortedValue->push_back(val->at(newTimeIdx)); + newTimeIdx++; + } + + while(currentTimeIdx < xDataLength) { + // add all missing items + sortedTime->push_back(xData->at(currentTimeIdx)); + sortedValue->push_back(m_dataAcquisitionManager->getDataMonitorMap() + ->value(monitorName) + ->getValueAtTime(xData->at(currentTimeIdx))); + currentTimeIdx++; + } + } + m_dataAcquisitionManager->getDataMonitorMap()->value(monitorName)->setXdata(*sortedTime); + m_dataAcquisitionManager->getDataMonitorMap()->value(monitorName)->setYdata(*sortedValue); + + Q_EMIT loadDataCompleted(); +}