mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 14:30:06 +00:00 
			
		
		
		
	Synchronize radar sweep updates to timeline manager
This commit is contained in:
		
							parent
							
								
									6b179fe35c
								
							
						
					
					
						commit
						27958415c5
					
				
					 5 changed files with 179 additions and 15 deletions
				
			
		|  | @ -560,6 +560,7 @@ void MainWindowImpl::ConfigureMapLayout() | ||||||
|    vs->setHandleWidth(1); |    vs->setHandleWidth(1); | ||||||
| 
 | 
 | ||||||
|    maps_.resize(mapCount); |    maps_.resize(mapCount); | ||||||
|  |    timelineManager_->SetMapCount(mapCount); | ||||||
| 
 | 
 | ||||||
|    auto MoveSplitter = [=, this](int /*pos*/, int /*index*/) |    auto MoveSplitter = [=, this](int /*pos*/, int /*index*/) | ||||||
|    { |    { | ||||||
|  | @ -720,6 +721,23 @@ void MainWindowImpl::ConnectAnimationSignals() | ||||||
|                  map->SetAutoUpdate(isLive); |                  map->SetAutoUpdate(isLive); | ||||||
|               } |               } | ||||||
|            }); |            }); | ||||||
|  | 
 | ||||||
|  |    for (std::size_t i = 0; i < maps_.size(); i++) | ||||||
|  |    { | ||||||
|  |       connect(maps_[i], | ||||||
|  |               &map::MapWidget::RadarSweepUpdated, | ||||||
|  |               timelineManager_.get(), | ||||||
|  |               [=, this]() { timelineManager_->ReceiveRadarSweepUpdated(i); }); | ||||||
|  |       connect(maps_[i], | ||||||
|  |               &map::MapWidget::RadarSweepNotUpdated, | ||||||
|  |               timelineManager_.get(), | ||||||
|  |               [=, this](types::NoUpdateReason reason) | ||||||
|  |               { timelineManager_->ReceiveRadarSweepNotUpdated(i, reason); }); | ||||||
|  |       connect(maps_[i], | ||||||
|  |               &map::MapWidget::WidgetPainted, | ||||||
|  |               timelineManager_.get(), | ||||||
|  |               [=, this]() { timelineManager_->ReceiveMapWidgetPainted(i); }); | ||||||
|  |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MainWindowImpl::ConnectOtherSignals() | void MainWindowImpl::ConnectOtherSignals() | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| #include <scwx/util/threads.hpp> | #include <scwx/util/threads.hpp> | ||||||
| #include <scwx/util/time.hpp> | #include <scwx/util/time.hpp> | ||||||
| 
 | 
 | ||||||
|  | #include <condition_variable> | ||||||
| #include <mutex> | #include <mutex> | ||||||
| 
 | 
 | ||||||
| #include <boost/asio/steady_timer.hpp> | #include <boost/asio/steady_timer.hpp> | ||||||
|  | @ -26,6 +27,9 @@ enum class Direction | ||||||
|    Next |    Next | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | // Wait up to 5 seconds for radar sweeps to update
 | ||||||
|  | static constexpr std::chrono::seconds kRadarSweepMonitorTimeout_ {5}; | ||||||
|  | 
 | ||||||
| class TimelineManager::Impl | class TimelineManager::Impl | ||||||
| { | { | ||||||
| public: | public: | ||||||
|  | @ -49,11 +53,16 @@ public: | ||||||
|       std::shared_ptr<manager::RadarProductManager> radarProductManager, |       std::shared_ptr<manager::RadarProductManager> radarProductManager, | ||||||
|       const std::set<std::chrono::system_clock::time_point>& volumeTimes); |       const std::set<std::chrono::system_clock::time_point>& volumeTimes); | ||||||
| 
 | 
 | ||||||
|  |    void RadarSweepMonitorDisable(); | ||||||
|  |    void RadarSweepMonitorReset(); | ||||||
|  |    void RadarSweepMonitorWait(std::unique_lock<std::mutex>& lock); | ||||||
|  | 
 | ||||||
|    void Pause(); |    void Pause(); | ||||||
|    void Play(); |    void Play(); | ||||||
|    void SelectTime(std::chrono::system_clock::time_point selectedTime = {}); |    void SelectTime(std::chrono::system_clock::time_point selectedTime = {}); | ||||||
|    void Step(Direction direction); |    void Step(Direction direction); | ||||||
| 
 | 
 | ||||||
|  |    std::size_t                           mapCount_ {0}; | ||||||
|    std::string                           radarSite_ {"?"}; |    std::string                           radarSite_ {"?"}; | ||||||
|    std::string                           previousRadarSite_ {"?"}; |    std::string                           previousRadarSite_ {"?"}; | ||||||
|    std::chrono::system_clock::time_point pinnedTime_ {}; |    std::chrono::system_clock::time_point pinnedTime_ {}; | ||||||
|  | @ -63,6 +72,12 @@ public: | ||||||
|    std::chrono::minutes                  loopTime_ {30}; |    std::chrono::minutes                  loopTime_ {30}; | ||||||
|    double                                loopSpeed_ {5.0}; |    double                                loopSpeed_ {5.0}; | ||||||
| 
 | 
 | ||||||
|  |    bool                    radarSweepMonitorActive_ {false}; | ||||||
|  |    std::mutex              radarSweepMonitorMutex_ {}; | ||||||
|  |    std::condition_variable radarSweepMonitorCondition_ {}; | ||||||
|  |    std::set<std::size_t>   radarSweepsUpdated_ {}; | ||||||
|  |    std::set<std::size_t>   radarSweepsComplete_ {}; | ||||||
|  | 
 | ||||||
|    types::AnimationState     animationState_ {types::AnimationState::Pause}; |    types::AnimationState     animationState_ {types::AnimationState::Pause}; | ||||||
|    boost::asio::steady_timer animationTimer_ {scwx::util::io_context()}; |    boost::asio::steady_timer animationTimer_ {scwx::util::io_context()}; | ||||||
|    std::mutex                animationTimerMutex_ {}; |    std::mutex                animationTimerMutex_ {}; | ||||||
|  | @ -73,6 +88,11 @@ public: | ||||||
| TimelineManager::TimelineManager() : p(std::make_unique<Impl>(this)) {} | TimelineManager::TimelineManager() : p(std::make_unique<Impl>(this)) {} | ||||||
| TimelineManager::~TimelineManager() = default; | TimelineManager::~TimelineManager() = default; | ||||||
| 
 | 
 | ||||||
|  | void TimelineManager::SetMapCount(std::size_t mapCount) | ||||||
|  | { | ||||||
|  |    p->mapCount_ = mapCount; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void TimelineManager::SetRadarSite(const std::string& radarSite) | void TimelineManager::SetRadarSite(const std::string& radarSite) | ||||||
| { | { | ||||||
|    if (p->radarSite_ == radarSite) |    if (p->radarSite_ == radarSite) | ||||||
|  | @ -217,6 +237,87 @@ void TimelineManager::AnimationStepEnd() | ||||||
|    } |    } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void TimelineManager::Impl::RadarSweepMonitorDisable() | ||||||
|  | { | ||||||
|  |    radarSweepMonitorActive_ = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TimelineManager::Impl::RadarSweepMonitorReset() | ||||||
|  | { | ||||||
|  |    radarSweepsUpdated_.clear(); | ||||||
|  |    radarSweepsComplete_.clear(); | ||||||
|  | 
 | ||||||
|  |    radarSweepMonitorActive_ = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TimelineManager::Impl::RadarSweepMonitorWait( | ||||||
|  |    std::unique_lock<std::mutex>& lock) | ||||||
|  | { | ||||||
|  |    radarSweepMonitorCondition_.wait_for(lock, kRadarSweepMonitorTimeout_); | ||||||
|  |    radarSweepMonitorActive_ = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TimelineManager::ReceiveRadarSweepUpdated(std::size_t mapIndex) | ||||||
|  | { | ||||||
|  |    if (!p->radarSweepMonitorActive_) | ||||||
|  |    { | ||||||
|  |       return; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    std::unique_lock lock {p->radarSweepMonitorMutex_}; | ||||||
|  | 
 | ||||||
|  |    // Radar sweep is updated, but still needs painted
 | ||||||
|  |    p->radarSweepsUpdated_.insert(mapIndex); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TimelineManager::ReceiveRadarSweepNotUpdated( | ||||||
|  |    std::size_t mapIndex, types::NoUpdateReason /* reason */) | ||||||
|  | { | ||||||
|  |    if (!p->radarSweepMonitorActive_) | ||||||
|  |    { | ||||||
|  |       return; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    std::unique_lock lock {p->radarSweepMonitorMutex_}; | ||||||
|  | 
 | ||||||
|  |    // Radar sweep is complete, no painting will occur
 | ||||||
|  |    p->radarSweepsComplete_.insert(mapIndex); | ||||||
|  | 
 | ||||||
|  |    // If all sweeps have completed rendering
 | ||||||
|  |    if (p->radarSweepsComplete_.size() == p->mapCount_) | ||||||
|  |    { | ||||||
|  |       // Notify monitors
 | ||||||
|  |       p->radarSweepMonitorActive_ = false; | ||||||
|  |       p->radarSweepMonitorCondition_.notify_all(); | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TimelineManager::ReceiveMapWidgetPainted(std::size_t mapIndex) | ||||||
|  | { | ||||||
|  |    if (!p->radarSweepMonitorActive_) | ||||||
|  |    { | ||||||
|  |       return; | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|  |    std::unique_lock lock {p->radarSweepMonitorMutex_}; | ||||||
|  | 
 | ||||||
|  |    // If the radar sweep has been updated
 | ||||||
|  |    if (p->radarSweepsUpdated_.contains(mapIndex)) | ||||||
|  |    { | ||||||
|  |       // Mark the radar sweep complete
 | ||||||
|  |       p->radarSweepsUpdated_.erase(mapIndex); | ||||||
|  |       p->radarSweepsComplete_.insert(mapIndex); | ||||||
|  | 
 | ||||||
|  |       // If all sweeps have completed rendering
 | ||||||
|  |       if (p->radarSweepsComplete_.size() == p->mapCount_) | ||||||
|  |       { | ||||||
|  |          // Notify monitors
 | ||||||
|  |          p->radarSweepMonitorActive_ = false; | ||||||
|  |          p->radarSweepMonitorCondition_.notify_all(); | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void TimelineManager::Impl::Pause() | void TimelineManager::Impl::Pause() | ||||||
| { | { | ||||||
|    // Cancel animation
 |    // Cancel animation
 | ||||||
|  | @ -306,12 +407,7 @@ void TimelineManager::Impl::Play() | ||||||
|             newTime = currentTime + 1min; |             newTime = currentTime + 1min; | ||||||
|          } |          } | ||||||
| 
 | 
 | ||||||
|          // Unlock prior to selecting time
 |          // Calculate the interval until the next update, prior to selecting
 | ||||||
|          lock.unlock(); |  | ||||||
| 
 |  | ||||||
|          // Select the time
 |  | ||||||
|          SelectTime(newTime); |  | ||||||
| 
 |  | ||||||
|          std::chrono::milliseconds interval; |          std::chrono::milliseconds interval; | ||||||
|          if (newTime != endTime) |          if (newTime != endTime) | ||||||
|          { |          { | ||||||
|  | @ -325,9 +421,16 @@ void TimelineManager::Impl::Play() | ||||||
|             interval = std::chrono::milliseconds(2500); |             interval = std::chrono::milliseconds(2500); | ||||||
|          } |          } | ||||||
| 
 | 
 | ||||||
|  |          animationTimer_.expires_after(interval); | ||||||
|  | 
 | ||||||
|  |          // Unlock prior to selecting time
 | ||||||
|  |          lock.unlock(); | ||||||
|  | 
 | ||||||
|  |          // Select the time
 | ||||||
|  |          SelectTime(newTime); | ||||||
|  | 
 | ||||||
|          std::unique_lock animationTimerLock {animationTimerMutex_}; |          std::unique_lock animationTimerLock {animationTimerMutex_}; | ||||||
| 
 | 
 | ||||||
|          animationTimer_.expires_after(interval); |  | ||||||
|          animationTimer_.async_wait( |          animationTimer_.async_wait( | ||||||
|             [this](const boost::system::error_code& e) |             [this](const boost::system::error_code& e) | ||||||
|             { |             { | ||||||
|  | @ -360,16 +463,31 @@ void TimelineManager::Impl::SelectTime( | ||||||
|    } |    } | ||||||
|    else if (selectedTime == std::chrono::system_clock::time_point {}) |    else if (selectedTime == std::chrono::system_clock::time_point {}) | ||||||
|    { |    { | ||||||
|  |       scwx::util::async( | ||||||
|  |          [=, this]() | ||||||
|  |          { | ||||||
|  |             // Take a lock for time selection
 | ||||||
|  |             std::unique_lock lock {selectTimeMutex_}; | ||||||
|  | 
 | ||||||
|             // If a default time point is given, reset to a live view
 |             // If a default time point is given, reset to a live view
 | ||||||
|             selectedTime_ = selectedTime; |             selectedTime_ = selectedTime; | ||||||
|             adjustedTime_ = selectedTime; |             adjustedTime_ = selectedTime; | ||||||
| 
 | 
 | ||||||
|             logger_->debug("Time updated: Live"); |             logger_->debug("Time updated: Live"); | ||||||
| 
 | 
 | ||||||
|  |             std::unique_lock radarSweepMonitorLock {radarSweepMonitorMutex_}; | ||||||
|  | 
 | ||||||
|  |             // Reset radar sweep monitor in preparation for update
 | ||||||
|  |             RadarSweepMonitorReset(); | ||||||
|  | 
 | ||||||
|             Q_EMIT self_->LiveStateUpdated(true); |             Q_EMIT self_->LiveStateUpdated(true); | ||||||
|             Q_EMIT self_->VolumeTimeUpdated(selectedTime); |             Q_EMIT self_->VolumeTimeUpdated(selectedTime); | ||||||
|             Q_EMIT self_->SelectedTimeUpdated(selectedTime); |             Q_EMIT self_->SelectedTimeUpdated(selectedTime); | ||||||
| 
 | 
 | ||||||
|  |             // Wait for radar sweeps to update
 | ||||||
|  |             RadarSweepMonitorWait(radarSweepMonitorLock); | ||||||
|  |          }); | ||||||
|  | 
 | ||||||
|       return; |       return; | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
|  | @ -395,9 +513,15 @@ void TimelineManager::Impl::SelectTime( | ||||||
|          // The timeline is no longer live
 |          // The timeline is no longer live
 | ||||||
|          Q_EMIT self_->LiveStateUpdated(false); |          Q_EMIT self_->LiveStateUpdated(false); | ||||||
| 
 | 
 | ||||||
|  |          bool volumeTimeUpdated = false; | ||||||
|  | 
 | ||||||
|  |          std::unique_lock radarSweepMonitorLock {radarSweepMonitorMutex_}; | ||||||
|  | 
 | ||||||
|  |          // Reset radar sweep monitor in preparation for update
 | ||||||
|  |          RadarSweepMonitorReset(); | ||||||
|  | 
 | ||||||
|          if (elementPtr != nullptr) |          if (elementPtr != nullptr) | ||||||
|          { |          { | ||||||
| 
 |  | ||||||
|             // If the adjusted time changed, or if a new radar site has been
 |             // If the adjusted time changed, or if a new radar site has been
 | ||||||
|             // selected
 |             // selected
 | ||||||
|             if (adjustedTime_ != *elementPtr || |             if (adjustedTime_ != *elementPtr || | ||||||
|  | @ -409,6 +533,7 @@ void TimelineManager::Impl::SelectTime( | ||||||
|                logger_->debug("Volume time updated: {}", |                logger_->debug("Volume time updated: {}", | ||||||
|                               scwx::util::TimeString(adjustedTime_)); |                               scwx::util::TimeString(adjustedTime_)); | ||||||
| 
 | 
 | ||||||
|  |                volumeTimeUpdated = true; | ||||||
|                Q_EMIT self_->VolumeTimeUpdated(adjustedTime_); |                Q_EMIT self_->VolumeTimeUpdated(adjustedTime_); | ||||||
|             } |             } | ||||||
|          } |          } | ||||||
|  | @ -426,6 +551,16 @@ void TimelineManager::Impl::SelectTime( | ||||||
|          Q_EMIT self_->SelectedTimeUpdated(selectedTime); |          Q_EMIT self_->SelectedTimeUpdated(selectedTime); | ||||||
| 
 | 
 | ||||||
|          previousRadarSite_ = radarSite_; |          previousRadarSite_ = radarSite_; | ||||||
|  | 
 | ||||||
|  |          if (volumeTimeUpdated) | ||||||
|  |          { | ||||||
|  |             // Wait for radar sweeps to update
 | ||||||
|  |             RadarSweepMonitorWait(radarSweepMonitorLock); | ||||||
|  |          } | ||||||
|  |          else | ||||||
|  |          { | ||||||
|  |             RadarSweepMonitorDisable(); | ||||||
|  |          } | ||||||
|       }); |       }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -24,6 +24,8 @@ public: | ||||||
| 
 | 
 | ||||||
|    static std::shared_ptr<TimelineManager> Instance(); |    static std::shared_ptr<TimelineManager> Instance(); | ||||||
| 
 | 
 | ||||||
|  |    void SetMapCount(std::size_t mapCount); | ||||||
|  | 
 | ||||||
| public slots: | public slots: | ||||||
|    void SetRadarSite(const std::string& radarSite); |    void SetRadarSite(const std::string& radarSite); | ||||||
| 
 | 
 | ||||||
|  | @ -39,6 +41,11 @@ public slots: | ||||||
|    void AnimationStepNext(); |    void AnimationStepNext(); | ||||||
|    void AnimationStepEnd(); |    void AnimationStepEnd(); | ||||||
| 
 | 
 | ||||||
|  |    void ReceiveRadarSweepUpdated(std::size_t mapIndex); | ||||||
|  |    void ReceiveRadarSweepNotUpdated(std::size_t           mapIndex, | ||||||
|  |                                     types::NoUpdateReason reason); | ||||||
|  |    void ReceiveMapWidgetPainted(std::size_t mapIndex); | ||||||
|  | 
 | ||||||
| signals: | signals: | ||||||
|    void SelectedTimeUpdated(std::chrono::system_clock::time_point dateTime); |    void SelectedTimeUpdated(std::chrono::system_clock::time_point dateTime); | ||||||
|    void VolumeTimeUpdated(std::chrono::system_clock::time_point dateTime); |    void VolumeTimeUpdated(std::chrono::system_clock::time_point dateTime); | ||||||
|  |  | ||||||
|  | @ -859,6 +859,9 @@ void MapWidget::paintGL() | ||||||
|    // Render ImGui Frame
 |    // Render ImGui Frame
 | ||||||
|    ImGui::Render(); |    ImGui::Render(); | ||||||
|    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); |    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); | ||||||
|  | 
 | ||||||
|  |    // Paint complete
 | ||||||
|  |    Q_EMIT WidgetPainted(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void MapWidget::mapChanged(QMapLibreGL::Map::MapChange mapChange) | void MapWidget::mapChanged(QMapLibreGL::Map::MapChange mapChange) | ||||||
|  |  | ||||||
|  | @ -145,6 +145,7 @@ signals: | ||||||
|    void RadarSiteUpdated(std::shared_ptr<config::RadarSite> radarSite); |    void RadarSiteUpdated(std::shared_ptr<config::RadarSite> radarSite); | ||||||
|    void RadarSweepUpdated(); |    void RadarSweepUpdated(); | ||||||
|    void RadarSweepNotUpdated(types::NoUpdateReason reason); |    void RadarSweepNotUpdated(types::NoUpdateReason reason); | ||||||
|  |    void WidgetPainted(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } // namespace map
 | } // namespace map
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat