mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 02:00:05 +00:00 
			
		
		
		
	Merge pull request #62 from dpaulat/feature/timeline-part-3
Complete Timeline Animation
This commit is contained in:
		
						commit
						effe78e1be
					
				
					 10 changed files with 730 additions and 525 deletions
				
			
		|  | @ -211,6 +211,7 @@ MainWindow::MainWindow(QWidget* parent) : | |||
|    ui->resourceExplorerDock->toggleViewAction()->setText( | ||||
|       tr("&Resource Explorer")); | ||||
|    ui->actionResourceExplorer->setVisible(false); | ||||
|    ui->resourceExplorerDock->toggleViewAction()->setVisible(false); | ||||
| 
 | ||||
|    ui->menuView->insertAction(ui->actionAlerts, | ||||
|                               p->alertDockWidget_->toggleViewAction()); | ||||
|  | @ -711,7 +712,10 @@ void MainWindowImpl::ConnectAnimationSignals() | |||
|            &manager::TimelineManager::AnimationStateUpdated, | ||||
|            animationDockWidget_, | ||||
|            &ui::AnimationDockWidget::UpdateAnimationState); | ||||
| 
 | ||||
|    connect(timelineManager_.get(), | ||||
|            &manager::TimelineManager::ViewTypeUpdated, | ||||
|            animationDockWidget_, | ||||
|            &ui::AnimationDockWidget::UpdateViewType); | ||||
|    connect(timelineManager_.get(), | ||||
|            &manager::TimelineManager::LiveStateUpdated, | ||||
|            animationDockWidget_, | ||||
|  | @ -790,6 +794,8 @@ void MainWindowImpl::ConnectOtherSignals() | |||
|          { | ||||
|             map->SetMapLocation(latitude, longitude, true); | ||||
|          } | ||||
| 
 | ||||
|          UpdateRadarSite(); | ||||
|       }, | ||||
|       Qt::QueuedConnection); | ||||
|    connect(mainWindow_, | ||||
|  | @ -807,6 +813,8 @@ void MainWindowImpl::ConnectOtherSignals() | |||
|               { | ||||
|                  map->SelectRadarSite(selectedRadarSite); | ||||
|               } | ||||
| 
 | ||||
|               UpdateRadarSite(); | ||||
|            }); | ||||
|    connect(updateManager_.get(), | ||||
|            &manager::UpdateManager::UpdateAvailable, | ||||
|  |  | |||
|  | @ -116,128 +116,181 @@ | |||
|       <number>2</number> | ||||
|      </property> | ||||
|      <item> | ||||
|       <widget class="QFrame" name="radarInfoFrame"> | ||||
|       <widget class="QScrollArea" name="radarToolboxScrollArea"> | ||||
|        <property name="frameShape"> | ||||
|         <enum>QFrame::StyledPanel</enum> | ||||
|         <enum>QFrame::NoFrame</enum> | ||||
|        </property> | ||||
|        <property name="frameShadow"> | ||||
|         <enum>QFrame::Raised</enum> | ||||
|        <property name="horizontalScrollBarPolicy"> | ||||
|         <enum>Qt::ScrollBarAlwaysOff</enum> | ||||
|        </property> | ||||
|        <layout class="QGridLayout" name="gridLayout" columnstretch="0,1,0"> | ||||
|         <item row="0" column="2"> | ||||
|          <widget class="QToolButton" name="radarSiteSelectButton"> | ||||
|           <property name="maximumSize"> | ||||
|            <size> | ||||
|             <width>16777215</width> | ||||
|             <height>13</height> | ||||
|            </size> | ||||
|           </property> | ||||
|           <property name="text"> | ||||
|            <string>...</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="1" column="1" colspan="2"> | ||||
|          <widget class="QLabel" name="radarLocationLabel"> | ||||
|           <property name="text"> | ||||
|            <string notr="true">St. Louis, MO</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="2" column="0"> | ||||
|          <widget class="QLabel" name="vcpLabel"> | ||||
|           <property name="toolTip"> | ||||
|            <string>Volume Coverage Pattern</string> | ||||
|           </property> | ||||
|           <property name="text"> | ||||
|            <string>VCP</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="0" column="1"> | ||||
|          <widget class="QLabel" name="radarSiteValueLabel"> | ||||
|           <property name="text"> | ||||
|            <string notr="true">KLSX</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="0" column="0"> | ||||
|          <widget class="QLabel" name="radarSiteLabel"> | ||||
|           <property name="text"> | ||||
|            <string>Radar Site</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="3" column="1" colspan="2"> | ||||
|          <widget class="QLabel" name="vcpDescriptionLabel"> | ||||
|           <property name="text"> | ||||
|            <string>Clear Air Mode</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item row="2" column="1" colspan="2"> | ||||
|          <widget class="QLabel" name="vcpValueLabel"> | ||||
|           <property name="text"> | ||||
|            <string notr="true">35</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QGroupBox" name="mapSettingsGroupBox"> | ||||
|        <property name="title"> | ||||
|         <string>Map Settings</string> | ||||
|        <property name="sizeAdjustPolicy"> | ||||
|         <enum>QAbstractScrollArea::AdjustToContents</enum> | ||||
|        </property> | ||||
|        <layout class="QVBoxLayout" name="verticalLayout_5"> | ||||
|         <item> | ||||
|          <widget class="QLabel" name="mapStyleLabel"> | ||||
|           <property name="text"> | ||||
|            <string>Map Style</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item> | ||||
|          <widget class="QComboBox" name="mapStyleComboBox"/> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <widget class="QGroupBox" name="radarProductGroupBox"> | ||||
|        <property name="title"> | ||||
|         <string>Radar Products</string> | ||||
|        <property name="widgetResizable"> | ||||
|         <bool>true</bool> | ||||
|        </property> | ||||
|        <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|         <item> | ||||
|          <widget class="QLabel" name="level2Label"> | ||||
|           <property name="text"> | ||||
|            <string>Level 2</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item> | ||||
|          <widget class="QWidget" name="level2ProductFrame" native="true"/> | ||||
|         </item> | ||||
|         <item> | ||||
|          <widget class="Line" name="level2Separator"> | ||||
|           <property name="orientation"> | ||||
|            <enum>Qt::Horizontal</enum> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item> | ||||
|          <widget class="QLabel" name="level3Label"> | ||||
|           <property name="text"> | ||||
|            <string>Level 3</string> | ||||
|           </property> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item> | ||||
|          <widget class="QWidget" name="level3ProductFrame" native="true"/> | ||||
|         </item> | ||||
|        </layout> | ||||
|        <widget class="QWidget" name="radarToolboxScrollAreaContents"> | ||||
|         <property name="geometry"> | ||||
|          <rect> | ||||
|           <x>0</x> | ||||
|           <y>0</y> | ||||
|           <width>175</width> | ||||
|           <height>696</height> | ||||
|          </rect> | ||||
|         </property> | ||||
|         <layout class="QVBoxLayout" name="verticalLayout_6"> | ||||
|          <property name="leftMargin"> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <property name="topMargin"> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <property name="rightMargin"> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <property name="bottomMargin"> | ||||
|           <number>0</number> | ||||
|          </property> | ||||
|          <item> | ||||
|           <widget class="QFrame" name="radarInfoFrame"> | ||||
|            <property name="frameShape"> | ||||
|             <enum>QFrame::StyledPanel</enum> | ||||
|            </property> | ||||
|            <property name="frameShadow"> | ||||
|             <enum>QFrame::Raised</enum> | ||||
|            </property> | ||||
|            <layout class="QGridLayout" name="gridLayout" columnstretch="0,1,0"> | ||||
|             <item row="0" column="2"> | ||||
|              <widget class="QToolButton" name="radarSiteSelectButton"> | ||||
|               <property name="maximumSize"> | ||||
|                <size> | ||||
|                 <width>16777215</width> | ||||
|                 <height>13</height> | ||||
|                </size> | ||||
|               </property> | ||||
|               <property name="text"> | ||||
|                <string>...</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item row="1" column="1" colspan="2"> | ||||
|              <widget class="QLabel" name="radarLocationLabel"> | ||||
|               <property name="text"> | ||||
|                <string notr="true">St. Louis, MO</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item row="2" column="0"> | ||||
|              <widget class="QLabel" name="vcpLabel"> | ||||
|               <property name="toolTip"> | ||||
|                <string>Volume Coverage Pattern</string> | ||||
|               </property> | ||||
|               <property name="text"> | ||||
|                <string>VCP</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item row="0" column="1"> | ||||
|              <widget class="QLabel" name="radarSiteValueLabel"> | ||||
|               <property name="text"> | ||||
|                <string notr="true">KLSX</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item row="0" column="0"> | ||||
|              <widget class="QLabel" name="radarSiteLabel"> | ||||
|               <property name="text"> | ||||
|                <string>Radar Site</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item row="3" column="1" colspan="2"> | ||||
|              <widget class="QLabel" name="vcpDescriptionLabel"> | ||||
|               <property name="text"> | ||||
|                <string>Clear Air Mode</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item row="2" column="1" colspan="2"> | ||||
|              <widget class="QLabel" name="vcpValueLabel"> | ||||
|               <property name="text"> | ||||
|                <string notr="true">35</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|            </layout> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QGroupBox" name="mapSettingsGroupBox"> | ||||
|            <property name="title"> | ||||
|             <string>Map Settings</string> | ||||
|            </property> | ||||
|            <layout class="QVBoxLayout" name="verticalLayout_5"> | ||||
|             <item> | ||||
|              <widget class="QLabel" name="mapStyleLabel"> | ||||
|               <property name="text"> | ||||
|                <string>Map Style</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="QComboBox" name="mapStyleComboBox"/> | ||||
|             </item> | ||||
|            </layout> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <widget class="QGroupBox" name="radarProductGroupBox"> | ||||
|            <property name="title"> | ||||
|             <string>Radar Products</string> | ||||
|            </property> | ||||
|            <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|             <item> | ||||
|              <widget class="QLabel" name="level2Label"> | ||||
|               <property name="text"> | ||||
|                <string>Level 2</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="QWidget" name="level2ProductFrame" native="true"/> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="Line" name="level2Separator"> | ||||
|               <property name="orientation"> | ||||
|                <enum>Qt::Horizontal</enum> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="QLabel" name="level3Label"> | ||||
|               <property name="text"> | ||||
|                <string>Level 3</string> | ||||
|               </property> | ||||
|              </widget> | ||||
|             </item> | ||||
|             <item> | ||||
|              <widget class="QWidget" name="level3ProductFrame" native="true"/> | ||||
|             </item> | ||||
|            </layout> | ||||
|           </widget> | ||||
|          </item> | ||||
|          <item> | ||||
|           <spacer name="radarToolboxSpacer"> | ||||
|            <property name="orientation"> | ||||
|             <enum>Qt::Vertical</enum> | ||||
|            </property> | ||||
|            <property name="sizeHint" stdset="0"> | ||||
|             <size> | ||||
|              <width>20</width> | ||||
|              <height>0</height> | ||||
|             </size> | ||||
|            </property> | ||||
|           </spacer> | ||||
|          </item> | ||||
|         </layout> | ||||
|        </widget> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|  | @ -258,19 +311,6 @@ | |||
|        </layout> | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item> | ||||
|       <spacer name="radarToolboxSpacer"> | ||||
|        <property name="orientation"> | ||||
|         <enum>Qt::Vertical</enum> | ||||
|        </property> | ||||
|        <property name="sizeHint" stdset="0"> | ||||
|         <size> | ||||
|          <width>20</width> | ||||
|          <height>0</height> | ||||
|         </size> | ||||
|        </property> | ||||
|       </spacer> | ||||
|      </item> | ||||
|     </layout> | ||||
|    </widget> | ||||
|   </widget> | ||||
|  |  | |||
|  | @ -19,7 +19,9 @@ | |||
| #   pragma warning(push, 0) | ||||
| #endif | ||||
| 
 | ||||
| #include <boost/asio/post.hpp> | ||||
| #include <boost/asio/steady_timer.hpp> | ||||
| #include <boost/asio/thread_pool.hpp> | ||||
| #include <boost/container_hash/hash.hpp> | ||||
| #include <boost/range/irange.hpp> | ||||
| #include <boost/timer/timer.hpp> | ||||
|  | @ -91,7 +93,7 @@ public: | |||
|        group_ {group}, | ||||
|        product_ {product}, | ||||
|        refreshEnabled_ {false}, | ||||
|        refreshTimer_ {scwx::util::io_context()}, | ||||
|        refreshTimer_ {threadPool_}, | ||||
|        refreshTimerMutex_ {}, | ||||
|        provider_ {nullptr} | ||||
|    { | ||||
|  | @ -106,6 +108,8 @@ public: | |||
| 
 | ||||
|    void Disable(); | ||||
| 
 | ||||
|    boost::asio::thread_pool threadPool_ {1u}; | ||||
| 
 | ||||
|    const std::string                             radarId_; | ||||
|    const common::RadarProductGroup               group_; | ||||
|    const std::string                             product_; | ||||
|  | @ -179,6 +183,8 @@ public: | |||
| 
 | ||||
|    RadarProductManager* self_; | ||||
| 
 | ||||
|    boost::asio::thread_pool threadPool_ {4u}; | ||||
| 
 | ||||
|    std::shared_ptr<ProviderManager> | ||||
|    GetLevel3ProviderManager(const std::string& product); | ||||
| 
 | ||||
|  | @ -199,6 +205,10 @@ public: | |||
|    void UpdateRecentRecords(RadarProductRecordList& recentList, | ||||
|                             std::shared_ptr<types::RadarProductRecord> record); | ||||
| 
 | ||||
|    void LoadNexradFileAsync(CreateNexradFileFunction                    load, | ||||
|                             std::shared_ptr<request::NexradFileRequest> request, | ||||
|                             std::mutex&                                 mutex, | ||||
|                             std::chrono::system_clock::time_point       time); | ||||
|    void LoadProviderData(std::chrono::system_clock::time_point time, | ||||
|                          std::shared_ptr<ProviderManager>      providerManager, | ||||
|                          RadarProductRecordMap&                recordMap, | ||||
|  | @ -206,6 +216,14 @@ public: | |||
|                          std::mutex&                           loadDataMutex, | ||||
|                          std::shared_ptr<request::NexradFileRequest> request); | ||||
|    void PopulateLevel2ProductTimes(std::chrono::system_clock::time_point time); | ||||
|    void PopulateLevel3ProductTimes(const std::string& product, | ||||
|                                    std::chrono::system_clock::time_point time); | ||||
| 
 | ||||
|    static void | ||||
|    PopulateProductTimes(std::shared_ptr<ProviderManager> providerManager, | ||||
|                         RadarProductRecordMap&           productRecordMap, | ||||
|                         std::shared_mutex&               productRecordMutex, | ||||
|                         std::chrono::system_clock::time_point time); | ||||
| 
 | ||||
|    static void | ||||
|    LoadNexradFile(CreateNexradFileFunction                    load, | ||||
|  | @ -515,7 +533,8 @@ void RadarProductManager::EnableRefresh(common::RadarProductGroup group, | |||
|          p->GetLevel3ProviderManager(product); | ||||
| 
 | ||||
|       // Only enable refresh on available products
 | ||||
|       scwx::util::async( | ||||
|       boost::asio::post( | ||||
|          p->threadPool_, | ||||
|          [=, this]() | ||||
|          { | ||||
|             providerManager->provider_->RequestAvailableProducts(); | ||||
|  | @ -606,7 +625,8 @@ void RadarProductManagerImpl::RefreshData( | |||
|       providerManager->refreshTimer_.cancel(); | ||||
|    } | ||||
| 
 | ||||
|    scwx::util::async( | ||||
|    boost::asio::post( | ||||
|       threadPool_, | ||||
|       [=, this]() | ||||
|       { | ||||
|          auto [newObjects, totalObjects] = | ||||
|  | @ -766,9 +786,8 @@ void RadarProductManagerImpl::LoadProviderData( | |||
|                   providerManager->name(), | ||||
|                   scwx::util::TimeString(time)); | ||||
| 
 | ||||
|    RadarProductManagerImpl::LoadNexradFile( | ||||
|       [=, &recordMap, &recordMutex, &loadDataMutex]() | ||||
|          -> std::shared_ptr<wsr88d::NexradFile> | ||||
|    LoadNexradFileAsync( | ||||
|       [=, &recordMap, &recordMutex]() -> std::shared_ptr<wsr88d::NexradFile> | ||||
|       { | ||||
|          std::shared_ptr<types::RadarProductRecord> existingRecord = nullptr; | ||||
|          std::shared_ptr<wsr88d::NexradFile>        nexradFile     = nullptr; | ||||
|  | @ -792,7 +811,16 @@ void RadarProductManagerImpl::LoadProviderData( | |||
|          if (existingRecord == nullptr) | ||||
|          { | ||||
|             std::string key = providerManager->provider_->FindKey(time); | ||||
|             nexradFile      = providerManager->provider_->LoadObjectByKey(key); | ||||
| 
 | ||||
|             if (!key.empty()) | ||||
|             { | ||||
|                nexradFile = providerManager->provider_->LoadObjectByKey(key); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                logger_->warn("Attempting to load object without key: {}", | ||||
|                              scwx::util::TimeString(time)); | ||||
|             } | ||||
|          } | ||||
|          else | ||||
|          { | ||||
|  | @ -857,11 +885,15 @@ void RadarProductManager::LoadData( | |||
| { | ||||
|    logger_->debug("LoadData()"); | ||||
| 
 | ||||
|    RadarProductManagerImpl::LoadNexradFile( | ||||
|       [=, &is]() -> std::shared_ptr<wsr88d::NexradFile> | ||||
|       { return wsr88d::NexradFileFactory::Create(is); }, | ||||
|       request, | ||||
|       fileLoadMutex_); | ||||
|    scwx::util::async( | ||||
|       [=, &is]() | ||||
|       { | ||||
|          RadarProductManagerImpl::LoadNexradFile( | ||||
|             [=, &is]() -> std::shared_ptr<wsr88d::NexradFile> | ||||
|             { return wsr88d::NexradFileFactory::Create(is); }, | ||||
|             request, | ||||
|             fileLoadMutex_); | ||||
|       }); | ||||
| } | ||||
| 
 | ||||
| void RadarProductManager::LoadFile( | ||||
|  | @ -898,11 +930,15 @@ void RadarProductManager::LoadFile( | |||
|                           } | ||||
|                        }); | ||||
| 
 | ||||
|       RadarProductManagerImpl::LoadNexradFile( | ||||
|          [=]() -> std::shared_ptr<wsr88d::NexradFile> | ||||
|          { return wsr88d::NexradFileFactory::Create(filename); }, | ||||
|          request, | ||||
|          fileLoadMutex_); | ||||
|       scwx::util::async( | ||||
|          [=]() | ||||
|          { | ||||
|             RadarProductManagerImpl::LoadNexradFile( | ||||
|                [=]() -> std::shared_ptr<wsr88d::NexradFile> | ||||
|                { return wsr88d::NexradFileFactory::Create(filename); }, | ||||
|                request, | ||||
|                fileLoadMutex_); | ||||
|          }); | ||||
|    } | ||||
|    else if (request != nullptr) | ||||
|    { | ||||
|  | @ -911,55 +947,91 @@ void RadarProductManager::LoadFile( | |||
|    } | ||||
| } | ||||
| 
 | ||||
| void RadarProductManagerImpl::LoadNexradFileAsync( | ||||
|    CreateNexradFileFunction                    load, | ||||
|    std::shared_ptr<request::NexradFileRequest> request, | ||||
|    std::mutex&                                 mutex, | ||||
|    std::chrono::system_clock::time_point       time) | ||||
| { | ||||
|    boost::asio::post(threadPool_, | ||||
|                      [=, &mutex]() | ||||
|                      { LoadNexradFile(load, request, mutex, time); }); | ||||
| } | ||||
| 
 | ||||
| void RadarProductManagerImpl::LoadNexradFile( | ||||
|    CreateNexradFileFunction                    load, | ||||
|    std::shared_ptr<request::NexradFileRequest> request, | ||||
|    std::mutex&                                 mutex, | ||||
|    std::chrono::system_clock::time_point       time) | ||||
| { | ||||
|    scwx::util::async( | ||||
|       [=, &mutex]() | ||||
|    std::unique_lock lock {mutex}; | ||||
| 
 | ||||
|    std::shared_ptr<wsr88d::NexradFile> nexradFile = load(); | ||||
| 
 | ||||
|    std::shared_ptr<types::RadarProductRecord> record = nullptr; | ||||
| 
 | ||||
|    bool fileValid = (nexradFile != nullptr); | ||||
| 
 | ||||
|    if (fileValid) | ||||
|    { | ||||
|       record = types::RadarProductRecord::Create(nexradFile); | ||||
| 
 | ||||
|       // If the time is already determined, override the time in the file.
 | ||||
|       // Sometimes, level 2 data has been seen to be a few seconds off
 | ||||
|       // between filename and file data. Overriding this can help prevent
 | ||||
|       // issues with locating and storing the correct records.
 | ||||
|       if (time != std::chrono::system_clock::time_point {}) | ||||
|       { | ||||
|          std::unique_lock lock {mutex}; | ||||
|          record->set_time(time); | ||||
|       } | ||||
| 
 | ||||
|          std::shared_ptr<wsr88d::NexradFile> nexradFile = load(); | ||||
|       std::shared_ptr<RadarProductManager> manager = | ||||
|          RadarProductManager::Instance(record->radar_id()); | ||||
| 
 | ||||
|          std::shared_ptr<types::RadarProductRecord> record = nullptr; | ||||
|       manager->Initialize(); | ||||
|       record = manager->p->StoreRadarProductRecord(record); | ||||
|    } | ||||
| 
 | ||||
|          bool fileValid = (nexradFile != nullptr); | ||||
|    lock.unlock(); | ||||
| 
 | ||||
|          if (fileValid) | ||||
|          { | ||||
|             record = types::RadarProductRecord::Create(nexradFile); | ||||
| 
 | ||||
|             // If the time is already determined, override the time in the file.
 | ||||
|             // Sometimes, level 2 data has been seen to be a few seconds off
 | ||||
|             // between filename and file data. Overriding this can help prevent
 | ||||
|             // issues with locating and storing the correct records.
 | ||||
|             if (time != std::chrono::system_clock::time_point {}) | ||||
|             { | ||||
|                record->set_time(time); | ||||
|             } | ||||
| 
 | ||||
|             std::shared_ptr<RadarProductManager> manager = | ||||
|                RadarProductManager::Instance(record->radar_id()); | ||||
| 
 | ||||
|             manager->Initialize(); | ||||
|             record = manager->p->StoreRadarProductRecord(record); | ||||
|          } | ||||
| 
 | ||||
|          lock.unlock(); | ||||
| 
 | ||||
|          if (request != nullptr) | ||||
|          { | ||||
|             request->set_radar_product_record(record); | ||||
|             Q_EMIT request->RequestComplete(request); | ||||
|          } | ||||
|       }); | ||||
|    if (request != nullptr) | ||||
|    { | ||||
|       request->set_radar_product_record(record); | ||||
|       Q_EMIT request->RequestComplete(request); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void RadarProductManagerImpl::PopulateLevel2ProductTimes( | ||||
|    std::chrono::system_clock::time_point time) | ||||
| { | ||||
|    PopulateProductTimes(level2ProviderManager_, | ||||
|                         level2ProductRecords_, | ||||
|                         level2ProductRecordMutex_, | ||||
|                         time); | ||||
| } | ||||
| 
 | ||||
| void RadarProductManagerImpl::PopulateLevel3ProductTimes( | ||||
|    const std::string& product, std::chrono::system_clock::time_point time) | ||||
| { | ||||
|    // Get provider manager
 | ||||
|    auto level3ProviderManager = GetLevel3ProviderManager(product); | ||||
| 
 | ||||
|    // Get product records
 | ||||
|    std::unique_lock level3ProductRecordLock {level3ProductRecordMutex_}; | ||||
|    auto&            level3ProductRecords = level3ProductRecordsMap_[product]; | ||||
|    level3ProductRecordLock.unlock(); | ||||
| 
 | ||||
|    PopulateProductTimes(level3ProviderManager, | ||||
|                         level3ProductRecords, | ||||
|                         level3ProductRecordMutex_, | ||||
|                         time); | ||||
| } | ||||
| 
 | ||||
| void RadarProductManagerImpl::PopulateProductTimes( | ||||
|    std::shared_ptr<ProviderManager>      providerManager, | ||||
|    RadarProductRecordMap&                productRecordMap, | ||||
|    std::shared_mutex&                    productRecordMutex, | ||||
|    std::chrono::system_clock::time_point time) | ||||
| { | ||||
|    const auto today     = std::chrono::floor<std::chrono::days>(time); | ||||
|    const auto yesterday = today - std::chrono::days {1}; | ||||
|  | @ -973,7 +1045,7 @@ void RadarProductManagerImpl::PopulateLevel2ProductTimes( | |||
|    std::for_each(std::execution::par_unseq, | ||||
|                  dates.begin(), | ||||
|                  dates.end(), | ||||
|                  [&, this](const auto& date) | ||||
|                  [&](const auto& date) | ||||
|                  { | ||||
|                     // Don't query for a time point in the future
 | ||||
|                     if (date > std::chrono::system_clock::now()) | ||||
|  | @ -983,8 +1055,7 @@ void RadarProductManagerImpl::PopulateLevel2ProductTimes( | |||
| 
 | ||||
|                     // Query the provider for volume time points
 | ||||
|                     auto timePoints = | ||||
|                        level2ProviderManager_->provider_->GetTimePointsByDate( | ||||
|                           date); | ||||
|                        providerManager->provider_->GetTimePointsByDate(date); | ||||
| 
 | ||||
|                     // Lock the merged volume time list
 | ||||
|                     std::unique_lock volumeTimesLock {volumeTimesMutex}; | ||||
|  | @ -995,20 +1066,19 @@ void RadarProductManagerImpl::PopulateLevel2ProductTimes( | |||
|                               std::inserter(volumeTimes, volumeTimes.end())); | ||||
|                  }); | ||||
| 
 | ||||
|    // Lock the level 2 product record map
 | ||||
|    std::unique_lock lock {level2ProductRecordMutex_}; | ||||
|    // Lock the product record map
 | ||||
|    std::unique_lock lock {productRecordMutex}; | ||||
| 
 | ||||
|    // Merge volume times into map
 | ||||
|    std::transform( | ||||
|       volumeTimes.cbegin(), | ||||
|       volumeTimes.cend(), | ||||
|       std::inserter(level2ProductRecords_, level2ProductRecords_.begin()), | ||||
|       [](const std::chrono::system_clock::time_point& time) | ||||
|       { | ||||
|          return std::pair<std::chrono::system_clock::time_point, | ||||
|                           std::weak_ptr<types::RadarProductRecord>>( | ||||
|             time, std::weak_ptr<types::RadarProductRecord> {}); | ||||
|       }); | ||||
|    std::transform(volumeTimes.cbegin(), | ||||
|                   volumeTimes.cend(), | ||||
|                   std::inserter(productRecordMap, productRecordMap.begin()), | ||||
|                   [](const std::chrono::system_clock::time_point& time) | ||||
|                   { | ||||
|                      return std::pair<std::chrono::system_clock::time_point, | ||||
|                                       std::weak_ptr<types::RadarProductRecord>>( | ||||
|                         time, std::weak_ptr<types::RadarProductRecord> {}); | ||||
|                   }); | ||||
| } | ||||
| 
 | ||||
| std::tuple<std::shared_ptr<types::RadarProductRecord>, | ||||
|  | @ -1042,7 +1112,7 @@ RadarProductManagerImpl::GetLevel2ProductRecord( | |||
|       record     = recordPtr->second.lock(); | ||||
|    } | ||||
| 
 | ||||
|    if (record == nullptr && | ||||
|    if (recordPtr != nullptr && record == nullptr && | ||||
|        recordTime != std::chrono::system_clock::time_point {}) | ||||
|    { | ||||
|       // Product is expired, reload it
 | ||||
|  | @ -1076,6 +1146,9 @@ RadarProductManagerImpl::GetLevel3ProductRecord( | |||
|    RadarProductRecordMap::const_pointer       recordPtr {nullptr}; | ||||
|    std::chrono::system_clock::time_point      recordTime {time}; | ||||
| 
 | ||||
|    // Ensure Level 3 product records are updated
 | ||||
|    PopulateLevel3ProductTimes(product, time); | ||||
| 
 | ||||
|    std::unique_lock lock {level3ProductRecordMutex_}; | ||||
| 
 | ||||
|    auto it = level3ProductRecordsMap_.find(product); | ||||
|  | @ -1099,15 +1172,12 @@ RadarProductManagerImpl::GetLevel3ProductRecord( | |||
| 
 | ||||
|    if (recordPtr != nullptr) | ||||
|    { | ||||
|       if (time == std::chrono::system_clock::time_point {} || | ||||
|           time == recordPtr->first) | ||||
|       { | ||||
|          recordTime = recordPtr->first; | ||||
|          record     = recordPtr->second.lock(); | ||||
|       } | ||||
|       // Don't check for an exact time match for level 3 products
 | ||||
|       recordTime = recordPtr->first; | ||||
|       record     = recordPtr->second.lock(); | ||||
|    } | ||||
| 
 | ||||
|    if (record == nullptr && | ||||
|    if (recordPtr != nullptr && record == nullptr && | ||||
|        recordTime != std::chrono::system_clock::time_point {}) | ||||
|    { | ||||
|       // Product is expired, reload it
 | ||||
|  | @ -1305,7 +1375,8 @@ void RadarProductManager::UpdateAvailableProducts() | |||
| 
 | ||||
|    logger_->debug("UpdateAvailableProducts()"); | ||||
| 
 | ||||
|    scwx::util::async( | ||||
|    boost::asio::post( | ||||
|       p->threadPool_, | ||||
|       [this]() | ||||
|       { | ||||
|          auto level3ProviderManager = | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| #define NOMINMAX | ||||
| 
 | ||||
| #include <scwx/qt/manager/timeline_manager.hpp> | ||||
| #include <scwx/qt/manager/radar_product_manager.hpp> | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
|  | @ -166,6 +168,8 @@ void TimelineManager::SetViewType(types::MapTime viewType) | |||
|       // If the selected view type is archive, select using the pinned time
 | ||||
|       p->SelectTimeAsync(p->pinnedTime_); | ||||
|    } | ||||
| 
 | ||||
|    Q_EMIT ViewTypeUpdated(viewType); | ||||
| } | ||||
| 
 | ||||
| void TimelineManager::SetLoopTime(std::chrono::minutes loopTime) | ||||
|  | @ -390,9 +394,10 @@ void TimelineManager::Impl::UpdateCacheLimit( | |||
|    auto endIter   = util::GetBoundedElementIterator(volumeTimes, endTime); | ||||
|    std::size_t numVolumeScans = std::distance(startIter, endIter) + 1; | ||||
| 
 | ||||
|    // Dynamically update maximum cached volume scans to 1.5x the loop length
 | ||||
|    radarProductManager->SetCacheLimit( | ||||
|       static_cast<std::size_t>(numVolumeScans * 1.5)); | ||||
|    // Dynamically update maximum cached volume scans to the lesser of
 | ||||
|    // either 1.5x the loop length or 5 greater than the loop length
 | ||||
|    radarProductManager->SetCacheLimit(std::min( | ||||
|       static_cast<std::size_t>(numVolumeScans * 1.5), numVolumeScans + 5u)); | ||||
| } | ||||
| 
 | ||||
| void TimelineManager::Impl::Play() | ||||
|  |  | |||
|  | @ -359,7 +359,8 @@ void MapWidget::SelectElevation(float elevation) | |||
| void MapWidget::SelectRadarProduct(common::RadarProductGroup group, | ||||
|                                    const std::string&        product, | ||||
|                                    std::int16_t              productCode, | ||||
|                                    std::chrono::system_clock::time_point time) | ||||
|                                    std::chrono::system_clock::time_point time, | ||||
|                                    bool                                  update) | ||||
| { | ||||
|    bool radarProductViewCreated = false; | ||||
| 
 | ||||
|  | @ -420,7 +421,7 @@ void MapWidget::SelectRadarProduct(common::RadarProductGroup group, | |||
|                common::GetLevel3Palette(productCode); | ||||
|          p->InitializeNewRadarProductView(palette); | ||||
|       } | ||||
|       else | ||||
|       else if (update) | ||||
|       { | ||||
|          radarProductView->Update(); | ||||
|       } | ||||
|  | @ -486,7 +487,9 @@ void MapWidget::SelectRadarSite(std::shared_ptr<config::RadarSite> radarSite, | |||
|          radarProductView->set_radar_product_manager(p->radarProductManager_); | ||||
|          SelectRadarProduct(radarProductView->GetRadarProductGroup(), | ||||
|                             radarProductView->GetRadarProductName(), | ||||
|                             0); | ||||
|                             0, | ||||
|                             radarProductView->selected_time(), | ||||
|                             false); | ||||
|       } | ||||
| 
 | ||||
|       AddLayers(); | ||||
|  | @ -1053,9 +1056,6 @@ void MapWidgetImpl::SetRadarSite(const std::string& radarSite) | |||
|       // Set new RadarProductManager
 | ||||
|       radarProductManager_ = manager::RadarProductManager::Instance(radarSite); | ||||
| 
 | ||||
|       // Re-enable auto-update
 | ||||
|       autoUpdateEnabled_ = true; | ||||
| 
 | ||||
|       // Connect signals to new RadarProductManager
 | ||||
|       RadarProductManagerConnect(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -54,12 +54,14 @@ public: | |||
|     * @param [in] group Radar product group | ||||
|     * @param [in] product Radar product name | ||||
|     * @param [in] productCode Radar product code (optional) | ||||
|     * @paran [in] time Product time. Default is the latest available. | ||||
|     * @param [in] time Product time. Default is the latest available. | ||||
|     * @param [in] update Whether to update the radar product view on selection | ||||
|     */ | ||||
|    void SelectRadarProduct(common::RadarProductGroup group, | ||||
|                            const std::string&        product, | ||||
|                            std::int16_t              productCode      = 0, | ||||
|                            std::chrono::system_clock::time_point time = {}); | ||||
|                            std::int16_t              productCode        = 0, | ||||
|                            std::chrono::system_clock::time_point time   = {}, | ||||
|                            bool                                  update = true); | ||||
| 
 | ||||
|    void SelectRadarProduct(std::shared_ptr<types::RadarProductRecord> record); | ||||
| 
 | ||||
|  |  | |||
|  | @ -20,20 +20,34 @@ static const auto        logger_    = scwx::util::Logger::Create(logPrefix_); | |||
| class AnimationDockWidgetImpl | ||||
| { | ||||
| public: | ||||
|    explicit AnimationDockWidgetImpl(AnimationDockWidget* self) : self_ {self} {} | ||||
|    explicit AnimationDockWidgetImpl(AnimationDockWidget* self) : self_ {self} | ||||
|    { | ||||
|       static const QString prefix   = QObject::tr("Auto Update"); | ||||
|       static const QString disabled = QObject::tr("Disabled"); | ||||
|       static const QString enabled  = QObject::tr("Enabled"); | ||||
| 
 | ||||
|       enabledString_  = QString("%1: %2").arg(prefix).arg(enabled); | ||||
|       disabledString_ = QString("%1: %2").arg(prefix).arg(disabled); | ||||
|    } | ||||
|    ~AnimationDockWidgetImpl() = default; | ||||
| 
 | ||||
|    const QIcon kPauseIcon_ {":/res/icons/font-awesome-6/pause-solid.svg"}; | ||||
|    const QIcon kPlayIcon_ {":/res/icons/font-awesome-6/play-solid.svg"}; | ||||
| 
 | ||||
|    QString enabledString_; | ||||
|    QString disabledString_; | ||||
| 
 | ||||
|    AnimationDockWidget* self_; | ||||
| 
 | ||||
|    types::AnimationState animationState_ {types::AnimationState::Pause}; | ||||
|    types::MapTime        viewType_ {types::MapTime::Live}; | ||||
|    bool                  isLive_ {true}; | ||||
| 
 | ||||
|    std::chrono::sys_days selectedDate_ {}; | ||||
|    std::chrono::seconds  selectedTime_ {}; | ||||
| 
 | ||||
|    void ConnectSignals(); | ||||
|    void UpdateAutoUpdateLabel(); | ||||
| }; | ||||
| 
 | ||||
| AnimationDockWidget::AnimationDockWidget(QWidget* parent) : | ||||
|  | @ -211,32 +225,56 @@ void AnimationDockWidgetImpl::ConnectSignals() | |||
| 
 | ||||
| void AnimationDockWidget::UpdateAnimationState(types::AnimationState state) | ||||
| { | ||||
|    // Update icon to opposite of state
 | ||||
|    switch (state) | ||||
|    if (p->animationState_ != state) | ||||
|    { | ||||
|    case types::AnimationState::Pause: | ||||
|       ui->playButton->setIcon(p->kPlayIcon_); | ||||
|       break; | ||||
|       // Update icon to opposite of state
 | ||||
|       switch (state) | ||||
|       { | ||||
|       case types::AnimationState::Pause: | ||||
|          ui->playButton->setIcon(p->kPlayIcon_); | ||||
|          break; | ||||
| 
 | ||||
|    case types::AnimationState::Play: | ||||
|       ui->playButton->setIcon(p->kPauseIcon_); | ||||
|       break; | ||||
|       case types::AnimationState::Play: | ||||
|          ui->playButton->setIcon(p->kPauseIcon_); | ||||
|          break; | ||||
|       } | ||||
| 
 | ||||
|       p->animationState_ = state; | ||||
|       p->UpdateAutoUpdateLabel(); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void AnimationDockWidget::UpdateLiveState(bool isLive) | ||||
| { | ||||
|    static const QString prefix   = tr("Auto Update"); | ||||
|    static const QString disabled = tr("Disabled"); | ||||
|    static const QString enabled  = tr("Enabled"); | ||||
| 
 | ||||
|    if (isLive) | ||||
|    if (p->isLive_ != isLive) | ||||
|    { | ||||
|       ui->autoUpdateLabel->setText(QString("%1: %2").arg(prefix).arg(enabled)); | ||||
|       p->isLive_ = isLive; | ||||
|       p->UpdateAutoUpdateLabel(); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void AnimationDockWidget::UpdateViewType(types::MapTime viewType) | ||||
| { | ||||
|    if (p->viewType_ != viewType) | ||||
|    { | ||||
|       p->viewType_ = viewType; | ||||
|       p->UpdateAutoUpdateLabel(); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| void AnimationDockWidgetImpl::UpdateAutoUpdateLabel() | ||||
| { | ||||
|    // Display "Auto Update: Enabled" if:
 | ||||
|    // - The map is live, and auto-updating (map widget update)
 | ||||
|    // - "Live" is selected, and the map is playing (timeline manager update)
 | ||||
|    if (isLive_ || (viewType_ == types::MapTime::Live && | ||||
|                    animationState_ == types::AnimationState::Play)) | ||||
|    { | ||||
|       self_->ui->autoUpdateLabel->setText(enabledString_); | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       ui->autoUpdateLabel->setText(QString("%1: %2").arg(prefix).arg(disabled)); | ||||
|       self_->ui->autoUpdateLabel->setText(disabledString_); | ||||
|    } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ public: | |||
| public slots: | ||||
|    void UpdateAnimationState(types::AnimationState state); | ||||
|    void UpdateLiveState(bool isLive); | ||||
|    void UpdateViewType(types::MapTime viewType); | ||||
| 
 | ||||
| signals: | ||||
|    void ViewTypeChanged(types::MapTime viewType); | ||||
|  |  | |||
|  | @ -7,7 +7,7 @@ | |||
|     <x>0</x> | ||||
|     <y>0</y> | ||||
|     <width>200</width> | ||||
|     <height>348</height> | ||||
|     <height>543</height> | ||||
|    </rect> | ||||
|   </property> | ||||
|   <property name="windowTitle"> | ||||
|  | @ -16,296 +16,336 @@ | |||
|   <widget class="QWidget" name="dockWidgetContents"> | ||||
|    <layout class="QVBoxLayout" name="verticalLayout"> | ||||
|     <item> | ||||
|      <widget class="QGroupBox" name="timelineGroupBox"> | ||||
|       <property name="title"> | ||||
|        <string>Timeline</string> | ||||
|      <widget class="QScrollArea" name="scrollArea"> | ||||
|       <property name="frameShape"> | ||||
|        <enum>QFrame::NoFrame</enum> | ||||
|       </property> | ||||
|       <layout class="QVBoxLayout" name="verticalLayout_6"> | ||||
|        <item> | ||||
|         <widget class="QLabel" name="autoUpdateLabel"> | ||||
|          <property name="text"> | ||||
|           <string>Auto Update: Enabled</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item> | ||||
|         <widget class="QRadioButton" name="liveViewRadioButton"> | ||||
|          <property name="text"> | ||||
|           <string>Live View</string> | ||||
|          </property> | ||||
|          <property name="checked"> | ||||
|           <bool>true</bool> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item> | ||||
|         <widget class="QRadioButton" name="archiveViewRadioButton"> | ||||
|          <property name="text"> | ||||
|           <string>Archive View</string> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item> | ||||
|         <widget class="QDateEdit" name="dateEdit"> | ||||
|          <property name="correctionMode"> | ||||
|           <enum>QAbstractSpinBox::CorrectToNearestValue</enum> | ||||
|          </property> | ||||
|          <property name="minimumDateTime"> | ||||
|           <datetime> | ||||
|            <hour>0</hour> | ||||
|            <minute>0</minute> | ||||
|            <second>0</second> | ||||
|            <year>1991</year> | ||||
|            <month>6</month> | ||||
|            <day>1</day> | ||||
|           </datetime> | ||||
|          </property> | ||||
|          <property name="displayFormat"> | ||||
|           <string>yyyy-MM-dd</string> | ||||
|          </property> | ||||
|          <property name="calendarPopup"> | ||||
|           <bool>true</bool> | ||||
|          </property> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item> | ||||
|         <widget class="QFrame" name="frame"> | ||||
|          <property name="frameShape"> | ||||
|           <enum>QFrame::StyledPanel</enum> | ||||
|          </property> | ||||
|          <property name="frameShadow"> | ||||
|           <enum>QFrame::Raised</enum> | ||||
|          </property> | ||||
|          <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|           <property name="leftMargin"> | ||||
|            <number>0</number> | ||||
|       <property name="lineWidth"> | ||||
|        <number>0</number> | ||||
|       </property> | ||||
|       <property name="horizontalScrollBarPolicy"> | ||||
|        <enum>Qt::ScrollBarAlwaysOff</enum> | ||||
|       </property> | ||||
|       <property name="widgetResizable"> | ||||
|        <bool>true</bool> | ||||
|       </property> | ||||
|       <widget class="QWidget" name="scrollAreaContents"> | ||||
|        <property name="geometry"> | ||||
|         <rect> | ||||
|          <x>0</x> | ||||
|          <y>0</y> | ||||
|          <width>182</width> | ||||
|          <height>506</height> | ||||
|         </rect> | ||||
|        </property> | ||||
|        <layout class="QVBoxLayout" name="verticalLayout_2"> | ||||
|         <property name="leftMargin"> | ||||
|          <number>0</number> | ||||
|         </property> | ||||
|         <property name="topMargin"> | ||||
|          <number>0</number> | ||||
|         </property> | ||||
|         <property name="rightMargin"> | ||||
|          <number>0</number> | ||||
|         </property> | ||||
|         <property name="bottomMargin"> | ||||
|          <number>0</number> | ||||
|         </property> | ||||
|         <item> | ||||
|          <widget class="QGroupBox" name="timelineGroupBox"> | ||||
|           <property name="title"> | ||||
|            <string>Timeline</string> | ||||
|           </property> | ||||
|           <property name="topMargin"> | ||||
|            <number>0</number> | ||||
|           <layout class="QVBoxLayout" name="verticalLayout_6"> | ||||
|            <item> | ||||
|             <widget class="QLabel" name="autoUpdateLabel"> | ||||
|              <property name="text"> | ||||
|               <string>Auto Update: Enabled</string> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QRadioButton" name="liveViewRadioButton"> | ||||
|              <property name="text"> | ||||
|               <string>Live View</string> | ||||
|              </property> | ||||
|              <property name="checked"> | ||||
|               <bool>true</bool> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QRadioButton" name="archiveViewRadioButton"> | ||||
|              <property name="text"> | ||||
|               <string>Archive View</string> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QDateEdit" name="dateEdit"> | ||||
|              <property name="correctionMode"> | ||||
|               <enum>QAbstractSpinBox::CorrectToNearestValue</enum> | ||||
|              </property> | ||||
|              <property name="minimumDateTime"> | ||||
|               <datetime> | ||||
|                <hour>0</hour> | ||||
|                <minute>0</minute> | ||||
|                <second>0</second> | ||||
|                <year>1991</year> | ||||
|                <month>6</month> | ||||
|                <day>1</day> | ||||
|               </datetime> | ||||
|              </property> | ||||
|              <property name="displayFormat"> | ||||
|               <string>yyyy-MM-dd</string> | ||||
|              </property> | ||||
|              <property name="calendarPopup"> | ||||
|               <bool>true</bool> | ||||
|              </property> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QFrame" name="frame"> | ||||
|              <property name="frameShape"> | ||||
|               <enum>QFrame::StyledPanel</enum> | ||||
|              </property> | ||||
|              <property name="frameShadow"> | ||||
|               <enum>QFrame::Raised</enum> | ||||
|              </property> | ||||
|              <layout class="QHBoxLayout" name="horizontalLayout"> | ||||
|               <property name="leftMargin"> | ||||
|                <number>0</number> | ||||
|               </property> | ||||
|               <property name="topMargin"> | ||||
|                <number>0</number> | ||||
|               </property> | ||||
|               <property name="rightMargin"> | ||||
|                <number>0</number> | ||||
|               </property> | ||||
|               <property name="bottomMargin"> | ||||
|                <number>0</number> | ||||
|               </property> | ||||
|               <item> | ||||
|                <widget class="QTimeEdit" name="timeEdit"> | ||||
|                 <property name="correctionMode"> | ||||
|                  <enum>QAbstractSpinBox::CorrectToNearestValue</enum> | ||||
|                 </property> | ||||
|                 <property name="displayFormat"> | ||||
|                  <string>HH:mm</string> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item> | ||||
|                <widget class="QLabel" name="label"> | ||||
|                 <property name="text"> | ||||
|                  <string>UTC</string> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|              </layout> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QFrame" name="frame_3"> | ||||
|              <property name="frameShape"> | ||||
|               <enum>QFrame::StyledPanel</enum> | ||||
|              </property> | ||||
|              <property name="frameShadow"> | ||||
|               <enum>QFrame::Raised</enum> | ||||
|              </property> | ||||
|              <layout class="QGridLayout" name="gridLayout"> | ||||
|               <property name="leftMargin"> | ||||
|                <number>0</number> | ||||
|               </property> | ||||
|               <property name="topMargin"> | ||||
|                <number>0</number> | ||||
|               </property> | ||||
|               <property name="rightMargin"> | ||||
|                <number>0</number> | ||||
|               </property> | ||||
|               <property name="bottomMargin"> | ||||
|                <number>0</number> | ||||
|               </property> | ||||
|               <item row="0" column="1"> | ||||
|                <widget class="QSpinBox" name="loopTimeSpinBox"> | ||||
|                 <property name="correctionMode"> | ||||
|                  <enum>QAbstractSpinBox::CorrectToNearestValue</enum> | ||||
|                 </property> | ||||
|                 <property name="suffix"> | ||||
|                  <string> min</string> | ||||
|                 </property> | ||||
|                 <property name="minimum"> | ||||
|                  <number>1</number> | ||||
|                 </property> | ||||
|                 <property name="maximum"> | ||||
|                  <number>1440</number> | ||||
|                 </property> | ||||
|                 <property name="value"> | ||||
|                  <number>30</number> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="0" column="0"> | ||||
|                <widget class="QLabel" name="loopTimeLabel"> | ||||
|                 <property name="text"> | ||||
|                  <string>Loop Time</string> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="1" column="0"> | ||||
|                <widget class="QLabel" name="loopSpeedLabel"> | ||||
|                 <property name="text"> | ||||
|                  <string>Loop Speed</string> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="1" column="1"> | ||||
|                <widget class="QDoubleSpinBox" name="loopSpeedSpinBox"> | ||||
|                 <property name="correctionMode"> | ||||
|                  <enum>QAbstractSpinBox::CorrectToNearestValue</enum> | ||||
|                 </property> | ||||
|                 <property name="suffix"> | ||||
|                  <string>x</string> | ||||
|                 </property> | ||||
|                 <property name="minimum"> | ||||
|                  <double>1.000000000000000</double> | ||||
|                 </property> | ||||
|                 <property name="value"> | ||||
|                  <double>1.000000000000000</double> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="2" column="0"> | ||||
|                <widget class="QLabel" name="loopDelayLabel"> | ||||
|                 <property name="text"> | ||||
|                  <string>Loop Delay</string> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item row="2" column="1"> | ||||
|                <widget class="QDoubleSpinBox" name="loopDelaySpinBox"> | ||||
|                 <property name="suffix"> | ||||
|                  <string> sec</string> | ||||
|                 </property> | ||||
|                 <property name="decimals"> | ||||
|                  <number>1</number> | ||||
|                 </property> | ||||
|                 <property name="maximum"> | ||||
|                  <double>15.000000000000000</double> | ||||
|                 </property> | ||||
|                 <property name="singleStep"> | ||||
|                  <double>0.100000000000000</double> | ||||
|                 </property> | ||||
|                 <property name="value"> | ||||
|                  <double>2.500000000000000</double> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|              </layout> | ||||
|             </widget> | ||||
|            </item> | ||||
|            <item> | ||||
|             <widget class="QFrame" name="frame_2"> | ||||
|              <property name="frameShape"> | ||||
|               <enum>QFrame::StyledPanel</enum> | ||||
|              </property> | ||||
|              <property name="frameShadow"> | ||||
|               <enum>QFrame::Raised</enum> | ||||
|              </property> | ||||
|              <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||||
|               <property name="spacing"> | ||||
|                <number>1</number> | ||||
|               </property> | ||||
|               <property name="leftMargin"> | ||||
|                <number>0</number> | ||||
|               </property> | ||||
|               <property name="topMargin"> | ||||
|                <number>0</number> | ||||
|               </property> | ||||
|               <property name="rightMargin"> | ||||
|                <number>0</number> | ||||
|               </property> | ||||
|               <property name="bottomMargin"> | ||||
|                <number>0</number> | ||||
|               </property> | ||||
|               <item> | ||||
|                <widget class="QToolButton" name="beginButton"> | ||||
|                 <property name="text"> | ||||
|                  <string>...</string> | ||||
|                 </property> | ||||
|                 <property name="icon"> | ||||
|                  <iconset resource="../../../../scwx-qt.qrc"> | ||||
|                   <normaloff>:/res/icons/font-awesome-6/backward-step-solid.svg</normaloff>:/res/icons/font-awesome-6/backward-step-solid.svg</iconset> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item> | ||||
|                <widget class="QToolButton" name="stepBackButton"> | ||||
|                 <property name="text"> | ||||
|                  <string>...</string> | ||||
|                 </property> | ||||
|                 <property name="icon"> | ||||
|                  <iconset resource="../../../../scwx-qt.qrc"> | ||||
|                   <normaloff>:/res/icons/font-awesome-6/angle-left-solid.svg</normaloff>:/res/icons/font-awesome-6/angle-left-solid.svg</iconset> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item> | ||||
|                <widget class="QToolButton" name="playButton"> | ||||
|                 <property name="text"> | ||||
|                  <string>...</string> | ||||
|                 </property> | ||||
|                 <property name="icon"> | ||||
|                  <iconset resource="../../../../scwx-qt.qrc"> | ||||
|                   <normaloff>:/res/icons/font-awesome-6/play-solid.svg</normaloff>:/res/icons/font-awesome-6/play-solid.svg</iconset> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item> | ||||
|                <widget class="QToolButton" name="stepNextButton"> | ||||
|                 <property name="text"> | ||||
|                  <string>...</string> | ||||
|                 </property> | ||||
|                 <property name="icon"> | ||||
|                  <iconset resource="../../../../scwx-qt.qrc"> | ||||
|                   <normaloff>:/res/icons/font-awesome-6/angle-right-solid.svg</normaloff>:/res/icons/font-awesome-6/angle-right-solid.svg</iconset> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|               <item> | ||||
|                <widget class="QToolButton" name="endButton"> | ||||
|                 <property name="text"> | ||||
|                  <string>...</string> | ||||
|                 </property> | ||||
|                 <property name="icon"> | ||||
|                  <iconset resource="../../../../scwx-qt.qrc"> | ||||
|                   <normaloff>:/res/icons/font-awesome-6/forward-step-solid.svg</normaloff>:/res/icons/font-awesome-6/forward-step-solid.svg</iconset> | ||||
|                 </property> | ||||
|                </widget> | ||||
|               </item> | ||||
|              </layout> | ||||
|             </widget> | ||||
|            </item> | ||||
|           </layout> | ||||
|          </widget> | ||||
|         </item> | ||||
|         <item> | ||||
|          <spacer name="verticalSpacer"> | ||||
|           <property name="orientation"> | ||||
|            <enum>Qt::Vertical</enum> | ||||
|           </property> | ||||
|           <property name="rightMargin"> | ||||
|            <number>0</number> | ||||
|           <property name="sizeHint" stdset="0"> | ||||
|            <size> | ||||
|             <width>20</width> | ||||
|             <height>40</height> | ||||
|            </size> | ||||
|           </property> | ||||
|           <property name="bottomMargin"> | ||||
|            <number>0</number> | ||||
|           </property> | ||||
|           <item> | ||||
|            <widget class="QTimeEdit" name="timeEdit"> | ||||
|             <property name="correctionMode"> | ||||
|              <enum>QAbstractSpinBox::CorrectToNearestValue</enum> | ||||
|             </property> | ||||
|             <property name="displayFormat"> | ||||
|              <string>HH:mm</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QLabel" name="label"> | ||||
|             <property name="text"> | ||||
|              <string>UTC</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|          </layout> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item> | ||||
|         <widget class="QFrame" name="frame_3"> | ||||
|          <property name="frameShape"> | ||||
|           <enum>QFrame::StyledPanel</enum> | ||||
|          </property> | ||||
|          <property name="frameShadow"> | ||||
|           <enum>QFrame::Raised</enum> | ||||
|          </property> | ||||
|          <layout class="QGridLayout" name="gridLayout"> | ||||
|           <property name="leftMargin"> | ||||
|            <number>0</number> | ||||
|           </property> | ||||
|           <property name="topMargin"> | ||||
|            <number>0</number> | ||||
|           </property> | ||||
|           <property name="rightMargin"> | ||||
|            <number>0</number> | ||||
|           </property> | ||||
|           <property name="bottomMargin"> | ||||
|            <number>0</number> | ||||
|           </property> | ||||
|           <item row="0" column="1"> | ||||
|            <widget class="QSpinBox" name="loopTimeSpinBox"> | ||||
|             <property name="correctionMode"> | ||||
|              <enum>QAbstractSpinBox::CorrectToNearestValue</enum> | ||||
|             </property> | ||||
|             <property name="suffix"> | ||||
|              <string> min</string> | ||||
|             </property> | ||||
|             <property name="minimum"> | ||||
|              <number>1</number> | ||||
|             </property> | ||||
|             <property name="maximum"> | ||||
|              <number>1440</number> | ||||
|             </property> | ||||
|             <property name="value"> | ||||
|              <number>30</number> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item row="0" column="0"> | ||||
|            <widget class="QLabel" name="loopTimeLabel"> | ||||
|             <property name="text"> | ||||
|              <string>Loop Time</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item row="1" column="0"> | ||||
|            <widget class="QLabel" name="loopSpeedLabel"> | ||||
|             <property name="text"> | ||||
|              <string>Loop Speed</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item row="1" column="1"> | ||||
|            <widget class="QDoubleSpinBox" name="loopSpeedSpinBox"> | ||||
|             <property name="correctionMode"> | ||||
|              <enum>QAbstractSpinBox::CorrectToNearestValue</enum> | ||||
|             </property> | ||||
|             <property name="suffix"> | ||||
|              <string>x</string> | ||||
|             </property> | ||||
|             <property name="minimum"> | ||||
|              <double>1.000000000000000</double> | ||||
|             </property> | ||||
|             <property name="value"> | ||||
|              <double>1.000000000000000</double> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item row="2" column="0"> | ||||
|            <widget class="QLabel" name="loopDelayLabel"> | ||||
|             <property name="text"> | ||||
|              <string>Loop Delay</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item row="2" column="1"> | ||||
|            <widget class="QDoubleSpinBox" name="loopDelaySpinBox"> | ||||
|             <property name="suffix"> | ||||
|              <string> sec</string> | ||||
|             </property> | ||||
|             <property name="decimals"> | ||||
|              <number>1</number> | ||||
|             </property> | ||||
|             <property name="maximum"> | ||||
|              <double>15.000000000000000</double> | ||||
|             </property> | ||||
|             <property name="singleStep"> | ||||
|              <double>0.100000000000000</double> | ||||
|             </property> | ||||
|             <property name="value"> | ||||
|              <double>2.500000000000000</double> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|          </layout> | ||||
|         </widget> | ||||
|        </item> | ||||
|        <item> | ||||
|         <widget class="QFrame" name="frame_2"> | ||||
|          <property name="frameShape"> | ||||
|           <enum>QFrame::StyledPanel</enum> | ||||
|          </property> | ||||
|          <property name="frameShadow"> | ||||
|           <enum>QFrame::Raised</enum> | ||||
|          </property> | ||||
|          <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||||
|           <property name="spacing"> | ||||
|            <number>1</number> | ||||
|           </property> | ||||
|           <property name="leftMargin"> | ||||
|            <number>0</number> | ||||
|           </property> | ||||
|           <property name="topMargin"> | ||||
|            <number>0</number> | ||||
|           </property> | ||||
|           <property name="rightMargin"> | ||||
|            <number>0</number> | ||||
|           </property> | ||||
|           <property name="bottomMargin"> | ||||
|            <number>0</number> | ||||
|           </property> | ||||
|           <item> | ||||
|            <widget class="QToolButton" name="beginButton"> | ||||
|             <property name="text"> | ||||
|              <string>...</string> | ||||
|             </property> | ||||
|             <property name="icon"> | ||||
|              <iconset resource="../../../../scwx-qt.qrc"> | ||||
|               <normaloff>:/res/icons/font-awesome-6/backward-step-solid.svg</normaloff>:/res/icons/font-awesome-6/backward-step-solid.svg</iconset> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QToolButton" name="stepBackButton"> | ||||
|             <property name="text"> | ||||
|              <string>...</string> | ||||
|             </property> | ||||
|             <property name="icon"> | ||||
|              <iconset resource="../../../../scwx-qt.qrc"> | ||||
|               <normaloff>:/res/icons/font-awesome-6/angle-left-solid.svg</normaloff>:/res/icons/font-awesome-6/angle-left-solid.svg</iconset> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QToolButton" name="playButton"> | ||||
|             <property name="text"> | ||||
|              <string>...</string> | ||||
|             </property> | ||||
|             <property name="icon"> | ||||
|              <iconset resource="../../../../scwx-qt.qrc"> | ||||
|               <normaloff>:/res/icons/font-awesome-6/play-solid.svg</normaloff>:/res/icons/font-awesome-6/play-solid.svg</iconset> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QToolButton" name="stepNextButton"> | ||||
|             <property name="text"> | ||||
|              <string>...</string> | ||||
|             </property> | ||||
|             <property name="icon"> | ||||
|              <iconset resource="../../../../scwx-qt.qrc"> | ||||
|               <normaloff>:/res/icons/font-awesome-6/angle-right-solid.svg</normaloff>:/res/icons/font-awesome-6/angle-right-solid.svg</iconset> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item> | ||||
|            <widget class="QToolButton" name="endButton"> | ||||
|             <property name="text"> | ||||
|              <string>...</string> | ||||
|             </property> | ||||
|             <property name="icon"> | ||||
|              <iconset resource="../../../../scwx-qt.qrc"> | ||||
|               <normaloff>:/res/icons/font-awesome-6/forward-step-solid.svg</normaloff>:/res/icons/font-awesome-6/forward-step-solid.svg</iconset> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|          </layout> | ||||
|         </widget> | ||||
|        </item> | ||||
|       </layout> | ||||
|          </spacer> | ||||
|         </item> | ||||
|        </layout> | ||||
|       </widget> | ||||
|      </widget> | ||||
|     </item> | ||||
|     <item> | ||||
|      <spacer name="verticalSpacer"> | ||||
|       <property name="orientation"> | ||||
|        <enum>Qt::Vertical</enum> | ||||
|       </property> | ||||
|       <property name="sizeHint" stdset="0"> | ||||
|        <size> | ||||
|         <width>20</width> | ||||
|         <height>40</height> | ||||
|        </size> | ||||
|       </property> | ||||
|      </spacer> | ||||
|     </item> | ||||
|    </layout> | ||||
|   </widget> | ||||
|  </widget> | ||||
|  |  | |||
|  | @ -176,28 +176,28 @@ bool WmoHeader::Parse(std::istream& is) | |||
| 
 | ||||
|       if (wmoTokenList.size() < 3 || wmoTokenList.size() > 4) | ||||
|       { | ||||
|          logger_->debug("Invalid number of WMO tokens"); | ||||
|          logger_->warn("Invalid number of WMO tokens"); | ||||
|          headerValid = false; | ||||
|       } | ||||
|       else if (wmoTokenList[0].size() != 6) | ||||
|       { | ||||
|          logger_->debug("WMO identifier malformed"); | ||||
|          logger_->warn("WMO identifier malformed"); | ||||
|          headerValid = false; | ||||
|       } | ||||
|       else if (wmoTokenList[1].size() != 4) | ||||
|       { | ||||
|          logger_->debug("ICAO malformed"); | ||||
|          logger_->warn("ICAO malformed"); | ||||
|          headerValid = false; | ||||
|       } | ||||
|       else if (wmoTokenList[2].size() != 6) | ||||
|       { | ||||
|          logger_->debug("Date/time malformed"); | ||||
|          logger_->warn("Date/time malformed"); | ||||
|          headerValid = false; | ||||
|       } | ||||
|       else if (wmoTokenList.size() == 4 && wmoTokenList[3].size() != 3) | ||||
|       { | ||||
|          // BBB indicator is optional
 | ||||
|          logger_->debug("BBB indicator malformed"); | ||||
|          logger_->warn("BBB indicator malformed"); | ||||
|          headerValid = false; | ||||
|       } | ||||
|       else | ||||
|  | @ -226,7 +226,7 @@ bool WmoHeader::Parse(std::istream& is) | |||
|    { | ||||
|       if (awipsLine.size() != 6) | ||||
|       { | ||||
|          logger_->debug("AWIPS Identifier Line bad size"); | ||||
|          logger_->warn("AWIPS Identifier Line bad size"); | ||||
|          headerValid = false; | ||||
|       } | ||||
|       else | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat