diff --git a/.gitignore b/.gitignore index ea8c4bf..791af9f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/logs diff --git a/Cargo.lock b/Cargo.lock index 0777bcd..643ae59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,37 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + [[package]] name = "clap" version = "3.1.1" @@ -64,6 +95,37 @@ dependencies = [ "syn", ] +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "fastrand" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +dependencies = [ + "instant", +] + [[package]] name = "hashbrown" version = "0.11.2" @@ -95,6 +157,21 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + [[package]] name = "lazy_static" version = "1.4.0" @@ -113,6 +190,25 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + [[package]] name = "os_str_bytes" version = "6.0.0" @@ -164,6 +260,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + [[package]] name = "serde" version = "1.0.136" @@ -206,8 +332,25 @@ name = "telemetry" version = "0.1.0" dependencies = [ "bincode", + "chrono", "clap", + "csv", "serde", + "tempfile", +] + +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", ] [[package]] @@ -225,6 +368,17 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + [[package]] name = "unicode-xid" version = "0.2.2" @@ -237,6 +391,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 4cc22f4..b12e425 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,9 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "3.1.1", features = ["derive"] } -serde = { version = "1.0.136", features = ["derive"] } -bincode = "1.3.3" \ No newline at end of file +clap = { version = "3.1", features = ["derive"] } +serde = { version = "1.0", features = ["derive"] } +bincode = "1.3" +csv = "1.1" +chrono = "0.4" +tempfile = "3.3" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 224e2d2..ee1f552 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,12 @@ -use std::net; use std::net::UdpSocket; +use std::path::Path; +use std::{fs, net}; use bincode; +use chrono::Local; use clap::Parser; use serde::{Deserialize, Serialize}; +use tempfile::tempfile; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] @@ -16,161 +19,86 @@ struct Args { #[clap(short, long)] verbose: bool, - - #[clap(short, long)] - errors: bool, } #[derive(Serialize, Deserialize, Debug)] struct Telemetry { - // is_race_on: i32, - // timestamp_ms: u32, - // engine_max_rpm: f32, - // engine_idle_rpm: f32, - // current_engine_rpm: f32, - // acceleration_x: f32, - // acceleration_y: f32, - // acceleration_z: f32, - // velocity_x: f32, - // velocity_y: f32, - // velocity_z: f32, - // angular_velocity_x: f32, - // angular_velocity_y: f32, - // angular_velocity_z: f32, - // yaw: f32, - // pitch: f32, - // roll: f32, - // normalized_suspension_travel_front_left: f32, - // normalized_suspension_travel_front_right: f32, - // normalized_suspension_travel_rear_left: f32, - // normalized_suspension_travel_rear_right: f32, - // tire_slip_ratio_front_left: f32, - // tire_slip_ratio_front_right: f32, - // tire_slip_ratio_rear_left: f32, - // tire_slip_ratio_rear_right: f32, - // wheel_rotation_speed_front_left: f32, - // wheel_rotation_speed_front_right: f32, - // wheel_rotation_speed_rear_left: f32, - // wheel_rotation_speed_rear_right: f32, - // wheel_on_rumble_strip_front_left: i32, - // wheel_on_rumble_strip_front_right: i32, - // wheel_on_rumble_strip_rear_left: i32, - // wheel_on_rumble_strip_rear_right: i32, - // wheel_in_puddle_depth_front_left: f32, - // wheel_in_puddle_depth_front_right: f32, - // wheel_in_puddle_depth_rear_left: f32, - // wheel_in_puddle_depth_rear_right: f32, - // surface_rumble_front_left: f32, - // surface_rumble_front_right: f32, - // surface_rumble_rear_left: f32, - // surface_rumble_rear_right: f32, - // tire_slip_angle_front_left: f32, - // tire_slip_angle_front_right: f32, - // tire_slip_angle_rear_left: f32, - // tire_slip_angle_rear_right: f32, - // tire_combined_slip_front_left: f32, - // tire_combined_slip_front_right: f32, - // tire_combined_slip_rear_left: f32, - // tire_combined_slip_rear_right: f32, - // suspension_travel_meters_front_left: f32, - // suspension_travel_meters_front_right: f32, - // suspension_travel_meters_rear_left: f32, - // suspension_travel_meters_rear_right: f32, - // car_ordinal: i32, - // car_class: i32, - // car_performance_index: i32, - // drivetrain_type: i32, - // num_cylinders: i32, - // horizon_placeholder1: u32, - // horizon_placeholder2: u32, - // horizon_placeholder3: u32, - // position_x: f32, - // position_y: f32, - // position_z: f32, - // speed: f32, - // power: f32, - // torque: f32, - // tire_temp_front_left: f32, - // tire_temp_front_right: f32, - // tire_temp_rear_left: f32, - // tire_temp_rear_right: f32, - // boost: f32, - // fuel: f32, - // distance_traveled: f32, - // best_lap: f32, - // last_lap: f32, - // current_lap: f32, - // current_race_time: f32, - // lap_number: u16, - // race_position: u8, - // accel: u8, - // brake: u8, - // clutch: u8, - // hand_brake: u8, - // gear: u8, - // steer: i8, - // normalized_driving_line: i8, - // normalized_ai_brake_difference: i8, - - // BORDER is_race_on: i32, + time_stamp_ms: u32, + engine_max_rpm: f32, engine_idle_rpm: f32, current_engine_rpm: f32, - acceleration_x: f32, + + acceleration_x: f32, // local space: X = right, Y = up, Z = forward acceleration_y: f32, acceleration_z: f32, - velocity_x: f32, + + velocity_x: f32, // local space: X = right, Y = up, Z = forward velocity_y: f32, velocity_z: f32, - angular_velocity_x: f32, + + angular_velocity_x: f32, // local space: X = pitch, Y = yaw, Z = roll angular_velocity_y: f32, angular_velocity_z: f32, + yaw: f32, pitch: f32, roll: f32, - normalized_suspension_travel_front_left: f32, + + normalized_suspension_travel_front_left: f32, // Suspension travel normalized: 0.0f = max stretch; 1.0 = max compression normalized_suspension_travel_front_right: f32, normalized_suspension_travel_rear_left: f32, normalized_suspension_travel_rear_right: f32, - tire_slip_ratio_front_left: f32, + + tire_slip_ratio_front_left: f32, // Tire normalized slip ratio, = 0 means 100% grip and |ratio| > 1.0 means loss of grip. tire_slip_ratio_front_right: f32, tire_slip_ratio_rear_left: f32, tire_slip_ratio_rear_right: f32, - wheel_rotation_speed_front_left: f32, + + wheel_rotation_speed_front_left: f32, // Wheel rotation speed radians/sec. wheel_rotation_speed_front_right: f32, wheel_rotation_speed_rear_left: f32, wheel_rotation_speed_rear_right: f32, - wheel_on_rumble_strip_front_left: i32, + + wheel_on_rumble_strip_front_left: i32, // = 1 when wheel is on rumble strip, = 0 when off. wheel_on_rumble_strip_front_right: i32, wheel_on_rumble_strip_rear_left: i32, wheel_on_rumble_strip_rear_right: i32, - wheel_in_puddle_depth_front_left: f32, + + wheel_in_puddle_depth_front_left: f32, // = from 0 to 1, where 1 is the deepest puddle wheel_in_puddle_depth_front_right: f32, wheel_in_puddle_depth_rear_left: f32, wheel_in_puddle_depth_rear_right: f32, - surface_rumble_front_left: f32, + + surface_rumble_front_left: f32, // Non-dimensional surface rumble values passed to controller force feedback surface_rumble_front_right: f32, surface_rumble_rear_left: f32, surface_rumble_rear_right: f32, - tire_slip_angle_front_left: f32, + + tire_slip_angle_front_left: f32, // Tire normalized slip angle, = 0 means 100% grip and |angle| > 1.0 means loss of grip. tire_slip_angle_front_right: f32, tire_slip_angle_rear_left: f32, tire_slip_angle_rear_right: f32, - tire_combined_slip_front_left: f32, + + tire_combined_slip_front_left: f32, // Tire normalized combined slip, = 0 means 100% grip and |slip| > 1.0 means loss of grip. tire_combined_slip_front_right: f32, tire_combined_slip_rear_left: f32, tire_combined_slip_rear_right: f32, - suspension_travel_meters_front_left: f32, + + suspension_travel_meters_front_left: f32, // Actual suspension travel in meters suspension_travel_meters_front_right: f32, suspension_travel_meters_rear_left: f32, suspension_travel_meters_rear_right: f32, + + car_ordinal: i32, car_class: i32, car_performance_index: i32, - drivetrai32ype: i32, + drivetraintype: i32, // 0 = FWD, 1 = RWD, 2 = AWD num_cylinders: i32, car_type: i32, + unknown1: u8, unknown2: u8, unknown3: u8, @@ -179,17 +107,20 @@ struct Telemetry { unknown6: u8, unknown7: u8, unknown8: u8, - car_ordinal: i32, - position_x: f32, + + position_x: f32, // meters position_y: f32, position_z: f32, - speed: f32, - power: f32, - torque: f32, + + speed: f32, // meters per second + power: f32, // watts + torque: f32, // newton meters + tire_temp_front_left: f32, tire_temp_front_right: f32, tire_temp_rear_left: f32, tire_temp_rear_right: f32, + boost: f32, fuel: f32, distance_traveled: f32, @@ -197,47 +128,95 @@ struct Telemetry { last_lap: f32, current_lap: f32, current_race_time: f32, + lap_number: i16, race_position: u8, + accel: u8, brake: u8, clutch: u8, handbrake: u8, gear: u8, steer: i8, + normalized_driving_line: i8, normalized_ai_brake_difference: i8, } fn listen(socket: &net::UdpSocket, mut buffer: &mut [u8]) -> usize { - let (number_of_bytes, _) = socket.recv_from(&mut buffer).expect("no data received"); + let number_of_bytes = match socket.recv_from(&mut buffer) { + Ok((num, _)) => num, + // skip packets if they're too big + Err(_) => 0, + }; number_of_bytes } + fn main() { let args = Args::parse(); let ip = format!("0.0.0.0:{}", args.port.to_string()); - if args.verbose { - if args.folder != "" { - println!("Folder: {}", args.folder); - } else { - println!("No folder specified - not saving logs"); - } + if args.folder != "" { + println!("Saving logs to {}", args.folder); + } else { + println!("No folder specified - saving to current directory"); } + let folder_path = Path::new(&args.folder); + fs::create_dir_all(folder_path).expect("couldnt create log directory!"); let socket = UdpSocket::bind(ip).expect("couldnt bind"); let mut buf = [0; 500]; - while listen(&socket, &mut buf) != 0 { - let deserialised: Telemetry = bincode::deserialize(&buf).expect("couldnt deser"); - if deserialised.current_engine_rpm == 0.0 { - continue; + let mut writer = csv::Writer::from_writer(tempfile().expect("couldnt open tempfile")); + + let mut inrace = false; + + 'listener: while listen(&socket, &mut buf) != 0 { + let deserialised: Telemetry = bincode::deserialize(&buf).expect("error parsing packet"); + if deserialised.is_race_on == 0 { + continue 'listener; } - if args.verbose { - println!( - "Gear: {}, RPM: {}", - deserialised.gear, deserialised.current_engine_rpm - ); + if args.verbose {} + + if inrace { + if deserialised.race_position == 0 { + // coming out of race + inrace = false; + writer.flush().expect("couldnt flush to file"); + println!( + "{}: no longer in race", + &Local::now().format("%H:%M:%S").to_string() + ); + continue 'listener; + } else { + // still in race + } + } else { + if deserialised.race_position > 0 { + // getting into race + inrace = true; + println!( + "{}: entering race", + &Local::now().format("%H:%M:%S").to_string() + ); + println!("car class: {}", deserialised.car_performance_index); + // open file for this race + // filename format: timestamp _ car class _ car ordinal + writer = csv::Writer::from_writer( + fs::File::create(folder_path.join(format!( + "{}_{}_{}{}", + &Local::now().format("%Y-%m-%d_%H-%M").to_string(), + deserialised.car_performance_index, + deserialised.car_ordinal, + ".csv", + ))) + .expect("couldnt open file"), + ); + } else { + // still not in race + continue 'listener; + } } + writer.serialize(deserialised).expect("couldnt serialise"); } }