diff --git a/Cargo.lock b/Cargo.lock index 200b6a4..9da36b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -228,6 +228,7 @@ dependencies = [ "chrono", "csv", "indexmap", + "num_enum", "saphyr", "serde", ] @@ -275,6 +276,28 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_enum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -290,6 +313,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -390,6 +422,36 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + [[package]] name = "unicode-ident" version = "1.0.22" @@ -499,3 +561,12 @@ checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ "windows-link", ] + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml index a2438db..75f2f21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ aligned-vec = "0.6.4" bytemuck = { version = "1.24.0", features = ["derive", "extern_crate_alloc"] } chrono = "0.4.42" indexmap = "2.12.1" +num_enum = "0.7.5" saphyr = "0.0.6" serde = { version = "1.0.228", features = ["derive"] } diff --git a/src/telemetry/enums.rs b/src/telemetry/enums.rs new file mode 100644 index 0000000..8902d0c --- /dev/null +++ b/src/telemetry/enums.rs @@ -0,0 +1,178 @@ +use num_enum::FromPrimitive; + +use crate::aligned::align_cast; + +#[derive(Clone, Copy, Debug)] +pub enum Enum { + TrackLocation(TrackLocation), + TrackSurface(TrackSurface), + SessionState(SessionState), + CarLeftRight(CarLeftRight), + PitServiceStatus(PitServiceStatus), + PaceMode(PaceMode), + TrackWetness(TrackWetness), +} + +impl Enum { + pub(crate) fn parse(slice: &[u8], unit: &str) -> Option { + let e = match unit { + "irsdk_TrkLoc" => Self::track_location(align_cast(slice)), + "irsdk_TrkSurf" => Self::track_surface(align_cast(slice)), + "irsdk_SessionState" => Self::session_state(align_cast(slice)), + "irsdk_CarLeftRight" => Self::car_left_right(align_cast(slice)), + "irsdk_PitSvStatus" => Self::pit_service_status(align_cast(slice)), + "irsdk_PaceMode" => Self::pace_mode(align_cast(slice)), + "irsdk_TrackWetness" => Self::track_wetness(align_cast(slice)), + + _ => return None, + }; + Some(e) + } + + fn track_location(raw: i32) -> Self { + Self::TrackLocation(TrackLocation::from(raw)) + } + + fn track_surface(raw: i32) -> Self { + Self::TrackSurface(TrackSurface::from(raw)) + } + + fn session_state(raw: i32) -> Self { + Self::SessionState(SessionState::from(raw)) + } + + fn car_left_right(raw: i32) -> Self { + Self::CarLeftRight(CarLeftRight::from(raw)) + } + + fn pit_service_status(raw: i32) -> Self { + Self::PitServiceStatus(PitServiceStatus::from(raw)) + } + + fn pace_mode(raw: i32) -> Self { + Self::PaceMode(PaceMode::from(raw)) + } + + fn track_wetness(raw: i32) -> Self { + Self::TrackWetness(TrackWetness::from(raw)) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)] +#[repr(i32)] +pub enum TrackLocation { + NotInWorld = -1, + OffTrack = 0, + InPitStall, + /// Includes the lead in to pit road as well as pit road itself. If you just want to know that + /// you're on the pit road surface, look at the live value `OnPitRoad`. + ApproachingPits, + OnTrack, + + #[default] + Unknown, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)] +#[repr(i32)] +pub enum TrackSurface { + SurfaceNotInWorld = -1, + #[default] + Undefined = 0, + + Asphalt1, + Asphalt2, + Asphalt3, + Asphalt4, + Concrete1, + Concrete2, + RacingDirt1, + RacingDirt2, + Paint1, + Paint2, + Rumble1, + Rumble2, + Rumble3, + Rumble4, + + Grass1, + Grass2, + Grass3, + Grass4, + Dirt1, + Dirt2, + Dirt3, + Dirt4, + Sand, + Gravel1, + Gravel2, + Grasscrete, + Astroturf, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)] +#[repr(i32)] +pub enum SessionState { + #[default] + Invalid = 0, + GetInCar, + Warmup, + ParadeLaps, + Racing, + Checkered, + Cooldown, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)] +#[repr(i32)] +pub enum CarLeftRight { + #[default] + Off = 0, + Clear, + CarLeft, + CarRight, + Middle, + TwoLeft, + TwoRight, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)] +#[repr(i32)] +pub enum PitServiceStatus { + #[default] + None = 0, + InProgress, + Complete, + // errors + TooFarLeft = 100, + TooFarRight, + TooFarForward, + TooFarBack, + BadAngle, + TerminalDamage, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)] +#[repr(i32)] +pub enum PaceMode { + SingleFileStart = 0, + DoubleFileStart, + SingleFileRestart, + DoubleFileRestart, + #[default] + NotPacing, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, FromPrimitive)] +#[repr(i32)] +pub enum TrackWetness { + #[default] + Unknown, + Dry, + MostlyDry, + VeryLightlyWet, + LightlyWet, + ModeratelyWet, + VeryWet, + ExtremelyWet, +} diff --git a/src/telemetry/mod.rs b/src/telemetry/mod.rs index 3af9847..b9ad73f 100644 --- a/src/telemetry/mod.rs +++ b/src/telemetry/mod.rs @@ -1,3 +1,4 @@ +mod enums; mod sample; mod var; @@ -7,6 +8,7 @@ use chrono::{DateTime, Utc}; use crate::raw; +pub use enums::{Enum, TrackLocation, TrackSurface}; pub use sample::{Sample, Value}; pub use var::{VarHeader, VarSet, VarType}; diff --git a/src/telemetry/sample.rs b/src/telemetry/sample.rs index 11564c2..2eca7b9 100644 --- a/src/telemetry/sample.rs +++ b/src/telemetry/sample.rs @@ -2,7 +2,7 @@ use bytemuck::pod_collect_to_vec; use crate::{ aligned::align_cast, - telemetry::{VarHeader, VarType}, + telemetry::{Enum, VarHeader, VarType}, }; pub struct Sample<'data>(&'data [u8]); @@ -16,6 +16,10 @@ impl<'data> Sample<'data> { let size = var.ty.size() * var.count; let slice = &self.0[var.offset..var.offset + size]; + if let Some(e) = Enum::parse(slice, var.unit.as_str()) { + return Value::Enum(e); + } + if var.count > 1 { match var.ty { VarType::Int => Value::IntArray(pod_collect_to_vec(slice)), @@ -44,6 +48,8 @@ pub enum Value { Float(f32), Double(f64), + Enum(Enum), + IntArray(Vec), FloatArray(Vec), }