From ea1569cb769d4c43b61309fe0a8cb00aa7cc3bb2 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 18 Jun 2023 22:34:29 -0500 Subject: [PATCH 1/9] Don't attempt to load an object that hasn't been listed yet by the provider - Fixes issue when switching to a new product, product does not display the first time --- .../scwx/qt/manager/radar_product_manager.cpp | 15 ++++++++++++--- wxdata/source/scwx/awips/wmo_header.cpp | 12 ++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp b/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp index e9bb8463..f387f807 100644 --- a/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp @@ -792,7 +792,16 @@ void RadarProductManagerImpl::LoadProviderData( if (existingRecord == nullptr) { std::string key = providerManager->provider_->FindKey(time); - nexradFile = providerManager->provider_->LoadObjectByKey(key); + + if (!key.empty()) + { + nexradFile = providerManager->provider_->LoadObjectByKey(key); + } + else + { + logger_->warn("Attempting to load object without key: {}", + scwx::util::TimeString(time)); + } } else { @@ -1042,7 +1051,7 @@ RadarProductManagerImpl::GetLevel2ProductRecord( record = recordPtr->second.lock(); } - if (record == nullptr && + if (recordPtr != nullptr && record == nullptr && recordTime != std::chrono::system_clock::time_point {}) { // Product is expired, reload it @@ -1107,7 +1116,7 @@ RadarProductManagerImpl::GetLevel3ProductRecord( } } - if (record == nullptr && + if (recordPtr != nullptr && record == nullptr && recordTime != std::chrono::system_clock::time_point {}) { // Product is expired, reload it diff --git a/wxdata/source/scwx/awips/wmo_header.cpp b/wxdata/source/scwx/awips/wmo_header.cpp index 8e6a8519..f75cbded 100644 --- a/wxdata/source/scwx/awips/wmo_header.cpp +++ b/wxdata/source/scwx/awips/wmo_header.cpp @@ -176,28 +176,28 @@ bool WmoHeader::Parse(std::istream& is) if (wmoTokenList.size() < 3 || wmoTokenList.size() > 4) { - logger_->debug("Invalid number of WMO tokens"); + logger_->warn("Invalid number of WMO tokens"); headerValid = false; } else if (wmoTokenList[0].size() != 6) { - logger_->debug("WMO identifier malformed"); + logger_->warn("WMO identifier malformed"); headerValid = false; } else if (wmoTokenList[1].size() != 4) { - logger_->debug("ICAO malformed"); + logger_->warn("ICAO malformed"); headerValid = false; } else if (wmoTokenList[2].size() != 6) { - logger_->debug("Date/time malformed"); + logger_->warn("Date/time malformed"); headerValid = false; } else if (wmoTokenList.size() == 4 && wmoTokenList[3].size() != 3) { // BBB indicator is optional - logger_->debug("BBB indicator malformed"); + logger_->warn("BBB indicator malformed"); headerValid = false; } else @@ -226,7 +226,7 @@ bool WmoHeader::Parse(std::istream& is) { if (awipsLine.size() != 6) { - logger_->debug("AWIPS Identifier Line bad size"); + logger_->warn("AWIPS Identifier Line bad size"); headerValid = false; } else From 93ae58424cbd5ecac3039d442f60b74f7c615007 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Mon, 19 Jun 2023 00:16:51 -0500 Subject: [PATCH 2/9] Populate level 3 product times prior to loading, don't require an exact time match - Consistent with level 2, will allow choosing of the correct level 3 product if an exact time isn't given - Fixes step begin/end when not all products have the same volume times - Expected to help when switching radar sites, and attempting to save selected time from previous site --- .../scwx/qt/manager/radar_product_manager.cpp | 77 ++++++++++++++----- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp b/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp index f387f807..9c5e3991 100644 --- a/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp @@ -206,6 +206,14 @@ public: std::mutex& loadDataMutex, std::shared_ptr request); 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, + RadarProductRecordMap& productRecordMap, + std::shared_mutex& productRecordMutex, + std::chrono::system_clock::time_point time); static void LoadNexradFile(CreateNexradFileFunction load, @@ -969,6 +977,35 @@ void RadarProductManagerImpl::LoadNexradFile( void RadarProductManagerImpl::PopulateLevel2ProductTimes( 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, + RadarProductRecordMap& productRecordMap, + std::shared_mutex& productRecordMutex, + std::chrono::system_clock::time_point time) { const auto today = std::chrono::floor(time); const auto yesterday = today - std::chrono::days {1}; @@ -982,7 +1019,7 @@ void RadarProductManagerImpl::PopulateLevel2ProductTimes( std::for_each(std::execution::par_unseq, dates.begin(), dates.end(), - [&, this](const auto& date) + [&](const auto& date) { // Don't query for a time point in the future if (date > std::chrono::system_clock::now()) @@ -992,8 +1029,7 @@ void RadarProductManagerImpl::PopulateLevel2ProductTimes( // Query the provider for volume time points auto timePoints = - level2ProviderManager_->provider_->GetTimePointsByDate( - date); + providerManager->provider_->GetTimePointsByDate(date); // Lock the merged volume time list std::unique_lock volumeTimesLock {volumeTimesMutex}; @@ -1004,20 +1040,19 @@ void RadarProductManagerImpl::PopulateLevel2ProductTimes( std::inserter(volumeTimes, volumeTimes.end())); }); - // Lock the level 2 product record map - std::unique_lock lock {level2ProductRecordMutex_}; + // Lock the product record map + std::unique_lock lock {productRecordMutex}; // Merge volume times into map - std::transform( - volumeTimes.cbegin(), - volumeTimes.cend(), - std::inserter(level2ProductRecords_, level2ProductRecords_.begin()), - [](const std::chrono::system_clock::time_point& time) - { - return std::pair>( - time, std::weak_ptr {}); - }); + std::transform(volumeTimes.cbegin(), + volumeTimes.cend(), + std::inserter(productRecordMap, productRecordMap.begin()), + [](const std::chrono::system_clock::time_point& time) + { + return std::pair>( + time, std::weak_ptr {}); + }); } std::tuple, @@ -1085,6 +1120,9 @@ RadarProductManagerImpl::GetLevel3ProductRecord( RadarProductRecordMap::const_pointer recordPtr {nullptr}; std::chrono::system_clock::time_point recordTime {time}; + // Ensure Level 3 product records are updated + PopulateLevel3ProductTimes(product, time); + std::unique_lock lock {level3ProductRecordMutex_}; auto it = level3ProductRecordsMap_.find(product); @@ -1108,12 +1146,9 @@ RadarProductManagerImpl::GetLevel3ProductRecord( if (recordPtr != nullptr) { - if (time == std::chrono::system_clock::time_point {} || - time == recordPtr->first) - { - recordTime = recordPtr->first; - record = recordPtr->second.lock(); - } + // Don't check for an exact time match for level 3 products + recordTime = recordPtr->first; + record = recordPtr->second.lock(); } if (recordPtr != nullptr && record == nullptr && From 55d9fe1da862bc447c0b08854be514551d10adb1 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Mon, 19 Jun 2023 19:08:18 -0500 Subject: [PATCH 3/9] Animation widget incorrectly displays auto update disabled when animating --- scwx-qt/source/scwx/qt/main/main_window.cpp | 5 +- .../scwx/qt/manager/timeline_manager.cpp | 2 + .../scwx/qt/ui/animation_dock_widget.cpp | 70 ++++++++++++++----- .../scwx/qt/ui/animation_dock_widget.hpp | 1 + 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index 4d258327..4611e915 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -711,7 +711,10 @@ void MainWindowImpl::ConnectAnimationSignals() &manager::TimelineManager::AnimationStateUpdated, animationDockWidget_, &ui::AnimationDockWidget::UpdateAnimationState); - + connect(timelineManager_.get(), + &manager::TimelineManager::ViewTypeUpdated, + animationDockWidget_, + &ui::AnimationDockWidget::UpdateViewType); connect(timelineManager_.get(), &manager::TimelineManager::LiveStateUpdated, animationDockWidget_, diff --git a/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp b/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp index cab719e1..2df72d32 100644 --- a/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp @@ -166,6 +166,8 @@ void TimelineManager::SetViewType(types::MapTime viewType) // If the selected view type is archive, select using the pinned time p->SelectTimeAsync(p->pinnedTime_); } + + Q_EMIT ViewTypeUpdated(viewType); } void TimelineManager::SetLoopTime(std::chrono::minutes loopTime) diff --git a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp index dc567d63..3d391407 100644 --- a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp +++ b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.cpp @@ -20,20 +20,34 @@ static const auto logger_ = scwx::util::Logger::Create(logPrefix_); class AnimationDockWidgetImpl { 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; const QIcon kPauseIcon_ {":/res/icons/font-awesome-6/pause-solid.svg"}; const QIcon kPlayIcon_ {":/res/icons/font-awesome-6/play-solid.svg"}; + QString enabledString_; + QString disabledString_; + AnimationDockWidget* self_; types::AnimationState animationState_ {types::AnimationState::Pause}; + types::MapTime viewType_ {types::MapTime::Live}; + bool isLive_ {true}; std::chrono::sys_days selectedDate_ {}; std::chrono::seconds selectedTime_ {}; void ConnectSignals(); + void UpdateAutoUpdateLabel(); }; AnimationDockWidget::AnimationDockWidget(QWidget* parent) : @@ -211,32 +225,56 @@ void AnimationDockWidgetImpl::ConnectSignals() void AnimationDockWidget::UpdateAnimationState(types::AnimationState state) { - // Update icon to opposite of state - switch (state) + if (p->animationState_ != state) { - case types::AnimationState::Pause: - ui->playButton->setIcon(p->kPlayIcon_); - break; + // Update icon to opposite of state + switch (state) + { + case types::AnimationState::Pause: + ui->playButton->setIcon(p->kPlayIcon_); + break; - case types::AnimationState::Play: - ui->playButton->setIcon(p->kPauseIcon_); - break; + case types::AnimationState::Play: + ui->playButton->setIcon(p->kPauseIcon_); + break; + } + + p->animationState_ = state; + p->UpdateAutoUpdateLabel(); } } void AnimationDockWidget::UpdateLiveState(bool isLive) { - static const QString prefix = tr("Auto Update"); - static const QString disabled = tr("Disabled"); - static const QString enabled = tr("Enabled"); - - if (isLive) + if (p->isLive_ != 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 { - ui->autoUpdateLabel->setText(QString("%1: %2").arg(prefix).arg(disabled)); + self_->ui->autoUpdateLabel->setText(disabledString_); } } diff --git a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.hpp b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.hpp index ea42a541..8b64d0c6 100644 --- a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.hpp +++ b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.hpp @@ -31,6 +31,7 @@ public: public slots: void UpdateAnimationState(types::AnimationState state); void UpdateLiveState(bool isLive); + void UpdateViewType(types::MapTime viewType); signals: void ViewTypeChanged(types::MapTime viewType); From bce4c1bfb294ba17c18c87b783acb36537fcbb71 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Mon, 19 Jun 2023 21:49:50 -0500 Subject: [PATCH 4/9] Fix switching radar sites in archive view - Reverts 4e5a28f, other changes have now made this unnecessary - Switching sites while animating, in archive, or live all work properly --- scwx-qt/source/scwx/qt/map/map_widget.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index 05ce34f4..1c3d5d0e 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.cpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp @@ -486,7 +486,8 @@ void MapWidget::SelectRadarSite(std::shared_ptr radarSite, radarProductView->set_radar_product_manager(p->radarProductManager_); SelectRadarProduct(radarProductView->GetRadarProductGroup(), radarProductView->GetRadarProductName(), - 0); + 0, + radarProductView->selected_time()); } AddLayers(); @@ -1053,9 +1054,6 @@ void MapWidgetImpl::SetRadarSite(const std::string& radarSite) // Set new RadarProductManager radarProductManager_ = manager::RadarProductManager::Instance(radarSite); - // Re-enable auto-update - autoUpdateEnabled_ = true; - // Connect signals to new RadarProductManager RadarProductManagerConnect(); From 16e3d1533f0a12cde8c01f84242a2ae00105c0b9 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Mon, 19 Jun 2023 22:58:08 -0500 Subject: [PATCH 5/9] Don't update a radar product view when the timeline manager will issue a subsequent update --- scwx-qt/source/scwx/qt/main/main_window.cpp | 4 ++++ scwx-qt/source/scwx/qt/map/map_widget.cpp | 8 +++++--- scwx-qt/source/scwx/qt/map/map_widget.hpp | 8 +++++--- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index 4611e915..e2c2d030 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -793,6 +793,8 @@ void MainWindowImpl::ConnectOtherSignals() { map->SetMapLocation(latitude, longitude, true); } + + UpdateRadarSite(); }, Qt::QueuedConnection); connect(mainWindow_, @@ -810,6 +812,8 @@ void MainWindowImpl::ConnectOtherSignals() { map->SelectRadarSite(selectedRadarSite); } + + UpdateRadarSite(); }); connect(updateManager_.get(), &manager::UpdateManager::UpdateAvailable, diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index 1c3d5d0e..187317dc 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.cpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.cpp @@ -359,7 +359,8 @@ void MapWidget::SelectElevation(float elevation) void MapWidget::SelectRadarProduct(common::RadarProductGroup group, const std::string& product, std::int16_t productCode, - std::chrono::system_clock::time_point time) + std::chrono::system_clock::time_point time, + bool update) { bool radarProductViewCreated = false; @@ -420,7 +421,7 @@ void MapWidget::SelectRadarProduct(common::RadarProductGroup group, common::GetLevel3Palette(productCode); p->InitializeNewRadarProductView(palette); } - else + else if (update) { radarProductView->Update(); } @@ -487,7 +488,8 @@ void MapWidget::SelectRadarSite(std::shared_ptr radarSite, SelectRadarProduct(radarProductView->GetRadarProductGroup(), radarProductView->GetRadarProductName(), 0, - radarProductView->selected_time()); + radarProductView->selected_time(), + false); } AddLayers(); diff --git a/scwx-qt/source/scwx/qt/map/map_widget.hpp b/scwx-qt/source/scwx/qt/map/map_widget.hpp index eb28dfc6..b2e4633c 100644 --- a/scwx-qt/source/scwx/qt/map/map_widget.hpp +++ b/scwx-qt/source/scwx/qt/map/map_widget.hpp @@ -54,12 +54,14 @@ public: * @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. + * @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, const std::string& product, - std::int16_t productCode = 0, - std::chrono::system_clock::time_point time = {}); + std::int16_t productCode = 0, + std::chrono::system_clock::time_point time = {}, + bool update = true); void SelectRadarProduct(std::shared_ptr record); From 081b6268557924696d7fc3a7dd965a3e92096b9a Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Tue, 20 Jun 2023 20:12:57 -0500 Subject: [PATCH 6/9] Fix crash when switching radar sites while loading data from the old site - Use thread pools owned by radar product manager, unless called statically (#51) --- .../scwx/qt/manager/radar_product_manager.cpp | 129 +++++++++++------- 1 file changed, 78 insertions(+), 51 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp b/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp index 9c5e3991..29bbc724 100644 --- a/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp @@ -19,7 +19,9 @@ # pragma warning(push, 0) #endif +#include #include +#include #include #include #include @@ -91,7 +93,7 @@ public: group_ {group}, product_ {product}, refreshEnabled_ {false}, - refreshTimer_ {scwx::util::io_context()}, + refreshTimer_ {threadPool_}, refreshTimerMutex_ {}, provider_ {nullptr} { @@ -106,6 +108,8 @@ public: void Disable(); + boost::asio::thread_pool threadPool_ {1u}; + const std::string radarId_; const common::RadarProductGroup group_; const std::string product_; @@ -179,6 +183,8 @@ public: RadarProductManager* self_; + boost::asio::thread_pool threadPool_ {4u}; + std::shared_ptr GetLevel3ProviderManager(const std::string& product); @@ -199,6 +205,10 @@ public: void UpdateRecentRecords(RadarProductRecordList& recentList, std::shared_ptr record); + void LoadNexradFileAsync(CreateNexradFileFunction load, + std::shared_ptr request, + std::mutex& mutex, + std::chrono::system_clock::time_point time); void LoadProviderData(std::chrono::system_clock::time_point time, std::shared_ptr providerManager, RadarProductRecordMap& recordMap, @@ -523,7 +533,8 @@ void RadarProductManager::EnableRefresh(common::RadarProductGroup group, p->GetLevel3ProviderManager(product); // Only enable refresh on available products - scwx::util::async( + boost::asio::post( + p->threadPool_, [=, this]() { providerManager->provider_->RequestAvailableProducts(); @@ -614,7 +625,8 @@ void RadarProductManagerImpl::RefreshData( providerManager->refreshTimer_.cancel(); } - scwx::util::async( + boost::asio::post( + threadPool_, [=, this]() { auto [newObjects, totalObjects] = @@ -774,9 +786,8 @@ void RadarProductManagerImpl::LoadProviderData( providerManager->name(), scwx::util::TimeString(time)); - RadarProductManagerImpl::LoadNexradFile( - [=, &recordMap, &recordMutex, &loadDataMutex]() - -> std::shared_ptr + LoadNexradFileAsync( + [=, &recordMap, &recordMutex]() -> std::shared_ptr { std::shared_ptr existingRecord = nullptr; std::shared_ptr nexradFile = nullptr; @@ -874,11 +885,15 @@ void RadarProductManager::LoadData( { logger_->debug("LoadData()"); - RadarProductManagerImpl::LoadNexradFile( - [=, &is]() -> std::shared_ptr - { return wsr88d::NexradFileFactory::Create(is); }, - request, - fileLoadMutex_); + scwx::util::async( + [=, &is]() + { + RadarProductManagerImpl::LoadNexradFile( + [=, &is]() -> std::shared_ptr + { return wsr88d::NexradFileFactory::Create(is); }, + request, + fileLoadMutex_); + }); } void RadarProductManager::LoadFile( @@ -915,11 +930,15 @@ void RadarProductManager::LoadFile( } }); - RadarProductManagerImpl::LoadNexradFile( - [=]() -> std::shared_ptr - { return wsr88d::NexradFileFactory::Create(filename); }, - request, - fileLoadMutex_); + scwx::util::async( + [=]() + { + RadarProductManagerImpl::LoadNexradFile( + [=]() -> std::shared_ptr + { return wsr88d::NexradFileFactory::Create(filename); }, + request, + fileLoadMutex_); + }); } else if (request != nullptr) { @@ -928,51 +947,58 @@ void RadarProductManager::LoadFile( } } +void RadarProductManagerImpl::LoadNexradFileAsync( + CreateNexradFileFunction load, + std::shared_ptr request, + std::mutex& mutex, + std::chrono::system_clock::time_point time) +{ + boost::asio::post(threadPool_, + [=, &mutex]() + { LoadNexradFile(load, request, mutex, time); }); +} + void RadarProductManagerImpl::LoadNexradFile( CreateNexradFileFunction load, std::shared_ptr request, std::mutex& mutex, std::chrono::system_clock::time_point time) { - scwx::util::async( - [=, &mutex]() + std::unique_lock lock {mutex}; + + std::shared_ptr nexradFile = load(); + + std::shared_ptr record = nullptr; + + bool fileValid = (nexradFile != nullptr); + + if (fileValid) + { + record = types::RadarProductRecord::Create(nexradFile); + + // If the time is already determined, override the time in the file. + // Sometimes, level 2 data has been seen to be a few seconds off + // between filename and file data. Overriding this can help prevent + // issues with locating and storing the correct records. + if (time != std::chrono::system_clock::time_point {}) { - std::unique_lock lock {mutex}; + record->set_time(time); + } - std::shared_ptr nexradFile = load(); + std::shared_ptr manager = + RadarProductManager::Instance(record->radar_id()); - std::shared_ptr record = nullptr; + manager->Initialize(); + record = manager->p->StoreRadarProductRecord(record); + } - bool fileValid = (nexradFile != nullptr); + lock.unlock(); - if (fileValid) - { - record = types::RadarProductRecord::Create(nexradFile); - - // If the time is already determined, override the time in the file. - // Sometimes, level 2 data has been seen to be a few seconds off - // between filename and file data. Overriding this can help prevent - // issues with locating and storing the correct records. - if (time != std::chrono::system_clock::time_point {}) - { - record->set_time(time); - } - - std::shared_ptr manager = - RadarProductManager::Instance(record->radar_id()); - - manager->Initialize(); - record = manager->p->StoreRadarProductRecord(record); - } - - lock.unlock(); - - if (request != nullptr) - { - request->set_radar_product_record(record); - Q_EMIT request->RequestComplete(request); - } - }); + if (request != nullptr) + { + request->set_radar_product_record(record); + Q_EMIT request->RequestComplete(request); + } } void RadarProductManagerImpl::PopulateLevel2ProductTimes( @@ -1349,7 +1375,8 @@ void RadarProductManager::UpdateAvailableProducts() logger_->debug("UpdateAvailableProducts()"); - scwx::util::async( + boost::asio::post( + p->threadPool_, [this]() { auto level3ProviderManager = From 72df99e45625e8fdd11640fff4c836e3bdbab151 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Tue, 20 Jun 2023 22:18:21 -0500 Subject: [PATCH 7/9] Make Radar Toolbox and Animation Toolbox scrollable --- scwx-qt/source/scwx/qt/main/main_window.ui | 300 +++++---- .../scwx/qt/ui/animation_dock_widget.ui | 610 ++++++++++-------- 2 files changed, 495 insertions(+), 415 deletions(-) diff --git a/scwx-qt/source/scwx/qt/main/main_window.ui b/scwx-qt/source/scwx/qt/main/main_window.ui index 33cf502f..0d4f0266 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.ui +++ b/scwx-qt/source/scwx/qt/main/main_window.ui @@ -116,128 +116,181 @@ 2 - + - QFrame::StyledPanel + QFrame::NoFrame - - QFrame::Raised + + Qt::ScrollBarAlwaysOff - - - - - - 16777215 - 13 - - - - ... - - - - - - - St. Louis, MO - - - - - - - Volume Coverage Pattern - - - VCP - - - - - - - KLSX - - - - - - - Radar Site - - - - - - - Clear Air Mode - - - - - - - 35 - - - - - - - - - - Map Settings + + QAbstractScrollArea::AdjustToContents - - - - - Map Style - - - - - - - - - - - - - Radar Products + + true - - - - - Level 2 - - - - - - - - - - Qt::Horizontal - - - - - - - Level 3 - - - - - - - + + + + 0 + 0 + 175 + 696 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 16777215 + 13 + + + + ... + + + + + + + St. Louis, MO + + + + + + + Volume Coverage Pattern + + + VCP + + + + + + + KLSX + + + + + + + Radar Site + + + + + + + Clear Air Mode + + + + + + + 35 + + + + + + + + + + Map Settings + + + + + + Map Style + + + + + + + + + + + + + Radar Products + + + + + + Level 2 + + + + + + + + + + Qt::Horizontal + + + + + + + Level 3 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + @@ -258,19 +311,6 @@ - - - - Qt::Vertical - - - - 20 - 0 - - - - diff --git a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.ui b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.ui index 8a3ab3b8..33485032 100644 --- a/scwx-qt/source/scwx/qt/ui/animation_dock_widget.ui +++ b/scwx-qt/source/scwx/qt/ui/animation_dock_widget.ui @@ -7,7 +7,7 @@ 0 0 200 - 348 + 543 @@ -16,296 +16,336 @@ - - - Timeline + + + QFrame::NoFrame - - - - - Auto Update: Enabled - - - - - - - Live View - - - true - - - - - - - Archive View - - - - - - - QAbstractSpinBox::CorrectToNearestValue - - - - 0 - 0 - 0 - 1991 - 6 - 1 - - - - yyyy-MM-dd - - - true - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 + + 0 + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 182 + 506 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Timeline - - 0 + + + + + Auto Update: Enabled + + + + + + + Live View + + + true + + + + + + + Archive View + + + + + + + QAbstractSpinBox::CorrectToNearestValue + + + + 0 + 0 + 0 + 1991 + 6 + 1 + + + + yyyy-MM-dd + + + true + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QAbstractSpinBox::CorrectToNearestValue + + + HH:mm + + + + + + + UTC + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QAbstractSpinBox::CorrectToNearestValue + + + min + + + 1 + + + 1440 + + + 30 + + + + + + + Loop Time + + + + + + + Loop Speed + + + + + + + QAbstractSpinBox::CorrectToNearestValue + + + x + + + 1.000000000000000 + + + 1.000000000000000 + + + + + + + Loop Delay + + + + + + + sec + + + 1 + + + 15.000000000000000 + + + 0.100000000000000 + + + 2.500000000000000 + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + ... + + + + :/res/icons/font-awesome-6/backward-step-solid.svg:/res/icons/font-awesome-6/backward-step-solid.svg + + + + + + + ... + + + + :/res/icons/font-awesome-6/angle-left-solid.svg:/res/icons/font-awesome-6/angle-left-solid.svg + + + + + + + ... + + + + :/res/icons/font-awesome-6/play-solid.svg:/res/icons/font-awesome-6/play-solid.svg + + + + + + + ... + + + + :/res/icons/font-awesome-6/angle-right-solid.svg:/res/icons/font-awesome-6/angle-right-solid.svg + + + + + + + ... + + + + :/res/icons/font-awesome-6/forward-step-solid.svg:/res/icons/font-awesome-6/forward-step-solid.svg + + + + + + + + + + + + + Qt::Vertical - - 0 + + + 20 + 40 + - - 0 - - - - - QAbstractSpinBox::CorrectToNearestValue - - - HH:mm - - - - - - - UTC - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QAbstractSpinBox::CorrectToNearestValue - - - min - - - 1 - - - 1440 - - - 30 - - - - - - - Loop Time - - - - - - - Loop Speed - - - - - - - QAbstractSpinBox::CorrectToNearestValue - - - x - - - 1.000000000000000 - - - 1.000000000000000 - - - - - - - Loop Delay - - - - - - - sec - - - 1 - - - 15.000000000000000 - - - 0.100000000000000 - - - 2.500000000000000 - - - - - - - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - ... - - - - :/res/icons/font-awesome-6/backward-step-solid.svg:/res/icons/font-awesome-6/backward-step-solid.svg - - - - - - - ... - - - - :/res/icons/font-awesome-6/angle-left-solid.svg:/res/icons/font-awesome-6/angle-left-solid.svg - - - - - - - ... - - - - :/res/icons/font-awesome-6/play-solid.svg:/res/icons/font-awesome-6/play-solid.svg - - - - - - - ... - - - - :/res/icons/font-awesome-6/angle-right-solid.svg:/res/icons/font-awesome-6/angle-right-solid.svg - - - - - - - ... - - - - :/res/icons/font-awesome-6/forward-step-solid.svg:/res/icons/font-awesome-6/forward-step-solid.svg - - - - - - - + + + + - - - - Qt::Vertical - - - - 20 - 40 - - - - From 4c30b0d6bb98282571a23699aeaf33b009ffacd8 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Tue, 20 Jun 2023 22:25:50 -0500 Subject: [PATCH 8/9] Disable resource explorer --- scwx-qt/source/scwx/qt/main/main_window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index e2c2d030..59a59c57 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -211,6 +211,7 @@ MainWindow::MainWindow(QWidget* parent) : ui->resourceExplorerDock->toggleViewAction()->setText( tr("&Resource Explorer")); ui->actionResourceExplorer->setVisible(false); + ui->resourceExplorerDock->toggleViewAction()->setVisible(false); ui->menuView->insertAction(ui->actionAlerts, p->alertDockWidget_->toggleViewAction()); From 9f6d775c174985ce36bd15007cc3b46a68f1be0d Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Tue, 20 Jun 2023 22:41:59 -0500 Subject: [PATCH 9/9] Reduce product cache limit --- scwx-qt/source/scwx/qt/manager/timeline_manager.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp b/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp index 2df72d32..667fa4dd 100644 --- a/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp @@ -1,3 +1,5 @@ +#define NOMINMAX + #include #include #include @@ -392,9 +394,10 @@ void TimelineManager::Impl::UpdateCacheLimit( auto endIter = util::GetBoundedElementIterator(volumeTimes, endTime); std::size_t numVolumeScans = std::distance(startIter, endIter) + 1; - // Dynamically update maximum cached volume scans to 1.5x the loop length - radarProductManager->SetCacheLimit( - static_cast(numVolumeScans * 1.5)); + // Dynamically update maximum cached volume scans to the lesser of + // either 1.5x the loop length or 5 greater than the loop length + radarProductManager->SetCacheLimit(std::min( + static_cast(numVolumeScans * 1.5), numVolumeScans + 5u)); } void TimelineManager::Impl::Play()