From c7a69a76be23df168a7952db2c36fef68194e79b Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Sun, 6 Nov 2022 23:39:01 -0600 Subject: [PATCH] Integrate Warnings Provider auto-refresh into Text Event Manager --- scwx-qt/source/scwx/qt/main/main_window.cpp | 8 +- .../scwx/qt/manager/text_event_manager.cpp | 96 ++++++++++++++++++- .../scwx/qt/manager/text_event_manager.hpp | 2 +- scwx-qt/source/scwx/qt/map/alert_layer.cpp | 11 ++- scwx-qt/source/scwx/qt/model/alert_model.cpp | 20 ++-- scwx-qt/source/scwx/qt/ui/alert_dialog.cpp | 20 ++-- .../source/scwx/qt/ui/alert_dock_widget.cpp | 10 +- 7 files changed, 131 insertions(+), 36 deletions(-) diff --git a/scwx-qt/source/scwx/qt/main/main_window.cpp b/scwx-qt/source/scwx/qt/main/main_window.cpp index 5702c98e..42fbc871 100644 --- a/scwx-qt/source/scwx/qt/main/main_window.cpp +++ b/scwx-qt/source/scwx/qt/main/main_window.cpp @@ -46,9 +46,11 @@ public: activeMap_ {nullptr}, level2ProductsWidget_ {nullptr}, level2SettingsWidget_ {nullptr}, + level3ProductsWidget_ {nullptr}, alertDockWidget_ {nullptr}, radarSiteDialog_ {nullptr}, radarProductModel_ {nullptr}, + textEventManager_ {manager::TextEventManager::Instance()}, maps_ {}, elevationCuts_ {}, elevationButtonsChanged_ {false}, @@ -109,7 +111,8 @@ public: ui::AlertDockWidget* alertDockWidget_; ui::RadarSiteDialog* radarSiteDialog_; - std::unique_ptr radarProductModel_; + std::unique_ptr radarProductModel_; + std::shared_ptr textEventManager_; std::vector maps_; std::vector elevationCuts_; @@ -306,8 +309,7 @@ void MainWindow::on_actionOpenTextEvent_triggered() [=](const QString& file) { logger_->info("Selected: {}", file.toStdString()); - manager::TextEventManager::Instance().LoadFile( - file.toStdString()); + p->textEventManager_->LoadFile(file.toStdString()); }); dialog->open(); diff --git a/scwx-qt/source/scwx/qt/manager/text_event_manager.cpp b/scwx-qt/source/scwx/qt/manager/text_event_manager.cpp index f2c364c0..5552e3c7 100644 --- a/scwx-qt/source/scwx/qt/manager/text_event_manager.cpp +++ b/scwx-qt/source/scwx/qt/manager/text_event_manager.cpp @@ -1,11 +1,14 @@ #include #include +#include #include #include #include #include +#include + namespace scwx { namespace qt @@ -16,25 +19,44 @@ namespace manager static const std::string logPrefix_ = "scwx::qt::manager::text_event_manager"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); +static const std::string& kDefaultWarningsProviderUrl { + "https://warnings.allisonhouse.com"}; + class TextEventManager::Impl { public: explicit Impl(TextEventManager* self) : - self_ {self}, textEventMap_ {}, textEventMutex_ {} + self_ {self}, + refreshTimer_ {util::io_context()}, + refreshMutex_ {}, + textEventMap_ {}, + textEventMutex_ {}, + warningsProvider_ {kDefaultWarningsProviderUrl} { + util::async([=]() { Refresh(); }); } - ~Impl() {} + ~Impl() + { + std::unique_lock lock(refreshMutex_); + refreshTimer_.cancel(); + } void HandleMessage(std::shared_ptr message); + void Refresh(); TextEventManager* self_; + boost::asio::steady_timer refreshTimer_; + std::mutex refreshMutex_; + std::unordered_map>, types::TextEventHash> textEventMap_; std::shared_mutex textEventMutex_; + + provider::WarningsProvider warningsProvider_; }; TextEventManager::TextEventManager() : p(std::make_unique(this)) {} @@ -159,10 +181,74 @@ void TextEventManager::Impl::HandleMessage( } } -TextEventManager& TextEventManager::Instance() +void TextEventManager::Impl::Refresh() { - static TextEventManager textEventManager_ {}; - return textEventManager_; + using namespace std::chrono; + + logger_->trace("Refresh"); + + // Take a unique lock before refreshing + std::unique_lock lock(refreshMutex_); + + // Set threshold to last 30 hours + auto newerThan = std::chrono::system_clock::now() - 30h; + + // Update the file listing from the warnings provider + auto [newFiles, totalFiles] = warningsProvider_.ListFiles(newerThan); + + if (newFiles > 0) + { + // Load new files + auto updatedFiles = warningsProvider_.LoadUpdatedFiles(newerThan); + + // Handle messages + for (auto& file : updatedFiles) + { + for (auto& message : file->messages()) + { + HandleMessage(message); + } + } + } + + // Schedule another update in 15 seconds + using namespace std::chrono; + refreshTimer_.expires_after(15s); + refreshTimer_.async_wait( + [=](const boost::system::error_code& e) + { + if (e == boost::asio::error::operation_aborted) + { + logger_->debug("Refresh timer cancelled"); + } + else if (e != boost::system::errc::success) + { + logger_->warn("Refresh timer error: {}", e.message()); + } + else + { + Refresh(); + } + }); +} + +std::shared_ptr TextEventManager::Instance() +{ + static std::weak_ptr textEventManagerReference_ {}; + static std::mutex instanceMutex_ {}; + + std::unique_lock lock(instanceMutex_); + + std::shared_ptr textEventManager = + textEventManagerReference_.lock(); + + if (textEventManager == nullptr) + { + textEventManager = std::make_shared(); + textEventManagerReference_ = textEventManager; + } + + return textEventManager; } } // namespace manager diff --git a/scwx-qt/source/scwx/qt/manager/text_event_manager.hpp b/scwx-qt/source/scwx/qt/manager/text_event_manager.hpp index 8f9f7719..f97ca223 100644 --- a/scwx-qt/source/scwx/qt/manager/text_event_manager.hpp +++ b/scwx-qt/source/scwx/qt/manager/text_event_manager.hpp @@ -29,7 +29,7 @@ public: void LoadFile(const std::string& filename); - static TextEventManager& Instance(); + static std::shared_ptr Instance(); signals: void AlertUpdated(const types::TextEventKey& key, size_t messageIndex); diff --git a/scwx-qt/source/scwx/qt/map/alert_layer.cpp b/scwx-qt/source/scwx/qt/map/alert_layer.cpp index 0210ad85..e1904239 100644 --- a/scwx-qt/source/scwx/qt/map/alert_layer.cpp +++ b/scwx-qt/source/scwx/qt/map/alert_layer.cpp @@ -55,6 +55,7 @@ class AlertLayerHandler : public QObject { Q_OBJECT public : explicit AlertLayerHandler() : + textEventManager_ {manager::TextEventManager::Instance()}, alertUpdateTimer_ {util::io_context()}, alertSourceMap_ {}, featureMap_ {} @@ -68,7 +69,7 @@ class AlertLayerHandler : public QObject } } - connect(&manager::TextEventManager::Instance(), + connect(textEventManager_.get(), &manager::TextEventManager::AlertUpdated, this, &AlertLayerHandler::HandleAlert); @@ -87,6 +88,8 @@ class AlertLayerHandler : public QObject void HandleAlert(const types::TextEventKey& key, size_t messageIndex); void UpdateAlerts(); + std::shared_ptr textEventManager_; + boost::asio::steady_timer alertUpdateTimer_; std::unordered_map, QVariantMap, @@ -252,15 +255,13 @@ AlertLayerHandler::FeatureList(awips::Phenomenon phenomenon, bool alertActive) void AlertLayerHandler::HandleAlert(const types::TextEventKey& key, size_t messageIndex) { - auto& textEventManager = manager::TextEventManager::Instance(); - // Skip alert if there are more messages to be processed - if (messageIndex + 1 < textEventManager.message_count(key)) + if (messageIndex + 1 < textEventManager_->message_count(key)) { return; } - auto message = textEventManager.message_list(key).at(messageIndex); + auto message = textEventManager_->message_list(key).at(messageIndex); std::unordered_set, AlertTypeHash>> alertsUpdated {}; diff --git a/scwx-qt/source/scwx/qt/model/alert_model.cpp b/scwx-qt/source/scwx/qt/model/alert_model.cpp index 902e5ac7..58a4636b 100644 --- a/scwx-qt/source/scwx/qt/model/alert_model.cpp +++ b/scwx-qt/source/scwx/qt/model/alert_model.cpp @@ -45,6 +45,8 @@ public: static std::string GetStartTime(const types::TextEventKey& key); static std::string GetEndTime(const types::TextEventKey& key); + std::shared_ptr textEventManager_; + QList textEventKeys_; GeographicLib::Geodesic geodesic_; @@ -199,8 +201,7 @@ void AlertModel::HandleAlert(const types::TextEventKey& alertKey, double distanceInMeters; // Get the most recent segment for the event - auto alertMessages = - manager::TextEventManager::Instance().message_list(alertKey); + auto alertMessages = p->textEventManager_->message_list(alertKey); std::shared_ptr alertSegment = alertMessages[messageIndex]->segments().back(); @@ -273,6 +274,7 @@ void AlertModel::HandleMapUpdate(double latitude, double longitude) } AlertModelImpl::AlertModelImpl() : + textEventManager_ {manager::TextEventManager::Instance()}, textEventKeys_ {}, geodesic_(GeographicLib::Constants::WGS84_a(), GeographicLib::Constants::WGS84_f()), @@ -284,8 +286,8 @@ AlertModelImpl::AlertModelImpl() : std::string AlertModelImpl::GetCounties(const types::TextEventKey& key) { - auto messageList = manager::TextEventManager::Instance().message_list(key); - auto& lastMessage = messageList.back(); + auto messageList = manager::TextEventManager::Instance()->message_list(key); + auto& lastMessage = messageList.back(); size_t segmentCount = lastMessage->segment_count(); auto lastSegment = lastMessage->segment(segmentCount - 1); auto fipsIds = lastSegment->header_->ugc_.fips_ids(); @@ -303,8 +305,8 @@ std::string AlertModelImpl::GetCounties(const types::TextEventKey& key) std::string AlertModelImpl::GetState(const types::TextEventKey& key) { - auto messageList = manager::TextEventManager::Instance().message_list(key); - auto& lastMessage = messageList.back(); + auto messageList = manager::TextEventManager::Instance()->message_list(key); + auto& lastMessage = messageList.back(); size_t segmentCount = lastMessage->segment_count(); auto lastSegment = lastMessage->segment(segmentCount - 1); return util::ToString(lastSegment->header_->ugc_.states()); @@ -312,7 +314,7 @@ std::string AlertModelImpl::GetState(const types::TextEventKey& key) std::string AlertModelImpl::GetStartTime(const types::TextEventKey& key) { - auto messageList = manager::TextEventManager::Instance().message_list(key); + auto messageList = manager::TextEventManager::Instance()->message_list(key); auto& firstMessage = messageList.front(); auto firstSegment = firstMessage->segment(0); return util::TimeString( @@ -321,8 +323,8 @@ std::string AlertModelImpl::GetStartTime(const types::TextEventKey& key) std::string AlertModelImpl::GetEndTime(const types::TextEventKey& key) { - auto messageList = manager::TextEventManager::Instance().message_list(key); - auto& lastMessage = messageList.back(); + auto messageList = manager::TextEventManager::Instance()->message_list(key); + auto& lastMessage = messageList.back(); size_t segmentCount = lastMessage->segment_count(); auto lastSegment = lastMessage->segment(segmentCount - 1); return util::TimeString( diff --git a/scwx-qt/source/scwx/qt/ui/alert_dialog.cpp b/scwx-qt/source/scwx/qt/ui/alert_dialog.cpp index e131e0aa..79e2e488 100644 --- a/scwx-qt/source/scwx/qt/ui/alert_dialog.cpp +++ b/scwx-qt/source/scwx/qt/ui/alert_dialog.cpp @@ -22,6 +22,7 @@ class AlertDialogImpl : public QObject public: explicit AlertDialogImpl(AlertDialog* self) : self_ {self}, + textEventManager_ {manager::TextEventManager::Instance()}, goButton_ {nullptr}, key_ {}, centroid_ {}, @@ -34,7 +35,10 @@ public: void SelectIndex(size_t newIndex); void UpdateAlertInfo(); - AlertDialog* self_; + AlertDialog* self_; + + std::shared_ptr textEventManager_; + QPushButton* goButton_; types::TextEventKey key_; common::Coordinate centroid_; @@ -67,7 +71,7 @@ AlertDialog::~AlertDialog() void AlertDialogImpl::ConnectSignals() { connect( - &manager::TextEventManager::Instance(), + textEventManager_.get(), &manager::TextEventManager::AlertUpdated, this, [=](const types::TextEventKey& key) @@ -94,7 +98,7 @@ bool AlertDialog::SelectAlert(const types::TextEventKey& key) setWindowTitle(QString::fromStdString(key.ToFullString())); - auto messages = manager::TextEventManager::Instance().message_list(key); + auto messages = p->textEventManager_->message_list(key); if (messages.empty()) { return false; @@ -107,15 +111,14 @@ bool AlertDialog::SelectAlert(const types::TextEventKey& key) void AlertDialogImpl::SelectIndex(size_t newIndex) { - size_t messageCount = - manager::TextEventManager::Instance().message_count(key_); + size_t messageCount = textEventManager_->message_count(key_); if (newIndex >= messageCount) { return; } - auto messages = manager::TextEventManager::Instance().message_list(key_); + auto messages = textEventManager_->message_list(key_); currentIndex_ = newIndex; @@ -127,7 +130,7 @@ void AlertDialogImpl::SelectIndex(size_t newIndex) void AlertDialogImpl::UpdateAlertInfo() { - auto messages = manager::TextEventManager::Instance().message_list(key_); + auto messages = textEventManager_->message_list(key_); size_t messageCount = messages.size(); bool firstSelected = (currentIndex_ == 0u); @@ -170,8 +173,7 @@ void AlertDialog::on_nextButton_clicked() void AlertDialog::on_lastButton_clicked() { - p->SelectIndex(manager::TextEventManager::Instance().message_count(p->key_) - - 1u); + p->SelectIndex(p->textEventManager_->message_count(p->key_) - 1u); } #include "alert_dialog.moc" diff --git a/scwx-qt/source/scwx/qt/ui/alert_dock_widget.cpp b/scwx-qt/source/scwx/qt/ui/alert_dock_widget.cpp index b8c1cef6..1b5370db 100644 --- a/scwx-qt/source/scwx/qt/ui/alert_dock_widget.cpp +++ b/scwx-qt/source/scwx/qt/ui/alert_dock_widget.cpp @@ -25,6 +25,7 @@ class AlertDockWidgetImpl : QObject public: explicit AlertDockWidgetImpl(AlertDockWidget* self) : self_ {self}, + textEventManager_ {manager::TextEventManager::Instance()}, alertModel_ {std::make_unique()}, proxyModel_ {std::make_unique()}, alertDialog_ {new AlertDialog(self)}, @@ -42,9 +43,10 @@ public: void ConnectSignals(); - AlertDockWidget* self_; - std::unique_ptr alertModel_; - std::unique_ptr proxyModel_; + AlertDockWidget* self_; + std::shared_ptr textEventManager_; + std::unique_ptr alertModel_; + std::unique_ptr proxyModel_; AlertDialog* alertDialog_; @@ -109,7 +111,7 @@ void AlertDockWidgetImpl::ConnectSignals() &QLineEdit::textChanged, proxyModel_.get(), &QSortFilterProxyModel::setFilterWildcard); - connect(&manager::TextEventManager::Instance(), + connect(textEventManager_.get(), &manager::TextEventManager::AlertUpdated, alertModel_.get(), &model::AlertModel::HandleAlert,