mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-11-01 08:00:05 +00:00
Add new AlertLayerHandler
This commit is contained in:
parent
044e6d6885
commit
0fa3f2162b
2 changed files with 178 additions and 8 deletions
|
|
@ -1,6 +1,15 @@
|
||||||
#include <scwx/qt/map/alert_layer.hpp>
|
#include <scwx/qt/map/alert_layer.hpp>
|
||||||
|
#include <scwx/qt/manager/text_event_manager.hpp>
|
||||||
#include <scwx/util/logger.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 scwx
|
||||||
{
|
{
|
||||||
namespace qt
|
namespace qt
|
||||||
|
|
@ -11,6 +20,81 @@ namespace map
|
||||||
static const std::string logPrefix_ = "scwx::qt::map::alert_layer";
|
static const std::string logPrefix_ = "scwx::qt::map::alert_layer";
|
||||||
static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
|
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
|
class AlertLayer::Impl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -71,6 +155,91 @@ bool AlertLayer::RunMousePicking(
|
||||||
eventHandler);
|
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 map
|
||||||
} // namespace qt
|
} // namespace qt
|
||||||
} // namespace scwx
|
} // namespace scwx
|
||||||
|
|
||||||
|
#include "alert_layer.moc"
|
||||||
|
|
|
||||||
|
|
@ -48,17 +48,18 @@ static const std::vector<awips::Phenomenon> kAlertPhenomena_ {
|
||||||
awips::Phenomenon::Tornado};
|
awips::Phenomenon::Tornado};
|
||||||
|
|
||||||
template<class Key>
|
template<class Key>
|
||||||
struct AlertTypeHash;
|
struct AlertTypeOldHash;
|
||||||
|
|
||||||
template<>
|
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;
|
size_t operator()(const std::pair<awips::Phenomenon, bool>& x) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AlertLayerOldHandler : public QObject
|
class AlertLayerOldHandler : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT public :
|
Q_OBJECT
|
||||||
|
public:
|
||||||
explicit AlertLayerOldHandler() :
|
explicit AlertLayerOldHandler() :
|
||||||
textEventManager_ {manager::TextEventManager::Instance()},
|
textEventManager_ {manager::TextEventManager::Instance()},
|
||||||
alertUpdateTimer_ {scwx::util::io_context()},
|
alertUpdateTimer_ {scwx::util::io_context()},
|
||||||
|
|
@ -98,7 +99,7 @@ class AlertLayerOldHandler : public QObject
|
||||||
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,
|
||||||
AlertTypeHash<std::pair<awips::Phenomenon, bool>>>
|
AlertTypeOldHash<std::pair<awips::Phenomenon, bool>>>
|
||||||
alertSourceMap_;
|
alertSourceMap_;
|
||||||
std::unordered_multimap<types::TextEventKey,
|
std::unordered_multimap<types::TextEventKey,
|
||||||
std::tuple<awips::Phenomenon,
|
std::tuple<awips::Phenomenon,
|
||||||
|
|
@ -195,7 +196,7 @@ void AlertLayerOldHandler::HandleAlert(const types::TextEventKey& key,
|
||||||
|
|
||||||
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>>>
|
AlertTypeOldHash<std::pair<awips::Phenomenon, bool>>>
|
||||||
alertsUpdated {};
|
alertsUpdated {};
|
||||||
|
|
||||||
// Take a unique lock before modifying feature lists
|
// Take a unique lock before modifying feature lists
|
||||||
|
|
@ -274,7 +275,7 @@ void AlertLayerOldHandler::UpdateAlerts()
|
||||||
std::unique_lock lock(alertMutex_);
|
std::unique_lock lock(alertMutex_);
|
||||||
|
|
||||||
std::unordered_set<std::pair<awips::Phenomenon, bool>,
|
std::unordered_set<std::pair<awips::Phenomenon, bool>,
|
||||||
AlertTypeHash<std::pair<awips::Phenomenon, bool>>>
|
AlertTypeOldHash<std::pair<awips::Phenomenon, bool>>>
|
||||||
alertsUpdated {};
|
alertsUpdated {};
|
||||||
|
|
||||||
// Evaluate each rendered feature for expiration
|
// Evaluate each rendered feature for expiration
|
||||||
|
|
@ -481,7 +482,7 @@ static QString GetSuffix(awips::Phenomenon phenomenon, bool alertActive)
|
||||||
.arg(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
|
const std::pair<awips::Phenomenon, bool>& x) const
|
||||||
{
|
{
|
||||||
size_t seed = 0;
|
size_t seed = 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue