Debug shm reader
This commit is contained in:
parent
cf31d3b784
commit
ede1fba66a
2 changed files with 225 additions and 33 deletions
|
@ -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))]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct rF2Scoring {
|
||||
|
@ -233,5 +251,5 @@ pub struct rF2Wheel {
|
|||
pub tire_carcass_temperature: f64,
|
||||
pub tire_inner_layer_temperature: [f64; 3],
|
||||
|
||||
expansion: [::std::os::raw::c_uchar; 24],
|
||||
expansion: [u8; 24],
|
||||
}
|
||||
|
|
238
src/lib.rs
238
src/lib.rs
|
@ -2,7 +2,7 @@ pub mod additional_rfactor;
|
|||
#[allow(warnings)]
|
||||
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::*;
|
||||
pub use additional_rfactor::{rF2Vec3, rF2VehicleTelemetry, rF2Wheel};
|
||||
|
@ -12,19 +12,17 @@ pub use rfactor_structs::{ScoringInfoV01, VehicleScoringInfoV01};
|
|||
|
||||
const RFACTOR_SHM_FILE: &str = "/dev/shm";
|
||||
|
||||
struct ShMReader<T> {
|
||||
struct ShMReader {
|
||||
_file: File,
|
||||
shm: Mmap,
|
||||
|
||||
version: Cell<VersionHeader>,
|
||||
|
||||
data: PhantomData<T>,
|
||||
last_query: Cell<f32>,
|
||||
time_between_writes: f32,
|
||||
}
|
||||
|
||||
impl<T> ShMReader<T> {
|
||||
const SIZE: usize = std::mem::size_of::<T>();
|
||||
|
||||
fn new(mm_file_name: &str) -> Result<Self> {
|
||||
impl ShMReader {
|
||||
fn new(mm_file_name: &str, now: f32, time_between_writes: f32) -> Result<Self> {
|
||||
let file = File::open(Path::new(RFACTOR_SHM_FILE).join(mm_file_name))?;
|
||||
let mmap = unsafe { Mmap::map(&file)? };
|
||||
|
||||
|
@ -33,67 +31,243 @@ impl<T> ShMReader<T> {
|
|||
shm: mmap,
|
||||
|
||||
version: Cell::new(VersionHeader::default()),
|
||||
|
||||
data: PhantomData,
|
||||
last_query: Cell::new(now),
|
||||
time_between_writes,
|
||||
})
|
||||
}
|
||||
|
||||
fn check_version_update(&self) -> bool {
|
||||
log("In ShMReader: start check version update");
|
||||
let new_version = VersionHeader::from(&self.shm[0..VersionHeader::SIZE]);
|
||||
|
||||
let are_different = self.version.get().check_for_update(&new_version);
|
||||
|
||||
if are_different {
|
||||
log("In ShMReader: new version found");
|
||||
self.version.set(new_version);
|
||||
}
|
||||
|
||||
are_different
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> ShMReader<T>
|
||||
where
|
||||
T: From<&'a [u8]>,
|
||||
{
|
||||
fn read(&'a self) -> Option<T> {
|
||||
if self.check_version_update() {
|
||||
Some(T::from(&self.shm[0..Self::SIZE]))
|
||||
} else {
|
||||
None
|
||||
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;
|
||||
}
|
||||
|
||||
self.last_query.set(now);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
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 {
|
||||
mm_reader: ShMReader<rF2Telemetry>,
|
||||
mm_reader: ShMReader,
|
||||
}
|
||||
|
||||
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 {
|
||||
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>> {
|
||||
self.mm_reader.read().map(|v| v.vehicles().to_vec())
|
||||
pub fn query_telemetry(&self, now: f32) -> Option<Vec<rF2VehicleTelemetry>> {
|
||||
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 {
|
||||
mm_reader: ShMReader<rF2Scoring>,
|
||||
mm_reader: ShMReader,
|
||||
}
|
||||
|
||||
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 {
|
||||
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>)> {
|
||||
self.mm_reader
|
||||
.read()
|
||||
.map(|s| (s.scoring_info, s.vehicles().to_vec()))
|
||||
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
|
||||
.read(VersionHeader::SIZE, mem::size_of::<ScoringInfoV01>()),
|
||||
);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue