From a3cb0eb7cacb02dcdc93d7f7f5a54d14be64f91e Mon Sep 17 00:00:00 2001 From: Mark van Renswoude Date: Wed, 2 Sep 2020 12:49:09 +0200 Subject: [PATCH] Proof of concept command-line version --- .gitignore | 7 + FSFlightLogger.sln | 71 +++ FSFlightLogger.sln.DotSettings | 9 + FSFlightLogger/App.config | 6 + FSFlightLogger/FSFlightLogger.csproj | 124 +++++ FSFlightLogger/MainForm.Designer.cs | 313 ++++++++++++ FSFlightLogger/MainForm.cs | 49 ++ FSFlightLogger/MainForm.resx | 474 ++++++++++++++++++ FSFlightLogger/Program.cs | 39 ++ FSFlightLogger/Properties/AssemblyInfo.cs | 36 ++ .../Properties/Resources.Designer.cs | 71 +++ FSFlightLogger/Properties/Resources.resx | 117 +++++ .../Properties/Settings.Designer.cs | 30 ++ FSFlightLogger/Properties/Settings.settings | 7 + .../Resources/Images/FSXConnected.png | Bin 0 -> 1852 bytes .../Resources/Images/FSXDisconnected.png | Bin 0 -> 1296 bytes FSFlightLogger/Resources/Images/Idle.png | Bin 0 -> 2539 bytes .../Resources/Images/P3DConnected.png | Bin 0 -> 1463 bytes .../Resources/Images/P3DDisconnected.png | Bin 0 -> 2237 bytes FSFlightLogger/Resources/Images/Recording.png | Bin 0 -> 3425 bytes FSFlightLogger/packages.config | 7 + FSFlightLoggerCmd/App.config | 6 + FSFlightLoggerCmd/FSFlightLoggerCmd.csproj | 86 ++++ FSFlightLoggerCmd/Program.cs | 216 ++++++++ FSFlightLoggerCmd/Properties/AssemblyInfo.cs | 36 ++ FSFlightLoggerCmd/packages.config | 4 + FlightLoggerLib/Concrete/CSVFlightLogger.cs | 71 +++ FlightLoggerLib/Concrete/KMLFlightLogger.cs | 208 ++++++++ FlightLoggerLib/FlightLoggerLib.csproj | 95 ++++ FlightLoggerLib/IFlightLogger.cs | 21 + FlightLoggerLib/Properties/AssemblyInfo.cs | 36 ++ FlightLoggerLib/app.config | 15 + FlightLoggerLib/packages.config | 11 + README.md | 11 + .../Attribute/SimConnectVariableAttribute.cs | 60 +++ .../Concrete/DefaultSimConnectClient.cs | 272 ++++++++++ .../Concrete/SimConnectClientFactory.cs | 34 ++ SimConnect/Concrete/SimConnectDefinition.cs | 197 ++++++++ SimConnect/FS2020-SimConnect.dll | Bin 0 -> 58368 bytes SimConnect/FSX-SE-SimConnect.dll | Bin 0 -> 45056 bytes SimConnect/FSX-SimConnect.dll | Bin 0 -> 37888 bytes SimConnect/FSXSP2-SimConnect.dll | Bin 0 -> 45568 bytes SimConnect/ISimConnectClient.cs | 46 ++ SimConnect/ISimConnectClientFactory.cs | 17 + SimConnect/ISimConnectLibrary.cs | 186 +++++++ SimConnect/Lib/LatLon.cs | 40 ++ SimConnect/Lib/UnmanagedLibrary.cs | 117 +++++ SimConnect/Properties/AssemblyInfo.cs | 36 ++ SimConnect/SimConnect.csproj | 98 ++++ SimConnect/Unmanaged/SimConnectDLLLibrary.cs | 77 +++ .../Properties/AssemblyInfo.cs | 36 ++ .../SimConnectDefinitionTest.cs | 58 +++ .../SimConnectUnitTests.csproj | 112 +++++ SimConnectUnitTests/packages.config | 11 + 54 files changed, 3573 insertions(+) create mode 100644 .gitignore create mode 100644 FSFlightLogger.sln create mode 100644 FSFlightLogger.sln.DotSettings create mode 100644 FSFlightLogger/App.config create mode 100644 FSFlightLogger/FSFlightLogger.csproj create mode 100644 FSFlightLogger/MainForm.Designer.cs create mode 100644 FSFlightLogger/MainForm.cs create mode 100644 FSFlightLogger/MainForm.resx create mode 100644 FSFlightLogger/Program.cs create mode 100644 FSFlightLogger/Properties/AssemblyInfo.cs create mode 100644 FSFlightLogger/Properties/Resources.Designer.cs create mode 100644 FSFlightLogger/Properties/Resources.resx create mode 100644 FSFlightLogger/Properties/Settings.Designer.cs create mode 100644 FSFlightLogger/Properties/Settings.settings create mode 100644 FSFlightLogger/Resources/Images/FSXConnected.png create mode 100644 FSFlightLogger/Resources/Images/FSXDisconnected.png create mode 100644 FSFlightLogger/Resources/Images/Idle.png create mode 100644 FSFlightLogger/Resources/Images/P3DConnected.png create mode 100644 FSFlightLogger/Resources/Images/P3DDisconnected.png create mode 100644 FSFlightLogger/Resources/Images/Recording.png create mode 100644 FSFlightLogger/packages.config create mode 100644 FSFlightLoggerCmd/App.config create mode 100644 FSFlightLoggerCmd/FSFlightLoggerCmd.csproj create mode 100644 FSFlightLoggerCmd/Program.cs create mode 100644 FSFlightLoggerCmd/Properties/AssemblyInfo.cs create mode 100644 FSFlightLoggerCmd/packages.config create mode 100644 FlightLoggerLib/Concrete/CSVFlightLogger.cs create mode 100644 FlightLoggerLib/Concrete/KMLFlightLogger.cs create mode 100644 FlightLoggerLib/FlightLoggerLib.csproj create mode 100644 FlightLoggerLib/IFlightLogger.cs create mode 100644 FlightLoggerLib/Properties/AssemblyInfo.cs create mode 100644 FlightLoggerLib/app.config create mode 100644 FlightLoggerLib/packages.config create mode 100644 README.md create mode 100644 SimConnect/Attribute/SimConnectVariableAttribute.cs create mode 100644 SimConnect/Concrete/DefaultSimConnectClient.cs create mode 100644 SimConnect/Concrete/SimConnectClientFactory.cs create mode 100644 SimConnect/Concrete/SimConnectDefinition.cs create mode 100644 SimConnect/FS2020-SimConnect.dll create mode 100644 SimConnect/FSX-SE-SimConnect.dll create mode 100644 SimConnect/FSX-SimConnect.dll create mode 100644 SimConnect/FSXSP2-SimConnect.dll create mode 100644 SimConnect/ISimConnectClient.cs create mode 100644 SimConnect/ISimConnectClientFactory.cs create mode 100644 SimConnect/ISimConnectLibrary.cs create mode 100644 SimConnect/Lib/LatLon.cs create mode 100644 SimConnect/Lib/UnmanagedLibrary.cs create mode 100644 SimConnect/Properties/AssemblyInfo.cs create mode 100644 SimConnect/SimConnect.csproj create mode 100644 SimConnect/Unmanaged/SimConnectDLLLibrary.cs create mode 100644 SimConnectUnitTests/Properties/AssemblyInfo.cs create mode 100644 SimConnectUnitTests/SimConnectDefinitionTest.cs create mode 100644 SimConnectUnitTests/SimConnectUnitTests.csproj create mode 100644 SimConnectUnitTests/packages.config diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6a1883 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +DesignTimeBuild +bin +obj +packages + +*.suo +*.user diff --git a/FSFlightLogger.sln b/FSFlightLogger.sln new file mode 100644 index 0000000..7d295fc --- /dev/null +++ b/FSFlightLogger.sln @@ -0,0 +1,71 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30413.136 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSFlightLogger", "FSFlightLogger\FSFlightLogger.csproj", "{5871FA9B-88A7-4F98-8755-2FC48D4DBF44}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimConnectUnitTests", "SimConnectUnitTests\SimConnectUnitTests.csproj", "{2CD97D07-EF26-4CDA-A090-DF3D698E7F60}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSFlightLoggerCmd", "FSFlightLoggerCmd\FSFlightLoggerCmd.csproj", "{767C7EAA-9230-4DEE-89FA-9699288C831B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlightLoggerLib", "FlightLoggerLib\FlightLoggerLib.csproj", "{D85BCC97-F653-4286-98D9-073A33A55857}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimConnect", "SimConnect\SimConnect.csproj", "{F160BB6A-7620-41E5-A99C-948C208875E4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5871FA9B-88A7-4F98-8755-2FC48D4DBF44}.Debug|x64.ActiveCfg = Debug|x64 + {5871FA9B-88A7-4F98-8755-2FC48D4DBF44}.Debug|x64.Build.0 = Debug|x64 + {5871FA9B-88A7-4F98-8755-2FC48D4DBF44}.Debug|x86.ActiveCfg = Debug|x86 + {5871FA9B-88A7-4F98-8755-2FC48D4DBF44}.Debug|x86.Build.0 = Debug|x86 + {5871FA9B-88A7-4F98-8755-2FC48D4DBF44}.Release|x64.ActiveCfg = Release|x64 + {5871FA9B-88A7-4F98-8755-2FC48D4DBF44}.Release|x64.Build.0 = Release|x64 + {5871FA9B-88A7-4F98-8755-2FC48D4DBF44}.Release|x86.ActiveCfg = Release|x86 + {5871FA9B-88A7-4F98-8755-2FC48D4DBF44}.Release|x86.Build.0 = Release|x86 + {2CD97D07-EF26-4CDA-A090-DF3D698E7F60}.Debug|x64.ActiveCfg = Debug|x64 + {2CD97D07-EF26-4CDA-A090-DF3D698E7F60}.Debug|x64.Build.0 = Debug|x64 + {2CD97D07-EF26-4CDA-A090-DF3D698E7F60}.Debug|x86.ActiveCfg = Debug|x86 + {2CD97D07-EF26-4CDA-A090-DF3D698E7F60}.Debug|x86.Build.0 = Debug|x86 + {2CD97D07-EF26-4CDA-A090-DF3D698E7F60}.Release|x64.ActiveCfg = Release|x64 + {2CD97D07-EF26-4CDA-A090-DF3D698E7F60}.Release|x64.Build.0 = Release|x64 + {2CD97D07-EF26-4CDA-A090-DF3D698E7F60}.Release|x86.ActiveCfg = Release|x86 + {2CD97D07-EF26-4CDA-A090-DF3D698E7F60}.Release|x86.Build.0 = Release|x86 + {767C7EAA-9230-4DEE-89FA-9699288C831B}.Debug|x64.ActiveCfg = Debug|x64 + {767C7EAA-9230-4DEE-89FA-9699288C831B}.Debug|x64.Build.0 = Debug|x64 + {767C7EAA-9230-4DEE-89FA-9699288C831B}.Debug|x86.ActiveCfg = Debug|x86 + {767C7EAA-9230-4DEE-89FA-9699288C831B}.Debug|x86.Build.0 = Debug|x86 + {767C7EAA-9230-4DEE-89FA-9699288C831B}.Release|x64.ActiveCfg = Release|x64 + {767C7EAA-9230-4DEE-89FA-9699288C831B}.Release|x64.Build.0 = Release|x64 + {767C7EAA-9230-4DEE-89FA-9699288C831B}.Release|x86.ActiveCfg = Release|x86 + {767C7EAA-9230-4DEE-89FA-9699288C831B}.Release|x86.Build.0 = Release|x86 + {D85BCC97-F653-4286-98D9-073A33A55857}.Debug|x64.ActiveCfg = Debug|x64 + {D85BCC97-F653-4286-98D9-073A33A55857}.Debug|x64.Build.0 = Debug|x64 + {D85BCC97-F653-4286-98D9-073A33A55857}.Debug|x86.ActiveCfg = Debug|x86 + {D85BCC97-F653-4286-98D9-073A33A55857}.Debug|x86.Build.0 = Debug|x86 + {D85BCC97-F653-4286-98D9-073A33A55857}.Release|x64.ActiveCfg = Release|x64 + {D85BCC97-F653-4286-98D9-073A33A55857}.Release|x64.Build.0 = Release|x64 + {D85BCC97-F653-4286-98D9-073A33A55857}.Release|x86.ActiveCfg = Release|x86 + {D85BCC97-F653-4286-98D9-073A33A55857}.Release|x86.Build.0 = Release|x86 + {F160BB6A-7620-41E5-A99C-948C208875E4}.Debug|x64.ActiveCfg = Debug|x64 + {F160BB6A-7620-41E5-A99C-948C208875E4}.Debug|x64.Build.0 = Debug|x64 + {F160BB6A-7620-41E5-A99C-948C208875E4}.Debug|x86.ActiveCfg = Debug|x86 + {F160BB6A-7620-41E5-A99C-948C208875E4}.Debug|x86.Build.0 = Debug|x86 + {F160BB6A-7620-41E5-A99C-948C208875E4}.Release|x64.ActiveCfg = Release|x64 + {F160BB6A-7620-41E5-A99C-948C208875E4}.Release|x64.Build.0 = Release|x64 + {F160BB6A-7620-41E5-A99C-948C208875E4}.Release|x86.ActiveCfg = Release|x86 + {F160BB6A-7620-41E5-A99C-948C208875E4}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {427083A8-FC16-46A5-9DD7-FB805B89A26A} + EndGlobalSection +EndGlobal diff --git a/FSFlightLogger.sln.DotSettings b/FSFlightLogger.sln.DotSettings new file mode 100644 index 0000000..f548907 --- /dev/null +++ b/FSFlightLogger.sln.DotSettings @@ -0,0 +1,9 @@ + + False + DLL + KML + XYZ + True + True + True + True \ No newline at end of file diff --git a/FSFlightLogger/App.config b/FSFlightLogger/App.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/FSFlightLogger/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/FSFlightLogger/FSFlightLogger.csproj b/FSFlightLogger/FSFlightLogger.csproj new file mode 100644 index 0000000..3d74b4a --- /dev/null +++ b/FSFlightLogger/FSFlightLogger.csproj @@ -0,0 +1,124 @@ + + + + + Debug + AnyCPU + {5871FA9B-88A7-4F98-8755-2FC48D4DBF44} + WinExe + FSFlightLogger + FSFlightLogger + v4.7.2 + 512 + true + true + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + true + bin\x64\Debug\ + x64 + 7.3 + MinimumRecommendedRules.ruleset + true + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + true + bin\x86\Debug\ + x86 + 7.3 + MinimumRecommendedRules.ruleset + true + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.1.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\packages\SimpleInjector.5.0.3\lib\net461\SimpleInjector.dll + + + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + + + + + + + + + + + + + + Form + + + MainForm.cs + + + + + MainForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + + + + {f160bb6a-7620-41e5-a99c-948c208875e4} + SimConnect + + + + \ No newline at end of file diff --git a/FSFlightLogger/MainForm.Designer.cs b/FSFlightLogger/MainForm.Designer.cs new file mode 100644 index 0000000..683db9f --- /dev/null +++ b/FSFlightLogger/MainForm.Designer.cs @@ -0,0 +1,313 @@ +namespace FSFlightLogger +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm)); + this.OutputGroupbox = new System.Windows.Forms.GroupBox(); + this.StatusImageList = new System.Windows.Forms.ImageList(this.components); + this.FlightSimulatorStatusIcon = new System.Windows.Forms.PictureBox(); + this.FlightSimulatorLabel = new System.Windows.Forms.Label(); + this.FlightSimulatorStatusLabel = new System.Windows.Forms.Label(); + this.RecordButton = new System.Windows.Forms.Button(); + this.RecordingStatusIcon = new System.Windows.Forms.PictureBox(); + this.OutputCSVCheckbox = new System.Windows.Forms.CheckBox(); + this.OutputKMLCheckbox = new System.Windows.Forms.CheckBox(); + this.OutputCSVPathTextbox = new System.Windows.Forms.TextBox(); + this.OutputCSVPathLabel = new System.Windows.Forms.Label(); + this.OutputCSVPathBrowseButton = new System.Windows.Forms.Button(); + this.OutputKMLPathBrowseButton = new System.Windows.Forms.Button(); + this.OutputKMLPathLabel = new System.Windows.Forms.Label(); + this.OutputKMLPathTextbox = new System.Windows.Forms.TextBox(); + this.TriggersGroupbox = new System.Windows.Forms.GroupBox(); + this.TriggerWaitForMovementCheckbox = new System.Windows.Forms.CheckBox(); + this.TriggerNewLogStationaryCheckbox = new System.Windows.Forms.CheckBox(); + this.TriggerNewLogStationaryTimeEdit = new System.Windows.Forms.NumericUpDown(); + this.TriggerNewLogStationaryUnitsLabel = new System.Windows.Forms.Label(); + this.OutputGroupbox.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.FlightSimulatorStatusIcon)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.RecordingStatusIcon)).BeginInit(); + this.TriggersGroupbox.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.TriggerNewLogStationaryTimeEdit)).BeginInit(); + this.SuspendLayout(); + // + // OutputGroupbox + // + this.OutputGroupbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.OutputGroupbox.Controls.Add(this.OutputKMLPathBrowseButton); + this.OutputGroupbox.Controls.Add(this.OutputKMLPathLabel); + this.OutputGroupbox.Controls.Add(this.OutputKMLPathTextbox); + this.OutputGroupbox.Controls.Add(this.OutputCSVPathBrowseButton); + this.OutputGroupbox.Controls.Add(this.OutputCSVPathLabel); + this.OutputGroupbox.Controls.Add(this.OutputCSVPathTextbox); + this.OutputGroupbox.Controls.Add(this.OutputKMLCheckbox); + this.OutputGroupbox.Controls.Add(this.OutputCSVCheckbox); + this.OutputGroupbox.Location = new System.Drawing.Point(12, 59); + this.OutputGroupbox.Name = "OutputGroupbox"; + this.OutputGroupbox.Size = new System.Drawing.Size(482, 136); + this.OutputGroupbox.TabIndex = 0; + this.OutputGroupbox.TabStop = false; + this.OutputGroupbox.Text = " Output "; + // + // StatusImageList + // + this.StatusImageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("StatusImageList.ImageStream"))); + this.StatusImageList.TransparentColor = System.Drawing.Color.Transparent; + this.StatusImageList.Images.SetKeyName(0, "Idle"); + this.StatusImageList.Images.SetKeyName(1, "Recording"); + this.StatusImageList.Images.SetKeyName(2, "FSDisconnected"); + this.StatusImageList.Images.SetKeyName(3, "FSConnected"); + // + // FlightSimulatorStatusIcon + // + this.FlightSimulatorStatusIcon.Location = new System.Drawing.Point(12, 12); + this.FlightSimulatorStatusIcon.Name = "FlightSimulatorStatusIcon"; + this.FlightSimulatorStatusIcon.Size = new System.Drawing.Size(32, 32); + this.FlightSimulatorStatusIcon.TabIndex = 1; + this.FlightSimulatorStatusIcon.TabStop = false; + // + // FlightSimulatorLabel + // + this.FlightSimulatorLabel.AutoSize = true; + this.FlightSimulatorLabel.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.FlightSimulatorLabel.Location = new System.Drawing.Point(50, 9); + this.FlightSimulatorLabel.Name = "FlightSimulatorLabel"; + this.FlightSimulatorLabel.Size = new System.Drawing.Size(133, 21); + this.FlightSimulatorLabel.TabIndex = 2; + this.FlightSimulatorLabel.Text = "Flight Simulator"; + // + // FlightSimulatorStatusLabel + // + this.FlightSimulatorStatusLabel.AutoSize = true; + this.FlightSimulatorStatusLabel.Location = new System.Drawing.Point(51, 31); + this.FlightSimulatorStatusLabel.Name = "FlightSimulatorStatusLabel"; + this.FlightSimulatorStatusLabel.Size = new System.Drawing.Size(70, 13); + this.FlightSimulatorStatusLabel.TabIndex = 3; + this.FlightSimulatorStatusLabel.Text = "Connecting..."; + // + // RecordButton + // + this.RecordButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.RecordButton.Enabled = false; + this.RecordButton.Location = new System.Drawing.Point(348, 17); + this.RecordButton.Name = "RecordButton"; + this.RecordButton.Size = new System.Drawing.Size(108, 23); + this.RecordButton.TabIndex = 4; + this.RecordButton.Text = "&Start recording"; + this.RecordButton.UseVisualStyleBackColor = true; + // + // RecordingStatusIcon + // + this.RecordingStatusIcon.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.RecordingStatusIcon.Location = new System.Drawing.Point(462, 12); + this.RecordingStatusIcon.Name = "RecordingStatusIcon"; + this.RecordingStatusIcon.Size = new System.Drawing.Size(32, 32); + this.RecordingStatusIcon.TabIndex = 5; + this.RecordingStatusIcon.TabStop = false; + // + // OutputCSVCheckbox + // + this.OutputCSVCheckbox.AutoSize = true; + this.OutputCSVCheckbox.Location = new System.Drawing.Point(10, 23); + this.OutputCSVCheckbox.Name = "OutputCSVCheckbox"; + this.OutputCSVCheckbox.Size = new System.Drawing.Size(141, 17); + this.OutputCSVCheckbox.TabIndex = 0; + this.OutputCSVCheckbox.Text = "Comma-separated (CSV)"; + this.OutputCSVCheckbox.UseVisualStyleBackColor = true; + // + // OutputKMLCheckbox + // + this.OutputKMLCheckbox.AutoSize = true; + this.OutputKMLCheckbox.Location = new System.Drawing.Point(10, 79); + this.OutputKMLCheckbox.Name = "OutputKMLCheckbox"; + this.OutputKMLCheckbox.Size = new System.Drawing.Size(119, 17); + this.OutputKMLCheckbox.TabIndex = 1; + this.OutputKMLCheckbox.Text = "Google Earth (KML)"; + this.OutputKMLCheckbox.UseVisualStyleBackColor = true; + // + // OutputCSVPathTextbox + // + this.OutputCSVPathTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.OutputCSVPathTextbox.Enabled = false; + this.OutputCSVPathTextbox.Location = new System.Drawing.Point(71, 46); + this.OutputCSVPathTextbox.Name = "OutputCSVPathTextbox"; + this.OutputCSVPathTextbox.Size = new System.Drawing.Size(373, 20); + this.OutputCSVPathTextbox.TabIndex = 2; + // + // OutputCSVPathLabel + // + this.OutputCSVPathLabel.AutoSize = true; + this.OutputCSVPathLabel.Location = new System.Drawing.Point(26, 49); + this.OutputCSVPathLabel.Name = "OutputCSVPathLabel"; + this.OutputCSVPathLabel.Size = new System.Drawing.Size(32, 13); + this.OutputCSVPathLabel.TabIndex = 3; + this.OutputCSVPathLabel.Text = "Path:"; + // + // OutputCSVPathBrowseButton + // + this.OutputCSVPathBrowseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.OutputCSVPathBrowseButton.Enabled = false; + this.OutputCSVPathBrowseButton.Location = new System.Drawing.Point(448, 46); + this.OutputCSVPathBrowseButton.Name = "OutputCSVPathBrowseButton"; + this.OutputCSVPathBrowseButton.Size = new System.Drawing.Size(28, 20); + this.OutputCSVPathBrowseButton.TabIndex = 4; + this.OutputCSVPathBrowseButton.Text = "..."; + this.OutputCSVPathBrowseButton.UseVisualStyleBackColor = true; + // + // OutputKMLPathBrowseButton + // + this.OutputKMLPathBrowseButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.OutputKMLPathBrowseButton.Enabled = false; + this.OutputKMLPathBrowseButton.Location = new System.Drawing.Point(448, 102); + this.OutputKMLPathBrowseButton.Name = "OutputKMLPathBrowseButton"; + this.OutputKMLPathBrowseButton.Size = new System.Drawing.Size(28, 20); + this.OutputKMLPathBrowseButton.TabIndex = 7; + this.OutputKMLPathBrowseButton.Text = "..."; + this.OutputKMLPathBrowseButton.UseVisualStyleBackColor = true; + // + // OutputKMLPathLabel + // + this.OutputKMLPathLabel.AutoSize = true; + this.OutputKMLPathLabel.Location = new System.Drawing.Point(26, 105); + this.OutputKMLPathLabel.Name = "OutputKMLPathLabel"; + this.OutputKMLPathLabel.Size = new System.Drawing.Size(32, 13); + this.OutputKMLPathLabel.TabIndex = 6; + this.OutputKMLPathLabel.Text = "Path:"; + // + // OutputKMLPathTextbox + // + this.OutputKMLPathTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.OutputKMLPathTextbox.Enabled = false; + this.OutputKMLPathTextbox.Location = new System.Drawing.Point(71, 102); + this.OutputKMLPathTextbox.Name = "OutputKMLPathTextbox"; + this.OutputKMLPathTextbox.Size = new System.Drawing.Size(373, 20); + this.OutputKMLPathTextbox.TabIndex = 5; + // + // TriggersGroupbox + // + this.TriggersGroupbox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.TriggersGroupbox.Controls.Add(this.TriggerNewLogStationaryUnitsLabel); + this.TriggersGroupbox.Controls.Add(this.TriggerNewLogStationaryTimeEdit); + this.TriggersGroupbox.Controls.Add(this.TriggerNewLogStationaryCheckbox); + this.TriggersGroupbox.Controls.Add(this.TriggerWaitForMovementCheckbox); + this.TriggersGroupbox.Location = new System.Drawing.Point(12, 201); + this.TriggersGroupbox.Name = "TriggersGroupbox"; + this.TriggersGroupbox.Size = new System.Drawing.Size(482, 99); + this.TriggersGroupbox.TabIndex = 6; + this.TriggersGroupbox.TabStop = false; + this.TriggersGroupbox.Text = " Triggers "; + // + // TriggerWaitForMovementCheckbox + // + this.TriggerWaitForMovementCheckbox.AutoSize = true; + this.TriggerWaitForMovementCheckbox.Checked = true; + this.TriggerWaitForMovementCheckbox.CheckState = System.Windows.Forms.CheckState.Checked; + this.TriggerWaitForMovementCheckbox.Location = new System.Drawing.Point(10, 23); + this.TriggerWaitForMovementCheckbox.Name = "TriggerWaitForMovementCheckbox"; + this.TriggerWaitForMovementCheckbox.Size = new System.Drawing.Size(454, 17); + this.TriggerWaitForMovementCheckbox.TabIndex = 0; + this.TriggerWaitForMovementCheckbox.Text = " Wait for movement before logging the starting point (recommended, ignores initia" + + "l teleports)"; + this.TriggerWaitForMovementCheckbox.UseVisualStyleBackColor = true; + // + // TriggerNewLogStationaryCheckbox + // + this.TriggerNewLogStationaryCheckbox.AutoSize = true; + this.TriggerNewLogStationaryCheckbox.Location = new System.Drawing.Point(10, 46); + this.TriggerNewLogStationaryCheckbox.Name = "TriggerNewLogStationaryCheckbox"; + this.TriggerNewLogStationaryCheckbox.Size = new System.Drawing.Size(353, 17); + this.TriggerNewLogStationaryCheckbox.TabIndex = 1; + this.TriggerNewLogStationaryCheckbox.Text = " Start a new log when stationary for at least (excluding when paused):"; + this.TriggerNewLogStationaryCheckbox.UseVisualStyleBackColor = true; + // + // TriggerNewLogStationaryTimeEdit + // + this.TriggerNewLogStationaryTimeEdit.Enabled = false; + this.TriggerNewLogStationaryTimeEdit.Location = new System.Drawing.Point(33, 69); + this.TriggerNewLogStationaryTimeEdit.Name = "TriggerNewLogStationaryTimeEdit"; + this.TriggerNewLogStationaryTimeEdit.Size = new System.Drawing.Size(78, 20); + this.TriggerNewLogStationaryTimeEdit.TabIndex = 2; + this.TriggerNewLogStationaryTimeEdit.Value = new decimal(new int[] { + 30, + 0, + 0, + 0}); + // + // TriggerNewLogStationaryUnitsLabel + // + this.TriggerNewLogStationaryUnitsLabel.AutoSize = true; + this.TriggerNewLogStationaryUnitsLabel.Location = new System.Drawing.Point(117, 71); + this.TriggerNewLogStationaryUnitsLabel.Name = "TriggerNewLogStationaryUnitsLabel"; + this.TriggerNewLogStationaryUnitsLabel.Size = new System.Drawing.Size(47, 13); + this.TriggerNewLogStationaryUnitsLabel.TabIndex = 3; + this.TriggerNewLogStationaryUnitsLabel.Text = "seconds"; + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(506, 309); + this.Controls.Add(this.TriggersGroupbox); + this.Controls.Add(this.RecordingStatusIcon); + this.Controls.Add(this.RecordButton); + this.Controls.Add(this.FlightSimulatorStatusLabel); + this.Controls.Add(this.FlightSimulatorLabel); + this.Controls.Add(this.FlightSimulatorStatusIcon); + this.Controls.Add(this.OutputGroupbox); + this.Name = "MainForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "FS Flight Logger"; + this.OutputGroupbox.ResumeLayout(false); + this.OutputGroupbox.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.FlightSimulatorStatusIcon)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.RecordingStatusIcon)).EndInit(); + this.TriggersGroupbox.ResumeLayout(false); + this.TriggersGroupbox.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.TriggerNewLogStationaryTimeEdit)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.GroupBox OutputGroupbox; + private System.Windows.Forms.Button OutputKMLPathBrowseButton; + private System.Windows.Forms.Label OutputKMLPathLabel; + private System.Windows.Forms.TextBox OutputKMLPathTextbox; + private System.Windows.Forms.Button OutputCSVPathBrowseButton; + private System.Windows.Forms.Label OutputCSVPathLabel; + private System.Windows.Forms.TextBox OutputCSVPathTextbox; + private System.Windows.Forms.CheckBox OutputKMLCheckbox; + private System.Windows.Forms.CheckBox OutputCSVCheckbox; + private System.Windows.Forms.ImageList StatusImageList; + private System.Windows.Forms.PictureBox FlightSimulatorStatusIcon; + private System.Windows.Forms.Label FlightSimulatorLabel; + private System.Windows.Forms.Label FlightSimulatorStatusLabel; + private System.Windows.Forms.Button RecordButton; + private System.Windows.Forms.PictureBox RecordingStatusIcon; + private System.Windows.Forms.GroupBox TriggersGroupbox; + private System.Windows.Forms.Label TriggerNewLogStationaryUnitsLabel; + private System.Windows.Forms.NumericUpDown TriggerNewLogStationaryTimeEdit; + private System.Windows.Forms.CheckBox TriggerNewLogStationaryCheckbox; + private System.Windows.Forms.CheckBox TriggerWaitForMovementCheckbox; + } +} + diff --git a/FSFlightLogger/MainForm.cs b/FSFlightLogger/MainForm.cs new file mode 100644 index 0000000..cfcdd8d --- /dev/null +++ b/FSFlightLogger/MainForm.cs @@ -0,0 +1,49 @@ +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; +using SimConnect; + +namespace FSFlightLogger +{ + public partial class MainForm : Form + { + private readonly ISimConnectClientFactory simConnectClientFactory; + + + public MainForm(ISimConnectClientFactory simConnectClientFactory) + { + this.simConnectClientFactory = simConnectClientFactory; + + InitializeComponent(); + + SetFlightSimulatorConnected(false); + SetRecording(false); + //var simConnectClient = simConnectClientFactory.TryConnect("FS Flight Logger"); + } + + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing) + { + components?.Dispose(); + } + + base.Dispose(disposing); + } + + + private void SetFlightSimulatorConnected(bool connected) + { + FlightSimulatorStatusIcon.Image = StatusImageList.Images[connected ? "FSConnected" : "FSDisconnected"]; + } + + private void SetRecording(bool recording) + { + RecordingStatusIcon.Image = StatusImageList.Images[recording ? "Recording" : "Idle"]; + } + } +} diff --git a/FSFlightLogger/MainForm.resx b/FSFlightLogger/MainForm.resx new file mode 100644 index 0000000..a7ddf24 --- /dev/null +++ b/FSFlightLogger/MainForm.resx @@ -0,0 +1,474 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACO + UAAAAk1TRnQBSQFMAgEBBAEAAQgBAAEIAQABIAEAASABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAGA + AwABQAMAAQEBAAEgBgABgP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A + /wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A + /wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8AfgADPAFJA3QBlwOGAcIDiwHZA4sB3AOLAdwDjAHaA4YBwwN0 + AZgDPQFLAwEBAlQAAjwBOwFJAXMBcgFzAZcBfAF7AYgBwgF1AXQBlQHZAXIBcAGYAdwBcgFwAZgB3AF3 + AXYBlQHaAXwBewGIAcMCcgFzAZgCPQE8AUsDAQECMAADAQECAwQEBQEHAwUBBwMFAQcDBQEHAwUBBwMF + AQcDBQEHAwUBBwMFAQcDBQEHAwUBBwMFAQcDBQEHAwUBBwMFAQcDBQEHAwUBBwMFAQcDBQEHAwUBBwMF + AQcDBQEHAwUBBwMFAQcDAwEEAwABARAAAwIBAwMHAQkDCgENAwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgEO + AwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgEOAwoBDgMKAQ4DCgEO + AwoBDgMKAQ0DBgEIAwEBAigAAw0BEANzAZYDhgHfA4cB/wOEAf8DhAH/A4sB/wOQAf8DkAH/A4sB/wOE + Af8DgwH/A4YB/wOGAeQDdQGbAw8BE0AAAw0BEAJyAXABlgFoAWcBlQHfAUYBRQG0Af8COQG6Af8BMAEx + AcMB/wExATMB0QH/ATUBNwHYAf8BNQE3AdgB/wExATMB0QH/AjABwgH/AjgBuQH/AUUBRAGzAf8BZwFm + AZYB5AJ0AXEBmwMPARMnAAEBAwYBCAMQARUDGAEiAx4BKwMeASwDHgEsAx4BLAMeASwDHgEsAx4BLAMe + ASwDHgEsAx4BLAMeASwDHgEsAx4BLAMeASwDHgEsAx4BLAMeASwDHgEsAx4BLAMeASwDHgEsAx4BKwMX + ASADDgETAwQBBg8AAQEDCwEPAxwBKQErAikBRAEyAi8BVgEyAi8BVwEyAi8BVwEyATABLwFXATMBMAEv + AVcBMwEwAS8BVwEzATABLwFXATMCLwFXATMCLwFXATMCLwFXATMCLwFXATMCLwFXATMCLwFXATMCLwFX + ATMCLwFXATMBMAEvAVcBMwEwAS8BVwEzATABLwFXATMBMAEvAVcBMgIvAVcBMgIvAVcBMgEwAS8BVgEp + AScBKAFAARsCGgEmAwkBDCQAA14BdgOJAekDeQH/A4YB/wOdAf8DqgH/A7UB/wO6Af8DvQH/A70B/wO5 + Af8DtAH/A6oB/wOdAf8DhgH/A3cB/wOJAe8DYQF8OAACXwFcAXYCZwGcAekBLgEtAbAB/wEnASgBzwH/ + AUIBRQHnAf8BVgFcAfAB/wFlAXAB9wH/AW4BfgH6Af8BdAGGAfsB/wFzAYYB+wH/AW0BfAH6Af8BZAFu + AfcB/wFVAVwB8AH/AUIBRQHnAf8BJwEoAc8B/wIsAa4B/wJlAZwB7wJiAV4BfCAAAwEBAgMQARUDNgFf + A0EBfwNCAYADQgGAA0IBgANCAYADQgGAA0MBgANDAYADQwGAA0MBgANDAYADQwGAA0MBgANDAYADQwGA + A0MBgANDAYADQwGAA0IBgANCAYADQgGAA0IBgANCAYADQQF+AzMBVwMLAQ8DAAEBCAADAgEDAR0CHAEp + AU0BMQErAb4BWwEbAQoB/QFeARsBCAH/AV4BGwEIAf8BXwEcAQgB/wFhARwBCAH/AWEBGwEIAf8BYwEb + AQkB/wFjARsBCQH/AWMBGwEJAf8BYwEbAQkB/wFjARsBCQH/AWMBGwEJAf8BYwEbAQkB/wFjARsBCQH/ + AWMBGwEJAf8BYwEbAQkB/wFjARsBCQH/AWMBGwEJAf8BYQEcAQgB/wFhARsBCAH/AV8BGwEIAf8BXgEb + AQgB/wFeARsBCAH/AVsBHAELAfsBSwE0ATABrQMVAR0DAQECGAADBAEGA3gBpgN1Af8DgAH/A6IB/wOv + Af8DtwH/A78B/wPGAf8DygH/A8wB/wPMAf8DygH/A8UB/wO9Af8DtgH/A68B/wOiAf8DgAH/A3IB/wN9 + AbEDDAEPKAADBAEGAnIBeAGmAjEBpAH/Ah4BywH/AUkBTAHqAf8BYgFoAe8B/wFrAXcB9gH/AXYBiQH8 + Af8BgwGbAv8BjQGpAv8BkQGtAv8BkAGsAv8BjAGnAv8BgQGYAv8BdAGGAfsB/wFqAXUB9QH/AWEBZwHv + Af8BSQFMAesB/wIfAcwB/wEuAS0BoQH/AnYBfQGxAwwBDxsAAQEDNwFdA0cBgANFAYADRQGAA0YBgANG + AYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANHAYADRwGAA0cBgANH + AYADRwGAA0YBgANGAYADRQGAA0UBgANHAYADMQFOAwABAQgAAwEBAgFaATcBLgG6AYMBKwENAf8BdgEl + AQsB/wF3ASYBDAH/AXsBKAEMAf8BfgEoAQwB/wGCASkBDQH/AYQBKgENAf8BhgEqAQ0B/wGGASsBDQH/ + AYYBKwENAf8BhgErAQ0B/wGGASsBDQH/AYYBKwENAf8BhgErAQ0B/wGGASsBDQH/AYYBKwENAf8BhgEr + AQ0B/wGGASsBDQH/AYUBKgENAf8BhAEqAQ0B/wGBASkBDQH/AX0BKQEMAf8BewEnAQwB/wF3ASUBDAH/ + AXYBJgELAf8BgwEsAQ0B/wFRAToBNQGbAwABARQAAxUBGgOIAdcDbgH/A5MB/wOpAf8DsgH/A7kB/wO+ + Af8DwwH/A8gB/wPLAf8DygH/A8oB/wPKAf8DxwH/A8EB/wO8Af8DuAH/A7EB/wOoAf8DlAH/A20B/wOE + Ad4DFwEcIAADFQEaAnQBjwHXARcBFgGwAf8BNgE3Ad0B/wFaAV4B6QH/AWUBbQHxAf8BbgF7AfcB/wF3 + AYoB+AH/AYMBmQH6Af8BjQGnAfsB/wGRAa0B/AH/AZABqgH8Af8BkAGrAfwB/wGRAa0B+wH/AYsBpQH6 + Af8BgAGWAfgB/wF1AYcB9wH/AW0BeQH2Af8BZAFrAfAB/wFZAV0B6AH/ATcBOAHeAf8BFgEVAa8B/wJv + AYwB3gMXARwUAAMOARMDSAF/A0wBgANMAYADTQGAA04BgANPAYADUAGAA1ABgANQAYADUAGAA1ABgANQ + AYADUAGAA1ABgANQAYADUAGAA1ABgANQAYADUAGAA1ABgANQAYADTwGAA08BgANOAYADTQGAA0wBgANM + AYADRwF9AwQBBggAAR0CGwEmAY8BNAEQAf4BpQE9AREB/wGqAT8BEgH/AbABQQETAf8BtgFDARQB/wG7 + AUUBFAH/AcABSAEVAf8BwwFIARUB/wHEAUkBFQH/AcUBSgEVAf8BxQFKARUB/wHFAUoBFQH/AcUBSgEV + Af8BxQFKARUB/wHFAUoBFQH/AcUBSgEVAf8BxQFKARUB/wHFAUoBFQH/AcUBSgEVAf8BxAFJARUB/wHC + AUgBFQH/Ab8BSAEVAf8BuwFFARQB/wG1AUMBFAH/Aa8BQAETAf8BqgE/ARIB/wGkATwBEQH/AYgBMgET + AfkDCQEMFAADhwHWA2wB/wOZAf8DpwH/A68B/wOzAf8DtwH/A7wB/wPCAf8DwwH/A8QB/wPFAf8DxQH/ + A8QB/wPDAf8DwAH/A7sB/wO2Af8DsgH/A64B/wOmAf8DmQH/A2wB/wOFAeMDBwEJHAACcgGPAdYCDgGy + Af8BQgFDAd0B/wFZAV0B5gH/AWIBagHvAf8BaQF2Ae8B/wFxAYMB8AH/AXoBjwHyAf8BhAGbAfUB/wGH + AaAB9QH/AYkBowH2Af8BigGlAfYB/wGKAaUB9gH/AYkBowH1Af8BhwGfAfUB/wGCAZgB8wH/AXgBjQHx + Af8BbwGBAfAB/wFoAXQB7wH/AWEBaAHuAf8BWAFcAeUB/wFCAUMB3QH/Ag4BsgH/Am0BjgHjAwcBCRAA + AxgBIANLAYADUAGAA1EBgANTAYADVAGAA1UBgANWAYADYgGAA2ABgANXAYADVwGAA1cBgANXAYADVwGA + A1cBgANXAYADVwGAA1cBgANXAYADZQGAA1cBgANVAYADVQGAA1QBgANSAYADUQGAA1ABgANKAYADDgET + CAABLwEqASgBPwGiAT0BEQH/AcMBTAEVAf8BzQFQARYB/wHVAVUBGQH/AdoBWgEeAf8B3AFeASMB/wHd + AWEBKAH/AeoBogGAAf8B5wGVAW4B/wHfAWcBLgH/Ad8BZwEuAf8B3wFnAS4B/wHfAWcBLgH/Ad8BZwEu + Af8B3wFnAS4B/wHfAWcBLgH/Ad8BZwEuAf8B3wFnAS4B/wHfAWcBLgH/Ae0BswGXAf8B3wFqATMB/wHd + AWEBJwH/AdsBXgEiAf8B2QFZAR4B/wHUAVQBGAH/AcwBTwEWAf8BwAFLARUB/wGaATcBEAH/AR0BHAEb + ASYQAANzAZ0DaQH/A5QB/wOjAf8DqgH/A6wB/wOxAf8DtQH/A7kB/wO8Af8DvgH/A78B/wO/Af8DvwH/ + A78B/wO9Af8DuwH/A7kB/wO1Af8DsAH/A6wB/wOqAf8DowH/A5YB/wNoAf8DfAGyGAACbQF0AZ0CDwGq + Af8BPgFAAdgB/wFVAVkB4gH/AVwBZAHpAf8BYgFuAegB/wFpAXoB6gH/AXIBhgHsAf8BeQGQAe0B/wF+ + AZcB7gH/AYIBnAHvAf8BgwGeAfAB/wGEAZ8B8AH/AYQBngHwAf8BgwGeAfAB/wGBAZsB7wH/AX0BlgHu + Af8BeAGOAe0B/wFxAYQB6wH/AWgBeAHpAf8BYQFsAegB/wFcAWIB6AH/AVQBWAHiAf8CQAHZAf8CDwGp + Af8CdAF9AbIQAAMYASADTAGAA1EBgANTAYADVQGAA1YBgANXAYADWAGAA3EBgANoAYADWQGAA1kBgANZ + AYADWQGAA1kBgANZAYADWQGAA1kBgANZAYADWwGAA3QBgANoAYADWAGAA1cBgANWAYADVQGAA1MBgANR + AYADSgGAAw8BFAgAATABKgEoAT8BqAE/ARIB/wHOAVEBFgH/AdkBVwEaAf8B3AFfASQB/wHeAWYBLQH/ + Ad8BawEyAf8B4QFvATgB/wH6AesB5AH/AfEBwQGpAf8B4gFzAT8B/wHiAXMBPwH/AeIBcwE/Af8B4gFz + AT8B/wHiAXMBPwH/AeIBcwE/Af8B4gFzAT8B/wHiAXMBPwH/AeIBcwE/Af8B5AF9AU0B/wH9AfwB+wH/ + AfEBwAGpAf8B4QFuATcB/wHfAWoBMQH/Ad0BZQErAf8B3AFeASMB/wHYAVYBGQH/Ac0BUQEWAf8BnAE5 + ARAB/wEeARwBGwEnDAADRgFWA2wB/wOIAf8DnwH/A6UB/wOmAf8DqgH/A68B/wOzAf8DtgH/A7gB/wO6 + Af8DugH/A7sB/wO7Af8DugH/A7kB/wO4Af8DtgH/A7IB/wOuAf8DqQH/A6UB/wOkAf8DngH/A4oB/wNn + Af8DTgFiEAACRgFFAVYCJAGdAf8BLgEvAc0B/wFQAVMB3AH/AVcBXAHjAf8BWwFlAeIB/wFhAW8B5AH/ + AWkBegHmAf8BcAGEAegB/wF1AYwB6gH/AXkBkgHsAf8BewGVAe0B/wF8AZcB7QH/AX0BmAHtAf8BfQGY + Ae0B/wF8AZYB7QH/AXoBlQHtAf8BeAGRAewB/wF0AYoB6gH/AW8BgwHoAf8BZwF4AeYB/wFgAW4B4wH/ + AVkBYwHiAf8BVgFbAeMB/wFPAVIB2wH/ATABMQHQAf8BIAEfAZgB/wJOAUwBYgwAAxgBIANNAYADUgGA + A1QBgANWAYADVwGAA1gBgANeAYADdAGAA2kBgANaAYADWgGAA1oBgANaAYADWgGAA1oBgANaAYADWgGA + A1oBgANmAYADdQGAA3QBgANaAYADWAGAA1cBgANWAYADVAGAA1IBgANLAYADDwEUCAABMAEqASgBPwGr + AUEBEgH/AdUBUwEXAf8B2wFbASAB/wHdAWUBKwH/Ad8BawEyAf8B4QFwAToB/wHnAYwBYAH/A/4B/wHz + AcQBrgH/AeQBeQFHAf8B5AF5AUcB/wHkAXkBRwH/AeQBeQFHAf8B5AF5AUcB/wHkAXkBRwH/AeQBeQFH + Af8B5AF5AUcB/wHkAXkBRwH/Ae8BtAGYBf8B/QH7AfoB/wHjAXYBQgH/AeEBbwE5Af8B3wFqATEB/wHd + AWQBKQH/AdsBWwEeAf8B0wFSARcB/wGeATkBEQH/AR8BHAEbAScLAAEBA4YB6QNsAf8DmQH/A6AB/wOg + Af8DpAH/A6kB/wOtAf8DsQH/A7QB/wO2Af8DuAH/A7kB/wO5Af8DuQH/A7gB/wO3Af8DtgH/A7QB/wOw + Af8DrAH/A6gB/wOjAf8DoAH/A58B/wOYAf8DcAH/A4MB8QMBAQILAAEBAmEBnAHpAg0BtAH/AUsBTAHV + Af8BUQFVAd4B/wFUAVsB3AH/AVoBZAHeAf8BYQFuAeIB/wFnAXcB5QH/AW0BgAHoAf8BcQGGAeoB/wF0 + AYsB7AH/AXYBjQHtAf8BeAGQAe0B/wF5AZEB7gH/AXgBkQHuAf8BdwGPAe0B/wF2AY0B7AH/AXMBigHr + Af8BcAGFAeoB/wFsAX0B5wH/AWYBdgHkAf8BYAFsAeEB/wFZAWMB3QH/AVQBWwHbAf8BUAFUAd0B/wFK + AUsB1AH/AhEBuAH/AloBmAHxAwEBAggAAxgBIANNAYADUwGAA1UBgANXAYADWAGAA1kBgANoAYADdQGA + A2kBgANbAYADWwGAA1sBgANbAYADWwGAA1sBgANbAYADWwGAA1sBgANvAYADdQGAA28BgANaAYADWQGA + A1gBgANWAYADVQGAA1MBgANLAYADDwEUCAABMAEqASgBPwGvAUIBEwH/AdgBVgEZAf8B3AFgASUB/wHf + AWgBLwH/AeEBbwE4Af8B4wFzAUAB/wHxAb4BpgX/AfMByAGyAf8B5gF9AUwB/wHmAX4BTAH/AeYBfgFM + Af8B5gF+AUwB/wHmAX4BTAH/AeYBfgFMAf8B5gF+AUwB/wHmAX4BTAH/AeYBfgFMAf8B+AHjAdkF/wH4 + AeIB2AH/AeQBdwFGAf8B4gFzAT8B/wHhAW4BNwH/Ad4BaAEuAf8B3AFeASMB/wHXAVUBGAH/AaIBOwER + Af8BHwEcARsBJwgAA1oBcwNoAf8DiwH/A5kB/wOcAf8DngH/A6MB/wOnAf8DqwH/A64B/wOwAf8DsgH/ + A7MB/wO1Af8DtQH/A7UB/wO1Af8DswH/A7IB/wOwAf8DrQH/A6oB/wOmAf8DogH/A50B/wObAf8DmAH/ + A4wB/wNkAf8DZQGGCAABWQFYAVoBcwIYAZ8B/wE3ATgBywH/AUoBTQHWAf8BTwFUAdcB/wFTAVsB2AH/ + AVoBYwHbAf8BXwFrAd8B/wFlAXQB4gH/AWkBegHkAf8BbQGBAeYB/wFwAYQB5wH/AXEBhwHoAf8BcwGK + AekB/wF0AYsB6QH/AXQBiwHpAf8BcwGKAekB/wFxAYYB5wH/AW8BhAHnAf8BbAGAAeYB/wFpAXkB4wH/ + AWQBcgHhAf8BXgFqAd4B/wFZAWIB2gH/AVMBWgHXAf8BTgFTAdYB/wFJAUwB1QH/ATgBOQHMAf8CFQGb + Af8CYgFkAYYIAAMYASADTgGAA1MBgANWAYADVwGAA10BgANmAYADcgGAA3UBgANqAYADXAGAA1wBgANc + AYADXAGAA1wBgANcAYADXAGAA1wBgANfAYADdAGAA3UBgANqAYADWwGAA1oBgANZAYADVwGAA1UBgANT + AYADTAGAAw8BFAgAATABKgEoAT8BsgFEARMB/wHZAVgBGwH/Ad0BYwEoAf8B3wFrATIB/wHmAYoBXQH/ + Ae8BtgGcAf8B+wHxAewF/wH0AcoBtQH/AecBggFPAf8B5wGCAU8B/wHnAYIBTwH/AecBggFPAf8B5wGC + AU8B/wHnAYIBTwH/AecBggFPAf8B5wGCAU8B/wHqAZQBagH/A/4F/wH0AcsBtwH/AeUBewFJAf8B4wF2 + AUQB/wHiAXEBPAH/Ad8BagExAf8B3AFiAScB/wHZAVcBGgH/AaUBPQERAf8BHwEcARsBJwgAA4AB3ANp + Af8DkQH/A5YB/wOYAf8DmwH/A58B/wOjAf8DpwH/A6kB/wOrAf8DrQH/A64B/wOvAf8DrwH/A68B/wOv + Af8DrgH/A60B/wOrAf8DqQH/A6YB/wOjAf8DnwH/A5oB/wOYAf8DlgH/A5EB/wNrAf8DgQHxCAACXAGU + AdwCDQGtAf8BQwFFAcwB/wFIAUwB0gH/AUwBUQHRAf8BUQFZAdQB/wFXAWEB1wH/AVwBZwHaAf8BYgFv + AdwB/wFlAXUB3gH/AWgBeQHgAf8BagF9AeEB/wFtAYAB4gH/AW4BgwHjAf8BbgGDAeMB/wFuAYMB4wH/ + AW4BgwHiAf8BbAGAAeIB/wFqAXwB4QH/AWgBeQHgAf8BZQF0Ad4B/wFhAW0B3AH/AVsBZwHaAf8BVgFf + AdcB/wFQAVcB0wH/AUwBUQHRAf8BSAFMAdIB/wFDAUQBywH/ARABDwGvAf8CVgGYAfEIAAMYASADTgGA + A1QBgANiAYADbgGAA3QBgAN1AYADdQGAA3UBgANxAYADXwGAA1wBgANcAYADXAGAA1wBgANcAYADXAGA + A1wBgANpAYADdQGAA3UBgANlAYADWwGAA1oBgANZAYADVwGAA1YBgANUAYADTAGAAw8BFAgAATABKgEo + AT8BtQFFARMB/wHaAVoBHQH/AekBoAF9Af8B9wHhAdYB/wH+Av0N/wH6Ae0B5wH/AeoBkgFmAf8B6AGF + AVMB/wHoAYUBUwH/AegBhQFTAf8B6AGFAVMB/wHoAYUBUwH/AegBhQFTAf8B6AGFAVMB/wHzAcUBrwn/ + Ae8BsQGTAf8B5gF9AUsB/wHkAXcBRgH/AeIBcgE+Af8B4AFsATMB/wHdAWMBKQH/AdoBWQEcAf8BpwE9 + ARIB/wEfARwBGwEnBAADEgEWA3cB/wN5Af8DjwH/A5IB/wOVAf8DmAH/A5sB/wOfAf8DogH/A6UB/wOn + Af8DqAH/A6kB/wOqAf8DqgH/A6oB/wOqAf8DqQH/A6gB/wOmAf8DpAH/A6IB/wOfAf8DmwH/A5gB/wOU + Af8DkgH/A48B/wN8Af8DcQH/AygBMQMSARYBNQE0AaUB/wEkASUBugH/AUEBQwHKAf8BRQFJAcwB/wFJ + AU8BzgH/AU4BVQHQAf8BUgFcAdMB/wFYAWIB1gH/AVwBaAHYAf8BYAFuAdoB/wFjAXIB2wH/AWQBdQHc + Af8BZgF4Ad0B/wFnAXkB3gH/AWcBegHeAf8BZwF5Ad4B/wFnAXkB3gH/AWYBeAHdAf8BZAF0AdwB/wFi + AXIB2wH/AV8BbQHZAf8BWwFnAdgB/wFXAWIB1gH/AVEBWwHTAf8BTQFUAdAB/wFJAU4BzQH/AUUBSQHM + Af8BQQFDAckB/wInAbwB/wIuAZ4B/wMoATEEAAMYASADTgGAA1QBgANmAYADagGAA2sBgANsAYADbQGA + A3QBgAN1AYADcQGAA2ABgANdAYADXQGAA10BgANdAYADXQGAA10BgANxAYADdQGAA3QBgANgAYADWwGA + A1oBgANZAYADVwGAA1YBgANUAYADTAGAAw8BFAgAATABKgEoAT8BtwFEARMB/wHbAVoBHgH/Ae4BtQGa + Af8B8wHLAbgB/wH0AdABvwH/AfYB1QHGAf8B9gHZAcsB/wH+AfwB+wX/AfsB7QHnAf8B6wGZAW8B/wHo + AYcBVQH/AegBhwFVAf8B6AGHAVUB/wHoAYcBVQH/AegBhwFVAf8B6AGHAVUB/wH7Ae4B6AX/A/4B/wHq + AZUBawH/AeYBfQFMAf8B5AF5AUcB/wHiAXMBPwH/AeABbQE0Af8B3QFkASsB/wHaAVoBHQH/AakBPQES + Af8BHwEcARsBJwQAA0kBXANmAf8DgwH/A40B/wOOAf8DkQH/A5UB/wOYAf8DmwH/A54B/wOgAf8DogH/ + A6MB/wOkAf8DpQH/A6UB/wOlAf8DpAH/A6QB/wOjAf8DoQH/A6AB/wOdAf8DmgH/A5gB/wOUAf8DkQH/ + A44B/wONAf8DhAH/A2EB/wNnAYgCRwFKAVwCGQGbAf8BMgEzAb8B/wFAAUIBxwH/AUIBRwHHAf8BRgFM + AcoB/wFLAVEBzQH/AU8BVwHPAf8BUwFeAdEB/wFYAWMB0wH/AVsBaAHVAf8BXQFsAdYB/wFfAW4B1wH/ + AWEBcAHYAf8BYgFyAdgB/wFiAXMB2QH/AWIBcwHZAf8BYQFxAdgB/wFgAXAB2AH/AV4BbgHXAf8BXAFq + AdYB/wFaAWcB1QH/AVcBYgHTAf8BUgFdAdEB/wFOAVYBzwH/AUkBUQHNAf8BRQFKAckB/wFBAUUBxwH/ + AT8BQQHHAf8BMwE0Ab8B/wIUAZYB/wJjAWcBiAQAAxgBIANOAYADVAGAA1YBgANYAYADWQGAA1sBgANb + AYADZAGAA3QBgAN1AYADcgGAA2EBgANdAYADXQGAA10BgANdAYADYwGAA3UBgAN1AYADcwGAA1wBgANb + AYADWgGAA1kBgANXAYADVgGAA1QBgANMAYADDwEUCAABMAEqASgBPwG4AUQBEwH/AdsBWgEfAf8B3gFm + ASwB/wHgAW0BNgH/AeMBcwFAAf8B5QF6AUgB/wHmAX4BTAH/Ae4BrAGMAf8C/gH9Bf8B/AHyAe4B/wHr + AZ0BdQH/AegBhwFVAf8B6AGHAVUB/wHoAYcBVQH/AegBhwFVAf8B7QGmAYIJ/wH9AfgB9gH/AecBggFQ + Af8B5gF+AUwB/wHkAXkBRwH/AeIBcwE/Af8B4AFtATQB/wHdAWUBKwH/AdoBWgEdAf8BqgE+ARIB/wEf + ARwBGwEnBAADcwGiA1wB/wOFAf8DigH/A4sB/wOOAf8DkQH/A5QB/wOXAf8DmQH/A5sB/wOdAf8DngH/ + A58B/wOgAf8DoAH/A6AB/wOgAf8DnwH/A54B/wOdAf8DmwH/A5gB/wOWAf8DkwH/A5AB/wONAf8DigH/ + A4kB/wOFAf8DXgH/A3sBvgJmAXgBogEHAQYBmAH/ATcBOAG9Af8BPQE/AcMB/wE/AUMBwgH/AUMBSAHF + Af8BRwFOAcgB/wFLAVQBygH/AU8BWgHMAf8BUwFeAc0B/wFWAWIBzwH/AVgBZgHQAf8BWgFpAdEB/wFb + AWoB0QH/AVwBbAHSAf8BXQFtAdMB/wFdAWwB0wH/AVwBbAHSAf8BWwFqAdEB/wFZAWgB0QH/AVgBZQHQ + Af8BVQFhAc8B/wFSAV4BzQH/AU8BWQHLAf8BSgFTAckB/wFGAU0BxwH/AUIBRwHEAf8BPQFBAcIB/wE8 + AT8BwgH/AjcBvQH/AQkBCAGbAf8CagGCAb4EAAMYASADTgGAA1QBgANWAYADWAGAA1kBgANbAYADWwGA + A1wBgANlAYADdAGAA3UBgANzAYADYgGAA10BgANdAYADXQGAA2sBgAN1AYADdQGAA24BgANcAYADWwGA + A1oBgANZAYADVwGAA1YBgANUAYADTQGAAw8BFAgAATABKgEoAT8BuQFFARMB/wHbAVoBHwH/Ad4BZgEs + Af8B4AFtATYB/wHjAXMBQAH/AeUBegFIAf8B5gF+AUwB/wHnAYIBUAH/Ae8BsQGTAf8D/gX/AfwB9gH0 + Af8B7AGkAYAB/wHoAYcBVQH/AegBhwFVAf8B6AGHAVUB/wH1AdEBvwn/AfgB4AHUAf8B5wGCAU8B/wHm + AX4BTAH/AeQBeQFHAf8B4gFzAT8B/wHgAW0BNAH/Ad0BZQErAf8B2gFaAR0B/wGrAT8BEgH/AR8BHAEb + AScEAAN9AckDXwH/A4IB/wOGAf8DhwH/A4oB/wONAf8DjwH/A5EB/wOUAf8DlgH/A5cB/wOYAf8DmgH/ + A5oB/wOaAf8DmgH/A5oB/wOaAf8DmAH/A5cB/wOWAf8DkwH/A5EB/wOPAf8DjQH/A4kB/wOGAf8DhQH/ + A4IB/wNiAf8DfAHUAmIBiwHJAQgBBwGeAf8CNgG5Af8BOQE8Ab4B/wE7AT4BvgH/AT8BRAHBAf8BQgFJ + AcMB/wFFAU4BxQH/AUkBUwHHAf8BTQFYAckB/wFQAVwBygH/AVIBXwHLAf8BUwFhAcwB/wFVAWMBzQH/ + AVYBZQHNAf8BVgFlAc0B/wFWAWUBzQH/AVYBZQHNAf8BVQFjAc0B/wFTAWABywH/AVIBXgHLAf8BTwFb + AcoB/wFMAVgByAH/AUkBUgHGAf8BRQFNAcUB/wFCAUgBwwH/AT4BRAHAAf8BOwE+Ab0B/wE5ATsBvQH/ + AjUBuQH/AgsBoQH/Al0BiwHUBAADGAEgA08BgANUAYADVgGAA1gBgANZAYADWwGAA1sBgANcAYADXAGA + A2gBgAN0AYADdQGAA3MBgANkAYADXQGAA2UBgAN0AYADdQGAA3UBgANoAYADXAGAA1sBgANaAYADWQGA + A1cBgANWAYADVAGAA00BgAMPARQIAAEwASoBKAE/AboBRQETAf8B2wFbAR8B/wHeAWYBLAH/AeABbQE2 + Af8B4wFzAUAB/wHlAXoBSAH/AeYBfgFMAf8B5wGCAVAB/wHoAYYBVAH/AfIBwAGoAf8D/gX/Af0B+gH4 + Af8B7gGqAYgB/wHoAYcBVQH/Ae8BsAGRAf8B/gH9AfwJ/wHzAcIBqgH/AecBggFPAf8B5gF+AUwB/wHk + AXkBRwH/AeIBcwE/Af8B4AFtATQB/wHdAWUBKwH/AdoBWgEdAf8BrAE/ARIB/wEfARwBGwEnBAADdgHH + A2IB/wN+Af8DggH/A4MB/wOFAf8DiAH/A4sB/wONAf8DjgH/A5EB/wOSAf8DkwH/A5QB/wOUAf8DlAH/ + A5QB/wOUAf8DkwH/A5MB/wOSAf8DkAH/A44B/wOMAf8DigH/A4cB/wOFAf8DgwH/A4IB/wN9Af8DYwH/ + A3oB3QJZAYYBxwILAZ8B/wIzAbUB/wE2ATgBuAH/ATcBOgG5Af8BOgE/AbsB/wE+AUMBvgH/AUEBSAHA + Af8BRAFMAcEB/wFGAVABwwH/AUkBVQHFAf8BSwFXAcUB/wFNAVkBxgH/AU8BXAHHAf8BTwFdAcYB/wFP + AV0BxwH/AU8BXQHHAf8BTwFdAcYB/wFOAVwBxgH/AU0BWQHGAf8BSwFXAcUB/wFJAVQBxAH/AUcBUAHC + Af8BQwFMAcEB/wFAAUcBvwH/AT0BQwG9Af8BOgE+AbsB/wE3ATsBuQH/ATYBNwG4Af8CMgG0Af8CDQGh + Af8CVgGOAd0EAAMYASADTwGAA1QBgANWAYADWAGAA1kBgANbAYADXAGAA10BgANeAYADXwGAA2wBgAN0 + AYADdQGAA3QBgANrAYADdAGAA3UBgAN1AYADdQGAA2YBgANdAYADXAGAA1sBgANZAYADVwGAA1YBgANU + AYADTQGAAw8BFAgAATABKgEoAT8BugFFARMB/wHbAVsBHwH/Ad4BZgEsAf8B4AFtATYB/wHjAXMBQQH/ + AeUBfQFNAf8B5gGFAVYB/wHnAYoBXQH/AegBjwFiAf8B6AGSAWcB/wH1AdYBxwH/A/4F/wH+AfwB+wH/ + AfMBzgG8Af8B/gL8Df8B7wG0AZcB/wHnAYkBXAH/AeYBhQFVAf8B5AF8AUsB/wHiAXMBPwH/AeABbQE0 + Af8B3QFlASsB/wHaAVoBHQH/AawBPwESAf8BHwEcARsBJwQAA3UByANeAf8DegH/A30B/wN+Af8DgQH/ + A4MB/wOEAf8DhgH/A4gB/wOKAf8DiwH/A40B/wONAf8DjQH/A40B/wONAf8DjQH/A40B/wOMAf8DiwH/ + A4kB/wOHAf8DhQH/A4QB/wODAf8DgQH/A30B/wN9Af8DegH/A2AB/wN4AdwCWAGGAcgBCQEIAZsB/wEv + ATABsAH/ATIBNAGzAf8BNAE3AbQB/wE2ATsBtgH/ATkBPgG4Af8BOwFBAbkB/wE9AUUBuwH/AT8BSAG8 + Af8BQgFMAb0B/wFDAU4BvgH/AUYBUgHAAf8BRwFUAcAB/wFHAVQBwAH/AUcBVAHAAf8BRwFUAcAB/wFH + AVQBwAH/AUcBUwHAAf8BRQFRAb8B/wFDAU4BvgH/AUEBSwG9Af8BPwFHAbsB/wE8AUUBugH/AToBQQG5 + Af8BOAE+AbcB/wE2AToBtQH/ATMBNgGzAf8BMgEzAbMB/wIvAbAB/wELAQoBnQH/AlQBjAHcBAADGAEg + A08BgANUAYADWAGAA1wBgANeAYADXwGAA18BgANgAYADYAGAA2ABgANhAYADbgGAA3UBgAN1AYADdQGA + A3UBgAN1AYADdQGAA3QBgANiAYADYAGAA18BgANfAYADXgGAA1sBgANYAYADVAGAA00BgAMPARQIAAEw + ASoBKAE/AboBRQETAf8B2wFcASAB/wHfAXEBOwH/AeIBggFTAf8B5AGNAWIB/wHmAZIBaQH/AecBlAFt + Af8B6AGWAW8B/wHoAZkBcQH/AegBmQFyAf8B6QGbAXUB/wH3Ad4B0xn/A/4B/wHqAaQBgwH/AegBlQFv + Af8B5wGUAWwB/wHmAZEBaAH/AeQBjQFhAf8B4gGBAVEB/wHfAXABOgH/AdoBWwEeAf8BrAE/ARIB/wEf + ARwBGwEnBAADdQHCA1cB/wN2Af8DeQH/A3oB/wN6Af8DfAH/A4EB/wOEAf8DhgH/A4gB/wOJAf8DiwH/ + A4wB/wOMAf8DjAH/A4wB/wOMAf8DjAH/A4sB/wOJAf8DiAH/A4YB/wOEAf8DgQH/A3sB/wN6Af8DeQH/ + A3gB/wN2Af8DWgH/A3YB0gJaAYMBwgIBAZIB/wIsAasB/wEvATABrwH/ATABMwGvAf8BMAE0Aa8B/wEy + ATgBsQH/ATgBPgG0Af8BPAFDAbYB/wE/AUcBuAH/AUIBSwG6Af8BRAFNAboB/wFGAVABvAH/AUcBUgG9 + Af8BRwFTAb0B/wFIAVMBvAH/AUgBUwG9Af8BRwFSAb0B/wFHAVIBvQH/AUYBUAG8Af8BQwFMAboB/wFC + AUoBuQH/AT8BRwG4Af8BPAFCAbYB/wE4AT4BtAH/ATIBNwGwAf8BMAEzAa8B/wEvATIBrgH/AS4BMAGu + Af8CKwGrAf8CBAGVAf8CVgGGAdIEAAMYASADTwGAA1cBgANdAYADXgGAA18BgANfAYADYAGAA2EBgANh + AYADYQGAA2EBgANhAYADcQGAA3UBgAN1AYADdQGAA3UBgAN1AYADcgGAA2EBgANgAYADYAGAA18BgANf + AYADXgGAA1wBgANXAYADTQGAAw8BFAgAATABKgEoAT8BugFFARMB/wHcAWkBMwH/AeIBhwFcAf8B4wGO + AWMB/wHlAZIBaQH/AeYBlQFtAf8B5wGYAXIB/wHoAZoBdQH/AegBnAF2Af8B6AGcAXcB/wHoAZwBdwH/ + AegBnwF7Af8B+gHtAecV/wH8AfQB8QH/AegBmwF3Af8B6AGZAXQB/wHnAZcBcQH/AeYBlQFsAf8B5QGS + AWkB/wHjAY0BYgH/AeEBhwFbAf8B3AFpATIB/wGsAT8BEgH/AR8BHAEbAScEAANpAZoDUQH/A3IB/wN2 + Af8DdAH/A3gB/wOCAf8DhwH/A4kB/wOKAf8DiwH/A4wB/wONAf8DjgH/A44B/wOOAf8DjgH/A40B/wON + Af8DjAH/A4wB/wOKAf8DiQH/A4gB/wOGAf8DgQH/A3cB/wNzAf8DdQH/A3EB/wNSAf8DdAG8AlwBbwGa + AgABhwH/AigBpgH/ASwBLQGqAf8BKAErAakB/wEuATIBrAH/AT0BQQGyAf8BQwFHAbUB/wFFAUoBtwH/ + AUcBTQG4Af8BSQFPAbkB/wFKAVIBugH/AUsBUwG7Af8BTAFUAbwB/wFMAVQBvAH/AU0BVQG8Af8BTQFV + AbwB/wFMAVQBuwH/AUwBUwG7Af8BSwFSAboB/wFKAVEBugH/AUgBTwG4Af8BRgFNAbcB/wFEAUkBtgH/ + AUIBRgG1Af8BPAFAAbEB/wEuATEBqwH/ASgBKgGoAf8CLAGpAf8BKAEnAaUB/wIAAYoB/wJgAXwBvAQA + AxgBIANPAYADWAGAA14BgANfAYADXwGAA2ABgANhAYADYQGAA2IBgANiAYADYgGAA2kBgAN0AYADdQGA + A3UBgAN1AYADdQGAA3UBgANvAYADYgGAA2EBgANhAYADYAGAA18BgANeAYADXQGAA1gBgANNAYADDwEU + CAABMAEqASgBPwG6AUUBEwH/Ad0BbQE7Af8B4gGLAWMB/wHkAZEBaQH/AeUBlQFuAf8B5gGaAXQB/wHn + AZwBeAH/AegBngF7Af8B6QGgAXwB/wHpAaABfQH/AekBoAF9Af8B8AHFAbAB/wP+Ff8B+AHkAdoB/wHp + AaABfAH/AegBngF6Af8B5wGcAXcB/wHmAZoBcwH/AeUBlQFuAf8B5AGRAWgB/wHiAYsBYgH/Ad0BbQE5 + Af8BrAE/ARIB/wEfARwBGwEnBAADSAFfA1EB/wNrAf8DbwH/A3MB/wOFAf8DiAH/A4gB/wOJAf8DigH/ + A4sB/wOMAf8DjQH/A44B/wOOAf8DjgH/A44B/wOOAf8DjgH/A40B/wOMAf8DiwH/A4kB/wOIAf8DiAH/ + A4gB/wOEAf8DcwH/A28B/wNsAf8DTQH/A2MBhwJGAUkBXwIDAYMB/wEhASABoAH/AiUBowH/ASoBLAGm + Af8BQwFFAbEB/wFIAUsBtAH/AUcBSwG0Af8BSQFNAbUB/wFKAU8BtgH/AUsBUQG3Af8BTQFTAbcB/wFO + AVQBuAH/AU8BVQG5Af8BTwFWAbkB/wFQAVYBuQH/AVABVgG5Af8BTwFWAbkB/wFPAVUBuAH/AU4BUwG3 + Af8BTQFSAbcB/wFLAVEBtgH/AUkBTwG1Af8BSAFNAbQB/wFHAUoBtAH/AUgBSwG0Af8BQgFEAbAB/wEq + ASwBpQH/AiUBowH/AiEBoAH/AgABfQH/Al0BZAGHBAADGAEgA04BgANZAYADXwGAA18BgANgAYADYQGA + A2EBgANiAYADYgGAA2cBgANtAYADdAGAA3UBgAN1AYADdQGAA3UBgAN1AYADdQGAA28BgANiAYADYgGA + A2EBgANhAYADYAGAA18BgANeAYADWAGAA00BgAMPARQIAAEwASoBKAE/AbkBRQETAf8B3gFyAUAB/wHj + AZIBagH/AeQBlQFwAf8B5QGYAXYB/wHnAZwBegH/AecBnwF9Af8B6AGhAYAB/wHpAaIBgQH/Ae0BuQGh + Af8B9QHbAc8B/wP+Gf8B+AHjAdoB/wHpAaQBgwH/AegBoAF+Af8B5wGfAXwB/wHmAZwBeQH/AeUBmAF1 + Af8B5AGVAW8B/wHjAZEBaQH/Ad0BcQE/Af8BqwE/ARIB/wEfARwBGwEnBAADDwETA2EB/wNdAf8DaQH/ + A3oB/wOMAf8DhwH/A4kB/wOLAf8DiwH/A4wB/wONAf8DjgH/A44B/wOOAf8DjwH/A48B/wOPAf8DjgH/ + A44B/wONAf8DjQH/A4sB/wOKAf8DiQH/A4cB/wOLAf8DegH/A2gB/wNfAf8DWAH/AyoBNAMPARMCHQGL + Af8CDgGTAf8CHQGcAf8CNwGnAf8BUAFRAbMB/wFJAUsBsQH/AUsBTgGyAf8BTQFQAbQB/wFOAVIBtAH/ + AU8BVAG1Af8BUAFVAbUB/wFRAVYBtgH/AVIBVwG2Af8BUgFYAbcB/wFTAVgBtwH/AVMBWAG3Af8BUwFX + AbcB/wFSAVgBtwH/AVEBVgG2Af8BUAFVAbUB/wFQAVQBtQH/AU4BUgG0Af8BTQFQAbIB/wFLAU4BsgH/ + AUkBSwGxAf8BUAFSAbIB/wI3AacB/wEcAR0BnAH/AhABlQH/AhQBggH/AyoBNAQAAxgBIANOAYADWQGA + A18BgANgAYADYQGAA2IBgANkAYADagGAA3IBgAN0AYADdQGAA3UBgAN1AYADdQGAA3UBgAN1AYADdQGA + A3UBgAN1AYADcAGAA2QBgANiAYADYQGAA2EBgANgAYADXwGAA1kBgANNAYADDwEUCAABMAEqASgBPwG5 + AUUBEwH/Ad4BdgFFAf8B4wGWAXEB/wHlAZoBdgH/AeYBngF7Af8B5wGhAYAB/wHpAaoBjQH/AfEBzQG8 + Af8B+gHwAewB/wP+Jf8B+AHmAd4B/wHpAakBjQH/AecBogGCAf8B5gGhAX4B/wHmAZ4BewH/AeQBmgF1 + Af8B4wGWAXAB/wHdAXUBRAH/AasBPgESAf8BHwEcARsBJwgAA2oB3QNPAf8DYwH/A3wB/wOPAf8DigH/ + A4wB/wOMAf8DjgH/A44B/wOPAf8DkAH/A5AB/wOQAf8DkAH/A5AB/wOQAf8DkAH/A5AB/wOPAf8DjgH/ + A44B/wOMAf8DiwH/A4oB/wOPAf8DfAH/A2IB/wNQAf8DaQHvCAACQAGCAd0CAAGDAf8CGAGVAf8BPQE8 + AacB/wJYAbMB/wFQAVEBsAH/AVEBUgGyAf8BUwFUAbIB/wFVAVYBswH/AVUBWAG0Af8BVgFZAbQB/wFX + AVsBtQH/AVcBWwG1Af8BVwFbAbUB/wFYAVsBtQH/AVgBWwG1Af8BVwFbAbUB/wFXAVsBtQH/AVcBWwG1 + Af8BVgFZAbQB/wFVAVcBtAH/AVUBVgGzAf8BUwFUAbIB/wFRAVIBsQH/AVABUQGwAf8CWAGzAf8CPAGn + Af8CFwGUAf8CAAGEAf8COQGEAe8IAAMYASADTgGAA1oBgANgAYADYgGAA2cBgANuAYADdAGAA3UBgAN1 + AYADdQGAA3UBgAN1AYADdQGAA3QBgAN0AYADcgGAA3MBgAN1AYADdQGAA3UBgANxAYADZAGAA2IBgANi + AYADYQGAA2ABgANaAYADTQGAAw8BFAgAATABKgEoAT8BuAFGARMB/wHeAXoBSgH/AeQBmgF3Af8B5gGf + AYAB/wHsAb0BpgH/AfYB4AHVAf8B/gL8Gf8D/gH/Af4B/QH8Af8B+gHyAe4B/wH8AfcB9A3/AfkB7gHp + Af8B6QGuAZIB/wHnAaMBhQH/AeYBogGBAf8B5QGdAXwB/wHkAZkBdgH/Ad4BegFKAf8BqwE+ARIB/wEf + ARwBGwEnCAADUgFwA0cB/wNZAf8DeQH/A5UB/wOMAf8DjgH/A48B/wOQAf8DkAH/A5IB/wOSAf8DkgH/ + A5IB/wOTAf8DkwH/A5IB/wOSAf8DkgH/A5EB/wOQAf8DjwH/A48B/wOOAf8DjAH/A5QB/wN5Af8DWgH/ + A0YB/wNiAYoIAAJNAVQBcAIAAXEB/wIKAY0B/wI6AaQB/wJhAbYB/wJWAa8B/wJXAbEB/wFYAVkBsgH/ + AVkBWgGzAf8BWgFbAbMB/wFcAV0BtAH/AVwBXgG0Af8BXAFeAbUB/wFcAV8BtAH/AV0BXwG1Af8BXQFf + AbUB/wFcAV8BtAH/AVwBXgG0Af8BXAFfAbQB/wFbAV0BtAH/AVoBWwGzAf8BWQFaAbIB/wJYAbIB/wJX + AbEB/wFXAVYBrwH/AmEBtQH/AjoBpAH/AgsBjgH/AgABbgH/AlsBZAGKCAADGAEgA04BgANbAYADaAGA + A3IBgAN0AYADdQGAA3UBgAN1AYADdQGAA3QBgANzAYADbwGAA2wBgANoAYADZQGAA2QBgANmAYADcgGA + A3UBgAN1AYADdQGAA3EBgANjAYADYgGAA2IBgANhAYADWwGAA0wBgAMPARQIAAEwASoBKAE/AbYBRAET + Af8B3wF9AVAB/wHtAcIBrwH/AfsB9AHxAf8D/hH/Af4C/QH/AfwB9wH1Af8B9gHhAdkB/wHzAdMBxwH/ + Ae0BvgGrAf8B6gGvAZcB/wHoAakBjwH/AesBtQGfAf8B+wH0AfEN/wH5AewB5gH/AegBqgGNAf8B5gGl + AYYB/wHlAaEBgwH/AeQBngF9Af8B3gF7AU8B/wGpAT0BEgH/AR8BHAEbAScMAANlAegDSAH/A3EB/wOb + Af8DkAH/A5EB/wORAf8DkgH/A5MB/wOUAf8DlAH/A5QB/wOUAf8DlQH/A5UB/wOUAf8DlAH/A5QB/wOU + Af8DkwH/A5IB/wORAf8DkQH/A5AB/wObAf8DcQH/A0kB/wNfAfMDBAEGDAACNQF/AegCAAFyAf8CMAGc + Af8CawG5Af8CXQGwAf8CXQGxAf8CXwGxAf8CXwGyAf8BYAFhAbMB/wFhAWIBtAH/AWEBYgG0Af8BYQFj + AbQB/wFiAWMBtAH/AWIBZAG1Af8BYgFkAbUB/wFiAWQBtAH/AWEBYgG0Af8BYQFiAbQB/wFhAWIBtAH/ + AWABYQGzAf8CXwGyAf8CXwGxAf8BXgFdAbEB/wJdAbAB/wJrAbgB/wIwAZwB/wIAAXQB/wIsAX0B8wME + AQYIAAMYASADTgGAA1sBgANkAYADcQGAA3QBgAN0AYADcAGAA20BgANqAYADZgGAA2QBgANkAYADZAGA + A2QBgANkAYADZAGAA2QBgANmAYADcgGAA3UBgAN1AYADdQGAA28BgANjAYADYwGAA2IBgANbAYADTAGA + Aw8BFAgAATABKgEoAT8BswFEARMB/wHeAYIBVgH/AecBrAGRAf8B+QHtAegB/wP+Af8B/QH6AfkB/wH4 + AeoB5AH/AfQB2QHNAf8B7wHKAbgB/wHqAbYBngH/AekBsAGUAf8B6AGuAZIB/wHoAa4BkgH/AegBrgGS + Af8B6AGuAZIB/wHoAa4BkgH/AegBrgGSAf8B6wG4AaAB/wH6AfIB7g3/AfYB4QHYAf8B5gGnAYsB/wHm + AaUBiAH/AeUBowGEAf8B3gGBAVUB/wGmAT0BEQH/AR8CHAEnDAADQgFVA0IB/wNVAf8DjgH/A54B/wOT + Af8DlQH/A5YB/wOWAf8DlwH/A5cB/wOXAf8DmAH/A5gB/wOYAf8DlwH/A5cB/wOXAf8DlgH/A5YB/wOV + Af8DlQH/A5MB/wOdAf8DjwH/A1YB/wM/Af8DUAFoEAACQAFDAVUCAAFmAf8CDAGCAf8CWwGuAf8CcQG6 + Af8CYgGyAf8CZQGzAf8CZgGzAf8BZgFnAbMB/wJnAbQB/wJnAbQB/wFnAWgBtAH/AWgBaQG1Af8BaAFp + AbUB/wFoAWkBtQH/AWcBaQG1Af8BZwFoAbQB/wJnAbQB/wFmAWcBswH/AWYBZwGzAf8CZgGyAf8CZQGy + Af8CYgGxAf8CcQG5Af8CXAGvAf8CDAGEAf8CAAFgAf8CTQFQAWgMAAMYASADTQGAA1wBgANjAYADZAGA + A2YBgANkAYADZAGAA2QBgANkAYADZAGAA2QBgANkAYADZAGAA2QBgANkAYADZAGAA2QBgANkAYADZgGA + A3EBgAN1AYADdQGAA3UBgANtAYADYwGAA2MBgANcAYADSwGAAw8BFAgAATABKgEoAT8BsAFDARMB/wHd + AYQBXAH/AeUBpAGKAf8B5wGsAZQB/wHpAbkBogH/AecBsAGVAf8B5wGvAZIB/wHoAa8BkwH/AegBsQGV + Af8B6AGwAZUB/wHoAbABlQH/AegBsAGVAf8B6AGwAZUB/wHoAbABlQH/AegBsAGVAf8B6AGwAZUB/wHo + AbABlQH/AegBsAGVAf8B6gG3AZ8B/wH6AfAB6w3/AfMB2QHNAf8B5gGmAYwB/wHlAaQBiQH/Ad0BhAFb + Af8BowE9AREB/wEfAhwBJxAAA2cBoQNAAf8DYgH/A6gB/wOhAf8DmQH/A5oB/wOaAf8DmwH/A5sB/wOb + Af8DmwH/A5sB/wObAf8DmwH/A5sB/wOaAf8DmgH/A5oB/wOaAf8DmQH/A6AB/wOoAf8DZAH/Az4B/wNt + AbYYAAJYAW4BoQIAAWEB/wIgAYwB/wKCAcAB/wJ3AboB/wJrAbQB/wJuAbUB/wJuAbUB/wJuAbYB/wFv + AW4BtgH/Am8BtgH/Am8BtgH/Am8BtgH/Am8BtgH/Am8BtgH/Am8BtgH/AW8BbgG1Af8CbgG1Af8CbgG1 + Af8CbgG1Af8CawG0Af8CdgG5Af8CggHAAf8CIQGOAf8CAAFcAf8CWQF2AbYQAAMYASADTQGAA10BgANj + AYADZAGAA2QBgANkAYADZAGAA2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANl + AYADZQGAA2YBgANxAYADdQGAA3UBgAN0AYADZQGAA2MBgANdAYADSwGAAw8BFAgAATABKgEoAT8BrgFD + ARMB/wHdAYoBZAH/AeUBqAGOAf8B5gGqAZAB/wHmAa0BkgH/AecBrgGUAf8B5wGuAZYB/wHoAa8BlwH/ + AegBrwGYAf8B6AGvAZgB/wHoAa8BmAH/AegBrwGYAf8B6AGvAZgB/wHoAa8BmAH/AegBrwGYAf8B6AGv + AZgB/wHoAa8BmAH/AegBrwGYAf8B6AGvAZgB/wHpAbUBoAH/AfgB7AHnCf8D/gH/AecBrwGYAf8B5QGo + AY0B/wHcAYoBYwH/AaIBPQERAf8BHwIcAScTAAEBA10B1QNDAf8DbAH/A7IB/wOuAf8DnwH/A6EB/wOh + Af8DoQH/A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/A6EB/wOhAf8DoQH/A58B/wOrAf8DswH/A28B/wM9 + Af8DXwHjAwYBCBsAAQECNwFwAdUCAAFnAf8CLwGRAf8CkQHHAf8CiwHDAf8CdQG3Af8CeAG5Af8CeQG5 + Af8CeQG6Af8CeQG5Af8CeQG5Af8CeQG5Af8CeQG5Af8CeQG5Af8CeQG5Af8CeQG5Af8CeQG5Af8CeAG5 + Af8CdQG3Af8CiAHBAf8CkgHIAf8CMwGVAf8CAAFbAf8CNwFzAeMDBgEIEAADGAEgA00BgANcAYADZAGA + A2QBgANkAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGA + A2UBgANlAYADZgGAA3ABgAN0AYADcgGAA2UBgANkAYADXAGAA0sBgAMPARQIAAEwASoBKAE/AawBQgES + Af8B2wGJAWEB/wHlAakBkgH/AeYBrAGUAf8B5gGvAZYB/wHmAbABmAH/AecBsQGZAf8B5wGwAZoB/wHn + AbIBmgH/AecBswGbAf8B5wGzAZsB/wHnAbMBmwH/AecBswGbAf8B5wGzAZsB/wHnAbMBmwH/AecBswGb + Af8B5wGzAZsB/wHnAbMBmwH/AecBswGbAf8B5wGyAZoB/wHoAbQBngH/AfgB6gHjAf8D/gH/AfoB8gHv + Af8B5gGvAZgB/wHlAakBkQH/AdoBigFhAf8BoQE8AREB/wEfARwBGwEnFAADDgESA1AB0QM/Af8DZAH/ + A6kB/wPCAf8DsQH/A6cB/wOoAf8DqQH/A6kB/wOpAf8DqQH/A6kB/wOpAf8DqAH/A6cB/wOvAf8DwQH/ + A64B/wNnAf8DOQH/A10B3AMWARsgAAMOARICKAFhAdECAAFfAf8CJwGJAf8ChQG/Af8CqAHSAf8CkQHE + Af8CgwG8Af8ChQG9Af8ChQG+Af8ChgG+Af8ChgG+Af8ChgG+Af8ChgG+Af8ChgG+Af8ChQG9Af8CgwG8 + Af8CjgHCAf8CpwHRAf8CiwHDAf8CLAGLAf8CAAFSAf8COQFuAdwDFgEbFAADEQEXA0sBgANXAYADZAGA + A2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGAA2UBgANlAYADZQGA + A2UBgANlAYADZQGAA2UBgANnAYADZgGAA2UBgANkAYADVwGAA0kBfgMGAQgIAAEjASABHwEtAZ4BOwER + Af8B0QFuAT4B/wHmAasBlwH/AeYBsAGZAf8B5gGzAZoB/wHnAbQBmwH/AecBtQGcAf8B5wG2AZwB/wHn + AbYBnAH/AecBuQGdAf8B5wG5AZ0B/wHnAbkBnQH/AecBuQGdAf8B5wG5AZ0B/wHnAbkBnQH/AecBuQGd + Af8B5wG5AZ0B/wHnAbkBnQH/AecBuQGdAf8B5wG1AZwB/wHnAbYBnAH/AecBtwGeAf8B6QG/AagB/wHn + AbcBnwH/AeYBsAGZAf8B5gGrAZcB/wHRAW8BPgH/AZIBNgERAfwBDQIMARAYAAMEAQUDZQGlAzkB/wNF + Af8DhAH/A7MB/wPKAf8DxAH/A70B/wO1Af8DswH/A7MB/wO0Af8DuwH/A8QB/wPKAf8DugH/A4wB/wNF + Af8DNQH/A2kBsQMKAQ0oAAMEAQUCVQFtAaUCAAFTAf8CAAFsAf8CVAGgAf8ClQHFAf8CtAHXAf8CrQHT + Af8CogHNAf8CmAHHAf8ClAHFAf8ClAHFAf8ClwHGAf8CoAHLAf8CrAHSAf8CtQHXAf8CngHKAf8CXgGm + Af8CAAFtAf8CAAFJAf8CVwFxAbEDCgENHAADPQFkA08BgANXAYADXgGAA2ABgANgAYADYAGAA2EBgANh + AYADYQGAA2EBgANhAYADYQGAA2EBgANhAYADYQGAA2EBgANhAYADYQGAA2EBgANhAYADYAGAA2ABgANg + AYADXgGAA1cBgANPAYADNwFVEAABcgE+ASwBxwG/AU0BFQH/AdIBcAFBAf8B3AGRAXAB/wHfAZwBeQH/ + AeABmwF6Af8B4gGdAXoB/wHjAZ8BewH/AeMBnwF8Af8B4wGgAXwB/wHjAZ8BfAH/AeMBnwF8Af8B4wGf + AXwB/wHjAZ8BfAH/AeMBnwF8Af8B4wGfAXwB/wHjAZ8BfAH/AeMBnwF8Af8B4wGgAXwB/wHjAZ8BewH/ + AeMBnwF7Af8B4gGcAXoB/wHgAZsBegH/Ad4BnAF5Af8B3AGRAXAB/wHSAXABQQH/AbsBSgEUAf8BZwFD + ATYBqSQAA00BdANPAeYDOAH/A0oB/wN9Af8DlwH/A6wB/wO/Af8DxgH/A8cB/wPBAf8DsQH/A5oB/wOD + Af8DSgH/AzQB/wNJAewDWAF7OAACRgFQAXQCHgFnAeYCAAFQAf8CBAFyAf8CSwGaAf8CbwGuAf8CjAG+ + Af8CpgHNAf8CsAHTAf8CsQHUAf8CqQHPAf8CkgHCAf8CcgGwAf8CUgGeAf8CBAFyAf8CAAFIAf8CGAFg + AewCUwFaAXskAAMMAQ8DPQFkA0sBgANNAYADTQGAA00BgANOAYADTgGAA04BgANOAYADTgGAA04BgANO + AYADTgGAA04BgANOAYADTgGAA04BgANOAYADTgGAA04BgANOAYADTQGAA00BgANNAYADSgF/AzkBWwMG + AQgQAAEXARYBFQEdAXIBPgEsAccBngE7AREB/wGsAUIBEgH/Aa0BQwETAf8BsAFDARMB/wGzAUQBEwH/ + AbYBRAETAf8BuAFFARMB/wG5AUUBEwH/AbkBRQETAf8BuQFFARMB/wG5AUUBEwH/AbkBRQETAf8BuQFF + ARMB/wG5AUUBEwH/AbkBRQETAf8BuQFFARMB/wG5AUUBEwH/AbgBRQETAf8BtgFEARMB/wGzAUQBEwH/ + AbABQwETAf8BrQFDARMB/wGsAUIBEgH/AZoBOgERAf4BbAFBATIBtQENAgwBECgAAwoBDQNbAZADSQHe + AzwB/wM5Af8DQAH/A04B/wNiAf8DZAH/A1AB/wNAAf8DOgH/AzoB/wNPAeADYQGWAw4BEkAAAwoBDQJQ + AWABkAIcAV8B3gIAAVkB/wIAAVMB/wIAAWIB/wIJAXUB/wIlAYYB/wIoAYgB/wIMAXYB/wIAAWEB/wIA + AVQB/wIAAVQB/wIjAWMB4AJXAWUBlgMOARIwAAMSARcDGAEgAxgBIAMYASADGAEgAxgBIAMYASADGAEg + AxgBIAMYASADGAEgAxgBIAMYASADGAEgAxgBIAMYASADGAEgAxgBIAMYASADGAEgAxgBIAMYASADGAEg + Aw4BEyAAASMBIAEfAS0BMAErASgBPwEwASsBKQE/ATABKwEpAT8BMQErASkBPwExASsBKQE/ATEBKwEp + AT8BMQErASkBPwExASsBKQE/ATEBKwEpAT8BMQErASkBPwExASsBKQE/ATEBKwEpAT8BMQErASkBPwEx + ASsBKQE/ATEBKwEpAT8BMQErASkBPwExASsBKQE/ATEBKwEpAT8BMQErASkBPwEwASsBKQE/ATABKwEp + AT8BMAErASgBPwEeARwBGwEmPAADMAFAA1UBigNYAb4DTAHsA0EB8gNAAfIDTQHuA1wBwANVAY0DNQFE + WAACLgEwAUACSgFaAYoCPAFlAb4CGgFkAewCCgFZAfICCgFZAfICGgFkAe4CQQFpAcACSgFZAY0CMwE1 + AUT/AC0AAUIBTQE+BwABPgMAASgDAAGAAwABQAMAAQEBAAEBBgABBBYAA///AP8AAwAB/wHgAQMC/wHg + AQMB/wHAAgABAwHAAgABAwH/AgAC/wIAAf8BgAIAAQMBgAIAAQMB/gIAAX8B/gIAAX8BgAIAAQEBgAIA + AQEB+AIAAR8B+AIAAR8BgAIAAQEBgAIAAQEB8AIAAQ8B8AIAAQ8BgAIAAQEBgAIAAQEB8AIAAQcB8AIA + AQcBgAIAAQEBgAIAAQEB4AIAAQcB4AIAAQcBgAIAAQEBgAIAAQEBwAIAAQMBwAIAAQMBgAIAAQEBgAIA + AQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIAAQEBgAIA + AQEBgAIAAQEBgAIAAQEIAAGAAgABAQGAAgABAQgAAYACAAEBAYACAAEBCAABgAIAAQEBgAIAAQEIAAGA + AgABAQGAAgABAQgAAYACAAEBAYACAAEBCAABgAIAAQEBgAIAAQEIAAGAAgABAQGAAgABAQgAAYACAAEB + AYACAAEBCAABgAIAAQEBgAIAAQEIAAGAAgABAQGAAgABAQGAAgABAQGAAgABAQGAAgABAQGAAgABAQGA + AgABAQGAAgABAQGAAgABAQGAAgABAQHAAgABAQHAAgABAQGAAgABAQGAAgABAQHAAgABAwHAAgABAwGA + AgABAQGAAgABAQHgAgABBwHgAgABBwGAAgABAQGAAgABAQHgAgABBwHgAgABBwGAAgABAQGAAgABAQHw + AgABDwHwAgABDwGAAgABAQGAAgABAQH4AgABHwH4AgABHwHAAgABAwHAAgABAwH+AgABfwH+AgABfwHA + AgABAwHAAgABAwH/AgAC/wIAAf8B8AIAAQ8B8AIAAQ8B/wHgAQcC/wHgAQcJ/ws= + + + \ No newline at end of file diff --git a/FSFlightLogger/Program.cs b/FSFlightLogger/Program.cs new file mode 100644 index 0000000..4b76b3c --- /dev/null +++ b/FSFlightLogger/Program.cs @@ -0,0 +1,39 @@ +using System; +using System.Windows.Forms; +using SimConnect; +using SimConnect.Concrete; +using SimpleInjector; +using SimpleInjector.Diagnostics; + +namespace FSFlightLogger +{ + public static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + public static void Main() + { + var container = CreateContainer(); + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(container.GetInstance()); + } + + + public static Container CreateContainer() + { + var container = new Container(); + + // Since the MainForm is registered as well, do not call Verify on the container as it would create an instance + container.Options.EnableAutoVerification = false; + + container.Register(); + container.Register(); + + return container; + } + } +} diff --git a/FSFlightLogger/Properties/AssemblyInfo.cs b/FSFlightLogger/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d2b856f --- /dev/null +++ b/FSFlightLogger/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FSFlightLogger")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FSFlightLogger")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5871fa9b-88a7-4f98-8755-2fc48d4dbf44")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/FSFlightLogger/Properties/Resources.Designer.cs b/FSFlightLogger/Properties/Resources.Designer.cs new file mode 100644 index 0000000..680bb11 --- /dev/null +++ b/FSFlightLogger/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace FSFlightLogger.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FSFlightLogger.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/FSFlightLogger/Properties/Resources.resx b/FSFlightLogger/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/FSFlightLogger/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/FSFlightLogger/Properties/Settings.Designer.cs b/FSFlightLogger/Properties/Settings.Designer.cs new file mode 100644 index 0000000..338a6d3 --- /dev/null +++ b/FSFlightLogger/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace FSFlightLogger.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/FSFlightLogger/Properties/Settings.settings b/FSFlightLogger/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/FSFlightLogger/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/FSFlightLogger/Resources/Images/FSXConnected.png b/FSFlightLogger/Resources/Images/FSXConnected.png new file mode 100644 index 0000000000000000000000000000000000000000..d67a18e6ef53cc6bd6da0e050ade4e9840bdaae1 GIT binary patch literal 1852 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 RLmU7A002ovPDHLkV1g0y$65dY literal 0 HcmV?d00001 diff --git a/FSFlightLogger/Resources/Images/P3DDisconnected.png b/FSFlightLogger/Resources/Images/P3DDisconnected.png new file mode 100644 index 0000000000000000000000000000000000000000..bdff778abb5954c2df49098c92039a75473efcdc GIT binary patch literal 2237 zcmaJ@X;@Qd8jT{03P=HE5hVueL;{2a2oRIVlCVeu8W2#{klZYin;ViF5*E?1q7rDO zvIv7(LDN#KD@Bcz0XM)X)JhZ-D6IlMRS{Ge85WrvD|UX2bDw+fw>;;Z_x;}Y{P+qs zM}}G~aa@8zp)A6~=xk&}8$L4=4!$FP!U;`Xp zR>m!WfA|4eJ z=fm=3NrM5QC@fnBL}y3FaI@37{yfY^8k(XaBLW}*bI>YKERmB{RLpx_GBP)8<1y&> z5ICKR`7^0_)@F1tBm>Ys9$xNTPXYl=B6$$JeMlsN8=B}zAmTldm*h_HCi{@cL|^oW z2ZKbD@%UsmJ>)|yWJSdYVOUDW-0;Xb+O#eIrDE%lak$)%?QZT%VBgGRuJPk*h2eMfI9}0pW z(Q=p#{FU$j6qd(iNdY_?kV6U?7ik>d#SltL4weBN7?Q<6ka)g|n}rYz$%T*<9UM(U zW8*{;9;B47e~)Le$l($>%#m<`a5@!(D0qlOJaUMyA3cx};^XJ(g;bs4>qiPA`1ue5 z=>#t?hBuu!&!t0L1qevsc`olSF6on8gB3t25}6LjM413DL)C}- zQ>n+ZJ_D!FmqDu}hQ2kV=t@eLyz@>|jxsZ`t2Eh3Fx;tqJJUIu+gBeO6&2;?_@(91 zW5-%owJrW7KbMvw8O-`yhzoKJ;8yg!lrtH~E^VpLu&RGTl34<WA5elZwy@yQ8RY5nf)e_$;u$;(Ow zz^(q&Fb9lyQOm`jvZ{<u9B|z7Qgy{$waXZ18(^X5-j3m-h}-U`ninq&_}2tUp{P_h zX)zkjxb7V`_C=m+LrR;P-xA9G`vTix=1|^>+y|9%$K%WPwcp)4g0&MmPug!zWp-ES zbCy(JxF-E>fNJfm>qy@5$g#3=Q{e9_2jg~Sm+i))wmw*&T7DR~sBW)|*jjt~wDHil z+f0g;ul>m6AX3S4?Uayut0y`3@}3v1zPp+iZ1vr?qCj6=Q^P)`xwgNgB>Kv)I@-g; z2TOy}T4d=9t(`v~8+*S09JtceOP`9Wudk0!NVxt4az1gP?!sPldDhhR=(TfUX)tWd zVzC%yrJ=;i8{-GN!Zhr#4GtW%Q~Hi6H(SeUt=6HxzrUxaCqPCm(8n(6?d|R9?G2Xy z;BaZQul1SEwKkU{$T3a$-u)rA>{09U?p@Uyjq%8;RjOCdmyS<N6l}raa6Nr}w%U>s zo3+-9;k<hVLqh;(?auOjN1u4#S#Ehmqj?&ilvE}d_G1>onZI6bZ#R3~eTKv5hZIMz zxvym^j#iv%`EsQ*rljLQul-EP-v(TcMiw$Fihfd_wOh72H~-zcJZ4l>2~Kv7@!2$a zRA}{39DB_DERWv~QF_`00)bE{Z29fCr!}Q*pR2#A^0iq=d+KmK=dQoYVf8)D(+K;S zG^>%35lkDAZQ2*Dn`i|06FfZws*<fy;-t8^!)9mCp1nOx>3)=*^paACUFV9!{nDdy zSQCCeXj|s<qLuiW+)aA9l~=?O=gi-W@J<a)O-(!?rdxb5ZO4vHxg7&<-@YZ#XlD+_ z#nLF0qDJLLMrqH2M62BM=0;{Ex6hDLZkFMk&7$jk&&^sa+Er0eaW{X@z%onA8;8Xi zp>2v=wr?hf$3YM*Q!=vFvJWO)qJ{jUuQee_BEB(;A1y4jTd`vA7XD4rY-hIvW4N=^ zGDch>iiwW?BJ|)t*XT~c7U#@$c6N3dDit=Jd)~gawH4k}Z{BuyaByvM)0B-?+zA92 zul!C|FW4CFxGC_J&l*ZazE+#!ZB)1Y)#`+V1l7M^E30$!@>VMp3hWV`W$kRuiz(K< z?R)w%l2RKQ8g{m+VPm&Hiqt<#V^c3C@SEGpaUpK~!2#1%&2Kahq~P7z-H{s<Q^xw8 znv;nKCp|j8_lIzL*P7)A4n?ltU3q+1KXAvSsY*^ZnX#z76;K@E@$ArStZ9=S8Dq4& zxywR!bJ{cGSk4V&wwpOB&vU4C!)!A4`aW=V6Ms?4jcZxDpQi(DPCD`9UYL$S@t4~z zh5|&WnHm}8K>i$VwV%oBnFN-mdhxZGsg%dn%*;X1wQ#-b=?oisF-~`WjK;Kjll1`n z`kp;VroZ*;4-PChEgLLEC8C(6Hq#sDTv|}~mtPGWxqNfXaG?liMAFX$rSAC;Gk>a& literal 0 HcmV?d00001 diff --git a/FSFlightLogger/Resources/Images/Recording.png b/FSFlightLogger/Resources/Images/Recording.png new file mode 100644 index 0000000000000000000000000000000000000000..0d75d2c36b2f838ae1487f99dc81aa0fa0900dc1 GIT binary patch literal 3425 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 AlmGw# literal 0 HcmV?d00001 diff --git a/FSFlightLogger/packages.config b/FSFlightLogger/packages.config new file mode 100644 index 0000000..bcba459 --- /dev/null +++ b/FSFlightLogger/packages.config @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Microsoft.Bcl.AsyncInterfaces" version="1.0.0" targetFramework="net472" /> + <package id="SimpleInjector" version="5.0.3" targetFramework="net472" /> + <package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net472" /> + <package id="System.Threading.Tasks.Extensions" version="4.5.2" targetFramework="net472" /> +</packages> \ No newline at end of file diff --git a/FSFlightLoggerCmd/App.config b/FSFlightLoggerCmd/App.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/FSFlightLoggerCmd/App.config @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8" ?> +<configuration> + <startup> + <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> + </startup> +</configuration> \ No newline at end of file diff --git a/FSFlightLoggerCmd/FSFlightLoggerCmd.csproj b/FSFlightLoggerCmd/FSFlightLoggerCmd.csproj new file mode 100644 index 0000000..1288739 --- /dev/null +++ b/FSFlightLoggerCmd/FSFlightLoggerCmd.csproj @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">x86</Platform> + <ProjectGuid>{767C7EAA-9230-4DEE-89FA-9699288C831B}</ProjectGuid> + <OutputType>Exe</OutputType> + <RootNamespace>FSFlightLoggerCmd</RootNamespace> + <AssemblyName>FSFlightLoggerCmd</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> + <Deterministic>true</Deterministic> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' "> + <OutputPath>bin\x64\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <Optimize>true</Optimize> + <DebugType>pdbonly</DebugType> + <PlatformTarget>x64</PlatformTarget> + <LangVersion>7.3</LangVersion> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x64\Debug\</OutputPath> + <PlatformTarget>x64</PlatformTarget> + <LangVersion>7.3</LangVersion> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> + <OutputPath>bin\x86\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <Optimize>true</Optimize> + <DebugType>pdbonly</DebugType> + <PlatformTarget>x86</PlatformTarget> + <LangVersion>7.3</LangVersion> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x86\Debug\</OutputPath> + <PlatformTarget>x86</PlatformTarget> + <LangVersion>7.3</LangVersion> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + </PropertyGroup> + <ItemGroup> + <Reference Include="CommandLine, Version=2.8.0.0, Culture=neutral, PublicKeyToken=5a870481e358d379, processorArchitecture=MSIL"> + <HintPath>..\packages\CommandLineParser.2.8.0\lib\net461\CommandLine.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Program.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="App.config" /> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\FlightLoggerLib\FlightLoggerLib.csproj"> + <Project>{D85BCC97-F653-4286-98D9-073A33A55857}</Project> + <Name>FlightLoggerLib</Name> + </ProjectReference> + <ProjectReference Include="..\SimConnect\SimConnect.csproj"> + <Project>{f160bb6a-7620-41e5-a99c-948c208875e4}</Project> + <Name>SimConnect</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> +</Project> \ No newline at end of file diff --git a/FSFlightLoggerCmd/Program.cs b/FSFlightLoggerCmd/Program.cs new file mode 100644 index 0000000..6c7f9cb --- /dev/null +++ b/FSFlightLoggerCmd/Program.cs @@ -0,0 +1,216 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using CommandLine; +using FlightLoggerLib; +using FlightLoggerLib.Concrete; +using SimConnect; +using SimConnect.Attribute; +using SimConnect.Concrete; +using SimConnect.Lib; + +namespace FSFlightLoggerCmd +{ + // TODO verb for converting CSV to KML + + + public class PositionData + { + [SimConnectVariable("PLANE LATITUDE", "degrees")] + public float Latitude; + + [SimConnectVariable("PLANE LONGITUDE", "degrees")] + public float Longitude; + + [SimConnectVariable("PLANE ALTITUDE", "feet")] + public float Altitude; + + [SimConnectVariable("AIRSPEED INDICATED", "knots")] + public float Airspeed; + } + + + public class Program + { + private enum OutputFormat + { + None, + CSV, + KML + }; + + + // ReSharper disable once ClassNeverInstantiated.Local - used by CommandLineParser + // ReSharper disable UnusedAutoPropertyAccessor.Local - used by CommandLineParser + private class Options + { + [Option('o', "outputPath", Required = false, HelpText = "Specifies the output path for the log files. Defaults to a 'Flight logs' folder on the desktop.")] + public string OutputPath { get; set; } + + [Option('i', "interval", Required = false, Default = 1, HelpText = "The minimum time, in seconds, between log entries.")] + public int IntervalTime { get; set; } + + [Option('d', "distance", Required = false, Default = 1, HelpText = "The minimum distance, in meters, between log entries.")] + public int IntervalDistance { get; set; } + + [Option('v', "verbose", Required = false, HelpText = "Enable verbose logging.")] + public bool Verbose { get; set; } + + [Option('f', "format", Required = false, Default = OutputFormat.CSV, HelpText = "The output format to use. Possible values: CSV, KML.")] + public OutputFormat OutputFormat { get; set; } + + [Option('s', "format2", Required = false, Default = OutputFormat.None, HelpText = "The secondary output format to use. Possible values: None, CSV, KML.")] + public OutputFormat OutputFormat2 { get; set; } + } + // ReSharper restore UnusedAutoPropertyAccessor.Local + + + public static void Main(string[] args) + { + var parser = new Parser(settings => + { + settings.CaseInsensitiveEnumValues = true; + }); + + parser.ParseArguments<Options>(args) + .WithParsed(Run); + } + + private static void Run(Options o) + { + var intervalTime = TimeSpan.FromSeconds(o.IntervalTime); + void VerboseLog(string message) + { + if (o.Verbose) + Console.WriteLine(message); + } + + + var factory = new SimConnectClientFactory(); + var client = TryConnect(factory).Result; + + var outputPath = o.OutputPath; + if (string.IsNullOrEmpty(outputPath)) + outputPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "Flight logs"); + + + Directory.CreateDirectory(outputPath); + var loggers = new List<IFlightLogger>(); + AddLogger(loggers, o.OutputFormat, outputPath); + + if (o.OutputFormat2 != o.OutputFormat) + AddLogger(loggers, o.OutputFormat2, outputPath); + + + var lastTime = DateTime.MinValue; + var lastData = new PositionData + { + Latitude = 0, + Longitude = 0, + Altitude = 0, + Airspeed = 0 + }; + + + client.AddDefinition<PositionData>(data => + { + // TODO take vertical position into account when going straight up or down (not a common use case, but still) + var distanceMeters = LatLon.DistanceBetweenInMeters(lastData.Latitude, lastData.Longitude, data.Latitude, data.Longitude); + if (distanceMeters < o.IntervalDistance) + { + if (data.Airspeed < 0.1) + data.Airspeed = 0; + + // Make an exception if we were last moving and have now stopped, so the 0 velocity record is logged as well + if (data.Airspeed > 0 || lastData.Airspeed == 0) + return; + } + + var now = DateTime.Now; + var time = now - lastTime; + if (time < intervalTime) + return; + + VerboseLog("Logging position, elapsed time: " + time.TotalSeconds + ", distance: " + distanceMeters); + + lastTime = now; + lastData = data; + + // ReSharper disable once AccessToDisposedClosure - covered by disposing the client first + loggers.ForEach(logger => + logger.LogPosition(now, new FlightPosition + { + Latitude = data.Latitude, + Longitude = data.Longitude, + Altitude = data.Altitude, + Airspeed = data.Airspeed + })); + }); + + + var stopEvent = new ManualResetEventSlim(false); + Console.CancelKeyPress += (sender, args) => + { + stopEvent.Set(); + args.Cancel = true; + }; + + Console.WriteLine("Flight log active, press Ctrl-C to stop"); + Console.WriteLine("Output path: " + outputPath); + stopEvent.Wait(Timeout.Infinite); + + Console.WriteLine("Closing..."); + client.Dispose(); + loggers.ForEach(logger => logger.Dispose()); + + if (!Debugger.IsAttached) + return; + + Console.WriteLine("Press any Enter key to continue"); + Console.ReadLine(); + } + + + private static void AddLogger(ICollection<IFlightLogger> loggers, OutputFormat outputFormat, string outputPath) + { + switch (outputFormat) + { + case OutputFormat.CSV: + loggers.Add(new CSVFlightLogger(outputPath)); + break; + + case OutputFormat.KML: + loggers.Add(new KMLFlightLogger(outputPath, TimeSpan.FromSeconds(5))); + break; + + case OutputFormat.None: + break; + + default: + throw new ArgumentOutOfRangeException(); + } + } + + + private static async Task<ISimConnectClient> TryConnect(ISimConnectClientFactory factory) + { + while (true) + { + Console.WriteLine("Attempting to connect to SimConnect..."); + + var client = await factory.TryConnect("FS Flight Logger"); + if (client != null) + { + Console.WriteLine("Success!"); + return client; + } + + Console.WriteLine("Failed to connect, retrying in 5 seconds"); + await Task.Delay(TimeSpan.FromSeconds(5)); + } + } + } +} diff --git a/FSFlightLoggerCmd/Properties/AssemblyInfo.cs b/FSFlightLoggerCmd/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a4c71de --- /dev/null +++ b/FSFlightLoggerCmd/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FSFlightLoggerCmd")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FSFlightLoggerCmd")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("767c7eaa-9230-4dee-89fa-9699288c831b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/FSFlightLoggerCmd/packages.config b/FSFlightLoggerCmd/packages.config new file mode 100644 index 0000000..3eb2507 --- /dev/null +++ b/FSFlightLoggerCmd/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="CommandLineParser" version="2.8.0" targetFramework="net472" /> +</packages> \ No newline at end of file diff --git a/FlightLoggerLib/Concrete/CSVFlightLogger.cs b/FlightLoggerLib/Concrete/CSVFlightLogger.cs new file mode 100644 index 0000000..f564c3c --- /dev/null +++ b/FlightLoggerLib/Concrete/CSVFlightLogger.cs @@ -0,0 +1,71 @@ +using CsvHelper; +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using CsvHelper.Configuration; +using CsvHelper.Configuration.Attributes; + +namespace FlightLoggerLib.Concrete +{ + public class CSVFlightLogger : IFlightLogger + { + private readonly CsvWriter output; + + + public CSVFlightLogger(string path) + { + var filename = Path.Combine(path, DateTime.Now.ToString("yyyy-MM-dd HH.mm.ss") + ".csv"); + var header = !File.Exists(filename); + + output = new CsvWriter(new StreamWriter(filename, true), new CsvConfiguration(CultureInfo.CurrentCulture) + { + SanitizeForInjection = false, + HasHeaderRecord = header + }); + } + + + public void Dispose() + { + output?.Dispose(); + } + + + public async Task LogPosition(DateTime eventTime, FlightPosition position) + { + var record = new OutputRecord + { + Time = eventTime, + Latitude = position.Latitude, + Longitude = position.Longitude, + Altitude = position.Altitude, + Airspeed = position.Airspeed + }; + + await output.WriteRecordsAsync(Enumerable.Repeat(record, 1)); + await output.FlushAsync(); + } + + + + protected class OutputRecord + { + [Index(0)] + public DateTime Time { get; set; } + + [Index(1)] + public float Latitude { get; set; } + + [Index(2)] + public float Longitude { get; set; } + + [Index(3)] + public float Altitude { get; set; } + + [Index(4)] + public float Airspeed { get; set; } + } + } +} diff --git a/FlightLoggerLib/Concrete/KMLFlightLogger.cs b/FlightLoggerLib/Concrete/KMLFlightLogger.cs new file mode 100644 index 0000000..483bdc1 --- /dev/null +++ b/FlightLoggerLib/Concrete/KMLFlightLogger.cs @@ -0,0 +1,208 @@ +using System; +using System.IO; +using System.Linq.Expressions; +using System.Threading.Tasks; +using SharpKml.Base; +using SharpKml.Dom; + +namespace FlightLoggerLib.Concrete +{ + public class KMLFlightLogger : IFlightLogger + { + private readonly string filename; + private readonly System.TimeSpan flushInterval; + private readonly Document output; + private readonly Folder rootFolder; + private readonly LineString positionPath; + + private DateTime lastFlush = DateTime.MinValue; + private Vector lastPosition; + private float lastSpeed; + private DateTime lastPointDate = DateTime.MinValue; + + + public KMLFlightLogger(string path, System.TimeSpan flushInterval) + { + var dateString = DateTime.Now.ToString("yyyy-MM-dd HH.mm.ss"); + filename = Path.Combine(path, dateString + ".kml"); + this.flushInterval = flushInterval; + + + // Create folder + rootFolder = new Folder + { + Name = dateString, + Open = true + }; + + + // Create flight path line and placemark + positionPath = new LineString + { + Tessellate = false, + AltitudeMode = AltitudeMode.Absolute, + Coordinates = new CoordinateCollection() + }; + + var positionPlacemark = new Placemark + { + Name = "Flight path", + StyleUrl = new Uri("#flightpath", UriKind.Relative), + Geometry = positionPath + }; + + rootFolder.AddFeature(positionPlacemark); + + output = new Document(); + + AddFlightPathStyleMap(); + + var paddleHotspot = new Hotspot { X = 31, XUnits = Unit.Pixel, Y = 1, YUnits = Unit.Pixel }; + AddIconStyleMap("start", 1.1, "http://maps.google.com/mapfiles/kml/paddle/grn-circle.png", "http://maps.google.com/mapfiles/kml/paddle/grn-circle-lv.png", paddleHotspot); + AddIconStyleMap("end", 1.1, "http://maps.google.com/mapfiles/kml/paddle/red-square.png", "http://maps.google.com/mapfiles/kml/paddle/red-square-lv.png", paddleHotspot); + AddIconStyleMap("fullstop", 1.1, "http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png", null, paddleHotspot); + + output.AddFeature(rootFolder); + } + + public void Dispose() + { + if (lastPosition != null) + AddPoint(lastPosition, "End", "end"); + + Flush().Wait(); + } + + + protected async Task AutoFlush() + { + var now = DateTime.Now; + var diff = now - lastFlush; + + if (diff < flushInterval) + return; + + await Flush(); + lastFlush = now; + } + + protected async Task Flush() + { + var serializer = new Serializer(); + serializer.Serialize(output); + + using (var writer = new StreamWriter(filename)) + { + await writer.WriteAsync(serializer.Xml); + } + } + + + protected void AddFlightPathStyleMap() + { + var styleMap = new StyleMapCollection { Id = "flightpath" }; + styleMap.Add(new Pair { State = StyleState.Normal, StyleUrl = new Uri("flightpath-normal", UriKind.Relative) }); + styleMap.Add(new Pair { State = StyleState.Highlight, StyleUrl = new Uri("flightpath-normal", UriKind.Relative) }); + + var styleNormal = new Style + { + Id = "flightpath-normal", + Line = new LineStyle + { + Color = new Color32(255, 10, 10, 138), + Width = 5 + } + }; + + output.AddStyle(styleMap); + output.AddStyle(styleNormal); + } + + + protected void AddIconStyleMap(string id, double scale, string iconUrl, string listUrl, Hotspot iconHotspot = null) + { + var styleMap = new StyleMapCollection { Id = id }; + styleMap.Add(new Pair { State = StyleState.Normal, StyleUrl = new Uri(id + "-normal", UriKind.Relative) }); + styleMap.Add(new Pair { State = StyleState.Highlight, StyleUrl = new Uri(id + "-normal", UriKind.Relative) }); + + var styleNormal = new Style + { + Id = id + "-normal", + Icon = new IconStyle + { + Scale = scale, + Icon = new IconStyle.IconLink(new Uri(iconUrl, UriKind.Absolute)), + Hotspot = iconHotspot + }, + }; + + if (!string.IsNullOrEmpty(listUrl)) + { + styleNormal.List = new ListStyle(); + styleNormal.List.AddItemIcon(new ItemIcon + { + Href = new Uri(listUrl, UriKind.Absolute) + }); + } + + + output.AddStyle(styleMap); + output.AddStyle(styleNormal); + } + + + protected string GetPointDate() + { + var now = DateTime.Now; + + // If the date hasn't changed since the last point label, just return the time + if (now.Date == lastPointDate) + return now.ToString("T"); + + lastPointDate = now.Date; + return now.ToString("F"); + } + + + protected void AddPoint(Vector coordinate, string label, string styleMapId, bool includeTimestamp = true) + { + var point = new Point { Coordinate = coordinate, AltitudeMode = AltitudeMode.Absolute }; + + var placemark = new Placemark + { + Name = includeTimestamp ? $"{label} ({GetPointDate()})" : label, + StyleUrl = new Uri("#" + styleMapId, UriKind.Relative), + Geometry = point, + Visibility = true + }; + + rootFolder.AddFeature(placemark); + } + + + private const float MetersPerFoot = 0.3048f; + + + public async Task LogPosition(DateTime eventTime, FlightPosition position) + { + var altitudeMeters = position.Altitude * MetersPerFoot; + var coordinate = new Vector(position.Latitude, position.Longitude, altitudeMeters); + + if (lastPosition == null) + AddPoint(coordinate, "Start", "start"); + + if (lastSpeed > 0 && position.Airspeed == 0) + AddPoint(coordinate, "Full stop", "fullstop"); + + lastPosition = coordinate; + lastSpeed = position.Airspeed; + + positionPath.Coordinates.Add(coordinate); + + await AutoFlush(); + } + + + // TODO log events, engine stop etc. + } +} diff --git a/FlightLoggerLib/FlightLoggerLib.csproj b/FlightLoggerLib/FlightLoggerLib.csproj new file mode 100644 index 0000000..74a5da7 --- /dev/null +++ b/FlightLoggerLib/FlightLoggerLib.csproj @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{D85BCC97-F653-4286-98D9-073A33A55857}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>FlightLoggerLib</RootNamespace> + <AssemblyName>FlightLoggerLib</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <Deterministic>true</Deterministic> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' "> + <OutputPath>bin\x64\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <Optimize>true</Optimize> + <DebugType>pdbonly</DebugType> + <PlatformTarget>x64</PlatformTarget> + <LangVersion>7.3</LangVersion> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x64\Debug\</OutputPath> + <PlatformTarget>x64</PlatformTarget> + <LangVersion>7.3</LangVersion> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> + <OutputPath>bin\x86\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <Optimize>true</Optimize> + <DebugType>pdbonly</DebugType> + <PlatformTarget>x86</PlatformTarget> + <LangVersion>7.3</LangVersion> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x86\Debug\</OutputPath> + <PlatformTarget>x86</PlatformTarget> + <LangVersion>7.3</LangVersion> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + </PropertyGroup> + <ItemGroup> + <Reference Include="CsvHelper, Version=15.0.0.0, Culture=neutral, PublicKeyToken=8c4959082be5c823, processorArchitecture=MSIL"> + <HintPath>..\packages\CsvHelper.15.0.5\lib\net47\CsvHelper.dll</HintPath> + </Reference> + <Reference Include="Ionic.Zip.Reduced, Version=1.9.1.8, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c, processorArchitecture=MSIL"> + <HintPath>..\packages\DotNetZip.Reduced.1.9.1.8\lib\net20\Ionic.Zip.Reduced.dll</HintPath> + </Reference> + <Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath> + </Reference> + <Reference Include="SharpKml.Core, Version=5.1.3.0, Culture=neutral, PublicKeyToken=9bb853c026a5c0ac, processorArchitecture=MSIL"> + <HintPath>..\packages\SharpKml.Core.5.1.3\lib\net45\SharpKml.Core.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.IO.Compression" /> + <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> + <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath> + </Reference> + <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> + <HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath> + </Reference> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Concrete\KMLFlightLogger.cs" /> + <Compile Include="Concrete\CSVFlightLogger.cs" /> + <Compile Include="IFlightLogger.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="app.config" /> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup /> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> +</Project> \ No newline at end of file diff --git a/FlightLoggerLib/IFlightLogger.cs b/FlightLoggerLib/IFlightLogger.cs new file mode 100644 index 0000000..9ad42c3 --- /dev/null +++ b/FlightLoggerLib/IFlightLogger.cs @@ -0,0 +1,21 @@ +using System; +using System.Threading.Tasks; + +namespace FlightLoggerLib +{ + public class FlightPosition + { + public float Latitude { get; set; } + public float Longitude { get; set; } + public float Altitude { get; set; } + public float Airspeed { get; set; } + } + + + + public interface IFlightLogger : IDisposable + { + Task LogPosition(DateTime eventTime, FlightPosition position); + //void LogEvent + } +} diff --git a/FlightLoggerLib/Properties/AssemblyInfo.cs b/FlightLoggerLib/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8acdc0d --- /dev/null +++ b/FlightLoggerLib/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("FlightLoggerLib")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FlightLoggerLib")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d85bcc97-f653-4286-98d9-073a33a55857")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/FlightLoggerLib/app.config b/FlightLoggerLib/app.config new file mode 100644 index 0000000..d0d658a --- /dev/null +++ b/FlightLoggerLib/app.config @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<configuration> + <runtime> + <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> + <dependentAssembly> + <assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" /> + </dependentAssembly> + <dependentAssembly> + <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> + <bindingRedirect oldVersion="0.0.0.0-4.0.6.0" newVersion="4.0.6.0" /> + </dependentAssembly> + </assemblyBinding> + </runtime> +</configuration> \ No newline at end of file diff --git a/FlightLoggerLib/packages.config b/FlightLoggerLib/packages.config new file mode 100644 index 0000000..caf7147 --- /dev/null +++ b/FlightLoggerLib/packages.config @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="CsvHelper" version="15.0.5" targetFramework="net472" /> + <package id="DotNetZip.Reduced" version="1.9.1.8" targetFramework="net472" /> + <package id="Microsoft.Bcl.AsyncInterfaces" version="1.1.1" targetFramework="net472" /> + <package id="Microsoft.CSharp" version="4.7.0" targetFramework="net472" /> + <package id="SharpKml.Core" version="5.1.3" targetFramework="net472" /> + <package id="System.Runtime.CompilerServices.Unsafe" version="4.7.1" targetFramework="net472" /> + <package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net472" /> + <package id="System.Xml.XDocument" version="4.3.0" targetFramework="net472" /> +</packages> \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..406170d --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# FSFlightLogger + +Log flight paths and other information to CSV or Google Earth KML files. + +Works with SimConnect compatible simulators, including Microsoft Flight Simulator X and 2020 (not 2004 and earlier) and Lockheed Martin's Prepar3D. + +32-bits builds uses the SimConnect clients up to Flight Simulator X, which will work with Flight Simulator 2020. 64-bits builds can only work with the Flight Simulator 2020 SimConnect client and is not compatible with earlier Flight Simualtors. + + +This project is in it's infancy. A working command-line version is implemented as a proof of concept, a proper UI with more features is in development. +It also includes a (far from incomplete) C# wrapper for the native SimConnect DLL instead of using the default managed wrapper, as it allowed for easier switching between versions. \ No newline at end of file diff --git a/SimConnect/Attribute/SimConnectVariableAttribute.cs b/SimConnect/Attribute/SimConnectVariableAttribute.cs new file mode 100644 index 0000000..fa672b0 --- /dev/null +++ b/SimConnect/Attribute/SimConnectVariableAttribute.cs @@ -0,0 +1,60 @@ +namespace SimConnect.Attribute +{ + /// <summary> + /// Indicates the property should be registered as a SimConnect variable in the definition to receive updates. + /// </summary> + public class SimConnectVariableAttribute : System.Attribute + { + /// <summary> + /// The name of the SimConnect variable + /// </summary> + public string VariableName { get; } + + /// <summary> + /// The SimConnect data type. If null, the SimConnect data type will be determined from the .NET type. + /// </summary> + public SimConnectDataType? DataType { get; } + + /// <summary> + /// The requested units for the value. See the SimConnect documentation for available units + /// </summary> + public string UnitsName { get; } + + /// <summary> + /// For integer and floating point types, determines how much the value must change before an update is sent + /// </summary> + public float Epsilon { get; } + + + /// <summary> + /// Indicates the property should be registered as a SimConnect variable in the definition to receive updates. + /// </summary> + /// <param name="variableName">The name of the SimConnect variable</param> + /// <param name="dataType">The SimConnect data type. If null, the SimConnect data type will be determined from the .NET type.</param> + /// <param name="unitsName">The requested units for the value. See the SimConnect documentation for available units</param> + /// <param name="epsilon">For integer and floating point types, determines how much the value must change before an update is sent</param> + public SimConnectVariableAttribute(string variableName, SimConnectDataType dataType, string unitsName = "", float epsilon = 0) + { + VariableName = variableName; + DataType = dataType; + UnitsName = unitsName; + Epsilon = epsilon; + } + + + /// <summary> + /// Indicates the property should be registered as a SimConnect variable in the definition to receive updates. + /// </summary> + /// <param name="variableName">The name of the SimConnect variable</param> + /// <param name="unitsName">The requested units for the value. See the SimConnect documentation for available units</param> + /// <param name="epsilon">For integer and floating point types, determines how much the value must change before an update is sent</param> + public SimConnectVariableAttribute(string variableName, string unitsName = "", float epsilon = 0) + { + VariableName = variableName; + DataType = null; + UnitsName = unitsName; + Epsilon = epsilon; + } + + } +} diff --git a/SimConnect/Concrete/DefaultSimConnectClient.cs b/SimConnect/Concrete/DefaultSimConnectClient.cs new file mode 100644 index 0000000..671c6ed --- /dev/null +++ b/SimConnect/Concrete/DefaultSimConnectClient.cs @@ -0,0 +1,272 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Win32.SafeHandles; + +namespace SimConnect.Concrete +{ + /// <summary> + /// Default implementation of ISimConnectClient. Compatible with Flight Simulator 2020, X Stream Edition, X and Prepar3D. + /// Requires the following DLL files to be present: FSX2020-SimConnect.dll, FSX-SE-SimConnect.dll, FSX-SE-SimConnect.dll, FSX-SimConnect.dll. + /// These are renamed versions of the SimConnect.dll from the various flight simulator installations. + /// </summary> + public class DefaultSimConnectClient : ISimConnectClient, IDisposable + { + private readonly ISimConnectLibrary simConnectLibrary; + private SimConnectWorker worker; + + private uint nextDefinitionID = 1; + + + /// <summary> + /// Creates an instance of DefaultSimConnectClient. + /// </summary> + /// <param name="simConnectLibrary">The low-level SimConnect library interface to use</param> + public DefaultSimConnectClient(ISimConnectLibrary simConnectLibrary) + { + this.simConnectLibrary = simConnectLibrary; + } + + + /// <inheritdoc /> + public void Dispose() + { + worker?.Close().Wait(); + simConnectLibrary?.Dispose(); + } + + /// <summary> + /// Attempts to open a connection to the SimConnect server + /// </summary> + /// <param name="appName">The application name passed to the SimConnect server.</param> + /// <returns></returns> + public async Task<bool> TryOpen(string appName) + { + if (worker != null) + await worker.Close(); + + worker = new SimConnectWorker(simConnectLibrary, appName); + return await worker.Open(); + } + + + /// <inheritdoc /> + public void AttachObserver(ISimConnectClientObserver observer) + { + throw new NotImplementedException(); + } + + + /// <inheritdoc /> + public IDisposable AddDefinition<T>(SimConnectDataHandlerAction<T> onData) where T : class + { + if (worker == null) + throw new InvalidOperationException("TryOpen must be called first"); + + var definition = new SimConnectDefinition(typeof(T)); + void HandleData(Stream stream) + { + var data = definition.ParseData(stream); + onData((T)data); + } + + var definitionRegistration = new SimConnectDefinitionRegistration<T>(nextDefinitionID, definition, HandleData, worker); + nextDefinitionID++; + + return definitionRegistration; + } + + + private class SimConnectDefinitionRegistration<T> : IDisposable where T : class + { + private readonly uint definitionID; + private readonly SimConnectWorker worker; + + + public SimConnectDefinitionRegistration(uint definitionID, SimConnectDefinition definition, Action<Stream> onData, SimConnectWorker worker) + { + this.definitionID = definitionID; + this.worker = worker; + + worker.RegisterDefinition(definitionID, definition, onData); + } + + + public void Dispose() + { + worker.UnregisterDefinition(definitionID); + } + } + + + private class SimConnectWorker + { + private readonly ISimConnectLibrary simConnectLibrary; + private readonly string appName; + + private Task workerTask; + + private readonly AutoResetEvent workerPulse = new AutoResetEvent(false); + private readonly object workerLock = new object(); + private volatile bool closed; + private readonly Queue<Action<IntPtr>> workQueue = new Queue<Action<IntPtr>>(); + + private readonly TaskCompletionSource<bool> openResult = new TaskCompletionSource<bool>(); + private readonly ConcurrentDictionary<uint, Action<Stream>> definitionDataHandler = new ConcurrentDictionary<uint, Action<Stream>>(); + + + public SimConnectWorker(ISimConnectLibrary simConnectLibrary, string appName) + { + this.simConnectLibrary = simConnectLibrary; + this.appName = appName; + } + + + public async Task<bool> Open() + { + if (workerTask == null) + workerTask = Task.Run(RunInBackground); + + return await openResult.Task; + } + + + public async Task Close() + { + closed = true; + + workerPulse.Set(); + await workerTask; + } + + + public void RegisterDefinition(uint definitionID, SimConnectDefinition definition, Action<Stream> onData) + { + Enqueue(hSimConnect => + { + foreach (var variable in definition.Variables) + { + simConnectLibrary.SimConnect_AddToDataDefinition(hSimConnect, definitionID, variable.VariableName, variable.UnitsName, + variable.DataType, variable.Epsilon); + } + + definitionDataHandler.AddOrUpdate(definitionID, onData, (key, value) => onData); + simConnectLibrary.SimConnect_RequestDataOnSimObject(hSimConnect, definitionID, definitionID, 0, SimConnectPeriod.SimFrame, 1); + }); + } + + + public void UnregisterDefinition(uint definitionID) + { + Enqueue(hSimConnect => + { + definitionDataHandler.TryRemove(definitionID, out var unused); + simConnectLibrary.SimConnect_ClearDataDefinition(hSimConnect, definitionID); + }); + } + + + private void Enqueue(Action<IntPtr> work) + { + lock(workerLock) + { + workQueue.Enqueue(work); + } + + workerPulse.Set(); + } + + + private void RunInBackground() + { + var dataEvent = new EventWaitHandle(false, EventResetMode.AutoReset); + + var errorCode = simConnectLibrary.SimConnect_Open(out var simConnectHandle, appName, IntPtr.Zero, 0, dataEvent.SafeWaitHandle.DangerousGetHandle(), 0); + openResult.TrySetResult(errorCode == 0); + + if (errorCode != 0) + return; + + + while(!closed) + { + switch (WaitHandle.WaitAny(new WaitHandle[] { workerPulse, dataEvent })) + { + case 0: + HandleWorkQueue(simConnectHandle); + break; + + case 1: + HandleData(ref simConnectHandle); + break; + } + } + + if (simConnectHandle != IntPtr.Zero) + simConnectLibrary.SimConnect_Close(simConnectHandle); + } + + + private void HandleWorkQueue(IntPtr simConnectHandle) + { + while (!closed) + { + Action<IntPtr> work; + + lock(workerLock) + { + work = workQueue.Count > 0 ? workQueue.Dequeue() : null; + } + + if (work == null) + break; + + work(simConnectHandle); + } + } + + + private void HandleData(ref IntPtr simConnectHandle) + { + while (!closed && simConnectLibrary.SimConnect_GetNextDispatch(simConnectHandle, out var dataPtr, out var dataSize) == 0) + { + var recv = Marshal.PtrToStructure<SimConnectRecv>(dataPtr); + + // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault + switch ((SimConnectRecvID) recv.dwID) + { + case SimConnectRecvID.Exception: + var recvException = Marshal.PtrToStructure<SimConnectRecvException>(dataPtr); + if (recvException.dwException == 0) + break; + + break; + + case SimConnectRecvID.SimobjectData: + case SimConnectRecvID.SimobjectDataByType: + var recvSimobjectData = Marshal.PtrToStructure<SimConnectRecvSimobjectData>(dataPtr); + if (!definitionDataHandler.TryGetValue((uint)recvSimobjectData.dwDefineID, out var dataHandler)) + break; + + unsafe + { + var streamOffset = Marshal.OffsetOf<SimConnectRecvSimobjectData>("dwData").ToInt32(); + var stream = new UnmanagedMemoryStream((byte*)IntPtr.Add(dataPtr, streamOffset).ToPointer(), (long)dataSize - streamOffset); + dataHandler(stream); + } + break; + + case SimConnectRecvID.Quit: + simConnectHandle = IntPtr.Zero; + closed = true; + break; + } + } + } + } + } +} diff --git a/SimConnect/Concrete/SimConnectClientFactory.cs b/SimConnect/Concrete/SimConnectClientFactory.cs new file mode 100644 index 0000000..054cdea --- /dev/null +++ b/SimConnect/Concrete/SimConnectClientFactory.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading.Tasks; +using SimConnect.Unmanaged; + +namespace SimConnect.Concrete +{ + /// <summary> + /// Default implementation of ISimConnectClientFactory + /// </summary> + public class SimConnectClientFactory : ISimConnectClientFactory + { + /// <inheritdoc /> + public async Task<ISimConnectClient> TryConnect(string appName) + { + // FS 2020 SimConnect.dll is 64-bits, the others are 32-bits + if (Environment.Is64BitProcess) + return await TryDefaultClient(appName, "FS2020-SimConnect.dll"); + + // This order prevents a version mismatch, but perhaps an option to explicitly set the version might be nice as well + return await TryDefaultClient(appName, "FSX-SimConnect.dll") + ?? await TryDefaultClient(appName, "FSXSP2-SimConnect.dll") + ?? await TryDefaultClient(appName, "FSX-SE-SimConnect.dll"); + } + + + private static async Task<ISimConnectClient> TryDefaultClient(string appName, string libraryFilename) + { + var library = new SimConnectDLLLibrary(libraryFilename); + var client = new DefaultSimConnectClient(library); + + return await client.TryOpen(appName) ? client : null; + } + } +} diff --git a/SimConnect/Concrete/SimConnectDefinition.cs b/SimConnect/Concrete/SimConnectDefinition.cs new file mode 100644 index 0000000..b001a43 --- /dev/null +++ b/SimConnect/Concrete/SimConnectDefinition.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using SimConnect.Attribute; + +namespace SimConnect.Concrete +{ + /// <summary> + /// Defines a SimConnect variable. + /// </summary> + public struct SimConnectVariable + { + /// <summary> + /// The name of the SimConnect variable + /// </summary> + public string VariableName; + + /// <summary> + /// The SimConnect data type. If null, the SimConnect data type will be determined from the .NET type. + /// </summary> + public SimConnectDataType DataType; + + /// <summary> + /// The requested units for the value. See the SimConnect documentation for available units + /// </summary> + public string UnitsName; + + /// <summary> + /// For integer and floating point types, determines how much the value must change before an update is sent + /// </summary> + public float Epsilon; + }; + + + /// <summary> + /// Creates a SimConnect definition from properties annotated with the SimConnectVariable attribute. + /// </summary> + public class SimConnectDefinition + { + private readonly Type type; + + private delegate void SetterProc(object instance, BinaryReader data); + + private struct SimConnectVariableProperty + { + public SimConnectVariable Variable; + public SetterProc Setter; + } + + /// <summary> + /// Provides access to the parsed variables. + /// </summary> + public IEnumerable<SimConnectVariable> Variables => variables.Select(v => v.Variable); + + private readonly List<SimConnectVariableProperty> variables; + + + /// <summary> + /// Creates an instance of a SimConnectDefinition based on the provided class. + /// </summary> + /// <param name="type">The class type used for the definition</param> + public SimConnectDefinition(Type type) + { + if (!type.IsClass) + throw new InvalidOperationException($"{type.FullName} is not a class type"); + + this.type = type; + variables = ParseVariables(type); + + if (variables.Count == 0) + throw new InvalidOperationException($"At least one property of {type.FullName} should be annotated with the SimConnectVariable attribute"); + } + + + /// <summary> + /// Parses the SimConnect data stream to an object according to this definition. + /// </summary> + /// <param name="data">The SimConnect data stream</param> + /// <returns>An instance of the type for this definition</returns> + public object ParseData(Stream data) + { + var reader = new BinaryReader(data); + var instance = Activator.CreateInstance(type); + + foreach (var variable in variables) + variable.Setter(instance, reader); + + return instance; + } + + + private static List<SimConnectVariableProperty> ParseVariables(IReflect type) + { + var result = new List<SimConnectVariableProperty>(); + + foreach (var member in type.GetMembers(BindingFlags.Public | BindingFlags.Instance) + .Where(m => m.MemberType == MemberTypes.Field || m.MemberType == MemberTypes.Property)) + { + var variableAttribute = member.GetCustomAttribute<SimConnectVariableAttribute>(); + if (variableAttribute == null) + continue; + + + var dataType = variableAttribute.DataType; + var setter = GetSetter(member, ref dataType); + + if (!dataType.HasValue) + throw new InvalidOperationException($"DataType could not be determined for member {member.Name}"); + + result.Add(new SimConnectVariableProperty + { + Variable = new SimConnectVariable + { + VariableName = variableAttribute.VariableName, + DataType = dataType.Value, + UnitsName = variableAttribute.UnitsName, + Epsilon = variableAttribute.Epsilon + }, + Setter = setter + }); + } + + return result; + } + + + private readonly struct SimConnectTypeMapping + { + public readonly Type Type; + public readonly SimConnectDataType SimConnectDataType; + public readonly bool IsDefaultForTypeForType; + public readonly Func<BinaryReader, object> Converter; + + public SimConnectTypeMapping(Type type, SimConnectDataType simConnectDataType, bool isDefaultForType, Func<BinaryReader, object> converter) + { + Type = type; + SimConnectDataType = simConnectDataType; + IsDefaultForTypeForType = isDefaultForType; + Converter = converter; + } + } + + + private static readonly List<SimConnectTypeMapping> SimConnectTypeMappings = new List<SimConnectTypeMapping> + { + { new SimConnectTypeMapping(typeof(double), SimConnectDataType.Float64, true, reader => reader.ReadDouble()) }, + { new SimConnectTypeMapping(typeof(float), SimConnectDataType.Float32, true, reader => reader.ReadSingle()) }, + { new SimConnectTypeMapping(typeof(int), SimConnectDataType.Int32, true, reader => reader.ReadInt32()) }, + { new SimConnectTypeMapping(typeof(uint), SimConnectDataType.Int32, true, reader => reader.ReadUInt32()) }, + { new SimConnectTypeMapping(typeof(long), SimConnectDataType.Int64, true, reader => reader.ReadInt64()) }, + { new SimConnectTypeMapping(typeof(ulong), SimConnectDataType.Int64, true, reader => reader.ReadUInt64()) } + }; + + + private static SetterProc GetSetter(MemberInfo member, ref SimConnectDataType? dataType) + { + Type valueType; + Action<object, object> valueSetter; + + if (member.MemberType == MemberTypes.Field) + { + var fieldInfo = (FieldInfo)member; + valueType = fieldInfo.FieldType; + valueSetter = (instance, data) => fieldInfo.SetValue(instance, data); + + } + else + { + var propertyInfo = (PropertyInfo)member; + valueType = propertyInfo.PropertyType; + valueSetter = (instance, data) => propertyInfo.SetValue(instance, data); + } + + + foreach (var mapping in SimConnectTypeMappings.Where(mapping => mapping.Type == valueType)) + { + if (dataType.HasValue && mapping.SimConnectDataType != dataType) + continue; + + if (!dataType.HasValue && !mapping.IsDefaultForTypeForType) + continue; + + if (!dataType.HasValue) + dataType = mapping.SimConnectDataType; + + return (instance, reader) => + { + valueSetter(instance, mapping.Converter(reader)); + }; + } + + throw new InvalidOperationException($"No mapping exists for type {valueType.FullName} with SimConnect DataType {dataType} for member {member.Name}"); + } + } +} diff --git a/SimConnect/FS2020-SimConnect.dll b/SimConnect/FS2020-SimConnect.dll new file mode 100644 index 0000000000000000000000000000000000000000..87b29a2295cab6d1303ec957979ceadbb132e1f7 GIT binary patch literal 58368 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 literal 0 HcmV?d00001 diff --git a/SimConnect/FSX-SE-SimConnect.dll b/SimConnect/FSX-SE-SimConnect.dll new file mode 100644 index 0000000000000000000000000000000000000000..afa03e913593d186c93e4e9f79dca29b23b6bffe GIT binary patch literal 45056 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 literal 0 HcmV?d00001 diff --git a/SimConnect/FSX-SimConnect.dll b/SimConnect/FSX-SimConnect.dll new file mode 100644 index 0000000000000000000000000000000000000000..c2f1ebe39a294fb98f90cb4e8ae67524a8a93cec GIT binary patch literal 37888 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 literal 0 HcmV?d00001 diff --git a/SimConnect/FSXSP2-SimConnect.dll b/SimConnect/FSXSP2-SimConnect.dll new file mode 100644 index 0000000000000000000000000000000000000000..a2bf93ed0d5b788d3b07f3d56fae333d12d06138 GIT binary patch literal 45568 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 XU^R3ZHe)pX@9%#(@V^|0#)1C>)eDqJ literal 0 HcmV?d00001 diff --git a/SimConnect/ISimConnectClient.cs b/SimConnect/ISimConnectClient.cs new file mode 100644 index 0000000..a521632 --- /dev/null +++ b/SimConnect/ISimConnectClient.cs @@ -0,0 +1,46 @@ +using System; + +namespace SimConnect +{ + /// <summary> + /// Called when new data arrives from the SimConnect server. + /// </summary> + /// <param name="data">An instance of the data class as passed to AddDefinition containing the variable values</param> + /// <typeparam name="T">The data class as passed to AddDefinition</typeparam> + public delegate void SimConnectDataHandlerAction<in T>(T data) where T : class; + + + /// <summary> + /// Gets notified of changes to the SimConnect state. + /// </summary> + public interface ISimConnectClientObserver + { + /// <summary> + /// Gets called when the SimConnect connection is lost. The client will not receive further notifications, + /// a new connection should be attempted if desired. + /// </summary> + void OnQuit(); + } + + + /// <summary> + /// Provides access to the SimConnect library. + /// </summary> + public interface ISimConnectClient : IDisposable + { + /// <summary> + /// Attaches the specified observer to receive status notifications. + /// </summary> + /// <param name="observer">The observer to receive status notifications</param> + void AttachObserver(ISimConnectClientObserver observer); + + + /// <summary> + /// Registers a definition to receive updates from the SimConnect server. + /// </summary> + /// <param name="onData">A callback method which is called whenever a data update is received</param> + /// <typeparam name="T">A class defining the variables to monitor annotated using the SimConnectVariable attribute</typeparam> + /// <returns>An IDisposable which can be disposed to unregister the definition. Dispose is not required to be called when the client is disconnected, but will not throw an exception.</returns> + IDisposable AddDefinition<T>(SimConnectDataHandlerAction<T> onData) where T : class; + } +} diff --git a/SimConnect/ISimConnectClientFactory.cs b/SimConnect/ISimConnectClientFactory.cs new file mode 100644 index 0000000..e646dad --- /dev/null +++ b/SimConnect/ISimConnectClientFactory.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; + +namespace SimConnect +{ + /// <summary> + /// Provides a factory for creating a SimConnect client instance. + /// </summary> + public interface ISimConnectClientFactory + { + /// <summary> + /// Tries to connect to any of the compatible running SimConnect servers. + /// </summary> + /// <param name="appName">The application name passed to the SimConnect server.</param> + /// <returns>A client interface if succesful or nil if no connection could be made.</returns> + Task<ISimConnectClient> TryConnect(string appName); + } +} diff --git a/SimConnect/ISimConnectLibrary.cs b/SimConnect/ISimConnectLibrary.cs new file mode 100644 index 0000000..8b9dbd2 --- /dev/null +++ b/SimConnect/ISimConnectLibrary.cs @@ -0,0 +1,186 @@ +using System; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +namespace SimConnect +{ + /// <summary> + /// Corresponds to SIMCONNECT_DATAType. + /// </summary> + public enum SimConnectDataType + { + /// <summary> + /// invalid data Type + /// </summary> + Invalid = 0, + + /// <summary> + /// 32-bit integer number + /// </summary> + Int32, + + /// <summary> + /// 64-bit integer number + /// </summary> + Int64, + + /// <summary> + /// 32-bit floating-point number (Single) + /// </summary> + Float32, + + /// <summary> + /// 64-bit floating-point number (Double) + /// </summary> + Float64, + + /// <summary> + /// 8-byte String + /// </summary> + String8, + + /// <summary> + /// 32-byte String + /// </summary> + String32, + + /// <summary> + /// 64-byte String + /// </summary> + String64, + + /// <summary> + /// 128-byte String + /// </summary> + String128, + + /// <summary> + /// 256-byte String + /// </summary> + String256, + + /// <summary> + /// 260-byte String + /// </summary> + String260, + + /// <summary> + /// variable-length String + /// </summary> + StringV, + + /// <summary> + /// see SIMCONNECT_DATA_INITPOSITION + /// </summary> + InitPosition, + + /// <summary> + /// see SIMCONNECT_DATA_MARKERSTATE + /// </summary> + MarkerState, + + /// <summary> + /// see SIMCONNECT_DATA_WAYPOINT + /// </summary> + Waypoint, + + /// <summary> + /// see SIMCONNECT_DATA_LATLONALT + /// </summary> + LatLonAlt, + + /// <summary> + /// see SIMCONNECT_DATA_XYZ + /// </summary> + XYZ + } + + + // see SimConnect documentation + #pragma warning disable 1591 + public enum SimConnectPeriod + { + Never = 0, + Once, + VisualFrame, + SimFrame, + Second + }; + + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct SimConnectRecv + { + public uint dwSize; + public uint dwVersion; + public uint dwID; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct SimConnectRecvException + { + public SimConnectRecv Recv; + + public uint dwException; + public uint dwSendID; + public uint dwIndex; + } + + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct SimConnectRecvSimobjectData + { + public SimConnectRecv Recv; + + public uint dwRequestID; + public uint dwObjectID; + public uint dwDefineID; + public uint dwFlags; // SIMCONNECT_DATA_REQUEST_FLAG + public uint dwentrynumber; + // if multiple objects returned, this is number <entrynumber> out of <outof>. + public uint dwoutof; // note: starts with 1, not 0. + public uint dwDefineCount; // data count (number of datums, *not* byte count) + public uint dwData; // data begins here, dwDefineCount data items + } + + + /// <summary> + /// Provides a low-level interface to a compatible SimConnect.dll. + /// </summary> + public interface ISimConnectLibrary : IDisposable + { + uint SimConnect_Open(out IntPtr phSimConnect, string szName, IntPtr hwnd, uint userEventWin32, IntPtr hEventHandle, uint configIndex); + uint SimConnect_Close(IntPtr hSimConnect); + + uint SimConnect_AddToDataDefinition(IntPtr hSimConnect, uint defineID, string datumName, string unitsName, SimConnectDataType datumType = SimConnectDataType.Float64, float epsilon = 0, uint datumID = uint.MaxValue); + uint SimConnect_ClearDataDefinition(IntPtr hSimConnect, uint defineID); + + uint SimConnect_RequestDataOnSimObject(IntPtr hSimConnect, uint requestID, uint defineID, uint objectID, SimConnectPeriod period, uint flags, uint origin = 0, uint interval = 0, uint limit = 0); + uint SimConnect_GetNextDispatch(IntPtr hSimConnect, out IntPtr ppData, out uint pcbData); + } + + + + public enum SimConnectRecvID + { + Null = 0, + Exception, + Open, + Quit, + Event, + EventObjectAddRemove, + EventFilename, + EventFrame, + SimobjectData, + SimobjectDataByType, + WeatherObservation, + CloudState, + AssignedObjectID, + ReservedKey, + CustomAction, + SystemState, + ClientData + }; + #pragma warning restore 1591 +} diff --git a/SimConnect/Lib/LatLon.cs b/SimConnect/Lib/LatLon.cs new file mode 100644 index 0000000..7fc565d --- /dev/null +++ b/SimConnect/Lib/LatLon.cs @@ -0,0 +1,40 @@ +using System; + +namespace SimConnect.Lib +{ + /// <summary> + /// Provides helpers methods for latitude / longitude calculations. + /// </summary> + public static class LatLon + { + /// <summary> + /// Defines the approximate radius of the earth in kilometers. + /// </summary> + public const double EarthRadiusKm = 6378.137; + + + // Source: https://stackoverflow.com/questions/639695/how-to-convert-latitude-or-longitude-to-meters + /// <summary> + /// Calculates the distance between two coordinates. + /// </summary> + /// <param name="lat1">Latitude of point 1 in degrees</param> + /// <param name="lon1">Longitude of point 1 in degrees</param> + /// <param name="lat2">Latitude of point 2 in degrees</param> + /// <param name="lon2">Longitude of point 2 in degrees</param> + /// <returns></returns> + public static double DistanceBetweenInMeters(float lat1, float lon1, float lat2, float lon2) + { + var distanceLat = lat2 * Math.PI / 180 - lat1 * Math.PI / 180; + var distanceLon = lon2 * Math.PI / 180 - lon1 * Math.PI / 180; + + var a = Math.Sin(distanceLat / 2) * Math.Sin(distanceLat / 2) + + Math.Cos(lat1 * Math.PI / 180) * Math.Cos(lat2 * Math.PI / 180) * + Math.Sin(distanceLon / 2) * Math.Sin(distanceLon / 2); + + var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); + var distanceKm = EarthRadiusKm * c; + + return distanceKm * 1000; + } + } +} diff --git a/SimConnect/Lib/UnmanagedLibrary.cs b/SimConnect/Lib/UnmanagedLibrary.cs new file mode 100644 index 0000000..8493480 --- /dev/null +++ b/SimConnect/Lib/UnmanagedLibrary.cs @@ -0,0 +1,117 @@ +using System; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using Microsoft.Win32.SafeHandles; + +// Source: https://docs.microsoft.com/en-us/archive/blogs/jmstall/type-safe-managed-wrappers-for-kernel32getprocaddress +namespace SimConnect.Lib +{ + /// <summary> + /// Utility class to wrap an unmanaged DLL and be responsible for freeing it. + /// </summary> + /// <remarks> + /// This is a managed wrapper over the native LoadLibrary, GetProcAddress, and FreeLibrary calls. + /// </remarks> + public sealed class UnmanagedLibrary : IDisposable + { + /// <summary> + /// See http://msdn.microsoft.com/msdnmag/issues/05/10/Reliability/ for more about safe handles. + /// </summary> + [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] + public sealed class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid + { + private SafeLibraryHandle() : base(true) { } + + /// <inheritdoc /> + protected override bool ReleaseHandle() + { + return NativeMethods.FreeLibrary(handle); + } + } + + private static class NativeMethods + { + private const string SKernel = "kernel32"; + + [DllImport(SKernel, CharSet = CharSet.Auto, BestFitMapping = false, SetLastError = true)] + public static extern SafeLibraryHandle LoadLibrary(string fileName); + + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [DllImport(SKernel, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool FreeLibrary(IntPtr hModule); + + [DllImport(SKernel)] + public static extern IntPtr GetProcAddress(SafeLibraryHandle hModule, String procname); + } + + /// <summary> + /// Constructor to load a dll and be responible for freeing it. + /// </summary> + /// <param name="fileName">full path name of dll to load</param> + /// <exception cref="System.IO.FileNotFoundException">if fileName can't be found</exception> + /// <remarks>Throws exceptions on failure. Most common failure would be file-not-found, or + /// that the file is not a loadable image.</remarks> + public UnmanagedLibrary(string fileName) + { + libraryHandle = NativeMethods.LoadLibrary(fileName); + if (!libraryHandle.IsInvalid) + return; + + var hr = Marshal.GetHRForLastWin32Error(); + Marshal.ThrowExceptionForHR(hr); + } + + /// <summary> + /// Dynamically lookup a function in the dll via kernel32!GetProcAddress. + /// </summary> + /// <param name="functionName">raw name of the function in the export table.</param> + /// <returns>null if function is not found. Else a delegate to the unmanaged function. + /// </returns> + /// <remarks>GetProcAddress results are valid as long as the dll is not yet unloaded. This + /// is very very dangerous to use since you need to ensure that the dll is not unloaded + /// until after you're done with any objects implemented by the dll. For example, if you + /// get a delegate that then gets an IUnknown implemented by this dll, + /// you can not dispose this library until that IUnknown is collected. Else, you may free + /// the library and then the CLR may call release on that IUnknown and it will crash.</remarks> + public TDelegate GetUnmanagedFunction<TDelegate>(string functionName) where TDelegate : class + { + var p = NativeMethods.GetProcAddress(libraryHandle, functionName); + + // Failure is a common case, especially for adaptive code. + if (p == IntPtr.Zero) + return null; + + var function = Marshal.GetDelegateForFunctionPointer(p, typeof(TDelegate)); + + // Ideally, we'd just make the constraint on TDelegate be + // System.Delegate, but compiler error CS0702 (constrained can't be System.Delegate) + // prevents that. So we make the constraint system.object and do the cast from object-->TDelegate. + object o = function; + + return (TDelegate)o; + } + + /// <summary> + /// Call FreeLibrary on the unmanaged dll. All function pointers + /// handed out from this class become invalid after this. + /// </summary> + /// <remarks>This is very dangerous because it suddenly invalidate + /// everything retrieved from this dll. This includes any functions + /// handed out via GetProcAddress, and potentially any objects returned + /// from those functions (which may have an implemention in the + /// dll). + /// </remarks> + public void Dispose() + { + if (!libraryHandle.IsClosed) + { + libraryHandle.Close(); + } + } + + // Unmanaged resource. CLR will ensure SafeHandles get freed, without requiring a finalizer on this class. + private readonly SafeLibraryHandle libraryHandle; + } +} diff --git a/SimConnect/Properties/AssemblyInfo.cs b/SimConnect/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..281e444 --- /dev/null +++ b/SimConnect/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SimConnect")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SimConnect")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f160bb6a-7620-41e5-a99c-948c208875e4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SimConnect/SimConnect.csproj b/SimConnect/SimConnect.csproj new file mode 100644 index 0000000..84cdd03 --- /dev/null +++ b/SimConnect/SimConnect.csproj @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{F160BB6A-7620-41E5-A99C-948C208875E4}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>SimConnect</RootNamespace> + <AssemblyName>SimConnect</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <Deterministic>true</Deterministic> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' "> + <OutputPath>bin\x64\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <Optimize>true</Optimize> + <DebugType>pdbonly</DebugType> + <PlatformTarget>x64</PlatformTarget> + <LangVersion>7.3</LangVersion> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x64\Debug\</OutputPath> + <PlatformTarget>x64</PlatformTarget> + <LangVersion>7.3</LangVersion> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> + <OutputPath>bin\x86\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <Optimize>true</Optimize> + <DebugType>pdbonly</DebugType> + <PlatformTarget>x86</PlatformTarget> + <LangVersion>7.3</LangVersion> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x86\Debug\</OutputPath> + <PlatformTarget>x86</PlatformTarget> + <LangVersion>7.3</LangVersion> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + <Prefer32Bit>true</Prefer32Bit> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="Attribute\SimConnectVariableAttribute.cs" /> + <Compile Include="Concrete\DefaultSimConnectClient.cs" /> + <Compile Include="Concrete\SimConnectClientFactory.cs" /> + <Compile Include="Concrete\SimConnectDefinition.cs" /> + <Compile Include="ISimConnectClient.cs" /> + <Compile Include="ISimConnectClientFactory.cs" /> + <Compile Include="ISimConnectLibrary.cs" /> + <Compile Include="Lib\LatLon.cs" /> + <Compile Include="Lib\UnmanagedLibrary.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="Unmanaged\SimConnectDLLLibrary.cs" /> + </ItemGroup> + <ItemGroup Condition="'$(Platform)' == 'x64'"> + <Content Include="FS2020-SimConnect.dll"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + </ItemGroup> + <ItemGroup Condition="'$(Platform)' == 'x86'"> + <Content Include="FSX-SE-SimConnect.dll"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="FSX-SimConnect.dll"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + <Content Include="FSXSP2-SimConnect.dll"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> +</Project> \ No newline at end of file diff --git a/SimConnect/Unmanaged/SimConnectDLLLibrary.cs b/SimConnect/Unmanaged/SimConnectDLLLibrary.cs new file mode 100644 index 0000000..0bea960 --- /dev/null +++ b/SimConnect/Unmanaged/SimConnectDLLLibrary.cs @@ -0,0 +1,77 @@ +using System; +using SimConnect.Lib; + +#pragma warning disable 1591 +namespace SimConnect.Unmanaged +{ + public class SimConnectDLLLibrary : ISimConnectLibrary, IDisposable + { + private delegate uint SimConnectOpenProc(out IntPtr phSimConnect, string szName, IntPtr hwnd, uint userEventWin32, IntPtr hEventHandle, uint configIndex); + private delegate uint SimConnectCloseProc(IntPtr hSimConnect); + private delegate uint SimConnectAddToDataDefinitionProc(IntPtr hSimConnect, uint defineID, string datumName, string unitsName, SimConnectDataType datumType = SimConnectDataType.Float64, float epsilon = 0, uint datumID = uint.MaxValue); + private delegate uint SimConnectClearDataDefinitionProc(IntPtr hSimConnect, uint defineID); + private delegate uint SimConnectRequestDataOnSimObjectProc(IntPtr hSimConnect, uint requestID, uint defineID, uint objectID, SimConnectPeriod period, uint flags, uint origin = 0, uint interval = 0, uint limit = 0); + private delegate uint SimConnectGetNextDispatchProc(IntPtr hSimConnect, out IntPtr ppData, out uint pcbData); + + private readonly UnmanagedLibrary library; + + private readonly SimConnectOpenProc simConnectOpen; + private readonly SimConnectCloseProc simConnectClose; + private readonly SimConnectAddToDataDefinitionProc simConnectAddToDataDefinition; + private readonly SimConnectClearDataDefinitionProc simConnectClearDataDefinition; + private readonly SimConnectRequestDataOnSimObjectProc simConnectRequestDataOnSimObject; + private readonly SimConnectGetNextDispatchProc simConnectGetNextDispatch; + + + + public SimConnectDLLLibrary(string libraryFilename) + { + library = new UnmanagedLibrary(libraryFilename); + simConnectOpen = library.GetUnmanagedFunction<SimConnectOpenProc>("SimConnect_Open"); + simConnectClose = library.GetUnmanagedFunction<SimConnectCloseProc>("SimConnect_Close"); + simConnectAddToDataDefinition = library.GetUnmanagedFunction<SimConnectAddToDataDefinitionProc>("SimConnect_AddToDataDefinition"); + simConnectClearDataDefinition = library.GetUnmanagedFunction<SimConnectClearDataDefinitionProc>("SimConnect_ClearDataDefinition"); + simConnectRequestDataOnSimObject = library.GetUnmanagedFunction<SimConnectRequestDataOnSimObjectProc>("SimConnect_RequestDataOnSimObject"); + simConnectGetNextDispatch = library.GetUnmanagedFunction<SimConnectGetNextDispatchProc>("SimConnect_GetNextDispatch"); + } + + public void Dispose() + { + library?.Dispose(); + } + + + public uint SimConnect_Open(out IntPtr phSimConnect, string szName, IntPtr hwnd, uint userEventWin32, IntPtr hEventHandle, uint configIndex) + { + return simConnectOpen(out phSimConnect, szName, hwnd, userEventWin32, hEventHandle, configIndex); + } + + public uint SimConnect_Close(IntPtr hSimConnect) + { + return simConnectClose(hSimConnect); + } + + + public uint SimConnect_AddToDataDefinition(IntPtr hSimConnect, uint defineID, string datumName, string unitsName, SimConnectDataType datumType = SimConnectDataType.Float64, float epsilon = 0, uint datumID = uint.MaxValue) + { + return simConnectAddToDataDefinition(hSimConnect, defineID, datumName, unitsName, datumType, epsilon, datumID); + } + + public uint SimConnect_ClearDataDefinition(IntPtr hSimConnect, uint defineID) + { + return simConnectClearDataDefinition(hSimConnect, defineID); + } + + + public uint SimConnect_RequestDataOnSimObject(IntPtr hSimConnect, uint requestID, uint defineID, uint objectID, SimConnectPeriod period, uint flags, uint origin = 0, uint interval = 0, uint limit = 0) + { + return simConnectRequestDataOnSimObject(hSimConnect, requestID, defineID, objectID, period, flags, origin, interval, limit); + } + + public uint SimConnect_GetNextDispatch(IntPtr hSimConnect, out IntPtr ppData, out uint pcbData) + { + return simConnectGetNextDispatch(hSimConnect, out ppData, out pcbData); + } + } +} +#pragma warning restore 1591 diff --git a/SimConnectUnitTests/Properties/AssemblyInfo.cs b/SimConnectUnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2063cd8 --- /dev/null +++ b/SimConnectUnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SimConnectUnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SimConnectUnitTests")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2cd97d07-ef26-4cda-a090-df3d698e7f60")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SimConnectUnitTests/SimConnectDefinitionTest.cs b/SimConnectUnitTests/SimConnectDefinitionTest.cs new file mode 100644 index 0000000..73e298a --- /dev/null +++ b/SimConnectUnitTests/SimConnectDefinitionTest.cs @@ -0,0 +1,58 @@ +using System; +using FluentAssertions; +using SimConnect.Attribute; +using SimConnect.Concrete; +using Xunit; + +namespace SimConnectUnitTests +{ + public class SimConnectDefinitionTest + { + [Fact] + public void EmptyObject() + { + Action test = () => + { + var unused = new SimConnectDefinition(typeof(object)); + }; + + test.Should().Throw<InvalidOperationException>("the object is empty"); + } + + + // ReSharper disable UnusedMember.Global + // ReSharper disable UnusedMember.Local + #pragma warning disable 649 + private class NativeVariablesTest + { + [SimConnectVariable("INT")] + public int IntField; + + [SimConnectVariable("UINT")] + public uint UIntField; + + [SimConnectVariable("LONG")] + public long LongField; + + [SimConnectVariable("ULONG")] + public ulong ULongField; + + [SimConnectVariable("SINGLE")] + public float SingleProperty { get; set; } + + [SimConnectVariable("DOUBLE")] + public double DoubleProperty { get; set; } + } + #pragma warning restore 649 + // ReSharper restore UnusedMember.Local + // ReSharper restore UnusedMember.Global + + + [Fact] + public void NativeVariables() + { + var definition = new SimConnectDefinition(typeof(NativeVariablesTest)); + definition.Variables.Should().HaveCount(6); + } + } +} diff --git a/SimConnectUnitTests/SimConnectUnitTests.csproj b/SimConnectUnitTests/SimConnectUnitTests.csproj new file mode 100644 index 0000000..9aa0dc5 --- /dev/null +++ b/SimConnectUnitTests/SimConnectUnitTests.csproj @@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="..\packages\xunit.core.2.4.1\build\xunit.core.props" Condition="Exists('..\packages\xunit.core.2.4.1\build\xunit.core.props')" /> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{2CD97D07-EF26-4CDA-A090-DF3D698E7F60}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>SimConnectUnitTests</RootNamespace> + <AssemblyName>SimConnectUnitTests</AssemblyName> + <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <Deterministic>true</Deterministic> + <NuGetPackageImportStamp> + </NuGetPackageImportStamp> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' "> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x64\Debug %2832 bits%29\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>x64</PlatformTarget> + <LangVersion>7.3</LangVersion> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' "> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x64\Release %2832 bits%29\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <Optimize>true</Optimize> + <DebugType>pdbonly</DebugType> + <PlatformTarget>x64</PlatformTarget> + <LangVersion>7.3</LangVersion> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x86\Debug %2832 bits%29\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>x86</PlatformTarget> + <LangVersion>7.3</LangVersion> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' "> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x86\Release %2832 bits%29\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <Optimize>true</Optimize> + <DebugType>pdbonly</DebugType> + <PlatformTarget>x86</PlatformTarget> + <LangVersion>7.3</LangVersion> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <ItemGroup> + <Reference Include="FluentAssertions, Version=5.10.3.0, Culture=neutral, PublicKeyToken=33f2691a05b67b6a, processorArchitecture=MSIL"> + <HintPath>..\packages\FluentAssertions.5.10.3\lib\net47\FluentAssertions.dll</HintPath> + </Reference> + <Reference Include="System" /> + <Reference Include="System.Configuration" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Xml" /> + <Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL"> + <HintPath>..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll</HintPath> + </Reference> + <Reference Include="xunit.assert, Version=2.4.1.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL"> + <HintPath>..\packages\xunit.assert.2.4.1\lib\netstandard1.1\xunit.assert.dll</HintPath> + </Reference> + <Reference Include="xunit.core, Version=2.4.1.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL"> + <HintPath>..\packages\xunit.extensibility.core.2.4.1\lib\net452\xunit.core.dll</HintPath> + </Reference> + <Reference Include="xunit.execution.desktop, Version=2.4.1.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL"> + <HintPath>..\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll</HintPath> + </Reference> + </ItemGroup> + <ItemGroup> + <Compile Include="SimConnectDefinitionTest.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> + <ItemGroup> + <Analyzer Include="..\packages\xunit.analyzers.0.10.0\analyzers\dotnet\cs\xunit.analyzers.dll" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\SimConnect\SimConnect.csproj"> + <Project>{f160bb6a-7620-41e5-a99c-948c208875e4}</Project> + <Name>SimConnect</Name> + </ProjectReference> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> + <PropertyGroup> + <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> + </PropertyGroup> + <Error Condition="!Exists('..\packages\xunit.core.2.4.1\build\xunit.core.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.4.1\build\xunit.core.props'))" /> + <Error Condition="!Exists('..\packages\xunit.core.2.4.1\build\xunit.core.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.core.2.4.1\build\xunit.core.targets'))" /> + </Target> + <Import Project="..\packages\xunit.core.2.4.1\build\xunit.core.targets" Condition="Exists('..\packages\xunit.core.2.4.1\build\xunit.core.targets')" /> +</Project> \ No newline at end of file diff --git a/SimConnectUnitTests/packages.config b/SimConnectUnitTests/packages.config new file mode 100644 index 0000000..31768df --- /dev/null +++ b/SimConnectUnitTests/packages.config @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="FluentAssertions" version="5.10.3" targetFramework="net472" /> + <package id="xunit" version="2.4.1" targetFramework="net472" /> + <package id="xunit.abstractions" version="2.0.3" targetFramework="net472" /> + <package id="xunit.analyzers" version="0.10.0" targetFramework="net472" /> + <package id="xunit.assert" version="2.4.1" targetFramework="net472" /> + <package id="xunit.core" version="2.4.1" targetFramework="net472" /> + <package id="xunit.extensibility.core" version="2.4.1" targetFramework="net472" /> + <package id="xunit.extensibility.execution" version="2.4.1" targetFramework="net472" /> +</packages> \ No newline at end of file