Merge pull request #30 from dpaulat/feature/garbage-collect

Feature/garbage collect
This commit is contained in:
Dan Paulat 2023-04-13 05:55:26 -05:00 committed by GitHub
commit a851918ec2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 639 additions and 166 deletions

View file

@ -357,6 +357,11 @@ void MainWindow::on_actionImGuiDebug_triggered()
p->imGuiDebugDialog_->show(); p->imGuiDebugDialog_->show();
} }
void MainWindow::on_actionDumpRadarProductRecords_triggered()
{
manager::RadarProductManager::DumpRecords();
}
void MainWindow::on_actionUserManual_triggered() void MainWindow::on_actionUserManual_triggered()
{ {
QDesktopServices::openUrl(QUrl {"https://supercell-wx.readthedocs.io/"}); QDesktopServices::openUrl(QUrl {"https://supercell-wx.readthedocs.io/"});
@ -392,6 +397,67 @@ void MainWindow::on_resourceTreeExpandAllButton_clicked()
ui->resourceTreeView->expandAll(); ui->resourceTreeView->expandAll();
} }
void MainWindow::on_resourceTreeView_doubleClicked(const QModelIndex& index)
{
std::string selectedString {index.data().toString().toStdString()};
std::chrono::system_clock::time_point time {};
logger_->debug("Selecting resource: {}",
index.data().toString().toStdString());
static const std::string timeFormat {"%Y-%m-%d %H:%M:%S"};
std::istringstream in {selectedString};
in >> std::chrono::parse(timeFormat, time);
if (in.fail())
{
// Not a time string, ignore double-click
return;
}
QModelIndex parent1 = index.parent();
QModelIndex parent2 = parent1.parent();
QModelIndex parent3 = parent2.parent();
std::string radarSite {};
std::string groupName {};
std::string product {};
if (!parent2.isValid())
{
// A time entry should be at the third or fourth level
logger_->error("Unexpected resource data");
return;
}
if (parent3.isValid())
{
// Level 3 Product
radarSite = parent3.data().toString().toStdString();
groupName = parent2.data().toString().toStdString();
product = parent1.data().toString().toStdString();
}
else
{
// Level 2 Product
radarSite = parent2.data().toString().toStdString();
groupName = parent1.data().toString().toStdString();
// No product index
}
common::RadarProductGroup group = common::GetRadarProductGroup(groupName);
// Update radar site if different from currently selected
if (p->activeMap_->GetRadarSite()->id() != radarSite)
{
p->activeMap_->SelectRadarSite(radarSite);
}
// Select the updated radar product
p->activeMap_->SelectRadarProduct(group, product, 0, time);
}
void MainWindowImpl::ConfigureMapLayout() void MainWindowImpl::ConfigureMapLayout()
{ {
auto& generalSettings = manager::SettingsManager::general_settings(); auto& generalSettings = manager::SettingsManager::general_settings();

View file

@ -37,6 +37,7 @@ private slots:
void on_actionSettings_triggered(); void on_actionSettings_triggered();
void on_actionExit_triggered(); void on_actionExit_triggered();
void on_actionImGuiDebug_triggered(); void on_actionImGuiDebug_triggered();
void on_actionDumpRadarProductRecords_triggered();
void on_actionUserManual_triggered(); void on_actionUserManual_triggered();
void on_actionDiscord_triggered(); void on_actionDiscord_triggered();
void on_actionGitHubRepository_triggered(); void on_actionGitHubRepository_triggered();
@ -44,6 +45,7 @@ private slots:
void on_radarSiteSelectButton_clicked(); void on_radarSiteSelectButton_clicked();
void on_resourceTreeCollapseAllButton_clicked(); void on_resourceTreeCollapseAllButton_clicked();
void on_resourceTreeExpandAllButton_clicked(); void on_resourceTreeExpandAllButton_clicked();
void on_resourceTreeView_doubleClicked(const QModelIndex& index);
private: private:
std::unique_ptr<MainWindowImpl> p; std::unique_ptr<MainWindowImpl> p;

View file

@ -83,6 +83,8 @@
<string>&amp;Debug</string> <string>&amp;Debug</string>
</property> </property>
<addaction name="actionImGuiDebug"/> <addaction name="actionImGuiDebug"/>
<addaction name="separator"/>
<addaction name="actionDumpRadarProductRecords"/>
</widget> </widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
<addaction name="menuView"/> <addaction name="menuView"/>
@ -397,6 +399,11 @@
<string>&amp;GitHub Repository</string> <string>&amp;GitHub Repository</string>
</property> </property>
</action> </action>
<action name="actionDumpRadarProductRecords">
<property name="text">
<string>Dump Radar &amp;Product Records</string>
</property>
</action>
</widget> </widget>
<resources> <resources>
<include location="../../../../scwx-qt.qrc"/> <include location="../../../../scwx-qt.qrc"/>

View file

@ -16,6 +16,7 @@
#pragma warning(push, 0) #pragma warning(push, 0)
#include <boost/asio/steady_timer.hpp> #include <boost/asio/steady_timer.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/range/irange.hpp> #include <boost/range/irange.hpp>
#include <boost/timer/timer.hpp> #include <boost/timer/timer.hpp>
#include <fmt/chrono.h> #include <fmt/chrono.h>
@ -36,8 +37,10 @@ static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
typedef std::function<std::shared_ptr<wsr88d::NexradFile>()> typedef std::function<std::shared_ptr<wsr88d::NexradFile>()>
CreateNexradFileFunction; CreateNexradFileFunction;
typedef std::map<std::chrono::system_clock::time_point, typedef std::map<std::chrono::system_clock::time_point,
std::shared_ptr<types::RadarProductRecord>> std::weak_ptr<types::RadarProductRecord>>
RadarProductRecordMap; RadarProductRecordMap;
typedef std::list<std::shared_ptr<types::RadarProductRecord>>
RadarProductRecordList;
static constexpr uint32_t NUM_RADIAL_GATES_0_5_DEGREE = static constexpr uint32_t NUM_RADIAL_GATES_0_5_DEGREE =
common::MAX_0_5_DEGREE_RADIALS * common::MAX_DATA_MOMENT_GATES; common::MAX_0_5_DEGREE_RADIALS * common::MAX_DATA_MOMENT_GATES;
@ -53,8 +56,8 @@ static const std::string kDefaultLevel3Product_ {"N0B"};
static constexpr std::chrono::seconds kRetryInterval_ {15}; static constexpr std::chrono::seconds kRetryInterval_ {15};
static std::unordered_map<std::string, std::weak_ptr<RadarProductManager>> static std::unordered_map<std::string, std::weak_ptr<RadarProductManager>>
instanceMap_; instanceMap_;
static std::mutex instanceMutex_; static std::shared_mutex instanceMutex_;
static std::unordered_map<std::string, static std::unordered_map<std::string,
std::shared_ptr<types::RadarProductRecord>> std::shared_ptr<types::RadarProductRecord>>
@ -123,7 +126,9 @@ public:
coordinates0_5Degree_ {}, coordinates0_5Degree_ {},
coordinates1Degree_ {}, coordinates1Degree_ {},
level2ProductRecords_ {}, level2ProductRecords_ {},
level2ProductRecentRecords_ {},
level3ProductRecordsMap_ {}, level3ProductRecordsMap_ {},
level3ProductRecentRecordsMap_ {},
level2ProductRecordMutex_ {}, level2ProductRecordMutex_ {},
level3ProductRecordMutex_ {}, level3ProductRecordMutex_ {},
level2ProviderManager_ {std::make_shared<ProviderManager>( level2ProviderManager_ {std::make_shared<ProviderManager>(
@ -159,6 +164,10 @@ public:
auto& [key, providerManager] = p; auto& [key, providerManager] = p;
providerManager->Disable(); providerManager->Disable();
}); });
// Lock other mutexes before destroying, ensure loading is complete
std::unique_lock loadLevel2DataLock {loadLevel2DataMutex_};
std::unique_lock loadLevel3DataLock {loadLevel3DataMutex_};
} }
RadarProductManager* self_; RadarProductManager* self_;
@ -166,17 +175,22 @@ public:
std::shared_ptr<ProviderManager> std::shared_ptr<ProviderManager>
GetLevel3ProviderManager(const std::string& product); GetLevel3ProviderManager(const std::string& product);
void EnableRefresh(std::shared_ptr<ProviderManager> providerManager, void EnableRefresh(boost::uuids::uuid uuid,
std::shared_ptr<ProviderManager> providerManager,
bool enabled); bool enabled);
void RefreshData(std::shared_ptr<ProviderManager> providerManager); void RefreshData(std::shared_ptr<ProviderManager> providerManager);
std::shared_ptr<types::RadarProductRecord> std::tuple<std::shared_ptr<types::RadarProductRecord>,
std::chrono::system_clock::time_point>
GetLevel2ProductRecord(std::chrono::system_clock::time_point time); GetLevel2ProductRecord(std::chrono::system_clock::time_point time);
std::shared_ptr<types::RadarProductRecord> std::tuple<std::shared_ptr<types::RadarProductRecord>,
std::chrono::system_clock::time_point>
GetLevel3ProductRecord(const std::string& product, GetLevel3ProductRecord(const std::string& product,
std::chrono::system_clock::time_point time); std::chrono::system_clock::time_point time);
std::shared_ptr<types::RadarProductRecord> std::shared_ptr<types::RadarProductRecord>
StoreRadarProductRecord(std::shared_ptr<types::RadarProductRecord> record); StoreRadarProductRecord(std::shared_ptr<types::RadarProductRecord> record);
void UpdateRecentRecords(RadarProductRecordList& recentList,
std::shared_ptr<types::RadarProductRecord> record);
void LoadProviderData(std::chrono::system_clock::time_point time, void LoadProviderData(std::chrono::system_clock::time_point time,
std::shared_ptr<ProviderManager> providerManager, std::shared_ptr<ProviderManager> providerManager,
@ -199,10 +213,12 @@ public:
std::vector<float> coordinates0_5Degree_; std::vector<float> coordinates0_5Degree_;
std::vector<float> coordinates1Degree_; std::vector<float> coordinates1Degree_;
RadarProductRecordMap level2ProductRecords_; RadarProductRecordMap level2ProductRecords_;
RadarProductRecordList level2ProductRecentRecords_;
std::unordered_map<std::string, RadarProductRecordMap> std::unordered_map<std::string, RadarProductRecordMap>
level3ProductRecordsMap_; level3ProductRecordsMap_;
std::unordered_map<std::string, RadarProductRecordList>
level3ProductRecentRecordsMap_;
std::shared_mutex level2ProductRecordMutex_; std::shared_mutex level2ProductRecordMutex_;
std::shared_mutex level3ProductRecordMutex_; std::shared_mutex level3ProductRecordMutex_;
@ -218,6 +234,12 @@ public:
common::Level3ProductCategoryMap availableCategoryMap_; common::Level3ProductCategoryMap availableCategoryMap_;
std::shared_mutex availableCategoryMutex_; std::shared_mutex availableCategoryMutex_;
std::unordered_map<boost::uuids::uuid,
std::shared_ptr<ProviderManager>,
boost::hash<boost::uuids::uuid>>
refreshMap_ {};
std::mutex refreshMapMutex_ {};
}; };
RadarProductManager::RadarProductManager(const std::string& radarId) : RadarProductManager::RadarProductManager(const std::string& radarId) :
@ -248,6 +270,8 @@ std::string ProviderManager::name() const
void ProviderManager::Disable() void ProviderManager::Disable()
{ {
logger_->debug("Disabling refresh: {}", name());
std::unique_lock lock(refreshTimerMutex_); std::unique_lock lock(refreshTimerMutex_);
refreshEnabled_ = false; refreshEnabled_ = false;
refreshTimer_.cancel(); refreshTimer_.cancel();
@ -266,6 +290,61 @@ void RadarProductManager::Cleanup()
} }
} }
void RadarProductManager::DumpRecords()
{
scwx::util::async(
[]
{
logger_->info("Record Dump");
std::shared_lock instanceLock {instanceMutex_};
for (auto& instance : instanceMap_)
{
auto radarProductManager = instance.second.lock();
if (radarProductManager != nullptr)
{
logger_->info(" {}", radarProductManager->radar_site()->id());
logger_->info(" Level 2");
{
std::shared_lock level2ProductLock {
radarProductManager->p->level2ProductRecordMutex_};
for (auto& record :
radarProductManager->p->level2ProductRecords_)
{
logger_->info(" {}{}",
scwx::util::TimeString(record.first),
record.second.expired() ? " (expired)" : "");
}
}
logger_->info(" Level 3");
{
std::shared_lock level3ProductLock {
radarProductManager->p->level3ProductRecordMutex_};
for (auto& recordMap :
radarProductManager->p->level3ProductRecordsMap_)
{
// Product Name
logger_->info(" {}", recordMap.first);
for (auto& record : recordMap.second)
{
logger_->info(" {}{}",
scwx::util::TimeString(record.first),
record.second.expired() ? " (expired)" :
"");
}
}
}
}
}
});
}
const std::vector<float>& const std::vector<float>&
RadarProductManager::coordinates(common::RadialSize radialSize) const RadarProductManager::coordinates(common::RadialSize radialSize) const
{ {
@ -413,11 +492,12 @@ RadarProductManagerImpl::GetLevel3ProviderManager(const std::string& product)
void RadarProductManager::EnableRefresh(common::RadarProductGroup group, void RadarProductManager::EnableRefresh(common::RadarProductGroup group,
const std::string& product, const std::string& product,
bool enabled) bool enabled,
boost::uuids::uuid uuid)
{ {
if (group == common::RadarProductGroup::Level2) if (group == common::RadarProductGroup::Level2)
{ {
p->EnableRefresh(p->level2ProviderManager_, enabled); p->EnableRefresh(uuid, p->level2ProviderManager_, enabled);
} }
else else
{ {
@ -437,16 +517,65 @@ void RadarProductManager::EnableRefresh(common::RadarProductGroup group,
availableProducts.cend(), availableProducts.cend(),
product) != availableProducts.cend()) product) != availableProducts.cend())
{ {
p->EnableRefresh(providerManager, enabled); p->EnableRefresh(uuid, providerManager, enabled);
} }
}); });
} }
} }
void RadarProductManagerImpl::EnableRefresh( void RadarProductManagerImpl::EnableRefresh(
std::shared_ptr<ProviderManager> providerManager, bool enabled) boost::uuids::uuid uuid,
std::shared_ptr<ProviderManager> providerManager,
bool enabled)
{ {
if (providerManager->refreshEnabled_ != enabled) // Lock the refresh map
std::unique_lock lock {refreshMapMutex_};
auto currentProviderManager = refreshMap_.find(uuid);
if (currentProviderManager != refreshMap_.cend())
{
// If the enabling refresh for a different product, or disabling refresh
if (currentProviderManager->second != providerManager || !enabled)
{
// Determine number of entries in the map for the current provider
// manager
auto currentProviderManagerCount = std::count_if(
refreshMap_.cbegin(),
refreshMap_.cend(),
[&](const auto& provider)
{ return provider.second == currentProviderManager->second; });
// If this is the last reference to the provider in the refresh map
if (currentProviderManagerCount == 1)
{
// Disable current provider
currentProviderManager->second->Disable();
}
// Dissociate uuid from current provider manager
refreshMap_.erase(currentProviderManager);
// If we are enabling a new provider manager
if (enabled)
{
// Associate uuid to providerManager
refreshMap_.emplace(uuid, providerManager);
}
}
}
else if (enabled)
{
// We are enabling a new provider manager
// Associate uuid to provider manager
refreshMap_.emplace(uuid, providerManager);
}
// Release the refresh map mutex
lock.unlock();
// We have already handled a disable request by this point. If enabling, and
// the provider manager refresh isn't already enabled, enable it.
if (enabled && providerManager->refreshEnabled_ != enabled)
{ {
providerManager->refreshEnabled_ = enabled; providerManager->refreshEnabled_ = enabled;
@ -475,7 +604,7 @@ void RadarProductManagerImpl::RefreshData(
std::chrono::milliseconds interval = kRetryInterval_; std::chrono::milliseconds interval = kRetryInterval_;
if (newObjects > 0) if (totalObjects > 0)
{ {
std::string key = providerManager->provider_->FindLatestKey(); std::string key = providerManager->provider_->FindLatestKey();
auto latestTime = auto latestTime =
@ -491,10 +620,14 @@ void RadarProductManagerImpl::RefreshData(
interval = kRetryInterval_; interval = kRetryInterval_;
} }
emit providerManager->NewDataAvailable( if (newObjects > 0)
providerManager->group_, providerManager->product_, latestTime); {
emit providerManager->NewDataAvailable(providerManager->group_,
providerManager->product_,
latestTime);
}
} }
else if (providerManager->refreshEnabled_ && totalObjects == 0) else if (providerManager->refreshEnabled_)
{ {
logger_->info("[{}] No data found, disabling refresh", logger_->info("[{}] No data found, disabling refresh",
providerManager->name()); providerManager->name());
@ -562,10 +695,13 @@ void RadarProductManagerImpl::LoadProviderData(
auto it = recordMap.find(time); auto it = recordMap.find(time);
if (it != recordMap.cend()) if (it != recordMap.cend())
{ {
logger_->debug( existingRecord = it->second.lock();
"Data previously loaded, loading from data cache");
existingRecord = it->second; if (existingRecord != nullptr)
{
logger_->debug(
"Data previously loaded, loading from data cache");
}
} }
} }
@ -727,38 +863,70 @@ void RadarProductManagerImpl::LoadNexradFile(
}); });
} }
std::shared_ptr<types::RadarProductRecord> std::tuple<std::shared_ptr<types::RadarProductRecord>,
std::chrono::system_clock::time_point>
RadarProductManagerImpl::GetLevel2ProductRecord( RadarProductManagerImpl::GetLevel2ProductRecord(
std::chrono::system_clock::time_point time) std::chrono::system_clock::time_point time)
{ {
std::shared_ptr<types::RadarProductRecord> record; std::shared_ptr<types::RadarProductRecord> record {nullptr};
RadarProductRecordMap::const_pointer recordPtr {nullptr};
std::chrono::system_clock::time_point recordTime {time};
if (!level2ProductRecords_.empty() && if (!level2ProductRecords_.empty() &&
time == std::chrono::system_clock::time_point {}) time == std::chrono::system_clock::time_point {})
{ {
// If a default-initialized time point is given, return the latest record // If a default-initialized time point is given, return the latest record
record = level2ProductRecords_.rbegin()->second; recordPtr = &(*level2ProductRecords_.rbegin());
} }
else else
{ {
// TODO: Round to minutes recordPtr =
record = scwx::util::GetBoundedElementValue(level2ProductRecords_, time); scwx::util::GetBoundedElementPointer(level2ProductRecords_, time);
}
// Does the record contain the time we are looking for? if (recordPtr != nullptr)
if (record != nullptr && (time < record->level2_file()->start_time())) {
if (time == std::chrono::system_clock::time_point {} ||
time == recordPtr->first)
{ {
record = nullptr; recordTime = recordPtr->first;
record = recordPtr->second.lock();
} }
} }
return record; if (record == nullptr &&
recordTime != std::chrono::system_clock::time_point {})
{
// Product is expired, reload it
std::shared_ptr<request::NexradFileRequest> request =
std::make_shared<request::NexradFileRequest>();
QObject::connect(
request.get(),
&request::NexradFileRequest::RequestComplete,
self_,
[this](std::shared_ptr<request::NexradFileRequest> request)
{
if (request->radar_product_record() != nullptr)
{
emit self_->DataReloaded(request->radar_product_record());
}
});
self_->LoadLevel2Data(recordTime, request);
}
return {record, recordTime};
} }
std::shared_ptr<types::RadarProductRecord> std::tuple<std::shared_ptr<types::RadarProductRecord>,
std::chrono::system_clock::time_point>
RadarProductManagerImpl::GetLevel3ProductRecord( RadarProductManagerImpl::GetLevel3ProductRecord(
const std::string& product, std::chrono::system_clock::time_point time) const std::string& product, std::chrono::system_clock::time_point time)
{ {
std::shared_ptr<types::RadarProductRecord> record = nullptr; std::shared_ptr<types::RadarProductRecord> record {nullptr};
RadarProductRecordMap::const_pointer recordPtr {nullptr};
std::chrono::system_clock::time_point recordTime {time};
std::unique_lock lock {level3ProductRecordMutex_}; std::unique_lock lock {level3ProductRecordMutex_};
@ -770,15 +938,50 @@ RadarProductManagerImpl::GetLevel3ProductRecord(
{ {
// If a default-initialized time point is given, return the latest // If a default-initialized time point is given, return the latest
// record // record
record = it->second.rbegin()->second; recordPtr = &(*it->second.rbegin());
} }
else else
{ {
record = scwx::util::GetBoundedElementValue(it->second, time); recordPtr = scwx::util::GetBoundedElementPointer(it->second, time);
} }
} }
return record; // Lock is no longer needed
lock.unlock();
if (recordPtr != nullptr)
{
if (time == std::chrono::system_clock::time_point {} ||
time == recordPtr->first)
{
recordTime = recordPtr->first;
record = recordPtr->second.lock();
}
}
if (record == nullptr &&
recordTime != std::chrono::system_clock::time_point {})
{
// Product is expired, reload it
std::shared_ptr<request::NexradFileRequest> request =
std::make_shared<request::NexradFileRequest>();
QObject::connect(
request.get(),
&request::NexradFileRequest::RequestComplete,
self_,
[this](std::shared_ptr<request::NexradFileRequest> request)
{
if (request->radar_product_record() != nullptr)
{
emit self_->DataReloaded(request->radar_product_record());
}
});
self_->LoadLevel3Data(product, recordTime, request);
}
return {record, recordTime};
} }
std::shared_ptr<types::RadarProductRecord> std::shared_ptr<types::RadarProductRecord>
@ -787,7 +990,7 @@ RadarProductManagerImpl::StoreRadarProductRecord(
{ {
logger_->debug("StoreRadarProductRecord()"); logger_->debug("StoreRadarProductRecord()");
std::shared_ptr<types::RadarProductRecord> storedRecord = record; std::shared_ptr<types::RadarProductRecord> storedRecord = nullptr;
auto timeInSeconds = auto timeInSeconds =
std::chrono::time_point_cast<std::chrono::seconds, std::chrono::time_point_cast<std::chrono::seconds,
@ -800,15 +1003,22 @@ RadarProductManagerImpl::StoreRadarProductRecord(
auto it = level2ProductRecords_.find(timeInSeconds); auto it = level2ProductRecords_.find(timeInSeconds);
if (it != level2ProductRecords_.cend()) if (it != level2ProductRecords_.cend())
{ {
logger_->debug( storedRecord = it->second.lock();
"Level 2 product previously loaded, loading from cache");
storedRecord = it->second; if (storedRecord != nullptr)
{
logger_->debug(
"Level 2 product previously loaded, loading from cache");
}
} }
else
if (storedRecord == nullptr)
{ {
storedRecord = record;
level2ProductRecords_[timeInSeconds] = record; level2ProductRecords_[timeInSeconds] = record;
} }
UpdateRecentRecords(level2ProductRecentRecords_, storedRecord);
} }
else if (record->radar_product_group() == common::RadarProductGroup::Level3) else if (record->radar_product_group() == common::RadarProductGroup::Level3)
{ {
@ -819,23 +1029,58 @@ RadarProductManagerImpl::StoreRadarProductRecord(
auto it = productMap.find(timeInSeconds); auto it = productMap.find(timeInSeconds);
if (it != productMap.cend()) if (it != productMap.cend())
{ {
logger_->debug( storedRecord = it->second.lock();
"Level 3 product previously loaded, loading from cache");
storedRecord = it->second; if (storedRecord != nullptr)
{
logger_->debug(
"Level 3 product previously loaded, loading from cache");
}
} }
else
if (storedRecord == nullptr)
{ {
storedRecord = record;
productMap[timeInSeconds] = record; productMap[timeInSeconds] = record;
} }
UpdateRecentRecords(
level3ProductRecentRecordsMap_[record->radar_product()], storedRecord);
} }
return storedRecord; return storedRecord;
} }
void RadarProductManagerImpl::UpdateRecentRecords(
RadarProductRecordList& recentList,
std::shared_ptr<types::RadarProductRecord> record)
{
static constexpr std::size_t kRecentListMaxSize_ {2u};
auto it = std::find(recentList.cbegin(), recentList.cend(), record);
if (it != recentList.cbegin() && it != recentList.cend())
{
// If the record exists beyond the front of the list, remove it
recentList.erase(it);
}
if (recentList.size() == 0 || it != recentList.cbegin())
{
// Add the record to the front of the list, unless it's already there
recentList.push_front(record);
}
while (recentList.size() > kRecentListMaxSize_)
{
// Remove from the end of the list while it's too big
recentList.pop_back();
}
}
std::tuple<std::shared_ptr<wsr88d::rda::ElevationScan>, std::tuple<std::shared_ptr<wsr88d::rda::ElevationScan>,
float, float,
std::vector<float>> std::vector<float>,
std::chrono::system_clock::time_point>
RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType, RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType,
float elevation, float elevation,
std::chrono::system_clock::time_point time) std::chrono::system_clock::time_point time)
@ -844,8 +1089,8 @@ RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType,
float elevationCut = 0.0f; float elevationCut = 0.0f;
std::vector<float> elevationCuts; std::vector<float> elevationCuts;
std::shared_ptr<types::RadarProductRecord> record = std::shared_ptr<types::RadarProductRecord> record;
p->GetLevel2ProductRecord(time); std::tie(record, time) = p->GetLevel2ProductRecord(time);
if (record != nullptr) if (record != nullptr)
{ {
@ -854,24 +1099,25 @@ RadarProductManager::GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType,
dataBlockType, elevation, time); dataBlockType, elevation, time);
} }
return std::tie(radarData, elevationCut, elevationCuts); return {radarData, elevationCut, elevationCuts, time};
} }
std::shared_ptr<wsr88d::rpg::Level3Message> std::tuple<std::shared_ptr<wsr88d::rpg::Level3Message>,
std::chrono::system_clock::time_point>
RadarProductManager::GetLevel3Data(const std::string& product, RadarProductManager::GetLevel3Data(const std::string& product,
std::chrono::system_clock::time_point time) std::chrono::system_clock::time_point time)
{ {
std::shared_ptr<wsr88d::rpg::Level3Message> message = nullptr; std::shared_ptr<wsr88d::rpg::Level3Message> message = nullptr;
std::shared_ptr<types::RadarProductRecord> record = std::shared_ptr<types::RadarProductRecord> record;
p->GetLevel3ProductRecord(product, time); std::tie(record, time) = p->GetLevel3ProductRecord(product, time);
if (record != nullptr) if (record != nullptr)
{ {
message = record->level3_file()->message(); message = record->level3_file()->message();
} }
return message; return {message, time};
} }
common::Level3ProductCategoryMap common::Level3ProductCategoryMap
@ -970,7 +1216,7 @@ RadarProductManager::Instance(const std::string& radarSite)
bool instanceCreated = false; bool instanceCreated = false;
{ {
std::lock_guard<std::mutex> guard(instanceMutex_); std::unique_lock lock {instanceMutex_};
// Look up instance weak pointer // Look up instance weak pointer
auto it = instanceMap_.find(radarSite); auto it = instanceMap_.find(radarSite);

View file

@ -12,6 +12,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <boost/uuid/nil_generator.hpp>
#include <QObject> #include <QObject>
namespace scwx namespace scwx
@ -33,23 +34,63 @@ public:
static void Cleanup(); static void Cleanup();
/**
* @brief Debug function to dump currently loaded products to the log.
*/
static void DumpRecords();
const std::vector<float>& coordinates(common::RadialSize radialSize) const; const std::vector<float>& coordinates(common::RadialSize radialSize) const;
float gate_size() const; float gate_size() const;
std::shared_ptr<config::RadarSite> radar_site() const; std::shared_ptr<config::RadarSite> radar_site() const;
void Initialize(); void Initialize();
/**
* @brief Enables or disables refresh associated with a unique identifier
* (UUID) for a given radar product group and product.
*
* Only a single product refresh can be enabled for a given UUID. If a second
* product refresh is enabled for the same UUID, the first product refresh is
* disabled (unless still enabled under a different UUID).
*
* @param [in] group Radar product group
* @param [in] product Radar product name
* @param [in] enabled Whether to enable refresh
* @param [in] uuid Unique identifier. Default is boost::uuids::nil_uuid().
*/
void EnableRefresh(common::RadarProductGroup group, void EnableRefresh(common::RadarProductGroup group,
const std::string& product, const std::string& product,
bool enabled); bool enabled,
boost::uuids::uuid uuid = boost::uuids::nil_uuid());
/**
* @brief Get level 2 radar data for a data block type, elevation, and time.
*
* @param [in] dataBlockType Data block type
* @param [in] elevation Elevation tilt
* @param [in] time Radar product time
*
* @return Level 2 radar data, selected elevation cut, available elevation
* cuts and selected time
*/
std::tuple<std::shared_ptr<wsr88d::rda::ElevationScan>, std::tuple<std::shared_ptr<wsr88d::rda::ElevationScan>,
float, float,
std::vector<float>> std::vector<float>,
std::chrono::system_clock::time_point>
GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType, GetLevel2Data(wsr88d::rda::DataBlockType dataBlockType,
float elevation, float elevation,
std::chrono::system_clock::time_point time = {}); std::chrono::system_clock::time_point time = {});
std::shared_ptr<wsr88d::rpg::Level3Message> /**
* @brief Get level 3 message data for a product and time.
*
* @param [in] product Radar product name
* @param [in] time Radar product time
*
* @return Level 3 message data and selected time
*/
std::tuple<std::shared_ptr<wsr88d::rpg::Level3Message>,
std::chrono::system_clock::time_point>
GetLevel3Data(const std::string& product, GetLevel3Data(const std::string& product,
std::chrono::system_clock::time_point time = {}); std::chrono::system_clock::time_point time = {});
@ -76,6 +117,7 @@ public:
void UpdateAvailableProducts(); void UpdateAvailableProducts();
signals: signals:
void DataReloaded(std::shared_ptr<types::RadarProductRecord> record);
void Level3ProductsChanged(); void Level3ProductsChanged();
void NewDataAvailable(common::RadarProductGroup group, void NewDataAvailable(common::RadarProductGroup group,
const std::string& product, const std::string& product,

View file

@ -17,6 +17,7 @@
#include <backends/imgui_impl_opengl3.h> #include <backends/imgui_impl_opengl3.h>
#include <backends/imgui_impl_qt.hpp> #include <backends/imgui_impl_qt.hpp>
#include <boost/uuid/random_generator.hpp>
#include <imgui.h> #include <imgui.h>
#include <QApplication> #include <QApplication>
#include <QColor> #include <QColor>
@ -58,6 +59,7 @@ class MapWidgetImpl : public QObject
public: public:
explicit MapWidgetImpl(MapWidget* widget, explicit MapWidgetImpl(MapWidget* widget,
const QMapLibreGL::Settings& settings) : const QMapLibreGL::Settings& settings) :
uuid_ {boost::uuids::random_generator()()},
context_ {std::make_shared<MapContext>()}, context_ {std::make_shared<MapContext>()},
widget_ {widget}, widget_ {widget},
settings_(settings), settings_(settings),
@ -126,6 +128,8 @@ public:
common::Level2Product common::Level2Product
GetLevel2ProductOrDefault(const std::string& productName) const; GetLevel2ProductOrDefault(const std::string& productName) const;
boost::uuids::uuid uuid_;
std::shared_ptr<MapContext> context_; std::shared_ptr<MapContext> context_;
MapWidget* widget_; MapWidget* widget_;
@ -303,7 +307,7 @@ std::shared_ptr<config::RadarSite> MapWidget::GetRadarSite() const
return radarSite; return radarSite;
} }
uint16_t MapWidget::GetVcp() const std::uint16_t MapWidget::GetVcp() const
{ {
auto radarProductView = p->context_->radar_product_view(); auto radarProductView = p->context_->radar_product_view();
@ -313,7 +317,7 @@ uint16_t MapWidget::GetVcp() const
} }
else else
{ {
return 0; return 0u;
} }
} }
@ -330,7 +334,8 @@ void MapWidget::SelectElevation(float elevation)
void MapWidget::SelectRadarProduct(common::RadarProductGroup group, void MapWidget::SelectRadarProduct(common::RadarProductGroup group,
const std::string& product, const std::string& product,
int16_t productCode) std::int16_t productCode,
std::chrono::system_clock::time_point time)
{ {
bool radarProductViewCreated = false; bool radarProductViewCreated = false;
@ -380,8 +385,8 @@ void MapWidget::SelectRadarProduct(common::RadarProductGroup group,
if (radarProductView != nullptr) if (radarProductView != nullptr)
{ {
// Always select the latest product available // Select the time associated with the request
radarProductView->SelectTime({}); radarProductView->SelectTime(time);
if (radarProductViewCreated) if (radarProductViewCreated)
{ {
@ -399,7 +404,8 @@ void MapWidget::SelectRadarProduct(common::RadarProductGroup group,
if (p->autoRefreshEnabled_) if (p->autoRefreshEnabled_)
{ {
p->radarProductManager_->EnableRefresh(group, productName, true); p->radarProductManager_->EnableRefresh(
group, productName, true, p->uuid_);
} }
} }
@ -485,7 +491,8 @@ void MapWidget::SetAutoRefresh(bool enabled)
p->radarProductManager_->EnableRefresh( p->radarProductManager_->EnableRefresh(
radarProductView->GetRadarProductGroup(), radarProductView->GetRadarProductGroup(),
radarProductView->GetRadarProductName(), radarProductView->GetRadarProductName(),
true); true,
p->uuid_);
} }
} }
} }

View file

@ -41,12 +41,23 @@ public:
common::RadarProductGroup GetRadarProductGroup() const; common::RadarProductGroup GetRadarProductGroup() const;
std::string GetRadarProductName() const; std::string GetRadarProductName() const;
std::shared_ptr<config::RadarSite> GetRadarSite() const; std::shared_ptr<config::RadarSite> GetRadarSite() const;
uint16_t GetVcp() const; std::uint16_t GetVcp() const;
void SelectElevation(float elevation); void SelectElevation(float elevation);
/**
* @brief Selects a radar product.
*
* @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.
*/
void SelectRadarProduct(common::RadarProductGroup group, void SelectRadarProduct(common::RadarProductGroup group,
const std::string& product, const std::string& product,
int16_t productCode); std::int16_t productCode = 0,
std::chrono::system_clock::time_point time = {});
void SelectRadarProduct(std::shared_ptr<types::RadarProductRecord> record); void SelectRadarProduct(std::shared_ptr<types::RadarProductRecord> record);
/** /**

View file

@ -105,10 +105,16 @@ RadarProductModelImpl::RadarProductModelImpl(RadarProductModel* self) :
} }
} }
// Create leaf item for product time // Find existing time item (e.g., 2023-04-10 10:11:12)
model_->AppendRow(productItem, const QString timeString =
new TreeItem {QString::fromStdString( QString::fromStdString(util::TimeString(latestTime));
util::TimeString(latestTime))}); TreeItem* timeItem = productItem->FindChild(0, timeString);
if (timeItem == nullptr)
{
// Create leaf item for product time
model_->AppendRow(productItem, new TreeItem {timeString});
}
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
}); });

View file

@ -119,8 +119,7 @@ void Level2ProductsWidgetImpl::UpdateProductSelection(
{ {
const std::string& productName = common::GetLevel2Name(product); const std::string& productName = common::GetLevel2Name(product);
std::for_each(std::execution::par_unseq, std::for_each(productButtons_.cbegin(),
productButtons_.cbegin(),
productButtons_.cend(), productButtons_.cend(),
[&](auto& toolButton) [&](auto& toolButton)
{ {

View file

@ -51,7 +51,6 @@ public:
void NormalizeElevationButtons(); void NormalizeElevationButtons();
void SelectElevation(float elevation); void SelectElevation(float elevation);
void UpdateSettings();
Level2SettingsWidget* self_; Level2SettingsWidget* self_;
QLayout* layout_; QLayout* layout_;
@ -135,8 +134,7 @@ void Level2SettingsWidget::UpdateElevationSelection(float elevation)
QString buttonText {QString::number(elevation, 'f', 1) + QString buttonText {QString::number(elevation, 'f', 1) +
common::Characters::DEGREE}; common::Characters::DEGREE};
std::for_each(std::execution::par_unseq, std::for_each(p->elevationButtons_.cbegin(),
p->elevationButtons_.cbegin(),
p->elevationButtons_.cend(), p->elevationButtons_.cend(),
[&](auto& toolButton) [&](auto& toolButton)
{ {

View file

@ -258,8 +258,7 @@ void Level3ProductsWidgetImpl::UpdateCategorySelection(
{ {
const std::string& categoryName = common::GetLevel3CategoryName(category); const std::string& categoryName = common::GetLevel3CategoryName(category);
std::for_each(std::execution::par_unseq, std::for_each(categoryButtons_.cbegin(),
categoryButtons_.cbegin(),
categoryButtons_.cend(), categoryButtons_.cend(),
[&](auto& toolButton) [&](auto& toolButton)
{ {

View file

@ -44,7 +44,6 @@ public:
explicit Level2ProductViewImpl(common::Level2Product product) : explicit Level2ProductViewImpl(common::Level2Product product) :
product_ {product}, product_ {product},
selectedElevation_ {0.0f}, selectedElevation_ {0.0f},
selectedTime_ {},
elevationScan_ {nullptr}, elevationScan_ {nullptr},
momentDataBlock0_ {nullptr}, momentDataBlock0_ {nullptr},
latitude_ {}, latitude_ {},
@ -72,8 +71,7 @@ public:
common::Level2Product product_; common::Level2Product product_;
wsr88d::rda::DataBlockType dataBlockType_; wsr88d::rda::DataBlockType dataBlockType_;
float selectedElevation_; float selectedElevation_;
std::chrono::system_clock::time_point selectedTime_;
std::shared_ptr<wsr88d::rda::ElevationScan> elevationScan_; std::shared_ptr<wsr88d::rda::ElevationScan> elevationScan_;
std::shared_ptr<wsr88d::rda::MomentDataBlock> momentDataBlock0_; std::shared_ptr<wsr88d::rda::MomentDataBlock> momentDataBlock0_;
@ -108,9 +106,36 @@ Level2ProductView::Level2ProductView(
RadarProductView(radarProductManager), RadarProductView(radarProductManager),
p(std::make_unique<Level2ProductViewImpl>(product)) p(std::make_unique<Level2ProductViewImpl>(product))
{ {
ConnectRadarProductManager();
} }
Level2ProductView::~Level2ProductView() = default; Level2ProductView::~Level2ProductView() = default;
void Level2ProductView::ConnectRadarProductManager()
{
connect(radar_product_manager().get(),
&manager::RadarProductManager::DataReloaded,
this,
[this](std::shared_ptr<types::RadarProductRecord> record)
{
if (record->radar_product_group() ==
common::RadarProductGroup::Level2 &&
record->time() == selected_time())
{
// If the data associated with the currently selected time is
// reloaded, update the view
Update();
}
});
}
void Level2ProductView::DisconnectRadarProductManager()
{
disconnect(radar_product_manager().get(),
&manager::RadarProductManager::DataReloaded,
this,
nullptr);
}
const std::vector<boost::gil::rgba8_pixel_t>& const std::vector<boost::gil::rgba8_pixel_t>&
Level2ProductView::color_table() const Level2ProductView::color_table() const
{ {
@ -243,11 +268,6 @@ void Level2ProductView::SelectProduct(const std::string& productName)
p->SetProduct(productName); p->SetProduct(productName);
} }
void Level2ProductView::SelectTime(std::chrono::system_clock::time_point time)
{
p->selectedTime_ = time;
}
void Level2ProductViewImpl::SetProduct(const std::string& productName) void Level2ProductViewImpl::SetProduct(const std::string& productName)
{ {
SetProduct(common::GetLevel2Product(productName)); SetProduct(common::GetLevel2Product(productName));
@ -376,9 +396,18 @@ void Level2ProductView::ComputeSweep()
radar_product_manager(); radar_product_manager();
std::shared_ptr<wsr88d::rda::ElevationScan> radarData; std::shared_ptr<wsr88d::rda::ElevationScan> radarData;
std::tie(radarData, p->elevationCut_, p->elevationCuts_) = std::chrono::system_clock::time_point requestedTime {selected_time()};
std::chrono::system_clock::time_point foundTime;
std::tie(radarData, p->elevationCut_, p->elevationCuts_, foundTime) =
radarProductManager->GetLevel2Data( radarProductManager->GetLevel2Data(
p->dataBlockType_, p->selectedElevation_, p->selectedTime_); p->dataBlockType_, p->selectedElevation_, requestedTime);
// If a different time was found than what was requested, update it
if (requestedTime != foundTime)
{
SelectTime(foundTime);
}
if (radarData == nullptr || radarData == p->elevationScan_) if (radarData == nullptr || radarData == p->elevationScan_)
{ {
return; return;

View file

@ -28,31 +28,34 @@ public:
~Level2ProductView(); ~Level2ProductView();
const std::vector<boost::gil::rgba8_pixel_t>& color_table() const override; const std::vector<boost::gil::rgba8_pixel_t>& color_table() const override;
uint16_t color_table_min() const override; std::uint16_t color_table_min() const override;
uint16_t color_table_max() const override; std::uint16_t color_table_max() const override;
float elevation() const override; float elevation() const override;
float range() const override; float range() const override;
std::chrono::system_clock::time_point sweep_time() const override; std::chrono::system_clock::time_point sweep_time() const override;
uint16_t vcp() const override; std::uint16_t vcp() const override;
const std::vector<float>& vertices() const override; const std::vector<float>& vertices() const override;
void LoadColorTable(std::shared_ptr<common::ColorTable> colorTable) override; void LoadColorTable(std::shared_ptr<common::ColorTable> colorTable) override;
void SelectElevation(float elevation) override; void SelectElevation(float elevation) override;
void SelectProduct(const std::string& productName) override; void SelectProduct(const std::string& productName) override;
void SelectTime(std::chrono::system_clock::time_point time) override;
void Update() override; void Update() override;
common::RadarProductGroup GetRadarProductGroup() const override; common::RadarProductGroup GetRadarProductGroup() const override;
std::string GetRadarProductName() const override; std::string GetRadarProductName() const override;
std::vector<float> GetElevationCuts() const override; std::vector<float> GetElevationCuts() const override;
std::tuple<const void*, size_t, size_t> GetMomentData() const override; std::tuple<const void*, std::size_t, std::size_t>
std::tuple<const void*, size_t, size_t> GetCfpMomentData() const override; GetMomentData() const override;
std::tuple<const void*, std::size_t, std::size_t>
GetCfpMomentData() const override;
static std::shared_ptr<Level2ProductView> static std::shared_ptr<Level2ProductView>
Create(common::Level2Product product, Create(common::Level2Product product,
std::shared_ptr<manager::RadarProductManager> radarProductManager); std::shared_ptr<manager::RadarProductManager> radarProductManager);
protected: protected:
void ConnectRadarProductManager() override;
void DisconnectRadarProductManager() override;
void UpdateColorTable() override; void UpdateColorTable() override;
protected slots: protected slots:

View file

@ -59,9 +59,37 @@ Level3ProductView::Level3ProductView(
RadarProductView(radarProductManager), RadarProductView(radarProductManager),
p(std::make_unique<Level3ProductViewImpl>(product)) p(std::make_unique<Level3ProductViewImpl>(product))
{ {
ConnectRadarProductManager();
} }
Level3ProductView::~Level3ProductView() = default; Level3ProductView::~Level3ProductView() = default;
void Level3ProductView::ConnectRadarProductManager()
{
connect(radar_product_manager().get(),
&manager::RadarProductManager::DataReloaded,
this,
[this](std::shared_ptr<types::RadarProductRecord> record)
{
if (record->radar_product_group() ==
common::RadarProductGroup::Level3 &&
record->radar_product() == p->product_ &&
record->time() == selected_time())
{
// If the data associated with the currently selected time is
// reloaded, update the view
Update();
}
});
}
void Level3ProductView::DisconnectRadarProductManager()
{
disconnect(radar_product_manager().get(),
&manager::RadarProductManager::DataReloaded,
this,
nullptr);
}
const std::vector<boost::gil::rgba8_pixel_t>& const std::vector<boost::gil::rgba8_pixel_t>&
Level3ProductView::color_table() const Level3ProductView::color_table() const
{ {

View file

@ -28,8 +28,8 @@ public:
virtual ~Level3ProductView(); virtual ~Level3ProductView();
const std::vector<boost::gil::rgba8_pixel_t>& color_table() const override; const std::vector<boost::gil::rgba8_pixel_t>& color_table() const override;
uint16_t color_table_min() const override; std::uint16_t color_table_min() const override;
uint16_t color_table_max() const override; std::uint16_t color_table_max() const override;
void LoadColorTable(std::shared_ptr<common::ColorTable> colorTable) override; void LoadColorTable(std::shared_ptr<common::ColorTable> colorTable) override;
void Update() override; void Update() override;
@ -45,6 +45,8 @@ protected:
void set_graphic_product_message( void set_graphic_product_message(
std::shared_ptr<wsr88d::rpg::GraphicProductMessage> gpm); std::shared_ptr<wsr88d::rpg::GraphicProductMessage> gpm);
void ConnectRadarProductManager() override;
void DisconnectRadarProductManager() override;
void UpdateColorTable() override; void UpdateColorTable() override;
private: private:

View file

@ -27,25 +27,18 @@ class Level3RadialViewImpl
{ {
public: public:
explicit Level3RadialViewImpl() : explicit Level3RadialViewImpl() :
selectedTime_ {}, latitude_ {}, longitude_ {}, range_ {}, vcp_ {}, sweepTime_ {}
latitude_ {},
longitude_ {},
range_ {},
vcp_ {},
sweepTime_ {}
{ {
} }
~Level3RadialViewImpl() = default; ~Level3RadialViewImpl() = default;
std::chrono::system_clock::time_point selectedTime_; std::vector<float> vertices_;
std::vector<std::uint8_t> dataMoments8_;
std::vector<float> vertices_; float latitude_;
std::vector<uint8_t> dataMoments8_; float longitude_;
float range_;
float latitude_; std::uint16_t vcp_;
float longitude_;
float range_;
uint16_t vcp_;
std::chrono::system_clock::time_point sweepTime_; std::chrono::system_clock::time_point sweepTime_;
}; };
@ -92,11 +85,6 @@ std::tuple<const void*, size_t, size_t> Level3RadialView::GetMomentData() const
return std::tie(data, dataSize, componentSize); return std::tie(data, dataSize, componentSize);
} }
void Level3RadialView::SelectTime(std::chrono::system_clock::time_point time)
{
p->selectedTime_ = time;
}
void Level3RadialView::ComputeSweep() void Level3RadialView::ComputeSweep()
{ {
logger_->debug("ComputeSweep()"); logger_->debug("ComputeSweep()");
@ -109,9 +97,18 @@ void Level3RadialView::ComputeSweep()
radar_product_manager(); radar_product_manager();
// Retrieve message from Radar Product Manager // Retrieve message from Radar Product Manager
std::shared_ptr<wsr88d::rpg::Level3Message> message = std::shared_ptr<wsr88d::rpg::Level3Message> message;
radarProductManager->GetLevel3Data(GetRadarProductName(), std::chrono::system_clock::time_point requestedTime {selected_time()};
p->selectedTime_); std::chrono::system_clock::time_point foundTime;
std::tie(message, foundTime) =
radarProductManager->GetLevel3Data(GetRadarProductName(), requestedTime);
// If a different time was found than what was requested, update it
if (requestedTime != foundTime)
{
SelectTime(foundTime);
}
if (message == nullptr) if (message == nullptr)
{ {
logger_->debug("Level 3 data not found"); logger_->debug("Level 3 data not found");

View file

@ -27,12 +27,11 @@ public:
float range() const override; float range() const override;
std::chrono::system_clock::time_point sweep_time() const override; std::chrono::system_clock::time_point sweep_time() const override;
uint16_t vcp() const override; std::uint16_t vcp() const override;
const std::vector<float>& vertices() const override; const std::vector<float>& vertices() const override;
void SelectTime(std::chrono::system_clock::time_point time) override; std::tuple<const void*, std::size_t, std::size_t>
GetMomentData() const override;
std::tuple<const void*, size_t, size_t> GetMomentData() const override;
static std::shared_ptr<Level3RadialView> static std::shared_ptr<Level3RadialView>
Create(const std::string& product, Create(const std::string& product,

View file

@ -27,18 +27,11 @@ class Level3RasterViewImpl
{ {
public: public:
explicit Level3RasterViewImpl() : explicit Level3RasterViewImpl() :
selectedTime_ {}, latitude_ {}, longitude_ {}, range_ {}, vcp_ {}, sweepTime_ {}
latitude_ {},
longitude_ {},
range_ {},
vcp_ {},
sweepTime_ {}
{ {
} }
~Level3RasterViewImpl() = default; ~Level3RasterViewImpl() = default;
std::chrono::system_clock::time_point selectedTime_;
std::vector<float> vertices_; std::vector<float> vertices_;
std::vector<uint8_t> dataMoments8_; std::vector<uint8_t> dataMoments8_;
@ -92,11 +85,6 @@ std::tuple<const void*, size_t, size_t> Level3RasterView::GetMomentData() const
return std::tie(data, dataSize, componentSize); return std::tie(data, dataSize, componentSize);
} }
void Level3RasterView::SelectTime(std::chrono::system_clock::time_point time)
{
p->selectedTime_ = time;
}
void Level3RasterView::ComputeSweep() void Level3RasterView::ComputeSweep()
{ {
logger_->debug("ComputeSweep()"); logger_->debug("ComputeSweep()");
@ -109,9 +97,18 @@ void Level3RasterView::ComputeSweep()
radar_product_manager(); radar_product_manager();
// Retrieve message from Radar Product Manager // Retrieve message from Radar Product Manager
std::shared_ptr<wsr88d::rpg::Level3Message> message = std::shared_ptr<wsr88d::rpg::Level3Message> message;
radarProductManager->GetLevel3Data(GetRadarProductName(), std::chrono::system_clock::time_point requestedTime {selected_time()};
p->selectedTime_); std::chrono::system_clock::time_point foundTime;
std::tie(message, foundTime) =
radarProductManager->GetLevel3Data(GetRadarProductName(), requestedTime);
// If a different time was found than what was requested, update it
if (requestedTime != foundTime)
{
SelectTime(foundTime);
}
if (message == nullptr) if (message == nullptr)
{ {
logger_->debug("Level 3 data not found"); logger_->debug("Level 3 data not found");

View file

@ -27,12 +27,11 @@ public:
float range() const override; float range() const override;
std::chrono::system_clock::time_point sweep_time() const override; std::chrono::system_clock::time_point sweep_time() const override;
uint16_t vcp() const override; std::uint16_t vcp() const override;
const std::vector<float>& vertices() const override; const std::vector<float>& vertices() const override;
void SelectTime(std::chrono::system_clock::time_point time) override; std::tuple<const void*, std::size_t, std::size_t>
GetMomentData() const override;
std::tuple<const void*, size_t, size_t> GetMomentData() const override;
static std::shared_ptr<Level3RasterView> static std::shared_ptr<Level3RasterView>
Create(const std::string& product, Create(const std::string& product,

View file

@ -16,12 +16,12 @@ static const std::string logPrefix_ = "scwx::qt::view::radar_product_view";
static const auto logger_ = scwx::util::Logger::Create(logPrefix_); static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
// Default color table should be transparent to prevent flicker // Default color table should be transparent to prevent flicker
static const std::vector<boost::gil::rgba8_pixel_t> DEFAULT_COLOR_TABLE = { static const std::vector<boost::gil::rgba8_pixel_t> kDefaultColorTable_ = {
boost::gil::rgba8_pixel_t(0, 128, 0, 0), boost::gil::rgba8_pixel_t(0, 128, 0, 0),
boost::gil::rgba8_pixel_t(255, 192, 0, 0), boost::gil::rgba8_pixel_t(255, 192, 0, 0),
boost::gil::rgba8_pixel_t(255, 0, 0, 0)}; boost::gil::rgba8_pixel_t(255, 0, 0, 0)};
static const uint16_t DEFAULT_COLOR_TABLE_MIN = 2u; static const std::uint16_t kDefaultColorTableMin_ = 2u;
static const uint16_t DEFAULT_COLOR_TABLE_MAX = 255u; static const std::uint16_t kDefaultColorTableMax_ = 255u;
class RadarProductViewImpl class RadarProductViewImpl
{ {
@ -30,6 +30,7 @@ public:
std::shared_ptr<manager::RadarProductManager> radarProductManager) : std::shared_ptr<manager::RadarProductManager> radarProductManager) :
initialized_ {false}, initialized_ {false},
sweepMutex_ {}, sweepMutex_ {},
selectedTime_ {},
radarProductManager_ {radarProductManager} radarProductManager_ {radarProductManager}
{ {
} }
@ -38,6 +39,8 @@ public:
bool initialized_; bool initialized_;
std::mutex sweepMutex_; std::mutex sweepMutex_;
std::chrono::system_clock::time_point selectedTime_;
std::shared_ptr<manager::RadarProductManager> radarProductManager_; std::shared_ptr<manager::RadarProductManager> radarProductManager_;
}; };
@ -49,17 +52,17 @@ RadarProductView::~RadarProductView() = default;
const std::vector<boost::gil::rgba8_pixel_t>& const std::vector<boost::gil::rgba8_pixel_t>&
RadarProductView::color_table() const RadarProductView::color_table() const
{ {
return DEFAULT_COLOR_TABLE; return kDefaultColorTable_;
} }
uint16_t RadarProductView::color_table_min() const std::uint16_t RadarProductView::color_table_min() const
{ {
return DEFAULT_COLOR_TABLE_MIN; return kDefaultColorTableMin_;
} }
uint16_t RadarProductView::color_table_max() const std::uint16_t RadarProductView::color_table_max() const
{ {
return DEFAULT_COLOR_TABLE_MAX; return kDefaultColorTableMax_;
} }
float RadarProductView::elevation() const float RadarProductView::elevation() const
@ -78,6 +81,11 @@ float RadarProductView::range() const
return 0.0f; return 0.0f;
} }
std::chrono::system_clock::time_point RadarProductView::selected_time() const
{
return p->selectedTime_;
}
std::chrono::system_clock::time_point RadarProductView::sweep_time() const std::chrono::system_clock::time_point RadarProductView::sweep_time() const
{ {
return {}; return {};
@ -91,7 +99,9 @@ std::mutex& RadarProductView::sweep_mutex()
void RadarProductView::set_radar_product_manager( void RadarProductView::set_radar_product_manager(
std::shared_ptr<manager::RadarProductManager> radarProductManager) std::shared_ptr<manager::RadarProductManager> radarProductManager)
{ {
DisconnectRadarProductManager();
p->radarProductManager_ = radarProductManager; p->radarProductManager_ = radarProductManager;
ConnectRadarProductManager();
} }
void RadarProductView::Initialize() void RadarProductView::Initialize()
@ -103,6 +113,11 @@ void RadarProductView::Initialize()
void RadarProductView::SelectElevation(float /*elevation*/) {} void RadarProductView::SelectElevation(float /*elevation*/) {}
void RadarProductView::SelectTime(std::chrono::system_clock::time_point time)
{
p->selectedTime_ = time;
}
bool RadarProductView::IsInitialized() const bool RadarProductView::IsInitialized() const
{ {
return p->initialized_; return p->initialized_;
@ -113,12 +128,12 @@ std::vector<float> RadarProductView::GetElevationCuts() const
return {}; return {};
} }
std::tuple<const void*, size_t, size_t> std::tuple<const void*, std::size_t, std::size_t>
RadarProductView::GetCfpMomentData() const RadarProductView::GetCfpMomentData() const
{ {
const void* data = nullptr; const void* data = nullptr;
size_t dataSize = 0; std::size_t dataSize = 0;
size_t componentSize = 0; std::size_t componentSize = 0;
return std::tie(data, dataSize, componentSize); return std::tie(data, dataSize, componentSize);
} }

View file

@ -30,15 +30,16 @@ public:
virtual ~RadarProductView(); virtual ~RadarProductView();
virtual const std::vector<boost::gil::rgba8_pixel_t>& color_table() const; virtual const std::vector<boost::gil::rgba8_pixel_t>& color_table() const;
virtual uint16_t color_table_min() const; virtual std::uint16_t color_table_min() const;
virtual uint16_t color_table_max() const; virtual std::uint16_t color_table_max() const;
virtual float elevation() const; virtual float elevation() const;
virtual float range() const; virtual float range() const;
virtual std::chrono::system_clock::time_point sweep_time() const; virtual std::chrono::system_clock::time_point sweep_time() const;
virtual uint16_t vcp() const = 0; virtual std::uint16_t vcp() const = 0;
virtual const std::vector<float>& vertices() const = 0; virtual const std::vector<float>& vertices() const = 0;
std::shared_ptr<manager::RadarProductManager> radar_product_manager() const; std::shared_ptr<manager::RadarProductManager> radar_product_manager() const;
std::chrono::system_clock::time_point selected_time() const;
std::mutex& sweep_mutex(); std::mutex& sweep_mutex();
void set_radar_product_manager( void set_radar_product_manager(
@ -48,20 +49,24 @@ public:
virtual void virtual void
LoadColorTable(std::shared_ptr<common::ColorTable> colorTable) = 0; LoadColorTable(std::shared_ptr<common::ColorTable> colorTable) = 0;
virtual void SelectElevation(float elevation); virtual void SelectElevation(float elevation);
virtual void SelectProduct(const std::string& productName) = 0; virtual void SelectProduct(const std::string& productName) = 0;
virtual void SelectTime(std::chrono::system_clock::time_point time) = 0; void SelectTime(std::chrono::system_clock::time_point time);
virtual void Update() = 0; virtual void Update() = 0;
bool IsInitialized() const; bool IsInitialized() const;
virtual common::RadarProductGroup GetRadarProductGroup() const = 0; virtual common::RadarProductGroup GetRadarProductGroup() const = 0;
virtual std::string GetRadarProductName() const = 0; virtual std::string GetRadarProductName() const = 0;
virtual std::vector<float> GetElevationCuts() const; virtual std::vector<float> GetElevationCuts() const;
virtual std::tuple<const void*, size_t, size_t> GetMomentData() const = 0; virtual std::tuple<const void*, std::size_t, std::size_t>
virtual std::tuple<const void*, size_t, size_t> GetCfpMomentData() const; GetMomentData() const = 0;
virtual std::tuple<const void*, std::size_t, std::size_t>
GetCfpMomentData() const;
protected: protected:
virtual void UpdateColorTable() = 0; virtual void ConnectRadarProductManager() = 0;
virtual void DisconnectRadarProductManager() = 0;
virtual void UpdateColorTable() = 0;
protected slots: protected slots:
virtual void ComputeSweep(); virtual void ComputeSweep();

View file

@ -8,10 +8,10 @@ namespace scwx
namespace util namespace util
{ {
template<class Key, class T, class ReturnType = std::optional<T>> template<class Key, class T, class ReturnType = std::map<Key, T>::const_pointer>
ReturnType GetBoundedElement(std::map<Key, T>& map, Key key) ReturnType GetBoundedElementPointer(std::map<Key, T>& map, const Key& key)
{ {
ReturnType element; ReturnType elementPtr {nullptr};
// Find the first element greater than the key requested // Find the first element greater than the key requested
auto it = map.upper_bound(key); auto it = map.upper_bound(key);
@ -24,26 +24,42 @@ ReturnType GetBoundedElement(std::map<Key, T>& map, Key key)
{ {
// Get the element immediately preceding, this the element we are // Get the element immediately preceding, this the element we are
// looking for // looking for
element = (--it)->second; elementPtr = &(*(--it));
} }
else else
{ {
// The current element is a good substitute // The current element is a good substitute
element = it->second; elementPtr = &(*it);
} }
} }
else if (map.size() > 0) else if (map.size() > 0)
{ {
// An element with a key greater was not found. If it exists, it must be // An element with a key greater was not found. If it exists, it must be
// the last element. // the last element.
element = map.rbegin()->second; elementPtr = &(*map.rbegin());
}
return elementPtr;
}
template<class Key, class T, class ReturnType = std::optional<T>>
ReturnType GetBoundedElement(std::map<Key, T>& map, const Key& key)
{
ReturnType element;
typename std::map<Key, T>::pointer elementPtr =
GetBoundedElementPointer<Key, T, typename std::map<Key, T>::pointer>(map,
key);
if (elementPtr != nullptr)
{
element = elementPtr->second;
} }
return element; return element;
} }
template<class Key, class T> template<class Key, class T>
inline T GetBoundedElementValue(std::map<Key, T>& map, Key key) inline T GetBoundedElementValue(std::map<Key, T>& map, const Key& key)
{ {
return GetBoundedElement<Key, T, T>(map, key); return GetBoundedElement<Key, T, T>(map, key);
} }