From c68ec210d13cad47a26e78986831325586634436 Mon Sep 17 00:00:00 2001 From: hawkeye Date: Thu, 14 Mar 2024 18:50:32 -0400 Subject: [PATCH] Added dynamic lod with auto fps --- DomainModel/DomainModel.csproj | 6 +- DomainModel/DynamicLod/DynamicLodSimData.cs | 38 + DomainModel/DynamicLod/LodConfig.cs | 11 - .../ObservableLodConfigLinkedList.cs | 72 -- DomainModel/Setting/DynamicLodSetting.cs | 69 +- MainApp/App.xaml.cs | 3 +- .../Dialog/AddProfileDialog.xaml | 2 +- .../AppUserControl/DynamicLodPreference.xaml | 753 ++++++------------ .../DynamicLodPreference.xaml.cs | 145 +--- MainApp/AppUserControl/PreferenceDrawer.xaml | 18 +- MainApp/MainApp.csproj | 6 +- Orchestration/AppOrchestrator.cs | 9 - Orchestration/AppSettingData.cs | 11 - Orchestration/DynamicLodManager.cs | 257 ------ Orchestration/DynamicLodOrchestrator.cs | 336 ++++++++ Orchestration/FlightSimData.cs | 11 +- Orchestration/FlightSimOrchestrator.cs | 197 +++-- Orchestration/Orchestration.csproj | 6 +- Shared/Shared.csproj | 6 +- SimconnectAgent/Enums.cs | 2 + SimconnectAgent/FpsCalc.cs | 36 + SimconnectAgent/SimConnectEvent.cs | 6 +- SimconnectAgent/SimConnectProvider.cs | 61 +- SimconnectAgent/SimConnector.cs | 136 +++- SimconnectAgent/SimDataDefinitions.cs | 18 +- SimconnectAgent/SimconnectAgent.csproj | 6 +- WindowsAgent/WindowActionManager.cs | 5 + WindowsAgent/WindowsAgent.csproj | 6 +- 28 files changed, 1066 insertions(+), 1166 deletions(-) create mode 100644 DomainModel/DynamicLod/DynamicLodSimData.cs delete mode 100644 DomainModel/DynamicLod/LodConfig.cs delete mode 100644 DomainModel/DynamicLod/ObservableLodConfigLinkedList.cs delete mode 100644 Orchestration/DynamicLodManager.cs create mode 100644 Orchestration/DynamicLodOrchestrator.cs create mode 100644 SimconnectAgent/FpsCalc.cs diff --git a/DomainModel/DomainModel.csproj b/DomainModel/DomainModel.csproj index 7703d85..591a6a0 100644 --- a/DomainModel/DomainModel.csproj +++ b/DomainModel/DomainModel.csproj @@ -11,9 +11,9 @@ https://github.com/hawkeye-stan/msfs-popout-panel-manager MSFSPopoutPanelManager.DomainModel x64 - 4.1.0.4 - 4.1.0.4 - 4.1.0.4 + 4.1.1.0 + 4.1.1.0 + 4.1.1.0 win-x64 Embedded Debug;Release;Local diff --git a/DomainModel/DynamicLod/DynamicLodSimData.cs b/DomainModel/DynamicLod/DynamicLodSimData.cs new file mode 100644 index 0000000..a09b952 --- /dev/null +++ b/DomainModel/DynamicLod/DynamicLodSimData.cs @@ -0,0 +1,38 @@ +using MSFSPopoutPanelManager.Shared; + +namespace MSFSPopoutPanelManager.DomainModel.DynamicLod +{ + public class DynamicLodSimData : ObservableObject + { + public int Fps { get; set; } + + public int Tlod { get; set; } + + public int Olod { get; set; } + + public double Agl { get; set; } + + public string CloudQuality { get; set; } = "N/A"; + + public double AltAboveGround { get; set; } + + public double AltAboveGroundMinusCg { get; set; } + + public double GroundVelocity { get; set; } + + public bool PlaneOnGround { get; set; } = true; + + public void Clear() + { + Fps = 0; + Tlod = 0; + Olod = 0; + Agl = 0; + CloudQuality = "N/A"; + AltAboveGround = 0; + AltAboveGroundMinusCg = 0; + GroundVelocity = 0; + PlaneOnGround = true; + } + } +} diff --git a/DomainModel/DynamicLod/LodConfig.cs b/DomainModel/DynamicLod/LodConfig.cs deleted file mode 100644 index b20d484..0000000 --- a/DomainModel/DynamicLod/LodConfig.cs +++ /dev/null @@ -1,11 +0,0 @@ -using MSFSPopoutPanelManager.Shared; - -namespace MSFSPopoutPanelManager.DomainModel.DynamicLod -{ - public class LodConfig : ObservableObject - { - public int Agl { get; set; } - - public int Lod { get; set; } - } -} diff --git a/DomainModel/DynamicLod/ObservableLodConfigLinkedList.cs b/DomainModel/DynamicLod/ObservableLodConfigLinkedList.cs deleted file mode 100644 index 4c20b04..0000000 --- a/DomainModel/DynamicLod/ObservableLodConfigLinkedList.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Specialized; - -namespace MSFSPopoutPanelManager.DomainModel.DynamicLod -{ - public class ObservableLodConfigLinkedList : LinkedList, INotifyCollectionChanged - { - public event NotifyCollectionChangedEventHandler CollectionChanged; - public void OnNotifyCollectionChanged(NotifyCollectionChangedAction action, LodConfig item) - { - if (CollectionChanged == null) - return; - - switch (action) - { - case NotifyCollectionChangedAction.Add: - case NotifyCollectionChangedAction.Remove: - case NotifyCollectionChangedAction.Reset: - CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); - break; - } - } - - public new void AddFirst(LodConfig item) - { - base.AddFirst(item); - OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item); - } - - public new void AddLast(LodConfig item) - { - base.AddLast(item); - OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item); - } - - public new void AddBefore(LinkedListNode node, LinkedListNode newNode) - { - base.AddBefore(node, newNode); - OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, newNode.Value); - } - - public new void AddAfter(LinkedListNode node, LinkedListNode newNode) - { - base.AddAfter(node, newNode); - OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, newNode.Value); - } - - public new void Remove(LodConfig item) - { - base.Remove(item); - OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, item); - } - - public new void RemoveFirst() - { - if (First == null) - return; - - base.RemoveFirst(); - OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, First.Value); - } - - public new void RemoveLast() - { - if (Last == null) - return; - - base.RemoveLast(); - OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, Last.Value); - } - } -} diff --git a/DomainModel/Setting/DynamicLodSetting.cs b/DomainModel/Setting/DynamicLodSetting.cs index ad6847b..95de666 100644 --- a/DomainModel/Setting/DynamicLodSetting.cs +++ b/DomainModel/Setting/DynamicLodSetting.cs @@ -1,8 +1,4 @@ -using MSFSPopoutPanelManager.DomainModel.DynamicLod; -using MSFSPopoutPanelManager.Shared; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Runtime.Serialization; +using MSFSPopoutPanelManager.Shared; namespace MSFSPopoutPanelManager.DomainModel.Setting { @@ -11,31 +7,13 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting public DynamicLodSetting() { InitializeChildPropertyChangeBinding(); - - TlodConfigs.CollectionChanged += (_, e) => - { - if (e.Action == NotifyCollectionChangedAction.Add) - { - if (e.NewItems?[0] is LodConfig item) - item.PropertyChanged += (_, _) => TlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item); - } - }; - - OlodConfigs.CollectionChanged += (_, e) => - { - if (e.Action == NotifyCollectionChangedAction.Add) - { - if (e.NewItems?[0] is LodConfig item) - item.PropertyChanged += (_, _) => OlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item); - } - }; } public bool IsEnabled { get; set; } = false; - public ObservableLodConfigLinkedList TlodConfigs { get; set; } = new(); + public bool PauseWhenMsfsLoseFocus { get; set; } = true; - public ObservableLodConfigLinkedList OlodConfigs { get; set; } = new(); + public bool PauseOutsideCockpitView { get; set; } = true; public bool ResetEnabled { get; set; } = false; @@ -43,29 +21,28 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting public int ResetOlod { get; set; } = 100; - public void AddDefaultTLodConfigs() - { - TlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 0, Lod = 100 })); - TlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 5000, Lod = 200 })); - TlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 10000, Lod = 300 })); - TlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 20000, Lod = 400 })); - } + public int TargetedFps { get; set; } = 60; - public void AddDefaultOLodConfigs() - { - OlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 0, Lod = 200 })); - OlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 1000, Lod = 150 })); - OlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 5000, Lod = 100 })); - } + public int FpsTolerance { get; set; } = 5; - [OnDeserialized] - private void OnDeserialization(StreamingContext context) - { - foreach (var item in TlodConfigs) - item.PropertyChanged += (_, _) => TlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item); + public bool TlodMinOnGround { get; set; } = true; - foreach (var item in OlodConfigs) - item.PropertyChanged += (_, _) => OlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item); - } + public int AltTlodBase { get; set; } = 1000; + + public int TlodMin { get; set; } = 50; + + public int TlodMax { get; set; } = 400; + + public int CloudRecoveryTlod { get; set; } = 100; + + public bool DecreaseCloudQuality { get; set; } = true; + + public int OlodTop { get; set; } = 20; + + public int OlodBase { get; set; } = 200; + + public int AltOlodBase { get; set; } = 1000; + + public int AltOlodTop { get; set; } = 10000; } } diff --git a/MainApp/App.xaml.cs b/MainApp/App.xaml.cs index 5ec511d..15c9509 100644 --- a/MainApp/App.xaml.cs +++ b/MainApp/App.xaml.cs @@ -53,10 +53,11 @@ namespace MSFSPopoutPanelManager.MainApp services.AddSingleton(s => new AppOrchestrator(SharedStorage, s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); services.AddSingleton(_ => new ProfileOrchestrator(SharedStorage)); + services.AddSingleton(_ => new DynamicLodOrchestrator(SharedStorage)); services.AddSingleton(s => new PanelSourceOrchestrator(SharedStorage, s.GetRequiredService())); services.AddSingleton(s => new PanelPopOutOrchestrator(SharedStorage, s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); services.AddSingleton(s => new PanelConfigurationOrchestrator(SharedStorage, s.GetRequiredService(), s.GetRequiredService())); - services.AddSingleton(_ => new FlightSimOrchestrator(SharedStorage)); + services.AddSingleton(s => new FlightSimOrchestrator(SharedStorage, s.GetRequiredService())); services.AddSingleton(_ => new KeyboardOrchestrator(SharedStorage)); services.AddSingleton(_ => new HelpOrchestrator()); diff --git a/MainApp/AppUserControl/Dialog/AddProfileDialog.xaml b/MainApp/AppUserControl/Dialog/AddProfileDialog.xaml index 8c7237c..ebb7c8e 100644 --- a/MainApp/AppUserControl/Dialog/AddProfileDialog.xaml +++ b/MainApp/AppUserControl/Dialog/AddProfileDialog.xaml @@ -2,13 +2,13 @@ x:Class="MSFSPopoutPanelManager.MainApp.AppUserControl.Dialog.AddProfileDialog" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:converter="clr-namespace:MSFSPopoutPanelManager.MainApp.Converter" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:MSFSPopoutPanelManager.MainApp" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewmodel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel" xmlns:wpf="clr-namespace:MaterialDesignThemes.Wpf;assembly=MaterialDesignThemes.Wpf" - xmlns:converter="clr-namespace:MSFSPopoutPanelManager.MainApp.Converter" Width="400" Height="210" DataContext="{Binding RelativeSource={RelativeSource Self}, Path=ViewModel}" diff --git a/MainApp/AppUserControl/DynamicLodPreference.xaml b/MainApp/AppUserControl/DynamicLodPreference.xaml index b985f66..b6f5bf7 100644 --- a/MainApp/AppUserControl/DynamicLodPreference.xaml +++ b/MainApp/AppUserControl/DynamicLodPreference.xaml @@ -3,26 +3,33 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mainApp="clr-namespace:MSFSPopoutPanelManager.MainApp" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:system="clr-namespace:System;assembly=System.Runtime" xmlns:viewModel="clr-namespace:MSFSPopoutPanelManager.MainApp.ViewModel" - xmlns:appUserControl="clr-namespace:MSFSPopoutPanelManager.MainApp.AppUserControl" d:DesignHeight="800" d:DesignWidth="800" mc:Ignorable="d"> 22 22 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Padding="5,2,5,2" + Header="Sim Values" + Style="{StaticResource MaterialDesignGroupBox}"> + + + + + + + + + + + + + - - - - - Object Level of Detail (OLOD) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Enable reset of TLOD and OLOD to the following values when flight session ends. - - + + + General Options + - - - - - - - - + Margin="0,0,0,0" + Padding="5,5,0,0" + materialDesign:HintAssist.FloatingScale="0.75" + materialDesign:HintAssist.Foreground="White" + materialDesign:HintAssist.Hint="Targeted FPS" + Background="#607D8B" + PreviewTextInput="TxtBox_NumbersOnly" + Style="{StaticResource MaterialDesignFloatingHintTextBox}" + Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.TargetedFps, Mode=TwoWay}" /> + + + + Pause when MSFS loses focus + + + + Pause when not in cockpit view + + + + TLOD Options + + + + + + + TLOD minimum on ground/TLOD Base Altitude + + + + OLOD Options + + + + + + + + Cloud Options + - - - - - - - - + Margin="0,0,0,0" + materialDesign:HintAssist.FloatingScale="0.75" + materialDesign:HintAssist.Hint="Cloud Adjust at TLOD" + PreviewTextInput="TxtBox_NumbersOnly" + Style="{StaticResource MaterialDesignFloatingHintTextBox}" + Text="{Binding AppSettingData.ApplicationSetting.DynamicLodSetting.CloudRecoveryTlod, Mode=TwoWay}" /> + + + Decrease cloud quality by one level to archive desired FPS + + + + Reset TLOD and OLOD on Exit + + + + + Reset on Exit + + + + diff --git a/MainApp/AppUserControl/DynamicLodPreference.xaml.cs b/MainApp/AppUserControl/DynamicLodPreference.xaml.cs index d8c07c1..4f420b6 100644 --- a/MainApp/AppUserControl/DynamicLodPreference.xaml.cs +++ b/MainApp/AppUserControl/DynamicLodPreference.xaml.cs @@ -1,22 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; +using MSFSPopoutPanelManager.MainApp.ViewModel; using System.ComponentModel; using System.Globalization; -using System.Linq; using System.Windows; using System.Windows.Controls; -using System.Windows.Data; -using MSFSPopoutPanelManager.DomainModel.DynamicLod; -using MSFSPopoutPanelManager.MainApp.ViewModel; +using System.Windows.Input; namespace MSFSPopoutPanelManager.MainApp.AppUserControl { public partial class DynamicLodPreference { - private ObservableLodConfigLinkedList _tlodConfigs; - private ObservableLodConfigLinkedList _olodConfigs; - public DynamicLodPreference() { if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) @@ -24,146 +16,21 @@ namespace MSFSPopoutPanelManager.MainApp.AppUserControl InitializeComponent(); return; } - - AddTlodConfigs = new ObservableCollection() { new () }; - AddOlodConfigs = new ObservableCollection() { new () }; - + Loaded += (_, _) => { InitializeComponent(); var dataContext = DataContext as ApplicationViewModel; - _tlodConfigs = dataContext?.AppSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs; - _olodConfigs = dataContext?.AppSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs; }; } - - public ObservableCollection AddTlodConfigs { get; set; } - public ObservableCollection AddOlodConfigs { get; set; } - - private void AddTlod_SourceUpdated(object sender, DataTransferEventArgs e) + private void TxtBox_NumbersOnly(object sender, TextCompositionEventArgs e) { - var textBox = sender as TextBox; - - var lodConfig = textBox?.DataContext as TempLodConfig; - - if (lodConfig?.Agl == null || lodConfig.Lod == null) - return; - - if (UpdateTlodDuplicate(lodConfig)) - { - RebindTLodGrid(); - return; - } - - var targetLodConfig = _tlodConfigs.LastOrDefault(x => lodConfig.Agl >= x.Agl); - var newLodConfig = new LodConfig { Agl = (int)lodConfig.Agl, Lod = (int)lodConfig.Lod }; - - if (targetLodConfig == null) - _tlodConfigs.AddFirst(newLodConfig); - else - _tlodConfigs.AddAfter(_tlodConfigs.Find(targetLodConfig), new LinkedListNode(newLodConfig)); - - RebindTLodGrid(); - } - - private void TLodDelete_Click(object sender, RoutedEventArgs e) - { - var button = e.Source as Button; - - if(button?.DataContext is not LodConfig lodConfig) - return; - - _tlodConfigs.Remove(lodConfig); - - RebindTLodGrid(); - } - - private void RebindTLodGrid() - { - this.TlodGrid.ItemsSource = null; - this.TlodGrid.ItemsSource = _tlodConfigs.ToList(); - - AddTlodConfigs.Clear(); - AddTlodConfigs.Add(new TempLodConfig()); - } - - private bool UpdateTlodDuplicate(TempLodConfig lodConfig) - { - var tlodConfig = _tlodConfigs.FirstOrDefault(x => x.Agl == lodConfig.Agl); - - if(tlodConfig == null) - return false; - - tlodConfig.Lod = Convert.ToInt32(lodConfig.Lod); - return true; - } - - private void AddOlod_SourceUpdated(object sender, DataTransferEventArgs e) - { - var textBox = sender as TextBox; - - var lodConfig = textBox?.DataContext as TempLodConfig; - - if (lodConfig?.Agl == null || lodConfig.Lod == null) - return; - - if (UpdateOlodDuplicate(lodConfig)) - { - RebindOLodGrid(); - return; - } - - var targetLodConfig = _olodConfigs.LastOrDefault(x => lodConfig.Agl >= x.Agl); - var newLodConfig = new LodConfig() { Agl = (int)lodConfig.Agl, Lod = (int)lodConfig.Lod }; - - if (targetLodConfig == null) - _olodConfigs.AddFirst(newLodConfig); - else - _olodConfigs.AddAfter(_olodConfigs.Find(targetLodConfig), new LinkedListNode(newLodConfig)); - - RebindOLodGrid(); - } - - private void OLodDelete_Click(object sender, RoutedEventArgs e) - { - var button = e.Source as Button; - - if (button?.DataContext is not LodConfig lodConfig) - return; - - _olodConfigs.Remove(lodConfig); - - RebindOLodGrid(); - } - - private void RebindOLodGrid() - { - this.OlodGrid.ItemsSource = null; - this.OlodGrid.ItemsSource = _olodConfigs.ToList(); - - AddOlodConfigs.Clear(); - AddOlodConfigs.Add(new TempLodConfig()); - } - - private bool UpdateOlodDuplicate(TempLodConfig lodConfig) - { - var olodConfig = _olodConfigs.FirstOrDefault(x => x.Agl == lodConfig.Agl); - - if (olodConfig == null) - return false; - - olodConfig.Lod = Convert.ToInt32(lodConfig.Lod); - return true; + e.Handled = !(int.TryParse(e.Text, out _) || (e.Text.Trim() == "-")); } } - public class TempLodConfig - { - public int? Agl { get; set; } - - public int? Lod { get; set; } - } + public class AglValidationRule : ValidationRule { diff --git a/MainApp/AppUserControl/PreferenceDrawer.xaml b/MainApp/AppUserControl/PreferenceDrawer.xaml index b9dd135..56ab59e 100644 --- a/MainApp/AppUserControl/PreferenceDrawer.xaml +++ b/MainApp/AppUserControl/PreferenceDrawer.xaml @@ -633,21 +633,23 @@ - Dynamically adjust Terrain and Object Level of Details (Not Supported) + + + (Unsupported Feature) + + Dynamically adjust Terrain and Object Level of Details + - - *** Experimental and not supported. Use at your own risk since this feature directly modifies MSFS memory. *** - - Enable automatic adjustment of terrain level of detail (TLOD) and object level of detail (OLOD) based on current aircraft's above ground level (in feet). + Enable automatic adjustments of TLOD and OLOD to get the desired targeted frame rate (FPS). diff --git a/MainApp/MainApp.csproj b/MainApp/MainApp.csproj index 9dea47e..4e2fcfc 100644 --- a/MainApp/MainApp.csproj +++ b/MainApp/MainApp.csproj @@ -14,9 +14,9 @@ MSFSPopoutPanelManager.MainApp logo.ico x64 - 4.1.0.4 - 4.1.0.4 - 4.1.0.4 + 4.1.1.0 + 4.1.1.0 + 4.1.1.0 embedded en diff --git a/Orchestration/AppOrchestrator.cs b/Orchestration/AppOrchestrator.cs index 671b411..81a1b4c 100644 --- a/Orchestration/AppOrchestrator.cs +++ b/Orchestration/AppOrchestrator.cs @@ -33,15 +33,6 @@ namespace MSFSPopoutPanelManager.Orchestration public void Initialize() { - // Add default dynamic LOD configs - if (!AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled && - AppSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.Count == 0 && - AppSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.Count == 0) - { - AppSettingData.ApplicationSetting.DynamicLodSetting.AddDefaultTLodConfigs(); - AppSettingData.ApplicationSetting.DynamicLodSetting.AddDefaultOLodConfigs(); - } - if (AppSettingData.ApplicationSetting.GeneralSetting.CheckForUpdate) CheckForAutoUpdate(); diff --git a/Orchestration/AppSettingData.cs b/Orchestration/AppSettingData.cs index 7dc2a31..0d219a0 100644 --- a/Orchestration/AppSettingData.cs +++ b/Orchestration/AppSettingData.cs @@ -38,17 +38,6 @@ namespace MSFSPopoutPanelManager.Orchestration break; } }; - - ApplicationSetting.DynamicLodSetting.TlodConfigs.CollectionChanged += (_, e) => - { - if (e.Action == NotifyCollectionChangedAction.Reset) - AppSettingDataManager.WriteAppSetting(ApplicationSetting); - }; - - ApplicationSetting.DynamicLodSetting.OlodConfigs.CollectionChanged += (_, _) => - { - AppSettingDataManager.WriteAppSetting(ApplicationSetting); - }; } } } diff --git a/Orchestration/DynamicLodManager.cs b/Orchestration/DynamicLodManager.cs deleted file mode 100644 index e08dda7..0000000 --- a/Orchestration/DynamicLodManager.cs +++ /dev/null @@ -1,257 +0,0 @@ -using MSFSPopoutPanelManager.DomainModel.DynamicLod; -using MSFSPopoutPanelManager.WindowsAgent; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices; - -namespace MSFSPopoutPanelManager.Orchestration -{ - public class DynamicLodManager - { - private const string SIMMODULE_NAME = "WwiseLibPCx64P.dll"; - private const int PROCESS_VM_OPERATION = 0x0008; - private const int PROCESS_VM_READ = 0x0010; - private const int PROCESS_VM_WRITE = 0x0020; - private const long OFFSET_MODULE_BASE =0x004B2368; - private const long OFFSET_POINTER_MAIN = 0x3D0; - private const long OFFSET_POINTER_TLOD_VR = 0x114; - private const long OFFSET_POINTER_TLOD = 0xC; - private const long OFFSET_POINTER_OLOD = 0xC; - - private static bool _isActive; - private static WindowProcess _process; - private static IntPtr _processHandle; - private static long _processModuleAddress; - - private static long _addressMain; - private static long _addressTlod; - private static long _addressOlod; - private static long _addressTlodVr; - private static long _addressOlodVr; - - private static FlightSimData _flightSimData; - private static AppSettingData _appSettingData; - private static LinkedListNode _nextTlod; - private static LinkedListNode _nextOlod; - private static LinkedListNode _currentTlod; - private static LinkedListNode _currentOlod; - - public static void Attach(FlightSimData flightSimData, AppSettingData appSettingData) - { - if (appSettingData == null || _isActive) - return; - - _flightSimData = flightSimData; - _appSettingData = appSettingData; - _flightSimData.OnAltAboveGroundChanged -= HandleOnAltAboveGroundChanged; - _flightSimData.OnAltAboveGroundChanged += HandleOnAltAboveGroundChanged; - - _process = WindowProcessManager.SimulatorProcess; - _processModuleAddress = GetSimModuleAddress(); - - if (_process == null || _processModuleAddress == IntPtr.Zero) - return; - - _processHandle = PInvoke.OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, _process.ProcessId); - - if (_processHandle == IntPtr.Zero) - return; - - _addressMain = ReadMemory(_processModuleAddress + OFFSET_MODULE_BASE) + OFFSET_POINTER_MAIN; - _addressTlod = ReadMemory(_addressMain) + OFFSET_POINTER_TLOD; - _addressTlodVr = ReadMemory(_addressMain) + OFFSET_POINTER_TLOD_VR; - _addressOlod = _addressTlod + OFFSET_POINTER_OLOD; - _addressOlodVr = _addressTlodVr + OFFSET_POINTER_OLOD; - - // Set initial LOD if already in cockpit - InitializeData(); - - _isActive = true; - } - - public static void Detach() - { - if (_appSettingData == null) - return; - - if (_appSettingData.ApplicationSetting.DynamicLodSetting.ResetEnabled) - { - WriteTlod(_appSettingData.ApplicationSetting.DynamicLodSetting.ResetTlod); - WriteOlod(_appSettingData.ApplicationSetting.DynamicLodSetting.ResetOlod); - } - - _isActive = false; - - Debug.WriteLine($"Reset to custom LOD: TLOD: {_appSettingData.ApplicationSetting.DynamicLodSetting.ResetTlod}, OLOD: {_appSettingData.ApplicationSetting.DynamicLodSetting.ResetOlod}"); - } - - private static void HandleOnAltAboveGroundChanged(object sender, EventArgs e) - { - if (!_flightSimData.IsFlightStarted) - return; - - var agl = _flightSimData.PlaneAltAboveGround < 0 ? 0 : _flightSimData.PlaneAltAboveGround; - - if (_nextTlod != null && _nextTlod.Value.Agl <= agl) - { - _currentTlod = _nextTlod; - _nextTlod = _currentTlod.Next; - DynamicLodManager.WriteTlod(_currentTlod.Value.Lod); - } - else if (_currentTlod is { Previous: not null } && _currentTlod.Value.Agl > agl) - { - _currentTlod = _currentTlod.Previous; - - if (_currentTlod != null) - { - _nextTlod = _currentTlod.Next; - DynamicLodManager.WriteTlod(_currentTlod.Value.Lod); - } - } - - if (_nextOlod != null && _nextOlod.Value.Agl <= agl) - { - _currentOlod = _nextOlod; - _nextOlod = _currentOlod.Next; - DynamicLodManager.WriteOlod(_currentOlod.Value.Lod); - } - else if (_currentOlod is { Previous: not null } && _currentOlod.Value.Agl > agl) - { - _currentOlod = _currentOlod.Previous; - - if (_currentOlod != null) - { - _nextOlod = _currentOlod.Next; - DynamicLodManager.WriteOlod(_currentOlod.Value.Lod); - } - } - - if(_currentTlod != null && _currentOlod != null) - Debug.WriteLine($"Altitude: {agl}, TLOD: {_currentTlod.Value.Lod}, OLOD: {_currentOlod.Value.Lod}"); - } - - private static void WriteTlod(int value, bool isVr = false) - { - WriteMemory(isVr ? _addressTlodVr : _addressTlod, value / 100.0f); - } - - private static void WriteOlod(int value, bool isVr = false) - { - WriteMemory(isVr ? _addressOlodVr : _addressOlod, value / 100.0f); - } - - private static long GetSimModuleAddress() - { - if (_process == null) - return -1; - - foreach (ProcessModule processModule in _process.Modules) - { - if (processModule.ModuleName == SIMMODULE_NAME) - return processModule.BaseAddress; - } - - return -1; - } - - private static void WriteMemory(long address, object value) - { - try - { - var buffer = StructureToByteArray(value); - PInvoke.NtWriteVirtualMemory(checked((int)_processHandle), address, buffer, buffer.Length, out _); - } - catch - { - // ignored - } - } - - private static T ReadMemory(long address) where T : struct - { - try - { - var byteSize = Marshal.SizeOf(typeof(T)); - var buffer = new byte[byteSize]; - PInvoke.NtReadVirtualMemory(checked((int)_processHandle), address, buffer, buffer.Length, out _); - return ByteArrayToStructure(buffer); - } - catch - { - // ignored - } - - return default(T); - } - - private static byte[] StructureToByteArray(object obj) - { - var length = Marshal.SizeOf(obj); - var array = new byte[length]; - var pointer = Marshal.AllocHGlobal(length); - - Marshal.StructureToPtr(obj, pointer, true); - Marshal.Copy(pointer, array, 0, length); - Marshal.FreeHGlobal(pointer); - - return array; - } - - private static T ByteArrayToStructure(byte[] bytes) where T : struct - { - var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); - - try - { - var result = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); - - if(result != null) - return (T)result; - - return default(T); - } - finally - { - handle.Free(); - } - } - - private static void InitializeData() - { - var agl = _flightSimData.PlaneAltAboveGround < 0 ? 0 : _flightSimData.PlaneAltAboveGround; - - var tlod = _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.FirstOrDefault(x => x.Agl > agl); - _nextTlod = _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.Find(tlod); - - tlod = _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.LastOrDefault(x => x.Agl <= agl); - _currentTlod = _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.Find(tlod) ?? _appSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs.Last; - - if (_currentTlod != null) - DynamicLodManager.WriteTlod(_currentTlod.Value.Lod); - - var olod = _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.FirstOrDefault(x => x.Agl > agl); - _nextOlod = _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.Find(olod); - - olod = _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.LastOrDefault(x => x.Agl <= agl); - _currentOlod = _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.Find(olod) ?? _appSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs.Last; - - if (_currentOlod != null) - DynamicLodManager.WriteOlod(_currentOlod.Value.Lod); - - if (_currentTlod != null && _currentOlod != null) - Debug.WriteLine($"Initialize Altitude: {agl}, TLOD: {_currentTlod.Value.Lod}, OLOD: {_currentOlod.Value.Lod}"); - } - - //private static int ReadTlod(bool isVr = false) - //{ - // return Convert.ToInt32(ReadMemory(isVr ? _addressTlodVr : _addressTlod) * 100.0f); - //} - - //private static int ReadOlod(bool isVr = false) - //{ - // return Convert.ToInt32(ReadMemory(isVr ? _addressOlodVr : _addressOlod) * 100.0f); - //} - } -} diff --git a/Orchestration/DynamicLodOrchestrator.cs b/Orchestration/DynamicLodOrchestrator.cs new file mode 100644 index 0000000..1253473 --- /dev/null +++ b/Orchestration/DynamicLodOrchestrator.cs @@ -0,0 +1,336 @@ +using MSFSPopoutPanelManager.DomainModel.DynamicLod; +using MSFSPopoutPanelManager.DomainModel.Setting; +using MSFSPopoutPanelManager.Shared; +using MSFSPopoutPanelManager.SimConnectAgent; +using MSFSPopoutPanelManager.WindowsAgent; +using Newtonsoft.Json.Linq; +using System; +using System.Diagnostics; +using System.Dynamic; +using System.Runtime.InteropServices; + +namespace MSFSPopoutPanelManager.Orchestration +{ + public class DynamicLodOrchestrator : BaseOrchestrator + { + private const string SIMMODULE_NAME = "WwiseLibPCx64P.dll"; + private const int PROCESS_VM_OPERATION = 0x0008; + private const int PROCESS_VM_READ = 0x0010; + private const int PROCESS_VM_WRITE = 0x0020; + private const long OFFSET_MODULE_BASE =0x004B2368; + private const long OFFSET_POINTER_MAIN = 0x3D0; + private const long OFFSET_POINTER_TLOD_VR = 0x114; + private const long OFFSET_POINTER_TLOD = 0xC; + private const long OFFSET_POINTER_OLOD = 0xC; + private const long OFFSET_POINTER_CLOUDQ = 0x44; + private const long OFFSET_POINTER_CLOUDQ_VR = 0x108; + private const long OFFSET_POINTER_VR_MODE = 0x1C; + private const long OFFSET_POINTER_FG_MODE = 0x4A; + private const long OFFSET_POINTER_ANSIO_FILTER = -0x18; + private const long OFFSET_POINTER_WATER_WAVES = 0x3C; + + private bool _isActive; + private WindowProcess _process; + private IntPtr _processHandle; + private long _processModuleAddress; + + private long _addressTlod; + private long _addressOlod; + private long _addressTlodVr; + private long _addressOlodVr; + private long _addressCloudQ; + private long _addressCloudQVr; + private long _addressVrMode; + private long _addressFgMode; + + private DynamicLodSetting DynamicLodSetting => AppSettingData.ApplicationSetting.DynamicLodSetting; + + private DynamicLodSimData SimData => FlightSimData.DynamicLodSimData; + + private DateTime _lastLodUpdateTime = DateTime.Now; + private bool _isDecreasedCloudQualityActive = false; + + public DynamicLodOrchestrator(SharedStorage sharedStorage) : base(sharedStorage) {} + + public void Attach() + { + if (AppSettingData == null || _isActive) + return; + + _process = WindowProcessManager.SimulatorProcess; + _processModuleAddress = GetSimModuleAddress(); + + if (_process == null || _processModuleAddress == IntPtr.Zero) + return; + + _processHandle = PInvoke.OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, _process.ProcessId); + + if (_processHandle == IntPtr.Zero) + return; + + _addressTlod = ReadMemory(_processModuleAddress + OFFSET_MODULE_BASE) + OFFSET_POINTER_MAIN; + + if (_addressTlod > 0) + { + _addressTlodVr = ReadMemory(_addressTlod) + OFFSET_POINTER_TLOD_VR; + _addressTlod = ReadMemory(_addressTlod) + OFFSET_POINTER_TLOD; + _addressOlod = _addressTlod + OFFSET_POINTER_OLOD; + _addressOlodVr = _addressTlodVr + OFFSET_POINTER_OLOD; + _addressCloudQ = _addressTlod + OFFSET_POINTER_CLOUDQ; + _addressCloudQVr = _addressCloudQ + OFFSET_POINTER_CLOUDQ_VR; + _addressVrMode = _addressTlod - OFFSET_POINTER_VR_MODE; + _addressFgMode = _addressTlod - OFFSET_POINTER_FG_MODE; + + if (!MemoryBoundaryTest()) + { + FileLogger.WriteLog("Unable to validate memory space for Dynamic LOD", StatusMessageType.Error); + return; + } + } + + _isActive = true; + + _lastLodUpdateTime = DateTime.Now; + _isDecreasedCloudQualityActive = false; + } + + public void Detach() + { + if (DynamicLodSetting == null) + return; + + if (DynamicLodSetting.ResetEnabled) + { + var isVr = ReadIsVr(); + WriteMemory(isVr ? _addressTlodVr : _addressTlod, DynamicLodSetting.ResetTlod / 100.0f); + WriteMemory(isVr ? _addressOlodVr : _addressOlod, DynamicLodSetting.ResetOlod / 100.0f); + } + + _isActive = false; + + Debug.WriteLine($"Reset to custom LOD: TLOD: {DynamicLodSetting.ResetTlod}, OLOD: {DynamicLodSetting.ResetOlod}"); + } + + public int ReadTlod(bool isVr = false) + { + return Convert.ToInt32(ReadMemory(isVr ? _addressTlodVr : _addressTlod) * 100.0f); + } + + public int ReadOlod(bool isVr = false) + { + return Convert.ToInt32(ReadMemory(isVr ? _addressOlodVr : _addressOlod) * 100.0f); + } + + public string ReadCloudQuality(bool isVr = false) + { + return ReadCloudQualitySimValue(isVr) switch + { + 0 => "Low", + 1 => "Medium", + 2 => "High", + 3 => "Ultra", + _ => "N/A" + }; + } + + public int ReadCloudQualitySimValue(bool isVr = false) + { + return Convert.ToInt32(ReadMemory(isVr ? _addressCloudQVr : _addressCloudQ)); + } + + public bool ReadIsVr() + { + return ReadMemory(_addressVrMode) == 1; + } + + public bool ReadIsFg(bool isVr) + { + if (isVr) + return false; + + return ReadMemory(_addressFgMode) == 1; + } + + public void UpdateLod(bool isVr) + { + if (DateTime.Now - _lastLodUpdateTime <= TimeSpan.FromSeconds(1)) + return; + + if (!FlightSimData.IsFlightStarted || !FlightSimData.IsInCockpit || (DynamicLodSetting.PauseOutsideCockpitView && FlightSimData.CameraState != CameraState.Cockpit)) + return; + + var deltaFps = SimData.Fps - DynamicLodSetting.TargetedFps; + if (Math.Abs(deltaFps) < DynamicLodSetting.TargetedFps * DynamicLodSetting.FpsTolerance / 100.0) // within FPS tolerance + return; + + _lastLodUpdateTime = DateTime.Now; + + SetTlod(deltaFps); + SetOlod(); + } + + private long GetSimModuleAddress() + { + if (_process == null) + return -1; + + foreach (ProcessModule processModule in _process.Modules) + { + if (processModule.ModuleName == SIMMODULE_NAME) + return processModule.BaseAddress; + } + + return -1; + } + + private void WriteMemory(long address, object value) + { + try + { + var buffer = StructureToByteArray(value); + PInvoke.NtWriteVirtualMemory(checked((int)_processHandle), address, buffer, buffer.Length, out _); + } + catch + { + // ignored + } + } + + private T ReadMemory(long address) where T : struct + { + try + { + var byteSize = Marshal.SizeOf(typeof(T)); + var buffer = new byte[byteSize]; + PInvoke.NtReadVirtualMemory(checked((int)_processHandle), address, buffer, buffer.Length, out _); + return ByteArrayToStructure(buffer); + } + catch + { + // ignored + } + + return default(T); + } + + private byte[] StructureToByteArray(object obj) + { + var length = Marshal.SizeOf(obj); + var array = new byte[length]; + var pointer = Marshal.AllocHGlobal(length); + + Marshal.StructureToPtr(obj, pointer, true); + Marshal.Copy(pointer, array, 0, length); + Marshal.FreeHGlobal(pointer); + + return array; + } + + private T ByteArrayToStructure(byte[] bytes) where T : struct + { + var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + + try + { + var result = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); + + if(result != null) + return (T)result; + + return default(T); + } + finally + { + handle.Free(); + } + } + + private bool MemoryBoundaryTest() + { + // Boundary check a few known setting memory addresses to see if any fail which likely indicates MSFS memory map has changed + if (ReadTlod() < 10 || ReadTlod() > 1000 || ReadTlod(true) < 10 || ReadTlod(true) > 1000 + || ReadOlod() < 10 || ReadOlod() > 1000 || ReadOlod(true) < 10 || ReadOlod(true) > 1000 + || ReadCloudQuality() == "N/A" || ReadCloudQuality(true) == "N/A" + || ReadMemory(_addressVrMode) < 0 || ReadMemory(_addressVrMode) > 1 + || ReadMemory(_addressTlod + OFFSET_POINTER_ANSIO_FILTER) < 1 || ReadMemory(_addressTlod + OFFSET_POINTER_ANSIO_FILTER) > 16 + || !(ReadMemory(_addressTlod + OFFSET_POINTER_WATER_WAVES) == 128 || ReadMemory(_addressTlod + OFFSET_POINTER_WATER_WAVES) == 256 || ReadMemory(_addressTlod + OFFSET_POINTER_WATER_WAVES) == 512)) + { + return false; + } + + return true; + } + + private void SetTlod(int deltaFps, bool isVr = false) + { + var tlodStep = Math.Max(5, Math.Abs(deltaFps / 2)); + var newTlod = SimData.Tlod + Math.Sign(deltaFps) * tlodStep; + + if (DynamicLodSetting.TlodMinOnGround && SimData.AltAboveGround <= DynamicLodSetting.AltTlodBase) + { + newTlod = DynamicLodSetting.TlodMin; + } + else if (newTlod < DynamicLodSetting.TlodMin) + { + newTlod = DynamicLodSetting.TlodMin; + } + else if (newTlod > DynamicLodSetting.TlodMax) + { + newTlod = DynamicLodSetting.TlodMax; + } + + if (ReadTlod(isVr) == newTlod) + return; + + // Adjust cloud quality if applicable + if (deltaFps < 0 && newTlod < DynamicLodSetting.CloudRecoveryTlod && !_isDecreasedCloudQualityActive) + { + _isDecreasedCloudQualityActive = true; + WriteMemory(isVr ? _addressCloudQVr : _addressCloudQ, ReadCloudQualitySimValue(isVr) - 1); // High + + _lastLodUpdateTime = _lastLodUpdateTime.AddSeconds(2); // Add extra delay for cloud setting to take effect + Debug.WriteLine("New Cloud Quality written - 2."); + + return; + } + + if (deltaFps > 0 && newTlod >= DynamicLodSetting.CloudRecoveryTlod && _isDecreasedCloudQualityActive) + { + _isDecreasedCloudQualityActive = false; + WriteMemory(isVr ? _addressCloudQVr : _addressCloudQ, ReadCloudQualitySimValue(isVr) + 1); // Ultra + + _lastLodUpdateTime = _lastLodUpdateTime.AddSeconds(2); + Debug.WriteLine("New Cloud Quality written - 3."); + + return; + } + + Debug.WriteLine($"New TLOD written - {newTlod}."); + WriteMemory(isVr ? _addressTlodVr : _addressTlod, newTlod / 100.0f); + } + + private void SetOlod(bool isVr = false) + { + int newOlod; + + if (SimData.AltAboveGround < DynamicLodSetting.AltOlodBase) + { + newOlod = DynamicLodSetting.OlodBase; + } + else if (SimData.AltAboveGround > DynamicLodSetting.AltOlodTop) + { + newOlod = DynamicLodSetting.OlodTop; + } + else + { + newOlod = Convert.ToInt32(DynamicLodSetting.OlodBase - (DynamicLodSetting.OlodBase - DynamicLodSetting.OlodTop) * (SimData.AltAboveGround - DynamicLodSetting.AltOlodBase) / (DynamicLodSetting.AltOlodTop - DynamicLodSetting.AltOlodBase)); + } + + if (ReadOlod(isVr) == newOlod) + return; + + Debug.WriteLine($"New OLOD written - {newOlod}."); + WriteMemory(isVr ? _addressOlodVr : _addressOlod, newOlod / 100.0f); + } + } +} diff --git a/Orchestration/FlightSimData.cs b/Orchestration/FlightSimData.cs index e8bb18c..3177087 100644 --- a/Orchestration/FlightSimData.cs +++ b/Orchestration/FlightSimData.cs @@ -2,6 +2,7 @@ using MSFSPopoutPanelManager.Shared; using System; using System.ComponentModel; +using MSFSPopoutPanelManager.DomainModel.DynamicLod; using MSFSPopoutPanelManager.SimConnectAgent; namespace MSFSPopoutPanelManager.Orchestration @@ -15,8 +16,6 @@ namespace MSFSPopoutPanelManager.Orchestration IsSimConnectActive = false; } - public event EventHandler OnAltAboveGroundChanged; - public bool IsSimConnectActive { get; set; } public string AircraftName { get; set; } @@ -43,8 +42,6 @@ namespace MSFSPopoutPanelManager.Orchestration public bool PlaneInParkingSpot { get; set; } - public int PlaneAltAboveGround { get; set; } - public bool IsSimulatorStarted { get; set; } public bool IsSimConnectDataReceived { get; set; } @@ -55,6 +52,8 @@ namespace MSFSPopoutPanelManager.Orchestration public IHudBarData HudBarData { get; set; } + public DynamicLodSimData DynamicLodSimData { get; set; } = new(); + [IgnorePropertyChanged] internal ProfileData ProfileDataRef { get; set; } @@ -65,9 +64,6 @@ namespace MSFSPopoutPanelManager.Orchestration // Automatic switching of active profile when SimConnect active aircraft change if (e.PropertyName == "AircraftName") ProfileDataRef.AutoSwitchProfile(); - - if(e.PropertyName == "PlaneAltAboveGround") - OnAltAboveGroundChanged?.Invoke(this, EventArgs.Empty); } public void Reset() @@ -85,7 +81,6 @@ namespace MSFSPopoutPanelManager.Orchestration PlaneInParkingSpot = false; CameraState = CameraState.Unknown; IsSimulatorStarted = false; - PlaneAltAboveGround = 0; CameraViewTypeAndIndex1Max = 0; CameraViewTypeAndIndex2Max = 0; } diff --git a/Orchestration/FlightSimOrchestrator.cs b/Orchestration/FlightSimOrchestrator.cs index 5923020..202c7a8 100644 --- a/Orchestration/FlightSimOrchestrator.cs +++ b/Orchestration/FlightSimOrchestrator.cs @@ -15,11 +15,13 @@ namespace MSFSPopoutPanelManager.Orchestration private System.Timers.Timer _msfsGameExitDetectionTimer; private SimConnectProvider _simConnectProvider; + private DynamicLodOrchestrator _dynamicLodOrchestrator; private bool _isTurnedOnPower; private bool _isTurnedOnAvionics; - public FlightSimOrchestrator(SharedStorage sharedStorage) : base(sharedStorage) + public FlightSimOrchestrator(SharedStorage sharedStorage, DynamicLodOrchestrator dynamicLodOrchestrator) : base(sharedStorage) { + _dynamicLodOrchestrator = dynamicLodOrchestrator; _simConnectProvider = new SimConnectProvider(); } @@ -40,9 +42,7 @@ namespace MSFSPopoutPanelManager.Orchestration WindowProcessManager.GetSimulatorProcess(); // refresh simulator process DetectMsfsExit(); - // Attach in memory override for Dynamic LOD - if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled) - DynamicLodManager.Attach(FlightSimData, AppSettingData); + StartDynamicLod(); }; _simConnectProvider.OnDisconnected += (_, _) => @@ -61,61 +61,39 @@ namespace MSFSPopoutPanelManager.Orchestration _simConnectProvider.OnSimConnectDataRequiredRefreshed += (_, e) => { - var electricalMasterBattery = Convert.ToBoolean(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.ElectricalMasterBattery).Value); - if (electricalMasterBattery != FlightSimData.ElectricalMasterBatteryStatus) - FlightSimData.ElectricalMasterBatteryStatus = electricalMasterBattery; - - var avionicsMasterSwitch = Convert.ToBoolean(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.AvionicsMasterSwitch).Value); - if (avionicsMasterSwitch != FlightSimData.AvionicsMasterSwitchStatus) - FlightSimData.AvionicsMasterSwitchStatus = avionicsMasterSwitch; - - var trackIR = Convert.ToBoolean(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.TrackIREnable).Value); - if (trackIR != FlightSimData.TrackIRStatus) - FlightSimData.TrackIRStatus = trackIR; - - var cameraStateInt = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraState).Value); - var result = Enum.TryParse(cameraStateInt.ToString(), out var cameraState); - if (!result) - cameraState = CameraState.Unknown; - if (cameraState != FlightSimData.CameraState) - FlightSimData.CameraState = cameraState; - - var cockpitCameraZoom = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CockpitCameraZoom).Value); - if (cockpitCameraZoom != FlightSimData.CockpitCameraZoom) - FlightSimData.CockpitCameraZoom = cockpitCameraZoom; - - var planeAltAboveGround = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.PlaneAltAboveGround).Value); - if (planeAltAboveGround != FlightSimData.PlaneAltAboveGround) - FlightSimData.PlaneAltAboveGround = planeAltAboveGround; - - var cameraViewTypeAndIndex0 = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex0).Value); - if (cameraViewTypeAndIndex0 != FlightSimData.CameraViewTypeAndIndex0) - FlightSimData.CameraViewTypeAndIndex0 = cameraViewTypeAndIndex0; - - var cameraViewTypeAndIndex1 = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex1).Value); - if (cameraViewTypeAndIndex1 != FlightSimData.CameraViewTypeAndIndex1) - FlightSimData.CameraViewTypeAndIndex1 = cameraViewTypeAndIndex1; - - var cameraViewTypeAndIndex1Max = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex1Max).Value); - if (cameraViewTypeAndIndex1Max != FlightSimData.CameraViewTypeAndIndex1Max) - FlightSimData.CameraViewTypeAndIndex1Max = cameraViewTypeAndIndex1Max; - - var cameraViewTypeAndIndex2Max = Convert.ToInt32(e.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex2Max).Value); - if (cameraViewTypeAndIndex2Max != FlightSimData.CameraViewTypeAndIndex2Max) - FlightSimData.CameraViewTypeAndIndex2Max = cameraViewTypeAndIndex2Max; - - FlightSimData.IsSimConnectDataReceived = true; + MapRequiredSimConnectData(e); }; _simConnectProvider.OnSimConnectDataHudBarRefreshed += (_, e) => { - if (ProfileData.ActiveProfile.ProfileSetting.HudBarConfig.IsEnabled) - MapHudBarSimConnectData(e); + if (!ProfileData.ActiveProfile.ProfileSetting.HudBarConfig.IsEnabled || !FlightSimData.IsFlightStarted) + return; + + MapHudBarSimConnectData(e); + }; + + _simConnectProvider.OnSimConnectDataDynamicLodRefreshed += (_, e) => + { + if (!AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled || !FlightSimData.IsFlightStarted || !WindowActionManager.IsMsfsInFocus()) + return; + + var isVr = _dynamicLodOrchestrator.ReadIsVr(); + + MapDynamicLodSimConnectData(e, isVr); + _dynamicLodOrchestrator.UpdateLod(isVr); + }; + + _simConnectProvider.OnSimConnectDataEventFrameRefreshed += (_, e) => + { + if (!AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled || !FlightSimData.IsFlightStarted) + return; + + MapEventFrameData(e); }; _simConnectProvider.OnActiveAircraftChanged += (_, e) => { - var aircraftName = String.IsNullOrEmpty(e) ? null : e; + var aircraftName = string.IsNullOrEmpty(e) ? null : e; if (FlightSimData.AircraftName != aircraftName) { FlightSimData.AircraftName = aircraftName; @@ -392,9 +370,7 @@ namespace MSFSPopoutPanelManager.Orchestration FlightSimData.IsFlightStarted = true; - // Attach in memory override for Dynamic LOD - if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled) - DynamicLodManager.Attach(FlightSimData, AppSettingData); + StartDynamicLod(); } private void HandleOnFlightStopped(object sender, EventArgs e) @@ -409,9 +385,8 @@ namespace MSFSPopoutPanelManager.Orchestration FlightSimData.IsFlightStarted = false; - // Detach in memory override for Dynamic LOD - if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled) - DynamicLodManager.Detach(); + StopDynamicLod(); + FlightSimData.DynamicLodSimData.Clear(); } private void DetectMsfsExit() @@ -436,6 +411,50 @@ namespace MSFSPopoutPanelManager.Orchestration }; } + private void MapRequiredSimConnectData(List simData) + { + var electricalMasterBattery = Convert.ToBoolean(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.ElectricalMasterBattery).Value); + if (electricalMasterBattery != FlightSimData.ElectricalMasterBatteryStatus) + FlightSimData.ElectricalMasterBatteryStatus = electricalMasterBattery; + + var avionicsMasterSwitch = Convert.ToBoolean(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.AvionicsMasterSwitch).Value); + if (avionicsMasterSwitch != FlightSimData.AvionicsMasterSwitchStatus) + FlightSimData.AvionicsMasterSwitchStatus = avionicsMasterSwitch; + + var trackIR = Convert.ToBoolean(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.TrackIREnable).Value); + if (trackIR != FlightSimData.TrackIRStatus) + FlightSimData.TrackIRStatus = trackIR; + + var cameraStateInt = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraState).Value); + var result = Enum.TryParse(cameraStateInt.ToString(), out var cameraState); + if (!result) + cameraState = CameraState.Unknown; + if (cameraState != FlightSimData.CameraState) + FlightSimData.CameraState = cameraState; + + var cockpitCameraZoom = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CockpitCameraZoom).Value); + if (cockpitCameraZoom != FlightSimData.CockpitCameraZoom) + FlightSimData.CockpitCameraZoom = cockpitCameraZoom; + + var cameraViewTypeAndIndex0 = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex0).Value); + if (cameraViewTypeAndIndex0 != FlightSimData.CameraViewTypeAndIndex0) + FlightSimData.CameraViewTypeAndIndex0 = cameraViewTypeAndIndex0; + + var cameraViewTypeAndIndex1 = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex1).Value); + if (cameraViewTypeAndIndex1 != FlightSimData.CameraViewTypeAndIndex1) + FlightSimData.CameraViewTypeAndIndex1 = cameraViewTypeAndIndex1; + + var cameraViewTypeAndIndex1Max = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex1Max).Value); + if (cameraViewTypeAndIndex1Max != FlightSimData.CameraViewTypeAndIndex1Max) + FlightSimData.CameraViewTypeAndIndex1Max = cameraViewTypeAndIndex1Max; + + var cameraViewTypeAndIndex2Max = Convert.ToInt32(simData.Find(d => d.PropertyName == SimDataDefinitions.PropName.CameraViewTypeAndIndex2Max).Value); + if (cameraViewTypeAndIndex2Max != FlightSimData.CameraViewTypeAndIndex2Max) + FlightSimData.CameraViewTypeAndIndex2Max = cameraViewTypeAndIndex2Max; + + FlightSimData.IsSimConnectDataReceived = true; + } + private void MapHudBarSimConnectData(List simData) { if (CompareSimConnectData(simData, SimDataDefinitions.PropName.ElevatorTrim, FlightSimData.HudBarData.ElevatorTrim, out var newValue)) @@ -466,6 +485,49 @@ namespace MSFSPopoutPanelManager.Orchestration FlightSimData.HudBarData.SimRate = newValue; } + private void MapDynamicLodSimConnectData(List simData, bool isVr) + { + if (CompareSimConnectData(simData, SimDataDefinitions.PropName.PlaneAltAboveGround, FlightSimData.DynamicLodSimData.Agl, out var newValue)) + FlightSimData.DynamicLodSimData.Agl = newValue; + + if (CompareSimConnectData(simData, SimDataDefinitions.PropName.PlaneAltAboveGround, FlightSimData.DynamicLodSimData.AltAboveGround, out newValue)) + FlightSimData.DynamicLodSimData.AltAboveGround = newValue; + + if (CompareSimConnectData(simData, SimDataDefinitions.PropName.PlaneAltAboveGroundMinusCg, FlightSimData.DynamicLodSimData.AltAboveGroundMinusCg, out newValue)) + FlightSimData.DynamicLodSimData.AltAboveGroundMinusCg = newValue; + + if (CompareSimConnectData(simData, SimDataDefinitions.PropName.GroundVelocity, FlightSimData.DynamicLodSimData.GroundVelocity, out newValue)) + FlightSimData.DynamicLodSimData.GroundVelocity = newValue; + + if (CompareSimConnectData(simData, SimDataDefinitions.PropName.SimOnGround, 1.0f, out newValue)) + FlightSimData.DynamicLodSimData.PlaneOnGround = Convert.ToBoolean(newValue); + + var tlod = _dynamicLodOrchestrator.ReadTlod(isVr); + if (FlightSimData.DynamicLodSimData.Tlod != tlod) + FlightSimData.DynamicLodSimData.Tlod = tlod; + + var olod = _dynamicLodOrchestrator.ReadOlod(isVr); + if (FlightSimData.DynamicLodSimData.Olod != olod) + FlightSimData.DynamicLodSimData.Olod = olod; + + var cloudQuality = _dynamicLodOrchestrator.ReadCloudQuality(isVr); + if (FlightSimData.DynamicLodSimData.CloudQuality != cloudQuality) + FlightSimData.DynamicLodSimData.CloudQuality = cloudQuality; + + if (FlightSimData.IsFlightStarted && FlightSimData.IsInCockpit && + (!AppSettingData.ApplicationSetting.DynamicLodSetting.PauseOutsideCockpitView || (AppSettingData.ApplicationSetting.DynamicLodSetting.PauseOutsideCockpitView && FlightSimData.CameraState == CameraState.Cockpit))) + FlightSimData.DynamicLodSimData.Fps = FpsCalc.GetAverageFps(_dynamicLodOrchestrator.ReadIsFg(isVr) ? _currentFps * 2 : _currentFps); + } + + private int _currentFps; + private void MapEventFrameData(int fps) + { + if (!AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled) + return; + + _currentFps = fps; + } + private bool CompareSimConnectData(List simData, string propName, double source, out double newValue) { var propData = simData.Find(d => d.PropertyName == propName); @@ -486,5 +548,26 @@ namespace MSFSPopoutPanelManager.Orchestration newValue = 0; return false; } + + private void StartDynamicLod() + { + if (_simConnectProvider != null) + { + // Attach in memory override for Dynamic LOD + if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled) + _dynamicLodOrchestrator.Attach(); + + _simConnectProvider.StartDynamicLod(); + } + } + + private void StopDynamicLod() + { + // Detach in memory override for Dynamic LOD + if (AppSettingData != null && AppSettingData.ApplicationSetting.DynamicLodSetting.IsEnabled) + _dynamicLodOrchestrator.Detach(); + + _simConnectProvider.StopDynamicLod(); + } } } diff --git a/Orchestration/Orchestration.csproj b/Orchestration/Orchestration.csproj index ef54440..6cf921a 100644 --- a/Orchestration/Orchestration.csproj +++ b/Orchestration/Orchestration.csproj @@ -11,9 +11,9 @@ https://github.com/hawkeye-stan/msfs-popout-panel-manager MSFSPopoutPanelManager.Orchestration x64 - 4.1.0.4 - 4.1.0.4 - 4.1.0.4 + 4.1.1.0 + 4.1.1.0 + 4.1.1.0 win-x64 Embedded Debug;Release;Local diff --git a/Shared/Shared.csproj b/Shared/Shared.csproj index b8f063f..08c68ae 100644 --- a/Shared/Shared.csproj +++ b/Shared/Shared.csproj @@ -11,9 +11,9 @@ https://github.com/hawkeye-stan/msfs-popout-panel-manager MSFSPopoutPanelManager.Shared x64 - 4.1.0.4 - 4.1.0.4 - 4.1.0.4 + 4.1.1.0 + 4.1.1.0 + 4.1.1.0 win-x64 Embedded Debug;Release;Local diff --git a/SimconnectAgent/Enums.cs b/SimconnectAgent/Enums.cs index 73d2872..f8d228a 100644 --- a/SimconnectAgent/Enums.cs +++ b/SimconnectAgent/Enums.cs @@ -4,6 +4,7 @@ { REQUIRED_DEFINITION = 0, HUDBAR_DEFINITION, + DYNAMICLOD_DEFINITION, WRITABLE_TRACK_IR_DEFINITION, WRITABLE_COCKPIT_CAMERA_ZOOM_DEFINITION, WRITABLE_COCKPIT_CAMERA_STATE_DEFINITION, @@ -17,6 +18,7 @@ { REQUIRED_REQUEST = 0, HUDBAR_REQUEST, + DYNAMICLOD_REQUEST, NA } diff --git a/SimconnectAgent/FpsCalc.cs b/SimconnectAgent/FpsCalc.cs new file mode 100644 index 0000000..903616f --- /dev/null +++ b/SimconnectAgent/FpsCalc.cs @@ -0,0 +1,36 @@ +using System; +using System.Linq; + +namespace MSFSPopoutPanelManager.SimConnectAgent +{ + public class FpsCalc + { + private const int FpsLen = 50; + private static readonly float[] FpsStatistic = new float[FpsLen]; + private static int _fpsIndex = -1; + + public static int GetAverageFps(int newValue) + { + if (_fpsIndex == -1) + { + for (var i = 0; i < FpsLen; i++) + FpsStatistic[i] = newValue; + + _fpsIndex = 1; + } + else + { + FpsStatistic[_fpsIndex] = newValue; + _fpsIndex++; + if (_fpsIndex >= FpsLen) + _fpsIndex = 0; + } + + var fps = 0; + if (_fpsIndex != -1) + fps = Convert.ToInt32(FpsStatistic.Sum() / FpsLen); + + return fps; + } + } +} diff --git a/SimconnectAgent/SimConnectEvent.cs b/SimconnectAgent/SimConnectEvent.cs index f926128..1142d80 100644 --- a/SimconnectAgent/SimConnectEvent.cs +++ b/SimconnectAgent/SimConnectEvent.cs @@ -5,15 +5,17 @@ SIM_START, SIM_STOP, AIRCRAFT_LOADED, - VIEW + VIEW, + FRAME } public enum ActionEvent { - DUMMY1, // must register 4 dummy events to reserve space for SimConnectEvent above + DUMMY1, // must register 5 dummy events to reserve space for SimConnectEvent above DUMMY2, DUMMY3, DUMMY4, + DUMMY5, MASTER_BATTERY_SET, AVIONICS_MASTER_SET, PAUSE_SET, diff --git a/SimconnectAgent/SimConnectProvider.cs b/SimconnectAgent/SimConnectProvider.cs index 942e649..c0f350a 100644 --- a/SimconnectAgent/SimConnectProvider.cs +++ b/SimconnectAgent/SimConnectProvider.cs @@ -11,6 +11,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent { private const int MSFS_DATA_REFRESH_TIMEOUT = 500; private const int MSFS_HUDBAR_DATA_REFRESH_TIMEOUT = 200; + private const int MSFS_DYNAMICLOD_DATA_REFRESH_TIMEOUT = 300; private readonly SimConnector _simConnector; @@ -19,6 +20,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent private System.Timers.Timer _requiredRequestDataTimer; private System.Timers.Timer _hudBarRequestDataTimer; + private System.Timers.Timer _dynamicLodRequestDataTimer; private bool _isPowerOnForPopOut; private bool _isAvionicsOnForPopOut; private bool _isTrackIRManaged; @@ -33,6 +35,8 @@ namespace MSFSPopoutPanelManager.SimConnectAgent public event EventHandler OnException; public event EventHandler> OnSimConnectDataRequiredRefreshed; public event EventHandler> OnSimConnectDataHudBarRefreshed; + public event EventHandler> OnSimConnectDataDynamicLodRefreshed; + public event EventHandler OnSimConnectDataEventFrameRefreshed; public event EventHandler OnActiveAircraftChanged; public SimConnectProvider() @@ -43,7 +47,9 @@ namespace MSFSPopoutPanelManager.SimConnectAgent _simConnector.OnException += HandleSimException; _simConnector.OnReceiveSystemEvent += HandleReceiveSystemEvent; _simConnector.OnReceivedRequiredData += HandleRequiredDataReceived; - _simConnector.OnReceivedHudBarData += HandleHudBarDataReceived; + _simConnector.OnReceivedHudBarData += (_, e) => OnSimConnectDataHudBarRefreshed?.Invoke(this, e); + _simConnector.OnReceivedDynamicLodData += (_, e) => OnSimConnectDataDynamicLodRefreshed?.Invoke(this, e); + _simConnector.OnReceivedEventFrameData += (_, e) => OnSimConnectDataEventFrameRefreshed?.Invoke(this, e); _simConnector.OnActiveAircraftChanged += (_, e) => OnActiveAircraftChanged?.Invoke(this, e); _isHandlingCriticalError = false; @@ -108,6 +114,29 @@ namespace MSFSPopoutPanelManager.SimConnectAgent _hudBarRequestDataTimer.Stop(); } + public void StartDynamicLod() + { + if (_dynamicLodRequestDataTimer.Enabled) + return; + + // shut down data request and wait for the last request to be completed + _dynamicLodRequestDataTimer.Stop(); + Thread.Sleep(MSFS_DYNAMICLOD_DATA_REFRESH_TIMEOUT); + + _simConnector.SetSimConnectDynamicLodDataDefinition(); + + _dynamicLodRequestDataTimer.Start(); + + _simConnector.StartReceiveFrameData(); + } + + public void StopDynamicLod() + { + _dynamicLodRequestDataTimer.Stop(); + _simConnector.StopReceiveFrameData(); + } + + public void TurnOnPower(bool isRequiredForColdStart) { if (!isRequiredForColdStart || _requiredSimData == null) @@ -295,10 +324,29 @@ namespace MSFSPopoutPanelManager.SimConnectAgent // ignored } }; - + if (_isHudBarDataActive) SetHudBarConfig(_activeHudBarType); + // Setup dynamic data request timer + _dynamicLodRequestDataTimer = new() + { + Interval = MSFS_DYNAMICLOD_DATA_REFRESH_TIMEOUT, + }; + _dynamicLodRequestDataTimer.Stop(); + _dynamicLodRequestDataTimer.Elapsed += (_, _) => + { + try + { + _simConnector.RequestDynamicLodData(); + } + catch + { + // ignored + } + }; + + OnConnected?.Invoke(this, EventArgs.Empty); } @@ -306,6 +354,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent { _requiredRequestDataTimer.Stop(); _hudBarRequestDataTimer.Stop(); + _dynamicLodRequestDataTimer.Stop(); OnDisconnected?.Invoke(this, EventArgs.Empty); StopAndReconnect(); } @@ -316,6 +365,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent _requiredRequestDataTimer.Stop(); _hudBarRequestDataTimer.Stop(); + _dynamicLodRequestDataTimer.Stop(); if (!_isHandlingCriticalError) { @@ -332,11 +382,6 @@ namespace MSFSPopoutPanelManager.SimConnectAgent OnSimConnectDataRequiredRefreshed?.Invoke(this, e); } - private void HandleHudBarDataReceived(object sender, List e) - { - OnSimConnectDataHudBarRefreshed?.Invoke(this, e); - } - private CameraState _currentCameraState = CameraState.Unknown; private void DetectFlightStartedOrStopped(List simData) @@ -375,6 +420,8 @@ namespace MSFSPopoutPanelManager.SimConnectAgent _isHudBarDataActive = false; _hudBarRequestDataTimer.Stop(); + + _dynamicLodRequestDataTimer.Stop(); } break; } diff --git a/SimconnectAgent/SimConnector.cs b/SimconnectAgent/SimConnector.cs index 094f1d8..3212624 100644 --- a/SimconnectAgent/SimConnector.cs +++ b/SimconnectAgent/SimConnector.cs @@ -20,15 +20,18 @@ namespace MSFSPopoutPanelManager.SimConnectAgent private bool _isDisabledReconnect; private readonly List _simConnectRequiredDataDefinitions = SimDataDefinitions.GetRequiredDefinitions(); + private readonly List _simConnectDynamicLodDataDefinitions = SimDataDefinitions.GetDynamicLodDefinitions(); private List _simConnectHudBarDataDefinitions; private readonly FieldInfo[] _simConnectStructFields = typeof(SimConnectStruct).GetFields(BindingFlags.Public | BindingFlags.Instance); public event EventHandler OnException; public event EventHandler> OnReceivedRequiredData; public event EventHandler> OnReceivedHudBarData; + public event EventHandler> OnReceivedDynamicLodData; public event EventHandler OnConnected; public event EventHandler OnDisconnected; public event EventHandler OnReceiveSystemEvent; + public event EventHandler OnReceivedEventFrameData; public event EventHandler OnActiveAircraftChanged; public bool Connected { get; set; } @@ -66,6 +69,11 @@ namespace MSFSPopoutPanelManager.SimConnectAgent AddHudBarDataDefinitions(); } + public void SetSimConnectDynamicLodDataDefinition() + { + AddDynamicLodDataDefinitions(); + } + private void HandleOnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data) { ReceiveMessage(); @@ -124,6 +132,26 @@ namespace MSFSPopoutPanelManager.SimConnectAgent } } + public void RequestDynamicLodData() + { + if (_simConnect == null || !Connected) + return; + + try + { + + if (_simConnectDynamicLodDataDefinitions != null) + _simConnect.RequestDataOnSimObjectType(DataRequest.DYNAMICLOD_REQUEST, DataDefinition.DYNAMICLOD_DEFINITION, 0, SIMCONNECT_SIMOBJECT_TYPE.USER); + } + catch + { + if (!_isDisabledReconnect) + _isDisabledReconnect = true; + + OnException?.Invoke(this, null); + } + } + public void ReceiveMessage() { if (_simConnect == null) @@ -208,6 +236,25 @@ namespace MSFSPopoutPanelManager.SimConnectAgent } } + public void StartReceiveFrameData() + { + if (_simConnect == null) + return; + + _simConnect.OnRecvEventFrame -= HandleOnRecvEventFrame; + _simConnect.OnRecvEventFrame += HandleOnRecvEventFrame; + + _simConnect.UnsubscribeFromSystemEvent(SimConnectEvent.FRAME); + _simConnect.SubscribeToSystemEvent(SimConnectEvent.FRAME, "Frame"); + } + + public void StopReceiveFrameData() + { + _simConnect.OnRecvEventFrame -= HandleOnRecvEventFrame; + + _simConnect.UnsubscribeFromSystemEvent(SimConnectEvent.FRAME); + } + private void InitializeSimConnect() { Debug.WriteLine("Trying to start simConnect"); @@ -234,8 +281,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent _simConnect.SubscribeToSystemEvent(SimConnectEvent.VIEW, "View"); _simConnect.UnsubscribeFromSystemEvent(SimConnectEvent.AIRCRAFT_LOADED); _simConnect.SubscribeToSystemEvent(SimConnectEvent.AIRCRAFT_LOADED, "AircraftLoaded"); - - + AddRequiredDataDefinitions(); SetupActionEvents(); @@ -256,6 +302,14 @@ namespace MSFSPopoutPanelManager.SimConnectAgent Connected = true; } + private void HandleOnRecvEventFrame(SimConnect sender, SIMCONNECT_RECV_EVENT_FRAME data) + { + if (data == null) + return; + + OnReceivedEventFrameData?.Invoke(this, Convert.ToInt32(data.fFrameRate)); + } + private void HandleOnRecvSystemState(SimConnect sender, SIMCONNECT_RECV_SYSTEM_STATE data) { switch ((SystemStateRequestId)Enum.Parse(typeof(SystemStateRequestId), data.dwRequestID.ToString())) @@ -361,6 +415,41 @@ namespace MSFSPopoutPanelManager.SimConnectAgent _simConnect.RegisterDataDefineStruct(DataDefinition.HUDBAR_DEFINITION); } + private void AddDynamicLodDataDefinitions() + { + if (_simConnect == null) + return; + + _simConnect.ClearDataDefinition(DataDefinition.DYNAMICLOD_DEFINITION); + + if (_simConnectDynamicLodDataDefinitions == null) + return; + + foreach (var definition in _simConnectDynamicLodDataDefinitions) + { + if (definition.DefinitionId != DataDefinition.DYNAMICLOD_DEFINITION || + definition.DataDefinitionType != DataDefinitionType.SimConnect) continue; + + SIMCONNECT_DATATYPE simConnectDataType; + switch (definition.DataType) + { + case DataType.String: + simConnectDataType = SIMCONNECT_DATATYPE.STRING256; + break; + case DataType.Float64: + simConnectDataType = SIMCONNECT_DATATYPE.FLOAT64; + break; + default: + simConnectDataType = SIMCONNECT_DATATYPE.FLOAT64; + break; + } + + _simConnect.AddToDataDefinition(definition.DefinitionId, definition.VariableName, definition.SimConnectUnit, simConnectDataType, 0.0f, SimConnect.SIMCONNECT_UNUSED); + } + + _simConnect.RegisterDataDefineStruct(DataDefinition.DYNAMICLOD_DEFINITION); + } + private void HandleOnRecvQuit(SimConnect sender, SIMCONNECT_RECV data) { Stop(); @@ -404,6 +493,9 @@ namespace MSFSPopoutPanelManager.SimConnectAgent case (int)DataRequest.HUDBAR_REQUEST: ParseHudBarReceivedSimData(data); break; + case (int)DataRequest.DYNAMICLOD_REQUEST: + ParseDynamicLodReceivedSimData(data); + break; } } @@ -483,6 +575,46 @@ namespace MSFSPopoutPanelManager.SimConnectAgent } } + + private void ParseDynamicLodReceivedSimData(SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE data) + { + try + { + if (_simConnectDynamicLodDataDefinitions == null) + return; + + var simData = new List(); + var simDataStruct = (SimConnectStruct)data.dwData[0]; + + var i = 0; + lock (_simConnectDynamicLodDataDefinitions) + { + foreach (var definition in _simConnectDynamicLodDataDefinitions) + { + if (definition.DataDefinitionType != DataDefinitionType.SimConnect) + continue; + + var dataValue = _simConnectStructFields[i].GetValue(simDataStruct); + var simDataItem = new SimDataItem + { + PropertyName = definition.PropName, + Value = dataValue == null ? 0 : (double)dataValue + }; + + simData.Add(simDataItem); + i++; + } + } + + OnReceivedDynamicLodData?.Invoke(this, simData); + } + catch (Exception ex) + { + FileLogger.WriteException($"SimConnector: SimConnect received dynamic lod data exception - {ex.Message}", ex); + StopAndReconnect(); + } + } + private void SetActiveAircraftTitle(string aircraftFilePath) { var filePathToken = aircraftFilePath.Split(@"\"); diff --git a/SimconnectAgent/SimDataDefinitions.cs b/SimconnectAgent/SimDataDefinitions.cs index d710bab..8ca9da2 100644 --- a/SimconnectAgent/SimDataDefinitions.cs +++ b/SimconnectAgent/SimDataDefinitions.cs @@ -14,7 +14,6 @@ namespace MSFSPopoutPanelManager.SimConnectAgent new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.PlaneInParkingSpot, VariableName = "ATC ON PARKING SPOT", SimConnectUnit = "Bool", DataType = DataType.Float64 }, new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraState, VariableName = "CAMERA STATE", SimConnectUnit = "Number", DataType = DataType.Float64 }, new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CockpitCameraZoom, VariableName = "COCKPIT CAMERA ZOOM", SimConnectUnit = "Percentage", DataType = DataType.Float64 }, - new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.PlaneAltAboveGround, VariableName = "PLANE ALT ABOVE GROUND", SimConnectUnit = "Feet", DataType = DataType.Float64 }, new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraViewTypeAndIndex0, VariableName = "CAMERA VIEW TYPE AND INDEX:0", SimConnectUnit = "Enum", DataType = DataType.Float64 }, new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraViewTypeAndIndex1, VariableName = "CAMERA VIEW TYPE AND INDEX:1", SimConnectUnit = "Enum", DataType = DataType.Float64 }, new() { DefinitionId = DataDefinition.REQUIRED_DEFINITION, RequestId = DataRequest.REQUIRED_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.CameraViewTypeAndIndex1Max, VariableName = "CAMERA VIEW TYPE AND INDEX MAX:1", SimConnectUnit = "Number", DataType = DataType.Float64 }, @@ -44,6 +43,18 @@ namespace MSFSPopoutPanelManager.SimConnectAgent } } + public static List GetDynamicLodDefinitions() + { + var definitions = new List + { + new() { DefinitionId = DataDefinition.DYNAMICLOD_DEFINITION, RequestId = DataRequest.DYNAMICLOD_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.PlaneAltAboveGround, VariableName = "PLANE ALT ABOVE GROUND", SimConnectUnit = "feet", DataType = DataType.Float64 }, + new() { DefinitionId = DataDefinition.DYNAMICLOD_DEFINITION, RequestId = DataRequest.DYNAMICLOD_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.PlaneAltAboveGroundMinusCg, VariableName = "PLANE ALT ABOVE GROUND MINUS CG", SimConnectUnit = "feet", DataType = DataType.Float64 }, + new() { DefinitionId = DataDefinition.DYNAMICLOD_DEFINITION, RequestId = DataRequest.DYNAMICLOD_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.SimOnGround, VariableName = "SIM ON GROUND", SimConnectUnit = "Bool", DataType = DataType.Float64 }, + new() { DefinitionId = DataDefinition.DYNAMICLOD_DEFINITION, RequestId = DataRequest.DYNAMICLOD_REQUEST, DataDefinitionType = DataDefinitionType.SimConnect, PropName = PropName.GroundVelocity, VariableName = "GROUND VELOCITY", SimConnectUnit = "knots", DataType = DataType.Float64 } + }; + return definitions; + } + private static List GetSharedHudBarDefinitions() { var definitions = new List @@ -80,7 +91,7 @@ namespace MSFSPopoutPanelManager.SimConnectAgent }; return definitions; } - + public static class PropName { public static string ElectricalMasterBattery = "ElectricalMasterBattery"; @@ -96,6 +107,9 @@ namespace MSFSPopoutPanelManager.SimConnectAgent // Dynamic LOD public static string PlaneAltAboveGround = "PlaneAltAboveGround"; + public static string PlaneAltAboveGroundMinusCg = "PlaneAltAboveGroundMinusCg"; + public static string SimOnGround = "SimOnGround"; + public static string GroundVelocity = "GroundVelocity"; // Hud Bar data public static string ElevatorTrim = "ElevatorTrim"; diff --git a/SimconnectAgent/SimconnectAgent.csproj b/SimconnectAgent/SimconnectAgent.csproj index 77cc1be..818840d 100644 --- a/SimconnectAgent/SimconnectAgent.csproj +++ b/SimconnectAgent/SimconnectAgent.csproj @@ -11,9 +11,9 @@ https://github.com/hawkeye-stan/msfs-popout-panel-manager MSFSPopoutPanelManager.SimConnectAgent x64 - 4.1.0.4 - 4.1.0.4 - 4.1.0.4 + 4.1.1.0 + 4.1.1.0 + 4.1.1.0 win-x64 Embedded Debug;Release;Local diff --git a/WindowsAgent/WindowActionManager.cs b/WindowsAgent/WindowActionManager.cs index 2656e06..337b1da 100644 --- a/WindowsAgent/WindowActionManager.cs +++ b/WindowsAgent/WindowActionManager.cs @@ -293,7 +293,12 @@ namespace MSFSPopoutPanelManager.WindowsAgent var bottomEdge = rect.Y + rect.Height; return point.X >= rect.X && point.X <= rightEdge && point.Y >= rect.Y && point.Y <= bottomEdge; + } + public static bool IsMsfsInFocus() + { + var handle = PInvoke.GetForegroundWindow(); + return PInvoke.GetWindowText(handle).Substring(0, 26).Equals("Microsoft Flight Simulator", StringComparison.InvariantCultureIgnoreCase); } } } diff --git a/WindowsAgent/WindowsAgent.csproj b/WindowsAgent/WindowsAgent.csproj index a7c06e0..337f6b8 100644 --- a/WindowsAgent/WindowsAgent.csproj +++ b/WindowsAgent/WindowsAgent.csproj @@ -11,9 +11,9 @@ https://github.com/hawkeye-stan/msfs-popout-panel-manager MSFSPopoutPanelManager.WindowsAgent x64 - 4.1.0.4 - 4.1.0.4 - 4.1.0.4 + 4.1.1.0 + 4.1.1.0 + 4.1.1.0 win-x64 Embedded Debug;Release;Local