satdump/plugins/firstparty_support/loader/aws.cpp

320 lines
13 KiB
C++
Raw Permalink Normal View History

2024-12-12 22:51:48 -05:00
#include "archive_loader.h"
2025-07-08 17:42:18 +01:00
#include "explorer/processing/processing.h"
2024-12-12 22:51:48 -05:00
#include "logger.h"
#include "main_ui.h"
2025-07-08 17:42:18 +01:00
#include "nlohmann/json.hpp"
#include "utils/http.h"
2025-05-26 19:21:10 +01:00
#include "utils/time.h"
2024-12-12 22:51:48 -05:00
#include "libs/rapidxml.hpp"
namespace satdump
{
2025-07-08 17:42:18 +01:00
void ArchiveLoader::queryAWS(std::string url_host, std::string url_path, std::string filter, void (ArchiveLoader::*parseTimestamp)(std::string, time_t &, std::string &))
2024-12-12 22:51:48 -05:00
{
std::string url_req = url_host + url_path;
std::string result;
2025-05-26 19:21:10 +01:00
while (satdump::perform_http_request(url_req, result) != 1)
2024-12-12 22:51:48 -05:00
{
// logger->trace("\nURL WAS : %s\n", url_req.c_str());
rapidxml::xml_document<> doc;
2025-07-08 17:42:18 +01:00
doc.parse<0>((char *)result.c_str());
2024-12-12 22:51:48 -05:00
if (doc.first_node("ListBucketResult") == 0)
throw satdump_exception("XML missing ListBucketResult!");
for (rapidxml::xml_node<> *content_node = doc.first_node("ListBucketResult")->first_node("Contents"); content_node; content_node = content_node->next_sibling())
{
std::string path = content_node->first_node("Key")->value();
std::string stem = std::filesystem::path(path).stem().string();
2024-12-23 10:29:39 -05:00
if (filter != "" && stem.find(filter) == std::string::npos)
continue;
2024-12-12 22:51:48 -05:00
time_t timestamp = 0;
std::string channel;
(this->*parseTimestamp)(stem, timestamp, channel);
2025-05-26 19:21:10 +01:00
aws_list[satdump::timestamp_to_string(timestamp)].insert(url_host + path);
2024-12-12 22:51:48 -05:00
}
// Continuation
if (doc.first_node("ListBucketResult")->first_node("NextContinuationToken") != nullptr)
{
std::string next_token = doc.first_node("ListBucketResult")->first_node("NextContinuationToken")->value();
logger->trace("Continuation token : " + next_token);
2025-07-08 17:42:18 +01:00
CURL *curl = curl_easy_init();
2024-12-12 22:51:48 -05:00
if (curl)
{
2025-07-08 17:42:18 +01:00
char *output = curl_easy_escape(curl, next_token.c_str(), next_token.length());
2024-12-12 22:51:48 -05:00
if (output)
{
url_req = url_host + url_path + "&continuation-token=" + std::string(output);
curl_free(output);
}
else
break;
curl_easy_cleanup(curl);
}
else
break;
result.clear();
continue;
}
break;
}
}
void ArchiveLoader::parseGOESTimestamp(std::string filename, time_t &timestamp, std::string &channel)
{
std::tm timeS;
int day_of_year = 0;
int channel_int = 0;
memset(&timeS, 0, sizeof(std::tm));
2025-07-08 17:42:18 +01:00
if (sscanf(filename.c_str(), "OR_ABI-L1b-Rad%*[FCM12]-M%*dC%2d_G%*d_s%4d%3d%2d%2d%2d%*d_e%*llu_c%*llu", &channel_int, &timeS.tm_year, &day_of_year, &timeS.tm_hour, &timeS.tm_min,
&timeS.tm_sec) == 6)
2024-12-12 22:51:48 -05:00
{
channel = std::to_string(channel_int);
timeS.tm_year -= 1900;
timestamp = timegm(&timeS) + day_of_year * 3600 * 24;
}
}
void ArchiveLoader::updateGOESRAWS()
{
try
{
aws_list.clear();
2025-01-06 11:30:09 -05:00
std::string year, dofy;
2024-12-12 22:51:48 -05:00
{
time_t tttime = request_time.get();
std::tm *timeReadable = gmtime(&tttime);
2025-01-06 11:30:09 -05:00
year = std::to_string(timeReadable->tm_year + 1900);
std::stringstream string_stream;
string_stream << std::setfill('0') << std::setw(3) << timeReadable->tm_yday + 1;
dofy = string_stream.str();
2024-12-12 22:51:48 -05:00
}
queryAWS(std::string("https://noaa-") + aws_options[aws_selected_dataset].satid + ".s3.amazonaws.com/",
2025-07-08 17:42:18 +01:00
std::string("?list-type=2&prefix=") + aws_options[aws_selected_dataset].pathid + "%2F" + year + "%2F" + dofy, aws_options[aws_selected_dataset].subpathid,
&ArchiveLoader::parseGOESTimestamp);
2024-12-12 22:51:48 -05:00
}
catch (std::exception &e)
{
logger->error("Error updating GOES Sector list! %s", e.what());
}
}
void ArchiveLoader::parseGK2ATimestamp(std::string filename, time_t &timestamp, std::string &channel)
{
std::tm timeS;
2025-07-08 17:42:18 +01:00
char channel_char[6] = {0};
2024-12-12 22:51:48 -05:00
memset(&timeS, 0, sizeof(std::tm));
2025-07-08 17:42:18 +01:00
if (sscanf(filename.c_str(), "gk2a_ami_le1b_%5c_%*5cge_%4d%2d%2d%2d%2d", channel_char, &timeS.tm_year, &timeS.tm_mon, &timeS.tm_mday, &timeS.tm_hour, &timeS.tm_min) == 6)
2024-12-12 22:51:48 -05:00
{
channel = std::string(channel_char);
timeS.tm_year -= 1900;
timeS.tm_mon--;
timestamp = timegm(&timeS);
}
}
void ArchiveLoader::updateGK2AAWS()
{
try
{
aws_list.clear();
std::string yearmonth, date;
{
std::stringstream string_stream;
time_t tttime = request_time.get();
struct tm timeinfo_struct;
#ifdef _WIN32
memcpy(&timeinfo_struct, gmtime(&tttime), sizeof(struct tm));
#else
gmtime_r(&tttime, &timeinfo_struct);
#endif
string_stream << timeinfo_struct.tm_year + 1900 << std::setfill('0') << std::setw(2) << timeinfo_struct.tm_mon + 1;
yearmonth = string_stream.str();
std::stringstream().swap(string_stream);
string_stream << std::setfill('0') << std::setw(2) << timeinfo_struct.tm_mday;
date = string_stream.str();
std::stringstream().swap(string_stream);
string_stream << std::setfill('0') << std::setw(2) << timeinfo_struct.tm_hour;
}
queryAWS(std::string("https://noaa-" + aws_options[aws_selected_dataset].satid + ".s3.amazonaws.com/"),
2025-07-08 17:42:18 +01:00
std::string("?list-type=2&prefix=AMI%2FL1B%2F" + aws_options[aws_selected_dataset].pathid + "%2F" + yearmonth + "%2F" + date), aws_options[aws_selected_dataset].subpathid,
&ArchiveLoader::parseGK2ATimestamp);
2024-12-12 22:51:48 -05:00
}
catch (std::exception &e)
{
logger->error("Error updating GK-2A Sector list! %s", e.what());
}
}
void ArchiveLoader::parseHimawariTimestamp(std::string filename, time_t &timestamp, std::string &channel)
{
std::tm timeS;
int channel_int;
2025-07-08 17:42:18 +01:00
char subtime[3] = {0};
char region[3] = {0};
memset(&timeS, 0, sizeof(std::tm));
2025-07-08 17:42:18 +01:00
if (sscanf(filename.c_str(), "HS_H%*d_%4d%2d%2d_%2d%2d_B%2d_%2c%2c_R%*2d_S%*4d.DAT", &timeS.tm_year, &timeS.tm_mon, &timeS.tm_mday, &timeS.tm_hour, &timeS.tm_min, &channel_int, region,
subtime) == 8)
{
channel = std::to_string(channel_int);
timeS.tm_year -= 1900;
timeS.tm_mon--;
timestamp = timegm(&timeS);
// Himawari does not have the actual time listed in the file name - it is rounded to the last 10 minutes
// Estimate the correct time
int subtime_int = strtol(subtime, nullptr, 10);
if (subtime_int != 0)
{
if (region[0] == 'J')
timestamp += (subtime_int - 1) * 150;
else
timestamp += subtime_int * 150;
}
}
}
void ArchiveLoader::updateHimawariAWS()
{
try
{
aws_list.clear();
std::string year, month, date;
std::stringstream string_stream;
{
time_t tttime = request_time.get();
struct tm timeinfo_struct;
#ifdef _WIN32
memcpy(&timeinfo_struct, gmtime(&tttime), sizeof(struct tm));
#else
gmtime_r(&tttime, &timeinfo_struct);
#endif
year = std::to_string(timeinfo_struct.tm_year + 1900);
string_stream << std::setfill('0') << std::setw(2) << timeinfo_struct.tm_mon + 1;
month = string_stream.str();
std::stringstream().swap(string_stream);
string_stream << std::setfill('0') << std::setw(2) << timeinfo_struct.tm_mday;
date = string_stream.str();
std::stringstream().swap(string_stream);
}
queryAWS(std::string("https://noaa-" + aws_options[aws_selected_dataset].satid + ".s3.amazonaws.com/"),
2025-07-08 17:42:18 +01:00
std::string("?list-type=2&prefix=" + aws_options[aws_selected_dataset].pathid + "%2F" + year + "%2F" + month + "%2F" + date), aws_options[aws_selected_dataset].subpathid,
&ArchiveLoader::parseHimawariTimestamp);
}
2025-07-08 17:42:18 +01:00
catch (std::exception &e)
{
logger->error("Error updating Himawari Sector list! %s", e.what());
}
}
2024-12-12 22:51:48 -05:00
void ArchiveLoader::renderAWS(ImVec2 wsize)
{
2025-07-08 17:42:18 +01:00
bool should_disable = file_downloader.is_busy();
2024-12-12 22:51:48 -05:00
if (should_disable)
style::beginDisabled();
if (ImGui::BeginCombo("##archiveloader_dataset", aws_options[aws_selected_dataset].name.c_str()))
{
for (int i = 0; i < (int)aws_options.size(); i++)
{
if (ImGui::Selectable(aws_options[i].name.c_str(), aws_selected_dataset == i))
{
aws_selected_dataset = i;
(this->*(aws_options[aws_selected_dataset].updateFunc))();
}
}
ImGui::EndCombo();
}
ImGui::SameLine();
if (ImGui::Button("Refresh##archiveloader_refresh"))
(this->*(aws_options[aws_selected_dataset].updateFunc))();
ImGui::TextUnformatted("Date: ");
ImGui::SameLine();
request_time.draw();
ImGui::SameLine();
if (ImGui::Button("Current##archiveloader_setcurrenttime"))
request_time.set(time(0));
float target_height = wsize.y - 260 * ui_scale;
2025-07-08 17:42:18 +01:00
ImGui::BeginChild("##archiveloader_subwindow", {ImGui::GetContentRegionAvail().x, target_height < 5 * ui_scale ? 5 * ui_scale : target_height}, false,
ImGuiWindowFlags_AlwaysVerticalScrollbar);
2024-12-12 22:51:48 -05:00
if (ImGui::BeginTable("##archiveloadertable", 2, ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
{
ImGui::TableSetupColumn("##archiveloadertable_name", ImGuiTableColumnFlags_None);
ImGui::TableSetupColumn("##archiveloadertable_butt", ImGuiTableColumnFlags_None);
for (auto &str : aws_list)
{
ImGui::TableNextColumn();
ImGui::Text("%s", str.first.c_str());
2024-12-12 22:51:48 -05:00
ImGui::TableNextColumn();
if (ImGui::Button(std::string("Load##archiveloadertablebutton_" + str.first).c_str()))
2024-12-12 22:51:48 -05:00
{
auto func = [this, str](int)
{
try
{
std::string l_download_path;
bool success = true;
for (auto &c : str.second)
2024-12-12 22:51:48 -05:00
{
2025-07-08 17:42:18 +01:00
std::string download_path = products_download_and_process_directory + "/" + std::filesystem::path(c).stem().string() + std::filesystem::path(c).extension().string();
2024-12-12 22:51:48 -05:00
if (file_downloader.download_file(c, download_path, "") == 1)
2024-12-12 22:51:48 -05:00
{
success = false;
break;
}
l_download_path = download_path;
}
2025-07-08 17:42:18 +01:00
std::string process_path = (download_location && output_selection.isValid() ? output_selection.getPath() : products_download_and_process_directory) + "/" +
std::filesystem::path(l_download_path).stem().string();
2024-12-12 22:51:48 -05:00
if (success)
2025-07-14 21:21:55 +02:00
eventBus->fire_event<explorer::ExplorerAddHandlerEvent>(
2025-07-13 14:40:31 +02:00
{std::make_shared<handlers::OffProcessingHandler>("off2pro", "file", l_download_path, process_path, nlohmann::json()), true, true});
2024-12-12 22:51:48 -05:00
}
catch (std::exception &e)
{
logger->error("Failed downloading file from AWS! %s", e.what());
}
};
ui_thread_pool.push(func);
}
}
ImGui::EndTable();
}
ImGui::EndChild();
if (should_disable)
style::endDisabled();
}
2025-07-08 17:42:18 +01:00
} // namespace satdump