diff --git a/HopsanGUI/Configuration.cpp b/HopsanGUI/Configuration.cpp index 4b438f431..573690b95 100644 --- a/HopsanGUI/Configuration.cpp +++ b/HopsanGUI/Configuration.cpp @@ -1351,6 +1351,7 @@ void Configuration::registerSettings() mBoolSettings.insert(CFG_SETPWDTOMWD, false); mBoolSettings.insert(CFG_SHOWLICENSEONSTARTUP, true); mBoolSettings.insert(CFG_CHECKFORDEVELOPMENTUPDATES, false); + mBoolSettings.insert(CFG_LOGDURINGSIMULATION, false); #ifdef _WIN32 mBoolSettings.insert(CFG_PREFERINCLUDEDCOMPILER, true); #else @@ -1365,6 +1366,7 @@ void Configuration::registerSettings() mIntegerSettings.insert(CFG_PLOEXPORTVERSION, 1); mIntegerSettings.insert(CFG_REMOTESHORTTIMEOUT, 5); mIntegerSettings.insert(CFG_REMOTELONGTIMEOUT, 30); + mIntegerSettings.insert(CFG_LOGSTEPS, 100); // Double settings mDoubleSettings.insert(CFG_PLOTGFXDPI, 96.0); diff --git a/HopsanGUI/Configuration.h b/HopsanGUI/Configuration.h index c201d8cb2..51bd087e8 100644 --- a/HopsanGUI/Configuration.h +++ b/HopsanGUI/Configuration.h @@ -62,6 +62,8 @@ #define CFG_AUTOLIMITGENERATIONS "autolimitgenerations" #define CFG_SETPWDTOMWD "setpwdtomwd" #define CFG_PLOTWINDOWSONTOP "plotwindowsontop" +#define CFG_LOGDURINGSIMULATION "logduringsimulation" +#define CFG_LOGSTEPS "logsteps" #define CFG_PLOTGFXIMAGEFORMAT "plotgfximageformat" #define CFG_PLOTGFXDIMENSIONSUNIT "plotgfxdimensionsunit" diff --git a/HopsanGUI/Dialogs/OptionsDialog.cpp b/HopsanGUI/Dialogs/OptionsDialog.cpp index 75ab34cc2..e7f954468 100644 --- a/HopsanGUI/Dialogs/OptionsDialog.cpp +++ b/HopsanGUI/Dialogs/OptionsDialog.cpp @@ -345,6 +345,15 @@ OptionsDialog::OptionsDialog(QWidget *parent) mpThreadsSpinBox->setMaximum(1000000); mpThreadsSpinBox->setSingleStep(1); + mpLogDuringSimulationCheckBox = new QCheckBox(tr("Collect Log Data During Simulation")); + mpLogDuringSimulationCheckBox->setCheckable(true); + + mpLogStepsLabel = new QLabel(tr("Logging interval (simulation steps):")); + mpLogStepsSpinBox = new QSpinBox(); + mpLogStepsSpinBox->setMinimum(1); + mpLogStepsSpinBox->setMaximum(INT_MAX); + mpLogStepsSpinBox->setSingleStep(1); + //mpThreadsWarningLabel = new QLabel(tr("Caution! Choosing more threads than the number of processor cores may be unstable on some systems.")); //mpThreadsWarningLabel->setWordWrap(true); //QPalette palette = mpThreadsWarningLabel->palette(); @@ -360,8 +369,11 @@ OptionsDialog::OptionsDialog(QWidget *parent) pSimulationLayout->addWidget(mpUseMulticoreCheckBox, 2, 0, 1, 2); pSimulationLayout->addWidget(mpThreadsLabel, 3, 0); pSimulationLayout->addWidget(mpThreadsSpinBox, 3, 1); - pSimulationLayout->addWidget(new QWidget(), 4, 0, 1, 2); - pSimulationLayout->setRowStretch(4, 1); + pSimulationLayout->addWidget(mpLogDuringSimulationCheckBox, 4, 0, 1, 2); + pSimulationLayout->addWidget(mpLogStepsLabel, 5, 0); + pSimulationLayout->addWidget(mpLogStepsSpinBox, 5, 1); + pSimulationLayout->addWidget(new QWidget(), 6, 0, 1, 2); + pSimulationLayout->setRowStretch(6, 1); //mpSimulationLayout->addWidget(mpThreadsWarningLabel, 4, 0, 1, 2); mpSimulationWidget->setLayout(pSimulationLayout); @@ -536,6 +548,8 @@ OptionsDialog::OptionsDialog(QWidget *parent) connect(mpUseMulticoreCheckBox, SIGNAL(toggled(bool)), mpThreadsLabel, SLOT(setEnabled(bool))); connect(mpUseMulticoreCheckBox, SIGNAL(toggled(bool)), mpThreadsSpinBox, SLOT(setEnabled(bool))); + connect(mpLogDuringSimulationCheckBox, SIGNAL(toggled(bool)), mpLogStepsLabel, SLOT(setEnabled(bool))); + connect(mpLogDuringSimulationCheckBox, SIGNAL(toggled(bool)), mpLogStepsSpinBox, SLOT(setEnabled(bool))); QTabWidget *pTabWidget = new QTabWidget(this); pTabWidget->addTab(mpInterfaceWidget, "Interface"); @@ -623,6 +637,8 @@ void OptionsDialog::setValues() gpConfig->setIntegerSetting(CFG_PROGRESSBARSTEP, mpProgressBarSpinBox->value()); gpConfig->setBoolSetting(CFG_MULTICORE, mpUseMulticoreCheckBox->isChecked()); gpConfig->setIntegerSetting(CFG_NUMBEROFTHREADS, mpThreadsSpinBox->value()); + gpConfig->setBoolSetting(CFG_LOGDURINGSIMULATION, mpLogDuringSimulationCheckBox->isChecked()); + gpConfig->setIntegerSetting(CFG_LOGSTEPS, mpLogStepsSpinBox->value()); gpConfig->setBoolSetting(CFG_AUTOLIMITGENERATIONS, mpAutoLimitGenerationsCheckBox->isChecked()); gpConfig->setBoolSetting(CFG_SHOWHIDDENNODEDATAVARIABLES, mpShowHiddenNodeDataVarCheckBox->isChecked()); gpConfig->setBoolSetting(CFG_PLOTWINDOWSONTOP, mpPlotWindowsOnTop->isChecked()); @@ -733,6 +749,10 @@ void OptionsDialog::show() mpUseMulticoreCheckBox->setChecked(gpConfig->getBoolSetting(CFG_MULTICORE)); mpThreadsSpinBox->setValue(gpConfig->getIntegerSetting(CFG_NUMBEROFTHREADS)); mpThreadsLabel->setEnabled(gpConfig->getBoolSetting(CFG_MULTICORE)); + mpLogDuringSimulationCheckBox->setChecked(gpConfig->getBoolSetting(CFG_LOGDURINGSIMULATION)); + mpLogStepsLabel->setEnabled(gpConfig->getBoolSetting(CFG_LOGDURINGSIMULATION)); + mpLogStepsSpinBox->setEnabled(gpConfig->getBoolSetting(CFG_LOGDURINGSIMULATION)); + mpLogStepsSpinBox->setValue(gpConfig->getIntegerSetting(CFG_LOGSTEPS)); mpGenerationLimitSpinBox->setValue(gpConfig->getIntegerSetting(CFG_GENERATIONLIMIT)); mpDefaultPloExportVersion->setValue(gpConfig->getIntegerSetting(CFG_PLOEXPORTVERSION)); mpAutoLimitGenerationsCheckBox->setChecked(gpConfig->getBoolSetting(CFG_AUTOLIMITGENERATIONS)); diff --git a/HopsanGUI/Dialogs/OptionsDialog.h b/HopsanGUI/Dialogs/OptionsDialog.h index 510aaeeef..3e56930b7 100644 --- a/HopsanGUI/Dialogs/OptionsDialog.h +++ b/HopsanGUI/Dialogs/OptionsDialog.h @@ -89,6 +89,9 @@ private slots: QLabel *mpThreadsLabel; QSpinBox *mpThreadsSpinBox; QSpinBox *mpProgressBarSpinBox; + QCheckBox *mpLogDuringSimulationCheckBox; + QLabel *mpLogStepsLabel; + QSpinBox *mpLogStepsSpinBox; QWidget *mpUnitScaleWidget; diff --git a/HopsanGUI/LogDataHandler2.cpp b/HopsanGUI/LogDataHandler2.cpp index 30cec4665..9af2a9123 100644 --- a/HopsanGUI/LogDataHandler2.cpp +++ b/HopsanGUI/LogDataHandler2.cpp @@ -106,6 +106,11 @@ ModelWidget *LogDataHandler2::getParentModel() return mpParentModel; } +void LogDataHandler2::createEmptyGeneration() +{ + ++mCurrentGenerationNumber; +} + void LogDataHandler2::exportToPlo(const QString &rFilePath, QList variables, int version) const { diff --git a/HopsanGUI/LogDataHandler2.h b/HopsanGUI/LogDataHandler2.h index 989bce681..1e8c71feb 100644 --- a/HopsanGUI/LogDataHandler2.h +++ b/HopsanGUI/LogDataHandler2.h @@ -58,6 +58,7 @@ class LogDataHandler2 : public QObject void setParentModel(ModelWidget *pParentModel); ModelWidget *getParentModel(); + void createEmptyGeneration(); void collectLogDataFromModel(bool overWriteLastGeneration=false); void collectLogDataFromRemoteModel(QVector &rResultVariables, bool overWriteLastGeneration=false); void importFromPlo(QString importFilePath=QString()); diff --git a/HopsanGUI/SimulationThreadHandler.cpp b/HopsanGUI/SimulationThreadHandler.cpp index 013463707..50cf7ee02 100644 --- a/HopsanGUI/SimulationThreadHandler.cpp +++ b/HopsanGUI/SimulationThreadHandler.cpp @@ -98,14 +98,41 @@ void LocalSimulationWorkerObject::initSimulateFinalize() { simulateSuccess = simuHandler.simulate(mStartTime, mStopTime, gpConfig->getIntegerSetting(CFG_NUMBEROFTHREADS), coreSystemAccessVector, mNoChanges); } - else if (gpConfig->getUseMulticore()) + else if (gpConfig->getUseMulticore() && !gpConfig->getBoolSetting(CFG_LOGDURINGSIMULATION)) { // Choose if we should simulate each system (or just the one system) using multiple cores (but each system in sequence) timer.start(); simulateSuccess = simuHandler.simulate(mStartTime, mStopTime, gpConfig->getIntegerSetting(CFG_NUMBEROFTHREADS), coreSystemAccessVector, mNoChanges); } - else + else if (gpConfig->getUseMulticore() && gpConfig->getBoolSetting(CFG_LOGDURINGSIMULATION)) + { + // Choose if we should simulate each system (or just the one system) using multiple cores (but each system in sequence) + int logSteps = gpConfig->getBoolSetting(CFG_LOGSTEPS); + timer.start(); + double time = mStartTime; + simulateSuccess = true; + bool noChanges = mNoChanges; + for(int i=0; igetIntegerSetting(CFG_NUMBEROFTHREADS), coreSystemAccessVector, mNoChanges); + } + else if(!gpConfig->getUseMulticore() && gpConfig->getBoolSetting(CFG_LOGDURINGSIMULATION)) { + int logSteps = gpConfig->getIntegerSetting(CFG_LOGSTEPS); + timer.start(); + double time = mStartTime; + simulateSuccess = true; + for(int i=0; igetBoolSetting(CFG_LOGDURINGSIMULATION)) { + connect(mpSimulationWorkerObject, SIGNAL(stepFinished()), this, SIGNAL(stepFinished())); + } connect(mpCheckMessagesTimer, &QTimer::timeout, mpMessageHandler, &GUIMessageHandler::collectHopsanCoreMessages); mpCheckMessagesTimer->setSingleShot(false); mpCheckMessagesTimer->start(1000); diff --git a/HopsanGUI/SimulationThreadHandler.h b/HopsanGUI/SimulationThreadHandler.h index 92015d095..75229f2fb 100644 --- a/HopsanGUI/SimulationThreadHandler.h +++ b/HopsanGUI/SimulationThreadHandler.h @@ -75,6 +75,7 @@ public slots: void setProgressState(SimulationState); void initDone(bool, int); void simulateDone(bool, int); + void stepFinished(); void finalizeDone(bool, int); }; @@ -206,6 +207,7 @@ protected slots: signals: void startSimulation(); + void stepFinished(); void done(bool); }; diff --git a/HopsanGUI/Widgets/ModelWidget.cpp b/HopsanGUI/Widgets/ModelWidget.cpp index 888e83bb2..0f742b251 100644 --- a/HopsanGUI/Widgets/ModelWidget.cpp +++ b/HopsanGUI/Widgets/ModelWidget.cpp @@ -123,6 +123,7 @@ ModelWidget::ModelWidget(ModelHandler *pModelHandler, CentralTabWidget *pParentT setMessageHandler(gpMessageHandler); connect(mpSimulationThreadHandler, SIGNAL(done(bool)), this, SIGNAL(simulationFinished())); + connect(mpSimulationThreadHandler, SIGNAL(stepFinished()), this, SLOT(collectAndAppendPlotData())); connect(this, SIGNAL(simulationFinished()), this, SLOT(collectPlotData()), Qt::UniqueConnection); connect(this, SIGNAL(simulationFinished()), this, SLOT(unlockSimulateMutex())); connect(this, SIGNAL(modelChanged(ModelWidget*)), mpParentModelHandler, SIGNAL(modelChanged(ModelWidget*))); @@ -621,8 +622,14 @@ bool ModelWidget::simulate_nonblocking() if(!mSimulateMutex.tryLock()) return false; qDebug() << "Calling simulate_nonblocking()"; + if(gpConfig->getBoolSetting(CFG_LOGDURINGSIMULATION)) { + prepareForLogDuringSimulation(); + } mpSimulationThreadHandler->setSimulationTimeVariables(mStartTime.toDouble(), mStopTime.toDouble(), mpToplevelSystem->getLogStartTime(), mpToplevelSystem->getNumberOfLogSamples()); mpSimulationThreadHandler->initSimulateFinalize(mpToplevelSystem); + if(gpConfig->getBoolSetting(CFG_LOGDURINGSIMULATION)) { + cleanupAfterLogDuringSimulation(); + } } return true; @@ -1043,6 +1050,22 @@ void ModelWidget::lockModelEditingLimited(bool lock) } +void ModelWidget::prepareForLogDuringSimulation() +{ + mpLogDataHandler->createEmptyGeneration(); + disconnect(this, SIGNAL(simulationFinished()), this, SLOT(collectPlotData())); +} + +void ModelWidget::cleanupAfterLogDuringSimulation() +{ + connect(this, SIGNAL(simulationFinished()), this, SLOT(collectPlotData()), Qt::UniqueConnection); +} + +void ModelWidget::collectAndAppendPlotData() +{ + collectPlotData(true); +} + //! @brief Slot that tells the current system to collect plot data from core void ModelWidget::collectPlotData(bool overWriteGeneration) { diff --git a/HopsanGUI/Widgets/ModelWidget.h b/HopsanGUI/Widgets/ModelWidget.h index de1808c09..d45a209eb 100644 --- a/HopsanGUI/Widgets/ModelWidget.h +++ b/HopsanGUI/Widgets/ModelWidget.h @@ -132,6 +132,9 @@ public slots: void lockModelEditingFull(bool lock); void lockModelEditingLimited(bool lock); void openAnimation(); + void prepareForLogDuringSimulation(); + void cleanupAfterLogDuringSimulation(); + void collectAndAppendPlotData(); void collectPlotData(bool overWriteGeneration=false); void setUseRemoteSimulation(bool tf); void revertModel(); @@ -145,6 +148,7 @@ private slots: void simulationTimeChanged(QString start, QString ts, QString stop); void checkMessages(); void simulationFinished(); + void simulationStepFinished(); void modelSaved(ModelWidget*); void aliasChanged(QString fullName, QString alias); void quantityChanged(QString fullName, QString quantity);