Add new AlertLayerHandler

This commit is contained in:
Dan Paulat 2024-07-04 10:12:30 -05:00
parent 044e6d6885
commit 0fa3f2162b
2 changed files with 178 additions and 8 deletions

View file

@ -1,6 +1,15 @@
#include <scwx/qt/map/alert_layer.hpp>
#include <scwx/qt/manager/text_event_manager.hpp>
#include <scwx/util/logger.hpp>
#include <chrono>
#include <mutex>
#include <unordered_map>
#include <unordered_set>
#include <boost/container/stable_vector.hpp>
#include <boost/container_hash/hash.hpp>
namespace scwx
{
namespace qt
@ -11,6 +20,81 @@ namespace map
static const std::string logPrefix_ = "scwx::qt::map::alert_layer";
static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
template<class Key>
struct AlertTypeHash;
template<>
struct AlertTypeHash<std::pair<awips::Phenomenon, bool>>
{
size_t operator()(const std::pair<awips::Phenomenon, bool>& x) const;
};
class AlertLayerHandler : QObject
{
Q_OBJECT
public:
struct SegmentRecord
{
std::shared_ptr<const awips::Segment> segment_;
types::TextEventKey key_;
std::shared_ptr<const awips::TextProductMessage> message_;
std::chrono::system_clock::time_point segmentBegin_;
std::chrono::system_clock::time_point segmentEnd_;
SegmentRecord(
const std::shared_ptr<const awips::Segment>& segment,
const types::TextEventKey& key,
const std::shared_ptr<const awips::TextProductMessage>& message) :
segment_ {segment},
key_ {key},
message_ {message},
segmentBegin_ {segment->event_begin()},
segmentEnd_ {segment->event_end()}
{
}
};
explicit AlertLayerHandler()
{
connect(textEventManager_.get(),
&manager::TextEventManager::AlertUpdated,
this,
[this](const types::TextEventKey& key, std::size_t messageIndex)
{ HandleAlert(key, messageIndex); });
}
~AlertLayerHandler()
{
disconnect(textEventManager_.get(), nullptr, this, nullptr);
std::unique_lock lock(alertMutex_);
}
// NOTE: iterators are no longer stable if the stable vector moves
std::unordered_map<
std::pair<awips::Phenomenon, bool>,
boost::container::stable_vector<std::shared_ptr<SegmentRecord>>,
AlertTypeHash<std::pair<awips::Phenomenon, bool>>>
segmentsByType_ {};
std::unordered_map<
types::TextEventKey,
boost::container::stable_vector<std::shared_ptr<SegmentRecord>>,
types::TextEventHash<types::TextEventKey>>
segmentsByKey_ {};
void HandleAlert(const types::TextEventKey& key, size_t messageIndex);
static AlertLayerHandler& Instance();
std::shared_ptr<manager::TextEventManager> textEventManager_ {
manager::TextEventManager::Instance()};
std::mutex alertMutex_ {};
signals:
void AlertsUpdated(awips::Phenomenon phenomenon, bool alertActive);
};
class AlertLayer::Impl
{
public:
@ -71,6 +155,91 @@ bool AlertLayer::RunMousePicking(
eventHandler);
}
void AlertLayerHandler::HandleAlert(const types::TextEventKey& key,
size_t messageIndex)
{
logger_->trace("HandleAlert: {}", key.ToString());
std::unordered_set<std::pair<awips::Phenomenon, bool>,
AlertTypeHash<std::pair<awips::Phenomenon, bool>>>
alertsUpdated {};
auto message = textEventManager_->message_list(key).at(messageIndex);
// Determine start time for first segment
std::chrono::system_clock::time_point segmentBegin {};
if (message->segment_count() > 0)
{
segmentBegin = message->segment(0)->event_begin();
}
// Take a unique mutex before modifying segments
std::unique_lock lock {alertMutex_};
// Update any existing segments with new end time
auto& segmentsForKey = segmentsByKey_[key];
for (auto& segmentRecord : segmentsForKey)
{
if (segmentRecord->segmentEnd_ > segmentBegin)
{
segmentRecord->segmentEnd_ = segmentBegin;
}
}
// Process new segments
for (auto& segment : message->segments())
{
if (!segment->codedLocation_.has_value())
{
// Cannot handle a segment without a location
continue;
}
auto& vtec = segment->header_->vtecString_.front();
auto action = vtec.pVtec_.action();
awips::Phenomenon phenomenon = vtec.pVtec_.phenomenon();
auto eventEnd = vtec.pVtec_.event_end();
bool alertActive = (action != awips::PVtec::Action::Canceled);
auto& segmentsForType = segmentsByType_[{key.phenomenon_, alertActive}];
// Insert segment into lists
std::shared_ptr<SegmentRecord> segmentRecord =
std::make_shared<SegmentRecord>(segment, key, message);
segmentsForKey.push_back(segmentRecord);
segmentsForType.push_back(segmentRecord);
alertsUpdated.emplace(phenomenon, alertActive);
}
// Release the lock after completing segment updates
lock.unlock();
for (auto& alert : alertsUpdated)
{
// Emit signal for each updated alert type
Q_EMIT AlertsUpdated(alert.first, alert.second);
}
}
AlertLayerHandler& AlertLayerHandler::Instance()
{
static AlertLayerHandler alertLayerHandler_ {};
return alertLayerHandler_;
}
size_t AlertTypeHash<std::pair<awips::Phenomenon, bool>>::operator()(
const std::pair<awips::Phenomenon, bool>& x) const
{
size_t seed = 0;
boost::hash_combine(seed, x.first);
boost::hash_combine(seed, x.second);
return seed;
}
} // namespace map
} // namespace qt
} // namespace scwx
#include "alert_layer.moc"

View file

@ -48,18 +48,19 @@ static const std::vector<awips::Phenomenon> kAlertPhenomena_ {
awips::Phenomenon::Tornado};
template<class Key>
struct AlertTypeHash;
struct AlertTypeOldHash;
template<>
struct AlertTypeHash<std::pair<awips::Phenomenon, bool>>
struct AlertTypeOldHash<std::pair<awips::Phenomenon, bool>>
{
size_t operator()(const std::pair<awips::Phenomenon, bool>& x) const;
};
class AlertLayerOldHandler : public QObject
{
Q_OBJECT public :
explicit AlertLayerOldHandler() :
Q_OBJECT
public:
explicit AlertLayerOldHandler() :
textEventManager_ {manager::TextEventManager::Instance()},
alertUpdateTimer_ {scwx::util::io_context()},
alertSourceMap_ {},
@ -98,7 +99,7 @@ class AlertLayerOldHandler : public QObject
boost::asio::steady_timer alertUpdateTimer_;
std::unordered_map<std::pair<awips::Phenomenon, bool>,
QVariantMap,
AlertTypeHash<std::pair<awips::Phenomenon, bool>>>
AlertTypeOldHash<std::pair<awips::Phenomenon, bool>>>
alertSourceMap_;
std::unordered_multimap<types::TextEventKey,
std::tuple<awips::Phenomenon,
@ -195,7 +196,7 @@ void AlertLayerOldHandler::HandleAlert(const types::TextEventKey& key,
auto message = textEventManager_->message_list(key).at(messageIndex);
std::unordered_set<std::pair<awips::Phenomenon, bool>,
AlertTypeHash<std::pair<awips::Phenomenon, bool>>>
AlertTypeOldHash<std::pair<awips::Phenomenon, bool>>>
alertsUpdated {};
// Take a unique lock before modifying feature lists
@ -274,7 +275,7 @@ void AlertLayerOldHandler::UpdateAlerts()
std::unique_lock lock(alertMutex_);
std::unordered_set<std::pair<awips::Phenomenon, bool>,
AlertTypeHash<std::pair<awips::Phenomenon, bool>>>
AlertTypeOldHash<std::pair<awips::Phenomenon, bool>>>
alertsUpdated {};
// Evaluate each rendered feature for expiration
@ -481,7 +482,7 @@ static QString GetSuffix(awips::Phenomenon phenomenon, bool alertActive)
.arg(alertActive);
}
size_t AlertTypeHash<std::pair<awips::Phenomenon, bool>>::operator()(
size_t AlertTypeOldHash<std::pair<awips::Phenomenon, bool>>::operator()(
const std::pair<awips::Phenomenon, bool>& x) const
{
size_t seed = 0;