mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 18:00:06 +00:00 
			
		
		
		
	Merge pull request #30 from dpaulat/feature/garbage-collect
Feature/garbage collect
This commit is contained in:
		
						commit
						a851918ec2
					
				
					 22 changed files with 639 additions and 166 deletions
				
			
		|  | @ -357,6 +357,11 @@ void MainWindow::on_actionImGuiDebug_triggered() | ||||||
|    p->imGuiDebugDialog_->show(); |    p->imGuiDebugDialog_->show(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void MainWindow::on_actionDumpRadarProductRecords_triggered() | ||||||
|  | { | ||||||
|  |    manager::RadarProductManager::DumpRecords(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void MainWindow::on_actionUserManual_triggered() | void MainWindow::on_actionUserManual_triggered() | ||||||
| { | { | ||||||
|    QDesktopServices::openUrl(QUrl {"https://supercell-wx.readthedocs.io/"}); |    QDesktopServices::openUrl(QUrl {"https://supercell-wx.readthedocs.io/"}); | ||||||
|  | @ -392,6 +397,67 @@ void MainWindow::on_resourceTreeExpandAllButton_clicked() | ||||||
|    ui->resourceTreeView->expandAll(); |    ui->resourceTreeView->expandAll(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void MainWindow::on_resourceTreeView_doubleClicked(const QModelIndex& index) | ||||||
|  | { | ||||||
|  |    std::string selectedString {index.data().toString().toStdString()}; | ||||||
|  |    std::chrono::system_clock::time_point time {}; | ||||||
|  | 
 | ||||||
|  |    logger_->debug("Selecting resource: {}", | ||||||
|  |                   index.data().toString().toStdString()); | ||||||
|  | 
 | ||||||
|  |    static const std::string timeFormat {"%Y-%m-%d %H:%M:%S"}; | ||||||
|  | 
 | ||||||
|  |    std::istringstream in {selectedString}; | ||||||
|  |    in >> std::chrono::parse(timeFormat, time); | ||||||
|  | 
 | ||||||
|  |    if (in.fail()) | ||||||
|  |    { | ||||||
|  |       // Not a time string, ignore double-click
 | ||||||
|  |       return; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    QModelIndex parent1 = index.parent(); | ||||||
|  |    QModelIndex parent2 = parent1.parent(); | ||||||
|  |    QModelIndex parent3 = parent2.parent(); | ||||||
|  | 
 | ||||||
|  |    std::string radarSite {}; | ||||||
|  |    std::string groupName {}; | ||||||
|  |    std::string product {}; | ||||||
|  | 
 | ||||||
|  |    if (!parent2.isValid()) | ||||||
|  |    { | ||||||
|  |       // A time entry should be at the third or fourth level
 | ||||||
|  |       logger_->error("Unexpected resource data"); | ||||||
|  |       return; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (parent3.isValid()) | ||||||
|  |    { | ||||||
|  |       // Level 3 Product
 | ||||||
|  |       radarSite = parent3.data().toString().toStdString(); | ||||||
|  |       groupName = parent2.data().toString().toStdString(); | ||||||
|  |       product   = parent1.data().toString().toStdString(); | ||||||
|  |    } | ||||||
|  |    else | ||||||
|  |    { | ||||||
|  |       // Level 2 Product
 | ||||||
|  |       radarSite = parent2.data().toString().toStdString(); | ||||||
|  |       groupName = parent1.data().toString().toStdString(); | ||||||
|  |       // No product index
 | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    common::RadarProductGroup group = common::GetRadarProductGroup(groupName); | ||||||
|  | 
 | ||||||
|  |    // Update radar site if different from currently selected
 | ||||||
|  |    if (p->activeMap_->GetRadarSite()->id() != radarSite) | ||||||
|  |    { | ||||||
|  |       p->activeMap_->SelectRadarSite(radarSite); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Select the updated radar product
 | ||||||
|  |    p->activeMap_->SelectRadarProduct(group, product, 0, time); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void MainWindowImpl::ConfigureMapLayout() | void MainWindowImpl::ConfigureMapLayout() | ||||||
| { | { | ||||||
|    auto& generalSettings = manager::SettingsManager::general_settings(); |    auto& generalSettings = manager::SettingsManager::general_settings(); | ||||||
|  |  | ||||||
|  | @ -37,6 +37,7 @@ private slots: | ||||||
|    void on_actionSettings_triggered(); |    void on_actionSettings_triggered(); | ||||||
|    void on_actionExit_triggered(); |    void on_actionExit_triggered(); | ||||||
|    void on_actionImGuiDebug_triggered(); |    void on_actionImGuiDebug_triggered(); | ||||||
|  |    void on_actionDumpRadarProductRecords_triggered(); | ||||||
|    void on_actionUserManual_triggered(); |    void on_actionUserManual_triggered(); | ||||||
|    void on_actionDiscord_triggered(); |    void on_actionDiscord_triggered(); | ||||||
|    void on_actionGitHubRepository_triggered(); |    void on_actionGitHubRepository_triggered(); | ||||||
|  | @ -44,6 +45,7 @@ private slots: | ||||||
|    void on_radarSiteSelectButton_clicked(); |    void on_radarSiteSelectButton_clicked(); | ||||||
|    void on_resourceTreeCollapseAllButton_clicked(); |    void on_resourceTreeCollapseAllButton_clicked(); | ||||||
|    void on_resourceTreeExpandAllButton_clicked(); |    void on_resourceTreeExpandAllButton_clicked(); | ||||||
|  |    void on_resourceTreeView_doubleClicked(const QModelIndex& index); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|    std::unique_ptr<MainWindowImpl> p; |    std::unique_ptr<MainWindowImpl> p; | ||||||
|  |  | ||||||
|  | @ -83,6 +83,8 @@ | ||||||
|      <string>&Debug</string> |      <string>&Debug</string> | ||||||
|     </property> |     </property> | ||||||
|     <addaction name="actionImGuiDebug"/> |     <addaction name="actionImGuiDebug"/> | ||||||
|  |     <addaction name="separator"/> | ||||||
|  |     <addaction name="actionDumpRadarProductRecords"/> | ||||||
|    </widget> |    </widget> | ||||||
|    <addaction name="menuFile"/> |    <addaction name="menuFile"/> | ||||||
|    <addaction name="menuView"/> |    <addaction name="menuView"/> | ||||||
|  | @ -397,6 +399,11 @@ | ||||||
|     <string>&GitHub Repository</string> |     <string>&GitHub Repository</string> | ||||||
|    </property> |    </property> | ||||||
|   </action> |   </action> | ||||||
|  |   <action name="actionDumpRadarProductRecords"> | ||||||
|  |    <property name="text"> | ||||||
|  |     <string>Dump Radar &Product Records</string> | ||||||
|  |    </property> | ||||||
|  |   </action> | ||||||
|  </widget> |  </widget> | ||||||
|  <resources> |  <resources> | ||||||
|   <include location="../../../../scwx-qt.qrc"/> |   <include location="../../../../scwx-qt.qrc"/> | ||||||
|  |  | ||||||
|  | @ -16,6 +16,7 @@ | ||||||
| 
 | 
 | ||||||
| #pragma warning(push, 0) | #pragma warning(push, 0) | ||||||
| #include <boost/asio/steady_timer.hpp> | #include <boost/asio/steady_timer.hpp> | ||||||
|  | #include <boost/container_hash/hash.hpp> | ||||||
| #include <boost/range/irange.hpp> | #include <boost/range/irange.hpp> | ||||||
| #include <boost/timer/timer.hpp> | #include <boost/timer/timer.hpp> | ||||||
| #include <fmt/chrono.h> | #include <fmt/chrono.h> | ||||||
|  | @ -36,8 +37,10 @@ static const auto logger_ = scwx::util::Logger::Create(logPrefix_); | ||||||
| typedef std::function<std::shared_ptr<wsr88d::NexradFile>()> | typedef std::function<std::shared_ptr<wsr88d::NexradFile>()> | ||||||
|    CreateNexradFileFunction; |    CreateNexradFileFunction; | ||||||
| typedef std::map<std::chrono::system_clock::time_point, | typedef std::map<std::chrono::system_clock::time_point, | ||||||
|                  std::shared_ptr<types::RadarProductRecord>> |                  std::weak_ptr<types::RadarProductRecord>> | ||||||
|    RadarProductRecordMap; |    RadarProductRecordMap; | ||||||
|  | typedef std::list<std::shared_ptr<types::RadarProductRecord>> | ||||||
|  |    RadarProductRecordList; | ||||||
| 
 | 
 | ||||||
| static constexpr uint32_t NUM_RADIAL_GATES_0_5_DEGREE = | static constexpr uint32_t NUM_RADIAL_GATES_0_5_DEGREE = | ||||||
|    common::MAX_0_5_DEGREE_RADIALS * common::MAX_DATA_MOMENT_GATES; |    common::MAX_0_5_DEGREE_RADIALS * common::MAX_DATA_MOMENT_GATES; | ||||||
|  | @ -53,8 +56,8 @@ static const std::string kDefaultLevel3Product_ {"N0B"}; | ||||||
| static constexpr std::chrono::seconds kRetryInterval_ {15}; | static constexpr std::chrono::seconds kRetryInterval_ {15}; | ||||||
| 
 | 
 | ||||||
| static std::unordered_map<std::string, std::weak_ptr<RadarProductManager>> | static std::unordered_map<std::string, std::weak_ptr<RadarProductManager>> | ||||||
|                   instanceMap_; |                          instanceMap_; | ||||||
| static std::mutex instanceMutex_; | static std::shared_mutex instanceMutex_; | ||||||
| 
 | 
 | ||||||
| static std::unordered_map<std::string, | static std::unordered_map<std::string, | ||||||
|                           std::shared_ptr<types::RadarProductRecord>> |                           std::shared_ptr<types::RadarProductRecord>> | ||||||
|  | @ -123,7 +126,9 @@ public: | ||||||
|        coordinates0_5Degree_ {}, |        coordinates0_5Degree_ {}, | ||||||
|        coordinates1Degree_ {}, |        coordinates1Degree_ {}, | ||||||
|        level2ProductRecords_ {}, |        level2ProductRecords_ {}, | ||||||
|  |        level2ProductRecentRecords_ {}, | ||||||
|        level3ProductRecordsMap_ {}, |        level3ProductRecordsMap_ {}, | ||||||
|  |        level3ProductRecentRecordsMap_ {}, | ||||||
|        level2ProductRecordMutex_ {}, |        level2ProductRecordMutex_ {}, | ||||||
|        level3ProductRecordMutex_ {}, |        level3ProductRecordMutex_ {}, | ||||||
|        level2ProviderManager_ {std::make_shared<ProviderManager>( |        level2ProviderManager_ {std::make_shared<ProviderManager>( | ||||||
|  | @ -159,6 +164,10 @@ public: | ||||||
|                        auto& [key, providerManager] = p; |                        auto& [key, providerManager] = p; | ||||||
|                        providerManager->Disable(); |                        providerManager->Disable(); | ||||||
|                     }); |                     }); | ||||||
|  | 
 | ||||||
|  |       // Lock other mutexes before destroying, ensure loading is complete
 | ||||||
|  |       std::unique_lock loadLevel2DataLock {loadLevel2DataMutex_}; | ||||||
|  |       std::unique_lock loadLevel3DataLock {loadLevel3DataMutex_}; | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    RadarProductManager* self_; |    RadarProductManager* self_; | ||||||
|  | @ -166,17 +175,22 @@ public: | ||||||
|    std::shared_ptr<ProviderManager> |    std::shared_ptr<ProviderManager> | ||||||
|    GetLevel3ProviderManager(const std::string& product); |    GetLevel3ProviderManager(const std::string& product); | ||||||
| 
 | 
 | ||||||
|    void EnableRefresh(std::shared_ptr<ProviderManager> providerManager, |    void EnableRefresh(boost::uuids::uuid               uuid, | ||||||
|  |                       std::shared_ptr<ProviderManager> providerManager, | ||||||
|                       bool                             enabled); |                       bool                             enabled); | ||||||
|    void RefreshData(std::shared_ptr<ProviderManager> providerManager); |    void RefreshData(std::shared_ptr<ProviderManager> providerManager); | ||||||
| 
 | 
 | ||||||
|    std::shared_ptr<types::RadarProductRecord> |    std::tuple<std::shared_ptr<types::RadarProductRecord>, | ||||||
|  |               std::chrono::system_clock::time_point> | ||||||
|    GetLevel2ProductRecord(std::chrono::system_clock::time_point time); |    GetLevel2ProductRecord(std::chrono::system_clock::time_point time); | ||||||
|    std::shared_ptr<types::RadarProductRecord> |    std::tuple<std::shared_ptr<types::RadarProductRecord>, | ||||||
|  |               std::chrono::system_clock::time_point> | ||||||
|    GetLevel3ProductRecord(const std::string&                    product, |    GetLevel3ProductRecord(const std::string&                    product, | ||||||
|                           std::chrono::system_clock::time_point time); |                           std::chrono::system_clock::time_point time); | ||||||
|    std::shared_ptr<types::RadarProductRecord> |    std::shared_ptr<types::RadarProductRecord> | ||||||
|    StoreRadarProductRecord(std::shared_ptr<types::RadarProductRecord> record); |    StoreRadarProductRecord(std::shared_ptr<types::RadarProductRecord> record); | ||||||
|  |    void UpdateRecentRecords(RadarProductRecordList& recentList, | ||||||
|  |                             std::shared_ptr<types::RadarProductRecord> record); | ||||||
| 
 | 
 | ||||||
|    void LoadProviderData(std::chrono::system_clock::time_point time, |    void LoadProviderData(std::chrono::system_clock::time_point time, | ||||||
|                          std::shared_ptr<ProviderManager>      providerManager, |                          std::shared_ptr<ProviderManager>      providerManager, | ||||||
|  | @ -199,10 +213,12 @@ public: | ||||||
|    std::vector<float> coordinates0_5Degree_; |    std::vector<float> coordinates0_5Degree_; | ||||||
|    std::vector<float> coordinates1Degree_; |    std::vector<float> coordinates1Degree_; | ||||||
| 
 | 
 | ||||||
|    RadarProductRecordMap level2ProductRecords_; |    RadarProductRecordMap  level2ProductRecords_; | ||||||
|  |    RadarProductRecordList level2ProductRecentRecords_; | ||||||
|    std::unordered_map<std::string, RadarProductRecordMap> |    std::unordered_map<std::string, RadarProductRecordMap> | ||||||
|       level3ProductRecordsMap_; |       level3ProductRecordsMap_; | ||||||
| 
 |    std::unordered_map<std::string, RadarProductRecordList> | ||||||
|  |                      level3ProductRecentRecordsMap_; | ||||||
|    std::shared_mutex level2ProductRecordMutex_; |    std::shared_mutex level2ProductRecordMutex_; | ||||||
|    std::shared_mutex level3ProductRecordMutex_; |    std::shared_mutex level3ProductRecordMutex_; | ||||||
| 
 | 
 | ||||||
|  | @ -218,6 +234,12 @@ public: | ||||||
| 
 | 
 | ||||||
|    common::Level3ProductCategoryMap availableCategoryMap_; |    common::Level3ProductCategoryMap availableCategoryMap_; | ||||||
|    std::shared_mutex                availableCategoryMutex_; |    std::shared_mutex                availableCategoryMutex_; | ||||||
|  | 
 | ||||||
|  |    std::unordered_map<boost::uuids::uuid, | ||||||
|  |                       std::shared_ptr<ProviderManager>, | ||||||
|  |                       boost::hash<boost::uuids::uuid>> | ||||||
|  |               refreshMap_ {}; | ||||||
|  |    std::mutex refreshMapMutex_ {}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| RadarProductManager::RadarProductManager(const std::string& radarId) : | RadarProductManager::RadarProductManager(const std::string& radarId) : | ||||||
|  | @ -248,6 +270,8 @@ std::string ProviderManager::name() const | ||||||
| 
 | 
 | ||||||
| void ProviderManager::Disable() | void ProviderManager::Disable() | ||||||
| { | { | ||||||
|  |    logger_->debug("Disabling refresh: {}", name()); | ||||||
|  | 
 | ||||||
|    std::unique_lock lock(refreshTimerMutex_); |    std::unique_lock lock(refreshTimerMutex_); | ||||||
|    refreshEnabled_ = false; |    refreshEnabled_ = false; | ||||||
|    refreshTimer_.cancel(); |    refreshTimer_.cancel(); | ||||||
|  | @ -266,6 +290,61 @@ void RadarProductManager::Cleanup() | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void RadarProductManager::DumpRecords() | ||||||
|  | { | ||||||
|  |    scwx::util::async( | ||||||
|  |       [] | ||||||
|  |       { | ||||||
|  |          logger_->info("Record Dump"); | ||||||
|  | 
 | ||||||
|  |          std::shared_lock instanceLock {instanceMutex_}; | ||||||
|  |          for (auto& instance : instanceMap_) | ||||||
|  |          { | ||||||
|  |             auto radarProductManager = instance.second.lock(); | ||||||
|  |             if (radarProductManager != nullptr) | ||||||
|  |             { | ||||||
|  |                logger_->info(" {}", radarProductManager->radar_site()->id()); | ||||||
|  |                logger_->info("  Level 2"); | ||||||
|  | 
 | ||||||
|  |                { | ||||||
|  |                   std::shared_lock level2ProductLock { | ||||||
|  |                      radarProductManager->p->level2ProductRecordMutex_}; | ||||||
|  | 
 | ||||||
|  |                   for (auto& record : | ||||||
|  |                        radarProductManager->p->level2ProductRecords_) | ||||||
|  |                   { | ||||||
|  |                      logger_->info("   {}{}", | ||||||
|  |                                    scwx::util::TimeString(record.first), | ||||||
|  |                                    record.second.expired() ? " (expired)" : ""); | ||||||
|  |                   } | ||||||
|  |                } | ||||||
|  | 
 | ||||||
|  |                logger_->info("  Level 3"); | ||||||
|  | 
 | ||||||
|  |                { | ||||||
|  |                   std::shared_lock level3ProductLock { | ||||||
|  |                      radarProductManager->p->level3ProductRecordMutex_}; | ||||||
|  | 
 | ||||||
|  |                   for (auto& recordMap : | ||||||
|  |                        radarProductManager->p->level3ProductRecordsMap_) | ||||||
|  |                   { | ||||||
|  |                      // Product Name
 | ||||||
|  |                      logger_->info("   {}", recordMap.first); | ||||||
|  | 
 | ||||||
|  |                      for (auto& record : recordMap.second) | ||||||
|  |                      { | ||||||
|  |                         logger_->info("    {}{}", | ||||||
|  |                                       scwx::util::TimeString(record.first), | ||||||
|  |                                       record.second.expired() ? " (expired)" : | ||||||
|  |                                                                 ""); | ||||||
|  |                      } | ||||||
|  |                   } | ||||||
|  |                } | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |       }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const std::vector<float>& | const std::vector<float>& | ||||||
| RadarProductManager::coordinates(common::RadialSize radialSize) const | RadarProductManager::coordinates(common::RadialSize radialSize) const | ||||||
| { | { | ||||||
|  | @ -413,11 +492,12 @@ RadarProductManagerImpl::GetLevel3ProviderManager(const std::string& product) | ||||||
| 
 | 
 | ||||||
| void RadarProductManager::EnableRefresh(common::RadarProductGroup group, | void RadarProductManager::EnableRefresh(common::RadarProductGroup group, | ||||||
|                                         const std::string&        product, |                                         const std::string&        product, | ||||||
|                                         bool                      enabled) |                                         bool                      enabled, | ||||||
|  |                                         boost::uuids::uuid        uuid) | ||||||
| { | { | ||||||
|    if (group == common::RadarProductGroup::Level2) |    if (group == common::RadarProductGroup::Level2) | ||||||
|    { |    { | ||||||
|       p->EnableRefresh(p->level2ProviderManager_, enabled); |       p->EnableRefresh(uuid, p->level2ProviderManager_, enabled); | ||||||
|    } |    } | ||||||
|    else |    else | ||||||
|    { |    { | ||||||
|  | @ -437,16 +517,65 @@ void RadarProductManager::EnableRefresh(common::RadarProductGroup group, | ||||||
|                           availableProducts.cend(), |                           availableProducts.cend(), | ||||||
|                           product) != availableProducts.cend()) |                           product) != availableProducts.cend()) | ||||||
|             { |             { | ||||||
|                p->EnableRefresh(providerManager, enabled); |                p->EnableRefresh(uuid, providerManager, enabled); | ||||||
|             } |             } | ||||||
|          }); |          }); | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RadarProductManagerImpl::EnableRefresh( | void RadarProductManagerImpl::EnableRefresh( | ||||||
|    std::shared_ptr<ProviderManager> providerManager, bool enabled) |    boost::uuids::uuid               uuid, | ||||||
|  |    std::shared_ptr<ProviderManager> providerManager, | ||||||
|  |    bool                             enabled) | ||||||
| { | { | ||||||
|    if (providerManager->refreshEnabled_ != enabled) |    // Lock the refresh map
 | ||||||
|  |    std::unique_lock lock {refreshMapMutex_}; | ||||||
|  | 
 | ||||||
|  |    auto currentProviderManager = refreshMap_.find(uuid); | ||||||
|  |    if (currentProviderManager != refreshMap_.cend()) | ||||||
|  |    { | ||||||
|  |       // If the enabling refresh for a different product, or disabling refresh
 | ||||||
|  |       if (currentProviderManager->second != providerManager || !enabled) | ||||||
|  |       { | ||||||
|  |          // Determine number of entries in the map for the current provider
 | ||||||
|  |          // manager
 | ||||||
|  |          auto currentProviderManagerCount = std::count_if( | ||||||
|  |             refreshMap_.cbegin(), | ||||||
|  |             refreshMap_.cend(), | ||||||
|  |             [&](const auto& provider) | ||||||
|  |             { return provider.second == currentProviderManager->second; }); | ||||||
|  | 
 | ||||||
|  |          // If this is the last reference to the provider in the refresh map
 | ||||||
|  |          if (currentProviderManagerCount == 1) | ||||||
|  |          { | ||||||
|  |             // Disable current provider
 | ||||||
|  |             currentProviderManager->second->Disable(); | ||||||
|  |          } | ||||||
|  | 
 | ||||||
|  |          // Dissociate uuid from current provider manager
 | ||||||
|  |          refreshMap_.erase(currentProviderManager); | ||||||
|  | 
 | ||||||
|  |          // If we are enabling a new provider manager
 | ||||||
|  |          if (enabled) | ||||||
|  |          { | ||||||
|  |             // Associate uuid to providerManager
 | ||||||
|  |             refreshMap_.emplace(uuid, providerManager); | ||||||
|  |          } | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  |    else if (enabled) | ||||||
|  |    { | ||||||
|  |       // We are enabling a new provider manager
 | ||||||
|  |       // Associate uuid to provider manager
 | ||||||
|  |       refreshMap_.emplace(uuid, providerManager); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    // Release the refresh map mutex
 | ||||||
|  |    lock.unlock(); | ||||||
|  | 
 | ||||||
|  |    // We have already handled a disable request by this point. If enabling, and
 | ||||||
|  |    // the provider manager refresh isn't already enabled, enable it.
 | ||||||
|  |    if (enabled && providerManager->refreshEnabled_ != enabled) | ||||||
|    { |    { | ||||||
|       providerManager->refreshEnabled_ = enabled; |       providerManager->refreshEnabled_ = enabled; | ||||||
| 
 | 
 | ||||||
|  | @ -475,7 +604,7 @@ void RadarProductManagerImpl::RefreshData( | ||||||
| 
 | 
 | ||||||
|          std::chrono::milliseconds interval = kRetryInterval_; |          std::chrono::milliseconds interval = kRetryInterval_; | ||||||
| 
 | 
 | ||||||
|          if (newObjects > 0) |          if (totalObjects > 0) | ||||||
|          { |          { | ||||||
|             std::string key = providerManager->provider_->FindLatestKey(); |             std::string key = providerManager->provider_->FindLatestKey(); | ||||||
|             auto        latestTime = |             auto        latestTime = | ||||||
|  | @ -491,10 +620,14 @@ void RadarProductManagerImpl::RefreshData( | ||||||
|                interval = kRetryInterval_; |                interval = kRetryInterval_; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             emit providerManager->NewDataAvailable( |             if (newObjects > 0) | ||||||
|                providerManager->group_, providerManager->product_, latestTime); |             { | ||||||
|  |                emit providerManager->NewDataAvailable(providerManager->group_, | ||||||
|  |                                                       providerManager->product_, | ||||||
|  |                                                       latestTime); | ||||||
|  |             } | ||||||
|          } |          } | ||||||
|          else if (providerManager->refreshEnabled_ && totalObjects == 0) |          else if (providerManager->refreshEnabled_) | ||||||
|          { |          { | ||||||
|             logger_->info("[{}] No data found, disabling refresh", |             logger_->info("[{}] No data found, disabling refresh", | ||||||
|                           providerManager->name()); |                           providerManager->name()); | ||||||
|  | @ -562,10 +695,13 @@ void RadarProductManagerImpl::LoadProviderData( | ||||||
|             auto it = recordMap.find(time); |             auto it = recordMap.find(time); | ||||||
|             if (it != recordMap.cend()) |             if (it != recordMap.cend()) | ||||||
|             { |             { | ||||||
|                logger_->debug( |                existingRecord = it->second.lock(); | ||||||
|                   "Data previously loaded, loading from data cache"); |  | ||||||
| 
 | 
 | ||||||
|                existingRecord = it->second; |                if (existingRecord != nullptr) | ||||||
|  |                { | ||||||
|  |                   logger_->debug( | ||||||
|  |                      "Data previously loaded, loading from data cache"); | ||||||
|  |                } | ||||||
|             } |             } | ||||||
|          } |          } | ||||||
| 
 | 
 | ||||||
|  | @ -727,38 +863,70 @@ void RadarProductManagerImpl::LoadNexradFile( | ||||||
|       }); |       }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::shared_ptr<types::RadarProductRecord> | std::tuple<std::shared_ptr<types::RadarProductRecord>, | ||||||
|  |            std::chrono::system_clock::time_point> | ||||||
| RadarProductManagerImpl::GetLevel2ProductRecord( | RadarProductManagerImpl::GetLevel2ProductRecord( | ||||||
|    std::chrono::system_clock::time_point time) |    std::chrono::system_clock::time_point time) | ||||||
| { | { | ||||||
|    std::shared_ptr<types::RadarProductRecord> record; |    std::shared_ptr<types::RadarProductRecord> record {nullptr}; | ||||||
|  |    RadarProductRecordMap::const_pointer       recordPtr {nullptr}; | ||||||
|  |    std::chrono::system_clock::time_point      recordTime {time}; | ||||||
| 
 | 
 | ||||||
|    if (!level2ProductRecords_.empty() && |    if (!level2ProductRecords_.empty() && | ||||||
|        time == std::chrono::system_clock::time_point {}) |        time == std::chrono::system_clock::time_point {}) | ||||||
|    { |    { | ||||||
|       // If a default-initialized time point is given, return the latest record
 |       // If a default-initialized time point is given, return the latest record
 | ||||||
|       record = level2ProductRecords_.rbegin()->second; |       recordPtr = &(*level2ProductRecords_.rbegin()); | ||||||
|    } |    } | ||||||
|    else |    else | ||||||
|    { |    { | ||||||
|       // TODO: Round to minutes
 |       recordPtr = | ||||||
|       record = scwx::util::GetBoundedElementValue(level2ProductRecords_, time); |          scwx::util::GetBoundedElementPointer(level2ProductRecords_, time); | ||||||
|  |    } | ||||||
| 
 | 
 | ||||||
|       // Does the record contain the time we are looking for?
 |    if (recordPtr != nullptr) | ||||||
|       if (record != nullptr && (time < record->level2_file()->start_time())) |    { | ||||||
|  |       if (time == std::chrono::system_clock::time_point {} || | ||||||
|  |           time == recordPtr->first) | ||||||
|       { |       { | ||||||
|          record = nullptr; |          recordTime = recordPtr->first; | ||||||
|  |          record     = recordPtr->second.lock(); | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    return record; |    if (record == nullptr && | ||||||
|  |        recordTime != std::chrono::system_clock::time_point {}) | ||||||
|  |    { | ||||||
|  |       // Product is expired, reload it
 | ||||||
|  |       std::shared_ptr<request::NexradFileRequest> request = | ||||||
|  |          std::make_shared<request::NexradFileRequest>(); | ||||||
|  | 
 | ||||||
|  |       QObject::connect( | ||||||
|  |          request.get(), | ||||||
|  |          &request::NexradFileRequest::RequestComplete, | ||||||
|  |          self_, | ||||||
|  |          [this](std::shared_ptr<request::NexradFileRequest> request) | ||||||
|  |          { | ||||||
|  |             if (request->radar_product_record() != nullptr) | ||||||
|  |             { | ||||||
|  |                emit self_->DataReloaded(request->radar_product_record()); | ||||||
|  |             } | ||||||
|  |          }); | ||||||
|  | 
 | ||||||
|  |       self_->LoadLevel2Data(recordTime, request); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return {record, recordTime}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::shared_ptr<types::RadarProductRecord> | std::tuple<std::shared_ptr<types::RadarProductRecord>, | ||||||
|  |            std::chrono::system_clock::time_point> | ||||||
| RadarProductManagerImpl::GetLevel3ProductRecord( | RadarProductManagerImpl::GetLevel3ProductRecord( | ||||||
|    const std::string& product, std::chrono::system_clock::time_point time) |    const std::string& product, std::chrono::system_clock::time_point time) | ||||||
| { | { | ||||||
|    std::shared_ptr<types::RadarProductRecord> record = nullptr; |    std::shared_ptr<types::RadarProductRecord> record {nullptr}; | ||||||
|  |    RadarProductRecordMap::const_pointer       recordPtr {nullptr}; | ||||||
|  |    std::chrono::system_clock::time_point      recordTime {time}; | ||||||
| 
 | 
 | ||||||
|    std::unique_lock lock {level3ProductRecordMutex_}; |    std::unique_lock lock {level3ProductRecordMutex_}; | ||||||
| 
 | 
 | ||||||
|  | @ -770,15 +938,50 @@ RadarProductManagerImpl::GetLevel3ProductRecord( | ||||||
|       { |       { | ||||||
|          // If a default-initialized time point is given, return the latest
 |          // If a default-initialized time point is given, return the latest
 | ||||||
|          // record
 |          // record
 | ||||||
|          record = it->second.rbegin()->second; |          recordPtr = &(*it->second.rbegin()); | ||||||
|       } |       } | ||||||
|       else |       else | ||||||
|       { |       { | ||||||
|          record = scwx::util::GetBoundedElementValue(it->second, time); |          recordPtr = scwx::util::GetBoundedElementPointer(it->second, time); | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    return record; |    // Lock is no longer needed
 | ||||||
|  |    lock.unlock(); | ||||||
|  | 
 | ||||||
|  |    if (recordPtr != nullptr) | ||||||
|  |    { | ||||||
|  |       if (time == std::chrono::system_clock::time_point {} || | ||||||
|  |           time == recordPtr->first) | ||||||
|  |       { | ||||||
|  |          recordTime = recordPtr->first; | ||||||
|  |          record     = recordPtr->second.lock(); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (record == nullptr && | ||||||
|  |        recordTime != std::chrono::system_clock::time_point {}) | ||||||
|  |    { | ||||||
|  |       // Product is expired, reload it
 | ||||||
|  |       std::shared_ptr<request::NexradFileRequest> request = | ||||||
|  |          std::make_shared<request::NexradFileRequest>(); | ||||||
|  | 
 | ||||||
|  |       QObject::connect( | ||||||
|  |          request.get(), | ||||||
|  |          &request::NexradFileRequest::RequestComplete, | ||||||
|  |          self_, | ||||||
|  |          [this](std::shared_ptr<request::NexradFileRequest> request) | ||||||
|  |          { | ||||||
|  |             if (request->radar_product_record() != nullptr) | ||||||
|  |             { | ||||||
|  |                emit self_->DataReloaded(request->radar_product_record()); | ||||||
|  |             } | ||||||
|  |          }); | ||||||
|  | 
 | ||||||
|  |       self_->LoadLevel3Data(product, recordTime, request); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return {record, recordTime}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::shared_ptr<types::RadarProductRecord> | std::shared_ptr<types::RadarProductRecord> | ||||||
|  | @ -787,7 +990,7 @@ RadarProductManagerImpl::StoreRadarProductRecord( | ||||||
| { | { | ||||||
|    logger_->debug("StoreRadarProductRecord()"); |    logger_->debug("StoreRadarProductRecord()"); | ||||||
| 
 | 
 | ||||||
|    std::shared_ptr<types::RadarProductRecord> storedRecord = record; |    std::shared_ptr<types::RadarProductRecord> storedRecord = nullptr; | ||||||
| 
 | 
 | ||||||
|    auto timeInSeconds = |    auto timeInSeconds = | ||||||
|       std::chrono::time_point_cast<std::chrono::seconds, |       std::chrono::time_point_cast<std::chrono::seconds, | ||||||
|  | @ -800,15 +1003,22 @@ RadarProductManagerImpl::StoreRadarProductRecord( | ||||||
|       auto it = level2ProductRecords_.find(timeInSeconds); |       auto it = level2ProductRecords_.find(timeInSeconds); | ||||||
|       if (it != level2ProductRecords_.cend()) |       if (it != level2ProductRecords_.cend()) | ||||||
|       { |       { | ||||||
|          logger_->debug( |          storedRecord = it->second.lock(); | ||||||
|             "Level 2 product previously loaded, loading from cache"); |  | ||||||
| 
 | 
 | ||||||
|          storedRecord = it->second; |          if (storedRecord != nullptr) | ||||||
|  |          { | ||||||
|  |             logger_->debug( | ||||||
|  |                "Level 2 product previously loaded, loading from cache"); | ||||||
|  |          } | ||||||
|       } |       } | ||||||
|       else | 
 | ||||||
|  |       if (storedRecord == nullptr) | ||||||
|       { |       { | ||||||
|  |          storedRecord                         = record; | ||||||
|          level2ProductRecords_[timeInSeconds] = record; |          level2ProductRecords_[timeInSeconds] = record; | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|  |       UpdateRecentRecords(level2ProductRecentRecords_, storedRecord); | ||||||
|    } |    } | ||||||
|    else if (record->radar_product_group() == common::RadarProductGroup::Level3) |    else if (record->radar_product_group() == common::RadarProductGroup::Level3) | ||||||
|    { |    { | ||||||
|  | @ -819,23 +1029,58 @@ RadarProductManagerImpl::StoreRadarProductRecord( | ||||||
|       auto it = productMap.find(timeInSeconds); |       auto it = productMap.find(timeInSeconds); | ||||||
|       if (it != productMap.cend()) |       if (it != productMap.cend()) | ||||||
|       { |       { | ||||||
|          logger_->debug( |          storedRecord = it->second.lock(); | ||||||
|             "Level 3 product previously loaded, loading from cache"); |  | ||||||
| 
 | 
 | ||||||
|          storedRecord = it->second; |          if (storedRecord != nullptr) | ||||||
|  |          { | ||||||
|  |             logger_->debug( | ||||||
|  |                "Level 3 product previously loaded, loading from cache"); | ||||||
|  |          } | ||||||
|       } |       } | ||||||
|       else | 
 | ||||||
|  |       if (storedRecord == nullptr) | ||||||
|       { |       { | ||||||
|  |          storedRecord              = record; | ||||||
|          productMap[timeInSeconds] = record; |          productMap[timeInSeconds] = record; | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|  |       UpdateRecentRecords( | ||||||
|  |          level3ProductRecentRecordsMap_[record->radar_product()], storedRecord); | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    return storedRecord; |    return storedRecord; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void RadarProductManagerImpl::UpdateRecentRecords( | ||||||
|  |    RadarProductRecordList&                    recentList, | ||||||
|  |    std::shared_ptr<types::RadarProductRecord> record) | ||||||
|  | { | ||||||
|  |    static constexpr std::size_t kRecentListMaxSize_ {2u}; | ||||||
|  | 
 | ||||||
|  |    auto it = std::find(recentList.cbegin(), recentList.cend(), record); | ||||||
|  |    if (it != recentList.cbegin() && it != recentList.cend()) | ||||||
|  |    { | ||||||
|  |       // If the record exists beyond the front of the list, remove it
 | ||||||
|  |       recentList.erase(it); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    if (recentList.size() == 0 || it != recentList.cbegin()) | ||||||
|  |    { | ||||||
|  |       // Add the record to the front of the list, unless it's already there
 | ||||||
|  |       recentList.push_front(record); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    while (recentList.size() > kRecentListMaxSize_) | ||||||
|  |    { | ||||||
|  |       // Remove from the end of the list while it's too big
 | ||||||
|  |       recentList.pop_back(); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| std::tuple<std::shared_ptr<wsr88d::rda::ElevationScan>, | std::tuple<std::shared_ptr<wsr88d::rda::ElevationScan>, | ||||||
|            float, |            float, | ||||||
|            std::vector<float>> |            std::vector<float>, | ||||||
|  |            std::chrono::system_clock::time_point> | ||||||
| RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType, | RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType, | ||||||
|                                    float                      elevation, |                                    float                      elevation, | ||||||
|                                    std::chrono::system_clock::time_point time) |                                    std::chrono::system_clock::time_point time) | ||||||
|  | @ -844,8 +1089,8 @@ RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType, | ||||||
|    float                                       elevationCut = 0.0f; |    float                                       elevationCut = 0.0f; | ||||||
|    std::vector<float>                          elevationCuts; |    std::vector<float>                          elevationCuts; | ||||||
| 
 | 
 | ||||||
|    std::shared_ptr<types::RadarProductRecord> record = |    std::shared_ptr<types::RadarProductRecord> record; | ||||||
|       p->GetLevel2ProductRecord(time); |    std::tie(record, time) = p->GetLevel2ProductRecord(time); | ||||||
| 
 | 
 | ||||||
|    if (record != nullptr) |    if (record != nullptr) | ||||||
|    { |    { | ||||||
|  | @ -854,24 +1099,25 @@ RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType, | ||||||
|             dataBlockType, elevation, time); |             dataBlockType, elevation, time); | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    return std::tie(radarData, elevationCut, elevationCuts); |    return {radarData, elevationCut, elevationCuts, time}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::shared_ptr<wsr88d::rpg::Level3Message> | std::tuple<std::shared_ptr<wsr88d::rpg::Level3Message>, | ||||||
|  |            std::chrono::system_clock::time_point> | ||||||
| RadarProductManager::GetLevel3Data(const std::string& product, | RadarProductManager::GetLevel3Data(const std::string& product, | ||||||
|                                    std::chrono::system_clock::time_point time) |                                    std::chrono::system_clock::time_point time) | ||||||
| { | { | ||||||
|    std::shared_ptr<wsr88d::rpg::Level3Message> message = nullptr; |    std::shared_ptr<wsr88d::rpg::Level3Message> message = nullptr; | ||||||
| 
 | 
 | ||||||
|    std::shared_ptr<types::RadarProductRecord> record = |    std::shared_ptr<types::RadarProductRecord> record; | ||||||
|       p->GetLevel3ProductRecord(product, time); |    std::tie(record, time) = p->GetLevel3ProductRecord(product, time); | ||||||
| 
 | 
 | ||||||
|    if (record != nullptr) |    if (record != nullptr) | ||||||
|    { |    { | ||||||
|       message = record->level3_file()->message(); |       message = record->level3_file()->message(); | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    return message; |    return {message, time}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| common::Level3ProductCategoryMap | common::Level3ProductCategoryMap | ||||||
|  | @ -970,7 +1216,7 @@ RadarProductManager::Instance(const std::string& radarSite) | ||||||
|    bool                                 instanceCreated = false; |    bool                                 instanceCreated = false; | ||||||
| 
 | 
 | ||||||
|    { |    { | ||||||
|       std::lock_guard<std::mutex> guard(instanceMutex_); |       std::unique_lock lock {instanceMutex_}; | ||||||
| 
 | 
 | ||||||
|       // Look up instance weak pointer
 |       // Look up instance weak pointer
 | ||||||
|       auto it = instanceMap_.find(radarSite); |       auto it = instanceMap_.find(radarSite); | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 | 
 | ||||||
|  | #include <boost/uuid/nil_generator.hpp> | ||||||
| #include <QObject> | #include <QObject> | ||||||
| 
 | 
 | ||||||
| namespace scwx | namespace scwx | ||||||
|  | @ -33,23 +34,63 @@ public: | ||||||
| 
 | 
 | ||||||
|    static void Cleanup(); |    static void Cleanup(); | ||||||
| 
 | 
 | ||||||
|  |    /**
 | ||||||
|  |     * @brief Debug function to dump currently loaded products to the log. | ||||||
|  |     */ | ||||||
|  |    static void DumpRecords(); | ||||||
|  | 
 | ||||||
|    const std::vector<float>& coordinates(common::RadialSize radialSize) const; |    const std::vector<float>& coordinates(common::RadialSize radialSize) const; | ||||||
|    float                     gate_size() const; |    float                     gate_size() const; | ||||||
|    std::shared_ptr<config::RadarSite> radar_site() const; |    std::shared_ptr<config::RadarSite> radar_site() const; | ||||||
| 
 | 
 | ||||||
|    void Initialize(); |    void Initialize(); | ||||||
|  | 
 | ||||||
|  |    /**
 | ||||||
|  |     * @brief Enables or disables refresh associated with a unique identifier | ||||||
|  |     * (UUID) for a given radar product group and product. | ||||||
|  |     * | ||||||
|  |     * Only a single product refresh can be enabled for a given UUID. If a second | ||||||
|  |     * product refresh is enabled for the same UUID, the first product refresh is | ||||||
|  |     * disabled (unless still enabled under a different UUID). | ||||||
|  |     * | ||||||
|  |     * @param [in] group Radar product group | ||||||
|  |     * @param [in] product Radar product name | ||||||
|  |     * @param [in] enabled Whether to enable refresh | ||||||
|  |     * @param [in] uuid Unique identifier. Default is boost::uuids::nil_uuid(). | ||||||
|  |     */ | ||||||
|    void EnableRefresh(common::RadarProductGroup group, |    void EnableRefresh(common::RadarProductGroup group, | ||||||
|                       const std::string&        product, |                       const std::string&        product, | ||||||
|                       bool                      enabled); |                       bool                      enabled, | ||||||
|  |                       boost::uuids::uuid uuid = boost::uuids::nil_uuid()); | ||||||
| 
 | 
 | ||||||
|  |    /**
 | ||||||
|  |     * @brief Get level 2 radar data for a data block type, elevation, and time. | ||||||
|  |     * | ||||||
|  |     * @param [in] dataBlockType Data block type | ||||||
|  |     * @param [in] elevation Elevation tilt | ||||||
|  |     * @param [in] time Radar product time | ||||||
|  |     * | ||||||
|  |     * @return Level 2 radar data, selected elevation cut, available elevation | ||||||
|  |     * cuts and selected time | ||||||
|  |     */ | ||||||
|    std::tuple<std::shared_ptr<wsr88d::rda::ElevationScan>, |    std::tuple<std::shared_ptr<wsr88d::rda::ElevationScan>, | ||||||
|               float, |               float, | ||||||
|               std::vector<float>> |               std::vector<float>, | ||||||
|  |               std::chrono::system_clock::time_point> | ||||||
|    GetLevel2Data(wsr88d::rda::DataBlockType            dataBlockType, |    GetLevel2Data(wsr88d::rda::DataBlockType            dataBlockType, | ||||||
|                  float                                 elevation, |                  float                                 elevation, | ||||||
|                  std::chrono::system_clock::time_point time = {}); |                  std::chrono::system_clock::time_point time = {}); | ||||||
| 
 | 
 | ||||||
|    std::shared_ptr<wsr88d::rpg::Level3Message> |    /**
 | ||||||
|  |     * @brief Get level 3 message data for a product and time. | ||||||
|  |     * | ||||||
|  |     * @param [in] product Radar product name | ||||||
|  |     * @param [in] time Radar product time | ||||||
|  |     * | ||||||
|  |     * @return Level 3 message data and selected time | ||||||
|  |     */ | ||||||
|  |    std::tuple<std::shared_ptr<wsr88d::rpg::Level3Message>, | ||||||
|  |               std::chrono::system_clock::time_point> | ||||||
|    GetLevel3Data(const std::string&                    product, |    GetLevel3Data(const std::string&                    product, | ||||||
|                  std::chrono::system_clock::time_point time = {}); |                  std::chrono::system_clock::time_point time = {}); | ||||||
| 
 | 
 | ||||||
|  | @ -76,6 +117,7 @@ public: | ||||||
|    void                             UpdateAvailableProducts(); |    void                             UpdateAvailableProducts(); | ||||||
| 
 | 
 | ||||||
| signals: | signals: | ||||||
|  |    void DataReloaded(std::shared_ptr<types::RadarProductRecord> record); | ||||||
|    void Level3ProductsChanged(); |    void Level3ProductsChanged(); | ||||||
|    void NewDataAvailable(common::RadarProductGroup             group, |    void NewDataAvailable(common::RadarProductGroup             group, | ||||||
|                          const std::string&                    product, |                          const std::string&                    product, | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <backends/imgui_impl_opengl3.h> | #include <backends/imgui_impl_opengl3.h> | ||||||
| #include <backends/imgui_impl_qt.hpp> | #include <backends/imgui_impl_qt.hpp> | ||||||
|  | #include <boost/uuid/random_generator.hpp> | ||||||
| #include <imgui.h> | #include <imgui.h> | ||||||
| #include <QApplication> | #include <QApplication> | ||||||
| #include <QColor> | #include <QColor> | ||||||
|  | @ -58,6 +59,7 @@ class MapWidgetImpl : public QObject | ||||||
| public: | public: | ||||||
|    explicit MapWidgetImpl(MapWidget*                   widget, |    explicit MapWidgetImpl(MapWidget*                   widget, | ||||||
|                           const QMapLibreGL::Settings& settings) : |                           const QMapLibreGL::Settings& settings) : | ||||||
|  |        uuid_ {boost::uuids::random_generator()()}, | ||||||
|        context_ {std::make_shared<MapContext>()}, |        context_ {std::make_shared<MapContext>()}, | ||||||
|        widget_ {widget}, |        widget_ {widget}, | ||||||
|        settings_(settings), |        settings_(settings), | ||||||
|  | @ -126,6 +128,8 @@ public: | ||||||
|    common::Level2Product |    common::Level2Product | ||||||
|    GetLevel2ProductOrDefault(const std::string& productName) const; |    GetLevel2ProductOrDefault(const std::string& productName) const; | ||||||
| 
 | 
 | ||||||
|  |    boost::uuids::uuid uuid_; | ||||||
|  | 
 | ||||||
|    std::shared_ptr<MapContext> context_; |    std::shared_ptr<MapContext> context_; | ||||||
| 
 | 
 | ||||||
|    MapWidget*                        widget_; |    MapWidget*                        widget_; | ||||||
|  | @ -303,7 +307,7 @@ std::shared_ptr<config::RadarSite> MapWidget::GetRadarSite() const | ||||||
|    return radarSite; |    return radarSite; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint16_t MapWidget::GetVcp() const | std::uint16_t MapWidget::GetVcp() const | ||||||
| { | { | ||||||
|    auto radarProductView = p->context_->radar_product_view(); |    auto radarProductView = p->context_->radar_product_view(); | ||||||
| 
 | 
 | ||||||
|  | @ -313,7 +317,7 @@ uint16_t MapWidget::GetVcp() const | ||||||
|    } |    } | ||||||
|    else |    else | ||||||
|    { |    { | ||||||
|       return 0; |       return 0u; | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -330,7 +334,8 @@ void MapWidget::SelectElevation(float elevation) | ||||||
| 
 | 
 | ||||||
| void MapWidget::SelectRadarProduct(common::RadarProductGroup group, | void MapWidget::SelectRadarProduct(common::RadarProductGroup group, | ||||||
|                                    const std::string&        product, |                                    const std::string&        product, | ||||||
|                                    int16_t                   productCode) |                                    std::int16_t              productCode, | ||||||
|  |                                    std::chrono::system_clock::time_point time) | ||||||
| { | { | ||||||
|    bool radarProductViewCreated = false; |    bool radarProductViewCreated = false; | ||||||
| 
 | 
 | ||||||
|  | @ -380,8 +385,8 @@ void MapWidget::SelectRadarProduct(common::RadarProductGroup group, | ||||||
| 
 | 
 | ||||||
|    if (radarProductView != nullptr) |    if (radarProductView != nullptr) | ||||||
|    { |    { | ||||||
|       // Always select the latest product available
 |       // Select the time associated with the request
 | ||||||
|       radarProductView->SelectTime({}); |       radarProductView->SelectTime(time); | ||||||
| 
 | 
 | ||||||
|       if (radarProductViewCreated) |       if (radarProductViewCreated) | ||||||
|       { |       { | ||||||
|  | @ -399,7 +404,8 @@ void MapWidget::SelectRadarProduct(common::RadarProductGroup group, | ||||||
| 
 | 
 | ||||||
|    if (p->autoRefreshEnabled_) |    if (p->autoRefreshEnabled_) | ||||||
|    { |    { | ||||||
|       p->radarProductManager_->EnableRefresh(group, productName, true); |       p->radarProductManager_->EnableRefresh( | ||||||
|  |          group, productName, true, p->uuid_); | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -485,7 +491,8 @@ void MapWidget::SetAutoRefresh(bool enabled) | ||||||
|          p->radarProductManager_->EnableRefresh( |          p->radarProductManager_->EnableRefresh( | ||||||
|             radarProductView->GetRadarProductGroup(), |             radarProductView->GetRadarProductGroup(), | ||||||
|             radarProductView->GetRadarProductName(), |             radarProductView->GetRadarProductName(), | ||||||
|             true); |             true, | ||||||
|  |             p->uuid_); | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -41,12 +41,23 @@ public: | ||||||
|    common::RadarProductGroup          GetRadarProductGroup() const; |    common::RadarProductGroup          GetRadarProductGroup() const; | ||||||
|    std::string                        GetRadarProductName() const; |    std::string                        GetRadarProductName() const; | ||||||
|    std::shared_ptr<config::RadarSite> GetRadarSite() const; |    std::shared_ptr<config::RadarSite> GetRadarSite() const; | ||||||
|    uint16_t                           GetVcp() const; |    std::uint16_t                      GetVcp() const; | ||||||
| 
 | 
 | ||||||
|    void SelectElevation(float elevation); |    void SelectElevation(float elevation); | ||||||
|  | 
 | ||||||
|  |    /**
 | ||||||
|  |     * @brief Selects a radar product. | ||||||
|  |     * | ||||||
|  |     * @param [in] group Radar product group | ||||||
|  |     * @param [in] product Radar product name | ||||||
|  |     * @param [in] productCode Radar product code (optional) | ||||||
|  |     * @paran [in] time Product time. Default is the latest available. | ||||||
|  |     */ | ||||||
|    void SelectRadarProduct(common::RadarProductGroup group, |    void SelectRadarProduct(common::RadarProductGroup group, | ||||||
|                            const std::string&        product, |                            const std::string&        product, | ||||||
|                            int16_t                   productCode); |                            std::int16_t              productCode      = 0, | ||||||
|  |                            std::chrono::system_clock::time_point time = {}); | ||||||
|  | 
 | ||||||
|    void SelectRadarProduct(std::shared_ptr<types::RadarProductRecord> record); |    void SelectRadarProduct(std::shared_ptr<types::RadarProductRecord> record); | ||||||
| 
 | 
 | ||||||
|    /**
 |    /**
 | ||||||
|  |  | ||||||
|  | @ -105,10 +105,16 @@ RadarProductModelImpl::RadarProductModelImpl(RadarProductModel* self) : | ||||||
|                   } |                   } | ||||||
|                } |                } | ||||||
| 
 | 
 | ||||||
|                // Create leaf item for product time
 |                // Find existing time item (e.g., 2023-04-10 10:11:12)
 | ||||||
|                model_->AppendRow(productItem, |                const QString timeString = | ||||||
|                                  new TreeItem {QString::fromStdString( |                   QString::fromStdString(util::TimeString(latestTime)); | ||||||
|                                     util::TimeString(latestTime))}); |                TreeItem* timeItem = productItem->FindChild(0, timeString); | ||||||
|  | 
 | ||||||
|  |                if (timeItem == nullptr) | ||||||
|  |                { | ||||||
|  |                   // Create leaf item for product time
 | ||||||
|  |                   model_->AppendRow(productItem, new TreeItem {timeString}); | ||||||
|  |                } | ||||||
|             }, |             }, | ||||||
|             Qt::QueuedConnection); |             Qt::QueuedConnection); | ||||||
|       }); |       }); | ||||||
|  |  | ||||||
|  | @ -119,8 +119,7 @@ void Level2ProductsWidgetImpl::UpdateProductSelection( | ||||||
| { | { | ||||||
|    const std::string& productName = common::GetLevel2Name(product); |    const std::string& productName = common::GetLevel2Name(product); | ||||||
| 
 | 
 | ||||||
|    std::for_each(std::execution::par_unseq, |    std::for_each(productButtons_.cbegin(), | ||||||
|                  productButtons_.cbegin(), |  | ||||||
|                  productButtons_.cend(), |                  productButtons_.cend(), | ||||||
|                  [&](auto& toolButton) |                  [&](auto& toolButton) | ||||||
|                  { |                  { | ||||||
|  |  | ||||||
|  | @ -51,7 +51,6 @@ public: | ||||||
| 
 | 
 | ||||||
|    void NormalizeElevationButtons(); |    void NormalizeElevationButtons(); | ||||||
|    void SelectElevation(float elevation); |    void SelectElevation(float elevation); | ||||||
|    void UpdateSettings(); |  | ||||||
| 
 | 
 | ||||||
|    Level2SettingsWidget* self_; |    Level2SettingsWidget* self_; | ||||||
|    QLayout*              layout_; |    QLayout*              layout_; | ||||||
|  | @ -135,8 +134,7 @@ void Level2SettingsWidget::UpdateElevationSelection(float elevation) | ||||||
|    QString buttonText {QString::number(elevation, 'f', 1) + |    QString buttonText {QString::number(elevation, 'f', 1) + | ||||||
|                        common::Characters::DEGREE}; |                        common::Characters::DEGREE}; | ||||||
| 
 | 
 | ||||||
|    std::for_each(std::execution::par_unseq, |    std::for_each(p->elevationButtons_.cbegin(), | ||||||
|                  p->elevationButtons_.cbegin(), |  | ||||||
|                  p->elevationButtons_.cend(), |                  p->elevationButtons_.cend(), | ||||||
|                  [&](auto& toolButton) |                  [&](auto& toolButton) | ||||||
|                  { |                  { | ||||||
|  |  | ||||||
|  | @ -258,8 +258,7 @@ void Level3ProductsWidgetImpl::UpdateCategorySelection( | ||||||
| { | { | ||||||
|    const std::string& categoryName = common::GetLevel3CategoryName(category); |    const std::string& categoryName = common::GetLevel3CategoryName(category); | ||||||
| 
 | 
 | ||||||
|    std::for_each(std::execution::par_unseq, |    std::for_each(categoryButtons_.cbegin(), | ||||||
|                  categoryButtons_.cbegin(), |  | ||||||
|                  categoryButtons_.cend(), |                  categoryButtons_.cend(), | ||||||
|                  [&](auto& toolButton) |                  [&](auto& toolButton) | ||||||
|                  { |                  { | ||||||
|  |  | ||||||
|  | @ -44,7 +44,6 @@ public: | ||||||
|    explicit Level2ProductViewImpl(common::Level2Product product) : |    explicit Level2ProductViewImpl(common::Level2Product product) : | ||||||
|        product_ {product}, |        product_ {product}, | ||||||
|        selectedElevation_ {0.0f}, |        selectedElevation_ {0.0f}, | ||||||
|        selectedTime_ {}, |  | ||||||
|        elevationScan_ {nullptr}, |        elevationScan_ {nullptr}, | ||||||
|        momentDataBlock0_ {nullptr}, |        momentDataBlock0_ {nullptr}, | ||||||
|        latitude_ {}, |        latitude_ {}, | ||||||
|  | @ -72,8 +71,7 @@ public: | ||||||
|    common::Level2Product      product_; |    common::Level2Product      product_; | ||||||
|    wsr88d::rda::DataBlockType dataBlockType_; |    wsr88d::rda::DataBlockType dataBlockType_; | ||||||
| 
 | 
 | ||||||
|    float                                 selectedElevation_; |    float selectedElevation_; | ||||||
|    std::chrono::system_clock::time_point selectedTime_; |  | ||||||
| 
 | 
 | ||||||
|    std::shared_ptr<wsr88d::rda::ElevationScan>   elevationScan_; |    std::shared_ptr<wsr88d::rda::ElevationScan>   elevationScan_; | ||||||
|    std::shared_ptr<wsr88d::rda::MomentDataBlock> momentDataBlock0_; |    std::shared_ptr<wsr88d::rda::MomentDataBlock> momentDataBlock0_; | ||||||
|  | @ -108,9 +106,36 @@ Level2ProductView::Level2ProductView( | ||||||
|     RadarProductView(radarProductManager), |     RadarProductView(radarProductManager), | ||||||
|     p(std::make_unique<Level2ProductViewImpl>(product)) |     p(std::make_unique<Level2ProductViewImpl>(product)) | ||||||
| { | { | ||||||
|  |    ConnectRadarProductManager(); | ||||||
| } | } | ||||||
| Level2ProductView::~Level2ProductView() = default; | Level2ProductView::~Level2ProductView() = default; | ||||||
| 
 | 
 | ||||||
|  | void Level2ProductView::ConnectRadarProductManager() | ||||||
|  | { | ||||||
|  |    connect(radar_product_manager().get(), | ||||||
|  |            &manager::RadarProductManager::DataReloaded, | ||||||
|  |            this, | ||||||
|  |            [this](std::shared_ptr<types::RadarProductRecord> record) | ||||||
|  |            { | ||||||
|  |               if (record->radar_product_group() == | ||||||
|  |                      common::RadarProductGroup::Level2 && | ||||||
|  |                   record->time() == selected_time()) | ||||||
|  |               { | ||||||
|  |                  // If the data associated with the currently selected time is
 | ||||||
|  |                  // reloaded, update the view
 | ||||||
|  |                  Update(); | ||||||
|  |               } | ||||||
|  |            }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Level2ProductView::DisconnectRadarProductManager() | ||||||
|  | { | ||||||
|  |    disconnect(radar_product_manager().get(), | ||||||
|  |               &manager::RadarProductManager::DataReloaded, | ||||||
|  |               this, | ||||||
|  |               nullptr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const std::vector<boost::gil::rgba8_pixel_t>& | const std::vector<boost::gil::rgba8_pixel_t>& | ||||||
| Level2ProductView::color_table() const | Level2ProductView::color_table() const | ||||||
| { | { | ||||||
|  | @ -243,11 +268,6 @@ void Level2ProductView::SelectProduct(const std::string& productName) | ||||||
|    p->SetProduct(productName); |    p->SetProduct(productName); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Level2ProductView::SelectTime(std::chrono::system_clock::time_point time) |  | ||||||
| { |  | ||||||
|    p->selectedTime_ = time; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Level2ProductViewImpl::SetProduct(const std::string& productName) | void Level2ProductViewImpl::SetProduct(const std::string& productName) | ||||||
| { | { | ||||||
|    SetProduct(common::GetLevel2Product(productName)); |    SetProduct(common::GetLevel2Product(productName)); | ||||||
|  | @ -376,9 +396,18 @@ void Level2ProductView::ComputeSweep() | ||||||
|       radar_product_manager(); |       radar_product_manager(); | ||||||
| 
 | 
 | ||||||
|    std::shared_ptr<wsr88d::rda::ElevationScan> radarData; |    std::shared_ptr<wsr88d::rda::ElevationScan> radarData; | ||||||
|    std::tie(radarData, p->elevationCut_, p->elevationCuts_) = |    std::chrono::system_clock::time_point       requestedTime {selected_time()}; | ||||||
|  |    std::chrono::system_clock::time_point       foundTime; | ||||||
|  |    std::tie(radarData, p->elevationCut_, p->elevationCuts_, foundTime) = | ||||||
|       radarProductManager->GetLevel2Data( |       radarProductManager->GetLevel2Data( | ||||||
|          p->dataBlockType_, p->selectedElevation_, p->selectedTime_); |          p->dataBlockType_, p->selectedElevation_, requestedTime); | ||||||
|  | 
 | ||||||
|  |    // If a different time was found than what was requested, update it
 | ||||||
|  |    if (requestedTime != foundTime) | ||||||
|  |    { | ||||||
|  |       SelectTime(foundTime); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|    if (radarData == nullptr || radarData == p->elevationScan_) |    if (radarData == nullptr || radarData == p->elevationScan_) | ||||||
|    { |    { | ||||||
|       return; |       return; | ||||||
|  |  | ||||||
|  | @ -28,31 +28,34 @@ public: | ||||||
|    ~Level2ProductView(); |    ~Level2ProductView(); | ||||||
| 
 | 
 | ||||||
|    const std::vector<boost::gil::rgba8_pixel_t>& color_table() const override; |    const std::vector<boost::gil::rgba8_pixel_t>& color_table() const override; | ||||||
|    uint16_t                              color_table_min() const override; |    std::uint16_t                         color_table_min() const override; | ||||||
|    uint16_t                              color_table_max() const override; |    std::uint16_t                         color_table_max() const override; | ||||||
|    float                                 elevation() const override; |    float                                 elevation() const override; | ||||||
|    float                                 range() const override; |    float                                 range() const override; | ||||||
|    std::chrono::system_clock::time_point sweep_time() const override; |    std::chrono::system_clock::time_point sweep_time() const override; | ||||||
|    uint16_t                              vcp() const override; |    std::uint16_t                         vcp() const override; | ||||||
|    const std::vector<float>&             vertices() const override; |    const std::vector<float>&             vertices() const override; | ||||||
| 
 | 
 | ||||||
|    void LoadColorTable(std::shared_ptr<common::ColorTable> colorTable) override; |    void LoadColorTable(std::shared_ptr<common::ColorTable> colorTable) override; | ||||||
|    void SelectElevation(float elevation) override; |    void SelectElevation(float elevation) override; | ||||||
|    void SelectProduct(const std::string& productName) override; |    void SelectProduct(const std::string& productName) override; | ||||||
|    void SelectTime(std::chrono::system_clock::time_point time) override; |  | ||||||
|    void Update() override; |    void Update() override; | ||||||
| 
 | 
 | ||||||
|    common::RadarProductGroup GetRadarProductGroup() const override; |    common::RadarProductGroup GetRadarProductGroup() const override; | ||||||
|    std::string               GetRadarProductName() const override; |    std::string               GetRadarProductName() const override; | ||||||
|    std::vector<float>        GetElevationCuts() const override; |    std::vector<float>        GetElevationCuts() const override; | ||||||
|    std::tuple<const void*, size_t, size_t> GetMomentData() const override; |    std::tuple<const void*, std::size_t, std::size_t> | ||||||
|    std::tuple<const void*, size_t, size_t> GetCfpMomentData() const override; |    GetMomentData() const override; | ||||||
|  |    std::tuple<const void*, std::size_t, std::size_t> | ||||||
|  |    GetCfpMomentData() const override; | ||||||
| 
 | 
 | ||||||
|    static std::shared_ptr<Level2ProductView> |    static std::shared_ptr<Level2ProductView> | ||||||
|    Create(common::Level2Product                         product, |    Create(common::Level2Product                         product, | ||||||
|           std::shared_ptr<manager::RadarProductManager> radarProductManager); |           std::shared_ptr<manager::RadarProductManager> radarProductManager); | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|  |    void ConnectRadarProductManager() override; | ||||||
|  |    void DisconnectRadarProductManager() override; | ||||||
|    void UpdateColorTable() override; |    void UpdateColorTable() override; | ||||||
| 
 | 
 | ||||||
| protected slots: | protected slots: | ||||||
|  |  | ||||||
|  | @ -59,9 +59,37 @@ Level3ProductView::Level3ProductView( | ||||||
|     RadarProductView(radarProductManager), |     RadarProductView(radarProductManager), | ||||||
|     p(std::make_unique<Level3ProductViewImpl>(product)) |     p(std::make_unique<Level3ProductViewImpl>(product)) | ||||||
| { | { | ||||||
|  |    ConnectRadarProductManager(); | ||||||
| } | } | ||||||
| Level3ProductView::~Level3ProductView() = default; | Level3ProductView::~Level3ProductView() = default; | ||||||
| 
 | 
 | ||||||
|  | void Level3ProductView::ConnectRadarProductManager() | ||||||
|  | { | ||||||
|  |    connect(radar_product_manager().get(), | ||||||
|  |            &manager::RadarProductManager::DataReloaded, | ||||||
|  |            this, | ||||||
|  |            [this](std::shared_ptr<types::RadarProductRecord> record) | ||||||
|  |            { | ||||||
|  |               if (record->radar_product_group() == | ||||||
|  |                      common::RadarProductGroup::Level3 && | ||||||
|  |                   record->radar_product() == p->product_ && | ||||||
|  |                   record->time() == selected_time()) | ||||||
|  |               { | ||||||
|  |                  // If the data associated with the currently selected time is
 | ||||||
|  |                  // reloaded, update the view
 | ||||||
|  |                  Update(); | ||||||
|  |               } | ||||||
|  |            }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Level3ProductView::DisconnectRadarProductManager() | ||||||
|  | { | ||||||
|  |    disconnect(radar_product_manager().get(), | ||||||
|  |               &manager::RadarProductManager::DataReloaded, | ||||||
|  |               this, | ||||||
|  |               nullptr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const std::vector<boost::gil::rgba8_pixel_t>& | const std::vector<boost::gil::rgba8_pixel_t>& | ||||||
| Level3ProductView::color_table() const | Level3ProductView::color_table() const | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -28,8 +28,8 @@ public: | ||||||
|    virtual ~Level3ProductView(); |    virtual ~Level3ProductView(); | ||||||
| 
 | 
 | ||||||
|    const std::vector<boost::gil::rgba8_pixel_t>& color_table() const override; |    const std::vector<boost::gil::rgba8_pixel_t>& color_table() const override; | ||||||
|    uint16_t color_table_min() const override; |    std::uint16_t color_table_min() const override; | ||||||
|    uint16_t color_table_max() const override; |    std::uint16_t color_table_max() const override; | ||||||
| 
 | 
 | ||||||
|    void LoadColorTable(std::shared_ptr<common::ColorTable> colorTable) override; |    void LoadColorTable(std::shared_ptr<common::ColorTable> colorTable) override; | ||||||
|    void Update() override; |    void Update() override; | ||||||
|  | @ -45,6 +45,8 @@ protected: | ||||||
|    void set_graphic_product_message( |    void set_graphic_product_message( | ||||||
|       std::shared_ptr<wsr88d::rpg::GraphicProductMessage> gpm); |       std::shared_ptr<wsr88d::rpg::GraphicProductMessage> gpm); | ||||||
| 
 | 
 | ||||||
|  |    void ConnectRadarProductManager() override; | ||||||
|  |    void DisconnectRadarProductManager() override; | ||||||
|    void UpdateColorTable() override; |    void UpdateColorTable() override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  |  | ||||||
|  | @ -27,25 +27,18 @@ class Level3RadialViewImpl | ||||||
| { | { | ||||||
| public: | public: | ||||||
|    explicit Level3RadialViewImpl() : |    explicit Level3RadialViewImpl() : | ||||||
|        selectedTime_ {}, |        latitude_ {}, longitude_ {}, range_ {}, vcp_ {}, sweepTime_ {} | ||||||
|        latitude_ {}, |  | ||||||
|        longitude_ {}, |  | ||||||
|        range_ {}, |  | ||||||
|        vcp_ {}, |  | ||||||
|        sweepTime_ {} |  | ||||||
|    { |    { | ||||||
|    } |    } | ||||||
|    ~Level3RadialViewImpl() = default; |    ~Level3RadialViewImpl() = default; | ||||||
| 
 | 
 | ||||||
|    std::chrono::system_clock::time_point selectedTime_; |    std::vector<float>        vertices_; | ||||||
|  |    std::vector<std::uint8_t> dataMoments8_; | ||||||
| 
 | 
 | ||||||
|    std::vector<float>   vertices_; |    float         latitude_; | ||||||
|    std::vector<uint8_t> dataMoments8_; |    float         longitude_; | ||||||
| 
 |    float         range_; | ||||||
|    float    latitude_; |    std::uint16_t vcp_; | ||||||
|    float    longitude_; |  | ||||||
|    float    range_; |  | ||||||
|    uint16_t vcp_; |  | ||||||
| 
 | 
 | ||||||
|    std::chrono::system_clock::time_point sweepTime_; |    std::chrono::system_clock::time_point sweepTime_; | ||||||
| }; | }; | ||||||
|  | @ -92,11 +85,6 @@ std::tuple<const void*, size_t, size_t> Level3RadialView::GetMomentData() const | ||||||
|    return std::tie(data, dataSize, componentSize); |    return std::tie(data, dataSize, componentSize); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Level3RadialView::SelectTime(std::chrono::system_clock::time_point time) |  | ||||||
| { |  | ||||||
|    p->selectedTime_ = time; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Level3RadialView::ComputeSweep() | void Level3RadialView::ComputeSweep() | ||||||
| { | { | ||||||
|    logger_->debug("ComputeSweep()"); |    logger_->debug("ComputeSweep()"); | ||||||
|  | @ -109,9 +97,18 @@ void Level3RadialView::ComputeSweep() | ||||||
|       radar_product_manager(); |       radar_product_manager(); | ||||||
| 
 | 
 | ||||||
|    // Retrieve message from Radar Product Manager
 |    // Retrieve message from Radar Product Manager
 | ||||||
|    std::shared_ptr<wsr88d::rpg::Level3Message> message = |    std::shared_ptr<wsr88d::rpg::Level3Message> message; | ||||||
|       radarProductManager->GetLevel3Data(GetRadarProductName(), |    std::chrono::system_clock::time_point       requestedTime {selected_time()}; | ||||||
|                                          p->selectedTime_); |    std::chrono::system_clock::time_point       foundTime; | ||||||
|  |    std::tie(message, foundTime) = | ||||||
|  |       radarProductManager->GetLevel3Data(GetRadarProductName(), requestedTime); | ||||||
|  | 
 | ||||||
|  |    // If a different time was found than what was requested, update it
 | ||||||
|  |    if (requestedTime != foundTime) | ||||||
|  |    { | ||||||
|  |       SelectTime(foundTime); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|    if (message == nullptr) |    if (message == nullptr) | ||||||
|    { |    { | ||||||
|       logger_->debug("Level 3 data not found"); |       logger_->debug("Level 3 data not found"); | ||||||
|  |  | ||||||
|  | @ -27,12 +27,11 @@ public: | ||||||
| 
 | 
 | ||||||
|    float                                 range() const override; |    float                                 range() const override; | ||||||
|    std::chrono::system_clock::time_point sweep_time() const override; |    std::chrono::system_clock::time_point sweep_time() const override; | ||||||
|    uint16_t                              vcp() const override; |    std::uint16_t                         vcp() const override; | ||||||
|    const std::vector<float>&             vertices() const override; |    const std::vector<float>&             vertices() const override; | ||||||
| 
 | 
 | ||||||
|    void SelectTime(std::chrono::system_clock::time_point time) override; |    std::tuple<const void*, std::size_t, std::size_t> | ||||||
| 
 |    GetMomentData() const override; | ||||||
|    std::tuple<const void*, size_t, size_t> GetMomentData() const override; |  | ||||||
| 
 | 
 | ||||||
|    static std::shared_ptr<Level3RadialView> |    static std::shared_ptr<Level3RadialView> | ||||||
|    Create(const std::string&                            product, |    Create(const std::string&                            product, | ||||||
|  |  | ||||||
|  | @ -27,18 +27,11 @@ class Level3RasterViewImpl | ||||||
| { | { | ||||||
| public: | public: | ||||||
|    explicit Level3RasterViewImpl() : |    explicit Level3RasterViewImpl() : | ||||||
|        selectedTime_ {}, |        latitude_ {}, longitude_ {}, range_ {}, vcp_ {}, sweepTime_ {} | ||||||
|        latitude_ {}, |  | ||||||
|        longitude_ {}, |  | ||||||
|        range_ {}, |  | ||||||
|        vcp_ {}, |  | ||||||
|        sweepTime_ {} |  | ||||||
|    { |    { | ||||||
|    } |    } | ||||||
|    ~Level3RasterViewImpl() = default; |    ~Level3RasterViewImpl() = default; | ||||||
| 
 | 
 | ||||||
|    std::chrono::system_clock::time_point selectedTime_; |  | ||||||
| 
 |  | ||||||
|    std::vector<float>   vertices_; |    std::vector<float>   vertices_; | ||||||
|    std::vector<uint8_t> dataMoments8_; |    std::vector<uint8_t> dataMoments8_; | ||||||
| 
 | 
 | ||||||
|  | @ -92,11 +85,6 @@ std::tuple<const void*, size_t, size_t> Level3RasterView::GetMomentData() const | ||||||
|    return std::tie(data, dataSize, componentSize); |    return std::tie(data, dataSize, componentSize); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Level3RasterView::SelectTime(std::chrono::system_clock::time_point time) |  | ||||||
| { |  | ||||||
|    p->selectedTime_ = time; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void Level3RasterView::ComputeSweep() | void Level3RasterView::ComputeSweep() | ||||||
| { | { | ||||||
|    logger_->debug("ComputeSweep()"); |    logger_->debug("ComputeSweep()"); | ||||||
|  | @ -109,9 +97,18 @@ void Level3RasterView::ComputeSweep() | ||||||
|       radar_product_manager(); |       radar_product_manager(); | ||||||
| 
 | 
 | ||||||
|    // Retrieve message from Radar Product Manager
 |    // Retrieve message from Radar Product Manager
 | ||||||
|    std::shared_ptr<wsr88d::rpg::Level3Message> message = |    std::shared_ptr<wsr88d::rpg::Level3Message> message; | ||||||
|       radarProductManager->GetLevel3Data(GetRadarProductName(), |    std::chrono::system_clock::time_point       requestedTime {selected_time()}; | ||||||
|                                          p->selectedTime_); |    std::chrono::system_clock::time_point       foundTime; | ||||||
|  |    std::tie(message, foundTime) = | ||||||
|  |       radarProductManager->GetLevel3Data(GetRadarProductName(), requestedTime); | ||||||
|  | 
 | ||||||
|  |    // If a different time was found than what was requested, update it
 | ||||||
|  |    if (requestedTime != foundTime) | ||||||
|  |    { | ||||||
|  |       SelectTime(foundTime); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|    if (message == nullptr) |    if (message == nullptr) | ||||||
|    { |    { | ||||||
|       logger_->debug("Level 3 data not found"); |       logger_->debug("Level 3 data not found"); | ||||||
|  |  | ||||||
|  | @ -27,12 +27,11 @@ public: | ||||||
| 
 | 
 | ||||||
|    float                                 range() const override; |    float                                 range() const override; | ||||||
|    std::chrono::system_clock::time_point sweep_time() const override; |    std::chrono::system_clock::time_point sweep_time() const override; | ||||||
|    uint16_t                              vcp() const override; |    std::uint16_t                         vcp() const override; | ||||||
|    const std::vector<float>&             vertices() const override; |    const std::vector<float>&             vertices() const override; | ||||||
| 
 | 
 | ||||||
|    void SelectTime(std::chrono::system_clock::time_point time) override; |    std::tuple<const void*, std::size_t, std::size_t> | ||||||
| 
 |    GetMomentData() const override; | ||||||
|    std::tuple<const void*, size_t, size_t> GetMomentData() const override; |  | ||||||
| 
 | 
 | ||||||
|    static std::shared_ptr<Level3RasterView> |    static std::shared_ptr<Level3RasterView> | ||||||
|    Create(const std::string&                            product, |    Create(const std::string&                            product, | ||||||
|  |  | ||||||
|  | @ -16,12 +16,12 @@ static const std::string logPrefix_ = "scwx::qt::view::radar_product_view"; | ||||||
| static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | ||||||
| 
 | 
 | ||||||
| // Default color table should be transparent to prevent flicker
 | // Default color table should be transparent to prevent flicker
 | ||||||
| static const std::vector<boost::gil::rgba8_pixel_t> DEFAULT_COLOR_TABLE = { | static const std::vector<boost::gil::rgba8_pixel_t> kDefaultColorTable_ = { | ||||||
|    boost::gil::rgba8_pixel_t(0, 128, 0, 0), |    boost::gil::rgba8_pixel_t(0, 128, 0, 0), | ||||||
|    boost::gil::rgba8_pixel_t(255, 192, 0, 0), |    boost::gil::rgba8_pixel_t(255, 192, 0, 0), | ||||||
|    boost::gil::rgba8_pixel_t(255, 0, 0, 0)}; |    boost::gil::rgba8_pixel_t(255, 0, 0, 0)}; | ||||||
| static const uint16_t DEFAULT_COLOR_TABLE_MIN = 2u; | static const std::uint16_t kDefaultColorTableMin_ = 2u; | ||||||
| static const uint16_t DEFAULT_COLOR_TABLE_MAX = 255u; | static const std::uint16_t kDefaultColorTableMax_ = 255u; | ||||||
| 
 | 
 | ||||||
| class RadarProductViewImpl | class RadarProductViewImpl | ||||||
| { | { | ||||||
|  | @ -30,6 +30,7 @@ public: | ||||||
|       std::shared_ptr<manager::RadarProductManager> radarProductManager) : |       std::shared_ptr<manager::RadarProductManager> radarProductManager) : | ||||||
|        initialized_ {false}, |        initialized_ {false}, | ||||||
|        sweepMutex_ {}, |        sweepMutex_ {}, | ||||||
|  |        selectedTime_ {}, | ||||||
|        radarProductManager_ {radarProductManager} |        radarProductManager_ {radarProductManager} | ||||||
|    { |    { | ||||||
|    } |    } | ||||||
|  | @ -38,6 +39,8 @@ public: | ||||||
|    bool       initialized_; |    bool       initialized_; | ||||||
|    std::mutex sweepMutex_; |    std::mutex sweepMutex_; | ||||||
| 
 | 
 | ||||||
|  |    std::chrono::system_clock::time_point selectedTime_; | ||||||
|  | 
 | ||||||
|    std::shared_ptr<manager::RadarProductManager> radarProductManager_; |    std::shared_ptr<manager::RadarProductManager> radarProductManager_; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -49,17 +52,17 @@ RadarProductView::~RadarProductView() = default; | ||||||
| const std::vector<boost::gil::rgba8_pixel_t>& | const std::vector<boost::gil::rgba8_pixel_t>& | ||||||
| RadarProductView::color_table() const | RadarProductView::color_table() const | ||||||
| { | { | ||||||
|    return DEFAULT_COLOR_TABLE; |    return kDefaultColorTable_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint16_t RadarProductView::color_table_min() const | std::uint16_t RadarProductView::color_table_min() const | ||||||
| { | { | ||||||
|    return DEFAULT_COLOR_TABLE_MIN; |    return kDefaultColorTableMin_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint16_t RadarProductView::color_table_max() const | std::uint16_t RadarProductView::color_table_max() const | ||||||
| { | { | ||||||
|    return DEFAULT_COLOR_TABLE_MAX; |    return kDefaultColorTableMax_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| float RadarProductView::elevation() const | float RadarProductView::elevation() const | ||||||
|  | @ -78,6 +81,11 @@ float RadarProductView::range() const | ||||||
|    return 0.0f; |    return 0.0f; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | std::chrono::system_clock::time_point RadarProductView::selected_time() const | ||||||
|  | { | ||||||
|  |    return p->selectedTime_; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| std::chrono::system_clock::time_point RadarProductView::sweep_time() const | std::chrono::system_clock::time_point RadarProductView::sweep_time() const | ||||||
| { | { | ||||||
|    return {}; |    return {}; | ||||||
|  | @ -91,7 +99,9 @@ std::mutex& RadarProductView::sweep_mutex() | ||||||
| void RadarProductView::set_radar_product_manager( | void RadarProductView::set_radar_product_manager( | ||||||
|    std::shared_ptr<manager::RadarProductManager> radarProductManager) |    std::shared_ptr<manager::RadarProductManager> radarProductManager) | ||||||
| { | { | ||||||
|  |    DisconnectRadarProductManager(); | ||||||
|    p->radarProductManager_ = radarProductManager; |    p->radarProductManager_ = radarProductManager; | ||||||
|  |    ConnectRadarProductManager(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RadarProductView::Initialize() | void RadarProductView::Initialize() | ||||||
|  | @ -103,6 +113,11 @@ void RadarProductView::Initialize() | ||||||
| 
 | 
 | ||||||
| void RadarProductView::SelectElevation(float /*elevation*/) {} | void RadarProductView::SelectElevation(float /*elevation*/) {} | ||||||
| 
 | 
 | ||||||
|  | void RadarProductView::SelectTime(std::chrono::system_clock::time_point time) | ||||||
|  | { | ||||||
|  |    p->selectedTime_ = time; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool RadarProductView::IsInitialized() const | bool RadarProductView::IsInitialized() const | ||||||
| { | { | ||||||
|    return p->initialized_; |    return p->initialized_; | ||||||
|  | @ -113,12 +128,12 @@ std::vector<float> RadarProductView::GetElevationCuts() const | ||||||
|    return {}; |    return {}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::tuple<const void*, size_t, size_t> | std::tuple<const void*, std::size_t, std::size_t> | ||||||
| RadarProductView::GetCfpMomentData() const | RadarProductView::GetCfpMomentData() const | ||||||
| { | { | ||||||
|    const void* data          = nullptr; |    const void* data          = nullptr; | ||||||
|    size_t      dataSize      = 0; |    std::size_t dataSize      = 0; | ||||||
|    size_t      componentSize = 0; |    std::size_t componentSize = 0; | ||||||
| 
 | 
 | ||||||
|    return std::tie(data, dataSize, componentSize); |    return std::tie(data, dataSize, componentSize); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -30,15 +30,16 @@ public: | ||||||
|    virtual ~RadarProductView(); |    virtual ~RadarProductView(); | ||||||
| 
 | 
 | ||||||
|    virtual const std::vector<boost::gil::rgba8_pixel_t>& color_table() const; |    virtual const std::vector<boost::gil::rgba8_pixel_t>& color_table() const; | ||||||
|    virtual uint16_t                              color_table_min() const; |    virtual std::uint16_t                         color_table_min() const; | ||||||
|    virtual uint16_t                              color_table_max() const; |    virtual std::uint16_t                         color_table_max() const; | ||||||
|    virtual float                                 elevation() const; |    virtual float                                 elevation() const; | ||||||
|    virtual float                                 range() const; |    virtual float                                 range() const; | ||||||
|    virtual std::chrono::system_clock::time_point sweep_time() const; |    virtual std::chrono::system_clock::time_point sweep_time() const; | ||||||
|    virtual uint16_t                              vcp() const      = 0; |    virtual std::uint16_t                         vcp() const      = 0; | ||||||
|    virtual const std::vector<float>&             vertices() const = 0; |    virtual const std::vector<float>&             vertices() const = 0; | ||||||
| 
 | 
 | ||||||
|    std::shared_ptr<manager::RadarProductManager> radar_product_manager() const; |    std::shared_ptr<manager::RadarProductManager> radar_product_manager() const; | ||||||
|  |    std::chrono::system_clock::time_point         selected_time() const; | ||||||
|    std::mutex&                                   sweep_mutex(); |    std::mutex&                                   sweep_mutex(); | ||||||
| 
 | 
 | ||||||
|    void set_radar_product_manager( |    void set_radar_product_manager( | ||||||
|  | @ -48,20 +49,24 @@ public: | ||||||
|    virtual void |    virtual void | ||||||
|    LoadColorTable(std::shared_ptr<common::ColorTable> colorTable) = 0; |    LoadColorTable(std::shared_ptr<common::ColorTable> colorTable) = 0; | ||||||
|    virtual void SelectElevation(float elevation); |    virtual void SelectElevation(float elevation); | ||||||
|    virtual void SelectProduct(const std::string& productName)          = 0; |    virtual void SelectProduct(const std::string& productName) = 0; | ||||||
|    virtual void SelectTime(std::chrono::system_clock::time_point time) = 0; |    void         SelectTime(std::chrono::system_clock::time_point time); | ||||||
|    virtual void Update()                                               = 0; |    virtual void Update() = 0; | ||||||
| 
 | 
 | ||||||
|    bool IsInitialized() const; |    bool IsInitialized() const; | ||||||
| 
 | 
 | ||||||
|    virtual common::RadarProductGroup GetRadarProductGroup() const = 0; |    virtual common::RadarProductGroup GetRadarProductGroup() const = 0; | ||||||
|    virtual std::string               GetRadarProductName() const  = 0; |    virtual std::string               GetRadarProductName() const  = 0; | ||||||
|    virtual std::vector<float>        GetElevationCuts() const; |    virtual std::vector<float>        GetElevationCuts() const; | ||||||
|    virtual std::tuple<const void*, size_t, size_t> GetMomentData() const = 0; |    virtual std::tuple<const void*, std::size_t, std::size_t> | ||||||
|    virtual std::tuple<const void*, size_t, size_t> GetCfpMomentData() const; |    GetMomentData() const = 0; | ||||||
|  |    virtual std::tuple<const void*, std::size_t, std::size_t> | ||||||
|  |    GetCfpMomentData() const; | ||||||
| 
 | 
 | ||||||
| protected: | protected: | ||||||
|    virtual void UpdateColorTable() = 0; |    virtual void ConnectRadarProductManager()    = 0; | ||||||
|  |    virtual void DisconnectRadarProductManager() = 0; | ||||||
|  |    virtual void UpdateColorTable()              = 0; | ||||||
| 
 | 
 | ||||||
| protected slots: | protected slots: | ||||||
|    virtual void ComputeSweep(); |    virtual void ComputeSweep(); | ||||||
|  |  | ||||||
|  | @ -8,10 +8,10 @@ namespace scwx | ||||||
| namespace util | namespace util | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| template<class Key, class T, class ReturnType = std::optional<T>> | template<class Key, class T, class ReturnType = std::map<Key, T>::const_pointer> | ||||||
| ReturnType GetBoundedElement(std::map<Key, T>& map, Key key) | ReturnType GetBoundedElementPointer(std::map<Key, T>& map, const Key& key) | ||||||
| { | { | ||||||
|    ReturnType element; |    ReturnType elementPtr {nullptr}; | ||||||
| 
 | 
 | ||||||
|    // Find the first element greater than the key requested
 |    // Find the first element greater than the key requested
 | ||||||
|    auto it = map.upper_bound(key); |    auto it = map.upper_bound(key); | ||||||
|  | @ -24,26 +24,42 @@ ReturnType GetBoundedElement(std::map<Key, T>& map, Key key) | ||||||
|       { |       { | ||||||
|          // Get the element immediately preceding, this the element we are
 |          // Get the element immediately preceding, this the element we are
 | ||||||
|          // looking for
 |          // looking for
 | ||||||
|          element = (--it)->second; |          elementPtr = &(*(--it)); | ||||||
|       } |       } | ||||||
|       else |       else | ||||||
|       { |       { | ||||||
|          // The current element is a good substitute
 |          // The current element is a good substitute
 | ||||||
|          element = it->second; |          elementPtr = &(*it); | ||||||
|       } |       } | ||||||
|    } |    } | ||||||
|    else if (map.size() > 0) |    else if (map.size() > 0) | ||||||
|    { |    { | ||||||
|       // An element with a key greater was not found. If it exists, it must be
 |       // An element with a key greater was not found. If it exists, it must be
 | ||||||
|       // the last element.
 |       // the last element.
 | ||||||
|       element = map.rbegin()->second; |       elementPtr = &(*map.rbegin()); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    return elementPtr; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<class Key, class T, class ReturnType = std::optional<T>> | ||||||
|  | ReturnType GetBoundedElement(std::map<Key, T>& map, const Key& key) | ||||||
|  | { | ||||||
|  |    ReturnType element; | ||||||
|  | 
 | ||||||
|  |    typename std::map<Key, T>::pointer elementPtr = | ||||||
|  |       GetBoundedElementPointer<Key, T, typename std::map<Key, T>::pointer>(map, | ||||||
|  |                                                                            key); | ||||||
|  |    if (elementPtr != nullptr) | ||||||
|  |    { | ||||||
|  |       element = elementPtr->second; | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    return element; |    return element; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template<class Key, class T> | template<class Key, class T> | ||||||
| inline T GetBoundedElementValue(std::map<Key, T>& map, Key key) | inline T GetBoundedElementValue(std::map<Key, T>& map, const Key& key) | ||||||
| { | { | ||||||
|    return GetBoundedElement<Key, T, T>(map, key); |    return GetBoundedElement<Key, T, T>(map, key); | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat