mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 05:00:05 +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); | ||||
| 
 | ||||
|    maps_.resize(mapCount); | ||||
|    timelineManager_->SetMapCount(mapCount); | ||||
| 
 | ||||
|    auto MoveSplitter = [=, this](int /*pos*/, int /*index*/) | ||||
|    { | ||||
|  | @ -720,6 +721,23 @@ void MainWindowImpl::ConnectAnimationSignals() | |||
|                  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() | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include <scwx/util/threads.hpp> | ||||
| #include <scwx/util/time.hpp> | ||||
| 
 | ||||
| #include <condition_variable> | ||||
| #include <mutex> | ||||
| 
 | ||||
| #include <boost/asio/steady_timer.hpp> | ||||
|  | @ -26,6 +27,9 @@ enum class Direction | |||
|    Next | ||||
| }; | ||||
| 
 | ||||
| // Wait up to 5 seconds for radar sweeps to update
 | ||||
| static constexpr std::chrono::seconds kRadarSweepMonitorTimeout_ {5}; | ||||
| 
 | ||||
| class TimelineManager::Impl | ||||
| { | ||||
| public: | ||||
|  | @ -49,11 +53,16 @@ public: | |||
|       std::shared_ptr<manager::RadarProductManager> radarProductManager, | ||||
|       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 Play(); | ||||
|    void SelectTime(std::chrono::system_clock::time_point selectedTime = {}); | ||||
|    void Step(Direction direction); | ||||
| 
 | ||||
|    std::size_t                           mapCount_ {0}; | ||||
|    std::string                           radarSite_ {"?"}; | ||||
|    std::string                           previousRadarSite_ {"?"}; | ||||
|    std::chrono::system_clock::time_point pinnedTime_ {}; | ||||
|  | @ -63,6 +72,12 @@ public: | |||
|    std::chrono::minutes                  loopTime_ {30}; | ||||
|    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}; | ||||
|    boost::asio::steady_timer animationTimer_ {scwx::util::io_context()}; | ||||
|    std::mutex                animationTimerMutex_ {}; | ||||
|  | @ -73,6 +88,11 @@ public: | |||
| TimelineManager::TimelineManager() : p(std::make_unique<Impl>(this)) {} | ||||
| TimelineManager::~TimelineManager() = default; | ||||
| 
 | ||||
| void TimelineManager::SetMapCount(std::size_t mapCount) | ||||
| { | ||||
|    p->mapCount_ = mapCount; | ||||
| } | ||||
| 
 | ||||
| void TimelineManager::SetRadarSite(const std::string& 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() | ||||
| { | ||||
|    // Cancel animation
 | ||||
|  | @ -306,12 +407,7 @@ void TimelineManager::Impl::Play() | |||
|             newTime = currentTime + 1min; | ||||
|          } | ||||
| 
 | ||||
|          // Unlock prior to selecting time
 | ||||
|          lock.unlock(); | ||||
| 
 | ||||
|          // Select the time
 | ||||
|          SelectTime(newTime); | ||||
| 
 | ||||
|          // Calculate the interval until the next update, prior to selecting
 | ||||
|          std::chrono::milliseconds interval; | ||||
|          if (newTime != endTime) | ||||
|          { | ||||
|  | @ -325,9 +421,16 @@ void TimelineManager::Impl::Play() | |||
|             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_}; | ||||
| 
 | ||||
|          animationTimer_.expires_after(interval); | ||||
|          animationTimer_.async_wait( | ||||
|             [this](const boost::system::error_code& e) | ||||
|             { | ||||
|  | @ -360,15 +463,30 @@ void TimelineManager::Impl::SelectTime( | |||
|    } | ||||
|    else if (selectedTime == std::chrono::system_clock::time_point {}) | ||||
|    { | ||||
|       // If a default time point is given, reset to a live view
 | ||||
|       selectedTime_ = selectedTime; | ||||
|       adjustedTime_ = selectedTime; | ||||
|       scwx::util::async( | ||||
|          [=, this]() | ||||
|          { | ||||
|             // Take a lock for time selection
 | ||||
|             std::unique_lock lock {selectTimeMutex_}; | ||||
| 
 | ||||
|       logger_->debug("Time updated: Live"); | ||||
|             // If a default time point is given, reset to a live view
 | ||||
|             selectedTime_ = selectedTime; | ||||
|             adjustedTime_ = selectedTime; | ||||
| 
 | ||||
|       Q_EMIT self_->LiveStateUpdated(true); | ||||
|       Q_EMIT self_->VolumeTimeUpdated(selectedTime); | ||||
|       Q_EMIT self_->SelectedTimeUpdated(selectedTime); | ||||
|             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_->VolumeTimeUpdated(selectedTime); | ||||
|             Q_EMIT self_->SelectedTimeUpdated(selectedTime); | ||||
| 
 | ||||
|             // Wait for radar sweeps to update
 | ||||
|             RadarSweepMonitorWait(radarSweepMonitorLock); | ||||
|          }); | ||||
| 
 | ||||
|       return; | ||||
|    } | ||||
|  | @ -395,9 +513,15 @@ void TimelineManager::Impl::SelectTime( | |||
|          // The timeline is no longer live
 | ||||
|          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 the adjusted time changed, or if a new radar site has been
 | ||||
|             // selected
 | ||||
|             if (adjustedTime_ != *elementPtr || | ||||
|  | @ -409,6 +533,7 @@ void TimelineManager::Impl::SelectTime( | |||
|                logger_->debug("Volume time updated: {}", | ||||
|                               scwx::util::TimeString(adjustedTime_)); | ||||
| 
 | ||||
|                volumeTimeUpdated = true; | ||||
|                Q_EMIT self_->VolumeTimeUpdated(adjustedTime_); | ||||
|             } | ||||
|          } | ||||
|  | @ -426,6 +551,16 @@ void TimelineManager::Impl::SelectTime( | |||
|          Q_EMIT self_->SelectedTimeUpdated(selectedTime); | ||||
| 
 | ||||
|          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(); | ||||
| 
 | ||||
|    void SetMapCount(std::size_t mapCount); | ||||
| 
 | ||||
| public slots: | ||||
|    void SetRadarSite(const std::string& radarSite); | ||||
| 
 | ||||
|  | @ -39,6 +41,11 @@ public slots: | |||
|    void AnimationStepNext(); | ||||
|    void AnimationStepEnd(); | ||||
| 
 | ||||
|    void ReceiveRadarSweepUpdated(std::size_t mapIndex); | ||||
|    void ReceiveRadarSweepNotUpdated(std::size_t           mapIndex, | ||||
|                                     types::NoUpdateReason reason); | ||||
|    void ReceiveMapWidgetPainted(std::size_t mapIndex); | ||||
| 
 | ||||
| signals: | ||||
|    void SelectedTimeUpdated(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
 | ||||
|    ImGui::Render(); | ||||
|    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); | ||||
| 
 | ||||
|    // Paint complete
 | ||||
|    Q_EMIT WidgetPainted(); | ||||
| } | ||||
| 
 | ||||
| void MapWidget::mapChanged(QMapLibreGL::Map::MapChange mapChange) | ||||
|  |  | |||
|  | @ -145,6 +145,7 @@ signals: | |||
|    void RadarSiteUpdated(std::shared_ptr<config::RadarSite> radarSite); | ||||
|    void RadarSweepUpdated(); | ||||
|    void RadarSweepNotUpdated(types::NoUpdateReason reason); | ||||
|    void WidgetPainted(); | ||||
| }; | ||||
| 
 | ||||
| } // namespace map
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat