Use weak_ptr to hold product manager records

- When selecting a product that's expired, it successfully refreshes the data, but doesn't display unless selected again
- When old data is downloaded, the refresh timer starts at 15 seconds, even if the newest data says the timer should be longer
- Selecting a product should update the recent lists
This commit is contained in:
Dan Paulat 2023-04-08 01:06:56 -05:00
parent 871cae68dd
commit 5fbb748328
2 changed files with 124 additions and 33 deletions

View file

@ -37,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;
@ -124,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>(
@ -179,6 +183,8 @@ public:
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,
@ -201,10 +207,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_;
@ -299,8 +307,9 @@ void RadarProductManager::DumpRecords()
for (auto& record : for (auto& record :
radarProductManager->p->level2ProductRecords_) radarProductManager->p->level2ProductRecords_)
{ {
logger_->info(" {}", logger_->info(" {}{}",
scwx::util::TimeString(record.first)); scwx::util::TimeString(record.first),
record.second.expired() ? " (expired)" : "");
} }
} }
@ -318,8 +327,10 @@ void RadarProductManager::DumpRecords()
for (auto& record : recordMap.second) for (auto& record : recordMap.second)
{ {
logger_->info(" {}", logger_->info(" {}{}",
scwx::util::TimeString(record.first)); scwx::util::TimeString(record.first),
record.second.expired() ? " (expired)" :
"");
} }
} }
} }
@ -674,10 +685,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");
}
} }
} }
@ -844,22 +858,28 @@ 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;
RadarProductRecordMap::const_pointer recordPtr {nullptr};
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 // TODO: Round to minutes
record = scwx::util::GetBoundedElementValue(level2ProductRecords_, time); recordPtr =
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())) {
record = recordPtr->second.lock();
if (record == nullptr)
{ {
record = nullptr; // Product is expired, reload it
self_->LoadLevel2Data(recordPtr->first, nullptr);
} }
} }
@ -871,6 +891,7 @@ 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::unique_lock lock {level3ProductRecordMutex_}; std::unique_lock lock {level3ProductRecordMutex_};
@ -882,11 +903,24 @@ 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);
}
}
// Lock is no longer needed
lock.unlock();
if (recordPtr != nullptr)
{
record = recordPtr->second.lock();
if (record == nullptr)
{
// Product is expired, reload it
self_->LoadLevel3Data(product, recordPtr->first, nullptr);
} }
} }
@ -899,7 +933,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,
@ -912,15 +946,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)
{ {
@ -931,20 +972,54 @@ 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>>

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);
} }