diff --git a/.env b/.env index 79e78c6..67008e3 100644 --- a/.env +++ b/.env @@ -28,7 +28,7 @@ MINIO_BROWSER_REDIRECT_URL=${NGINX_PROTOCOL}://${NGINX_HOST}:${NGINX_HTTP_PORT}/ UI_PORT=3000 API_PORT=5000 -API_METAR_TIME_OFFSET=3000 +API_METAR_TIME_OFFSET=1800 SSL_CA_NAME=ca SSL_CA_PATH=../ssl/${SSL_CA_NAME}.pem diff --git a/api/src/airports/model/airport.rs b/api/src/airports/model/airport.rs index 0bc28da..cf9c64c 100644 --- a/api/src/airports/model/airport.rs +++ b/api/src/airports/model/airport.rs @@ -214,7 +214,7 @@ impl Airport { let metar_fut = async { if metar { - match Metar::find_all(client, &vec![icao.to_string()], &false).await { + match Metar::find_all_distinct(client, &vec![icao.to_string()]).await { Ok(m) => Some(m.into_iter().nth(0)), Err(err) => { log::error!("{}", err); @@ -345,7 +345,7 @@ impl Airport { let runway_future = Runway::select_all_map(icaos.clone()); let frequency_future = Frequency::select_all_map(icaos.clone()); let metar_future = if query.metars.unwrap_or(false) { - Some(Metar::find_all(client, &icaos, &false)) + Some(Metar::find_all_distinct(client, &icaos)) } else { None }; diff --git a/api/src/metars/metar_check.rs b/api/src/metars/metar_check.rs index 124ea73..f4ed940 100644 --- a/api/src/metars/metar_check.rs +++ b/api/src/metars/metar_check.rs @@ -3,20 +3,31 @@ use redis::{AsyncCommands, RedisResult}; use serde::{Deserialize, Serialize}; use crate::db::redis_async_connection; use crate::error::ApiResult; +use crate::metars::Metar; #[derive(Debug, Serialize, Deserialize)] pub struct MetarCheck { pub icao: String, pub status: bool, pub updated_at: DateTime, + pub last_metar: Option, } impl MetarCheck { - pub fn new(icao: String, status: bool) -> Self { - Self { - icao, - status, - updated_at: Utc::now(), + pub async fn new(icao: String, status: bool) -> Self { + match Self::get(&icao).await { + Some(c) => Self { + icao, + status, + updated_at: Utc::now(), + last_metar: c.last_metar, + }, + None => Self { + icao, + status, + updated_at: Utc::now(), + last_metar: None, + } } } diff --git a/api/src/metars/model.rs b/api/src/metars/model.rs index 2e3996a..0924642 100644 --- a/api/src/metars/model.rs +++ b/api/src/metars/model.rs @@ -14,7 +14,7 @@ use crate::metars::MetarCheck; const TABLE_NAME: &str = "metars"; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Metar { pub icao: String, pub raw_text: String, @@ -60,7 +60,7 @@ pub struct Metar { pub density_altitude: Option, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum ReportModifier { #[serde(rename = "AUTO")] Auto, @@ -88,7 +88,7 @@ impl Display for ReportModifier { } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct RunwayVisualRange { pub runway: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -110,7 +110,7 @@ impl Default for RunwayVisualRange { } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum AutomatedStationType { #[serde(rename = "AO1")] WithoutPrecipitationDiscriminator, @@ -141,7 +141,7 @@ impl Display for AutomatedStationType { } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Remarks { #[serde(skip_serializing_if = "Option::is_none")] pub peak_wind: Option, @@ -165,7 +165,7 @@ pub struct Remarks { pub sky_condition_at_secondary_location_not_available: Option, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct PeakWind { pub degrees: i32, pub speed: i32, @@ -190,7 +190,7 @@ impl Default for Remarks { } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct SkyCondition { pub sky_cover: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -209,7 +209,7 @@ impl Default for SkyCondition { } } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum FlightCategory { VFR, MVFR, @@ -993,10 +993,9 @@ impl Metar { }) } - pub async fn find_all( + pub async fn find_all_distinct( client: &Client, - icao_list: &Vec, - _force: &bool, + icao_list: &Vec ) -> ApiResult> { if icao_list.is_empty() { return Ok(Vec::new()); @@ -1017,9 +1016,9 @@ impl Metar { let current_time = Utc::now().timestamp(); let time_offset = env::var("API_METAR_TIME_OFFSET") - .unwrap_or("3000".to_string()) + .unwrap_or("1800".to_string()) .parse::() - .unwrap_or(3000); + .unwrap_or(1800); let short_time_offset: i64 = 300; // Setup metars and missing metar structures @@ -1042,8 +1041,8 @@ impl Metar { }; // If the metar was cached more than short_time_offset minutes ago, refresh it if refresh_seconds >= short_time_offset { - log::trace!("{} METAR data is outdated, refreshing now", &icao); - missing_metar_icaos.push(icao); + log::trace!("{} METAR data is outdated, marked for refresh", &icao); + missing_metar_icaos.push(icao.clone()); } // Otherwise return outdated data and wait else { @@ -1058,7 +1057,7 @@ impl Metar { // Otherwise add the metar to the vector else { found_metar_icaos.insert(icao.clone()); - let metar_check = MetarCheck::new(icao, true); + let metar_check = MetarCheck::new(icao, true).await; metar_check.insert(time_offset as u64).await?; metars.push(Metar::from_db(metar_row)?); } @@ -1092,21 +1091,29 @@ impl Metar { if remote_metars.len() > 0 { // Insert missing METARs - for remote_metar in &remote_metars { - found_metar_icaos.insert(remote_metar.icao.to_string()); - let metar_check = MetarCheck::new(remote_metar.icao.clone(), true); - metar_check.insert(time_offset as u64).await?; + for remote_metar in remote_metars.clone() { remote_metar.insert().await?; + found_metar_icaos.insert(remote_metar.icao.to_string()); + let mut metar_check = MetarCheck::new(remote_metar.icao.clone(), true).await; + metar_check.last_metar = Some(remote_metar); + metar_check.insert(time_offset as u64).await?; } - metars.append(&mut remote_metars) + metars.append(&mut remote_metars); } // Update still missing metars - let mut still_missing_metar_icaos: Vec = vec![]; + // let mut still_missing_metar_icaos: Vec = vec![]; for difference in found_metar_icaos.symmetric_difference(&requested_icaos) { - still_missing_metar_icaos.push(difference.to_string()); - let metar_check = MetarCheck::new(difference.to_string(), false); - metar_check.insert(short_time_offset as u64).await? + // still_missing_metar_icaos.push(difference.to_string()); + let metar_check = MetarCheck::new(difference.to_string(), false).await; + metar_check.insert(short_time_offset as u64).await?; + // Only add cached metar data if it's less than 4 hours old + if let Some(last_metar) = metar_check.last_metar { + let four_hours_ago = Utc::now() - chrono::Duration::hours(4); + if last_metar.observation_time < four_hours_ago { + metars.push(last_metar); + } + } } // if !still_missing_metar_icaos.is_empty() { // log::trace!("Still missing METAR data from {:?}", still_missing_metar_icaos); diff --git a/api/src/metars/routes.rs b/api/src/metars/routes.rs index fe8dbea..7218ed7 100644 --- a/api/src/metars/routes.rs +++ b/api/src/metars/routes.rs @@ -7,7 +7,6 @@ use crate::AppState; #[derive(Debug, Serialize, Deserialize)] struct FindAllParameters { icaos: Option, - force: Option, } #[get("metars")] @@ -19,10 +18,9 @@ async fn find_all(data: web::Data, req: HttpRequest) -> HttpResponse { None => return HttpResponse::UnprocessableEntity().body("Missing icaos parameter"), }; let icaos: Vec = icao_string.split(',').map(|s| s.to_string()).collect(); - let force = ¶meters.force.unwrap_or(false); let client = &data.client; - let metars = match Metar::find_all(client, &icaos, force).await { + let metars = match Metar::find_all_distinct(client, &icaos).await { Ok(a) => a, Err(err) => { error!("{}", err);