///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2024 Equinor ASA // // ResInsight is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at // for more details. // ///////////////////////////////////////////////////////////////////////////////// #pragma once #include "RimEclipseCase.h" #include "Cloud/RiaSumoGrid.h" #include "cafPdmField.h" #include "cafPdmPtrField.h" #include "cvfObject.h" #include #include #include #include #include class RiaSumoConnector; class RifReaderSumoGridProperty; class RimSumoDataSource; namespace caf { class ProgressInfo; } //================================================================================================== // // Eclipse grid case backed by a roff grid stored on Sumo. The grid geometry is downloaded as a // blob through RiaSumoConnector and parsed in memory, so there is no grid file on disk. // //================================================================================================== class RimRoffCaseSumo : public RimEclipseCase { CAF_PDM_HEADER_INIT; public: RimRoffCaseSumo(); ~RimRoffCaseSumo() override; // Create a grid case for a single realization of the given grid, linked back to the data source // so the case can be updated when the data source realization filter changes. static RimRoffCaseSumo* createFromDataSource( RimSumoDataSource* dataSource, const QString& gridName, int realization ); void setSumoDataSource( RimSumoDataSource* dataSource ); void setSumoCaseId( const QString& sumoCaseId ); void setEnsembleName( const QString& ensembleName ); void setGridName( const QString& gridName ); void setRealization( int realization ); QString gridName() const; int realization() const; // Data source this case was created from -- used to check whether a view's case belongs to // the same Sumo case/ensemble before auto-following that view's realization. See // RimPolygonCloudSource::resolveViewMatchingRealization. RimSumoDataSource* dataSource() const; bool openEclipseGridFile() override; void closeReservoirCase() override; // Aborts any transfers still in flight for this realization, without discarding already loaded grid and // result data. Used when switching away from this case in a view while it may still be shown elsewhere, // so a still loading realization does not keep competing for bandwidth with a newly selected one, while a // fully loaded realization is not forced through a full reload if switched back to. void cancelPendingTransfers(); // Fetches and stores exactly the given time steps of a dynamic property, in parallel, without pulling in // the rest of the time series. See RifReaderSumoGridProperty::prefetchDynamicResult. bool prefetchDynamicResult( const QString& resultName, const std::vector& stepIndices ) override; QString locationOnDisc() const override; QString dataLoadingText() const override; protected: void defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering ) override; private: // Download the roff grid blob for this realization and parse it into the case data. bool downloadAndParseGrid(); // Register the results and attach the property reader. Shared by the two ways the grid can end up on // this case: parsed and kept, or parsed and replaced by the grid shared with the other realizations. void finalizeCaseSetup(); // Discover the available grid properties from Sumo, register them as cell results and attach a reader // that fetches the property data on demand. Static properties only in this first version. void registerSumoGridProperties(); // Start the transfer of the property time step about to be displayed. Called before the grid download so // the two run in parallel; only the decode needs the grid. // // The values go to the reader, which does not exist yet at this point. That is safe: arrivals come through // the event loop, and runOnTransferThreadBlocking holds the grid download on a semaphore without // dispatching events, so nothing lands before the case is set up. void startPropertyFetch(); // The property and time step a view of this case is about to show. False when no view has picked one yet, // leaving the on demand path to fetch whatever is asked for. bool propertyToFetch( QString& propertyName, size_t& stepIndex, QString& isoDateOrInterval ) const; // Callbacks handed to m_propertyReader, so the reader itself stays free of GUI code. Shows the loading // banner and status bar message, and redraws the views once a time step has arrived. void onPropertyPendingChanged( const QString& message ); void onPropertyTimeStepArrived( const QString& propertyName, size_t stepIndex, bool ok ); // Turns the reader's plain completed/total/description numbers into an actual progress dialog: opens one // the first time total is non-zero (or replaces it with a freshly-sized one if total grew while a dialog // was already open - caf::ProgressInfo's maximum cannot change after construction), ticks it as completed // grows, and closes it once total is reported back as 0. void onPropertyBatchProgressChanged( size_t completed, size_t total, const QString& description ); // Redraw the views of this case, so arrived values become visible. void scheduleRedrawOfViews(); private: caf::PdmPtrField m_sumoDataSource; caf::PdmField m_sumoCaseId; caf::PdmField m_ensembleName; caf::PdmField m_gridName; caf::PdmField m_realization; QPointer m_sumoConnector; // The same reader the cell results hold, set in registerSumoGridProperties. Time steps fetched before it // existed are handed to it. cvf::ref m_propertyReader; // The properties Sumo reports for this realization. Fetched before the grid and reused when they are // registered, so the request is made once. std::vector m_propertyInfos; // The time step startPropertyFetch put in flight, until the reader exists and takes it over. Handed to // the reader as pending so it is reported to the user and not requested a second time. std::optional> m_fetchInFlight; // Identifies the transfer startPropertyFetch issues before the reader (and its own lifetime token) exists, // so it can still be cancelled from closeReservoirCase. Recreated on every close, so a transfer left over // from a previous open is never cancelled by a later one. std::shared_ptr m_lifetimeToken; // A redraw can read another time step and arrive back here through the reader's callback. Coalesce // rather than recurse. bool m_isRedrawingViews = false; bool m_hasMissedViewRedraw = false; // One single dialog for however many time steps the reader currently has pending, so waiting for a run // of single steps in a row does not pop open and close a new dialog for each one. Owned here rather than // in the reader itself, which reports only plain numbers via onPropertyBatchProgressChanged - no GUI code // lives in RifReaderSumoGridProperty. Null when nothing is pending. std::unique_ptr m_batchProgress; size_t m_batchProgressShownTotal = 0; // What m_batchProgress was actually constructed with. };