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},
level2ProductsWidget_ {nullptr},
level2SettingsWidget_ {nullptr},
level3ProductsWidget_ {nullptr},
alertDockWidget_ {nullptr},
radarSiteDialog_ {nullptr},
radarProductModel_ {nullptr},
textEventManager_ {manager::TextEventManager::Instance()},
maps_ {},
elevationCuts_ {},
elevationButtonsChanged_ {false},
@ -110,6 +112,7 @@ public:
ui::RadarSiteDialog* radarSiteDialog_;
std::unique_ptr<model::RadarProductModel> radarProductModel_;
std::shared_ptr<manager::TextEventManager> textEventManager_;
std::vector<map::MapWidget*> maps_;
std::vector<float> 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();

View file

@ -1,11 +1,14 @@
#include <scwx/qt/manager/text_event_manager.hpp>
#include <scwx/awips/text_product_file.hpp>
#include <scwx/provider/warnings_provider.hpp>
#include <scwx/util/logger.hpp>
#include <scwx/util/threads.hpp>
#include <shared_mutex>
#include <unordered_map>
#include <boost/asio/steady_timer.hpp>
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<awips::TextProductMessage> message);
void Refresh();
TextEventManager* self_;
boost::asio::steady_timer refreshTimer_;
std::mutex refreshMutex_;
std::unordered_map<types::TextEventKey,
std::vector<std::shared_ptr<awips::TextProductMessage>>,
types::TextEventHash<types::TextEventKey>>
textEventMap_;
std::shared_mutex textEventMutex_;
provider::WarningsProvider warningsProvider_;
};
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_ {};
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> 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

View file

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

View file

@ -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<manager::TextEventManager> textEventManager_;
boost::asio::steady_timer alertUpdateTimer_;
std::unordered_map<std::pair<awips::Phenomenon, bool>,
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<std::pair<awips::Phenomenon, bool>,
AlertTypeHash<std::pair<awips::Phenomenon, bool>>>
alertsUpdated {};

View file

@ -45,6 +45,8 @@ public:
static std::string GetStartTime(const types::TextEventKey& key);
static std::string GetEndTime(const types::TextEventKey& key);
std::shared_ptr<manager::TextEventManager> textEventManager_;
QList<types::TextEventKey> 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<const awips::Segment> 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,7 +286,7 @@ AlertModelImpl::AlertModelImpl() :
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();
size_t segmentCount = lastMessage->segment_count();
auto lastSegment = lastMessage->segment(segmentCount - 1);
@ -303,7 +305,7 @@ 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 messageList = manager::TextEventManager::Instance()->message_list(key);
auto& lastMessage = messageList.back();
size_t segmentCount = lastMessage->segment_count();
auto lastSegment = lastMessage->segment(segmentCount - 1);
@ -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,7 +323,7 @@ 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 messageList = manager::TextEventManager::Instance()->message_list(key);
auto& lastMessage = messageList.back();
size_t segmentCount = lastMessage->segment_count();
auto lastSegment = lastMessage->segment(segmentCount - 1);

View file

@ -22,6 +22,7 @@ class AlertDialogImpl : public QObject
public:
explicit AlertDialogImpl(AlertDialog* self) :
self_ {self},
textEventManager_ {manager::TextEventManager::Instance()},
goButton_ {nullptr},
key_ {},
centroid_ {},
@ -35,6 +36,9 @@ public:
void UpdateAlertInfo();
AlertDialog* self_;
std::shared_ptr<manager::TextEventManager> 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"

View file

@ -25,6 +25,7 @@ class AlertDockWidgetImpl : QObject
public:
explicit AlertDockWidgetImpl(AlertDockWidget* self) :
self_ {self},
textEventManager_ {manager::TextEventManager::Instance()},
alertModel_ {std::make_unique<model::AlertModel>()},
proxyModel_ {std::make_unique<QSortFilterProxyModel>()},
alertDialog_ {new AlertDialog(self)},
@ -43,6 +44,7 @@ public:
void ConnectSignals();
AlertDockWidget* self_;
std::shared_ptr<manager::TextEventManager> textEventManager_;
std::unique_ptr<model::AlertModel> alertModel_;
std::unique_ptr<QSortFilterProxyModel> proxyModel_;
@ -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,