Files
ECAP_Server/module/Local_Server/External_Database/Faa_Aircraft_Registry.cpp
T
2026-06-16 11:18:22 +08:00

116 lines
5.2 KiB
C++

// 数据来源:FAA Aircraft Registry。美国 FAA 官方注册库,每日更新,MASTER.txt 包含 Mode-S Hex。
// 链接:https://www.faa.gov/licenses_certificates/aircraft_certification/aircraft_registry/releasable_aircraft_download
// 下载:https://registry.faa.gov/database/ReleasableAircraft.zip
// 官方更新要求:FAA 下载页说明文件每日刷新。本地告警策略:超过 24 小时提示更新。
#include "../server/libarchive.h"
#include "Resource_Utils.h"
#include <SQLiteCpp/Transaction.h>
#include <fstream>
#include <stdexcept>
namespace {
const std::vector<std::string> kFaaColumns = {
"n_number", "serial_number", "aircraft_mfr_model_code", "engine_mfr_model_code",
"year_mfr", "registrant_type", "name", "street", "street2", "city", "state",
"zip_code", "region", "county", "country", "last_action_date", "cert_issue_date",
"certification", "type_aircraft", "type_engine", "status_code", "mode_s_code",
"fractional_ownership", "airworthiness_date", "other_names_1", "other_names_2",
"other_names_3", "other_names_4", "other_names_5", "expiration_date",
"unique_id", "kit_mfr", "kit_model", "icao24"
};
const std::vector<std::string> kFaaPrimaryKey = {"icao24"};
class Faa_Aircraft_Registry_Resource final : public External_Resources {
public:
Faa_Aircraft_Registry_Resource()
: External_Resources("faa_aircraft_registry",
"faa_registry/MASTER.txt",
"https://registry.faa.gov/database/ReleasableAircraft.zip",
"FAA 官方可公开下载的美国飞机注册库,MASTER.txt 包含 Mode-S Hex 注册信息。",
86400) {}
void create_table(SQLite::Database& db) const override {
db.exec(R"sql(
CREATE TABLE IF NOT EXISTS faa_aircraft_registry (
n_number TEXT, serial_number TEXT, aircraft_mfr_model_code TEXT,
engine_mfr_model_code TEXT, year_mfr TEXT, registrant_type TEXT,
name TEXT, street TEXT, street2 TEXT, city TEXT, state TEXT,
zip_code TEXT, region TEXT, county TEXT, country TEXT,
last_action_date TEXT, cert_issue_date TEXT, certification TEXT,
type_aircraft TEXT, type_engine TEXT, status_code TEXT,
mode_s_code TEXT, fractional_ownership TEXT, airworthiness_date TEXT,
other_names_1 TEXT, other_names_2 TEXT, other_names_3 TEXT,
other_names_4 TEXT, other_names_5 TEXT, expiration_date TEXT,
unique_id TEXT, kit_mfr TEXT, kit_model TEXT,
icao24 TEXT NOT NULL PRIMARY KEY
);
)sql");
}
protected:
bool fetch(const std::filesystem::path& cache_pos,
const bool force_download) const override {
const auto master_file = source_file(cache_pos);
if (!force_download) return false;
const auto zip_file = cache_pos / "ReleasableAircraft.zip";
External_Database_Utils::download_to_file(download_url, zip_file);
unarchive_file(zip_file, cache_pos / "faa_registry");
return true;
}
std::size_t import_file(SQLite::Database& db,
const std::filesystem::path& source_file) const override {
auto master_file = source_file;
if (source_file.extension() == ".zip") {
const auto output_dir = source_file.parent_path() / "faa_registry";
unarchive_file(source_file, output_dir);
master_file = output_dir / "MASTER.txt";
}
std::ifstream input(master_file, std::ios::binary);
if (!input) throw std::runtime_error("Cannot open FAA MASTER.txt: " +
master_file.string());
SQLite::Transaction transaction(db);
db.exec("DELETE FROM faa_aircraft_registry");
SQLite::Statement statement(
db, External_Database_Utils::make_upsert_sql(
name, kFaaColumns, kFaaPrimaryKey));
std::vector<std::string> fields;
External_Database_Utils::read_csv_row(input, fields, ','); // 跳过表头。
std::size_t row_count = 0;
while (External_Database_Utils::read_csv_row(input, fields, ',')) {
if (fields.size() < kFaaColumns.size()) fields.resize(kFaaColumns.size());
const auto icao24 = External_Database_Utils::normalize_key(fields[33]);
if (icao24.empty()) continue;
statement.reset();
statement.clearBindings();
for (std::size_t index = 0; index < kFaaColumns.size(); ++index) {
auto value = External_Database_Utils::normalize_field(fields[index]);
if (index == 33) value = icao24;
External_Database_Utils::bind_optional(
statement, static_cast<int>(index + 1), value);
}
statement.exec();
++row_count;
}
transaction.commit();
return row_count;
}
[[nodiscard]] const std::vector<std::string>& primary_key_columns() const override {
return kFaaPrimaryKey;
}
};
} // 命名空间
std::unique_ptr<External_Resources> make_faa_aircraft_registry_resource() {
return std::make_unique<Faa_Aircraft_Registry_Resource>();
}