mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 04:50:06 +00:00 
			
		
		
		
	Create a timer to expire alerts and remove them from the map
This commit is contained in:
		
							parent
							
								
									c418995682
								
							
						
					
					
						commit
						655fb5042f
					
				
					 1 changed files with 118 additions and 15 deletions
				
			
		|  | @ -1,10 +1,13 @@ | |||
| #include <scwx/qt/map/alert_layer.hpp> | ||||
| #include <scwx/qt/manager/text_event_manager.hpp> | ||||
| #include <scwx/util/logger.hpp> | ||||
| #include <scwx/util/threads.hpp> | ||||
| 
 | ||||
| #include <chrono> | ||||
| #include <shared_mutex> | ||||
| #include <unordered_set> | ||||
| 
 | ||||
| #include <boost/asio/steady_timer.hpp> | ||||
| #include <boost/container_hash/hash.hpp> | ||||
| 
 | ||||
| namespace scwx | ||||
|  | @ -52,7 +55,10 @@ class AlertLayerHandler : public QObject | |||
| { | ||||
|    Q_OBJECT public : | ||||
|        explicit AlertLayerHandler() : | ||||
|        alertSourceMap_ {}, featureMap_ {} | ||||
|        alertUpdateTimer_ {util::io_context()}, | ||||
|        alertUpdateTimerActive_ {true}, | ||||
|        alertSourceMap_ {}, | ||||
|        featureMap_ {} | ||||
|    { | ||||
|       for (auto& phenomenon : kAlertPhenomena_) | ||||
|       { | ||||
|  | @ -66,8 +72,9 @@ class AlertLayerHandler : public QObject | |||
|       connect(&manager::TextEventManager::Instance(), | ||||
|               &manager::TextEventManager::AlertUpdated, | ||||
|               this, | ||||
|               &AlertLayerHandler::HandleAlert, | ||||
|               Qt::QueuedConnection); | ||||
|               &AlertLayerHandler::HandleAlert); | ||||
| 
 | ||||
|       ScheduleAlertUpdate(); | ||||
|    } | ||||
|    ~AlertLayerHandler() = default; | ||||
| 
 | ||||
|  | @ -75,17 +82,24 @@ class AlertLayerHandler : public QObject | |||
| 
 | ||||
|    std::list<QMapLibreGL::Feature>* FeatureList(awips::Phenomenon phenomenon, | ||||
|                                                 bool              alertActive); | ||||
| 
 | ||||
|    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>, | ||||
|                       QVariantMap, | ||||
|                       AlertTypeHash<std::pair<awips::Phenomenon, bool>>> | ||||
|       alertSourceMap_; | ||||
|    std::unordered_multimap< | ||||
|       types::TextEventKey, | ||||
|    std::unordered_multimap<types::TextEventKey, | ||||
|                            std::tuple<awips::Phenomenon, | ||||
|                                       bool, | ||||
|                  std::list<QMapLibreGL::Feature>::iterator>, | ||||
|                                       std::list<QMapLibreGL::Feature>::iterator, | ||||
|                                       std::chrono::system_clock::time_point>, | ||||
|                            types::TextEventHash<types::TextEventKey>> | ||||
|                      featureMap_; | ||||
|    std::shared_mutex alertMutex_; | ||||
|  | @ -106,7 +120,13 @@ public: | |||
|               this, | ||||
|               &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); | ||||
| 
 | ||||
|  | @ -260,7 +280,7 @@ void AlertLayerHandler::HandleAlert(const types::TextEventKey& key, | |||
|    auto existingFeatures = featureMap_.equal_range(key); | ||||
|    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); | ||||
|       if (featureList != nullptr) | ||||
|       { | ||||
|  | @ -283,6 +303,7 @@ void AlertLayerHandler::HandleAlert(const types::TextEventKey& key, | |||
|       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 featureList = FeatureList(phenomenon, alertActive); | ||||
|  | @ -294,10 +315,10 @@ void AlertLayerHandler::HandleAlert(const types::TextEventKey& key, | |||
|             CreateFeature(segment->codedLocation_.value())); | ||||
| 
 | ||||
|          // Store iterator for created feature in feature map
 | ||||
|          featureMap_.emplace( | ||||
|             std::piecewise_construct, | ||||
|          featureMap_.emplace(std::piecewise_construct, | ||||
|                              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
 | ||||
|          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, | ||||
|                                   bool              alertActive) | ||||
| { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat