From 067ee63ba4587d17ddfa13eeec23e28ae4f0c65e Mon Sep 17 00:00:00 2001 From: "2569718930@qq.com" <2569718930@qq.com> Date: Wed, 13 May 2026 20:39:15 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E5=B0=94/=E9=87=9C=E5=B1=B1=E8=B7=91?= =?UTF-8?q?=E9=81=93=E6=95=B0=E6=8D=AE=EF=BC=9APython=20=E5=AD=98=20RKSI?= =?UTF-8?q?=5FRWY=5F0/1=EF=BC=8CRust=20=E6=9F=A5=E8=AF=A2=E5=B1=95?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - weather_sources: AMOS 采集时每条跑道单独写 airport_obs_log - Rust: 按 RKSI_RWY_0、RKSI_RWY_1 等 icao 查询跑道温度 - 跑道标签 18L/36R、18R/36L 写死在 config 中 --- monitoring-web/src/main.rs | 52 ++++++++++++++++++-------- src/data_collection/weather_sources.py | 12 ++++++ 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/monitoring-web/src/main.rs b/monitoring-web/src/main.rs index be28eaf9..3bd1acfe 100644 --- a/monitoring-web/src/main.rs +++ b/monitoring-web/src/main.rs @@ -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 { CITIES .iter() - .map(|c| load_city_snapshot(db_path, c)) + .enumerate() + .map(|(i, c)| load_city_snapshot(db_path, i, c)) .collect() } diff --git a/src/data_collection/weather_sources.py b/src/data_collection/weather_sources.py index 1b0783a7..632f1e67 100644 --- a/src/data_collection/weather_sources.py +++ b/src/data_collection/weather_sources.py @@ -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: