diff --git a/test/source/scwx/provider/aws_level3_data_provider.test.cpp b/test/source/scwx/provider/aws_level3_data_provider.test.cpp new file mode 100644 index 00000000..a5417ecc --- /dev/null +++ b/test/source/scwx/provider/aws_level3_data_provider.test.cpp @@ -0,0 +1,91 @@ +#include + +#include + +namespace scwx +{ +namespace provider +{ + +TEST(AwsLevel3DataProvider, FindKeyFixed) +{ + using namespace std::chrono; + using sys_days = time_point; + + const auto date = sys_days {2021y / May / 27d}; + const auto time = date + 17h + 59min; + + AwsLevel3DataProvider provider("KLSX", "N0Q"); + + provider.ListObjects(date); + std::string key = provider.FindKey(time); + + EXPECT_EQ(key, "LSX_N0Q_2021_05_27_17_57_17"); +} + +TEST(AwsLevel3DataProvider, FindKeyNow) +{ + AwsLevel3DataProvider provider("KLSX", "N0B"); + + provider.Refresh(); + std::string key = provider.FindKey(std::chrono::system_clock::now()); + + EXPECT_GT(key.size(), 0); +} + +TEST(AwsLevel3DataProvider, LoadObjectByKey) +{ + const std::string key = "LSX_N0Q_2021_05_27_17_57_17"; + + AwsLevel3DataProvider provider("KLSX", "N0Q"); + + auto file = provider.LoadObjectByKey(key); + + EXPECT_NE(file, nullptr); +} + +TEST(AwsLevel3DataProvider, Refresh) +{ + AwsLevel3DataProvider provider("KLSX", "N0B"); + + size_t newObjects = provider.Refresh(); + + EXPECT_GT(newObjects, 0); + EXPECT_GT(provider.cache_size(), 0); +} + +TEST(AwsLevel3DataProvider, TimePointValid) +{ + using namespace std::chrono; + using sys_days = time_point; + + constexpr auto expectedTime = + sys_days {2022y / April / 30d} + 17h + 27min + 34s; + + auto time = + AwsLevel3DataProvider::GetTimePointFromKey("LSX_N0Q_2022_04_30_17_27_34"); + + EXPECT_EQ(time, expectedTime); +} + +TEST(AwsLevel3DataProvider, TimePointInvalid) +{ + constexpr std::chrono::system_clock::time_point expectedTime {}; + + auto time = AwsLevel3DataProvider::GetTimePointFromKey( + "LSX_N0Q_2022-04-30_17-27-34"); + + EXPECT_EQ(time, expectedTime); +} + +TEST(AwsLevel3DataProvider, TimePointBadKey) +{ + constexpr std::chrono::system_clock::time_point expectedTime {}; + + auto time = AwsLevel3DataProvider::GetTimePointFromKey("???"); + + EXPECT_EQ(time, expectedTime); +} + +} // namespace provider +} // namespace scwx diff --git a/test/test.cmake b/test/test.cmake index d5c4042c..a1d4c1bf 100644 --- a/test/test.cmake +++ b/test/test.cmake @@ -13,7 +13,8 @@ set(SRC_AWIPS_TESTS source/scwx/awips/coded_location.test.cpp source/scwx/awips/pvtec.test.cpp source/scwx/awips/text_product_file.test.cpp) set(SRC_COMMON_TESTS source/scwx/common/color_table.test.cpp) -set(SRC_PROVIDER_TESTS source/scwx/provider/aws_level2_data_provider.test.cpp) +set(SRC_PROVIDER_TESTS source/scwx/provider/aws_level2_data_provider.test.cpp + source/scwx/provider/aws_level3_data_provider.test.cpp) set(SRC_QT_CONFIG_TESTS source/scwx/qt/config/radar_site.test.cpp) set(SRC_QT_MANAGER_TESTS source/scwx/qt/manager/settings_manager.test.cpp) set(SRC_UTIL_TESTS source/scwx/util/float.test.cpp diff --git a/wxdata/include/scwx/provider/aws_level3_data_provider.hpp b/wxdata/include/scwx/provider/aws_level3_data_provider.hpp new file mode 100644 index 00000000..5c307a2f --- /dev/null +++ b/wxdata/include/scwx/provider/aws_level3_data_provider.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include + +namespace scwx +{ +namespace provider +{ + +/** + * @brief AWS Level 3 Data Provider + */ +class AwsLevel3DataProvider : public AwsNexradDataProvider +{ +public: + explicit AwsLevel3DataProvider(const std::string& radarSite, + const std::string& product); + explicit AwsLevel3DataProvider(const std::string& radarSite, + const std::string& product, + const std::string& bucketName, + const std::string& region); + ~AwsLevel3DataProvider(); + + AwsLevel3DataProvider(const AwsLevel3DataProvider&) = delete; + AwsLevel3DataProvider& operator=(const AwsLevel3DataProvider&) = delete; + + AwsLevel3DataProvider(AwsLevel3DataProvider&&) noexcept; + AwsLevel3DataProvider& operator=(AwsLevel3DataProvider&&) noexcept; + + std::chrono::system_clock::time_point + GetTimePointByKey(const std::string& key) const; + + static std::chrono::system_clock::time_point + GetTimePointFromKey(const std::string& key); + +protected: + std::string GetPrefix(std::chrono::system_clock::time_point date); + +private: + class Impl; + std::unique_ptr p; +}; + +} // namespace provider +} // namespace scwx diff --git a/wxdata/include/scwx/provider/nexrad_data_provider_factory.hpp b/wxdata/include/scwx/provider/nexrad_data_provider_factory.hpp index fd49e07a..bdd51b9e 100644 --- a/wxdata/include/scwx/provider/nexrad_data_provider_factory.hpp +++ b/wxdata/include/scwx/provider/nexrad_data_provider_factory.hpp @@ -26,6 +26,10 @@ private: public: static std::shared_ptr CreateLevel2DataProvider(const std::string& radarSite); + + static std::shared_ptr + CreateLevel3DataProvider(const std::string& radarSite, + const std::string& product); }; } // namespace provider diff --git a/wxdata/source/scwx/provider/aws_level3_data_provider.cpp b/wxdata/source/scwx/provider/aws_level3_data_provider.cpp new file mode 100644 index 00000000..48410974 --- /dev/null +++ b/wxdata/source/scwx/provider/aws_level3_data_provider.cpp @@ -0,0 +1,107 @@ +#include +#include +#include +#include + +#include +#include + +namespace scwx +{ +namespace provider +{ + +static const std::string logPrefix_ = + "scwx::provider::aws_level3_data_provider"; +static const auto logger_ = util::Logger::Create(logPrefix_); + +static const std::string kDefaultBucketName_ = "unidata-nexrad-level3"; +static const std::string kDefaultRegion_ = "us-east-1"; + +class AwsLevel3DataProvider::Impl +{ +public: + explicit Impl(const std::string& radarSite, const std::string& product) : + radarSite_ {radarSite}, + siteId_ {common::GetSiteId(radarSite_)}, + product_ {product} + { + } + + ~Impl() {} + + std::string radarSite_; + std::string siteId_; + std::string product_; +}; + +AwsLevel3DataProvider::AwsLevel3DataProvider(const std::string& radarSite, + const std::string& product) : + AwsLevel3DataProvider( + radarSite, product, kDefaultBucketName_, kDefaultRegion_) +{ +} +AwsLevel3DataProvider::AwsLevel3DataProvider(const std::string& radarSite, + const std::string& product, + const std::string& bucketName, + const std::string& region) : + AwsNexradDataProvider(radarSite, bucketName, region), + p(std::make_unique(radarSite, product)) +{ +} +AwsLevel3DataProvider::~AwsLevel3DataProvider() = default; + +AwsLevel3DataProvider::AwsLevel3DataProvider(AwsLevel3DataProvider&&) noexcept = + default; +AwsLevel3DataProvider& +AwsLevel3DataProvider::operator=(AwsLevel3DataProvider&&) noexcept = default; + +std::string +AwsLevel3DataProvider::GetPrefix(std::chrono::system_clock::time_point date) +{ + return fmt::format( + "{0}_{1}_{2:%Y_%m_%d}_", p->siteId_, p->product_, fmt::gmtime(date)); +} + +std::chrono::system_clock::time_point +AwsLevel3DataProvider::GetTimePointByKey(const std::string& key) const +{ + return GetTimePointFromKey(key); +} + +std::chrono::system_clock::time_point +AwsLevel3DataProvider::GetTimePointFromKey(const std::string& key) +{ + std::chrono::system_clock::time_point time {}; + + constexpr size_t offset = 8; + + // Filename format is GGG_PPP_YYYY_MM_DD_HH_MM_SS + static constexpr size_t formatSize = + std::string("YYYY_MM_DD_HH_MM_SS").size(); + + if (key.size() >= offset + formatSize) + { + using namespace std::chrono; + + static const std::string timeFormat {"%Y_%m_%d_%H_%M_%S"}; + + std::string timeStr {key.substr(offset, formatSize)}; + std::istringstream in {timeStr}; + in >> parse(timeFormat, time); + + if (in.fail()) + { + logger_->warn("Invalid time: \"{}\"", timeStr); + } + } + else + { + logger_->warn("Time not parsable from key: \"{}\"", key); + } + + return time; +} + +} // namespace provider +} // namespace scwx diff --git a/wxdata/source/scwx/provider/nexrad_data_provider_factory.cpp b/wxdata/source/scwx/provider/nexrad_data_provider_factory.cpp index 58779ca6..5e75fd96 100644 --- a/wxdata/source/scwx/provider/nexrad_data_provider_factory.cpp +++ b/wxdata/source/scwx/provider/nexrad_data_provider_factory.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace scwx { @@ -16,5 +17,12 @@ NexradDataProviderFactory::CreateLevel2DataProvider( return std::make_unique(radarSite); } +std::shared_ptr +NexradDataProviderFactory::CreateLevel3DataProvider( + const std::string& radarSite, const std::string& product) +{ + return std::make_unique(radarSite, product); +} + } // namespace provider } // namespace scwx diff --git a/wxdata/wxdata.cmake b/wxdata/wxdata.cmake index b3852a84..c741b03a 100644 --- a/wxdata/wxdata.cmake +++ b/wxdata/wxdata.cmake @@ -35,10 +35,12 @@ set(SRC_COMMON source/scwx/common/color_table.cpp source/scwx/common/sites.cpp source/scwx/common/vcp.cpp) set(HDR_PROVIDER include/scwx/provider/aws_level2_data_provider.hpp + include/scwx/provider/aws_level3_data_provider.hpp include/scwx/provider/aws_nexrad_data_provider.hpp include/scwx/provider/nexrad_data_provider.hpp include/scwx/provider/nexrad_data_provider_factory.hpp) set(SRC_PROVIDER source/scwx/provider/aws_level2_data_provider.cpp + source/scwx/provider/aws_level3_data_provider.cpp source/scwx/provider/aws_nexrad_data_provider.cpp source/scwx/provider/nexrad_data_provider.cpp source/scwx/provider/nexrad_data_provider_factory.cpp)