diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index 4d258327..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()); @@ -711,7 +712,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_, @@ -790,6 +794,8 @@ void MainWindowImpl::ConnectOtherSignals() { map->SetMapLocation(latitude, longitude, true); } + + UpdateRadarSite(); }, Qt::QueuedConnection); connect(mainWindow_, @@ -807,6 +813,8 @@ void MainWindowImpl::ConnectOtherSignals() { map->SelectRadarSite(selectedRadarSite); } + + UpdateRadarSite(); }); connect(updateManager_.get(), &manager::UpdateManager::UpdateAvailable, 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/manager/radar_product_manager.cpp b/scwx-qt/source/scwx/qt/manager/radar_product_manager.cpp index e9bb8463..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, @@ -206,6 +216,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, @@ -515,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(); @@ -606,7 +625,8 @@ void RadarProductManagerImpl::RefreshData( providerManager->refreshTimer_.cancel(); } - scwx::util::async( + boost::asio::post( + threadPool_, [=, this]() { auto [newObjects, totalObjects] = @@ -766,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; @@ -792,7 +811,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 { @@ -857,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( @@ -898,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) { @@ -911,55 +947,91 @@ 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( 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}; @@ -973,7 +1045,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()) @@ -983,8 +1055,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}; @@ -995,20 +1066,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, @@ -1042,7 +1112,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 @@ -1076,6 +1146,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); @@ -1099,15 +1172,12 @@ 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 (record == nullptr && + if (recordPtr != nullptr && record == nullptr && recordTime != std::chrono::system_clock::time_point {}) { // Product is expired, reload it @@ -1305,7 +1375,8 @@ void RadarProductManager::UpdateAvailableProducts() logger_->debug("UpdateAvailableProducts()"); - scwx::util::async( + boost::asio::post( + p->threadPool_, [this]() { auto level3ProviderManager = diff --git a/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp b/scwx-qt/source/scwx/qt/manager/timeline_manager.cpp index cab719e1..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 @@ -166,6 +168,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) @@ -390,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() diff --git a/scwx-qt/source/scwx/qt/map/map_widget.cpp b/scwx-qt/source/scwx/qt/map/map_widget.cpp index 05ce34f4..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(); } @@ -486,7 +487,9 @@ void MapWidget::SelectRadarSite(std::shared_ptr radarSite, radarProductView->set_radar_product_manager(p->radarProductManager_); SelectRadarProduct(radarProductView->GetRadarProductGroup(), radarProductView->GetRadarProductName(), - 0); + 0, + radarProductView->selected_time(), + false); } AddLayers(); @@ -1053,9 +1056,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(); 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); 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); 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 - - - - 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