mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 07:00:06 +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,18 +48,19 @@ 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 | ||||||
|        explicit AlertLayerOldHandler() : | public: | ||||||
|  |    explicit AlertLayerOldHandler() : | ||||||
|        textEventManager_ {manager::TextEventManager::Instance()}, |        textEventManager_ {manager::TextEventManager::Instance()}, | ||||||
|        alertUpdateTimer_ {scwx::util::io_context()}, |        alertUpdateTimer_ {scwx::util::io_context()}, | ||||||
|        alertSourceMap_ {}, |        alertSourceMap_ {}, | ||||||
|  | @ -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
	
	 Dan Paulat
						Dan Paulat