116 lines
5.2 KiB
C++
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>();
|
|
}
|