Integrate Warnings Provider auto-refresh into Text Event Manager

This commit is contained in:
Dan Paulat 2022-11-06 23:39:01 -06:00
parent a2616b0ee0
commit c7a69a76be
7 changed files with 131 additions and 36 deletions

View file

@ -46,9 +46,11 @@ public:
activeMap_ {nullptr}, activeMap_ {nullptr},
level2ProductsWidget_ {nullptr}, level2ProductsWidget_ {nullptr},
level2SettingsWidget_ {nullptr}, level2SettingsWidget_ {nullptr},
level3ProductsWidget_ {nullptr},
alertDockWidget_ {nullptr}, alertDockWidget_ {nullptr},
radarSiteDialog_ {nullptr}, radarSiteDialog_ {nullptr},
radarProductModel_ {nullptr}, radarProductModel_ {nullptr},
textEventManager_ {manager::TextEventManager::Instance()},
maps_ {}, maps_ {},
elevationCuts_ {}, elevationCuts_ {},
elevationButtonsChanged_ {false}, elevationButtonsChanged_ {false},
@ -109,7 +111,8 @@ public:
ui::AlertDockWidget* alertDockWidget_; ui::AlertDockWidget* alertDockWidget_;
ui::RadarSiteDialog* radarSiteDialog_; ui::RadarSiteDialog* radarSiteDialog_;
std::unique_ptr<model::RadarProductModel> radarProductModel_; std::unique_ptr<model::RadarProductModel> radarProductModel_;
std::shared_ptr<manager::TextEventManager> textEventManager_;
std::vector<map::MapWidget*> maps_; std::vector<map::MapWidget*> maps_;
std::vector<float> elevationCuts_; std::vector<float> elevationCuts_;
@ -306,8 +309,7 @@ void MainWindow::on_actionOpenTextEvent_triggered()
[=](const QString& file) [=](const QString& file)
{ {
logger_->info("Selected: {}", file.toStdString()); logger_->info("Selected: {}", file.toStdString());
manager::TextEventManager::Instance().LoadFile( p->textEventManager_->LoadFile(file.toStdString());
file.toStdString());
}); });
dialog->open(); dialog->open();

View file

@ -1,11 +1,14 @@
#include <scwx/qt/manager/text_event_manager.hpp> #include <scwx/qt/manager/text_event_manager.hpp>
#include <scwx/awips/text_product_file.hpp> #include <scwx/awips/text_product_file.hpp>
#include <scwx/provider/warnings_provider.hpp>
#include <scwx/util/logger.hpp> #include <scwx/util/logger.hpp>
#include <scwx/util/threads.hpp> #include <scwx/util/threads.hpp>
#include <shared_mutex> #include <shared_mutex>
#include <unordered_map> #include <unordered_map>
#include <boost/asio/steady_timer.hpp>
namespace scwx namespace scwx
{ {
namespace qt namespace qt
@ -16,25 +19,44 @@ namespace manager
static const std::string logPrefix_ = "scwx::qt::manager::text_event_manager"; static const std::string logPrefix_ = "scwx::qt::manager::text_event_manager";
static const auto logger_ = scwx::util::Logger::Create(logPrefix_); static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
static const std::string& kDefaultWarningsProviderUrl {
"https://warnings.allisonhouse.com"};
class TextEventManager::Impl class TextEventManager::Impl
{ {
public: public:
explicit Impl(TextEventManager* self) : 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<awips::TextProductMessage> message); void HandleMessage(std::shared_ptr<awips::TextProductMessage> message);
void Refresh();
TextEventManager* self_; TextEventManager* self_;
boost::asio::steady_timer refreshTimer_;
std::mutex refreshMutex_;
std::unordered_map<types::TextEventKey, std::unordered_map<types::TextEventKey,
std::vector<std::shared_ptr<awips::TextProductMessage>>, std::vector<std::shared_ptr<awips::TextProductMessage>>,
types::TextEventHash<types::TextEventKey>> types::TextEventHash<types::TextEventKey>>
textEventMap_; textEventMap_;
std::shared_mutex textEventMutex_; std::shared_mutex textEventMutex_;
provider::WarningsProvider warningsProvider_;
}; };
TextEventManager::TextEventManager() : p(std::make_unique<Impl>(this)) {} TextEventManager::TextEventManager() : p(std::make_unique<Impl>(this)) {}
@ -159,10 +181,74 @@ void TextEventManager::Impl::HandleMessage(
} }
} }
TextEventManager& TextEventManager::Instance() void TextEventManager::Impl::Refresh()
{ {
static TextEventManager textEventManager_ {}; using namespace std::chrono;
return textEventManager_;
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> TextEventManager::Instance()
{
static std::weak_ptr<TextEventManager> textEventManagerReference_ {};
static std::mutex instanceMutex_ {};
std::unique_lock lock(instanceMutex_);
std::shared_ptr<TextEventManager> textEventManager =
textEventManagerReference_.lock();
if (textEventManager == nullptr)
{
textEventManager = std::make_shared<TextEventManager>();
textEventManagerReference_ = textEventManager;
}
return textEventManager;
} }
} // namespace manager } // namespace manager

View file

@ -29,7 +29,7 @@ public:
void LoadFile(const std::string& filename); void LoadFile(const std::string& filename);
static TextEventManager& Instance(); static std::shared_ptr<TextEventManager> Instance();
signals: signals:
void AlertUpdated(const types::TextEventKey& key, size_t messageIndex); void AlertUpdated(const types::TextEventKey& key, size_t messageIndex);

View file

@ -55,6 +55,7 @@ class AlertLayerHandler : public QObject
{ {
Q_OBJECT public : Q_OBJECT public :
explicit AlertLayerHandler() : explicit AlertLayerHandler() :
textEventManager_ {manager::TextEventManager::Instance()},
alertUpdateTimer_ {util::io_context()}, alertUpdateTimer_ {util::io_context()},
alertSourceMap_ {}, alertSourceMap_ {},
featureMap_ {} featureMap_ {}
@ -68,7 +69,7 @@ class AlertLayerHandler : public QObject
} }
} }
connect(&manager::TextEventManager::Instance(), connect(textEventManager_.get(),
&manager::TextEventManager::AlertUpdated, &manager::TextEventManager::AlertUpdated,
this, this,
&AlertLayerHandler::HandleAlert); &AlertLayerHandler::HandleAlert);
@ -87,6 +88,8 @@ class AlertLayerHandler : public QObject
void HandleAlert(const types::TextEventKey& key, size_t messageIndex); void HandleAlert(const types::TextEventKey& key, size_t messageIndex);
void UpdateAlerts(); void UpdateAlerts();
std::shared_ptr<manager::TextEventManager> textEventManager_;
boost::asio::steady_timer alertUpdateTimer_; boost::asio::steady_timer alertUpdateTimer_;
std::unordered_map<std::pair<awips::Phenomenon, bool>, std::unordered_map<std::pair<awips::Phenomenon, bool>,
QVariantMap, QVariantMap,
@ -252,15 +255,13 @@ AlertLayerHandler::FeatureList(awips::Phenomenon phenomenon, bool alertActive)
void AlertLayerHandler::HandleAlert(const types::TextEventKey& key, void AlertLayerHandler::HandleAlert(const types::TextEventKey& key,
size_t messageIndex) size_t messageIndex)
{ {
auto& textEventManager = manager::TextEventManager::Instance();
// Skip alert if there are more messages to be processed // 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; return;
} }
auto message = textEventManager.message_list(key).at(messageIndex); auto message = textEventManager_->message_list(key).at(messageIndex);
std::unordered_set<std::pair<awips::Phenomenon, bool>, std::unordered_set<std::pair<awips::Phenomenon, bool>,
AlertTypeHash<std::pair<awips::Phenomenon, bool>>> AlertTypeHash<std::pair<awips::Phenomenon, bool>>>
alertsUpdated {}; alertsUpdated {};

View file

@ -45,6 +45,8 @@ public:
static std::string GetStartTime(const types::TextEventKey& key); static std::string GetStartTime(const types::TextEventKey& key);
static std::string GetEndTime(const types::TextEventKey& key); static std::string GetEndTime(const types::TextEventKey& key);
std::shared_ptr<manager::TextEventManager> textEventManager_;
QList<types::TextEventKey> textEventKeys_; QList<types::TextEventKey> textEventKeys_;
GeographicLib::Geodesic geodesic_; GeographicLib::Geodesic geodesic_;
@ -199,8 +201,7 @@ void AlertModel::HandleAlert(const types::TextEventKey& alertKey,
double distanceInMeters; double distanceInMeters;
// Get the most recent segment for the event // Get the most recent segment for the event
auto alertMessages = auto alertMessages = p->textEventManager_->message_list(alertKey);
manager::TextEventManager::Instance().message_list(alertKey);
std::shared_ptr<const awips::Segment> alertSegment = std::shared_ptr<const awips::Segment> alertSegment =
alertMessages[messageIndex]->segments().back(); alertMessages[messageIndex]->segments().back();
@ -273,6 +274,7 @@ void AlertModel::HandleMapUpdate(double latitude, double longitude)
} }
AlertModelImpl::AlertModelImpl() : AlertModelImpl::AlertModelImpl() :
textEventManager_ {manager::TextEventManager::Instance()},
textEventKeys_ {}, textEventKeys_ {},
geodesic_(GeographicLib::Constants::WGS84_a(), geodesic_(GeographicLib::Constants::WGS84_a(),
GeographicLib::Constants::WGS84_f()), GeographicLib::Constants::WGS84_f()),
@ -284,8 +286,8 @@ AlertModelImpl::AlertModelImpl() :
std::string AlertModelImpl::GetCounties(const types::TextEventKey& key) std::string AlertModelImpl::GetCounties(const types::TextEventKey& key)
{ {
auto messageList = manager::TextEventManager::Instance().message_list(key); auto messageList = manager::TextEventManager::Instance()->message_list(key);
auto& lastMessage = messageList.back(); auto& lastMessage = messageList.back();
size_t segmentCount = lastMessage->segment_count(); size_t segmentCount = lastMessage->segment_count();
auto lastSegment = lastMessage->segment(segmentCount - 1); auto lastSegment = lastMessage->segment(segmentCount - 1);
auto fipsIds = lastSegment->header_->ugc_.fips_ids(); 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) std::string AlertModelImpl::GetState(const types::TextEventKey& key)
{ {
auto messageList = manager::TextEventManager::Instance().message_list(key); auto messageList = manager::TextEventManager::Instance()->message_list(key);
auto& lastMessage = messageList.back(); auto& lastMessage = messageList.back();
size_t segmentCount = lastMessage->segment_count(); size_t segmentCount = lastMessage->segment_count();
auto lastSegment = lastMessage->segment(segmentCount - 1); auto lastSegment = lastMessage->segment(segmentCount - 1);
return util::ToString(lastSegment->header_->ugc_.states()); 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) 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& firstMessage = messageList.front();
auto firstSegment = firstMessage->segment(0); auto firstSegment = firstMessage->segment(0);
return util::TimeString( return util::TimeString(
@ -321,8 +323,8 @@ std::string AlertModelImpl::GetStartTime(const types::TextEventKey& key)
std::string AlertModelImpl::GetEndTime(const types::TextEventKey& key) std::string AlertModelImpl::GetEndTime(const types::TextEventKey& key)
{ {
auto messageList = manager::TextEventManager::Instance().message_list(key); auto messageList = manager::TextEventManager::Instance()->message_list(key);
auto& lastMessage = messageList.back(); auto& lastMessage = messageList.back();
size_t segmentCount = lastMessage->segment_count(); size_t segmentCount = lastMessage->segment_count();
auto lastSegment = lastMessage->segment(segmentCount - 1); auto lastSegment = lastMessage->segment(segmentCount - 1);
return util::TimeString( return util::TimeString(

View file

@ -22,6 +22,7 @@ class AlertDialogImpl : public QObject
public: public:
explicit AlertDialogImpl(AlertDialog* self) : explicit AlertDialogImpl(AlertDialog* self) :
self_ {self}, self_ {self},
textEventManager_ {manager::TextEventManager::Instance()},
goButton_ {nullptr}, goButton_ {nullptr},
key_ {}, key_ {},
centroid_ {}, centroid_ {},
@ -34,7 +35,10 @@ public:
void SelectIndex(size_t newIndex); void SelectIndex(size_t newIndex);
void UpdateAlertInfo(); void UpdateAlertInfo();
AlertDialog* self_; AlertDialog* self_;
std::shared_ptr<manager::TextEventManager> textEventManager_;
QPushButton* goButton_; QPushButton* goButton_;
types::TextEventKey key_; types::TextEventKey key_;
common::Coordinate centroid_; common::Coordinate centroid_;
@ -67,7 +71,7 @@ AlertDialog::~AlertDialog()
void AlertDialogImpl::ConnectSignals() void AlertDialogImpl::ConnectSignals()
{ {
connect( connect(
&manager::TextEventManager::Instance(), textEventManager_.get(),
&manager::TextEventManager::AlertUpdated, &manager::TextEventManager::AlertUpdated,
this, this,
[=](const types::TextEventKey& key) [=](const types::TextEventKey& key)
@ -94,7 +98,7 @@ bool AlertDialog::SelectAlert(const types::TextEventKey& key)
setWindowTitle(QString::fromStdString(key.ToFullString())); setWindowTitle(QString::fromStdString(key.ToFullString()));
auto messages = manager::TextEventManager::Instance().message_list(key); auto messages = p->textEventManager_->message_list(key);
if (messages.empty()) if (messages.empty())
{ {
return false; return false;
@ -107,15 +111,14 @@ bool AlertDialog::SelectAlert(const types::TextEventKey& key)
void AlertDialogImpl::SelectIndex(size_t newIndex) void AlertDialogImpl::SelectIndex(size_t newIndex)
{ {
size_t messageCount = size_t messageCount = textEventManager_->message_count(key_);
manager::TextEventManager::Instance().message_count(key_);
if (newIndex >= messageCount) if (newIndex >= messageCount)
{ {
return; return;
} }
auto messages = manager::TextEventManager::Instance().message_list(key_); auto messages = textEventManager_->message_list(key_);
currentIndex_ = newIndex; currentIndex_ = newIndex;
@ -127,7 +130,7 @@ void AlertDialogImpl::SelectIndex(size_t newIndex)
void AlertDialogImpl::UpdateAlertInfo() void AlertDialogImpl::UpdateAlertInfo()
{ {
auto messages = manager::TextEventManager::Instance().message_list(key_); auto messages = textEventManager_->message_list(key_);
size_t messageCount = messages.size(); size_t messageCount = messages.size();
bool firstSelected = (currentIndex_ == 0u); bool firstSelected = (currentIndex_ == 0u);
@ -170,8 +173,7 @@ void AlertDialog::on_nextButton_clicked()
void AlertDialog::on_lastButton_clicked() void AlertDialog::on_lastButton_clicked()
{ {
p->SelectIndex(manager::TextEventManager::Instance().message_count(p->key_) - p->SelectIndex(p->textEventManager_->message_count(p->key_) - 1u);
1u);
} }
#include "alert_dialog.moc" #include "alert_dialog.moc"

View file

@ -25,6 +25,7 @@ class AlertDockWidgetImpl : QObject
public: public:
explicit AlertDockWidgetImpl(AlertDockWidget* self) : explicit AlertDockWidgetImpl(AlertDockWidget* self) :
self_ {self}, self_ {self},
textEventManager_ {manager::TextEventManager::Instance()},
alertModel_ {std::make_unique<model::AlertModel>()}, alertModel_ {std::make_unique<model::AlertModel>()},
proxyModel_ {std::make_unique<QSortFilterProxyModel>()}, proxyModel_ {std::make_unique<QSortFilterProxyModel>()},
alertDialog_ {new AlertDialog(self)}, alertDialog_ {new AlertDialog(self)},
@ -42,9 +43,10 @@ public:
void ConnectSignals(); void ConnectSignals();
AlertDockWidget* self_; AlertDockWidget* self_;
std::unique_ptr<model::AlertModel> alertModel_; std::shared_ptr<manager::TextEventManager> textEventManager_;
std::unique_ptr<QSortFilterProxyModel> proxyModel_; std::unique_ptr<model::AlertModel> alertModel_;
std::unique_ptr<QSortFilterProxyModel> proxyModel_;
AlertDialog* alertDialog_; AlertDialog* alertDialog_;
@ -109,7 +111,7 @@ void AlertDockWidgetImpl::ConnectSignals()
&QLineEdit::textChanged, &QLineEdit::textChanged,
proxyModel_.get(), proxyModel_.get(),
&QSortFilterProxyModel::setFilterWildcard); &QSortFilterProxyModel::setFilterWildcard);
connect(&manager::TextEventManager::Instance(), connect(textEventManager_.get(),
&manager::TextEventManager::AlertUpdated, &manager::TextEventManager::AlertUpdated,
alertModel_.get(), alertModel_.get(),
&model::AlertModel::HandleAlert, &model::AlertModel::HandleAlert,