diff --git a/README.md b/README.md index dd0511d..f93ca10 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ If, on the other hand, you just want to connect to Flight Simulator and read som FsConnect uses the _Microsoft.FlightSimulator.SimConnect_ .NET Framework library and the underlying native x64 _simconnect.dll_ library. These files are distributed via the Flight Simulator 2020 SDK, currently version 0.10.0, but are included for easy use. -At the moment this project is intended as an easier to use wrapper than the current SimConnect for simple projects, creating a simpler C# programming model and reducing the need for repeated boiler plate code. Expect breaking changes. +At the moment this project is intended as an easier to use wrapper than the current SimConnect for simple projects, creating a simpler C# programming model and reducing the need for repeated boiler plate code. + +> NOTE: **Expect breaking changes and infrequent updates.** ## Additional information For more information about SimConnect and the Flight Simulator SDK see the [Microsoft Flight Simulator SDK site]( @@ -58,8 +60,10 @@ Such an object must: * Be a struct * Be attributed with: [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +## SimConnect data definition + Create a struct similar to shown below to hold this information: -``` +```csharp [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] public struct PlaneInfoResponse { @@ -76,7 +80,7 @@ See the documentation for [simulation variables](https://docs.flightsimulator.co Before Flight Simulator can use this structure it needs the definition of the object. Flight Simulator sees a definition as a list of properties, with specified units and data types. This definition is then used to fill the registred struct with data from Flight Simulator. -``` +```csharp List definition = new List(); // Consult the SDK for valid sim variable names, units and whether they can be written to. @@ -89,12 +93,37 @@ fsConnect.RegisterDataDefinition(Definitions.PlaneInfo, defin See the enums FsSimVar and FsUnit in the FsConnect library for simpler to use enums instead of strings for specifying variable name and units. This would be an equal definition: -``` +```csharp definition.Add(new SimProperty(FsSimVar.Title, FsUnit.None, SIMCONNECT_DATATYPE.STRING256)); definition.Add(new SimProperty(FsSimVar.PlaneLatitude, FsUnit.Radians, SIMCONNECT_DATATYPE.FLOAT64)); definition.Add(new SimProperty(FsSimVar.PlaneLongitude, FsUnit.Radians, SIMCONNECT_DATATYPE.FLOAT64)); ``` +## Reflection based data definition + +An alternative method of defining the data definition is to decorate the type with the SimProperty attribute to describe field names and units: + +```csharp +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct PlaneInfo +{ + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public String Title; + [SimProperty(UnitId = FsUnit.Degree)] + public double PlaneLatitude; + [SimProperty(NameId=FsSimVar.PlaneLongitude, UnitId = FsUnit.Degree)] + public double Longitude; +} +``` + +Such a type can be easily registered using FsConnect using the following method: + +```csharp + +int planeInfoDefinitionId = fsConnect.RegisterDataDefinition(); + +``` + # Example @@ -142,27 +171,29 @@ using Microsoft.FlightSimulator.SimConnect; namespace FsConnectTest { - public enum Definitions - { - PlaneInfo = 0 - } - public enum Requests { PlaneInfoRequest = 0 } + // Use field name and SimProperty attribute to configure the data definition for the type. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] public struct PlaneInfoResponse { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public String Title; - public double Latitude; - public double Longitude; - public double Altitude; - public double Heading; - public double SpeedMpS; - public double SpeedKnots; + [SimProperty(UnitId = FsUnit.Degree)] + public double PlaneLatitude; + [SimProperty(UnitId = FsUnit.Degree)] + public double PlaneLongitude; + [SimProperty(UnitId = FsUnit.Feet)] + public double PlaneAltitude; + [SimProperty(UnitId = FsUnit.Degree)] + public double PlaneHeadingDegreesTrue; + [SimProperty(NameId = FsSimVar.AirspeedTrue, UnitId = FsUnit.MeterPerSecond)] + public double AirspeedTrueInMeterPerSecond; + [SimProperty(NameId = FsSimVar.AirspeedTrue, UnitId = FsUnit.Knot)] + public double AirspeedTrueInKnot; } public class FsConnectTestConsole @@ -194,23 +225,17 @@ namespace FsConnectTest fsConnect.FsDataReceived += HandleReceivedFsData; - List definition = new List(); - // Consult the SDK for valid sim variable names, units and whether they can be written to. - definition.Add(new SimProperty("Title", null, SIMCONNECT_DATATYPE.STRING256)); - definition.Add(new SimProperty("Plane Latitude", "degrees", SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty("Plane Longitude", "degrees", SIMCONNECT_DATATYPE.FLOAT64)); + int planeInfoDefinitionId = fsConnect.RegisterDataDefinition(); - // Can also use predefined enums for sim variables and units (incomplete) - definition.Add(new SimProperty(FsSimVar.PlaneAltitude, FsUnit.Feet, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.PlaneHeadingDegreesTrue, FsUnit.Degrees, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.AirspeedTrue, FsUnit.MeterPerSecond, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.AirspeedTrue, FsUnit.Knot, SIMCONNECT_DATATYPE.FLOAT64)); + ConsoleKeyInfo cki; - fsConnect.RegisterDataDefinition(Definitions.PlaneInfo, definition); + do + { + fsConnect.RequestData(Requests.PlaneInfoRequest, planeInfoDefinitionId); + cki = Console.ReadKey(); + } while (cki.Key != ConsoleKey.Escape); - fsConnect.RequestData(Requests.PlaneInfoRequest, Definitions.PlaneInfo); - Console.ReadKey(); fsConnect.Disconnect(); } @@ -218,8 +243,8 @@ namespace FsConnectTest { if (e.RequestId == (uint)Requests.PlaneInfoRequest) { - PlaneInfoResponse r = (PlaneInfoResponse)e.Data; - Console.WriteLine($"{r.Latitude:F4} {r.Longitude:F4} {r.Altitude:F1}ft {r.Heading:F1}deg {r.SpeedMpS:F0}m/s {r.SpeedKnots:F0}kt"); + PlaneInfoResponse r = (PlaneInfoResponse)e.Data.FirstOrDefault(); + Console.WriteLine($"{r.PlaneLatitude:F4} {r.PlaneLongitude:F4} {r.PlaneAltitude:F1}ft {r.PlaneHeadingDegreesTrue:F1}deg {r.AirspeedTrueInMeterPerSecond:F0}m/s {r.AirspeedTrueInKnot:F0}kt"); } } } @@ -236,7 +261,7 @@ The manager can either poll as often as needed, or set up to continously get upd The definition for the data to be requested needs to be done from FsConnect using _RegisterDataDefinition_ and the type then provided to the aircraft manager. See the example for how to register the PlaneInfoResponse definition. -``` +```csharp AircraftManager aircraftManager = new AircraftManager(fsConnect, Definitions.PlaneInfo, Requests.AircraftManager); @@ -262,7 +287,7 @@ The Sim Object Manager is a addon to the FsConnect to avoid making the base libr See the example for how to register the PlaneInfoResponse definition. -``` +```csharp SimObjectManager simObjectManager = new SimObjectManager(_fsConnect, Definitions.PlaneInfo, Requests.SimObjects); @@ -287,8 +312,35 @@ Future updates will provide more functionality such as updating sim objects. The test console has an example for how to use the Sim Object Manager, see the SimObjectMenu class. +## World Manager + +The world manager currently supports updating the time in flight simulator. + +```csharp + +WorldManager worldManager = new WorldManager(fsConnect); +worldManager.SetTime(new DateTime(year, month, day, hour, minute, 0)); + +``` + ## Community * Have you find a bug? Do you have an idea for a new feature? ... [open an issue on GitHub](https://github.com/c-true/FsConnect/issues) * Do you want to contribute piece of code? ... [submit a pull-request](https://github.com/c-true/FsConnect/pulls) * `master` branch contains the code being worked on * Or from your own fork + +#Change log + +## 1.3.0 + +- Aspect based data definition, using SimProperty attribute to define fields. +- Simple support for registering client events. +- Support for setting time in flight simulator, through the WorldManager. + +## 1.2.0 + +- Opened up access to more of SimConnect's API. +- Added support for some key simulator events, such as Pause. +- Added support for requesting data on Sim Objects. +- Added managers as example on how to wrap FsConnect for common operations, such as requesting aircraft or sim object data. +- Documentation and refactorings. \ No newline at end of file diff --git a/src/CTrue.FsConnect.ExampleConsole/Program.cs b/src/CTrue.FsConnect.ExampleConsole/Program.cs index 0bd87ca..fed7388 100644 --- a/src/CTrue.FsConnect.ExampleConsole/Program.cs +++ b/src/CTrue.FsConnect.ExampleConsole/Program.cs @@ -7,11 +7,6 @@ namespace FsConnectTest { - public enum Definitions - { - PlaneInfo = 0 - } - public enum Requests { PlaneInfoRequest = 0 @@ -22,12 +17,18 @@ public struct PlaneInfoResponse { [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public String Title; - public double Latitude; - public double Longitude; - public double Altitude; - public double Heading; - public double SpeedMpS; - public double SpeedKnots; + [SimProperty(UnitId = FsUnit.Degree)] + public double PlaneLatitude; + [SimProperty(UnitId = FsUnit.Degree)] + public double PlaneLongitude; + [SimProperty(UnitId = FsUnit.Feet)] + public double PlaneAltitude; + [SimProperty(UnitId = FsUnit.Degree)] + public double PlaneHeadingDegreesTrue; + [SimProperty(NameId = FsSimVar.AirspeedTrue, UnitId = FsUnit.MeterPerSecond)] + public double AirspeedTrueInMeterPerSecond; + [SimProperty(NameId = FsSimVar.AirspeedTrue, UnitId = FsUnit.Knot)] + public double AirspeedTrueInKnot; } public class FsConnectTestConsole @@ -59,23 +60,16 @@ public static void Main(string[] args) fsConnect.FsDataReceived += HandleReceivedFsData; - List definition = new List(); + int planeInfoDefinitionId = fsConnect.RegisterDataDefinition(); - // Consult the SDK for valid sim variable names, units and whether they can be written to. - definition.Add(new SimProperty("Title", null, SIMCONNECT_DATATYPE.STRING256)); - definition.Add(new SimProperty("Plane Latitude", "degrees", SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty("Plane Longitude", "degrees", SIMCONNECT_DATATYPE.FLOAT64)); + ConsoleKeyInfo cki; - // Can also use predefined enums for sim variables and units (incomplete) - definition.Add(new SimProperty(FsSimVar.PlaneAltitude, FsUnit.Feet, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.PlaneHeadingDegreesTrue, FsUnit.Degrees, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.AirspeedTrue, FsUnit.MeterPerSecond, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.AirspeedTrue, FsUnit.Knot, SIMCONNECT_DATATYPE.FLOAT64)); - - fsConnect.RegisterDataDefinition(Definitions.PlaneInfo, definition); + do + { + fsConnect.RequestData(Requests.PlaneInfoRequest, planeInfoDefinitionId); + cki = Console.ReadKey(); + } while (cki.Key != ConsoleKey.Escape); - fsConnect.RequestData(Requests.PlaneInfoRequest, Definitions.PlaneInfo); - Console.ReadKey(); fsConnect.Disconnect(); } @@ -86,7 +80,7 @@ private static void HandleReceivedFsData(object sender, FsDataReceivedEventArgs if (e.RequestId == (uint)Requests.PlaneInfoRequest) { PlaneInfoResponse r = (PlaneInfoResponse)e.Data.FirstOrDefault(); - Console.WriteLine($"{r.Latitude:F4} {r.Longitude:F4} {r.Altitude:F1}ft {r.Heading:F1}deg {r.SpeedMpS:F0}m/s {r.SpeedKnots:F0}kt"); + Console.WriteLine($"{r.PlaneLatitude:F4} {r.PlaneLongitude:F4} {r.PlaneAltitude:F1}ft {r.PlaneHeadingDegreesTrue:F1}deg {r.AirspeedTrueInMeterPerSecond:F0}m/s {r.AirspeedTrueInKnot:F0}kt"); } } } diff --git a/src/CTrue.FsConnect.Managers/CTrue.FsConnect.Managers.csproj b/src/CTrue.FsConnect.Managers/CTrue.FsConnect.Managers.csproj index 418825c..037607d 100644 --- a/src/CTrue.FsConnect.Managers/CTrue.FsConnect.Managers.csproj +++ b/src/CTrue.FsConnect.Managers/CTrue.FsConnect.Managers.csproj @@ -9,12 +9,12 @@ Managers that supports common operations working with Microsoft Flightsimulator 2020, through the use of FsConnect. Supports: - Aircraft information - - Retriveing sim objects + - Retrieving sim objects C-True true - - + LICENSE.txt + true https://github.com/c-true/FsConnect msfs flight-simulator simconnect 1.2.0 @@ -42,4 +42,8 @@ + + + + diff --git a/src/CTrue.FsConnect.Managers/WorldManager.cs b/src/CTrue.FsConnect.Managers/WorldManager.cs new file mode 100644 index 0000000..3d813b6 --- /dev/null +++ b/src/CTrue.FsConnect.Managers/WorldManager.cs @@ -0,0 +1,38 @@ +using System; + +namespace CTrue.FsConnect.Managers +{ + public enum WorldManagerId + { + SetTime = 100, + SetZuluYears = 101, + SetZuluDays = 102, + SetZuluHours = 103, + SetZuluMinute = 104, + }; + + public class WorldManager + { + private readonly IFsConnect _fsConnect; + + public WorldManager(IFsConnect fsConnect) + { + _fsConnect = fsConnect; + + _fsConnect.MapClientEventToSimEvent(WorldManagerId.SetTime, WorldManagerId.SetZuluYears, "ZULU_YEAR_SET"); + _fsConnect.MapClientEventToSimEvent(WorldManagerId.SetTime, WorldManagerId.SetZuluDays, "ZULU_DAY_SET"); + _fsConnect.MapClientEventToSimEvent(WorldManagerId.SetTime, WorldManagerId.SetZuluHours, "ZULU_HOURS_SET"); + _fsConnect.MapClientEventToSimEvent(WorldManagerId.SetTime, WorldManagerId.SetZuluMinute, "ZULU_MINUTES_SET"); + + _fsConnect.SetNotificationGroupPriority(WorldManagerId.SetTime); + } + + public void SetTime(DateTime time) + { + _fsConnect.TransmitClientEvent(WorldManagerId.SetZuluYears, (uint)time.Year, WorldManagerId.SetTime); + _fsConnect.TransmitClientEvent(WorldManagerId.SetZuluDays, (uint)time.DayOfYear, WorldManagerId.SetTime); + _fsConnect.TransmitClientEvent(WorldManagerId.SetZuluHours, (uint)time.Hour, WorldManagerId.SetTime); + _fsConnect.TransmitClientEvent(WorldManagerId.SetZuluMinute, (uint)time.Minute, WorldManagerId.SetTime); + } + } +} \ No newline at end of file diff --git a/src/CTrue.FsConnect.Managers/licenses/LICENSE.txt b/src/CTrue.FsConnect.Managers/licenses/LICENSE.txt new file mode 100644 index 0000000..d071abb --- /dev/null +++ b/src/CTrue.FsConnect.Managers/licenses/LICENSE.txt @@ -0,0 +1,9 @@ +Copyright 2020-2021 C-True + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +This software is dependant on binaries, "simconnect.dll" and "Microsoft.FlightSimulator.SimConnect.dll" as released by the Flight Simulator 2020 Software Development Kit, that are the copyright of Microsoft. \ No newline at end of file diff --git a/src/CTrue.FsConnect.Test/CTrue.FsConnect.Test.csproj b/src/CTrue.FsConnect.Test/CTrue.FsConnect.Test.csproj new file mode 100644 index 0000000..48e48d8 --- /dev/null +++ b/src/CTrue.FsConnect.Test/CTrue.FsConnect.Test.csproj @@ -0,0 +1,29 @@ + + + + net45 + x64 + + + + + + + + + + + + + ..\Dependencies\SimConnect\lib\net40\Microsoft.FlightSimulator.SimConnect.dll + + + + + + %(FileName)%(Extension) + PreserveNewest + + + + diff --git a/src/CTrue.FsConnect.Test/SimPropertyReflectorTest.cs b/src/CTrue.FsConnect.Test/SimPropertyReflectorTest.cs new file mode 100644 index 0000000..d170097 --- /dev/null +++ b/src/CTrue.FsConnect.Test/SimPropertyReflectorTest.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Microsoft.FlightSimulator.SimConnect; +using NUnit.Framework; + +namespace CTrue.FsConnect.Test +{ + [TestFixture] + public class SimPropertyReflectorTest + { + [Test] + public void Test() + { + SimPropertyReflector reflector = new SimPropertyReflector(); + + List list = reflector.GetSimProperties(); + + Assert.That(list, Is.Not.Null); + Assert.That(list.Count, Is.EqualTo(7)); + + Assert.That(list[0].Name, Is.EqualTo("TITLE")); + Assert.That(list[0].Unit, Is.EqualTo("")); + Assert.That(list[0].DataType, Is.EqualTo(SIMCONNECT_DATATYPE.STRING256)); + + Assert.That(list[1].Name, Is.EqualTo("Category")); + Assert.That(list[1].Unit, Is.EqualTo("")); + Assert.That(list[1].DataType, Is.EqualTo(SIMCONNECT_DATATYPE.STRING256)); + + Assert.That(list[2].Name, Is.EqualTo("NAV NAME")); + Assert.That(list[2].Unit, Is.EqualTo("")); + Assert.That(list[2].DataType, Is.EqualTo(SIMCONNECT_DATATYPE.STRING128)); + + Assert.That(list[3].Name, Is.EqualTo("PLANE ALTITUDE")); + Assert.That(list[3].Unit, Is.EqualTo("feet")); + Assert.That(list[3].DataType, Is.EqualTo(SIMCONNECT_DATATYPE.FLOAT64)); + + Assert.That(list[4].Name, Is.EqualTo("PLANE ALTITUDE")); + Assert.That(list[4].Unit, Is.EqualTo("feet")); + Assert.That(list[4].DataType, Is.EqualTo(SIMCONNECT_DATATYPE.FLOAT64)); + + Assert.That(list[5].Name, Is.EqualTo("PLANE ALTITUDE")); + Assert.That(list[5].Unit, Is.EqualTo("meter")); + Assert.That(list[5].DataType, Is.EqualTo(SIMCONNECT_DATATYPE.FLOAT64)); + + Assert.That(list[6].Name, Is.EqualTo("PLANE ALT ABOVE GROUND")); + Assert.That(list[6].Unit, Is.EqualTo("meter")); + Assert.That(list[6].DataType, Is.EqualTo(SIMCONNECT_DATATYPE.FLOAT32)); + } + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] + public struct SimConnectObject + { + // #0 + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string Title; + + // #1 + [SimProperty(Name = "Category")] + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string A256CharString; + + // #2 + [SimProperty(NameId = FsSimVar.NavName)] + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string A128CharString; + + // #3 + [SimProperty(NameId = FsSimVar.PlaneAltitude, Unit = "feet")] + public double Altitude; + + // #4 + [SimProperty(Unit = "feet")] + public double PlaneAltitude; + + // #5 + [SimProperty(UnitId = FsUnit.Meter)] + public double Plane_Altitude; + + // #6 + [SimProperty(UnitId = FsUnit.Meter)] + public float PlaneAltAboveGround; + + public double APropertyThatIsIgnored { get; set; } + + private double APrivateFieldIsIgnored; + }; +} diff --git a/src/CTrue.FsConnect.TestConsole/Menus/MainMenu.cs b/src/CTrue.FsConnect.TestConsole/Menus/MainMenu.cs index bfef6a0..f618ef8 100644 --- a/src/CTrue.FsConnect.TestConsole/Menus/MainMenu.cs +++ b/src/CTrue.FsConnect.TestConsole/Menus/MainMenu.cs @@ -1,4 +1,5 @@ using System; +using CTrue.FsConnect.Managers; namespace CTrue.FsConnect.TestConsole { @@ -32,6 +33,13 @@ protected override void SetUpMenu() Description = "2 - Aircraft information", Handler = NavigateToMenu }); + + Add(new MenuItem() + { + Key = ConsoleKey.D3, + Description = "3 - World", + Handler = NavigateToMenu + }); } protected override bool NavigateToParentMenu() diff --git a/src/CTrue.FsConnect.TestConsole/Menus/WorldMenu.cs b/src/CTrue.FsConnect.TestConsole/Menus/WorldMenu.cs new file mode 100644 index 0000000..d890cb9 --- /dev/null +++ b/src/CTrue.FsConnect.TestConsole/Menus/WorldMenu.cs @@ -0,0 +1,61 @@ +using System; +using CTrue.FsConnect.Managers; + +namespace CTrue.FsConnect.TestConsole +{ + public class WorldMenu : Menu + { + private WorldManager _worldManager; + + public WorldMenu(IFsConnect fsConnect) : base(fsConnect) + { + _worldManager = new WorldManager(fsConnect); + } + + protected override void SetUpMenu() + { + base.SetUpMenu(); + + Add(new MenuItem() + { + Key = ConsoleKey.D1, + Description = "1 - Set Time", + Handler = SetTime + }); + } + + private bool SetTime() + { + DateTime utc = DateTime.UtcNow; + + Console.WriteLine("Year:"); + string input = Console.ReadLine(); + int year = int.Parse(input); + + Console.WriteLine("Month:"); + input = Console.ReadLine(); + int month = int.Parse(input); + + Console.WriteLine("Day:"); + input = Console.ReadLine(); + int day = int.Parse(input); + + Console.WriteLine("Hour:"); + input = Console.ReadLine(); + int hour = int.Parse(input); + + Console.WriteLine("Minutes:"); + input = Console.ReadLine(); + int minute = int.Parse(input); + + _worldManager.SetTime(new DateTime(year, month, day, hour, minute, 0)); + + return false; + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } + } +} \ No newline at end of file diff --git a/src/CTrue.FsConnect.TestConsole/PlaneInfoResponse.cs b/src/CTrue.FsConnect.TestConsole/PlaneInfoResponse.cs index fddf367..7400712 100644 --- a/src/CTrue.FsConnect.TestConsole/PlaneInfoResponse.cs +++ b/src/CTrue.FsConnect.TestConsole/PlaneInfoResponse.cs @@ -10,11 +10,17 @@ public struct PlaneInfoResponse public String Title; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public String Category; + [SimProperty(NameId = FsSimVar.PlaneLatitude, UnitId = FsUnit.Radian)] public double Latitude; + [SimProperty(NameId = FsSimVar.PlaneLongitude, UnitId = FsUnit.Radian)] public double Longitude; + [SimProperty(NameId = FsSimVar.PlaneAltitudeAboveGround, UnitId = FsUnit.Feet)] public double AltitudeAboveGround; + [SimProperty(NameId = FsSimVar.PlaneAltitude, UnitId = FsUnit.Feet)] public double Altitude; + [SimProperty(NameId = FsSimVar.PlaneHeadingDegreesTrue, UnitId = FsUnit.Degree)] public double Heading; + [SimProperty(NameId = FsSimVar.AirspeedTrue, UnitId = FsUnit.Knot)] public double Speed; public override string ToString() @@ -27,9 +33,13 @@ public override string ToString() [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] public struct PlanePosition { + [SimProperty(NameId = FsSimVar.PlaneLatitude, UnitId = FsUnit.Degree)] public double Latitude; + [SimProperty(NameId = FsSimVar.PlaneLongitude, UnitId = FsUnit.Degree)] public double Longitude; + [SimProperty(NameId = FsSimVar.PlaneAltitude, UnitId = FsUnit.Feet)] public double Altitude; + [SimProperty(NameId = FsSimVar.PlaneHeadingDegreesTrue, UnitId = FsUnit.Degree)] public double Heading; public override string ToString() diff --git a/src/CTrue.FsConnect.TestConsole/Program.cs b/src/CTrue.FsConnect.TestConsole/Program.cs index 2192811..d2073f0 100644 --- a/src/CTrue.FsConnect.TestConsole/Program.cs +++ b/src/CTrue.FsConnect.TestConsole/Program.cs @@ -134,27 +134,8 @@ private static void OnFsConnectOnConnectionChanged(object sender, bool args) private static void InitializeDataDefinitions(FsConnect fsConnect) { - List definition = new List(); - - definition.Add(new SimProperty(FsSimVar.Title, FsUnit.None, SIMCONNECT_DATATYPE.STRING256)); - definition.Add(new SimProperty(FsSimVar.Category, FsUnit.None, SIMCONNECT_DATATYPE.STRING256)); - definition.Add(new SimProperty(FsSimVar.PlaneLatitude, FsUnit.Radians, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.PlaneLongitude, FsUnit.Radians, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.PlaneAltitudeAboveGround, FsUnit.Feet, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.PlaneAltitude, FsUnit.Feet, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.PlaneHeadingDegreesTrue, FsUnit.Degrees, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.AirspeedTrue, FsUnit.Knot, SIMCONNECT_DATATYPE.FLOAT64)); - - fsConnect.RegisterDataDefinition(Definitions.PlaneInfo, definition); - - definition = new List(); - - definition.Add(new SimProperty(FsSimVar.PlaneLatitude, FsUnit.Degrees, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.PlaneLongitude, FsUnit.Degrees, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.PlaneAltitude, FsUnit.Feet, SIMCONNECT_DATATYPE.FLOAT64)); - definition.Add(new SimProperty(FsSimVar.PlaneHeadingDegreesTrue, FsUnit.Degrees, SIMCONNECT_DATATYPE.FLOAT64)); - - fsConnect.RegisterDataDefinition(Definitions.PlanePosition, definition); + fsConnect.RegisterDataDefinition(Definitions.PlaneInfo); + fsConnect.RegisterDataDefinition(Definitions.PlanePosition); } static void DisplayHelp(ParserResult result, IEnumerable errs) diff --git a/src/CTrue.FsConnect.sln b/src/CTrue.FsConnect.sln index 0e69dee..385fbfd 100644 --- a/src/CTrue.FsConnect.sln +++ b/src/CTrue.FsConnect.sln @@ -14,7 +14,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CTrue.FsConnect.ExampleConsole", "CTrue.FsConnect.ExampleConsole\CTrue.FsConnect.ExampleConsole.csproj", "{EF83E27B-B413-4FEC-A49B-2F8E5B4CA213}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CTrue.FsConnect.Managers", "CTrue.FsConnect.Managers\CTrue.FsConnect.Managers.csproj", "{8D9B0572-5BB1-4097-8D11-0339F6B8C57E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CTrue.FsConnect.Managers", "CTrue.FsConnect.Managers\CTrue.FsConnect.Managers.csproj", "{8D9B0572-5BB1-4097-8D11-0339F6B8C57E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CTrue.FsConnect.Test", "CTrue.FsConnect.Test\CTrue.FsConnect.Test.csproj", "{0DB161A6-82E6-4C8C-BB2E-71B87E367756}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -74,6 +76,18 @@ Global {8D9B0572-5BB1-4097-8D11-0339F6B8C57E}.Release|x64.Build.0 = Release|Any CPU {8D9B0572-5BB1-4097-8D11-0339F6B8C57E}.Release|x86.ActiveCfg = Release|Any CPU {8D9B0572-5BB1-4097-8D11-0339F6B8C57E}.Release|x86.Build.0 = Release|Any CPU + {0DB161A6-82E6-4C8C-BB2E-71B87E367756}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0DB161A6-82E6-4C8C-BB2E-71B87E367756}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0DB161A6-82E6-4C8C-BB2E-71B87E367756}.Debug|x64.ActiveCfg = Debug|Any CPU + {0DB161A6-82E6-4C8C-BB2E-71B87E367756}.Debug|x64.Build.0 = Debug|Any CPU + {0DB161A6-82E6-4C8C-BB2E-71B87E367756}.Debug|x86.ActiveCfg = Debug|Any CPU + {0DB161A6-82E6-4C8C-BB2E-71B87E367756}.Debug|x86.Build.0 = Debug|Any CPU + {0DB161A6-82E6-4C8C-BB2E-71B87E367756}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0DB161A6-82E6-4C8C-BB2E-71B87E367756}.Release|Any CPU.Build.0 = Release|Any CPU + {0DB161A6-82E6-4C8C-BB2E-71B87E367756}.Release|x64.ActiveCfg = Release|Any CPU + {0DB161A6-82E6-4C8C-BB2E-71B87E367756}.Release|x64.Build.0 = Release|Any CPU + {0DB161A6-82E6-4C8C-BB2E-71B87E367756}.Release|x86.ActiveCfg = Release|Any CPU + {0DB161A6-82E6-4C8C-BB2E-71B87E367756}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/CTrue.FsConnect/FsConnect.cs b/src/CTrue.FsConnect/FsConnect.cs index 081965e..b745717 100644 --- a/src/CTrue.FsConnect/FsConnect.cs +++ b/src/CTrue.FsConnect/FsConnect.cs @@ -20,6 +20,7 @@ public class FsConnect : IFsConnect private Thread _simConnectReceiveThread = null; private readonly FsConnectionInfo _connectionInfo = new FsConnectionInfo(); private bool _paused = false; + private int _nextId = (int)FsConnectEnum.Base; #region Simconnect structures @@ -190,8 +191,10 @@ public void Disconnect() } } + #region RegisterDataDefinition + /// - public void RegisterDataDefinition(Enum id, List definition) where T : struct + public int RegisterDataDefinition(Enum id, List definition) where T : struct { foreach (var item in definition) { @@ -199,24 +202,126 @@ public void RegisterDataDefinition(Enum id, List definition) whe } _simConnect.RegisterDataDefineStruct(id); + + return Convert.ToInt32(id); + } + + /// + public int RegisterDataDefinition(int id, List definition) where T : struct + { + return RegisterDataDefinition((FsConnectEnum) id, definition); + } + + /// + public int RegisterDataDefinition(List definition) where T : struct + { + int nextId = GetNextId(); + + return RegisterDataDefinition(nextId, definition); + } + + /// + public int RegisterDataDefinition(Enum defineId) where T : struct + { + SimPropertyReflector reflector = new SimPropertyReflector(); + List definition = reflector.GetSimProperties(); + + RegisterDataDefinition(defineId, definition); + + return Convert.ToInt32(defineId); } + /// + public int RegisterDataDefinition(int defineId) where T : struct + { + return RegisterDataDefinition((FsConnectEnum)defineId); + } + + /// + public int RegisterDataDefinition() where T : struct + { + return RegisterDataDefinition(GetNextId()); + } + + #endregion + + #region RequestData + /// public void RequestDataOnSimObject(Enum requestId, Enum defineId, uint objectId, FsConnectPeriod period, FsConnectDRequestFlag flags, uint interval, uint origin, uint limit) { _simConnect?.RequestDataOnSimObject(requestId, defineId, objectId, (SIMCONNECT_PERIOD)period, (SIMCONNECT_DATA_REQUEST_FLAG)flags, interval, origin, limit); } + /// + public void RequestDataOnSimObject(Enum requestId, int defineId, uint objectId, FsConnectPeriod period, FsConnectDRequestFlag flags, uint interval, uint origin, uint limit) + { + _simConnect?.RequestDataOnSimObject(requestId, (FsConnectEnum)defineId, objectId, (SIMCONNECT_PERIOD)period, (SIMCONNECT_DATA_REQUEST_FLAG)flags, interval, origin, limit); + } + /// public void RequestData(Enum requestId, Enum defineId, uint radius = 0, FsConnectSimobjectType type = FsConnectSimobjectType.User) { _simConnect?.RequestDataOnSimObjectType( requestId, defineId, radius, (SIMCONNECT_SIMOBJECT_TYPE)type); } + public void RequestData(Enum requestId, int defineId, uint radius = 0, FsConnectSimobjectType type = FsConnectSimobjectType.User) + { + _simConnect?.RequestDataOnSimObjectType(requestId, (FsConnectEnum)defineId, radius, (SIMCONNECT_SIMOBJECT_TYPE)type); + } + + #endregion + + /// + public void UpdateData(Enum defineId, T data, uint objectId = 1) + { + _simConnect?.SetDataOnSimObject(defineId, objectId, SIMCONNECT_DATA_SET_FLAG.DEFAULT, data); + } + + /// + public void UpdateData(int defineId, T data, uint objectId = 1) + { + _simConnect?.SetDataOnSimObject((FsConnectEnum)defineId, objectId, SIMCONNECT_DATA_SET_FLAG.DEFAULT, data); + } + + /// + /// Gets the next id, for definitions and other SimConnect artifacts that require it. + /// + /// Returns an int that can be used to identifying SimConnect artifacts, such as definitions and events. + public int GetNextId() + { + return _nextId++; + } + + /// + public void MapClientEventToSimEvent(Enum groupId, Enum eventId, string eventName) + { + _simConnect.MapClientEventToSimEvent(eventId, eventName); + _simConnect.AddClientEventToNotificationGroup(groupId, eventId, false); + } + + /// + public void MapClientEventToSimEvent(int groupId, int eventId, string eventName) + { + MapClientEventToSimEvent((FsConnectEnum)groupId, (FsConnectEnum)eventId, eventName); + } + + /// + public void SetNotificationGroupPriority(Enum groupId) + { + _simConnect.SetNotificationGroupPriority(groupId, SimConnect.SIMCONNECT_GROUP_PRIORITY_HIGHEST); + } + + /// + public void SetNotificationGroupPriority(int groupId) + { + SetNotificationGroupPriority((FsConnectEnum) groupId); + } + /// - public void UpdateData(Enum id, T data, uint objectId = 1) + public void TransmitClientEvent(Enum eventId, uint dwData, Enum groupID) { - _simConnect?.SetDataOnSimObject(id, objectId, SIMCONNECT_DATA_SET_FLAG.DEFAULT, data); + _simConnect.TransmitClientEvent((uint)SIMCONNECT_SIMOBJECT_TYPE.USER, eventId, dwData, groupID, SIMCONNECT_EVENT_FLAG.DEFAULT); } /// diff --git a/src/CTrue.FsConnect/FsConnectEnum.cs b/src/CTrue.FsConnect/FsConnectEnum.cs new file mode 100644 index 0000000..3683489 --- /dev/null +++ b/src/CTrue.FsConnect/FsConnectEnum.cs @@ -0,0 +1,13 @@ +namespace CTrue.FsConnect +{ + /// + /// The is a dummy enum used when identifying definitions, events and other SimConnect artifacts that needs to be identified. + /// + public enum FsConnectEnum + { + /// + /// Base enum identifying the first enum id used by FsConnect. + /// + Base = 10_000 + } +} \ No newline at end of file diff --git a/src/CTrue.FsConnect/FsSimVarFactory.cs b/src/CTrue.FsConnect/FsSimVarFactory.cs index ef5fa8b..5204161 100644 --- a/src/CTrue.FsConnect/FsSimVarFactory.cs +++ b/src/CTrue.FsConnect/FsSimVarFactory.cs @@ -5,12 +5,18 @@ namespace CTrue.FsConnect { + internal class FsSimVarInfo + { + public FsSimVar FsSimVarId { get; set; } + public string SimVarName { get; set; } + public string SimVarNameNoWhitespace { get; set; } + } internal static class FsSimVarFactory { - private static Dictionary _enumToCodeDictionary = new Dictionary(); + private static Dictionary _enumToCodeDictionary = new Dictionary(); - static private readonly string[] _simVarNames = new string[] + private static readonly string[] _simVarNames = new string[] { "NONE", "ABSOLUTE TIME", @@ -1074,20 +1080,39 @@ internal static class FsSimVarFactory static FsSimVarFactory() { - foreach (FsSimVar simVar in Enum.GetValues(typeof(FsSimVar))) - { - _enumToCodeDictionary[simVar] = _simVarNames[(uint)simVar]; + foreach (FsSimVar simVarId in Enum.GetValues(typeof(FsSimVar))) + { + FsSimVarInfo svi = new FsSimVarInfo(); + + svi.SimVarName = _simVarNames[(uint)simVarId]; + svi.SimVarNameNoWhitespace = string.Join("", svi.SimVarName.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries)); + + _enumToCodeDictionary[simVarId] = svi; } - } + } /// /// Gets the corresponding simulation variable name to a simulation variable code. /// /// A enum. /// The corresponding sim variable name. - public static string GetSimVarCode(FsSimVar simVar) - { - return _enumToCodeDictionary[simVar]; + public static string GetSimVarCode(FsSimVar simVarId) + { + if (!_enumToCodeDictionary.ContainsKey(simVarId)) + throw new Exception("SimVar id not found."); + + return _enumToCodeDictionary[simVarId].SimVarName; } + + public static string GetSimVarCode(string simVarName) + { + // Strip any underscores + simVarName = string.Join("", simVarName.Split('_')); + + var svi = _enumToCodeDictionary.Values.ToList().Find(x => + x.SimVarNameNoWhitespace.Equals(simVarName, StringComparison.InvariantCultureIgnoreCase)); + + return svi?.SimVarName; + } } } diff --git a/src/CTrue.FsConnect/FsSystemEvent.cs b/src/CTrue.FsConnect/FsSystemEvent.cs new file mode 100644 index 0000000..1f8e9f4 --- /dev/null +++ b/src/CTrue.FsConnect/FsSystemEvent.cs @@ -0,0 +1,23 @@ +namespace CTrue.FsConnect +{ + public enum FsSystemEvent + { + AircraftLoaded, + FlightLoaded, + Paused, + Pause, + Sim, + Crashed, + ObjectAdded, + ObjectRemoved, + } + + public enum FsSimEvent + { + PauseSet, + ZuluYearSet, + ZuluDaySet, + ZuluHoursSet, + ZuluMinutesSet + } +} \ No newline at end of file diff --git a/src/CTrue.FsConnect/FsUnit.cs b/src/CTrue.FsConnect/FsUnit.cs index ffc3848..0b18ff5 100644 --- a/src/CTrue.FsConnect/FsUnit.cs +++ b/src/CTrue.FsConnect/FsUnit.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace CTrue.FsConnect { @@ -354,5 +355,6 @@ public enum FsUnit Yd3, Year, Years, + Undefined = Int32.MaxValue, }; } \ No newline at end of file diff --git a/src/CTrue.FsConnect/IFsConnect.cs b/src/CTrue.FsConnect/IFsConnect.cs index 81f2ae7..64f4ac9 100644 --- a/src/CTrue.FsConnect/IFsConnect.cs +++ b/src/CTrue.FsConnect/IFsConnect.cs @@ -105,14 +105,6 @@ public interface IFsConnect : IDisposable /// void Disconnect(); - /// - /// Registers data structures for requesting data from Flight Simulator. - /// - /// - /// A connection to Flight Simulator must have been established before registering data definitions. - /// - void RegisterDataDefinition(Enum id, List definition) where T : struct; - /// /// Displays a text in Flight Simulator. /// @@ -131,11 +123,82 @@ public interface IFsConnect : IDisposable /// true to pause, false to unpause. void Pause(bool pause); + /// + /// Registers data structures for requesting data from Flight Simulator. + /// + /// + /// The definition id to associated with the data definition. + /// + /// The definition id used to register the data definition + /// + /// A connection to Flight Simulator must have been established before registering data definitions. + /// + int RegisterDataDefinition(Enum defineId, List definition) where T : struct; + + /// + /// Registers data structures for requesting data from Flight Simulator. + /// + /// + /// The definition id to associated with the data definition. + /// + /// The definition id used to register the data definition + /// + /// A connection to Flight Simulator must have been established before registering data definitions. + /// + int RegisterDataDefinition(int defineId, List definition) where T : struct; + + /// + /// Registers data structures for requesting data from Flight Simulator. + /// + /// + /// + /// + /// + /// A connection to Flight Simulator must have been established before registering data definitions. + /// + int RegisterDataDefinition(List definition) where T : struct; + + /// + /// Registers data structures for requesting data from Flight Simulator. + /// + /// + /// The definition id to associated with the data definition. + /// The definition id used to register the data definition + /// + /// A connection to Flight Simulator must have been established before registering data definitions. + /// The data definition is based on reflection, by analyzing the type. See . + /// + int RegisterDataDefinition(Enum defineId) where T : struct; + + + /// + /// Registers data structures for requesting data from Flight Simulator. + /// + /// + /// The definition id to associated with the data definition. + /// The definition id used to register the data definition + /// + /// A connection to Flight Simulator must have been established before registering data definitions. + /// The data definition is based on reflection, by analyzing the type. See . + /// + int RegisterDataDefinition(int defineId) where T : struct; + + /// + /// Registers data structures for requesting data from Flight Simulator. + /// + /// + /// The definition id used to register the data definition + /// + /// A connection to Flight Simulator must have been established before registering data definitions. + /// The data definition is based on reflection, by analyzing the type. See . + /// + int RegisterDataDefinition() where T : struct; + /// /// Requests data on a Sim object, periodically or/and when changed. /// /// - /// + /// The definition id to associated with the data definition. /// /// /// @@ -144,22 +207,95 @@ public interface IFsConnect : IDisposable /// void RequestDataOnSimObject(Enum requestId, Enum defineId, uint objectId, FsConnectPeriod period, FsConnectDRequestFlag flags, uint interval, uint origin, uint limit); + /// + /// Requests data on a Sim object, periodically or/and when changed. + /// + /// + /// The definition id to associated with the data definition. + /// + /// + /// + /// + /// + /// + void RequestDataOnSimObject(Enum requestId, int defineId, uint objectId, FsConnectPeriod period, FsConnectDRequestFlag flags, uint interval, uint origin, uint limit); + /// /// Requests data from Flight Simulator. /// /// - /// + /// The definition id to associated with the data definition. /// Radius in meters. Should be less that 2000000 (200km). /// void RequestData(Enum requestId, Enum defineId, uint radius = 0, FsConnectSimobjectType type = FsConnectSimobjectType.User); /// - /// Updated data. + /// Requests data from Flight Simulator. + /// + /// + /// The definition id to associated with the data definition. + /// Radius in meters. Should be less that 2000000 (200km). + /// + void RequestData(Enum requestId, int defineId, uint radius = 0, FsConnectSimobjectType type = FsConnectSimobjectType.User); + + /// + /// Updates a sim object in the flight simulator. /// - /// + /// The definition id to associated with the data definition. /// /// /// - void UpdateData(Enum id, T data, uint objectId = 1); + void UpdateData(Enum defineId, T data, uint objectId = 1); + + /// + /// Updates a sim object in the flight simulator. + /// + /// + /// The definition id to associated with the data definition. + /// + /// + void UpdateData(int defineId, T data, uint objectId = 1); + + /// + /// Sets notification group priority to highest level. + /// + /// + void SetNotificationGroupPriority(Enum groupId); + + /// + /// Sets notification group priority to highest level. + /// + /// + void SetNotificationGroupPriority(int groupId); + + /// + /// Sends a client event to flight simulator. + /// + /// + /// + /// + void TransmitClientEvent(Enum eventId, uint dwData, Enum groupId); + + /// + /// Maps a client event to a sim event. + /// + /// + /// + /// + void MapClientEventToSimEvent(Enum groupId, Enum eventId, string eventName); + + /// + /// Maps a client event to a sim event. + /// + /// + /// + /// + void MapClientEventToSimEvent(int groupId, int eventId, string eventName); + + /// + /// Gets the next id, for definitions and other SimConnect artifacts that require it. + /// + /// Returns an int that can be used to identifying SimConnect artifacts, such as definitions and events. + int GetNextId(); } } \ No newline at end of file diff --git a/src/CTrue.FsConnect/SimPropertyAttribute.cs b/src/CTrue.FsConnect/SimPropertyAttribute.cs new file mode 100644 index 0000000..333916a --- /dev/null +++ b/src/CTrue.FsConnect/SimPropertyAttribute.cs @@ -0,0 +1,19 @@ +using System; +using Microsoft.FlightSimulator.SimConnect; + +namespace CTrue.FsConnect +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] + public class SimPropertyAttribute : Attribute + { + public string Name { get; set; } = null; + + public FsSimVar NameId { get; set; } = FsSimVar.None; + + public string Unit { get; set; } = null; + + public FsUnit UnitId { get; set; } = FsUnit.Undefined; + + public SIMCONNECT_DATATYPE DataType { get; set; } = SIMCONNECT_DATATYPE.INVALID; + } +} \ No newline at end of file diff --git a/src/CTrue.FsConnect/SimPropertyReflector.cs b/src/CTrue.FsConnect/SimPropertyReflector.cs new file mode 100644 index 0000000..adc57b6 --- /dev/null +++ b/src/CTrue.FsConnect/SimPropertyReflector.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.FlightSimulator.SimConnect; + +namespace CTrue.FsConnect +{ + /// + /// The analyzes a type and determines a SimVar definition based on types, property names and the use of the . + /// + public class SimPropertyReflector + { + public List GetSimProperties() where T : struct + { + List simProperties = new List(); + + Type type = typeof(T); + + var fields = type.GetFields(); + + foreach (var field in fields) + { + SimProperty simProperty = new SimProperty(); + + SimPropertyAttribute attr = field.GetCustomAttribute(); + + // + // Name + // + + simProperty.Name = GetName(field, attr); + + // + // Unit + // + + simProperty.Unit = GetUnit(field, attr); + + // + // Data type + // + + simProperty.DataType = GetDataType(field, attr); + + simProperties.Add(simProperty); + } + + return simProperties; + } + + private string GetName(FieldInfo fieldInfo, SimPropertyAttribute attr) + { + if (attr != null && attr.NameId != FsSimVar.None) + { + return FsSimVarFactory.GetSimVarCode(attr.NameId); + } + + if (!string.IsNullOrEmpty(attr?.Name)) + { + return attr.Name; + } + + // Try to lookup variations of the SimVar name using PascalCase and Under_Score + string simVarName = FsSimVarFactory.GetSimVarCode(fieldInfo.Name); + if (simVarName != null) + return simVarName; + + return fieldInfo.Name; + } + + private string GetUnit(FieldInfo fieldInfo, SimPropertyAttribute attr) + { + if (attr != null && attr.UnitId != FsUnit.Undefined) + { + return FsUnitFactory.GetUnitName(attr.UnitId); + } + + if (!string.IsNullOrEmpty(attr?.Unit)) + { + return attr.Unit; + } + + return ""; + } + + private SIMCONNECT_DATATYPE GetDataType(FieldInfo field, SimPropertyAttribute attr) + { + if (attr != null && attr.DataType != SIMCONNECT_DATATYPE.INVALID) + { + return attr.DataType; + } + + if (field.FieldType == typeof(string)) + { + MarshalAsAttribute marshallAsAttr = field.GetCustomAttribute(); + + if(marshallAsAttr != null) + { + int stringSize = marshallAsAttr.SizeConst; + + if(stringSize == 8) + return SIMCONNECT_DATATYPE.STRING8; + if (stringSize == 32) + return SIMCONNECT_DATATYPE.STRING32; + if (stringSize == 64) + return SIMCONNECT_DATATYPE.STRING64; + if (stringSize == 128) + return SIMCONNECT_DATATYPE.STRING128; + if (stringSize ==256) + return SIMCONNECT_DATATYPE.STRING256; + if (stringSize == 260) + return SIMCONNECT_DATATYPE.STRING260; + } + + // String of undefined length. Set to STRINGV + // TODO: Consider throwing exception. + return SIMCONNECT_DATATYPE.STRINGV; + } + + if (field.FieldType == typeof(float)) + return SIMCONNECT_DATATYPE.FLOAT32; + + // Default data type + return SIMCONNECT_DATATYPE.FLOAT64; + } + } +} \ No newline at end of file