From 3ca99ca0234446b7983a3a8455e44f8ce7a875f3 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sat, 17 Feb 2024 00:08:45 -0600 Subject: [PATCH] Initial implementation for overlay product view, currently only handling NST --- scwx-qt/scwx-qt.cmake | 2 + .../scwx/qt/view/overlay_product_view.cpp | 217 ++++++++++++++++++ .../scwx/qt/view/overlay_product_view.hpp | 54 +++++ 3 files changed, 273 insertions(+) create mode 100644 scwx-qt/source/scwx/qt/view/overlay_product_view.cpp create mode 100644 scwx-qt/source/scwx/qt/view/overlay_product_view.hpp diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 475b69c8..ec0979d1 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -302,12 +302,14 @@ set(HDR_VIEW source/scwx/qt/view/level2_product_view.hpp source/scwx/qt/view/level3_product_view.hpp source/scwx/qt/view/level3_radial_view.hpp source/scwx/qt/view/level3_raster_view.hpp + source/scwx/qt/view/overlay_product_view.hpp source/scwx/qt/view/radar_product_view.hpp source/scwx/qt/view/radar_product_view_factory.hpp) set(SRC_VIEW source/scwx/qt/view/level2_product_view.cpp source/scwx/qt/view/level3_product_view.cpp source/scwx/qt/view/level3_radial_view.cpp source/scwx/qt/view/level3_raster_view.cpp + source/scwx/qt/view/overlay_product_view.cpp source/scwx/qt/view/radar_product_view.cpp source/scwx/qt/view/radar_product_view_factory.cpp) diff --git a/scwx-qt/source/scwx/qt/view/overlay_product_view.cpp b/scwx-qt/source/scwx/qt/view/overlay_product_view.cpp new file mode 100644 index 00000000..1e0085b4 --- /dev/null +++ b/scwx-qt/source/scwx/qt/view/overlay_product_view.cpp @@ -0,0 +1,217 @@ +#include +#include +#include +#include + +#include + +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace view +{ + +static const std::string logPrefix_ = "scwx::qt::view::overlay_product_view"; +static const auto logger_ = util::Logger::Create(logPrefix_); + +static const std::string kNst_ = "NST"; + +class OverlayProductView::Impl +{ +public: + explicit Impl(OverlayProductView* self) : self_ {self} {} + ~Impl() { threadPool_.join(); } + + void ConnectRadarProductManager(); + void DisconnectRadarProductManager(); + void LoadProduct(const std::string& product, + std::chrono::system_clock::time_point time, + bool autoUpdate); + void ResetProducts(); + void UpdateAutoRefresh(bool enabled) const; + + OverlayProductView* self_; + boost::uuids::uuid uuid_ {boost::uuids::random_generator()()}; + + boost::asio::thread_pool threadPool_ {1u}; + + bool autoRefreshEnabled_ {false}; + bool autoUpdateEnabled_ {false}; + + std::chrono::system_clock::time_point selectedTime_ {}; + + std::shared_ptr radarProductManager_ {nullptr}; + + std::unordered_map> + recordMap_ {}; + std::mutex recordMutex_ {}; +}; + +OverlayProductView::OverlayProductView() : p(std::make_unique(this)) {}; +OverlayProductView::~OverlayProductView() = default; + +std::shared_ptr +OverlayProductView::radar_product_manager() const +{ + return p->radarProductManager_; +} + +std::shared_ptr +OverlayProductView::radar_product_record(const std::string& product) const +{ + std::unique_lock lock {p->recordMutex_}; + + auto it = p->recordMap_.find(product); + if (it != p->recordMap_.cend()) + { + return it->second; + } + + return nullptr; +} + +std::chrono::system_clock::time_point OverlayProductView::selected_time() const +{ + return p->selectedTime_; +} + +void OverlayProductView::set_radar_product_manager( + const std::shared_ptr& radarProductManager) +{ + p->UpdateAutoRefresh(false); + p->DisconnectRadarProductManager(); + p->ResetProducts(); + + p->radarProductManager_ = radarProductManager; + + p->ConnectRadarProductManager(); + p->UpdateAutoRefresh(p->autoRefreshEnabled_); +} + +void OverlayProductView::Impl::ConnectRadarProductManager() +{ + connect( + radarProductManager_.get(), + &manager::RadarProductManager::NewDataAvailable, + self_, + [this](common::RadarProductGroup group, + const std::string& product, + std::chrono::system_clock::time_point latestTime) + { + if (autoRefreshEnabled_ && + group == common::RadarProductGroup::Level3 && product == kNst_) + { + LoadProduct(product, latestTime, autoUpdateEnabled_); + } + }, + Qt::QueuedConnection); +} + +void OverlayProductView::Impl::DisconnectRadarProductManager() +{ + if (radarProductManager_ != nullptr) + { + disconnect(radarProductManager_.get(), + &manager::RadarProductManager::NewDataAvailable, + self_, + nullptr); + } +} + +void OverlayProductView::Impl::LoadProduct( + const std::string& product, + std::chrono::system_clock::time_point time, + bool autoUpdate) +{ + logger_->debug( + "Load Product: {}, {}, {}", product, util::TimeString(time), autoUpdate); + + // Create file request + std::shared_ptr request = + std::make_shared( + radarProductManager_->radar_id()); + + if (autoUpdate) + { + connect(request.get(), + &request::NexradFileRequest::RequestComplete, + self_, + [=, this](std::shared_ptr request) + { + // Select loaded record + const auto& record = request->radar_product_record(); + + // Validate record + if (record != nullptr) + { + // Store loaded record + std::unique_lock lock {recordMutex_}; + + auto it = recordMap_.find(product); + if (it == recordMap_.cend() || it->second != record) + { + recordMap_.insert_or_assign(product, record); + + lock.unlock(); + + Q_EMIT self_->ProductUpdated(product); + } + } + }); + } + + // Load file + boost::asio::post( + threadPool_, + [=, this]() + { radarProductManager_->LoadLevel3Data(product, time, request); }); +} + +void OverlayProductView::Impl::ResetProducts() +{ + std::unique_lock lock {recordMutex_}; + recordMap_.clear(); + lock.unlock(); + + Q_EMIT self_->ProductUpdated(kNst_); +} + +void OverlayProductView::SelectTime(std::chrono::system_clock::time_point time) +{ + if (time != p->selectedTime_) + { + p->selectedTime_ = time; + p->LoadProduct(kNst_, time, true); + } +} + +void OverlayProductView::SetAutoRefresh(bool enabled) +{ + if (p->autoRefreshEnabled_ != enabled) + { + p->autoRefreshEnabled_ = enabled; + p->UpdateAutoRefresh(enabled); + } +} + +void OverlayProductView::Impl::UpdateAutoRefresh(bool enabled) const +{ + if (radarProductManager_ != nullptr) + { + radarProductManager_->EnableRefresh( + common::RadarProductGroup::Level3, kNst_, enabled, uuid_); + } +} + +void OverlayProductView::SetAutoUpdate(bool enabled) +{ + p->autoUpdateEnabled_ = enabled; +} + +} // namespace view +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/view/overlay_product_view.hpp b/scwx-qt/source/scwx/qt/view/overlay_product_view.hpp new file mode 100644 index 00000000..31e1cec0 --- /dev/null +++ b/scwx-qt/source/scwx/qt/view/overlay_product_view.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include +#include + +#include + +namespace scwx +{ +namespace qt +{ +namespace manager +{ + +class RadarProductManager; + +} // namespace manager + +namespace view +{ + +class OverlayProductView : public QObject +{ + Q_OBJECT + +public: + explicit OverlayProductView(); + virtual ~OverlayProductView(); + + std::shared_ptr radar_product_manager() const; + std::shared_ptr + radar_product_record(const std::string& product) const; + std::chrono::system_clock::time_point selected_time() const; + + void set_radar_product_manager( + const std::shared_ptr& radarProductManager); + + void SelectTime(std::chrono::system_clock::time_point time); + void SetAutoRefresh(bool enabled); + void SetAutoUpdate(bool enabled); + +signals: + void ProductUpdated(std::string product); + +private: + class Impl; + std::unique_ptr p; +}; + +} // namespace view +} // namespace qt +} // namespace scwx