From bf2a852b4d53a54486c74512a87b3c32772862df Mon Sep 17 00:00:00 2001 From: Kristian Bendiksen Date: Wed, 14 Aug 2024 16:54:41 +0200 Subject: [PATCH] Add cancellation of well path collection loading. --- .../Tools/Cloud/RiaOsduConnector.cpp | 19 ++++++++ .../Tools/Cloud/RiaOsduConnector.h | 4 ++ .../WellPath/RimOsduWellPathDataLoader.cpp | 14 ++++++ .../WellPath/RimOsduWellPathDataLoader.h | 1 + .../WellPath/RimWellPathCollection.cpp | 33 +++++++++---- Fwk/AppFwk/cafDataLoader/CMakeLists.txt | 4 +- .../cafDataLoader/cafDataLoadController.cpp | 13 +++++ .../cafDataLoader/cafDataLoadController.h | 1 + Fwk/AppFwk/cafDataLoader/cafDataLoader.cpp | 7 +++ Fwk/AppFwk/cafDataLoader/cafDataLoader.h | 2 + .../cafUserInterface/cafProgressInfo.cpp | 48 ++++++++++++++++--- Fwk/AppFwk/cafUserInterface/cafProgressInfo.h | 16 ++++++- 12 files changed, 143 insertions(+), 19 deletions(-) diff --git a/ApplicationLibCode/Application/Tools/Cloud/RiaOsduConnector.cpp b/ApplicationLibCode/Application/Tools/Cloud/RiaOsduConnector.cpp index d2f3fd0329..5915450de0 100644 --- a/ApplicationLibCode/Application/Tools/Cloud/RiaOsduConnector.cpp +++ b/ApplicationLibCode/Application/Tools/Cloud/RiaOsduConnector.cpp @@ -762,6 +762,9 @@ void RiaOsduConnector::requestParquetData( const QString& url, const QString& da RiaLogging::info( "Requesting download of parquet from: " + url ); auto reply = makeDownloadRequest( url, dataPartitionId, token, RiaCloudDefines::contentTypeParquet() ); + m_repliesMutex.lock(); + m_replies[id] = reply; + m_repliesMutex.unlock(); connect( reply, &QNetworkReply::finished, @@ -792,3 +795,19 @@ void RiaOsduConnector::parquetDownloadComplete( const QByteArray& contents, cons m_parquetData[id] = contents; m_parquetErrors[id] = errorMessage; } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RiaOsduConnector::cancelRequestForId( const QString& id ) +{ + QMutexLocker lock( &m_repliesMutex ); + auto it = m_replies.find( id ); + if ( it != m_replies.end() ) + { + if ( !it->second.isNull() ) + { + it->second->abort(); + } + } +} diff --git a/ApplicationLibCode/Application/Tools/Cloud/RiaOsduConnector.h b/ApplicationLibCode/Application/Tools/Cloud/RiaOsduConnector.h index bbb4e2af85..6095768093 100644 --- a/ApplicationLibCode/Application/Tools/Cloud/RiaOsduConnector.h +++ b/ApplicationLibCode/Application/Tools/Cloud/RiaOsduConnector.h @@ -113,6 +113,8 @@ public: QString wellIdForWellboreId( const QString& wellboreId ) const; + void cancelRequestForId( const QString& id ); + void clearCachedData(); QString dataPartition() const; @@ -173,6 +175,7 @@ private: const QString m_dataPartitionId; mutable QMutex m_mutex; + mutable QMutex m_repliesMutex; std::vector m_fields; std::vector m_wells; std::map> m_wellbores; @@ -180,4 +183,5 @@ private: std::map> m_wellLogs; std::map m_parquetData; std::map m_parquetErrors; + std::map> m_replies; }; diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimOsduWellPathDataLoader.cpp b/ApplicationLibCode/ProjectDataModel/WellPath/RimOsduWellPathDataLoader.cpp index b823e7b51c..ac8c957607 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimOsduWellPathDataLoader.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimOsduWellPathDataLoader.cpp @@ -79,6 +79,20 @@ bool RimOsduWellPathDataLoader::isRunnable() const return false; } +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void RimOsduWellPathDataLoader::cancel() +{ + RiaApplication* app = RiaApplication::instance(); + RiaOsduConnector* osduConnector = app->makeOsduConnector(); + + for ( auto& [trajectoryId, taskId] : m_taskIds ) + { + osduConnector->cancelRequestForId( trajectoryId ); + } +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimOsduWellPathDataLoader.h b/ApplicationLibCode/ProjectDataModel/WellPath/RimOsduWellPathDataLoader.h index 230cb7c6cf..58ded9b6fd 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimOsduWellPathDataLoader.h +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimOsduWellPathDataLoader.h @@ -38,6 +38,7 @@ public: RimOsduWellPathDataLoader(); void loadData( caf::PdmObject& pdmObject, const QString& dataType, int taskId, caf::ProgressInfo& progressInfo ) override; bool isRunnable() const override; + void cancel() override; private slots: void parquetDownloadComplete( const QByteArray& contents, const QString& url, const QString& id ); diff --git a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.cpp b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.cpp index 3b25cfc7e6..406f780326 100644 --- a/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/WellPath/RimWellPathCollection.cpp @@ -151,10 +151,6 @@ void RimWellPathCollection::fieldChangedByUi( const caf::PdmFieldHandle* changed //-------------------------------------------------------------------------------------------------- void RimWellPathCollection::loadDataAndUpdate() { - caf::ProgressInfo progress( 3, "Reading well paths from file" ); - - readWellPathFormationFiles(); - auto hasOsduData = []( const std::vector& wellPaths ) -> bool { for ( RimWellPath* wellPath : wellPaths ) @@ -168,6 +164,22 @@ void RimWellPathCollection::loadDataAndUpdate() return false; }; + auto countWellLogs = []( const std::vector& wellPaths ) -> bool + { + size_t count = 0; + for ( RimWellPath* wellPath : wellPaths ) + { + count += wellPath->wellLogs().size(); + } + return count; + }; + + caf::ProgressInfo progress( allWellPaths().size() + countWellLogs( allWellPaths() ) + 2, "Reading well paths from file", false, true ); + + readWellPathFormationFiles(); + + progress.incrementProgress(); + if ( hasOsduData( allWellPaths() ) ) { auto osduConnector = RiaApplication::instance()->makeOsduConnector(); @@ -185,7 +197,11 @@ void RimWellPathCollection::loadDataAndUpdate() dataLoadController->loadData( *wellPath, wellPathGeometryKeyword, progress ); } dataLoadController->blockUntilDone( wellPathGeometryKeyword ); - progress.incrementProgress(); + + if ( progress.isCancelled() ) + { + return; + } progress.setProgressDescription( QString( "Reading well logs." ) ); for ( RimWellPath* wellPath : allWellPaths() ) @@ -196,7 +212,6 @@ void RimWellPathCollection::loadDataAndUpdate() } } dataLoadController->blockUntilDone( wellLogKeyword ); - progress.incrementProgress(); progress.setProgressDescription( QString( "Reading additional data." ) ); for ( RimWellPath* wellPath : allWellPaths() ) @@ -692,15 +707,15 @@ void RimWellPathCollection::readWellPathFormationFiles() { caf::ProgressInfo progress( m_wellPaths.size(), "Reading well picks from file" ); - for ( size_t wpIdx = 0; wpIdx < m_wellPaths.size(); wpIdx++ ) + for ( const auto& wellPath : m_wellPaths ) { QString errorMessage; - if ( !m_wellPaths[wpIdx]->readWellPathFormationsFile( &errorMessage, m_wellPathFormationsImporter.get() ) ) + if ( !wellPath->readWellPathFormationsFile( &errorMessage, m_wellPathFormationsImporter.get() ) ) { RiaLogging::errorInMessageBox( Riu3DMainWindowTools::mainWindowWidget(), "File open error", errorMessage ); } - progress.setProgressDescription( QString( "Reading formation file %1" ).arg( wpIdx ) ); + progress.setProgressDescription( QString( "Reading formation file for %1" ).arg( wellPath->name() ) ); progress.incrementProgress(); } } diff --git a/Fwk/AppFwk/cafDataLoader/CMakeLists.txt b/Fwk/AppFwk/cafDataLoader/CMakeLists.txt index a4296954fe..28a736c7e8 100644 --- a/Fwk/AppFwk/cafDataLoader/CMakeLists.txt +++ b/Fwk/AppFwk/cafDataLoader/CMakeLists.txt @@ -31,7 +31,9 @@ set(PROJECT_FILES add_library(${PROJECT_NAME} ${PROJECT_FILES}) -target_link_libraries(${PROJECT_NAME} cafProjectDataModel ${QT_LIBRARIES}) +target_link_libraries( + ${PROJECT_NAME} cafProjectDataModel cafUserInterface ${QT_LIBRARIES} +) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/Fwk/AppFwk/cafDataLoader/cafDataLoadController.cpp b/Fwk/AppFwk/cafDataLoader/cafDataLoadController.cpp index 04313323ae..1d5d3b200d 100644 --- a/Fwk/AppFwk/cafDataLoader/cafDataLoadController.cpp +++ b/Fwk/AppFwk/cafDataLoader/cafDataLoadController.cpp @@ -39,6 +39,7 @@ #include "cafDataLoadTask.h" #include "cafDataLoader.h" #include "cafPdmObject.h" +#include "cafProgressInfo.h" #include #include @@ -87,6 +88,7 @@ void DataLoadController::loadData( caf::PdmObject& object, const QString& dataTy { QMutexLocker locker( &m_mutex ); m_pendingTasksByType[dataType]++; + m_progressInfos[dataType] = &progressInfo; locker.unlock(); if ( it->second->isRunnable() ) @@ -117,6 +119,16 @@ void DataLoadController::blockUntilDone( const QString& dataType ) { QMutexLocker locker( &m_mutex ); numPending = m_pendingTasksByType[dataType]; + if ( m_progressInfos[dataType]->isCancelled() ) + { + for ( auto& [key, loader] : m_dataLoaders ) + { + if ( key.second == dataType ) + { + loader->cancel(); + } + } + } } QApplication::processEvents(); @@ -131,4 +143,5 @@ void DataLoadController::onTaskFinished( const caf::SignalEmitter* emitter, QStr { QMutexLocker locker( &m_mutex ); m_pendingTasksByType[dataType]--; + m_progressInfos[dataType]->incrementProgress(); } diff --git a/Fwk/AppFwk/cafDataLoader/cafDataLoadController.h b/Fwk/AppFwk/cafDataLoader/cafDataLoadController.h index 2ac62c7662..4fb5b79dfa 100644 --- a/Fwk/AppFwk/cafDataLoader/cafDataLoadController.h +++ b/Fwk/AppFwk/cafDataLoader/cafDataLoadController.h @@ -77,6 +77,7 @@ private: std::map, std::unique_ptr> m_dataLoaders; std::map m_pendingTasksByType; + std::map m_progressInfos; int m_taskId; QMutex m_mutex; diff --git a/Fwk/AppFwk/cafDataLoader/cafDataLoader.cpp b/Fwk/AppFwk/cafDataLoader/cafDataLoader.cpp index 0f05a0d850..46a32fc08b 100644 --- a/Fwk/AppFwk/cafDataLoader/cafDataLoader.cpp +++ b/Fwk/AppFwk/cafDataLoader/cafDataLoader.cpp @@ -45,3 +45,10 @@ DataLoader::DataLoader() : taskDone( this ) { } + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +void DataLoader::cancel() +{ +} diff --git a/Fwk/AppFwk/cafDataLoader/cafDataLoader.h b/Fwk/AppFwk/cafDataLoader/cafDataLoader.h index 737d426f01..9467e914ee 100644 --- a/Fwk/AppFwk/cafDataLoader/cafDataLoader.h +++ b/Fwk/AppFwk/cafDataLoader/cafDataLoader.h @@ -59,6 +59,8 @@ public: virtual bool isRunnable() const = 0; + virtual void cancel(); + caf::Signal taskDone; }; diff --git a/Fwk/AppFwk/cafUserInterface/cafProgressInfo.cpp b/Fwk/AppFwk/cafUserInterface/cafProgressInfo.cpp index 7980caa197..59c4706441 100644 --- a/Fwk/AppFwk/cafUserInterface/cafProgressInfo.cpp +++ b/Fwk/AppFwk/cafUserInterface/cafProgressInfo.cpp @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -71,6 +72,7 @@ ProgressTask::ProgressTask( ProgressInfo& parentTask ) : m_parentTask( parentTask ) { } + ProgressTask::~ProgressTask() { m_parentTask.incrementProgress(); @@ -125,9 +127,11 @@ ProgressTask::~ProgressTask() /// If you do not need a title for a particular level, simply pass "" and it will be ignored. /// \sa setProgressDescription //-------------------------------------------------------------------------------------------------- -ProgressInfo::ProgressInfo( size_t maxProgressValue, const QString& title, bool delayShowingProgress ) +ProgressInfo::ProgressInfo( size_t maxProgressValue, const QString& title, bool delayShowingProgress, bool allowCancel ) { - ProgressInfoStatic::start( maxProgressValue, title, delayShowingProgress ); + m_isCancelled.store( false ); + + ProgressInfoStatic::start( *this, maxProgressValue, title, delayShowingProgress, allowCancel ); if ( dynamic_cast( QCoreApplication::instance() ) ) { @@ -195,6 +199,16 @@ void ProgressInfo::setNextProgressIncrement( size_t nextStepSize ) ProgressInfoStatic::setNextProgressIncrement( nextStepSize ); } +void ProgressInfo::cancel() +{ + m_isCancelled.store( true ); +} + +bool ProgressInfo::isCancelled() const +{ + return m_isCancelled; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- @@ -462,13 +476,18 @@ ProgressInfoBlocker::~ProgressInfoBlocker() /// //================================================================================================== -bool ProgressInfoStatic::s_disabled = false; -bool ProgressInfoStatic::s_running = false; +bool ProgressInfoStatic::s_disabled = false; +bool ProgressInfoStatic::s_running = false; +bool ProgressInfoStatic::s_isButtonConnected = false; //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -void ProgressInfoStatic::start( size_t maxProgressValue, const QString& title, bool delayShowingProgress ) +void ProgressInfoStatic::start( ProgressInfo& progressInfo, + size_t maxProgressValue, + const QString& title, + bool delayShowingProgress, + bool allowCancel ) { if ( !isUpdatePossible() ) return; @@ -485,7 +504,20 @@ void ProgressInfoStatic::start( size_t maxProgressValue, const QString& title, b { dialog->setMinimum( 0 ); dialog->setWindowTitle( title ); - dialog->setCancelButton( nullptr ); + if ( allowCancel ) + { + dialog->setCancelButtonText( "Cancel" ); + if ( !s_isButtonConnected ) + { + QObject::connect( dialog, &QProgressDialog::canceled, [&progressInfo]() { progressInfo.cancel(); } ); + s_isButtonConnected = true; + } + } + else + { + dialog->setCancelButton( nullptr ); + } + if ( delayShowingProgress ) { dialog->setMinimumDuration( 2000 ); @@ -548,7 +580,7 @@ void ProgressInfoStatic::setProgress( size_t progressValue ) if ( progressValue > maxProgressStack_v.back() ) { reportError( "setProgress() is called with a progressValue > max, progressValue == " + - std::to_string( progressValue ) ); + std::to_string( progressValue ) + " max == " + std::to_string( maxProgressStack_v.back() ) ); progressValue = maxProgressStack_v.back(); } @@ -662,6 +694,8 @@ void ProgressInfoStatic::finished() { if ( dialog ) { + QObject::disconnect( dialog, &QProgressDialog::canceled, nullptr, nullptr ); + dialog->reset(); dialog->close(); s_running = false; diff --git a/Fwk/AppFwk/cafUserInterface/cafProgressInfo.h b/Fwk/AppFwk/cafUserInterface/cafProgressInfo.h index 321716f450..48a50170a0 100644 --- a/Fwk/AppFwk/cafUserInterface/cafProgressInfo.h +++ b/Fwk/AppFwk/cafUserInterface/cafProgressInfo.h @@ -38,6 +38,8 @@ #include +#include + class QString; namespace caf @@ -57,15 +59,20 @@ private: class ProgressInfo { public: - ProgressInfo( size_t maxProgressValue, const QString& title, bool delayShowingProgress = true ); + ProgressInfo( size_t maxProgressValue, const QString& title, bool delayShowingProgress = true, bool allowCancel = false ); ~ProgressInfo(); void setProgressDescription( const QString& description ); void setProgress( size_t progressValue ); void incrementProgress(); void setNextProgressIncrement( size_t nextStepSize ); + void cancel(); + bool isCancelled() const; ProgressTask task( const QString& description, int stepSize = 1 ); + +private: + std::atomic m_isCancelled; }; class ProgressInfoBlocker @@ -78,7 +85,11 @@ public: class ProgressInfoStatic { public: - static void start( size_t maxProgressValue, const QString& title, bool delayShowingProgress ); + static void start( ProgressInfo& progressInfo, + size_t maxProgressValue, + const QString& title, + bool delayShowingProgress, + bool allowCance ); static void setProgressDescription( const QString& description ); static void setProgress( size_t progressValue ); @@ -95,6 +106,7 @@ private: friend class ProgressInfoBlocker; static bool s_running; static bool s_disabled; + static bool s_isButtonConnected; }; } // namespace caf