mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 14:10:05 +00:00 
			
		
		
		
	Merge pull request #62 from dpaulat/feature/timeline-part-3
Complete Timeline Animation
This commit is contained in:
		
						commit
						effe78e1be
					
				
					 10 changed files with 730 additions and 525 deletions
				
			
		|  | @ -211,6 +211,7 @@ MainWindow::MainWindow(QWidget* parent) : | ||||||
|    ui->resourceExplorerDock->toggleViewAction()->setText( |    ui->resourceExplorerDock->toggleViewAction()->setText( | ||||||
|       tr("&Resource Explorer")); |       tr("&Resource Explorer")); | ||||||
|    ui->actionResourceExplorer->setVisible(false); |    ui->actionResourceExplorer->setVisible(false); | ||||||
|  |    ui->resourceExplorerDock->toggleViewAction()->setVisible(false); | ||||||
| 
 | 
 | ||||||
|    ui->menuView->insertAction(ui->actionAlerts, |    ui->menuView->insertAction(ui->actionAlerts, | ||||||
|                               p->alertDockWidget_->toggleViewAction()); |                               p->alertDockWidget_->toggleViewAction()); | ||||||
|  | @ -711,7 +712,10 @@ void MainWindowImpl::ConnectAnimationSignals() | ||||||
|            &manager::TimelineManager::AnimationStateUpdated, |            &manager::TimelineManager::AnimationStateUpdated, | ||||||
|            animationDockWidget_, |            animationDockWidget_, | ||||||
|            &ui::AnimationDockWidget::UpdateAnimationState); |            &ui::AnimationDockWidget::UpdateAnimationState); | ||||||
| 
 |    connect(timelineManager_.get(), | ||||||
|  |            &manager::TimelineManager::ViewTypeUpdated, | ||||||
|  |            animationDockWidget_, | ||||||
|  |            &ui::AnimationDockWidget::UpdateViewType); | ||||||
|    connect(timelineManager_.get(), |    connect(timelineManager_.get(), | ||||||
|            &manager::TimelineManager::LiveStateUpdated, |            &manager::TimelineManager::LiveStateUpdated, | ||||||
|            animationDockWidget_, |            animationDockWidget_, | ||||||
|  | @ -790,6 +794,8 @@ void MainWindowImpl::ConnectOtherSignals() | ||||||
|          { |          { | ||||||
|             map->SetMapLocation(latitude, longitude, true); |             map->SetMapLocation(latitude, longitude, true); | ||||||
|          } |          } | ||||||
|  | 
 | ||||||
|  |          UpdateRadarSite(); | ||||||
|       }, |       }, | ||||||
|       Qt::QueuedConnection); |       Qt::QueuedConnection); | ||||||
|    connect(mainWindow_, |    connect(mainWindow_, | ||||||
|  | @ -807,6 +813,8 @@ void MainWindowImpl::ConnectOtherSignals() | ||||||
|               { |               { | ||||||
|                  map->SelectRadarSite(selectedRadarSite); |                  map->SelectRadarSite(selectedRadarSite); | ||||||
|               } |               } | ||||||
|  | 
 | ||||||
|  |               UpdateRadarSite(); | ||||||
|            }); |            }); | ||||||
|    connect(updateManager_.get(), |    connect(updateManager_.get(), | ||||||
|            &manager::UpdateManager::UpdateAvailable, |            &manager::UpdateManager::UpdateAvailable, | ||||||
|  |  | ||||||
|  | @ -115,6 +115,42 @@ | ||||||
|      <property name="bottomMargin"> |      <property name="bottomMargin"> | ||||||
|       <number>2</number> |       <number>2</number> | ||||||
|      </property> |      </property> | ||||||
|  |      <item> | ||||||
|  |       <widget class="QScrollArea" name="radarToolboxScrollArea"> | ||||||
|  |        <property name="frameShape"> | ||||||
|  |         <enum>QFrame::NoFrame</enum> | ||||||
|  |        </property> | ||||||
|  |        <property name="horizontalScrollBarPolicy"> | ||||||
|  |         <enum>Qt::ScrollBarAlwaysOff</enum> | ||||||
|  |        </property> | ||||||
|  |        <property name="sizeAdjustPolicy"> | ||||||
|  |         <enum>QAbstractScrollArea::AdjustToContents</enum> | ||||||
|  |        </property> | ||||||
|  |        <property name="widgetResizable"> | ||||||
|  |         <bool>true</bool> | ||||||
|  |        </property> | ||||||
|  |        <widget class="QWidget" name="radarToolboxScrollAreaContents"> | ||||||
|  |         <property name="geometry"> | ||||||
|  |          <rect> | ||||||
|  |           <x>0</x> | ||||||
|  |           <y>0</y> | ||||||
|  |           <width>175</width> | ||||||
|  |           <height>696</height> | ||||||
|  |          </rect> | ||||||
|  |         </property> | ||||||
|  |         <layout class="QVBoxLayout" name="verticalLayout_6"> | ||||||
|  |          <property name="leftMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="topMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="rightMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|  |          <property name="bottomMargin"> | ||||||
|  |           <number>0</number> | ||||||
|  |          </property> | ||||||
|          <item> |          <item> | ||||||
|           <widget class="QFrame" name="radarInfoFrame"> |           <widget class="QFrame" name="radarInfoFrame"> | ||||||
|            <property name="frameShape"> |            <property name="frameShape"> | ||||||
|  | @ -240,6 +276,23 @@ | ||||||
|            </layout> |            </layout> | ||||||
|           </widget> |           </widget> | ||||||
|          </item> |          </item> | ||||||
|  |          <item> | ||||||
|  |           <spacer name="radarToolboxSpacer"> | ||||||
|  |            <property name="orientation"> | ||||||
|  |             <enum>Qt::Vertical</enum> | ||||||
|  |            </property> | ||||||
|  |            <property name="sizeHint" stdset="0"> | ||||||
|  |             <size> | ||||||
|  |              <width>20</width> | ||||||
|  |              <height>0</height> | ||||||
|  |             </size> | ||||||
|  |            </property> | ||||||
|  |           </spacer> | ||||||
|  |          </item> | ||||||
|  |         </layout> | ||||||
|  |        </widget> | ||||||
|  |       </widget> | ||||||
|  |      </item> | ||||||
|      <item> |      <item> | ||||||
|       <widget class="QWidget" name="settingsFrame" native="true"> |       <widget class="QWidget" name="settingsFrame" native="true"> | ||||||
|        <layout class="QVBoxLayout" name="verticalLayout_4"> |        <layout class="QVBoxLayout" name="verticalLayout_4"> | ||||||
|  | @ -258,19 +311,6 @@ | ||||||
|        </layout> |        </layout> | ||||||
|       </widget> |       </widget> | ||||||
|      </item> |      </item> | ||||||
|      <item> |  | ||||||
|       <spacer name="radarToolboxSpacer"> |  | ||||||
|        <property name="orientation"> |  | ||||||
|         <enum>Qt::Vertical</enum> |  | ||||||
|        </property> |  | ||||||
|        <property name="sizeHint" stdset="0"> |  | ||||||
|         <size> |  | ||||||
|          <width>20</width> |  | ||||||
|          <height>0</height> |  | ||||||
|         </size> |  | ||||||
|        </property> |  | ||||||
|       </spacer> |  | ||||||
|      </item> |  | ||||||
|     </layout> |     </layout> | ||||||
|    </widget> |    </widget> | ||||||
|   </widget> |   </widget> | ||||||
|  |  | ||||||
|  | @ -19,7 +19,9 @@ | ||||||
| #   pragma warning(push, 0) | #   pragma warning(push, 0) | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #include <boost/asio/post.hpp> | ||||||
| #include <boost/asio/steady_timer.hpp> | #include <boost/asio/steady_timer.hpp> | ||||||
|  | #include <boost/asio/thread_pool.hpp> | ||||||
| #include <boost/container_hash/hash.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> | ||||||
|  | @ -91,7 +93,7 @@ public: | ||||||
|        group_ {group}, |        group_ {group}, | ||||||
|        product_ {product}, |        product_ {product}, | ||||||
|        refreshEnabled_ {false}, |        refreshEnabled_ {false}, | ||||||
|        refreshTimer_ {scwx::util::io_context()}, |        refreshTimer_ {threadPool_}, | ||||||
|        refreshTimerMutex_ {}, |        refreshTimerMutex_ {}, | ||||||
|        provider_ {nullptr} |        provider_ {nullptr} | ||||||
|    { |    { | ||||||
|  | @ -106,6 +108,8 @@ public: | ||||||
| 
 | 
 | ||||||
|    void Disable(); |    void Disable(); | ||||||
| 
 | 
 | ||||||
|  |    boost::asio::thread_pool threadPool_ {1u}; | ||||||
|  | 
 | ||||||
|    const std::string                             radarId_; |    const std::string                             radarId_; | ||||||
|    const common::RadarProductGroup               group_; |    const common::RadarProductGroup               group_; | ||||||
|    const std::string                             product_; |    const std::string                             product_; | ||||||
|  | @ -179,6 +183,8 @@ public: | ||||||
| 
 | 
 | ||||||
|    RadarProductManager* self_; |    RadarProductManager* self_; | ||||||
| 
 | 
 | ||||||
|  |    boost::asio::thread_pool threadPool_ {4u}; | ||||||
|  | 
 | ||||||
|    std::shared_ptr<ProviderManager> |    std::shared_ptr<ProviderManager> | ||||||
|    GetLevel3ProviderManager(const std::string& product); |    GetLevel3ProviderManager(const std::string& product); | ||||||
| 
 | 
 | ||||||
|  | @ -199,6 +205,10 @@ public: | ||||||
|    void UpdateRecentRecords(RadarProductRecordList& recentList, |    void UpdateRecentRecords(RadarProductRecordList& recentList, | ||||||
|                             std::shared_ptr<types::RadarProductRecord> record); |                             std::shared_ptr<types::RadarProductRecord> record); | ||||||
| 
 | 
 | ||||||
|  |    void LoadNexradFileAsync(CreateNexradFileFunction                    load, | ||||||
|  |                             std::shared_ptr<request::NexradFileRequest> request, | ||||||
|  |                             std::mutex&                                 mutex, | ||||||
|  |                             std::chrono::system_clock::time_point       time); | ||||||
|    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, | ||||||
|                          RadarProductRecordMap&                recordMap, |                          RadarProductRecordMap&                recordMap, | ||||||
|  | @ -206,6 +216,14 @@ public: | ||||||
|                          std::mutex&                           loadDataMutex, |                          std::mutex&                           loadDataMutex, | ||||||
|                          std::shared_ptr<request::NexradFileRequest> request); |                          std::shared_ptr<request::NexradFileRequest> request); | ||||||
|    void PopulateLevel2ProductTimes(std::chrono::system_clock::time_point time); |    void PopulateLevel2ProductTimes(std::chrono::system_clock::time_point time); | ||||||
|  |    void PopulateLevel3ProductTimes(const std::string& product, | ||||||
|  |                                    std::chrono::system_clock::time_point time); | ||||||
|  | 
 | ||||||
|  |    static void | ||||||
|  |    PopulateProductTimes(std::shared_ptr<ProviderManager> providerManager, | ||||||
|  |                         RadarProductRecordMap&           productRecordMap, | ||||||
|  |                         std::shared_mutex&               productRecordMutex, | ||||||
|  |                         std::chrono::system_clock::time_point time); | ||||||
| 
 | 
 | ||||||
|    static void |    static void | ||||||
|    LoadNexradFile(CreateNexradFileFunction                    load, |    LoadNexradFile(CreateNexradFileFunction                    load, | ||||||
|  | @ -515,7 +533,8 @@ void RadarProductManager::EnableRefresh(common::RadarProductGroup group, | ||||||
|          p->GetLevel3ProviderManager(product); |          p->GetLevel3ProviderManager(product); | ||||||
| 
 | 
 | ||||||
|       // Only enable refresh on available products
 |       // Only enable refresh on available products
 | ||||||
|       scwx::util::async( |       boost::asio::post( | ||||||
|  |          p->threadPool_, | ||||||
|          [=, this]() |          [=, this]() | ||||||
|          { |          { | ||||||
|             providerManager->provider_->RequestAvailableProducts(); |             providerManager->provider_->RequestAvailableProducts(); | ||||||
|  | @ -606,7 +625,8 @@ void RadarProductManagerImpl::RefreshData( | ||||||
|       providerManager->refreshTimer_.cancel(); |       providerManager->refreshTimer_.cancel(); | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    scwx::util::async( |    boost::asio::post( | ||||||
|  |       threadPool_, | ||||||
|       [=, this]() |       [=, this]() | ||||||
|       { |       { | ||||||
|          auto [newObjects, totalObjects] = |          auto [newObjects, totalObjects] = | ||||||
|  | @ -766,9 +786,8 @@ void RadarProductManagerImpl::LoadProviderData( | ||||||
|                   providerManager->name(), |                   providerManager->name(), | ||||||
|                   scwx::util::TimeString(time)); |                   scwx::util::TimeString(time)); | ||||||
| 
 | 
 | ||||||
|    RadarProductManagerImpl::LoadNexradFile( |    LoadNexradFileAsync( | ||||||
|       [=, &recordMap, &recordMutex, &loadDataMutex]() |       [=, &recordMap, &recordMutex]() -> std::shared_ptr<wsr88d::NexradFile> | ||||||
|          -> std::shared_ptr<wsr88d::NexradFile> |  | ||||||
|       { |       { | ||||||
|          std::shared_ptr<types::RadarProductRecord> existingRecord = nullptr; |          std::shared_ptr<types::RadarProductRecord> existingRecord = nullptr; | ||||||
|          std::shared_ptr<wsr88d::NexradFile>        nexradFile     = nullptr; |          std::shared_ptr<wsr88d::NexradFile>        nexradFile     = nullptr; | ||||||
|  | @ -792,9 +811,18 @@ void RadarProductManagerImpl::LoadProviderData( | ||||||
|          if (existingRecord == nullptr) |          if (existingRecord == nullptr) | ||||||
|          { |          { | ||||||
|             std::string key = providerManager->provider_->FindKey(time); |             std::string key = providerManager->provider_->FindKey(time); | ||||||
|  | 
 | ||||||
|  |             if (!key.empty()) | ||||||
|  |             { | ||||||
|                nexradFile = providerManager->provider_->LoadObjectByKey(key); |                nexradFile = providerManager->provider_->LoadObjectByKey(key); | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|  |             { | ||||||
|  |                logger_->warn("Attempting to load object without key: {}", | ||||||
|  |                              scwx::util::TimeString(time)); | ||||||
|  |             } | ||||||
|  |          } | ||||||
|  |          else | ||||||
|          { |          { | ||||||
|             nexradFile = existingRecord->nexrad_file(); |             nexradFile = existingRecord->nexrad_file(); | ||||||
|          } |          } | ||||||
|  | @ -857,11 +885,15 @@ void RadarProductManager::LoadData( | ||||||
| { | { | ||||||
|    logger_->debug("LoadData()"); |    logger_->debug("LoadData()"); | ||||||
| 
 | 
 | ||||||
|  |    scwx::util::async( | ||||||
|  |       [=, &is]() | ||||||
|  |       { | ||||||
|          RadarProductManagerImpl::LoadNexradFile( |          RadarProductManagerImpl::LoadNexradFile( | ||||||
|             [=, &is]() -> std::shared_ptr<wsr88d::NexradFile> |             [=, &is]() -> std::shared_ptr<wsr88d::NexradFile> | ||||||
|             { return wsr88d::NexradFileFactory::Create(is); }, |             { return wsr88d::NexradFileFactory::Create(is); }, | ||||||
|             request, |             request, | ||||||
|             fileLoadMutex_); |             fileLoadMutex_); | ||||||
|  |       }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RadarProductManager::LoadFile( | void RadarProductManager::LoadFile( | ||||||
|  | @ -898,11 +930,15 @@ void RadarProductManager::LoadFile( | ||||||
|                           } |                           } | ||||||
|                        }); |                        }); | ||||||
| 
 | 
 | ||||||
|  |       scwx::util::async( | ||||||
|  |          [=]() | ||||||
|  |          { | ||||||
|             RadarProductManagerImpl::LoadNexradFile( |             RadarProductManagerImpl::LoadNexradFile( | ||||||
|                [=]() -> std::shared_ptr<wsr88d::NexradFile> |                [=]() -> std::shared_ptr<wsr88d::NexradFile> | ||||||
|                { return wsr88d::NexradFileFactory::Create(filename); }, |                { return wsr88d::NexradFileFactory::Create(filename); }, | ||||||
|                request, |                request, | ||||||
|                fileLoadMutex_); |                fileLoadMutex_); | ||||||
|  |          }); | ||||||
|    } |    } | ||||||
|    else if (request != nullptr) |    else if (request != nullptr) | ||||||
|    { |    { | ||||||
|  | @ -911,14 +947,22 @@ void RadarProductManager::LoadFile( | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RadarProductManagerImpl::LoadNexradFile( | void RadarProductManagerImpl::LoadNexradFileAsync( | ||||||
|    CreateNexradFileFunction                    load, |    CreateNexradFileFunction                    load, | ||||||
|    std::shared_ptr<request::NexradFileRequest> request, |    std::shared_ptr<request::NexradFileRequest> request, | ||||||
|    std::mutex&                                 mutex, |    std::mutex&                                 mutex, | ||||||
|    std::chrono::system_clock::time_point       time) |    std::chrono::system_clock::time_point       time) | ||||||
| { | { | ||||||
|    scwx::util::async( |    boost::asio::post(threadPool_, | ||||||
|                      [=, &mutex]() |                      [=, &mutex]() | ||||||
|  |                      { LoadNexradFile(load, request, mutex, time); }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RadarProductManagerImpl::LoadNexradFile( | ||||||
|  |    CreateNexradFileFunction                    load, | ||||||
|  |    std::shared_ptr<request::NexradFileRequest> request, | ||||||
|  |    std::mutex&                                 mutex, | ||||||
|  |    std::chrono::system_clock::time_point       time) | ||||||
| { | { | ||||||
|    std::unique_lock lock {mutex}; |    std::unique_lock lock {mutex}; | ||||||
| 
 | 
 | ||||||
|  | @ -955,11 +999,39 @@ void RadarProductManagerImpl::LoadNexradFile( | ||||||
|       request->set_radar_product_record(record); |       request->set_radar_product_record(record); | ||||||
|       Q_EMIT request->RequestComplete(request); |       Q_EMIT request->RequestComplete(request); | ||||||
|    } |    } | ||||||
|       }); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RadarProductManagerImpl::PopulateLevel2ProductTimes( | void RadarProductManagerImpl::PopulateLevel2ProductTimes( | ||||||
|    std::chrono::system_clock::time_point time) |    std::chrono::system_clock::time_point time) | ||||||
|  | { | ||||||
|  |    PopulateProductTimes(level2ProviderManager_, | ||||||
|  |                         level2ProductRecords_, | ||||||
|  |                         level2ProductRecordMutex_, | ||||||
|  |                         time); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RadarProductManagerImpl::PopulateLevel3ProductTimes( | ||||||
|  |    const std::string& product, std::chrono::system_clock::time_point time) | ||||||
|  | { | ||||||
|  |    // Get provider manager
 | ||||||
|  |    auto level3ProviderManager = GetLevel3ProviderManager(product); | ||||||
|  | 
 | ||||||
|  |    // Get product records
 | ||||||
|  |    std::unique_lock level3ProductRecordLock {level3ProductRecordMutex_}; | ||||||
|  |    auto&            level3ProductRecords = level3ProductRecordsMap_[product]; | ||||||
|  |    level3ProductRecordLock.unlock(); | ||||||
|  | 
 | ||||||
|  |    PopulateProductTimes(level3ProviderManager, | ||||||
|  |                         level3ProductRecords, | ||||||
|  |                         level3ProductRecordMutex_, | ||||||
|  |                         time); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void RadarProductManagerImpl::PopulateProductTimes( | ||||||
|  |    std::shared_ptr<ProviderManager>      providerManager, | ||||||
|  |    RadarProductRecordMap&                productRecordMap, | ||||||
|  |    std::shared_mutex&                    productRecordMutex, | ||||||
|  |    std::chrono::system_clock::time_point time) | ||||||
| { | { | ||||||
|    const auto today     = std::chrono::floor<std::chrono::days>(time); |    const auto today     = std::chrono::floor<std::chrono::days>(time); | ||||||
|    const auto yesterday = today - std::chrono::days {1}; |    const auto yesterday = today - std::chrono::days {1}; | ||||||
|  | @ -973,7 +1045,7 @@ void RadarProductManagerImpl::PopulateLevel2ProductTimes( | ||||||
|    std::for_each(std::execution::par_unseq, |    std::for_each(std::execution::par_unseq, | ||||||
|                  dates.begin(), |                  dates.begin(), | ||||||
|                  dates.end(), |                  dates.end(), | ||||||
|                  [&, this](const auto& date) |                  [&](const auto& date) | ||||||
|                  { |                  { | ||||||
|                     // Don't query for a time point in the future
 |                     // Don't query for a time point in the future
 | ||||||
|                     if (date > std::chrono::system_clock::now()) |                     if (date > std::chrono::system_clock::now()) | ||||||
|  | @ -983,8 +1055,7 @@ void RadarProductManagerImpl::PopulateLevel2ProductTimes( | ||||||
| 
 | 
 | ||||||
|                     // Query the provider for volume time points
 |                     // Query the provider for volume time points
 | ||||||
|                     auto timePoints = |                     auto timePoints = | ||||||
|                        level2ProviderManager_->provider_->GetTimePointsByDate( |                        providerManager->provider_->GetTimePointsByDate(date); | ||||||
|                           date); |  | ||||||
| 
 | 
 | ||||||
|                     // Lock the merged volume time list
 |                     // Lock the merged volume time list
 | ||||||
|                     std::unique_lock volumeTimesLock {volumeTimesMutex}; |                     std::unique_lock volumeTimesLock {volumeTimesMutex}; | ||||||
|  | @ -995,14 +1066,13 @@ void RadarProductManagerImpl::PopulateLevel2ProductTimes( | ||||||
|                               std::inserter(volumeTimes, volumeTimes.end())); |                               std::inserter(volumeTimes, volumeTimes.end())); | ||||||
|                  }); |                  }); | ||||||
| 
 | 
 | ||||||
|    // Lock the level 2 product record map
 |    // Lock the product record map
 | ||||||
|    std::unique_lock lock {level2ProductRecordMutex_}; |    std::unique_lock lock {productRecordMutex}; | ||||||
| 
 | 
 | ||||||
|    // Merge volume times into map
 |    // Merge volume times into map
 | ||||||
|    std::transform( |    std::transform(volumeTimes.cbegin(), | ||||||
|       volumeTimes.cbegin(), |  | ||||||
|                   volumeTimes.cend(), |                   volumeTimes.cend(), | ||||||
|       std::inserter(level2ProductRecords_, level2ProductRecords_.begin()), |                   std::inserter(productRecordMap, productRecordMap.begin()), | ||||||
|                   [](const std::chrono::system_clock::time_point& time) |                   [](const std::chrono::system_clock::time_point& time) | ||||||
|                   { |                   { | ||||||
|                      return std::pair<std::chrono::system_clock::time_point, |                      return std::pair<std::chrono::system_clock::time_point, | ||||||
|  | @ -1042,7 +1112,7 @@ RadarProductManagerImpl::GetLevel2ProductRecord( | ||||||
|       record     = recordPtr->second.lock(); |       record     = recordPtr->second.lock(); | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|    if (record == nullptr && |    if (recordPtr != nullptr && record == nullptr && | ||||||
|        recordTime != std::chrono::system_clock::time_point {}) |        recordTime != std::chrono::system_clock::time_point {}) | ||||||
|    { |    { | ||||||
|       // Product is expired, reload it
 |       // Product is expired, reload it
 | ||||||
|  | @ -1076,6 +1146,9 @@ RadarProductManagerImpl::GetLevel3ProductRecord( | ||||||
|    RadarProductRecordMap::const_pointer       recordPtr {nullptr}; |    RadarProductRecordMap::const_pointer       recordPtr {nullptr}; | ||||||
|    std::chrono::system_clock::time_point      recordTime {time}; |    std::chrono::system_clock::time_point      recordTime {time}; | ||||||
| 
 | 
 | ||||||
|  |    // Ensure Level 3 product records are updated
 | ||||||
|  |    PopulateLevel3ProductTimes(product, time); | ||||||
|  | 
 | ||||||
|    std::unique_lock lock {level3ProductRecordMutex_}; |    std::unique_lock lock {level3ProductRecordMutex_}; | ||||||
| 
 | 
 | ||||||
|    auto it = level3ProductRecordsMap_.find(product); |    auto it = level3ProductRecordsMap_.find(product); | ||||||
|  | @ -1099,15 +1172,12 @@ RadarProductManagerImpl::GetLevel3ProductRecord( | ||||||
| 
 | 
 | ||||||
|    if (recordPtr != nullptr) |    if (recordPtr != nullptr) | ||||||
|    { |    { | ||||||
|       if (time == std::chrono::system_clock::time_point {} || |       // Don't check for an exact time match for level 3 products
 | ||||||
|           time == recordPtr->first) |  | ||||||
|       { |  | ||||||
|       recordTime = recordPtr->first; |       recordTime = recordPtr->first; | ||||||
|       record     = recordPtr->second.lock(); |       record     = recordPtr->second.lock(); | ||||||
|    } |    } | ||||||
|    } |  | ||||||
| 
 | 
 | ||||||
|    if (record == nullptr && |    if (recordPtr != nullptr && record == nullptr && | ||||||
|        recordTime != std::chrono::system_clock::time_point {}) |        recordTime != std::chrono::system_clock::time_point {}) | ||||||
|    { |    { | ||||||
|       // Product is expired, reload it
 |       // Product is expired, reload it
 | ||||||
|  | @ -1305,7 +1375,8 @@ void RadarProductManager::UpdateAvailableProducts() | ||||||
| 
 | 
 | ||||||
|    logger_->debug("UpdateAvailableProducts()"); |    logger_->debug("UpdateAvailableProducts()"); | ||||||
| 
 | 
 | ||||||
|    scwx::util::async( |    boost::asio::post( | ||||||
|  |       p->threadPool_, | ||||||
|       [this]() |       [this]() | ||||||
|       { |       { | ||||||
|          auto level3ProviderManager = |          auto level3ProviderManager = | ||||||
|  |  | ||||||
|  | @ -1,3 +1,5 @@ | ||||||
|  | #define NOMINMAX | ||||||
|  | 
 | ||||||
| #include <scwx/qt/manager/timeline_manager.hpp> | #include <scwx/qt/manager/timeline_manager.hpp> | ||||||
| #include <scwx/qt/manager/radar_product_manager.hpp> | #include <scwx/qt/manager/radar_product_manager.hpp> | ||||||
| #include <scwx/qt/manager/settings_manager.hpp> | #include <scwx/qt/manager/settings_manager.hpp> | ||||||
|  | @ -166,6 +168,8 @@ void TimelineManager::SetViewType(types::MapTime viewType) | ||||||
|       // If the selected view type is archive, select using the pinned time
 |       // If the selected view type is archive, select using the pinned time
 | ||||||
|       p->SelectTimeAsync(p->pinnedTime_); |       p->SelectTimeAsync(p->pinnedTime_); | ||||||
|    } |    } | ||||||
|  | 
 | ||||||
|  |    Q_EMIT ViewTypeUpdated(viewType); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void TimelineManager::SetLoopTime(std::chrono::minutes loopTime) | void TimelineManager::SetLoopTime(std::chrono::minutes loopTime) | ||||||
|  | @ -390,9 +394,10 @@ void TimelineManager::Impl::UpdateCacheLimit( | ||||||
|    auto endIter   = util::GetBoundedElementIterator(volumeTimes, endTime); |    auto endIter   = util::GetBoundedElementIterator(volumeTimes, endTime); | ||||||
|    std::size_t numVolumeScans = std::distance(startIter, endIter) + 1; |    std::size_t numVolumeScans = std::distance(startIter, endIter) + 1; | ||||||
| 
 | 
 | ||||||
|    // Dynamically update maximum cached volume scans to 1.5x the loop length
 |    // Dynamically update maximum cached volume scans to the lesser of
 | ||||||
|    radarProductManager->SetCacheLimit( |    // either 1.5x the loop length or 5 greater than the loop length
 | ||||||
|       static_cast<std::size_t>(numVolumeScans * 1.5)); |    radarProductManager->SetCacheLimit(std::min( | ||||||
|  |       static_cast<std::size_t>(numVolumeScans * 1.5), numVolumeScans + 5u)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void TimelineManager::Impl::Play() | void TimelineManager::Impl::Play() | ||||||
|  |  | ||||||
|  | @ -359,7 +359,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, | ||||||
|                                    std::int16_t              productCode, |                                    std::int16_t              productCode, | ||||||
|                                    std::chrono::system_clock::time_point time) |                                    std::chrono::system_clock::time_point time, | ||||||
|  |                                    bool                                  update) | ||||||
| { | { | ||||||
|    bool radarProductViewCreated = false; |    bool radarProductViewCreated = false; | ||||||
| 
 | 
 | ||||||
|  | @ -420,7 +421,7 @@ void MapWidget::SelectRadarProduct(common::RadarProductGroup group, | ||||||
|                common::GetLevel3Palette(productCode); |                common::GetLevel3Palette(productCode); | ||||||
|          p->InitializeNewRadarProductView(palette); |          p->InitializeNewRadarProductView(palette); | ||||||
|       } |       } | ||||||
|       else |       else if (update) | ||||||
|       { |       { | ||||||
|          radarProductView->Update(); |          radarProductView->Update(); | ||||||
|       } |       } | ||||||
|  | @ -486,7 +487,9 @@ void MapWidget::SelectRadarSite(std::shared_ptr<config::RadarSite> radarSite, | ||||||
|          radarProductView->set_radar_product_manager(p->radarProductManager_); |          radarProductView->set_radar_product_manager(p->radarProductManager_); | ||||||
|          SelectRadarProduct(radarProductView->GetRadarProductGroup(), |          SelectRadarProduct(radarProductView->GetRadarProductGroup(), | ||||||
|                             radarProductView->GetRadarProductName(), |                             radarProductView->GetRadarProductName(), | ||||||
|                             0); |                             0, | ||||||
|  |                             radarProductView->selected_time(), | ||||||
|  |                             false); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       AddLayers(); |       AddLayers(); | ||||||
|  | @ -1053,9 +1056,6 @@ void MapWidgetImpl::SetRadarSite(const std::string& radarSite) | ||||||
|       // Set new RadarProductManager
 |       // Set new RadarProductManager
 | ||||||
|       radarProductManager_ = manager::RadarProductManager::Instance(radarSite); |       radarProductManager_ = manager::RadarProductManager::Instance(radarSite); | ||||||
| 
 | 
 | ||||||
|       // Re-enable auto-update
 |  | ||||||
|       autoUpdateEnabled_ = true; |  | ||||||
| 
 |  | ||||||
|       // Connect signals to new RadarProductManager
 |       // Connect signals to new RadarProductManager
 | ||||||
|       RadarProductManagerConnect(); |       RadarProductManagerConnect(); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -54,12 +54,14 @@ public: | ||||||
|     * @param [in] group Radar product group |     * @param [in] group Radar product group | ||||||
|     * @param [in] product Radar product name |     * @param [in] product Radar product name | ||||||
|     * @param [in] productCode Radar product code (optional) |     * @param [in] productCode Radar product code (optional) | ||||||
|     * @paran [in] time Product time. Default is the latest available. |     * @param [in] time Product time. Default is the latest available. | ||||||
|  |     * @param [in] update Whether to update the radar product view on selection | ||||||
|     */ |     */ | ||||||
|    void SelectRadarProduct(common::RadarProductGroup group, |    void SelectRadarProduct(common::RadarProductGroup group, | ||||||
|                            const std::string&        product, |                            const std::string&        product, | ||||||
|                            std::int16_t              productCode        = 0, |                            std::int16_t              productCode        = 0, | ||||||
|                            std::chrono::system_clock::time_point time = {}); |                            std::chrono::system_clock::time_point time   = {}, | ||||||
|  |                            bool                                  update = true); | ||||||
| 
 | 
 | ||||||
|    void SelectRadarProduct(std::shared_ptr<types::RadarProductRecord> record); |    void SelectRadarProduct(std::shared_ptr<types::RadarProductRecord> record); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -20,20 +20,34 @@ static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | ||||||
| class AnimationDockWidgetImpl | class AnimationDockWidgetImpl | ||||||
| { | { | ||||||
| public: | public: | ||||||
|    explicit AnimationDockWidgetImpl(AnimationDockWidget* self) : self_ {self} {} |    explicit AnimationDockWidgetImpl(AnimationDockWidget* self) : self_ {self} | ||||||
|  |    { | ||||||
|  |       static const QString prefix   = QObject::tr("Auto Update"); | ||||||
|  |       static const QString disabled = QObject::tr("Disabled"); | ||||||
|  |       static const QString enabled  = QObject::tr("Enabled"); | ||||||
|  | 
 | ||||||
|  |       enabledString_  = QString("%1: %2").arg(prefix).arg(enabled); | ||||||
|  |       disabledString_ = QString("%1: %2").arg(prefix).arg(disabled); | ||||||
|  |    } | ||||||
|    ~AnimationDockWidgetImpl() = default; |    ~AnimationDockWidgetImpl() = default; | ||||||
| 
 | 
 | ||||||
|    const QIcon kPauseIcon_ {":/res/icons/font-awesome-6/pause-solid.svg"}; |    const QIcon kPauseIcon_ {":/res/icons/font-awesome-6/pause-solid.svg"}; | ||||||
|    const QIcon kPlayIcon_ {":/res/icons/font-awesome-6/play-solid.svg"}; |    const QIcon kPlayIcon_ {":/res/icons/font-awesome-6/play-solid.svg"}; | ||||||
| 
 | 
 | ||||||
|  |    QString enabledString_; | ||||||
|  |    QString disabledString_; | ||||||
|  | 
 | ||||||
|    AnimationDockWidget* self_; |    AnimationDockWidget* self_; | ||||||
| 
 | 
 | ||||||
|    types::AnimationState animationState_ {types::AnimationState::Pause}; |    types::AnimationState animationState_ {types::AnimationState::Pause}; | ||||||
|  |    types::MapTime        viewType_ {types::MapTime::Live}; | ||||||
|  |    bool                  isLive_ {true}; | ||||||
| 
 | 
 | ||||||
|    std::chrono::sys_days selectedDate_ {}; |    std::chrono::sys_days selectedDate_ {}; | ||||||
|    std::chrono::seconds  selectedTime_ {}; |    std::chrono::seconds  selectedTime_ {}; | ||||||
| 
 | 
 | ||||||
|    void ConnectSignals(); |    void ConnectSignals(); | ||||||
|  |    void UpdateAutoUpdateLabel(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| AnimationDockWidget::AnimationDockWidget(QWidget* parent) : | AnimationDockWidget::AnimationDockWidget(QWidget* parent) : | ||||||
|  | @ -210,6 +224,8 @@ void AnimationDockWidgetImpl::ConnectSignals() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void AnimationDockWidget::UpdateAnimationState(types::AnimationState state) | void AnimationDockWidget::UpdateAnimationState(types::AnimationState state) | ||||||
|  | { | ||||||
|  |    if (p->animationState_ != state) | ||||||
|    { |    { | ||||||
|       // Update icon to opposite of state
 |       // Update icon to opposite of state
 | ||||||
|       switch (state) |       switch (state) | ||||||
|  | @ -222,21 +238,43 @@ void AnimationDockWidget::UpdateAnimationState(types::AnimationState state) | ||||||
|          ui->playButton->setIcon(p->kPauseIcon_); |          ui->playButton->setIcon(p->kPauseIcon_); | ||||||
|          break; |          break; | ||||||
|       } |       } | ||||||
|  | 
 | ||||||
|  |       p->animationState_ = state; | ||||||
|  |       p->UpdateAutoUpdateLabel(); | ||||||
|  |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void AnimationDockWidget::UpdateLiveState(bool isLive) | void AnimationDockWidget::UpdateLiveState(bool isLive) | ||||||
| { | { | ||||||
|    static const QString prefix   = tr("Auto Update"); |    if (p->isLive_ != isLive) | ||||||
|    static const QString disabled = tr("Disabled"); |  | ||||||
|    static const QString enabled  = tr("Enabled"); |  | ||||||
| 
 |  | ||||||
|    if (isLive) |  | ||||||
|    { |    { | ||||||
|       ui->autoUpdateLabel->setText(QString("%1: %2").arg(prefix).arg(enabled)); |       p->isLive_ = isLive; | ||||||
|  |       p->UpdateAutoUpdateLabel(); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AnimationDockWidget::UpdateViewType(types::MapTime viewType) | ||||||
|  | { | ||||||
|  |    if (p->viewType_ != viewType) | ||||||
|  |    { | ||||||
|  |       p->viewType_ = viewType; | ||||||
|  |       p->UpdateAutoUpdateLabel(); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void AnimationDockWidgetImpl::UpdateAutoUpdateLabel() | ||||||
|  | { | ||||||
|  |    // Display "Auto Update: Enabled" if:
 | ||||||
|  |    // - The map is live, and auto-updating (map widget update)
 | ||||||
|  |    // - "Live" is selected, and the map is playing (timeline manager update)
 | ||||||
|  |    if (isLive_ || (viewType_ == types::MapTime::Live && | ||||||
|  |                    animationState_ == types::AnimationState::Play)) | ||||||
|  |    { | ||||||
|  |       self_->ui->autoUpdateLabel->setText(enabledString_); | ||||||
|    } |    } | ||||||
|    else |    else | ||||||
|    { |    { | ||||||
|       ui->autoUpdateLabel->setText(QString("%1: %2").arg(prefix).arg(disabled)); |       self_->ui->autoUpdateLabel->setText(disabledString_); | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -31,6 +31,7 @@ public: | ||||||
| public slots: | public slots: | ||||||
|    void UpdateAnimationState(types::AnimationState state); |    void UpdateAnimationState(types::AnimationState state); | ||||||
|    void UpdateLiveState(bool isLive); |    void UpdateLiveState(bool isLive); | ||||||
|  |    void UpdateViewType(types::MapTime viewType); | ||||||
| 
 | 
 | ||||||
| signals: | signals: | ||||||
|    void ViewTypeChanged(types::MapTime viewType); |    void ViewTypeChanged(types::MapTime viewType); | ||||||
|  |  | ||||||
|  | @ -7,7 +7,7 @@ | ||||||
|     <x>0</x> |     <x>0</x> | ||||||
|     <y>0</y> |     <y>0</y> | ||||||
|     <width>200</width> |     <width>200</width> | ||||||
|     <height>348</height> |     <height>543</height> | ||||||
|    </rect> |    </rect> | ||||||
|   </property> |   </property> | ||||||
|   <property name="windowTitle"> |   <property name="windowTitle"> | ||||||
|  | @ -15,6 +15,42 @@ | ||||||
|   </property> |   </property> | ||||||
|   <widget class="QWidget" name="dockWidgetContents"> |   <widget class="QWidget" name="dockWidgetContents"> | ||||||
|    <layout class="QVBoxLayout" name="verticalLayout"> |    <layout class="QVBoxLayout" name="verticalLayout"> | ||||||
|  |     <item> | ||||||
|  |      <widget class="QScrollArea" name="scrollArea"> | ||||||
|  |       <property name="frameShape"> | ||||||
|  |        <enum>QFrame::NoFrame</enum> | ||||||
|  |       </property> | ||||||
|  |       <property name="lineWidth"> | ||||||
|  |        <number>0</number> | ||||||
|  |       </property> | ||||||
|  |       <property name="horizontalScrollBarPolicy"> | ||||||
|  |        <enum>Qt::ScrollBarAlwaysOff</enum> | ||||||
|  |       </property> | ||||||
|  |       <property name="widgetResizable"> | ||||||
|  |        <bool>true</bool> | ||||||
|  |       </property> | ||||||
|  |       <widget class="QWidget" name="scrollAreaContents"> | ||||||
|  |        <property name="geometry"> | ||||||
|  |         <rect> | ||||||
|  |          <x>0</x> | ||||||
|  |          <y>0</y> | ||||||
|  |          <width>182</width> | ||||||
|  |          <height>506</height> | ||||||
|  |         </rect> | ||||||
|  |        </property> | ||||||
|  |        <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||||
|  |         <property name="leftMargin"> | ||||||
|  |          <number>0</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="topMargin"> | ||||||
|  |          <number>0</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="rightMargin"> | ||||||
|  |          <number>0</number> | ||||||
|  |         </property> | ||||||
|  |         <property name="bottomMargin"> | ||||||
|  |          <number>0</number> | ||||||
|  |         </property> | ||||||
|         <item> |         <item> | ||||||
|          <widget class="QGroupBox" name="timelineGroupBox"> |          <widget class="QGroupBox" name="timelineGroupBox"> | ||||||
|           <property name="title"> |           <property name="title"> | ||||||
|  | @ -309,6 +345,10 @@ | ||||||
|        </layout> |        </layout> | ||||||
|       </widget> |       </widget> | ||||||
|      </widget> |      </widget> | ||||||
|  |     </item> | ||||||
|  |    </layout> | ||||||
|  |   </widget> | ||||||
|  |  </widget> | ||||||
|  <resources> |  <resources> | ||||||
|   <include location="../../../../scwx-qt.qrc"/> |   <include location="../../../../scwx-qt.qrc"/> | ||||||
|  </resources> |  </resources> | ||||||
|  |  | ||||||
|  | @ -176,28 +176,28 @@ bool WmoHeader::Parse(std::istream& is) | ||||||
| 
 | 
 | ||||||
|       if (wmoTokenList.size() < 3 || wmoTokenList.size() > 4) |       if (wmoTokenList.size() < 3 || wmoTokenList.size() > 4) | ||||||
|       { |       { | ||||||
|          logger_->debug("Invalid number of WMO tokens"); |          logger_->warn("Invalid number of WMO tokens"); | ||||||
|          headerValid = false; |          headerValid = false; | ||||||
|       } |       } | ||||||
|       else if (wmoTokenList[0].size() != 6) |       else if (wmoTokenList[0].size() != 6) | ||||||
|       { |       { | ||||||
|          logger_->debug("WMO identifier malformed"); |          logger_->warn("WMO identifier malformed"); | ||||||
|          headerValid = false; |          headerValid = false; | ||||||
|       } |       } | ||||||
|       else if (wmoTokenList[1].size() != 4) |       else if (wmoTokenList[1].size() != 4) | ||||||
|       { |       { | ||||||
|          logger_->debug("ICAO malformed"); |          logger_->warn("ICAO malformed"); | ||||||
|          headerValid = false; |          headerValid = false; | ||||||
|       } |       } | ||||||
|       else if (wmoTokenList[2].size() != 6) |       else if (wmoTokenList[2].size() != 6) | ||||||
|       { |       { | ||||||
|          logger_->debug("Date/time malformed"); |          logger_->warn("Date/time malformed"); | ||||||
|          headerValid = false; |          headerValid = false; | ||||||
|       } |       } | ||||||
|       else if (wmoTokenList.size() == 4 && wmoTokenList[3].size() != 3) |       else if (wmoTokenList.size() == 4 && wmoTokenList[3].size() != 3) | ||||||
|       { |       { | ||||||
|          // BBB indicator is optional
 |          // BBB indicator is optional
 | ||||||
|          logger_->debug("BBB indicator malformed"); |          logger_->warn("BBB indicator malformed"); | ||||||
|          headerValid = false; |          headerValid = false; | ||||||
|       } |       } | ||||||
|       else |       else | ||||||
|  | @ -226,7 +226,7 @@ bool WmoHeader::Parse(std::istream& is) | ||||||
|    { |    { | ||||||
|       if (awipsLine.size() != 6) |       if (awipsLine.size() != 6) | ||||||
|       { |       { | ||||||
|          logger_->debug("AWIPS Identifier Line bad size"); |          logger_->warn("AWIPS Identifier Line bad size"); | ||||||
|          headerValid = false; |          headerValid = false; | ||||||
|       } |       } | ||||||
|       else |       else | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat