Debug shm reader

This commit is contained in:
hodasemi 2023-01-15 05:59:21 +01:00
parent cf31d3b784
commit ede1fba66a
2 changed files with 225 additions and 33 deletions

View file

@ -64,6 +64,24 @@ impl From<&[u8]> for rF2Telemetry {
} }
} }
impl From<&[u8]> for ScoringInfoV01 {
fn from(value: &[u8]) -> Self {
debug_assert!(value.len() == mem::size_of::<Self>());
let fixed_size: [u8; mem::size_of::<Self>()] = value.try_into().unwrap();
unsafe { mem::transmute(fixed_size) }
}
}
impl From<&[u8]> for VehicleScoringInfoV01 {
fn from(value: &[u8]) -> Self {
debug_assert!(value.len() == mem::size_of::<Self>());
let fixed_size: [u8; mem::size_of::<Self>()] = value.try_into().unwrap();
unsafe { mem::transmute(fixed_size) }
}
}
#[repr(C, packed(4))] #[repr(C, packed(4))]
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct rF2Scoring { pub struct rF2Scoring {
@ -233,5 +251,5 @@ pub struct rF2Wheel {
pub tire_carcass_temperature: f64, pub tire_carcass_temperature: f64,
pub tire_inner_layer_temperature: [f64; 3], pub tire_inner_layer_temperature: [f64; 3],
expansion: [::std::os::raw::c_uchar; 24], expansion: [u8; 24],
} }

View file

@ -2,7 +2,7 @@ pub mod additional_rfactor;
#[allow(warnings)] #[allow(warnings)]
mod rfactor_structs; mod rfactor_structs;
use std::{cell::Cell, fs::File, marker::PhantomData, path::Path}; use std::{cell::Cell, fs::File, mem, path::Path};
use additional_rfactor::*; use additional_rfactor::*;
pub use additional_rfactor::{rF2Vec3, rF2VehicleTelemetry, rF2Wheel}; pub use additional_rfactor::{rF2Vec3, rF2VehicleTelemetry, rF2Wheel};
@ -12,19 +12,17 @@ pub use rfactor_structs::{ScoringInfoV01, VehicleScoringInfoV01};
const RFACTOR_SHM_FILE: &str = "/dev/shm"; const RFACTOR_SHM_FILE: &str = "/dev/shm";
struct ShMReader<T> { struct ShMReader {
_file: File, _file: File,
shm: Mmap, shm: Mmap,
version: Cell<VersionHeader>, version: Cell<VersionHeader>,
last_query: Cell<f32>,
data: PhantomData<T>, time_between_writes: f32,
} }
impl<T> ShMReader<T> { impl ShMReader {
const SIZE: usize = std::mem::size_of::<T>(); fn new(mm_file_name: &str, now: f32, time_between_writes: f32) -> Result<Self> {
fn new(mm_file_name: &str) -> Result<Self> {
let file = File::open(Path::new(RFACTOR_SHM_FILE).join(mm_file_name))?; let file = File::open(Path::new(RFACTOR_SHM_FILE).join(mm_file_name))?;
let mmap = unsafe { Mmap::map(&file)? }; let mmap = unsafe { Mmap::map(&file)? };
@ -33,67 +31,243 @@ impl<T> ShMReader<T> {
shm: mmap, shm: mmap,
version: Cell::new(VersionHeader::default()), version: Cell::new(VersionHeader::default()),
last_query: Cell::new(now),
data: PhantomData, time_between_writes,
}) })
} }
fn check_version_update(&self) -> bool { fn check_version_update(&self) -> bool {
log("In ShMReader: start check version update");
let new_version = VersionHeader::from(&self.shm[0..VersionHeader::SIZE]); let new_version = VersionHeader::from(&self.shm[0..VersionHeader::SIZE]);
let are_different = self.version.get().check_for_update(&new_version); let are_different = self.version.get().check_for_update(&new_version);
if are_different { if are_different {
log("In ShMReader: new version found");
self.version.set(new_version); self.version.set(new_version);
} }
are_different are_different
} }
fn check_time(&self, now: f32) -> bool {
log("In ShMReader: start time check");
if now < self.last_query.get() + self.time_between_writes {
log("In ShMReader: time check failed");
return false;
} }
impl<'a, T> ShMReader<T> self.last_query.set(now);
where
T: From<&'a [u8]>, true
{
fn read(&'a self) -> Option<T> {
if self.check_version_update() {
Some(T::from(&self.shm[0..Self::SIZE]))
} else {
None
} }
fn read(&self, offset: usize, length: usize) -> &[u8] {
log(format!(
"In ShMReader: read (offset: {}, length: {})",
offset, length
));
let b = &self.shm[offset..offset + length];
debug_assert_eq!(b.len(), length);
log(format!("In ShMReader: successfully read {} bytes", b.len()));
b
} }
} }
pub struct TelemetryReader { pub struct TelemetryReader {
mm_reader: ShMReader<rF2Telemetry>, mm_reader: ShMReader,
} }
impl TelemetryReader { impl TelemetryReader {
pub fn new() -> Result<Self> { pub fn new(now: f32) -> Result<Self> {
// telemetry gets updated 50 times per second
const WAIT_TIME: f32 = 1.0 / 50.0;
Ok(Self { Ok(Self {
mm_reader: ShMReader::new(MM_TELEMETRY_FILE_NAME)?, mm_reader: ShMReader::new(MM_TELEMETRY_FILE_NAME, now, WAIT_TIME)?,
}) })
} }
pub fn query_telemetry(&self) -> Option<Vec<rF2VehicleTelemetry>> { pub fn query_telemetry(&self, now: f32) -> Option<Vec<rF2VehicleTelemetry>> {
self.mm_reader.read().map(|v| v.vehicles().to_vec()) log("In TelemetryReader: start query telemetry");
if !self.mm_reader.check_time(now) || !self.mm_reader.check_version_update() {
return None;
}
log("In TelemetryReader: read num vehicles");
// first: query only the amount of vehicles
let num_vehicles = i32::from_be_bytes(
self.mm_reader
.read(VersionHeader::SIZE, mem::size_of::<i32>())
.try_into()
.unwrap(),
);
log(format!(
"In TelemetryReader: vehicle count {}",
num_vehicles
));
debug_assert!(num_vehicles >= 0);
debug_assert!(num_vehicles < MAX_MAPPED_VEHICLES as i32);
log("In TelemetryReader: read vehicles");
// query only the required amount of memory based on vehicle count
let vehicles: &[rF2VehicleTelemetry] = unsafe {
mem::transmute(self.mm_reader.read(
VersionHeader::SIZE + mem::size_of::<i32>(),
mem::size_of::<rF2VehicleTelemetry>() * num_vehicles as usize,
))
};
log(format!(
"In TelemetryReader: successfully read vehicles ({})",
vehicles.len()
));
debug_assert_eq!(num_vehicles as usize, vehicles.len());
Some(vehicles.to_vec())
} }
} }
pub struct ScoringReader { pub struct ScoringReader {
mm_reader: ShMReader<rF2Scoring>, mm_reader: ShMReader,
} }
impl ScoringReader { impl ScoringReader {
pub fn new() -> Result<Self> { pub fn new(now: f32) -> Result<Self> {
// scoring gets updated 5 times per second
const WAIT_TIME: f32 = 1.0 / 5.0;
Ok(Self { Ok(Self {
mm_reader: ShMReader::new(MM_SCORING_FILE_NAME)?, mm_reader: ShMReader::new(MM_SCORING_FILE_NAME, now, WAIT_TIME)?,
}) })
} }
pub fn vehicle_scoring(&self) -> Option<(ScoringInfoV01, Vec<VehicleScoringInfoV01>)> { pub fn vehicle_scoring(
&self,
now: f32,
) -> Option<(ScoringInfoV01, Vec<VehicleScoringInfoV01>)> {
log("In ScoringReader: start vehicle scoring");
if !self.mm_reader.check_time(now) || !self.mm_reader.check_version_update() {
return None;
}
log("In ScoringReader: read scoring info");
let scoring_info = ScoringInfoV01::from(
self.mm_reader self.mm_reader
.read() .read(VersionHeader::SIZE, mem::size_of::<ScoringInfoV01>()),
.map(|s| (s.scoring_info, s.vehicles().to_vec())) );
log(format!(
"In ScoringReader: successfully read scoring info (num vehicles {})",
scoring_info.mNumVehicles
));
let vehicle_info: &[VehicleScoringInfoV01] = unsafe {
mem::transmute(self.mm_reader.read(
VersionHeader::SIZE + mem::size_of::<ScoringInfoV01>(),
mem::size_of::<VehicleScoringInfoV01>() * scoring_info.mNumVehicles as usize,
))
};
debug_assert_eq!(scoring_info.mNumVehicles as usize, vehicle_info.len());
Some((scoring_info, vehicle_info.to_vec()))
}
}
pub fn log(msg: impl ToString) {
let home = std::env::var("HOME").unwrap();
let log_file = format!("{}/rf2_vk_hud.log", home);
if let Ok(mut file) = std::fs::OpenOptions::new()
.append(true)
.create(true)
.open(&log_file)
{
if let Err(_) =
std::io::Write::write_all(&mut file, format!("{}\n", msg.to_string()).as_bytes())
{
}
}
}
#[cfg(test)]
mod tests {
use std::{
thread::sleep,
time::{Duration, Instant},
};
use super::*;
#[test]
fn test_telemetry() {
let start = Instant::now();
let telemetry_reader = TelemetryReader::new(start.elapsed().as_secs_f32()).unwrap();
for _ in 0..25 {
telemetry_reader.query_telemetry(start.elapsed().as_secs_f32());
sleep(Duration::from_secs(2));
}
}
#[test]
fn test_scoring() {
let start = Instant::now();
let scoring_reader = ScoringReader::new(start.elapsed().as_secs_f32()).unwrap();
for _ in 0..25 {
scoring_reader.vehicle_scoring(start.elapsed().as_secs_f32());
sleep(Duration::from_secs(2));
}
}
#[test]
fn test_combined() {
let start = Instant::now();
let scoring_reader = ScoringReader::new(start.elapsed().as_secs_f32()).unwrap();
let telemetry_reader = TelemetryReader::new(start.elapsed().as_secs_f32()).unwrap();
let mut player_id = None;
for _ in 0..25 {
if let Some((score, vehicles)) =
scoring_reader.vehicle_scoring(start.elapsed().as_secs_f32())
{
if score.mNumVehicles == 0 {
player_id = None;
} else if player_id.is_none() {
for vehicle in vehicles {
if vehicle.mIsPlayer != 0 {
player_id = Some(vehicle.mID);
break;
}
}
}
}
if player_id.is_some() {
if let Some(telemetries) =
telemetry_reader.query_telemetry(start.elapsed().as_secs_f32())
{
for telemetry in telemetries {
println!("ID: {}", telemetry.id);
}
}
}
sleep(Duration::from_secs(2));
}
} }
} }