diff --git a/Tests/simulationEngines/hecras/test_hecras.cpp b/Tests/simulationEngines/hecras/test_hecras.cpp index e9d4a4df9..73b24d06b 100644 --- a/Tests/simulationEngines/hecras/test_hecras.cpp +++ b/Tests/simulationEngines/hecras/test_hecras.cpp @@ -917,6 +917,7 @@ void ReosHecrasTesting::importAndLaunchStructure() if ( !versions.isEmpty() ) { ReosHecRasController controller( versions.last() ); + controller.initialize(); QVERIFY( controller.isValid() ); @@ -928,8 +929,8 @@ void ReosHecrasTesting::importAndLaunchStructure() QCOMPARE( plans.at( 0 ), QStringLiteral( "plan_test" ) ); QCOMPARE( plans.at( 1 ), QStringLiteral( "plan_test_2" ) ); - QVERIFY( controller.setCurrentPlan( plans.at( 1 ) ) ); - QVERIFY( controller.setCurrentPlan( plans.at( 0 ) ) ); + QVERIFY( controller.setCurrentPlanPrivate( plans.at( 1 ) ) ); + QVERIFY( controller.setCurrentPlanPrivate( plans.at( 0 ) ) ); QVERIFY( !controller.computeCurrentPlan().isEmpty() ); } diff --git a/src/dataProviders/hec-dss/CMakeLists.txt b/src/dataProviders/hec-dss/CMakeLists.txt index 41ad882ab..1efa190a0 100644 --- a/src/dataProviders/hec-dss/CMakeLists.txt +++ b/src/dataProviders/hec-dss/CMakeLists.txt @@ -4,11 +4,13 @@ SET(REOS_HEC_DSS_SOURCES reosdssutils.cpp reosdssfile.cpp + reosdsswatcher.cpp ) SET(REOS_HEC_DSS_HEADERS reosdssutils.h reosdssfile.h + reosdsswatcher.h ) SET(REOS_PROVIDER_HEC_DSS_SOURCES diff --git a/src/dataProviders/hec-dss/reosdsswatcher.cpp b/src/dataProviders/hec-dss/reosdsswatcher.cpp new file mode 100644 index 000000000..d1881c79f --- /dev/null +++ b/src/dataProviders/hec-dss/reosdsswatcher.cpp @@ -0,0 +1,8 @@ +#include "reosdsswatcher.h" + + + +void ReosDssWatcher::setTimeWatchingTimeWindow( const ReosTimeWindow &timeWatchingTimeWindow ) +{ + mTimeWatchingTimeWindow = timeWatchingTimeWindow; +} diff --git a/src/dataProviders/hec-dss/reosdsswatcher.h b/src/dataProviders/hec-dss/reosdsswatcher.h new file mode 100644 index 000000000..03cf197d4 --- /dev/null +++ b/src/dataProviders/hec-dss/reosdsswatcher.h @@ -0,0 +1,124 @@ +#ifndef REOSDSSWATCHER_H +#define REOSDSSWATCHER_H + +#include +#include +#include + +#include "reosdssfile.h" + +class ReosDssWatcher : public QObject +{ + Q_OBJECT + public: + ReosDssWatcher( const QString &filePath ) + : mFilePath( filePath ) + { + connect( &mTimer, &QTimer::timeout, this, &ReosDssWatcher::watch ); + } + + void startWatch() + { + mFile.reset( new ReosDssFile( mFilePath ) ); + if ( !mFile->isValid() ) + return; + mTimer.start( mWatchInterval ); + } + + void setTimeWatchingTimeWindow( const ReosTimeWindow &newTimeWatchingTimeWindow ); + + signals: + void sendValues( QString path, QList values, qint64 timeStepMillisec ); + + private slots: + void watch() + { + + } + + private: + QString mFilePath; + std::unique_ptr mFile; + ReosTimeWindow mTimeWatchingTimeWindow; + QList mPaths; + QTimer mTimer; + int mWatchInterval = 500; + + QHash mLastObtainedTime; + + void watchForTimeSerie( const ReosDssPath &path ) + { + QVector values; + ReosDuration timeStep; + QDateTime startTime; + mFile->getSeries( path, mTimeWatchingTimeWindow, values, timeStep, startTime ); + + if ( values.count() > 0 ) + { + const QDateTime lastTimeRecorded = mLastObtainedTime.value( path.string() ); + const QDateTime lastTimeValue = startTime.addMSecs( ( values.count() - 1 ) * timeStep.valueMilliSecond() ); + if ( !lastTimeRecorded.isValid() || lastTimeValue > lastTimeRecorded ) + { + int timeStepCount; + if ( lastTimeRecorded.isValid() ) + { + timeStepCount = values.count(); + } + else + { + timeStepCount = lastTimeRecorded.msecsTo( lastTimeValue ) / timeStep.valueMilliSecond(); + } + + mLastObtainedTime.insert( path.string(), lastTimeValue ); + + QList valuesToSend; + valuesToSend.reserve( timeStepCount ); + for ( int i = values.count() - timeStepCount; i < values.count(); ++i ) + { + valuesToSend.append( values.at( i ) ); + } + + emit sendValues( path.string(), valuesToSend, timeStep.valueMilliSecond() ); + } + } + } + +}; + +class ReosDssWatcherControler : public QObject +{ + Q_OBJECT + public: + ReosDssWatcherControler( const QString &dssFilePath ) + : mFilePath( dssFilePath ) + { + + } + + ~ReosDssWatcherControler() + { + mThread.quit(); + mThread.wait(); + mWatcher->deleteLater(); + } + + void startWatching() + { + mWatcher = new ReosDssWatcher( mFilePath ); + QObject::connect( &mThread, &QThread::started, mWatcher, &ReosDssWatcher::startWatch ); + mWatcher->moveToThread( &mThread ); + } + + private slots: + void receiveFlowValues( QString &path, QList values, qint64 timeStepMillisec ) + { + + } + + private: + QThread mThread; + QString mFilePath; + ReosDssWatcher *mWatcher = nullptr; +}; + +#endif // REOSDSSWATCHER_H diff --git a/src/simulationEngines/hecras/reoshecrascontroller.cpp b/src/simulationEngines/hecras/reoshecrascontroller.cpp index 3e4235ac4..e03970090 100644 --- a/src/simulationEngines/hecras/reoshecrascontroller.cpp +++ b/src/simulationEngines/hecras/reoshecrascontroller.cpp @@ -136,8 +136,7 @@ QStringList ReosHecRasController::availableVersion() return ret; } -ReosHecRasController::ReosHecRasController( const QString &version ) - : mVersion( version ) +void ReosHecRasController::initialize() { #ifdef _WIN32 if ( !SUCCEEDED( CoInitializeEx( nullptr, COINIT_APARTMENTTHREADED ) ) ) @@ -196,6 +195,10 @@ ReosHecRasController::ReosHecRasController( const QString &version ) #endif } +ReosHecRasController::ReosHecRasController( const QString &version ) + : mVersion( version ) +{} + ReosHecRasController::~ReosHecRasController() { #ifdef _WIN32 @@ -340,7 +343,12 @@ QStringList ReosHecRasController::planNames() const return ret; } -bool ReosHecRasController::setCurrentPlan( const QString &planName ) +void ReosHecRasController::setCurrentPlan( const QString ¤tPlan ) +{ + mCurrentPlan = currentPlan; +} + +bool ReosHecRasController::setCurrentPlanPrivate( const QString &planName ) { bool ok = false; #ifdef _WIN32 @@ -376,6 +384,8 @@ bool ReosHecRasController::setCurrentPlan( const QString &planName ) QStringList ReosHecRasController::computeCurrentPlan() { + setCurrentPlanPrivate( mCurrentPlan ); + QStringList ret; #ifdef _WIN32 DISPID id = mFunctionNames.value( QStringLiteral( "Compute_CurrentPlan" ) ); @@ -669,5 +679,57 @@ bool ReosHecRasController::hideComputationWindow() const return true; else #endif - return false; + return false; +} + +void ReosHecRasController::setProjectFileName(const QString &newProjectFileName) +{ + mProjectFileName = newProjectFileName; +} + +void ReosHecRasController::startComputation() +{ + initialize(); + + if ( !isValid() ) + { + emit sendInformation( tr( "Controller of HEC-RAS found is not valid.\nCalculation cancelled." ) ); + return; + } + + if ( !openHecrasProject( mProjectFileName ) ) + { + emit sendInformation( tr( "UNable to open HEC-RAS project file \"%1\".\nCalculation cancelled." ).arg( mProjectFileName ) ); + return; + } + + QStringList plans = planNames(); + + if ( !plans.contains( mCurrentPlan ) ) + { + emit sendInformation( tr( "Plan \"%1\" not found.\nCalculation cancelled." ).arg( mCurrentPlan ) ); + return; + } + + if ( !setCurrentPlanPrivate( mCurrentPlan ) ) + { + emit sendInformation( tr( "Unable to set plan \"%1\" as current plan.\nCalculation cancelled." ).arg( mCurrentPlan ) ); + return; + } + + showComputationWindow(); + + const QStringList returnedMessages = computeCurrentPlan(); + + for ( const QString &mes : returnedMessages ) + { + emit sendInformation( mes ); + } + + mIsSuccessful = !returnedMessages.isEmpty() && returnedMessages.last() == QStringLiteral( "Computations Completed" ); +} + +bool ReosHecRasController::isSuccessful() const +{ + return mIsSuccessful; } diff --git a/src/simulationEngines/hecras/reoshecrascontroller.h b/src/simulationEngines/hecras/reoshecrascontroller.h index c19288d4b..fa1f5dcd0 100644 --- a/src/simulationEngines/hecras/reoshecrascontroller.h +++ b/src/simulationEngines/hecras/reoshecrascontroller.h @@ -24,9 +24,12 @@ #include #include +#include -class ReosHecRasController + +class ReosHecRasController : public QObject { + Q_OBJECT public: //! Constructor with \a version of HecRas, \see availableVersion() explicit ReosHecRasController( const QString &version ); @@ -38,42 +41,65 @@ class ReosHecRasController //! Returns whether the controller is valid bool isValid() const; - //! Returns the user-friendly string of the controller version of this instance - QString version() const; + //! Sets the current plan + void setCurrentPlan( const QString ¤tPlan ); - //! Opens a project with path \a projFileName - bool openHecrasProject( const QString &projFileName ); + //! Sets the current project file name + void setProjectFileName( const QString &newProjectFileName ); - //! Returns the plan names of the currently opened project - QStringList planNames() const; + bool isSuccessful() const; - //! Set the current plan - bool setCurrentPlan( const QString &planName ); +public slots: + void startComputation(); - //! Starts computation of the current plan - QStringList computeCurrentPlan(); + signals: + void sendInformation( const QString &info ); - //! Returns the flow 2D area names of the currently opened project - QStringList flowAreas2D() const; + private: + + QString mVersion; + QString mProjectFileName; + QString mCurrentPlan; + bool mIsValid = false; + bool mIsSuccessful = false; +#ifdef _WIN32 + IDispatch *mDispatch = nullptr; + QMap mFunctionNames; +#endif + + bool exitRas() const; + + bool setCurrentPlanPrivate( const QString &planName ); + + //! Initializes the controller before using it. ONce initialized, the controller must be used in the thread where is has been initializd. + void initialize(); //! Returns the domain of the flow area with \a areaName QPolygonF flow2DAreasDomain( const QString &areaName ) const; + //! Returns the flow 2D area names of the currently opened project + QStringList flowAreas2D() const; + + //! Returns the plan names of the currently opened project + QStringList planNames() const; + + //! Returns the user-friendly string of the controller version of this instance + QString version() const; + + //! Opens a project with path \a projFileName + bool openHecrasProject( const QString &projFileName ); + + //! Starts computation of the current plan + QStringList computeCurrentPlan(); + bool showRas() const; bool showComputationWindow() const; bool hideComputationWindow() const; - private: - QString mVersion; - bool mIsValid = false; -#ifdef _WIN32 - IDispatch *mDispatch = nullptr; - QMap mFunctionNames; -#endif + friend class ReosHecrasTesting; - bool exitRas() const; }; diff --git a/src/simulationEngines/hecras/reoshecrassimulation.cpp b/src/simulationEngines/hecras/reoshecrassimulation.cpp index 042f520f3..3964b9f10 100644 --- a/src/simulationEngines/hecras/reoshecrassimulation.cpp +++ b/src/simulationEngines/hecras/reoshecrassimulation.cpp @@ -28,6 +28,7 @@ #include "reosdssprovider.h" #include +#include REOSEXTERN ReosSimulationEngineFactory *engineSimulationFactory() { @@ -1022,16 +1023,6 @@ void ReosHecRasSimulationProcess::start() return; } - std::unique_ptr controller( new ReosHecRasController( mControllerVersion ) ); - - if ( !controller->isValid() ) - { - emit sendInformation( tr( "Controller of HEC-RAS found is not valid.\nCalculation cancelled." ) ); - return; - } - - emit sendInformation( tr( "Start HEC-RAS model calculation with %1." ).arg( controller->version() ) ); - QFileInfo fileProjectInfo( mProject.fileName() ); if ( !fileProjectInfo.exists() ) { @@ -1039,36 +1030,24 @@ void ReosHecRasSimulationProcess::start() return; } - if ( !controller->openHecrasProject( mProject.fileName() ) ) - { - emit sendInformation( tr( "UNable to open HEC-RAS project file \"%1\".\nCalculation cancelled." ).arg( mProject.fileName() ) ); - return; - } + QThread thread; + std::unique_ptr controller( new ReosHecRasController( mControllerVersion ) ); + controller->setCurrentPlan( mPlan.title() ); - QStringList plans = controller->planNames(); + controller->moveToThread( &thread ); - if ( !plans.contains( mPlan.title() ) ) - { - emit sendInformation( tr( "Plan \"%1\" not found.\nCalculation cancelled." ).arg( mPlan.title() ) ); - return; - } + connect( &thread, &QThread::started, controller.get(), &ReosHecRasController::startComputation ); - if ( !controller->setCurrentPlan( mPlan.title() ) ) - { - emit sendInformation( tr( "Unable to set plan \"%1\" as current plan.\nCalculation cancelled." ).arg( mPlan.title() ) ); - return; - } + QEventLoop loop; + connect( &thread, &QThread::finished, &loop, &QEventLoop::quit ); - controller->showComputationWindow(); + thread.start(); - const QStringList returnedMessages = controller->computeCurrentPlan(); + if ( thread.isRunning() ) + loop.exec(); - for ( const QString &mes : returnedMessages ) - { - emit sendInformation( mes ); - } + mIsSuccessful = controller->isSuccessful(); - mIsSuccessful = !returnedMessages.isEmpty() && returnedMessages.last() == QStringLiteral( "Computations Completed" ); } ReosHecRasStructureImporterSource::ReosHecRasStructureImporterSource( const QString &file, const ReosHydraulicNetworkContext &context )