use crate::types::{Sector, WeatherCondition};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WeatherForecast {
pub overall_condition: WeatherCondition,
pub air_temperature: f32,
pub track_temperature: f32,
pub humidity: f32,
pub wind_speed: f32,
pub wind_direction: f32,
pub rain_probability: f32,
pub rainfall_intensity: f32,
pub sector_conditions: Vec<SectorWeather>,
pub predictions: Vec<WeatherPrediction>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SectorWeather {
pub sector: Sector,
pub condition: WeatherCondition,
pub rain_intensity: f32, pub track_temp: f32, pub grip_level: f32, }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WeatherPrediction {
pub minutes_ahead: u16,
pub condition: WeatherCondition,
pub rain_probability: f32,
pub confidence: f32,
}
impl WeatherForecast {
pub fn has_rain_anywhere(&self) -> bool {
self.sector_conditions.iter().any(|s| s.rain_intensity > 0.0)
}
pub fn max_rain_intensity(&self) -> f32 {
self.sector_conditions
.iter()
.map(|s| s.rain_intensity)
.fold(0.0, f32::max)
}
pub fn average_grip_level(&self) -> f32 {
let sum: f32 = self.sector_conditions.iter().map(|s| s.grip_level).sum();
sum / self.sector_conditions.len() as f32
}
pub fn rain_expected_in(&self, minutes: u16) -> bool {
self.predictions
.iter()
.filter(|p| p.minutes_ahead <= minutes)
.any(|p| p.rain_probability > 0.5)
}
pub fn recommended_compound(&self) -> RecommendedTire {
if self.max_rain_intensity() > 5.0 {
RecommendedTire::Wet
} else if self.max_rain_intensity() > 0.5 {
RecommendedTire::Intermediate
} else if self.rain_expected_in(10) && self.rain_probability > 0.7 {
RecommendedTire::Intermediate
} else {
RecommendedTire::Dry
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RecommendedTire {
Dry,
Intermediate,
Wet,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_weather_forecast() {
let forecast = WeatherForecast {
overall_condition: WeatherCondition::LightRain,
air_temperature: 18.5,
track_temperature: 22.0,
humidity: 0.85,
wind_speed: 15.0,
wind_direction: 270.0,
rain_probability: 0.75,
rainfall_intensity: 2.5,
sector_conditions: vec![
SectorWeather {
sector: Sector::Sector1,
condition: WeatherCondition::LightRain,
rain_intensity: 2.0,
track_temp: 22.0,
grip_level: 0.65,
},
SectorWeather {
sector: Sector::Sector2,
condition: WeatherCondition::LightRain,
rain_intensity: 3.0,
track_temp: 21.5,
grip_level: 0.60,
},
SectorWeather {
sector: Sector::Sector3,
condition: WeatherCondition::Dry,
rain_intensity: 0.0,
track_temp: 23.0,
grip_level: 0.85,
},
],
predictions: vec![
WeatherPrediction {
minutes_ahead: 5,
condition: WeatherCondition::LightRain,
rain_probability: 0.8,
confidence: 0.9,
},
WeatherPrediction {
minutes_ahead: 15,
condition: WeatherCondition::HeavyRain,
rain_probability: 0.9,
confidence: 0.75,
},
],
};
assert!(forecast.has_rain_anywhere());
assert_eq!(forecast.max_rain_intensity(), 3.0);
assert!((forecast.average_grip_level() - 0.7).abs() < 0.01);
assert!(forecast.rain_expected_in(10));
assert_eq!(forecast.recommended_compound(), RecommendedTire::Intermediate);
}
}