use anyhow::Result; use rfactor_sm_reader::*; use std::{ cell::RefCell, rc::Rc, time::{Duration, Instant}, }; use crate::write_log; use super::UiOverlay; pub trait DataReceiver { fn game_phase_change(&mut self, phase: GamePhase) -> Result<()>; fn update_for_phase(&self, phase: GamePhase) -> bool; fn scoring_update( &mut self, phase: GamePhase, vehicle_scoring: &[VehicleScoringInfoV01], ) -> Result<()>; fn telemetry_update( &mut self, player_id: Option, telemetries: &[rF2VehicleTelemetry], ) -> Result<()>; } const GAME_PHASE_TIME_OUT: Duration = Duration::from_secs(2); #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum GamePhase { TestDay, Practice, Qualifying, Warmup, Race, None, } impl TryFrom for GamePhase { type Error = anyhow::Error; fn try_from(value: i32) -> Result { Ok(match value { 0 => Self::TestDay, 1..=4 => Self::Practice, 5..=8 => Self::Qualifying, 9 => Self::Warmup, 10..=13 => Self::Race, _ => return Err(anyhow::anyhow!("Failed to parse GamePhase from: {}", value)), }) } } pub struct RFactorData { // rf2 memory mapped data telemetry_reader: TelemetryReader, scoring_reader: ScoringReader, last_scoring_read: Duration, start_time: Instant, player_id: Option, previous_game_phase: GamePhase, receivers: Vec>>, } impl RFactorData { pub fn new() -> Result { write_log!(" =================== create RFactorData ==================="); let start_time = Instant::now(); Ok(Self { telemetry_reader: TelemetryReader::new(start_time.elapsed().as_secs_f32())?, scoring_reader: ScoringReader::new(start_time.elapsed().as_secs_f32())?, last_scoring_read: start_time.elapsed(), start_time, player_id: None, previous_game_phase: GamePhase::None, receivers: Vec::new(), }) } pub fn add_receiver(&mut self, receiver: Rc>) { self.receivers.push(receiver); } fn now(&self) -> f32 { self.start_time.elapsed().as_secs_f32() } pub fn update(&mut self) -> Result<()> { write_log!(" =================== update RFactorData ==================="); // get scoring info match self.scoring_reader.vehicle_scoring(self.now()) { Some((scoring_info, vehicle_scorings)) => { self.last_scoring_read = self.start_time.elapsed(); write_log!(format!( "new scoring info: vehicles: {}", scoring_info.mNumVehicles )); // check for player id if scoring_info.mNumVehicles == 0 { self.player_id = None; } else if self.player_id.is_none() { for vehicle_scoring in vehicle_scorings.iter() { if vehicle_scoring.mIsPlayer != 0 { write_log!(format!("player found: {}", vehicle_scoring.mID)); self.player_id = Some(vehicle_scoring.mID); break; } } } { let phase = GamePhase::try_from(scoring_info.mSession)?; if self.previous_game_phase != phase { self.previous_game_phase = phase; for receiver in self.receivers.iter() { receiver .borrow_mut() .game_phase_change(self.previous_game_phase)?; } } write_log!(format!("GamePhase: {:?}", self.previous_game_phase)); } for receiver in self.receivers.iter() { let mut rec_mut = receiver.borrow_mut(); if rec_mut.update_for_phase(self.previous_game_phase) { rec_mut.scoring_update(self.previous_game_phase, &vehicle_scorings)?; } } } None => { let now = self.start_time.elapsed(); if now > (self.last_scoring_read + GAME_PHASE_TIME_OUT) { if self.previous_game_phase != GamePhase::None { self.previous_game_phase = GamePhase::None; for receiver in self.receivers.iter() { receiver .borrow_mut() .game_phase_change(self.previous_game_phase)?; } } } } } // check telemetry data write_log!("before telemetry update"); if let Some(telemetries) = self.telemetry_reader.query_telemetry(self.now()) { write_log!("new telemetry update"); for receiver in self.receivers.iter() { let mut rec_mut = receiver.borrow_mut(); if rec_mut.update_for_phase(self.previous_game_phase) { rec_mut.telemetry_update(self.player_id, &telemetries)?; } } } Ok(()) } }