f1-telemetry-client 0.1.1

Telemetry client for F1 game by Codemasters
Documentation
use crate::f1_2020::car::{
    ActualTyreCompound, AntiLockBrakes, CarStatusData, DRSStatus, ERSDeploymentMode, FuelMix,
    PacketCarStatusData, TractionControl, VisualTyreCompound, CAR_STATUS_MIN_SIZE, TOTAL_CARS,
};
use crate::f1_2020::header::PacketHeader;
use crate::f1_2020::motion::Wheel;
use crate::f1_2020::session::parse_flag;
use async_std::io::{Cursor, Error, ErrorKind};
use byteorder_async::{LittleEndian, ReaderToByteOrder};

pub async fn parse_car_status_data(
    cursor: &mut Cursor<Vec<u8>>,
    header: PacketHeader,
    size: usize,
) -> Result<PacketCarStatusData, Error> {
    ensure_car_status_size(size)?;

    let mut car_status_data = Vec::with_capacity(TOTAL_CARS);
    for _ in 0..TOTAL_CARS {
        let csd = parse_car_status(cursor).await?;
        car_status_data.push(csd);
    }

    Ok(PacketCarStatusData {
        header,
        car_status_data,
    })
}

async fn parse_car_status(cursor: &mut Cursor<Vec<u8>>) -> Result<CarStatusData, Error> {
    let traction_control = parse_traction_control(cursor.byte_order().read_u8().await?)?;
    let anti_lock_brakes = parse_anti_lock_brakes(cursor.byte_order().read_u8().await?)?;
    let fuel_mix = parse_fuel_mix(cursor.byte_order().read_u8().await?)?;
    let front_brake_bias = cursor.byte_order().read_u8().await?;
    let pit_limiter = cursor.byte_order().read_u8().await? == 1;
    let fuel_in_tank = cursor.byte_order().read_f32::<LittleEndian>().await?;
    let fuel_capacity = cursor.byte_order().read_f32::<LittleEndian>().await?;
    let fuel_remaining_laps = cursor.byte_order().read_f32::<LittleEndian>().await?;
    let max_rpm = cursor.byte_order().read_u16::<LittleEndian>().await?;
    let idle_rpm = cursor.byte_order().read_u16::<LittleEndian>().await?;
    let max_gears = cursor.byte_order().read_u8().await?;
    let drs_allowed = parse_drs(cursor.byte_order().read_i8().await?)?;
    let drs_activation_distance = cursor.byte_order().read_u16::<LittleEndian>().await?;
    let tyres_wear = Wheel {
        rear_left: cursor.byte_order().read_u8().await?,
        rear_right: cursor.byte_order().read_u8().await?,
        front_left: cursor.byte_order().read_u8().await?,
        front_right: cursor.byte_order().read_u8().await?,
    };
    let actual_tyre_compound = parse_actual_tyre_compound(cursor.byte_order().read_u8().await?)?;
    let visual_tyre_compound = parse_visual_tyre_compound(cursor.byte_order().read_u8().await?)?;
    let tyres_age_laps = cursor.byte_order().read_u8().await?;
    let tyres_damage = Wheel {
        rear_left: cursor.byte_order().read_u8().await?,
        rear_right: cursor.byte_order().read_u8().await?,
        front_left: cursor.byte_order().read_u8().await?,
        front_right: cursor.byte_order().read_u8().await?,
    };
    let front_left_wing_damage = cursor.byte_order().read_u8().await?;
    let front_right_wing_damage = cursor.byte_order().read_u8().await?;
    let rear_wing_damage = cursor.byte_order().read_u8().await?;
    let drs_fault = cursor.byte_order().read_u8().await? == 1;
    let engine_damage = cursor.byte_order().read_u8().await?;
    let gear_box_damage = cursor.byte_order().read_u8().await?;
    let vehicle_fia_flags = parse_flag(cursor.byte_order().read_i8().await?)?;
    let ers_store_energy = cursor.byte_order().read_f32::<LittleEndian>().await?;
    let ers_deploy_mode = parse_ers_deployment_mode(cursor.byte_order().read_u8().await?)?;
    let ers_harvested_this_lap_mguk = cursor.byte_order().read_f32::<LittleEndian>().await?;
    let ers_harvested_this_lap_mguh = cursor.byte_order().read_f32::<LittleEndian>().await?;
    let ers_deployed_this_lap = cursor.byte_order().read_f32::<LittleEndian>().await?;

    Ok(CarStatusData {
        traction_control,
        anti_lock_brakes,
        fuel_mix,
        front_brake_bias,
        pit_limiter,
        fuel_in_tank,
        fuel_capacity,
        fuel_remaining_laps,
        max_rpm,
        idle_rpm,
        max_gears,
        drs_allowed,
        drs_activation_distance,
        tyres_wear,
        actual_tyre_compound,
        visual_tyre_compound,
        tyres_age_laps,
        tyres_damage,
        front_left_wing_damage,
        front_right_wing_damage,
        rear_wing_damage,
        drs_fault,
        engine_damage,
        gear_box_damage,
        vehicle_fia_flags,
        ers_store_energy,
        ers_deploy_mode,
        ers_harvested_this_lap_mguk,
        ers_harvested_this_lap_mguh,
        ers_deployed_this_lap,
    })
}

fn ensure_car_status_size(size: usize) -> Result<(), Error> {
    if size == CAR_STATUS_MIN_SIZE {
        return Ok(());
    }

    Err(Error::new(
        ErrorKind::InvalidData,
        "Car status size is too small",
    ))
}

fn parse_traction_control(value: u8) -> Result<TractionControl, Error> {
    match value {
        0 => Ok(TractionControl::Off),
        1 => Ok(TractionControl::Low),
        2 => Ok(TractionControl::High),
        _ => Err(Error::new(
            ErrorKind::InvalidData,
            "Traction control invalid",
        )),
    }
}

fn parse_fuel_mix(value: u8) -> Result<FuelMix, Error> {
    match value {
        0 => Ok(FuelMix::Lean),
        1 => Ok(FuelMix::Standard),
        2 => Ok(FuelMix::Rich),
        3 => Ok(FuelMix::Max),
        _ => Err(Error::new(ErrorKind::InvalidData, "Fuel mix invalid")),
    }
}

fn parse_drs(value: i8) -> Result<DRSStatus, Error> {
    match value {
        0 => Ok(DRSStatus::NotAllowed),
        1 => Ok(DRSStatus::Allowed),
        -1 => Ok(DRSStatus::Unknown),
        _ => Err(Error::new(ErrorKind::InvalidData, "DRS status invalid")),
    }
}

fn parse_ers_deployment_mode(value: u8) -> Result<ERSDeploymentMode, Error> {
    match value {
        0 => Ok(ERSDeploymentMode::None),
        1 => Ok(ERSDeploymentMode::Medium),
        2 => Ok(ERSDeploymentMode::Overtake),
        3 => Ok(ERSDeploymentMode::Hotlap),
        _ => Err(Error::new(
            ErrorKind::InvalidData,
            "ERS Deployment mode invalid",
        )),
    }
}

pub fn parse_actual_tyre_compound(value: u8) -> Result<ActualTyreCompound, Error> {
    match value {
        16 => Ok(ActualTyreCompound::C5),
        17 => Ok(ActualTyreCompound::C4),
        18 => Ok(ActualTyreCompound::C3),
        19 => Ok(ActualTyreCompound::C2),
        20 => Ok(ActualTyreCompound::C1),
        7 => Ok(ActualTyreCompound::Inter),
        8 => Ok(ActualTyreCompound::Wet),
        9 => Ok(ActualTyreCompound::F1ClassicDry),
        10 => Ok(ActualTyreCompound::F1ClassicWet),
        11 => Ok(ActualTyreCompound::F2SuperSoft),
        12 => Ok(ActualTyreCompound::F2Soft),
        13 => Ok(ActualTyreCompound::F2Medium),
        14 => Ok(ActualTyreCompound::F2Hard),
        15 => Ok(ActualTyreCompound::F2Wet),
        0 | 255 => Ok(ActualTyreCompound::Unknown),
        _ => Err(Error::new(ErrorKind::InvalidData, "Tyre compound invalid")),
    }
}

pub fn parse_visual_tyre_compound(value: u8) -> Result<VisualTyreCompound, Error> {
    match value {
        16 => Ok(VisualTyreCompound::Soft),
        17 => Ok(VisualTyreCompound::Medium),
        18 => Ok(VisualTyreCompound::Hard),
        7 => Ok(VisualTyreCompound::Inter),
        8 => Ok(VisualTyreCompound::Wet),
        9 => Ok(VisualTyreCompound::F1ClassicDry),
        10 => Ok(VisualTyreCompound::F1ClassicWet),
        11 => Ok(VisualTyreCompound::F2SuperSoft),
        12 => Ok(VisualTyreCompound::F2Soft),
        13 => Ok(VisualTyreCompound::F2Medium),
        14 => Ok(VisualTyreCompound::F2Hard),
        15 => Ok(VisualTyreCompound::F2Wet),
        0 => Ok(VisualTyreCompound::Unknown),
        _ => Err(Error::new(
            ErrorKind::InvalidData,
            "Tyre compound visual invalid",
        )),
    }
}

pub fn parse_anti_lock_brakes(value: u8) -> Result<AntiLockBrakes, Error> {
    match value {
        0 => Ok(AntiLockBrakes::Off),
        1 => Ok(AntiLockBrakes::On),
        _ => Err(Error::new(
            ErrorKind::InvalidData,
            "Anti lock brakes invalid",
        )),
    }
}