diff --git a/src/analysis.rs b/src/analysis.rs index dc28bc3..a710fa2 100644 --- a/src/analysis.rs +++ b/src/analysis.rs @@ -16,7 +16,7 @@ use crate::{ image_processing::{self, extract_and_filter, hash_image, to_png_bytes, Region}, learned_tracks::get_track_hash, ocr_db::OcrDatabase, - state::{AppState, DebugOcrFrame, LapState, RaceState, SharedAppState}, + state::{AppState, DebugOcrFrame, LapState, RaceState, SharedAppState, LapMetrics}, }; fn check_penalty(image: &RgbImage, config: &Config) -> bool { @@ -98,6 +98,7 @@ fn handle_new_frame(state: &mut AppState, lap_state: LapState, image: &RgbImage) if state.current_race.is_none() { crate::tts::pre_race_summary(&state.tts, state.config.as_ref(), &lap_state); state.penalties_this_lap = 0; + state.current_lap_metrics = LapMetrics::default(); let track_hash = get_track_hash(state.config.as_ref(), image); let track_name = state @@ -124,6 +125,8 @@ fn handle_new_frame(state: &mut AppState, lap_state: LapState, image: &RgbImage) state.current_race = Some(race); } + state.current_lap_metrics.record(&lap_state); + if check_penalty(image, state.config.as_ref()) { if !state.detecting_penalty { state.penalties_this_lap += 1; @@ -147,6 +150,9 @@ fn handle_new_frame(state: &mut AppState, lap_state: LapState, image: &RgbImage) if let Some(lap) = &merged.lap { merged.lap = Some(lap - 1); } + merged.metrics = state.current_lap_metrics.clone(); + state.current_lap_metrics = LapMetrics::default(); + merged.screenshot = Some(to_png_bytes(image)); merged.penalties = state.penalties_this_lap; state.penalties_this_lap = 0; diff --git a/src/state.rs b/src/state.rs index 270a732..6ea45de 100644 --- a/src/state.rs +++ b/src/state.rs @@ -6,11 +6,32 @@ use std::{ use egui_extras::RetainedImage; use image::RgbImage; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; use time::{format_description, OffsetDateTime}; use crate::{config::Config, learned_tracks::LearnedTracks, ocr_db::OcrDatabase, tts::Tts}; +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct LapMetrics { + pub position: Vec, + pub health: Vec, + pub gas: Vec, + pub tyres: Vec, +} +fn record_metric(vec: &mut Vec, val: &Option) { + if let Some(v) = val { + vec.push(*v); + } +} +impl LapMetrics { + pub fn record(&mut self, lap: &LapState) { + record_metric(&mut self.position, &lap.position); + record_metric(&mut self.gas, &lap.gas); + record_metric(&mut self.tyres, &lap.tyres); + record_metric(&mut self.health, &lap.health); + } +} + #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct LapState { pub lap: Option, @@ -32,6 +53,8 @@ pub struct LapState { pub debug: bool, pub penalties: usize, + + pub metrics: LapMetrics, } fn parse_duration(time: &str) -> Option { @@ -151,7 +174,11 @@ impl RaceState { } pub fn fastest_lap(&self) -> Option { - self.laps.iter().filter(|lap| !lap.striked).filter_map(|lap| lap.lap_time).min() + self.laps + .iter() + .filter(|lap| !lap.striked) + .filter_map(|lap| lap.lap_time) + .min() } pub fn tyre_wear(&self) -> Option { @@ -163,7 +190,12 @@ impl RaceState { } pub fn latest_gas_diff(&self, to: u8) -> Option { - let last_gas = self.laps.iter().last().map(|l| l.gas).unwrap_or(Some(100))?; + let last_gas = self + .laps + .iter() + .last() + .map(|l| l.gas) + .unwrap_or(Some(100))?; if to < last_gas { Some(last_gas - to) } else { @@ -171,7 +203,12 @@ impl RaceState { } } pub fn latest_tyre_diff(&self, to: u8) -> Option { - let last_tyres = self.laps.iter().last().map(|l| l.tyres).unwrap_or(Some(100))?; + let last_tyres = self + .laps + .iter() + .last() + .map(|l| l.tyres) + .unwrap_or(Some(100))?; if to < last_tyres { Some(last_tyres - to) } else { @@ -190,6 +227,7 @@ pub struct DebugOcrFrame { #[derive(Default, Serialize, Deserialize)] pub struct AppState { pub last_frame: Option, + pub current_lap_metrics: LapMetrics, pub buffered_frames: VecDeque,