Create a timer to expire alerts and remove them from the map

This commit is contained in:
Dan Paulat 2022-10-28 18:36:18 -05:00
parent c418995682
commit 655fb5042f

View file

@ -1,10 +1,13 @@
#include <scwx/qt/map/alert_layer.hpp> #include <scwx/qt/map/alert_layer.hpp>
#include <scwx/qt/manager/text_event_manager.hpp> #include <scwx/qt/manager/text_event_manager.hpp>
#include <scwx/util/logger.hpp> #include <scwx/util/logger.hpp>
#include <scwx/util/threads.hpp>
#include <chrono>
#include <shared_mutex> #include <shared_mutex>
#include <unordered_set> #include <unordered_set>
#include <boost/asio/steady_timer.hpp>
#include <boost/container_hash/hash.hpp> #include <boost/container_hash/hash.hpp>
namespace scwx namespace scwx
@ -52,7 +55,10 @@ class AlertLayerHandler : public QObject
{ {
Q_OBJECT public : Q_OBJECT public :
explicit AlertLayerHandler() : explicit AlertLayerHandler() :
alertSourceMap_ {}, featureMap_ {} alertUpdateTimer_ {util::io_context()},
alertUpdateTimerActive_ {true},
alertSourceMap_ {},
featureMap_ {}
{ {
for (auto& phenomenon : kAlertPhenomena_) for (auto& phenomenon : kAlertPhenomena_)
{ {
@ -66,8 +72,9 @@ class AlertLayerHandler : public QObject
connect(&manager::TextEventManager::Instance(), connect(&manager::TextEventManager::Instance(),
&manager::TextEventManager::AlertUpdated, &manager::TextEventManager::AlertUpdated,
this, this,
&AlertLayerHandler::HandleAlert, &AlertLayerHandler::HandleAlert);
Qt::QueuedConnection);
ScheduleAlertUpdate();
} }
~AlertLayerHandler() = default; ~AlertLayerHandler() = default;
@ -75,17 +82,24 @@ class AlertLayerHandler : public QObject
std::list<QMapLibreGL::Feature>* FeatureList(awips::Phenomenon phenomenon, std::list<QMapLibreGL::Feature>* FeatureList(awips::Phenomenon phenomenon,
bool alertActive); bool alertActive);
void HandleAlert(const types::TextEventKey& key, size_t messageIndex); void HandleAlert(const types::TextEventKey& key, size_t messageIndex);
void CancelAlertTimer();
void ScheduleAlertUpdate();
void UpdateAlerts(const boost::system::error_code& e = {});
boost::asio::steady_timer alertUpdateTimer_;
bool alertUpdateTimerActive_;
std::unordered_map<std::pair<awips::Phenomenon, bool>, std::unordered_map<std::pair<awips::Phenomenon, bool>,
QVariantMap, QVariantMap,
AlertTypeHash<std::pair<awips::Phenomenon, bool>>> AlertTypeHash<std::pair<awips::Phenomenon, bool>>>
alertSourceMap_; alertSourceMap_;
std::unordered_multimap< std::unordered_multimap<types::TextEventKey,
types::TextEventKey,
std::tuple<awips::Phenomenon, std::tuple<awips::Phenomenon,
bool, bool,
std::list<QMapLibreGL::Feature>::iterator>, std::list<QMapLibreGL::Feature>::iterator,
std::chrono::system_clock::time_point>,
types::TextEventHash<types::TextEventKey>> types::TextEventHash<types::TextEventKey>>
featureMap_; featureMap_;
std::shared_mutex alertMutex_; std::shared_mutex alertMutex_;
@ -106,7 +120,13 @@ public:
this, this,
&AlertLayerImpl::UpdateSource); &AlertLayerImpl::UpdateSource);
} }
~AlertLayerImpl() = default; ~AlertLayerImpl()
{
// Alert layer should never be destructed until the end of execution, this
// allows the alert timer to be cancelled and application cleanup to
// continue
AlertLayerHandler::Instance().CancelAlertTimer();
};
void UpdateSource(awips::Phenomenon phenomenon, bool alertActive); void UpdateSource(awips::Phenomenon phenomenon, bool alertActive);
@ -260,7 +280,7 @@ void AlertLayerHandler::HandleAlert(const types::TextEventKey& key,
auto existingFeatures = featureMap_.equal_range(key); auto existingFeatures = featureMap_.equal_range(key);
for (auto it = existingFeatures.first; it != existingFeatures.second; ++it) for (auto it = existingFeatures.first; it != existingFeatures.second; ++it)
{ {
auto& [phenomenon, alertActive, featureIt] = it->second; auto& [phenomenon, alertActive, featureIt, eventEnd] = it->second;
auto featureList = FeatureList(phenomenon, alertActive); auto featureList = FeatureList(phenomenon, alertActive);
if (featureList != nullptr) if (featureList != nullptr)
{ {
@ -283,6 +303,7 @@ void AlertLayerHandler::HandleAlert(const types::TextEventKey& key,
auto& vtec = segment->header_->vtecString_.front(); auto& vtec = segment->header_->vtecString_.front();
auto action = vtec.pVtec_.action(); auto action = vtec.pVtec_.action();
awips::Phenomenon phenomenon = vtec.pVtec_.phenomenon(); awips::Phenomenon phenomenon = vtec.pVtec_.phenomenon();
auto eventEnd = vtec.pVtec_.event_end();
bool alertActive = (action != awips::PVtec::Action::Canceled); bool alertActive = (action != awips::PVtec::Action::Canceled);
auto featureList = FeatureList(phenomenon, alertActive); auto featureList = FeatureList(phenomenon, alertActive);
@ -294,10 +315,10 @@ void AlertLayerHandler::HandleAlert(const types::TextEventKey& key,
CreateFeature(segment->codedLocation_.value())); CreateFeature(segment->codedLocation_.value()));
// Store iterator for created feature in feature map // Store iterator for created feature in feature map
featureMap_.emplace( featureMap_.emplace(std::piecewise_construct,
std::piecewise_construct,
std::forward_as_tuple(key), std::forward_as_tuple(key),
std::forward_as_tuple(phenomenon, alertActive, featureIt)); std::forward_as_tuple(
phenomenon, alertActive, featureIt, eventEnd));
// Mark alert type as updated // Mark alert type as updated
alertsUpdated.emplace(phenomenon, alertActive); alertsUpdated.emplace(phenomenon, alertActive);
@ -314,6 +335,88 @@ void AlertLayerHandler::HandleAlert(const types::TextEventKey& key,
} }
} }
void AlertLayerHandler::UpdateAlerts(const boost::system::error_code& e)
{
logger_->trace("UpdateAlerts");
if (e == boost::asio::error::operation_aborted)
{
logger_->debug("Alert update timer cancelled");
return;
}
else if (e != boost::system::errc::success)
{
logger_->warn("Alert update timer error: {}", e.message());
return;
}
// Take a unique lock before modifying feature lists
std::unique_lock lock(alertMutex_);
std::unordered_set<std::pair<awips::Phenomenon, bool>,
AlertTypeHash<std::pair<awips::Phenomenon, bool>>>
alertsUpdated {};
// Evaluate each rendered feature for expiration
for (auto it = featureMap_.begin(); it != featureMap_.end();)
{
auto& [phenomenon, alertActive, featureIt, eventEnd] = it->second;
// If the event has ended, remove it from the feature list
if (eventEnd < std::chrono::system_clock::now())
{
logger_->debug("Alert expired: {}", it->first.ToString());
auto featureList = FeatureList(phenomenon, alertActive);
if (featureList != nullptr)
{
// Remove existing feature for key
featureList->erase(featureIt);
// Mark alert type as updated
alertsUpdated.emplace(phenomenon, alertActive);
}
// Erase current item and increment iterator
it = featureMap_.erase(it);
}
else
{
// Current item is not expired, continue
++it;
}
}
// Release the lock after completing feature list updates
lock.unlock();
for (auto& alert : alertsUpdated)
{
// Emit signal for each updated alert type
emit AlertsUpdated(alert.first, alert.second);
}
if (alertUpdateTimerActive_)
{
ScheduleAlertUpdate();
}
}
void AlertLayerHandler::ScheduleAlertUpdate()
{
using namespace std::chrono;
alertUpdateTimer_.expires_after(15s);
alertUpdateTimer_.async_wait([=](const boost::system::error_code& e)
{ UpdateAlerts(e); });
}
void AlertLayerHandler::CancelAlertTimer()
{
alertUpdateTimerActive_ = false;
alertUpdateTimer_.cancel();
}
void AlertLayerImpl::UpdateSource(awips::Phenomenon phenomenon, void AlertLayerImpl::UpdateSource(awips::Phenomenon phenomenon,
bool alertActive) bool alertActive)
{ {