Add text event pruning

- Still need to prune AlertLayer
- Still need to test alerts reload after being pruned
This commit is contained in:
Dan Paulat 2025-05-03 23:20:26 -05:00
parent 4719badc54
commit f37a77a9f7
6 changed files with 158 additions and 8 deletions

View file

@ -138,8 +138,10 @@ common::Coordinate AlertManager::Impl::CurrentCoordinate(
void AlertManager::Impl::HandleAlert(const types::TextEventKey& key,
size_t messageIndex) const
{
auto messages = textEventManager_->message_list(key);
// Skip alert if there are more messages to be processed
if (messageIndex + 1 < textEventManager_->message_count(key))
if (messages.empty() || messageIndex + 1 < messages.size())
{
return;
}
@ -153,7 +155,7 @@ void AlertManager::Impl::HandleAlert(const types::TextEventKey& key,
audioSettings.alert_radius().GetValue());
std::string alertWFO = audioSettings.alert_wfo().GetValue();
auto message = textEventManager_->message_list(key).at(messageIndex);
auto message = messages.at(messageIndex);
for (auto& segment : message->segments())
{

View file

@ -116,13 +116,14 @@ public:
Impl(const Impl&&) = delete;
Impl& operator=(const Impl&&) = delete;
void
HandleMessage(const std::shared_ptr<awips::TextProductMessage>& message);
void HandleMessage(const std::shared_ptr<awips::TextProductMessage>& message,
bool archiveEvent = false);
template<ranges::forward_range DateRange>
requires std::same_as<ranges::range_value_t<DateRange>,
std::chrono::sys_days>
void ListArchives(DateRange dates);
void LoadArchives(std::chrono::system_clock::time_point dateTime);
void PruneArchives();
void RefreshAsync();
void Refresh();
template<ranges::forward_range DateRange>
@ -155,6 +156,15 @@ public:
std::mutex archiveMutex_ {};
std::list<std::chrono::sys_days> archiveDates_ {};
std::mutex archiveEventKeyMutex_ {};
std::map<std::chrono::sys_days,
std::unordered_set<types::TextEventKey,
types::TextEventHash<types::TextEventKey>>>
archiveEventKeys_ {};
std::unordered_set<types::TextEventKey,
types::TextEventHash<types::TextEventKey>>
liveEventKeys_ {};
std::mutex unloadedProductMapMutex_ {};
std::map<std::chrono::sys_days,
boost::container::stable_vector<scwx::types::iem::AfosEntry>>
@ -249,7 +259,7 @@ void TextEventManager::SelectTime(
const auto today = std::chrono::floor<std::chrono::days>(dateTime);
const auto yesterday = today - std::chrono::days {1};
const auto tomorrow = today + std::chrono::days {1};
const auto dateArray = std::array {today, yesterday, tomorrow};
const auto dateArray = std::array {yesterday, today, tomorrow};
const auto dates =
dateArray |
@ -265,6 +275,7 @@ void TextEventManager::SelectTime(
p->UpdateArchiveDates(dates);
p->ListArchives(dates);
p->LoadArchives(dateTime);
p->PruneArchives();
}
catch (const std::exception& ex)
{
@ -274,7 +285,7 @@ void TextEventManager::SelectTime(
}
void TextEventManager::Impl::HandleMessage(
const std::shared_ptr<awips::TextProductMessage>& message)
const std::shared_ptr<awips::TextProductMessage>& message, bool archiveEvent)
{
using namespace std::chrono_literals;
@ -335,6 +346,12 @@ void TextEventManager::Impl::HandleMessage(
textEventMap_.emplace(key, std::vector {message});
messageIndex = 0;
updated = true;
if (!archiveEvent)
{
// Add the Text Event Key to the list of live events to prevent pruning
liveEventKeys_.insert(key);
}
}
else if (std::find_if(it->second.cbegin(),
it->second.cend(),
@ -368,6 +385,17 @@ void TextEventManager::Impl::HandleMessage(
updated = true;
};
// If this is an archive event, and the key does not exist in the live events
// Assumption: A live event will always be loaded before a duplicate archive
// event
if (archiveEvent && !liveEventKeys_.contains(key))
{
// Add the Text Event Key to the current date's archive
const std::unique_lock archiveEventKeyLock {archiveEventKeyMutex_};
auto& archiveKeys = archiveEventKeys_[wmoDate];
archiveKeys.insert(key);
}
lock.unlock();
if (updated)
@ -518,11 +546,87 @@ void TextEventManager::Impl::LoadArchives(
for (auto& message : messages)
{
HandleMessage(message);
HandleMessage(message, true);
}
}
}
void TextEventManager::Impl::PruneArchives()
{
static constexpr std::size_t kMaxArchiveDates_ = 5;
std::unordered_set<types::TextEventKey,
types::TextEventHash<types::TextEventKey>>
eventKeysToKeep {};
std::unordered_set<types::TextEventKey,
types::TextEventHash<types::TextEventKey>>
eventKeysToPrune {};
// Remove oldest dates from the archive
while (archiveDates_.size() > kMaxArchiveDates_)
{
archiveDates_.pop_front();
}
const std::unique_lock archiveEventKeyLock {archiveEventKeyMutex_};
// If there are the same number of dates in both archiveEventKeys_ and
// archiveDates_, there is nothing to prune
if (archiveEventKeys_.size() == archiveDates_.size())
{
// Nothing to prune
return;
}
const std::unique_lock unloadedProductMapLock {unloadedProductMapMutex_};
for (auto it = archiveEventKeys_.begin(); it != archiveEventKeys_.end();)
{
const auto& date = it->first;
const auto& eventKeys = it->second;
// If date is not in recent days map
if (std::find(archiveDates_.cbegin(), archiveDates_.cend(), date) ==
archiveDates_.cend())
{
// Prune these keys (unless they are in the eventKeysToKeep set)
eventKeysToPrune.insert(eventKeys.begin(), eventKeys.end());
// The date is not in the list of recent dates, remove it
it = archiveEventKeys_.erase(it);
unloadedProductMap_.erase(date);
}
else
{
// Make sure these keys don't get pruned
eventKeysToKeep.insert(eventKeys.begin(), eventKeys.end());
// The date is recent, keep it
++it;
}
}
// Remove elements from eventKeysToPrune if they are in eventKeysToKeep
for (const auto& eventKey : eventKeysToKeep)
{
eventKeysToPrune.erase(eventKey);
}
// Remove eventKeysToPrune from textEventMap
for (const auto& eventKey : eventKeysToPrune)
{
textEventMap_.erase(eventKey);
}
// If event keys were pruned, emit a signal
if (!eventKeysToPrune.empty())
{
logger_->debug("Pruned {} archive events", eventKeysToPrune.size());
Q_EMIT self_->AlertsRemoved(eventKeysToPrune);
}
}
void TextEventManager::Impl::RefreshAsync()
{
boost::asio::post(threadPool_,

View file

@ -6,6 +6,7 @@
#include <chrono>
#include <memory>
#include <string>
#include <unordered_set>
#include <boost/uuid/uuid.hpp>
#include <QObject>
@ -35,6 +36,10 @@ public:
static std::shared_ptr<TextEventManager> Instance();
signals:
void AlertsRemoved(
const std::unordered_set<types::TextEventKey,
types::TextEventHash<types::TextEventKey>>&
keys);
void AlertUpdated(const types::TextEventKey& key,
std::size_t messageIndex,
boost::uuids::uuid uuid);

View file

@ -338,7 +338,7 @@ void AlertModel::HandleAlert(const types::TextEventKey& alertKey,
auto alertMessages = p->textEventManager_->message_list(alertKey);
// Skip alert if this is not the most recent message
if (messageIndex + 1 < alertMessages.size())
if (alertMessages.empty() || messageIndex + 1 < alertMessages.size())
{
return;
}
@ -393,6 +393,35 @@ void AlertModel::HandleAlert(const types::TextEventKey& alertKey,
}
}
void AlertModel::HandleAlertsRemoved(
const std::unordered_set<types::TextEventKey,
types::TextEventHash<types::TextEventKey>>&
alertKeys)
{
logger_->trace("Handle alerts removed");
for (const auto& alertKey : alertKeys)
{
// Remove from the list of text event keys
auto it = std::find(
p->textEventKeys_.begin(), p->textEventKeys_.end(), alertKey);
if (it != p->textEventKeys_.end())
{
int row = std::distance(p->textEventKeys_.begin(), it);
beginRemoveRows(QModelIndex(), row, row);
p->textEventKeys_.erase(it);
endRemoveRows();
}
// Remove from internal maps
p->observedMap_.erase(alertKey);
p->threatCategoryMap_.erase(alertKey);
p->tornadoPossibleMap_.erase(alertKey);
p->centroidMap_.erase(alertKey);
p->distanceMap_.erase(alertKey);
}
}
void AlertModel::HandleMapUpdate(double latitude, double longitude)
{
logger_->trace("Handle map update: {}, {}", latitude, longitude);

View file

@ -4,6 +4,7 @@
#include <scwx/common/geographic.hpp>
#include <memory>
#include <unordered_set>
#include <QAbstractTableModel>
@ -51,6 +52,10 @@ public:
public slots:
void HandleAlert(const types::TextEventKey& alertKey, size_t messageIndex);
void HandleAlertsRemoved(
const std::unordered_set<types::TextEventKey,
types::TextEventHash<types::TextEventKey>>&
alertKeys);
void HandleMapUpdate(double latitude, double longitude);
private:

View file

@ -131,6 +131,11 @@ void AlertDockWidgetImpl::ConnectSignals()
&QAction::toggled,
proxyModel_.get(),
&model::AlertProxyModel::SetAlertActiveFilter);
connect(textEventManager_.get(),
&manager::TextEventManager::AlertsRemoved,
alertModel_.get(),
&model::AlertModel::HandleAlertsRemoved,
Qt::QueuedConnection);
connect(textEventManager_.get(),
&manager::TextEventManager::AlertUpdated,
alertModel_.get(),