首尔/釜山跑道数据:Python 存 RKSI_RWY_0/1,Rust 查询展示

- weather_sources: AMOS 采集时每条跑道单独写 airport_obs_log
- Rust: 按 RKSI_RWY_0、RKSI_RWY_1 等 icao 查询跑道温度
- 跑道标签 18L/36R、18R/36L 写死在 config 中
This commit is contained in:
2569718930@qq.com
2026-05-13 20:39:15 +08:00
parent 88918677c0
commit 067ee63ba4
2 changed files with 48 additions and 16 deletions
+36 -16
View File
@@ -17,19 +17,25 @@ use tower_http::services::ServeDir;
use tracing_subscriber;
// ── city config ──
// (key, zh_name, en_name, icao, airport_en, utc_offset_hours, threshold, tz_abbr)
const CITIES: &[(&str, &str, &str, &str, &str, i32, f64, &str)] = &[
("seoul", "首尔", "Seoul", "RKSI", "Incheon", 9, 3.0, "KST"),
("busan", "釜山", "Busan", "RKPK", "Gimhae", 9, 2.0, "KST"),
("tokyo", "东京", "Tokyo", "44166", "Haneda", 9, 2.0, "JST"),
("ankara", "安卡拉", "Ankara", "17128", "Esenboğa", 3, 3.0, "TRT"),
("helsinki", "赫尔辛基", "Helsinki", "EFHK", "Vantaa", 3, 2.0, "EEST"),
("amsterdam","阿姆斯特丹","Amsterdam","EHAM","Schiphol",2,2.0,"CEST"),
("istanbul","伊斯坦布尔","Istanbul","17058","Airport",3,3.0,"TRT"),
("paris", "巴黎", "Paris", "LFPB", "Le Bourget", 2, 3.0, "CEST"),
("hong kong","香港","Hong Kong","HKO","Observatory",8,1.5,"HKT"),
("lau fau shan","流浮山","Lau Fau Shan","LFS","Lau Fau Shan",8,1.5,"HKT"),
("taipei", "台北", "Taipei", "466920", "Songshan", 8, 1.5, "TST"),
// (key, zh_name, en_name, icao, airport_en, utc_offset_hours, threshold, tz_abbr, runway_count)
const CITIES: &[(&str, &str, &str, &str, &str, i32, f64, &str, usize)] = &[
("seoul", "首尔", "Seoul", "RKSI", "Incheon", 9, 3.0, "KST", 2),
("busan", "釜山", "Busan", "RKPK", "Gimhae", 9, 2.0, "KST", 2),
("tokyo", "东京", "Tokyo", "44166", "Haneda", 9, 2.0, "JST", 0),
("ankara", "安卡拉", "Ankara", "17128", "Esenboğa", 3, 3.0, "TRT", 0),
("helsinki", "赫尔辛基", "Helsinki", "EFHK", "Vantaa", 3, 2.0, "EEST", 0),
("amsterdam","阿姆斯特丹","Amsterdam","EHAM","Schiphol",2,2.0,"CEST", 0),
("istanbul","伊斯坦布尔","Istanbul","17058","Airport",3,3.0,"TRT", 0),
("paris", "巴黎", "Paris", "LFPB", "Le Bourget", 2, 3.0, "CEST", 0),
("hong kong","香港","Hong Kong","HKO","Observatory",8,1.5,"HKT", 0),
("lau fau shan","流浮山","Lau Fau Shan","LFS","Lau Fau Shan",8,1.5,"HKT", 0),
("taipei", "台北", "Taipei", "466920", "Songshan", 8, 1.5, "TST", 0),
];
// 跑道标签(与 AMOS scrape 返回的一致)
const RUNWAY_LABELS: &[&[&str]] = &[
&["18L/36R", "18R/36L"], // seoul
&["18L/36R", "18R/36L"], // busan
];
// ── app state ──
@@ -49,7 +55,7 @@ struct MonitorTemplate {
// ── data loading ──
fn load_city_snapshot(db_path: &str, (key, _zh, en, icao, airport, tz, thresh, tz_abbr): &(&str, &str, &str, &str, &str, i32, f64, &str)) -> CitySnapshot {
fn load_city_snapshot(db_path: &str, idx: usize, (key, _zh, en, icao, airport, tz, thresh, tz_abbr, rw_count): &(&str, &str, &str, &str, &str, i32, f64, &str, usize)) -> CitySnapshot {
let now_utc = Utc::now();
let local = now_utc + chrono::Duration::hours(*tz as i64);
let local_time = format!("{} {}", local.format("%H:%M"), tz_abbr);
@@ -84,6 +90,19 @@ fn load_city_snapshot(db_path: &str, (key, _zh, en, icao, airport, tz, thresh, t
_ => false,
};
// Runway data: query each runway ICAO
let mut runway_pairs: Vec<(String, f64)> = vec![];
if *rw_count > 0 && idx < RUNWAY_LABELS.len() {
for i in 0..*rw_count {
let rw_icao = format!("{}_RWY_{}", icao, i);
let rw_obs = db::get_recent_obs(db_path, &rw_icao, 120, 1);
if let Some(rw_temp) = rw_obs.first().and_then(|o| o.temp_c) {
let label = RUNWAY_LABELS[idx].get(i).unwrap_or(&"?").to_string();
runway_pairs.push((label, rw_temp));
}
}
}
CitySnapshot {
name: key.to_string(),
en_name: en.to_string(),
@@ -95,7 +114,7 @@ fn load_city_snapshot(db_path: &str, (key, _zh, en, icao, airport, tz, thresh, t
max_time: None,
trend,
new_high,
runway_pairs: vec![],
runway_pairs,
gap: None,
threshold: *thresh,
time_ok: false,
@@ -110,7 +129,8 @@ fn load_city_snapshot(db_path: &str, (key, _zh, en, icao, airport, tz, thresh, t
fn load_all_cities(db_path: &str) -> Vec<CitySnapshot> {
CITIES
.iter()
.map(|c| load_city_snapshot(db_path, c))
.enumerate()
.map(|(i, c)| load_city_snapshot(db_path, i, c))
.collect()
}
+12
View File
@@ -1033,6 +1033,18 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour
pressure_hpa=amos_data.get("pressure_hpa"),
obs_time=amos_data.get("observation_time") or datetime.now().isoformat(),
)
# 也存每条跑道的数据,用 RKSI_RWY_0 / RKSI_RWY_1 做 icao
runway_obs = amos_data.get("runway_obs") or {}
rw_pairs = runway_obs.get("runway_pairs") or []
rw_temps = runway_obs.get("temperatures") or []
for i, (pair, (t, _d)) in enumerate(zip(rw_pairs, rw_temps)):
if t is not None and i < 4:
DBManager().append_airport_obs(
icao=f"{amos_data.get('icao', '')}_RWY_{i}",
city=city_lower,
temp_c=t,
obs_time=amos_data.get("observation_time") or datetime.now().isoformat(),
)
except Exception:
logger.exception("airport_obs_log append failed for amos city={}", city_lower)
else: