feat: add 2022 spec car status packet parser

This commit is contained in:
Maciej Pędzich 2025-02-18 21:34:16 +01:00
parent 8ea06e5a53
commit 839e1ab30b
Signed by: maciejpedzich
GPG Key ID: CE4A303D84882F0D
5 changed files with 272 additions and 28 deletions

View File

@ -1,14 +1,11 @@
pub mod wheel_index;
pub mod driver_id; pub mod driver_id;
pub mod wheel_index;
use binrw::BinRead; use binrw::BinRead;
use bitflags::bitflags; use bitflags::bitflags;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub(crate) const MAX_NUM_CARS: usize = 22; pub(crate) const MAX_NUM_CARS: usize = 22;
pub(crate) const MAX_NUM_MARSHAL_ZONES: usize = 21;
pub(crate) const MAX_NUM_WEATHER_FORECAST_SAMPLES: usize = 56;
pub(crate) const MAX_AI_DIFFICULTY: u8 = 110;
#[non_exhaustive] #[non_exhaustive]
#[derive( #[derive(
@ -983,3 +980,170 @@ pub enum MfdPanelIndex {
Temperatures = 4, Temperatures = 4,
Closed = 255, Closed = 255,
} }
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum TractionControl {
Off = 0,
Medium = 1,
Full = 2,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum FuelMix {
Lean = 0,
Standard = 1,
Rich = 2,
Max = 3,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum ErsDeployMode {
None = 0,
Medium = 1,
Overtake = 2,
Hotlap = 3,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(i8))]
pub enum VehicleFiaFlag {
Unknown = -1,
None = 0,
Green = 1,
Blue = 2,
Yellow = 3,
Red = 4,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(i8))]
pub enum DrsAllowed {
Unknown = -1,
NotAllowed = 0,
Allowed = 1,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum ActualTyreCompound {
Unknown = 0,
F1C5 = 16,
F1C4 = 17,
F1C3 = 18,
F1C2 = 19,
F1C1 = 20,
F1Inter = 7,
F1Wet = 8,
ClassicDry = 9,
ClassicWet = 10,
F2SuperSoft = 11,
F2Soft = 12,
F2Medium = 13,
F2Hard = 14,
F2Wet = 15,
}
#[non_exhaustive]
#[derive(
BinRead,
Eq,
PartialEq,
Ord,
PartialOrd,
Copy,
Clone,
Debug,
Serialize,
Deserialize,
)]
#[br(little, repr(u8))]
pub enum VisualTyreCompound {
Unknown = 0,
F1Soft = 16,
F1Medium = 17,
F1Hard = 18,
F1Inter = 7,
F1Wet = 8,
ClassicDry = 9,
ClassicWet = 10,
F2SuperSoft = 19,
F2Soft = 20,
F2Medium = 21,
F2Hard = 22,
F2Wet = 15,
}

View File

@ -3,9 +3,9 @@ pub mod packets;
use crate::constants::PacketId; use crate::constants::PacketId;
use crate::packets::{ use crate::packets::{
F1PacketCarSetupsData, F1PacketCarTelemetryData, F1PacketEventData, F1PacketCarSetupsData, F1PacketCarStatusData, F1PacketCarTelemetryData,
F1PacketLapData, F1PacketMotionData, F1PacketParticipantsData, F1PacketEventData, F1PacketLapData, F1PacketMotionData,
F1PacketSessionData, F1PacketParticipantsData, F1PacketSessionData,
}; };
use binrw::io::Cursor; use binrw::io::Cursor;
@ -93,4 +93,6 @@ pub struct F1PacketBody {
pub car_setups: Option<F1PacketCarSetupsData>, pub car_setups: Option<F1PacketCarSetupsData>,
#[br(if(packet_id == PacketId::CarTelemetry), args(packet_format))] #[br(if(packet_id == PacketId::CarTelemetry), args(packet_format))]
pub car_telemetry: Option<F1PacketCarTelemetryData>, pub car_telemetry: Option<F1PacketCarTelemetryData>,
#[br(if(packet_id == PacketId::CarStatus), args(packet_format))]
pub car_status: Option<F1PacketCarStatusData>,
} }

72
src/packets/car_status.rs Normal file
View File

@ -0,0 +1,72 @@
use super::u8_to_bool;
use crate::constants::{
ActualTyreCompound, DrsAllowed, ErsDeployMode, FuelMix, TractionControl,
VehicleFiaFlag, VisualTyreCompound,
};
use binrw::BinRead;
use serde::{Deserialize, Serialize};
#[derive(
BinRead, PartialEq, PartialOrd, Copy, Clone, Debug, Serialize, Deserialize,
)]
#[br(
little,
import(_packet_format: u16),
assert(
max_gears <= 8,
"Car status entry has an invalid max number of gears: {}",
max_gears
)
)]
pub struct CarStatusData {
/// How much traction control is enabled.
pub traction_control: TractionControl,
/// Whether ABS is enabled.
#[br(map(u8_to_bool))]
pub anti_lock_brakes: bool,
/// Fuel mix currently in use.
pub fuel_mix: FuelMix,
/// Front brake bias (percentage).
pub front_brake_bias: u8,
/// Whether the pit limiter is enabled.
#[br(map(u8_to_bool))]
pub pit_limiter_enabled: bool,
/// Current fuel mass.
pub fuel_in_tank: f32,
/// Fuel capacity.
pub fuel_capacity: f32,
/// Fuel remaining in terms of laps.
pub fuel_remaining_laps: f32,
/// Car's max RPM, point of rev limiter.
pub max_rpm: u16,
/// Car's idle RPM.
pub idle_rpm: u16,
/// Maximum number of gears.
pub max_gears: u8,
/// Whether DRS can be used (might be unknown).
pub drs_allowed: DrsAllowed,
/// 0 = DRS is unavailable, Non-zero = DRS will be available in X metres.
pub drs_activation_distance: u16,
/// Actual tyre compound currently in use.
pub actual_tyre_compound: ActualTyreCompound,
/// Visible tyre compound currently in use.
pub visual_tyre_compound: VisualTyreCompound,
/// Age of the current set of tyres in laps.
pub tyres_age_laps: u8,
/// Flag the driver is currently being shown.
pub vehicle_fia_flag: VehicleFiaFlag,
/// ERS energy store in Joules.
pub ers_store_energy: f32,
/// ERS deployment mode.
pub ers_deploy_mode: ErsDeployMode,
/// ERS energy harvested this lap by the MGU-K.
pub ers_harvested_this_lap_mguk: f32,
/// ERS energy harvested this lap by the MGU-H.
pub ers_harvested_this_lap_mguh: f32,
/// ERS energy deployed this lap.
pub ers_deployed_this_lap: f32,
/// Whether the car has paused in a network game.
#[br(map(u8_to_bool))]
pub network_paused: bool,
}

View File

@ -27,7 +27,7 @@ use serde::{Deserialize, Serialize};
brake brake
), ),
assert( assert(
(0..=100).contains(&clutch), clutch <= 100,
"Car telemetry entry has an invalid clutch value: {}", "Car telemetry entry has an invalid clutch value: {}",
clutch clutch
), ),
@ -37,7 +37,7 @@ use serde::{Deserialize, Serialize};
gear gear
), ),
assert( assert(
(0..=100).contains(&rev_lights_percent), rev_lights_percent <= 100,
"Car telemetry entry has an invalid rev lights percentage: {}", "Car telemetry entry has an invalid rev lights percentage: {}",
rev_lights_percent rev_lights_percent
), ),
@ -51,7 +51,7 @@ pub struct CarTelemetryData {
pub steer: f32, pub steer: f32,
/// Amount of brake applied. Value in range `(0.0..=1.0)`. /// Amount of brake applied. Value in range `(0.0..=1.0)`.
pub brake: f32, pub brake: f32,
/// Amount of clutch applied. Value in range `(0..=100)`. /// Amount of clutch applied (percentage).
pub clutch: u8, pub clutch: u8,
/// Selected gear. Neutral = 0, reverse = -1. /// Selected gear. Neutral = 0, reverse = -1.
pub gear: i8, pub gear: i8,

View File

@ -1,4 +1,5 @@
pub mod car_setups; pub mod car_setups;
pub mod car_status;
pub mod car_telemetry; pub mod car_telemetry;
pub mod event; pub mod event;
pub mod lap_data; pub mod lap_data;
@ -9,8 +10,7 @@ pub mod session;
use crate::constants::{ use crate::constants::{
BrakingAssist, DynamicRacingLine, DynamicRacingLineType, ForecastAccuracy, BrakingAssist, DynamicRacingLine, DynamicRacingLineType, ForecastAccuracy,
Formula, GameMode, GearboxAssist, MfdPanelIndex, Ruleset, SafetyCarStatus, Formula, GameMode, GearboxAssist, MfdPanelIndex, Ruleset, SafetyCarStatus,
SessionLength, SessionType, TrackId, Weather, MAX_AI_DIFFICULTY, SessionLength, SessionType, TrackId, Weather, MAX_NUM_CARS,
MAX_NUM_CARS, MAX_NUM_MARSHAL_ZONES, MAX_NUM_WEATHER_FORECAST_SAMPLES,
}; };
use crate::packets::event::EventDataDetails; use crate::packets::event::EventDataDetails;
use crate::packets::lap_data::LapData; use crate::packets::lap_data::LapData;
@ -19,6 +19,7 @@ use crate::packets::participants::ParticipantsData;
use crate::packets::session::{MarshalZone, WeatherForecastSample}; use crate::packets::session::{MarshalZone, WeatherForecastSample};
use crate::packets::car_setups::CarSetupData; use crate::packets::car_setups::CarSetupData;
use crate::packets::car_status::CarStatusData;
use crate::packets::car_telemetry::CarTelemetryData; use crate::packets::car_telemetry::CarTelemetryData;
use binrw::BinRead; use binrw::BinRead;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -49,17 +50,17 @@ pub struct F1PacketMotionData {
little, little,
import(packet_format: u16), import(packet_format: u16),
assert( assert(
num_marshal_zones <= MAX_NUM_MARSHAL_ZONES, num_marshal_zones <= 21,
"Session packet has an invalid number of marshal zones: {}", "Session packet has an invalid number of marshal zones: {}",
num_marshal_zones num_marshal_zones
), ),
assert( assert(
num_weather_forecast_samples <= MAX_NUM_WEATHER_FORECAST_SAMPLES, num_weather_forecast_samples <= 56,
"Session packet has an invalid number of weather forecast samples: {}", "Session packet has an invalid number of weather forecast samples: {}",
num_weather_forecast_samples num_weather_forecast_samples
), ),
assert( assert(
ai_difficulty <= MAX_AI_DIFFICULTY, ai_difficulty <= 110,
"Session packet has an invalid AI difficulty value: {}", "Session packet has an invalid AI difficulty value: {}",
ai_difficulty ai_difficulty
), ),
@ -104,7 +105,7 @@ pub struct F1PacketSessionData {
pub num_marshal_zones: usize, pub num_marshal_zones: usize,
/// List of marshal zones. /// List of marshal zones.
/// Has a size of 21 regardless of the `num_marshal_zones` value. /// Has a size of 21 regardless of the `num_marshal_zones` value.
#[br(count(MAX_NUM_MARSHAL_ZONES), args{ inner: (packet_format,) })] #[br(count(21), args{ inner: (packet_format,) })]
pub marshal_zones: Vec<MarshalZone>, pub marshal_zones: Vec<MarshalZone>,
/// Safety car deployment status. /// Safety car deployment status.
pub safety_car_status: SafetyCarStatus, pub safety_car_status: SafetyCarStatus,
@ -116,10 +117,7 @@ pub struct F1PacketSessionData {
pub num_weather_forecast_samples: usize, pub num_weather_forecast_samples: usize,
/// List of up to weather forecast samples. /// List of up to weather forecast samples.
/// Has a size of 56 regardless of the `num_weather_forecast_samples` value. /// Has a size of 56 regardless of the `num_weather_forecast_samples` value.
#[br( #[br(count(56), args{ inner: (packet_format,) })]
count(MAX_NUM_WEATHER_FORECAST_SAMPLES),
args{ inner: (packet_format,) }
)]
pub weather_forecast_samples: Vec<WeatherForecastSample>, pub weather_forecast_samples: Vec<WeatherForecastSample>,
/// Weather forecast accuracy. /// Weather forecast accuracy.
pub forecast_accuracy: ForecastAccuracy, pub forecast_accuracy: ForecastAccuracy,
@ -224,8 +222,7 @@ pub struct F1PacketParticipantsData {
#[br(map(u8_to_usize))] #[br(map(u8_to_usize))]
pub num_active_cars: usize, pub num_active_cars: usize,
/// Data for all participants. /// Data for all participants.
/// Should have a size equal to /// Should have a size equal to `num_active_cars`.
/// [`num_active_cars`](field@F1PacketParticipantsData::num_active_cars)
#[br(count(num_active_cars), args{ inner: (packet_format,) })] #[br(count(num_active_cars), args{ inner: (packet_format,) })]
pub participants: Vec<ParticipantsData>, pub participants: Vec<ParticipantsData>,
} }
@ -261,20 +258,29 @@ pub struct F1PacketCarSetupsData {
)] )]
pub struct F1PacketCarTelemetryData { pub struct F1PacketCarTelemetryData {
/// Telemetry data for all cars on track. /// Telemetry data for all cars on track.
/// Should have a size of 22 /// Should have a size of 22.
#[br( #[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
count(MAX_NUM_CARS),
args{ inner: (packet_format,) }
)]
pub car_telemetry_data: Vec<CarTelemetryData>, pub car_telemetry_data: Vec<CarTelemetryData>,
/// Index of currently open MFD panel. /// Index of currently open MFD panel.
pub mfd_panel_index: MfdPanelIndex, pub mfd_panel_index: MfdPanelIndex,
/// See [`mfd_panel_index`](field@F1PacketCarTelemetryData::mfd_panel_index) /// See [`mfd_panel_index`](field@F1PacketCarTelemetryData::mfd_panel_index).
pub mfd_panel_index_secondary_player: MfdPanelIndex, pub mfd_panel_index_secondary_player: MfdPanelIndex,
/// Suggested gear (0 if no gear suggested). /// Suggested gear (0 if no gear suggested).
pub suggested_gear: i8, pub suggested_gear: i8,
} }
/// Car status data for each car in the race.
#[non_exhaustive]
#[derive(
BinRead, PartialEq, PartialOrd, Clone, Debug, Serialize, Deserialize,
)]
#[br(little, import(packet_format: u16))]
pub struct F1PacketCarStatusData {
/// Status data for all cars. Should have a size of 22.
#[br(count(MAX_NUM_CARS), args{ inner: (packet_format,) })]
pub car_status_data: Vec<CarStatusData>,
}
/// Extended motion data for player's car. Available as a: /// Extended motion data for player's car. Available as a:
/// - part of [`F1PacketMotionData`] in the 2022 format /// - part of [`F1PacketMotionData`] in the 2022 format
/// - standalone packet from the 2023 format onwards /// - standalone packet from the 2023 format onwards