mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 14:50:05 +00:00
Start of implementation to load a window of archive warning data, currently broken
This commit is contained in:
parent
e3ccce5d5b
commit
33e18765b7
4 changed files with 163 additions and 30 deletions
|
|
@ -7,6 +7,7 @@
|
||||||
#include <scwx/util/logger.hpp>
|
#include <scwx/util/logger.hpp>
|
||||||
#include <scwx/util/time.hpp>
|
#include <scwx/util/time.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <shared_mutex>
|
#include <shared_mutex>
|
||||||
|
|
@ -15,9 +16,16 @@
|
||||||
#include <boost/asio/post.hpp>
|
#include <boost/asio/post.hpp>
|
||||||
#include <boost/asio/steady_timer.hpp>
|
#include <boost/asio/steady_timer.hpp>
|
||||||
#include <boost/asio/thread_pool.hpp>
|
#include <boost/asio/thread_pool.hpp>
|
||||||
|
#include <boost/container/stable_vector.hpp>
|
||||||
|
#include <boost/range/irange.hpp>
|
||||||
|
#include <range/v3/range/conversion.hpp>
|
||||||
#include <range/v3/view/any_view.hpp>
|
#include <range/v3/view/any_view.hpp>
|
||||||
#include <range/v3/view/filter.hpp>
|
#include <range/v3/view/filter.hpp>
|
||||||
|
|
||||||
|
#if (__cpp_lib_chrono < 201907L)
|
||||||
|
# include <date/date.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace scwx
|
namespace scwx
|
||||||
{
|
{
|
||||||
namespace qt
|
namespace qt
|
||||||
|
|
@ -25,6 +33,8 @@ namespace qt
|
||||||
namespace manager
|
namespace manager
|
||||||
{
|
{
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
static const std::string logPrefix_ = "scwx::qt::manager::text_event_manager";
|
static const std::string logPrefix_ = "scwx::qt::manager::text_event_manager";
|
||||||
static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
|
static const auto logger_ = scwx::util::Logger::Create(logPrefix_);
|
||||||
|
|
||||||
|
|
@ -33,8 +43,23 @@ static constexpr std::chrono::hours kInitialLoadHistoryDuration_ =
|
||||||
static constexpr std::chrono::hours kDefaultLoadHistoryDuration_ =
|
static constexpr std::chrono::hours kDefaultLoadHistoryDuration_ =
|
||||||
std::chrono::hours {1};
|
std::chrono::hours {1};
|
||||||
|
|
||||||
static const std::array<std::string, 5> kPils_ = {
|
static const std::array<std::string, 8> kPils_ = {
|
||||||
"TOR", "SVR", "SVS", "FFW", "FFS"};
|
"FFS", "FFW", "MWS", "SMW", "SQW", "SVR", "SVS", "TOR"};
|
||||||
|
|
||||||
|
static const std::
|
||||||
|
unordered_map<std::string, std::pair<std::chrono::hours, std::chrono::hours>>
|
||||||
|
kPilLoadWindows_ {{"FFS", {-24h, 1h}},
|
||||||
|
{"FFW", {-24h, 1h}},
|
||||||
|
{"MWS", {-4h, 1h}},
|
||||||
|
{"SMW", {-4h, 1h}},
|
||||||
|
{"SQW", {-4h, 1h}},
|
||||||
|
{"SVR", {-4h, 1h}},
|
||||||
|
{"SVS", {-4h, 1h}},
|
||||||
|
{"TOR", {-4h, 1h}}};
|
||||||
|
|
||||||
|
// Widest load window provided by kPilLoadWindows_
|
||||||
|
static const std::pair<std::chrono::hours, std::chrono::hours>
|
||||||
|
kArchiveLoadWindow_ {-24h, 1h};
|
||||||
|
|
||||||
class TextEventManager::Impl
|
class TextEventManager::Impl
|
||||||
{
|
{
|
||||||
|
|
@ -91,7 +116,8 @@ public:
|
||||||
|
|
||||||
void
|
void
|
||||||
HandleMessage(const std::shared_ptr<awips::TextProductMessage>& message);
|
HandleMessage(const std::shared_ptr<awips::TextProductMessage>& message);
|
||||||
void LoadArchives(ranges::any_view<std::chrono::sys_days> dates);
|
void ListArchives(ranges::any_view<std::chrono::sys_days> dates);
|
||||||
|
void LoadArchives(std::chrono::system_clock::time_point dateTime);
|
||||||
void RefreshAsync();
|
void RefreshAsync();
|
||||||
void Refresh();
|
void Refresh();
|
||||||
void UpdateArchiveDates(ranges::any_view<std::chrono::sys_days> dates);
|
void UpdateArchiveDates(ranges::any_view<std::chrono::sys_days> dates);
|
||||||
|
|
@ -125,6 +151,9 @@ public:
|
||||||
std::map<std::chrono::sys_days,
|
std::map<std::chrono::sys_days,
|
||||||
std::vector<std::shared_ptr<awips::TextProductFile>>>
|
std::vector<std::shared_ptr<awips::TextProductFile>>>
|
||||||
archiveMap_;
|
archiveMap_;
|
||||||
|
std::map<std::chrono::sys_days,
|
||||||
|
boost::container::stable_vector<scwx::types::iem::AfosEntry>>
|
||||||
|
unloadedProductMap_;
|
||||||
|
|
||||||
boost::uuids::uuid warningsProviderChangedCallbackUuid_ {};
|
boost::uuids::uuid warningsProviderChangedCallbackUuid_ {};
|
||||||
};
|
};
|
||||||
|
|
@ -219,7 +248,8 @@ void TextEventManager::SelectTime(
|
||||||
date < p->archiveLimit_;
|
date < p->archiveLimit_;
|
||||||
});
|
});
|
||||||
|
|
||||||
p->LoadArchives(dates);
|
p->ListArchives(dates);
|
||||||
|
p->LoadArchives(dateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextEventManager::Impl::HandleMessage(
|
void TextEventManager::Impl::HandleMessage(
|
||||||
|
|
@ -301,23 +331,127 @@ void TextEventManager::Impl::HandleMessage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextEventManager::Impl::LoadArchives(
|
void TextEventManager::Impl::ListArchives(
|
||||||
ranges::any_view<std::chrono::sys_days> dates)
|
ranges::any_view<std::chrono::sys_days> dates)
|
||||||
{
|
{
|
||||||
UpdateArchiveDates(dates);
|
|
||||||
|
|
||||||
std::unique_lock lock {archiveMutex_};
|
std::unique_lock lock {archiveMutex_};
|
||||||
|
|
||||||
|
UpdateArchiveDates(dates);
|
||||||
|
|
||||||
// Don't reload data that has already been loaded
|
// Don't reload data that has already been loaded
|
||||||
const ranges::any_view<std::chrono::sys_days> filteredDates =
|
ranges::any_view<std::chrono::sys_days> filteredDates =
|
||||||
dates | ranges::views::filter([this](const auto& date)
|
dates |
|
||||||
{ return !archiveMap_.contains(date); });
|
ranges::views::filter([this](const auto& date)
|
||||||
|
{ return !unloadedProductMap_.contains(date); });
|
||||||
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
// Query for products
|
const auto dv = ranges::to<std::vector>(filteredDates);
|
||||||
const auto& productIds =
|
|
||||||
iemApiProvider_->ListTextProducts(filteredDates, {}, kPils_);
|
std::for_each(
|
||||||
|
std::execution::par,
|
||||||
|
dv.begin(),
|
||||||
|
dv.end(),
|
||||||
|
[this](const auto& date)
|
||||||
|
{
|
||||||
|
const auto dateArray = std::array {date};
|
||||||
|
|
||||||
|
auto productEntries =
|
||||||
|
iemApiProvider_->ListTextProducts(dateArray, {}, kPils_);
|
||||||
|
|
||||||
|
std::unique_lock lock {archiveMutex_};
|
||||||
|
|
||||||
|
if (productEntries.has_value())
|
||||||
|
{
|
||||||
|
unloadedProductMap_.try_emplace(
|
||||||
|
date,
|
||||||
|
{std::make_move_iterator(productEntries.value().begin()),
|
||||||
|
std::make_move_iterator(productEntries.value().end())});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextEventManager::Impl::LoadArchives(
|
||||||
|
std::chrono::system_clock::time_point dateTime)
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
#if (__cpp_lib_chrono >= 201907L)
|
||||||
|
namespace df = std;
|
||||||
|
|
||||||
|
static constexpr std::string_view kDateFormat {"{:%Y%m%d%H%M}"};
|
||||||
|
#else
|
||||||
|
using namespace date;
|
||||||
|
namespace df = date;
|
||||||
|
|
||||||
|
# define kDateFormat "%Y%m%d%H%M"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Search unloaded products in the widest archive load window
|
||||||
|
const std::chrono::sys_days startDate =
|
||||||
|
std::chrono::floor<std::chrono::days>(dateTime +
|
||||||
|
kArchiveLoadWindow_.first);
|
||||||
|
const std::chrono::sys_days endDate = std::chrono::floor<std::chrono::days>(
|
||||||
|
dateTime + kArchiveLoadWindow_.second + std::chrono::days {1});
|
||||||
|
|
||||||
|
// Determine load windows for each PIL
|
||||||
|
std::unordered_map<std::string, std::pair<std::string, std::string>>
|
||||||
|
pilLoadWindowStrings;
|
||||||
|
|
||||||
|
for (auto& loadWindow : kPilLoadWindows_)
|
||||||
|
{
|
||||||
|
const std::string& pil = loadWindow.first;
|
||||||
|
|
||||||
|
pilLoadWindowStrings.insert_or_assign(
|
||||||
|
pil,
|
||||||
|
std::pair<std::string, std::string> {
|
||||||
|
df::format(kDateFormat, (dateTime + loadWindow.second.first)),
|
||||||
|
df::format(kDateFormat, (dateTime + loadWindow.second.second))});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<scwx::types::iem::AfosEntry> loadList {};
|
||||||
|
|
||||||
|
std::unique_lock lock {archiveMutex_};
|
||||||
|
|
||||||
|
for (auto date : boost::irange(startDate, endDate))
|
||||||
|
{
|
||||||
|
auto mapIt = unloadedProductMap_.find(date);
|
||||||
|
if (mapIt == unloadedProductMap_.cend())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = mapIt->second.begin(); it != mapIt->second.end();)
|
||||||
|
{
|
||||||
|
const auto& pil = it->pil_;
|
||||||
|
|
||||||
|
// Check PIL
|
||||||
|
if (pil.size() >= 3)
|
||||||
|
{
|
||||||
|
auto pilPrefix = pil.substr(0, 3);
|
||||||
|
auto windowIt = pilLoadWindowStrings.find(pilPrefix);
|
||||||
|
|
||||||
|
// Check Window
|
||||||
|
if (windowIt != pilLoadWindowStrings.cend())
|
||||||
|
{
|
||||||
|
const auto& productId = it->productId_;
|
||||||
|
const auto& windowStart = windowIt->second.first;
|
||||||
|
const auto& windowEnd = windowIt->second.second;
|
||||||
|
|
||||||
|
if (windowStart <= productId && productId <= windowEnd)
|
||||||
|
{
|
||||||
|
// Product matches, move it to the load list
|
||||||
|
loadList.emplace_back(std::move(*it));
|
||||||
|
it = mapIt->second.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current iterator was not matched
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (productIds.has_value())
|
if (productIds.has_value())
|
||||||
{
|
{
|
||||||
|
|
@ -435,8 +569,6 @@ void TextEventManager::Impl::Refresh()
|
||||||
void TextEventManager::Impl::UpdateArchiveDates(
|
void TextEventManager::Impl::UpdateArchiveDates(
|
||||||
ranges::any_view<std::chrono::sys_days> dates)
|
ranges::any_view<std::chrono::sys_days> dates)
|
||||||
{
|
{
|
||||||
std::unique_lock lock {archiveMutex_};
|
|
||||||
|
|
||||||
for (const auto& date : dates)
|
for (const auto& date : dates)
|
||||||
{
|
{
|
||||||
// Remove any existing occurrences of day, and add to the back of the list
|
// Remove any existing occurrences of day, and add to the back of the list
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,13 @@ TEST(IemApiProviderTest, ListTextProducts)
|
||||||
|
|
||||||
if (torProducts.value().size() >= 1)
|
if (torProducts.value().size() >= 1)
|
||||||
{
|
{
|
||||||
EXPECT_EQ(torProducts.value().at(0), "202303250016-KMEG-WFUS54-TORMEG");
|
EXPECT_EQ(torProducts.value().at(0).productId_,
|
||||||
|
"202303250016-KMEG-WFUS54-TORMEG");
|
||||||
}
|
}
|
||||||
if (torProducts.value().size() >= 35)
|
if (torProducts.value().size() >= 35)
|
||||||
{
|
{
|
||||||
EXPECT_EQ(torProducts.value().at(34), "202303252015-KFFC-WFUS52-TORFFC");
|
EXPECT_EQ(torProducts.value().at(34).productId_,
|
||||||
|
"202303252015-KFFC-WFUS52-TORFFC");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <scwx/awips/text_product_file.hpp>
|
#include <scwx/awips/text_product_file.hpp>
|
||||||
|
#include <scwx/types/iem_types.hpp>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -36,11 +37,12 @@ public:
|
||||||
IemApiProvider(IemApiProvider&&) noexcept;
|
IemApiProvider(IemApiProvider&&) noexcept;
|
||||||
IemApiProvider& operator=(IemApiProvider&&) noexcept;
|
IemApiProvider& operator=(IemApiProvider&&) noexcept;
|
||||||
|
|
||||||
static boost::outcome_v2::result<std::vector<std::string>>
|
static boost::outcome_v2::result<std::vector<types::iem::AfosEntry>>
|
||||||
ListTextProducts(std::chrono::sys_time<std::chrono::days> date,
|
ListTextProducts(std::chrono::sys_time<std::chrono::days> date,
|
||||||
std::optional<std::string_view> cccc = {},
|
std::optional<std::string_view> cccc = {},
|
||||||
std::optional<std::string_view> pil = {});
|
std::optional<std::string_view> pil = {});
|
||||||
static boost::outcome_v2::result<std::vector<std::string>> ListTextProducts(
|
static boost::outcome_v2::result<std::vector<types::iem::AfosEntry>>
|
||||||
|
ListTextProducts(
|
||||||
ranges::any_view<std::chrono::sys_time<std::chrono::days>> dates,
|
ranges::any_view<std::chrono::sys_time<std::chrono::days>> dates,
|
||||||
ranges::any_view<std::string_view> ccccs = {},
|
ranges::any_view<std::string_view> ccccs = {},
|
||||||
ranges::any_view<std::string_view> pils = {});
|
ranges::any_view<std::string_view> pils = {});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#include <scwx/provider/iem_api_provider.hpp>
|
#include <scwx/provider/iem_api_provider.hpp>
|
||||||
#include <scwx/network/cpr.hpp>
|
#include <scwx/network/cpr.hpp>
|
||||||
#include <scwx/types/iem_types.hpp>
|
|
||||||
#include <scwx/util/json.hpp>
|
#include <scwx/util/json.hpp>
|
||||||
#include <scwx/util/logger.hpp>
|
#include <scwx/util/logger.hpp>
|
||||||
|
|
||||||
|
|
@ -42,7 +41,7 @@ IemApiProvider::~IemApiProvider() = default;
|
||||||
IemApiProvider::IemApiProvider(IemApiProvider&&) noexcept = default;
|
IemApiProvider::IemApiProvider(IemApiProvider&&) noexcept = default;
|
||||||
IemApiProvider& IemApiProvider::operator=(IemApiProvider&&) noexcept = default;
|
IemApiProvider& IemApiProvider::operator=(IemApiProvider&&) noexcept = default;
|
||||||
|
|
||||||
boost::outcome_v2::result<std::vector<std::string>>
|
boost::outcome_v2::result<std::vector<types::iem::AfosEntry>>
|
||||||
IemApiProvider::ListTextProducts(std::chrono::sys_time<std::chrono::days> date,
|
IemApiProvider::ListTextProducts(std::chrono::sys_time<std::chrono::days> date,
|
||||||
std::optional<std::string_view> optionalCccc,
|
std::optional<std::string_view> optionalCccc,
|
||||||
std::optional<std::string_view> optionalPil)
|
std::optional<std::string_view> optionalPil)
|
||||||
|
|
@ -59,7 +58,7 @@ IemApiProvider::ListTextProducts(std::chrono::sys_time<std::chrono::days> date,
|
||||||
return ListTextProducts(dateArray, ccccArray, pilArray);
|
return ListTextProducts(dateArray, ccccArray, pilArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::outcome_v2::result<std::vector<std::string>>
|
boost::outcome_v2::result<std::vector<types::iem::AfosEntry>>
|
||||||
IemApiProvider::ListTextProducts(
|
IemApiProvider::ListTextProducts(
|
||||||
ranges::any_view<std::chrono::sys_time<std::chrono::days>> dates,
|
ranges::any_view<std::chrono::sys_time<std::chrono::days>> dates,
|
||||||
ranges::any_view<std::string_view> ccccs,
|
ranges::any_view<std::string_view> ccccs,
|
||||||
|
|
@ -118,7 +117,7 @@ IemApiProvider::ListTextProducts(
|
||||||
parameters));
|
parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> textProducts {};
|
std::vector<types::iem::AfosEntry> textProducts {};
|
||||||
|
|
||||||
for (auto& asyncResponse : responses)
|
for (auto& asyncResponse : responses)
|
||||||
{
|
{
|
||||||
|
|
@ -132,13 +131,9 @@ IemApiProvider::ListTextProducts(
|
||||||
{
|
{
|
||||||
// Get AFOS list from response
|
// Get AFOS list from response
|
||||||
auto entries = boost::json::value_to<types::iem::AfosList>(json);
|
auto entries = boost::json::value_to<types::iem::AfosList>(json);
|
||||||
|
textProducts.insert(textProducts.end(),
|
||||||
for (auto& entry : entries.data_)
|
std::make_move_iterator(entries.data_.begin()),
|
||||||
{
|
std::make_move_iterator(entries.data_.end()));
|
||||||
textProducts.push_back(entry.productId_);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger_->trace("Found {} products", entries.data_.size());
|
|
||||||
}
|
}
|
||||||
catch (const std::exception& ex)
|
catch (const std::exception& ex)
|
||||||
{
|
{
|
||||||
|
|
@ -198,6 +193,8 @@ IemApiProvider::ListTextProducts(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger_->trace("Found {} products", textProducts.size());
|
||||||
|
|
||||||
return textProducts;
|
return textProducts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue