1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
use crate::f1_2020::car::TOTAL_CARS;
use crate::f1_2020::header::PacketHeader;
use async_std::io::{Cursor, Error, ErrorKind};
use byteorder_async::{LittleEndian, ReaderToByteOrder};
use derivative::Derivative;
use std::time::Duration;

const LAP_DATA_MIN_SIZE: usize = 1190;

#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum PitStatus {
    None,
    Pitting,
    PitArea,
}

#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum DriverStatus {
    Garage,
    FlyingLap,
    InLap,
    OutLap,
    OnTrack,
}

#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum ResultStatus {
    Invalid,
    Inactive,
    Active,
    Finished,
    Disqualified,
    NotClassified,
    Retired,
}

#[derive(Debug, PartialEq, Clone, Copy, Derivative)]
#[derivative(Eq)]
pub struct LapData {
    /// Last lap time in seconds
    pub last_lap_time: Duration,
    /// Current time around the lap in seconds
    pub current_lap_time: Duration,
    /// Sector 1 time in milliseconds
    pub sector_1_time: Duration,
    /// Sector 2 time in milliseconds
    pub sector_2_time: Duration,
    /// Best lap time of the session in seconds
    pub best_lap_time: Duration,
    pub best_lap_num: u8,
    /// Sector 1 time of best lap in the session in milliseconds
    pub best_lap_sector_1_time: Duration,
    /// Sector 2 time of best lap in the session in milliseconds
    pub best_lap_sector_2_time: Duration,
    /// Sector 3 time of best lap in the session in milliseconds
    pub best_lap_sector_3_time: Duration,
    /// Best overall sector 1 time of the session in milliseconds
    pub best_overall_sector_1_time: Duration,
    pub best_overall_sector_1_lap_num: u8,
    /// Best overall sector 2 time of the session in milliseconds
    pub best_overall_sector_2_time: Duration,
    pub best_overall_sector_2_lap_num: u8,
    /// Best overall sector 3 time of the session in milliseconds
    pub best_overall_sector_3_time: Duration,
    pub best_overall_sector_3_lap_num: u8,
    // #[derivative(Eq="ignore")]
    pub lap_distance: f32,
    // #[derivative(Eq="ignore")]
    pub total_distance: f32,
    // #[derivative(Eq="ignore")]
    /// Delta in seconds for safety car
    pub safety_car_delta: Duration,
    pub car_position: u8,
    pub current_lap_num: u8,
    pub pit_status: PitStatus,
    pub sector: u8,
    pub current_lap_invalid: bool,
    pub penalties: u8,
    pub grid_position: u8,
    pub driver_status: DriverStatus,
    pub result_status: ResultStatus,
}

#[derive(Debug, Eq, PartialEq, Clone)]
pub struct PacketLapData {
    pub header: PacketHeader,
    pub lap_data: Vec<LapData>,
}

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

    let mut laps = Vec::with_capacity(TOTAL_CARS);
    for _ in 0..TOTAL_CARS {
        let lap = parse_lap(cursor).await?;
        laps.push(lap);
    }

    Ok(PacketLapData {
        header,
        lap_data: laps,
    })
}

async fn parse_lap(cursor: &mut Cursor<Vec<u8>>) -> Result<LapData, Error> {
    let last_lap_time =
        Duration::from_secs_f32(cursor.byte_order().read_f32::<LittleEndian>().await?); // in seconds
    let current_lap_time =
        Duration::from_secs_f32(cursor.byte_order().read_f32::<LittleEndian>().await?); // in seconds
    let sector_1_time =
        Duration::from_millis(cursor.byte_order().read_u16::<LittleEndian>().await? as u64); // in ms
    let sector_2_time =
        Duration::from_millis(cursor.byte_order().read_u16::<LittleEndian>().await? as u64); // in ms
    let best_lap_time =
        Duration::from_secs_f32(cursor.byte_order().read_f32::<LittleEndian>().await?); // in seconds
    let best_lap_num = cursor.byte_order().read_u8().await?;
    let best_lap_sector_1_time =
        Duration::from_millis(cursor.byte_order().read_u16::<LittleEndian>().await? as u64); // in ms
    let best_lap_sector_2_time =
        Duration::from_millis(cursor.byte_order().read_u16::<LittleEndian>().await? as u64); // in ms
    let best_lap_sector_3_time =
        Duration::from_millis(cursor.byte_order().read_u16::<LittleEndian>().await? as u64); // in ms
    let best_overall_sector_1_time =
        Duration::from_millis(cursor.byte_order().read_u16::<LittleEndian>().await? as u64); // in ms
    let best_overall_sector_1_lap_num = cursor.byte_order().read_u8().await?;
    let best_overall_sector_2_time =
        Duration::from_millis(cursor.byte_order().read_u16::<LittleEndian>().await? as u64); // in ms
    let best_overall_sector_2_lap_num = cursor.byte_order().read_u8().await?;
    let best_overall_sector_3_time =
        Duration::from_millis(cursor.byte_order().read_u16::<LittleEndian>().await? as u64); // in ms
    let best_overall_sector_3_lap_num = cursor.byte_order().read_u8().await?;
    let lap_distance = cursor.byte_order().read_f32::<LittleEndian>().await?;
    let total_distance = cursor.byte_order().read_f32::<LittleEndian>().await?;
    let safety_car_delta =
        Duration::from_secs_f32(cursor.byte_order().read_f32::<LittleEndian>().await?); // in seconds
    let car_position = cursor.byte_order().read_u8().await?;
    let current_lap_num = cursor.byte_order().read_u8().await?;
    let pit_status = parse_pit_status(cursor).await?;
    let sector = cursor.byte_order().read_u8().await?;
    let current_lap_invalid = cursor.byte_order().read_u8().await? == 1;
    let penalties = cursor.byte_order().read_u8().await?;
    let grid_position = cursor.byte_order().read_u8().await?;
    let driver_status = parse_driver_status(cursor).await?;
    let result_status = parse_result_status(cursor).await?;

    Ok(LapData {
        last_lap_time,
        current_lap_time,
        sector_1_time,
        sector_2_time,
        best_lap_time,
        best_lap_num,
        best_lap_sector_1_time,
        best_lap_sector_2_time,
        best_lap_sector_3_time,
        best_overall_sector_1_time,
        best_overall_sector_1_lap_num,
        best_overall_sector_2_time,
        best_overall_sector_2_lap_num,
        best_overall_sector_3_time,
        best_overall_sector_3_lap_num,
        lap_distance,
        total_distance,
        safety_car_delta,
        car_position,
        current_lap_num,
        pit_status,
        sector,
        current_lap_invalid,
        penalties,
        grid_position,
        driver_status,
        result_status,
    })
}

pub async fn parse_pit_status(cursor: &mut Cursor<Vec<u8>>) -> Result<PitStatus, Error> {
    match cursor.byte_order().read_u8().await? {
        0 => Ok(PitStatus::None),
        1 => Ok(PitStatus::Pitting),
        2 => Ok(PitStatus::PitArea),
        _ => Err(Error::new(ErrorKind::InvalidData, "Invalid pit status")),
    }
}

pub async fn parse_driver_status(cursor: &mut Cursor<Vec<u8>>) -> Result<DriverStatus, Error> {
    match cursor.byte_order().read_u8().await? {
        0 => Ok(DriverStatus::Garage),
        1 => Ok(DriverStatus::FlyingLap),
        2 => Ok(DriverStatus::InLap),
        3 => Ok(DriverStatus::OutLap),
        4 => Ok(DriverStatus::OnTrack),
        _ => Err(Error::new(ErrorKind::InvalidData, "Invalid driver status")),
    }
}

pub async fn parse_result_status(cursor: &mut Cursor<Vec<u8>>) -> Result<ResultStatus, Error> {
    match cursor.byte_order().read_u8().await? {
        0 => Ok(ResultStatus::Invalid),
        1 => Ok(ResultStatus::Inactive),
        2 => Ok(ResultStatus::Active),
        3 => Ok(ResultStatus::Finished),
        4 => Ok(ResultStatus::Disqualified),
        5 => Ok(ResultStatus::NotClassified),
        6 => Ok(ResultStatus::Retired),
        _ => Err(Error::new(ErrorKind::InvalidData, "Invalid result status")),
    }
}

fn ensure_lap_data_size(size: usize) -> Result<(), Error> {
    if size < LAP_DATA_MIN_SIZE {
        return Err(Error::new(
            ErrorKind::InvalidData,
            "Lap data size is too small",
        ));
    }

    return Ok(());
}