首尔/釜山跑道数据: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; use tracing_subscriber;
// ── city config ── // ── city config ──
// (key, zh_name, en_name, icao, airport_en, utc_offset_hours, threshold, tz_abbr) // (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)] = &[ const CITIES: &[(&str, &str, &str, &str, &str, i32, f64, &str, usize)] = &[
("seoul", "首尔", "Seoul", "RKSI", "Incheon", 9, 3.0, "KST"), ("seoul", "首尔", "Seoul", "RKSI", "Incheon", 9, 3.0, "KST", 2),
("busan", "釜山", "Busan", "RKPK", "Gimhae", 9, 2.0, "KST"), ("busan", "釜山", "Busan", "RKPK", "Gimhae", 9, 2.0, "KST", 2),
("tokyo", "东京", "Tokyo", "44166", "Haneda", 9, 2.0, "JST"), ("tokyo", "东京", "Tokyo", "44166", "Haneda", 9, 2.0, "JST", 0),
("ankara", "安卡拉", "Ankara", "17128", "Esenboğa", 3, 3.0, "TRT"), ("ankara", "安卡拉", "Ankara", "17128", "Esenboğa", 3, 3.0, "TRT", 0),
("helsinki", "赫尔辛基", "Helsinki", "EFHK", "Vantaa", 3, 2.0, "EEST"), ("helsinki", "赫尔辛基", "Helsinki", "EFHK", "Vantaa", 3, 2.0, "EEST", 0),
("amsterdam","阿姆斯特丹","Amsterdam","EHAM","Schiphol",2,2.0,"CEST"), ("amsterdam","阿姆斯特丹","Amsterdam","EHAM","Schiphol",2,2.0,"CEST", 0),
("istanbul","伊斯坦布尔","Istanbul","17058","Airport",3,3.0,"TRT"), ("istanbul","伊斯坦布尔","Istanbul","17058","Airport",3,3.0,"TRT", 0),
("paris", "巴黎", "Paris", "LFPB", "Le Bourget", 2, 3.0, "CEST"), ("paris", "巴黎", "Paris", "LFPB", "Le Bourget", 2, 3.0, "CEST", 0),
("hong kong","香港","Hong Kong","HKO","Observatory",8,1.5,"HKT"), ("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"), ("lau fau shan","流浮山","Lau Fau Shan","LFS","Lau Fau Shan",8,1.5,"HKT", 0),
("taipei", "台北", "Taipei", "466920", "Songshan", 8, 1.5, "TST"), ("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 ── // ── app state ──
@@ -49,7 +55,7 @@ struct MonitorTemplate {
// ── data loading ── // ── 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 now_utc = Utc::now();
let local = now_utc + chrono::Duration::hours(*tz as i64); let local = now_utc + chrono::Duration::hours(*tz as i64);
let local_time = format!("{} {}", local.format("%H:%M"), tz_abbr); 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, _ => 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 { CitySnapshot {
name: key.to_string(), name: key.to_string(),
en_name: en.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, max_time: None,
trend, trend,
new_high, new_high,
runway_pairs: vec![], runway_pairs,
gap: None, gap: None,
threshold: *thresh, threshold: *thresh,
time_ok: false, 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> { fn load_all_cities(db_path: &str) -> Vec<CitySnapshot> {
CITIES CITIES
.iter() .iter()
.map(|c| load_city_snapshot(db_path, c)) .enumerate()
.map(|(i, c)| load_city_snapshot(db_path, i, c))
.collect() .collect()
} }
+12
View File
@@ -1033,6 +1033,18 @@ class WeatherDataCollector(OpenMeteoCacheMixin, SettlementSourceMixin, MetarSour
pressure_hpa=amos_data.get("pressure_hpa"), pressure_hpa=amos_data.get("pressure_hpa"),
obs_time=amos_data.get("observation_time") or datetime.now().isoformat(), 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: except Exception:
logger.exception("airport_obs_log append failed for amos city={}", city_lower) logger.exception("airport_obs_log append failed for amos city={}", city_lower)
else: else: