Update IemApiProvider to use template functions

This commit is contained in:
Dan Paulat 2025-04-25 00:15:05 -05:00
parent 1a1c668d62
commit 65e3a66750
5 changed files with 159 additions and 115 deletions

View file

@ -10,7 +10,6 @@
#include <algorithm> #include <algorithm>
#include <list> #include <list>
#include <map> #include <map>
#include <ranges>
#include <shared_mutex> #include <shared_mutex>
#include <unordered_map> #include <unordered_map>
@ -20,8 +19,9 @@
#include <boost/container/stable_vector.hpp> #include <boost/container/stable_vector.hpp>
#include <boost/range/irange.hpp> #include <boost/range/irange.hpp>
#include <range/v3/range/conversion.hpp> #include <range/v3/range/conversion.hpp>
#include <range/v3/view/any_view.hpp>
#include <range/v3/view/filter.hpp> #include <range/v3/view/filter.hpp>
#include <range/v3/view/single.hpp>
#include <range/v3/view/transform.hpp>
#if (__cpp_lib_chrono < 201907L) #if (__cpp_lib_chrono < 201907L)
# include <date/date.h> # include <date/date.h>
@ -117,11 +117,17 @@ public:
void void
HandleMessage(const std::shared_ptr<awips::TextProductMessage>& message); HandleMessage(const std::shared_ptr<awips::TextProductMessage>& message);
void ListArchives(ranges::any_view<std::chrono::sys_days> dates); template<ranges::forward_range DateRange>
requires std::same_as<ranges::range_value_t<DateRange>,
std::chrono::sys_days>
void ListArchives(DateRange dates);
void LoadArchives(std::chrono::system_clock::time_point dateTime); 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); template<ranges::forward_range DateRange>
requires std::same_as<ranges::range_value_t<DateRange>,
std::chrono::sys_days>
void UpdateArchiveDates(DateRange dates);
// Thread pool sized for: // Thread pool sized for:
// - Live Refresh (1x) // - Live Refresh (1x)
@ -139,8 +145,6 @@ public:
textEventMap_; textEventMap_;
std::shared_mutex textEventMutex_; std::shared_mutex textEventMutex_;
std::unique_ptr<provider::IemApiProvider> iemApiProvider_ {
std::make_unique<provider::IemApiProvider>()};
std::shared_ptr<provider::WarningsProvider> warningsProvider_ {nullptr}; std::shared_ptr<provider::WarningsProvider> warningsProvider_ {nullptr};
std::chrono::hours loadHistoryDuration_ {kInitialLoadHistoryDuration_}; std::chrono::hours loadHistoryDuration_ {kInitialLoadHistoryDuration_};
@ -240,7 +244,7 @@ void TextEventManager::SelectTime(
const auto tomorrow = today + std::chrono::days {1}; const auto tomorrow = today + std::chrono::days {1};
const auto dateArray = std::array {today, yesterday, tomorrow}; const auto dateArray = std::array {today, yesterday, tomorrow};
const ranges::any_view<std::chrono::sys_days> dates = const auto dates =
dateArray | ranges::views::filter( dateArray | ranges::views::filter(
[this](const auto& date) [this](const auto& date)
{ {
@ -334,11 +338,13 @@ void TextEventManager::Impl::HandleMessage(
} }
} }
void TextEventManager::Impl::ListArchives( template<ranges::forward_range DateRange>
ranges::any_view<std::chrono::sys_days> dates) requires std::same_as<ranges::range_value_t<DateRange>,
std::chrono::sys_days>
void TextEventManager::Impl::ListArchives(DateRange dates)
{ {
// Don't reload data that has already been loaded // Don't reload data that has already been loaded
ranges::any_view<std::chrono::sys_days> filteredDates = auto filteredDates =
dates | dates |
ranges::views::filter([this](const auto& date) ranges::views::filter([this](const auto& date)
{ return !unloadedProductMap_.contains(date); }); { return !unloadedProductMap_.contains(date); });
@ -351,10 +357,17 @@ void TextEventManager::Impl::ListArchives(
dv.end(), dv.end(),
[this](const auto& date) [this](const auto& date)
{ {
static const auto kEmptyRange_ =
ranges::views::single(std::string_view {});
static const auto kPilsView_ =
kPils_ |
ranges::views::transform([](const std::string& pil)
{ return std::string_view {pil}; });
const auto dateArray = std::array {date}; const auto dateArray = std::array {date};
auto productEntries = auto productEntries = provider::IemApiProvider::ListTextProducts(
iemApiProvider_->ListTextProducts(dateArray, {}, kPils_); dateArray | ranges::views::all, kEmptyRange_, kPilsView_);
std::unique_lock lock {unloadedProductMapMutex_}; std::unique_lock lock {unloadedProductMapMutex_};
@ -450,10 +463,10 @@ void TextEventManager::Impl::LoadArchives(
} }
// Load the load list // Load the load list
auto loadView = loadListEntries | auto loadView =
std::ranges::views::transform([](const auto& entry) loadListEntries | ranges::views::transform([](const auto& entry)
{ return entry.productId_; }); { return entry.productId_; });
auto products = iemApiProvider_->LoadTextProducts(loadView); auto products = provider::IemApiProvider::LoadTextProducts(loadView);
// Process loaded products // Process loaded products
for (auto& product : products) for (auto& product : products)
@ -550,8 +563,10 @@ void TextEventManager::Impl::Refresh()
}); });
} }
void TextEventManager::Impl::UpdateArchiveDates( template<ranges::forward_range DateRange>
ranges::any_view<std::chrono::sys_days> dates) requires std::same_as<ranges::range_value_t<DateRange>,
std::chrono::sys_days>
void TextEventManager::Impl::UpdateArchiveDates(DateRange dates)
{ {
for (const auto& date : dates) for (const auto& date : dates)
{ {

View file

@ -1,24 +1,21 @@
#pragma once #pragma once
#include <scwx/awips/text_product_file.hpp> #include <scwx/awips/text_product_file.hpp>
#include <scwx/network/cpr.hpp>
#include <scwx/types/iem_types.hpp> #include <scwx/types/iem_types.hpp>
#include <memory> #include <memory>
#include <ranges>
#include <string> #include <string>
#include <vector> #include <vector>
#include <boost/outcome/result.hpp> #include <boost/outcome/result.hpp>
#include <cpr/cpr.h> #include <cpr/session.h>
#include <range/v3/range/concepts.hpp>
#if defined(_MSC_VER) #if defined(_MSC_VER)
# pragma warning(push) # pragma warning(push)
# pragma warning(disable : 4702) # pragma warning(disable : 4702)
#endif #endif
#include <range/v3/view/any_view.hpp>
#if defined(_MSC_VER) #if defined(_MSC_VER)
# pragma warning(pop) # pragma warning(pop)
#endif #endif
@ -42,50 +39,41 @@ public:
IemApiProvider& operator=(IemApiProvider&&) noexcept; IemApiProvider& operator=(IemApiProvider&&) noexcept;
static boost::outcome_v2::result<std::vector<types::iem::AfosEntry>> static boost::outcome_v2::result<std::vector<types::iem::AfosEntry>>
ListTextProducts(std::chrono::sys_time<std::chrono::days> date, ListTextProducts(std::chrono::sys_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 = {});
template<ranges::forward_range DateRange,
ranges::forward_range CcccRange,
ranges::forward_range PilRange>
requires std::same_as<ranges::range_value_t<DateRange>,
std::chrono::sys_days> &&
std::same_as<ranges::range_value_t<CcccRange>,
std::string_view> &&
std::same_as<ranges::range_value_t<PilRange>, std::string_view>
static boost::outcome_v2::result<std::vector<types::iem::AfosEntry>> static boost::outcome_v2::result<std::vector<types::iem::AfosEntry>>
ListTextProducts( ListTextProducts(DateRange dates, CcccRange ccccs, PilRange pils);
ranges::any_view<std::chrono::sys_time<std::chrono::days>> dates,
ranges::any_view<std::string_view> ccccs = {},
ranges::any_view<std::string_view> pils = {});
template<std::ranges::forward_range Range> template<ranges::forward_range Range>
requires std::same_as<std::ranges::range_value_t<Range>, std::string> requires std::same_as<ranges::range_value_t<Range>, std::string>
static std::vector<std::shared_ptr<awips::TextProductFile>> static std::vector<std::shared_ptr<awips::TextProductFile>>
LoadTextProducts(const Range& textProducts) LoadTextProducts(const Range& textProducts);
{
auto parameters = cpr::Parameters {{"nolimit", "true"}};
std::vector<std::pair<std::string, cpr::AsyncResponse>> asyncResponses {};
asyncResponses.reserve(textProducts.size());
const std::string endpointUrl = kBaseUrl_ + kNwsTextProductEndpoint_;
for (const auto& productId : textProducts)
{
asyncResponses.emplace_back(
productId,
cpr::GetAsync(cpr::Url {endpointUrl + productId},
network::cpr::GetHeader(),
parameters));
}
return ProcessTextProductResponses(asyncResponses);
}
private: private:
class Impl; class Impl;
std::unique_ptr<Impl> p; std::unique_ptr<Impl> p;
static boost::outcome_v2::result<std::vector<types::iem::AfosEntry>>
ProcessTextProductLists(std::vector<cpr::AsyncResponse>& asyncResponses);
static std::vector<std::shared_ptr<awips::TextProductFile>>
ProcessTextProductFiles(
std::vector<std::pair<std::string, cpr::AsyncResponse>>& asyncResponses);
static const std::string kBaseUrl_; static const std::string kBaseUrl_;
static const std::string kListNwsTextProductsEndpoint_; static const std::string kListNwsTextProductsEndpoint_;
static const std::string kNwsTextProductEndpoint_; static const std::string kNwsTextProductEndpoint_;
static std::vector<std::shared_ptr<awips::TextProductFile>>
ProcessTextProductResponses(
std::vector<std::pair<std::string, cpr::AsyncResponse>>& asyncResponses);
}; };
} // namespace scwx::provider } // namespace scwx::provider
#include <scwx/provider/iem_api_provider.ipp>

View file

@ -0,0 +1,95 @@
#pragma once
#include <scwx/provider/iem_api_provider.hpp>
#include <scwx/network/cpr.hpp>
#include <cpr/cpr.h>
#include <range/v3/view/cartesian_product.hpp>
#include <range/v3/view/single.hpp>
#if (__cpp_lib_chrono < 201907L)
# include <date/date.h>
#endif
namespace scwx::provider
{
template<ranges::forward_range DateRange,
ranges::forward_range CcccRange,
ranges::forward_range PilRange>
requires std::same_as<ranges::range_value_t<DateRange>,
std::chrono::sys_days> &&
std::same_as<ranges::range_value_t<CcccRange>, std::string_view> &&
std::same_as<ranges::range_value_t<PilRange>, std::string_view>
boost::outcome_v2::result<std::vector<types::iem::AfosEntry>>
IemApiProvider::ListTextProducts(DateRange dates,
CcccRange ccccs,
PilRange pils)
{
using namespace std::chrono;
#if (__cpp_lib_chrono >= 201907L)
namespace df = std;
static constexpr std::string_view kDateFormat {"{:%Y-%m-%d}"};
#else
using namespace date;
namespace df = date;
# define kDateFormat "%Y-%m-%d"
#endif
std::vector<cpr::AsyncResponse> asyncResponses {};
for (const auto& [date, cccc, pil] :
ranges::views::cartesian_product(dates, ccccs, pils))
{
auto parameters =
cpr::Parameters {{"date", df::format(kDateFormat, date)}};
// WMO Source Code
if (!cccc.empty())
{
parameters.Add({"cccc", std::string {cccc}});
}
// AFOS / AWIPS ID / 3-6 length identifier
if (!pil.empty())
{
parameters.Add({"pil", std::string {pil}});
}
asyncResponses.emplace_back(
cpr::GetAsync(cpr::Url {kBaseUrl_ + kListNwsTextProductsEndpoint_},
network::cpr::GetHeader(),
parameters));
}
return ProcessTextProductLists(asyncResponses);
}
template<ranges::forward_range Range>
requires std::same_as<ranges::range_value_t<Range>, std::string>
std::vector<std::shared_ptr<awips::TextProductFile>>
IemApiProvider::LoadTextProducts(const Range& textProducts)
{
auto parameters = cpr::Parameters {{"nolimit", "true"}};
std::vector<std::pair<std::string, cpr::AsyncResponse>> asyncResponses {};
asyncResponses.reserve(textProducts.size());
const std::string endpointUrl = kBaseUrl_ + kNwsTextProductEndpoint_;
for (const auto& productId : textProducts)
{
asyncResponses.emplace_back(
productId,
cpr::GetAsync(cpr::Url {endpointUrl + productId},
network::cpr::GetHeader(),
parameters));
}
return ProcessTextProductFiles(asyncResponses);
}
} // namespace scwx::provider

View file

@ -44,7 +44,7 @@ IemApiProvider::IemApiProvider(IemApiProvider&&) noexcept = default;
IemApiProvider& IemApiProvider::operator=(IemApiProvider&&) noexcept = default; IemApiProvider& IemApiProvider::operator=(IemApiProvider&&) noexcept = default;
boost::outcome_v2::result<std::vector<types::iem::AfosEntry>> boost::outcome_v2::result<std::vector<types::iem::AfosEntry>>
IemApiProvider::ListTextProducts(std::chrono::sys_time<std::chrono::days> date, IemApiProvider::ListTextProducts(std::chrono::sys_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)
{ {
@ -61,67 +61,12 @@ IemApiProvider::ListTextProducts(std::chrono::sys_time<std::chrono::days> date,
} }
boost::outcome_v2::result<std::vector<types::iem::AfosEntry>> boost::outcome_v2::result<std::vector<types::iem::AfosEntry>>
IemApiProvider::ListTextProducts( IemApiProvider::ProcessTextProductLists(
ranges::any_view<std::chrono::sys_time<std::chrono::days>> dates, std::vector<cpr::AsyncResponse>& asyncResponses)
ranges::any_view<std::string_view> ccccs,
ranges::any_view<std::string_view> pils)
{ {
using namespace std::chrono;
#if (__cpp_lib_chrono >= 201907L)
namespace df = std;
static constexpr std::string_view kDateFormat {"{:%Y-%m-%d}"};
#else
using namespace date;
namespace df = date;
# define kDateFormat "%Y-%m-%d"
#endif
if (ccccs.begin() == ccccs.end())
{
ccccs = ranges::views::single(std::string_view {});
}
if (pils.begin() == pils.end())
{
pils = ranges::views::single(std::string_view {});
}
const auto dv = ranges::to<std::vector>(dates);
const auto cv = ranges::to<std::vector>(ccccs);
const auto pv = ranges::to<std::vector>(pils);
std::vector<cpr::AsyncResponse> responses {};
for (const auto& [date, cccc, pil] :
ranges::views::cartesian_product(dv, cv, pv))
{
auto parameters =
cpr::Parameters {{"date", df::format(kDateFormat, date)}};
// WMO Source Code
if (!cccc.empty())
{
parameters.Add({"cccc", std::string {cccc}});
}
// AFOS / AWIPS ID / 3-6 length identifier
if (!pil.empty())
{
parameters.Add({"pil", std::string {pil}});
}
responses.emplace_back(
cpr::GetAsync(cpr::Url {kBaseUrl_ + kListNwsTextProductsEndpoint_},
network::cpr::GetHeader(),
parameters));
}
std::vector<types::iem::AfosEntry> textProducts {}; std::vector<types::iem::AfosEntry> textProducts {};
for (auto& asyncResponse : responses) for (auto& asyncResponse : asyncResponses)
{ {
auto response = asyncResponse.get(); auto response = asyncResponse.get();
@ -201,7 +146,7 @@ IemApiProvider::ListTextProducts(
} }
std::vector<std::shared_ptr<awips::TextProductFile>> std::vector<std::shared_ptr<awips::TextProductFile>>
IemApiProvider::ProcessTextProductResponses( IemApiProvider::ProcessTextProductFiles(
std::vector<std::pair<std::string, cpr::AsyncResponse>>& asyncResponses) std::vector<std::pair<std::string, cpr::AsyncResponse>>& asyncResponses)
{ {
std::vector<std::shared_ptr<awips::TextProductFile>> textProductFiles; std::vector<std::shared_ptr<awips::TextProductFile>> textProductFiles;

View file

@ -63,6 +63,7 @@ set(HDR_PROVIDER include/scwx/provider/aws_level2_data_provider.hpp
include/scwx/provider/aws_level3_data_provider.hpp include/scwx/provider/aws_level3_data_provider.hpp
include/scwx/provider/aws_nexrad_data_provider.hpp include/scwx/provider/aws_nexrad_data_provider.hpp
include/scwx/provider/iem_api_provider.hpp include/scwx/provider/iem_api_provider.hpp
include/scwx/provider/iem_api_provider.ipp
include/scwx/provider/nexrad_data_provider.hpp include/scwx/provider/nexrad_data_provider.hpp
include/scwx/provider/nexrad_data_provider_factory.hpp include/scwx/provider/nexrad_data_provider_factory.hpp
include/scwx/provider/warnings_provider.hpp) include/scwx/provider/warnings_provider.hpp)