diff --git a/Artemis/Artemis/Artemis.csproj b/Artemis/Artemis/Artemis.csproj
index c3f023764..9d63b14bb 100644
--- a/Artemis/Artemis/Artemis.csproj
+++ b/Artemis/Artemis/Artemis.csproj
@@ -377,6 +377,13 @@
AssettoCorsaView.xaml
+
+
+
+ FormulaOne2017View.xaml
+
+
+
@@ -881,6 +888,10 @@
MSBuild:Compile
Designer
+
+ MSBuild:Compile
+ Designer
+
Designer
MSBuild:Compile
diff --git a/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017DataModel.cs b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017DataModel.cs
new file mode 100644
index 000000000..3d036f77d
--- /dev/null
+++ b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017DataModel.cs
@@ -0,0 +1,89 @@
+using System.Runtime.InteropServices;
+using Artemis.Modules.Abstract;
+using MoonSharp.Interpreter;
+
+namespace Artemis.Modules.Games.FormulaOne2017
+{
+ [MoonSharpUserData]
+ public class FormulaOne2017DataModel : ModuleDataModel
+ {
+ public FormulaOne2017DataModel()
+ {
+ }
+
+ [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_xv; // Velocity in world space
+ public float m_yv; // Velocity in world space
+ public float m_zv; // Velocity in world space
+ public float m_xr; // World space right direction
+ public float m_yr; // World space right direction
+ public float m_zr; // World space right direction
+ 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
+ public float m_sli_pro_native_support; // SLI Pro support
+ 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_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_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 Rpm { get; set; }
+ public float MaxRpm { get; set; }
+ public float IdleRpm { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017Model.cs b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017Model.cs
new file mode 100644
index 000000000..31d10f66b
--- /dev/null
+++ b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017Model.cs
@@ -0,0 +1,69 @@
+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;
+
+ public FormulaOne2017Model(DeviceManager deviceManager, LuaManager luaManager) : base(deviceManager, luaManager)
+ {
+ Settings = SettingsProvider.Load();
+ DataModel = new FormulaOne2017DataModel();
+ ProcessNames.Add("F1_2017");
+ }
+
+ public override string Name => "FormulaOne2017";
+ public override bool IsOverlay => false;
+ public override bool IsBoundToProcess => true;
+
+ public override void Update()
+ {
+ }
+
+ public override void Enable()
+ {
+ _mustListen = true;
+ Task.Run(async () =>
+ {
+ using (var udpClient = new UdpClient(20777))
+ {
+ string loggingEvent = "";
+ while (_mustListen)
+ {
+ //IPEndPoint object will allow us to read datagrams sent from any source.
+ var receivedResults = await udpClient.ReceiveAsync();
+ HandleGameData(receivedResults);
+ }
+ }
+ });
+ base.Enable();
+ }
+
+ private void HandleGameData(UdpReceiveResult receivedResults)
+ {
+ 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;
+ }
+
+ public override void Dispose()
+ {
+ _mustListen = false;
+ base.Dispose();
+ }
+ }
+}
diff --git a/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017Settings.cs b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017Settings.cs
new file mode 100644
index 000000000..47e68c6bc
--- /dev/null
+++ b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017Settings.cs
@@ -0,0 +1,8 @@
+using Artemis.Modules.Abstract;
+
+namespace Artemis.Modules.Games.FormulaOne2017
+{
+ public class FormulaOne2017Settings : ModuleSettings
+ {
+ }
+}
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017View.xaml b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017View.xaml
new file mode 100644
index 000000000..c758c77f2
--- /dev/null
+++ b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017View.xaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017View.xaml.cs b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017View.xaml.cs
new file mode 100644
index 000000000..0d3e85bcd
--- /dev/null
+++ b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017View.xaml.cs
@@ -0,0 +1,12 @@
+using System.Windows.Controls;
+
+namespace Artemis.Modules.Games.FormulaOne2017
+{
+ public partial class FormulaOne2017View : UserControl
+ {
+ public FormulaOne2017View()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017ViewModel.cs b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017ViewModel.cs
new file mode 100644
index 000000000..42faf13bd
--- /dev/null
+++ b/Artemis/Artemis/Modules/Games/FormulaOne2017/FormulaOne2017ViewModel.cs
@@ -0,0 +1,17 @@
+using Artemis.Managers;
+using Artemis.Modules.Abstract;
+using Ninject;
+
+namespace Artemis.Modules.Games.FormulaOne2017
+{
+ public sealed class FormulaOne2017ViewModel : ModuleViewModel
+ {
+ public FormulaOne2017ViewModel(MainManager mainManager, [Named(nameof(FormulaOne2017Model))] ModuleModel moduleModel,
+ IKernel kernel) : base(mainManager, moduleModel, kernel)
+ {
+ DisplayName = "F1 2017";
+ }
+
+ public override bool UsesProfileEditor => true;
+ }
+}
\ No newline at end of file
diff --git a/Artemis/Artemis/Profiles/Layers/Models/LayerPropertiesModel.cs b/Artemis/Artemis/Profiles/Layers/Models/LayerPropertiesModel.cs
index 3fcd8df46..482782a29 100644
--- a/Artemis/Artemis/Profiles/Layers/Models/LayerPropertiesModel.cs
+++ b/Artemis/Artemis/Profiles/Layers/Models/LayerPropertiesModel.cs
@@ -42,7 +42,6 @@ namespace Artemis.Profiles.Layers.Models
public double Width { get; set; }
public double Height { get; set; }
public bool Contain { get; set; }
- public double Opacity { get; set; }
public double AnimationSpeed { get; set; }
public double OpacityEaseTime { get; set; }
public double HeightEaseTime { get; set; }
@@ -55,6 +54,10 @@ namespace Artemis.Profiles.Layers.Models
public List LayerKeybindModels { get; set; } = new List();
public List DynamicProperties { get; set; } = new List();
+ // Opacity isn't saved since it's only accesable by LUA
+ [JsonIgnore]
+ public double Opacity { get; set; } = 1;
+
[JsonConverter(typeof(BrushJsonConverter))]
public Brush Brush
{