diff --git a/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017DataModel.cs b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017DataModel.cs index 1a74a22d5..60a7c3156 100644 --- a/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017DataModel.cs +++ b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017DataModel.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; using Artemis.Modules.Abstract; using MoonSharp.Interpreter; @@ -21,14 +22,14 @@ namespace Artemis.Modules.Games.FormulaOne2017 [StructLayout(LayoutKind.Sequential)] public struct UdpPacketData { - public float m_time; // Total seconds driven from start line - public float m_lapTime; // Total seconds of current lap - public float m_lapDistance; // Total distance through lap in meters - public float m_totalDistance; // Total distance driven from start line - public float m_x; // World space position - public float m_y; // World space position - public float m_z; // World space position - public float m_speed; // Meters/sec + public float m_time; + public float m_lapTime; + public float m_lapDistance; + public float m_totalDistance; + public float m_x; // World space position + public float m_y; // World space position + public float m_z; // World space position + public float m_speed; // Speed of car in MPH public float m_xv; // Velocity in world space public float m_yv; // Velocity in world space public float m_zv; // Velocity in world space @@ -38,58 +39,122 @@ namespace Artemis.Modules.Games.FormulaOne2017 public float m_xd; // World space forward direction public float m_yd; // World space forward direction public float m_zd; // World space forward direction - public float m_susp_pos_bl; // - public float m_susp_pos_br; // - public float m_susp_pos_fl; // - public float m_susp_pos_fr; // - public float m_susp_vel_bl; // - public float m_susp_vel_br; // - public float m_susp_vel_fl; // - public float m_susp_vel_fr; // - public float m_wheel_speed_bl; // - public float m_wheel_speed_br; // - public float m_wheel_speed_fl; // - public float m_wheel_speed_fr; // - public float m_throttle; // Throttle input - public float m_steer; // Steering input (-1 left to +1 right) - public float m_brake; // Brake input - public float m_clutch; // Clutch input - public float m_gear; // 0 - R | 1 - N | 2-9 - 1-8 - public float m_gforce_lat; // Lateral G's - public float m_gforce_lon; // Longiitude G's - public float m_lap; // Current lap number - public float m_engineRate; // Engine RPM + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public float[] m_susp_pos; // Note: All wheel arrays have the order: + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public float[] m_susp_vel; // RL, RR, FL, FR + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public float[] m_wheel_speed; + public float m_throttle; + public float m_steer; + public float m_brake; + public float m_clutch; + public float m_gear; + public float m_gforce_lat; + public float m_gforce_lon; + public float m_lap; + public float m_engineRate; public float m_sli_pro_native_support; // SLI Pro support - public float m_car_position; // car race position + public float m_car_position; // car race position public float m_kers_level; // kers energy left public float m_kers_max_level; // kers maximum energy - public float m_drs; // 0 = off, 1 = on - public float m_traction_control; // 0 (off) - 2 (high) - public float m_anti_lock_brakes; // 0 (off) - 1 (on) - public float m_fuel_in_tank; // current fuel mass - public float m_fuel_capacity; // fuel capacity - public float m_in_pits; // 0 = none, 1 = pitting, 2 = in pit area - public float m_sector; // 0 = sector1, 1 = sector2; 2 = sector3 - public float m_sector1_time; // time of sector1 (or 0) - public float m_sector2_time; // time of sector2 (or 0) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public float[] m_brakes_temp; // brakes temperature (centigrade) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public float[] m_wheels_pressure; // wheels pressure PSI - public float m_team_info; // team ID + public float m_drs; // 0 = off, 1 = on + public float m_traction_control; // 0 (off) - 2 (high) + public float m_anti_lock_brakes; // 0 (off) - 1 (on) + public float m_fuel_in_tank; // current fuel mass + public float m_fuel_capacity; // fuel capacity + public float m_in_pits; // 0 = none, 1 = pitting, 2 = in pit area + public float m_sector; // 0 = sector1, 1 = sector2, 2 = sector3 + public float m_sector1_time; // time of sector1 (or 0) + public float m_sector2_time; // time of sector2 (or 0) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public float[] m_brakes_temp; // brakes temperature (centigrade) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public float[] m_tyres_pressure; // tyres pressure PSI + public float m_team_info; // team ID public float m_total_laps; // total number of laps in this race public float m_track_size; // track size meters - public float m_last_lap_time; // last lap time - public float m_max_rpm; // cars max RPM, at which point the rev limiter will kick in - public float m_idle_rpm; // cars idle RPM - public float m_max_gears; // maximum number of gears - public float m_sessionType; // 0 = unknown, 1 = practice, 2 = qualifying, 3 = race + public float m_last_lap_time; // last lap time + public float m_max_rpm; // cars max RPM, at which point the rev limiter will kick in + public float m_idle_rpm; // cars idle RPM + public float m_max_gears; // maximum number of gears + public float m_sessionType; // 0 = unknown, 1 = practice, 2 = qualifying, 3 = race public float m_drsAllowed; // 0 = not allowed, 1 = allowed, -1 = invalid / unknown - public float m_track_number; // -1 for unknown, 0-21 for tracks - public float m_vehicleFIAFlags; // -1 = invalid/unknown, 0 = none, 1 = green, 2 = blue, 3 = yellow, 4 = red + public float m_track_number; // -1 for unknown, 0-21 for tracks + public float m_vehicleFIAFlags; // -1 = invalid/unknown, 0 = none, 1 = green, 2 = blue, 3 = yellow, 4 = red + public float m_era; // era, 2017 (modern) or 1980 (classic) + public float m_engine_temperature; // engine temperature (centigrade) + public float m_gforce_vert; // vertical g-force component + public float m_ang_vel_x; // angular velocity x-component + public float m_ang_vel_y; // angular velocity y-component + public float m_ang_vel_z; // angular velocity z-component + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] m_tyres_temperature; // tyres temperature (centigrade) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] m_tyres_wear; // tyre wear percentage + public byte m_tyre_compound; // compound of tyre – 0 = ultra soft, 1 = super soft, 2 = soft, 3 = medium, 4 = hard, 5 = inter, 6 = wet + public byte m_front_brake_bias; // front brake bias (percentage) + public byte m_fuel_mix; // fuel mix - 0 = lean, 1 = standard, 2 = rich, 3 = max + public byte m_currentLapInvalid; // current lap invalid - 0 = valid, 1 = invalid + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] m_tyres_damage; // tyre damage (percentage) + public byte m_front_left_wing_damage; // front left wing damage (percentage) + public byte m_front_right_wing_damage; // front right wing damage (percentage) + public byte m_rear_wing_damage; // rear wing damage (percentage) + public byte m_engine_damage; // engine damage (percentage) + public byte m_gear_box_damage; // gear box damage (percentage) + public byte m_exhaust_damage; // exhaust damage (percentage) + public byte m_pit_limiter_status; // pit limiter status – 0 = off, 1 = on + public byte m_pit_speed_limit; // pit speed limit in mph + public float m_session_time_left; // NEW: time left in session in seconds + public byte m_rev_lights_percent; // NEW: rev lights indicator (percentage) + public byte m_is_spectating; // NEW: whether the player is spectating + public byte m_spectator_car_index; // NEW: index of the car being spectated + + // Car data + public byte m_num_cars; // number of cars in data + public byte m_player_car_index; // index of player's car in the array + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public CarUDPData[] m_car_data; // data for all cars on track + + public float m_yaw; // NEW (v1.8) + public float m_pitch; // NEW (v1.8) + public float m_roll; // NEW (v1.8) + public float m_x_local_velocity; // NEW (v1.8) Velocity in local space + public float m_y_local_velocity; // NEW (v1.8) Velocity in local space + public float m_z_local_velocity; // NEW (v1.8) Velocity in local space + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public float[] m_susp_acceleration; // NEW (v1.8) RL, RR, FL, FR + public float m_ang_acc_x; // NEW (v1.8) angular acceleration x-component + public float m_ang_acc_y; // NEW (v1.8) angular acceleration y-component + public float m_ang_acc_z; // NEW (v1.8) angular acceleration z-component } + public struct CarUDPData + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public float[] m_worldPosition; // world co-ordinates of vehicle + public float m_lastLapTime; + public float m_currentLapTime; + public float m_bestLapTime; + public float m_sector1Time; + public float m_sector2Time; + public float m_lapDistance; + public byte m_driverId; + public byte m_teamId; + public byte m_carPosition; // UPDATED: track positions of vehicle + public byte m_currentLapNum; + public byte m_tyreCompound; // compound of tyre – 0 = ultra soft, 1 = super soft, 2 = soft, 3 = medium, 4 = hard, 5 = inter, 6 = wet + public byte m_inPits; // 0 = none, 1 = pitting, 2 = in pit area + public byte m_sector; // 0 = sector1, 1 = sector2, 2 = sector3 + public byte m_currentLapInvalid; // current lap invalid - 0 = valid, 1 = invalid + public byte m_penalties; // NEW: accumulated time penalties in seconds to be added + }; + #endregion } + [MoonSharpUserData] public class Car { public Car() @@ -101,21 +166,24 @@ namespace Artemis.Modules.Games.FormulaOne2017 public CarOverview Overview { get; set; } public CarDetails Details { get; set; } - public float SpeedMps { get; set; } + public double SpeedKph { get; set; } + public double SpeedMph { get; set; } public float Steering { get; set; } public float Throttle { get; set; } public float Brake { get; set; } public float Clutch { get; set; } - + public bool Drs { get; set; } } + [MoonSharpUserData] public class CarDetails { public float Rpm { get; set; } public float MaxRpm { get; set; } public float IdleRpm { get; set; } + public int RevLightsPercent { get; set; } public int Gear { get; set; } public int MaxGear { get; set; } @@ -127,7 +195,7 @@ namespace Artemis.Modules.Games.FormulaOne2017 public float MaxFuel { get; set; } public float LateralG { get; set; } - public float LongitudeG { get; set; } + public float LongitudinalG { get; set; } public float WheelSpeedFrontLeft { get; set; } public float WheelSpeedFrontRight { get; set; } @@ -135,21 +203,53 @@ namespace Artemis.Modules.Games.FormulaOne2017 public float WheelSpeedRearRight { get; set; } } + [MoonSharpUserData] public class CarOverview { - public TractionControl TractionControl { get; set; } + public F1Team Team { get; set; } + public AssistLevel TractionControl { get; set; } public bool AntiLockBrakes { get; set; } + + public static AssistLevel FloatToAssistLevel(float input) + { + // ReSharper disable CompareOfFloatsByEqualityOperator + if (input == 0) + return AssistLevel.Off; + if (input == 0.5) + return AssistLevel.Medium; + if (input == 1) + return AssistLevel.High; + // ReSharper restore CompareOfFloatsByEqualityOperator + + return AssistLevel.Off; + } } - public enum TractionControl + public enum F1Team { - Off = 0, - Medium = 1, - High = 2 + RedBull = 0, + Ferrari = 1, + McLaren = 2, + Renault = 3, + Mercedes = 4, + Sauber = 5, + ForceIndia = 6, + Williams = 7, + TorroRosso = 8, + Haas = 11 } + public enum AssistLevel + { + Off, + Medium, + High + } + + [MoonSharpUserData] public class Session { + public F1Track Track { get; set; } public SessionType SessionType { get; set; } public bool DrsEnabled { get; set; } public SessionFlag Flags { get; set; } @@ -161,10 +261,39 @@ namespace Artemis.Modules.Games.FormulaOne2017 public float TotalDistance { get; set; } public float LapDistance { get; set; } - public int LabNumber { get; set; } + public int LapNumber { get; set; } + public int TotalLaps { get; set; } public int Position { get; set; } } + public enum F1Track + { + Australia = 0, + China = 2, + Bahrain = 3, + BahrainShort = 21, + Russia = 18, + Spain = 4, + Monaco = 5, + Canada = 6, + Azerbaijan = 20, + Austria = 17, + Britain = 7, + BritainShort = 22, + Hungary = 9, + Belgium = 10, + Italy = 11, + Singapore = 12, + Malaysia = 1, + Japan = 13, + JapanShort = 24, + USA = 15, + USAShort = 23, + Mexico = 19, + Brazil = 16, + AbuDhabi = 14 + } + public enum SessionFlag { Unknown = -1, diff --git a/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017Model.cs b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017Model.cs index df8d71f2f..e6b2596a9 100644 --- a/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017Model.cs +++ b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017Model.cs @@ -1,18 +1,21 @@ using System; using System.Net.Sockets; using System.Runtime.InteropServices; -using System.Text; using System.Threading.Tasks; using Artemis.DAL; using Artemis.Managers; using Artemis.Modules.Abstract; + namespace Artemis.Modules.Games.FormulaOne2017 { public class FormulaOne2017Model : ModuleModel { - private UdpClient _udpListener; private bool _mustListen; + private UdpClient _udpClient; + private UdpClient _udpListener; + private DateTime _lastUpdate; + private int _revAtZeroFrames; public FormulaOne2017Model(DeviceManager deviceManager, LuaManager luaManager) : base(deviceManager, luaManager) { @@ -27,6 +30,9 @@ namespace Artemis.Modules.Games.FormulaOne2017 public override void Update() { + // If we're not receiving updates, assume the game is paused/in the main menu + if (DateTime.Now - _lastUpdate > TimeSpan.FromSeconds(1)) + ((FormulaOne2017DataModel) DataModel).Session.SessionType = SessionType.Unknown; } public override void Enable() @@ -34,15 +40,19 @@ namespace Artemis.Modules.Games.FormulaOne2017 _mustListen = true; Task.Run(async () => { - using (var udpClient = new UdpClient(20777)) + _udpClient = new UdpClient(20777); + while (_mustListen) { - string loggingEvent = ""; - while (_mustListen) + //IPEndPoint object will allow us to read datagrams sent from any source. + try { - //IPEndPoint object will allow us to read datagrams sent from any source. - var receivedResults = await udpClient.ReceiveAsync(); + var receivedResults = await _udpClient.ReceiveAsync(); HandleGameData(receivedResults); } + catch (ObjectDisposedException) + { + // ignored, happens when shutting the module down + } } }); base.Enable(); @@ -50,19 +60,82 @@ namespace Artemis.Modules.Games.FormulaOne2017 private void HandleGameData(UdpReceiveResult receivedResults) { + _lastUpdate = DateTime.Now; var dataModel = (FormulaOne2017DataModel) DataModel; var pinnedPacket = GCHandle.Alloc(receivedResults.Buffer, GCHandleType.Pinned); var msg = (FormulaOne2017DataModel.UdpPacketData) Marshal.PtrToStructure(pinnedPacket.AddrOfPinnedObject(), typeof(FormulaOne2017DataModel.UdpPacketData)); pinnedPacket.Free(); -// dataModel.Rpm = msg.m_engineRate; -// dataModel.MaxRpm = msg.m_max_rpm; -// dataModel.IdleRpm = msg.m_idle_rpm; + dataModel.Car.SpeedKph = msg.m_speed * 1.609344; + dataModel.Car.SpeedMph = msg.m_speed; + + dataModel.Car.Steering = msg.m_steer; + dataModel.Car.Throttle = msg.m_throttle; + dataModel.Car.Brake = msg.m_brake; + dataModel.Car.Clutch = msg.m_clutch; + + dataModel.Car.Drs = msg.m_drs > 0; + + dataModel.Car.Overview.Team = (F1Team) msg.m_team_info; + dataModel.Car.Overview.TractionControl = CarOverview.FloatToAssistLevel(msg.m_traction_control); + dataModel.Car.Overview.AntiLockBrakes = msg.m_anti_lock_brakes > 0; + + dataModel.Car.Details.Rpm = msg.m_engineRate; + dataModel.Car.Details.MaxRpm = msg.m_max_rpm; + dataModel.Car.Details.IdleRpm = msg.m_idle_rpm; + + // The one the game provides is all over the place at max rev causing blinking etc + // easily fixed by simply ignoring rapid changes from 100 to 0 + if (msg.m_rev_lights_percent == 0) + _revAtZeroFrames++; + else + _revAtZeroFrames = 0; + if (_revAtZeroFrames > 2 || msg.m_rev_lights_percent != 0 || dataModel.Car.Details.RevLightsPercent != 100) + dataModel.Car.Details.RevLightsPercent = msg.m_rev_lights_percent; + + dataModel.Car.Details.Gear = (int) msg.m_gear; + dataModel.Car.Details.MaxGear = (int) msg.m_max_gears; + + dataModel.Car.Details.Kers = msg.m_kers_level; + dataModel.Car.Details.MaxKers = msg.m_kers_max_level; + + dataModel.Car.Details.Fuel = msg.m_fuel_in_tank; + dataModel.Car.Details.MaxFuel = msg.m_fuel_capacity; + + dataModel.Car.Details.LateralG = msg.m_gforce_lat; + dataModel.Car.Details.LongitudinalG = msg.m_gforce_lon; + +// dataModel.Car.Details.WheelSpeedFrontLeft = msg.m_wheel_speed_fl; +// dataModel.Car.Details.WheelSpeedFrontRight = msg.m_wheel_speed_fr; +// dataModel.Car.Details.WheelSpeedRearLeft = msg.m_wheel_speed_bl; +// dataModel.Car.Details.WheelSpeedRearRight = msg.m_wheel_speed_br; + + dataModel.Session.SessionType = (SessionType) msg.m_sessionType; + // It's unknown in time trial but lets overwrite that to race + if (dataModel.Session.SessionType == SessionType.Unknown && dataModel.Car.SpeedMph > 0) + dataModel.Session.SessionType = SessionType.Race; + + dataModel.Session.DrsEnabled = msg.m_drsAllowed > 0; + dataModel.Session.Flags = (SessionFlag) msg.m_vehicleFIAFlags; + + dataModel.Session.TotalSeconds = msg.m_time; + dataModel.Session.LapSeconds = msg.m_lapTime; + + dataModel.Session.Track = (F1Track) msg.m_track_number; + dataModel.Session.TrackLength = msg.m_track_size; + dataModel.Session.TotalDistance = msg.m_totalDistance; + dataModel.Session.LapDistance = msg.m_lapDistance; + + dataModel.Session.LapNumber = (int) msg.m_lap; + dataModel.Session.TotalLaps = (int) msg.m_total_laps; + dataModel.Session.Position = (int) msg.m_car_position; } public override void Dispose() { _mustListen = false; + _udpClient.Dispose(); + _udpClient = null; base.Dispose(); } }