Skip to content

Commit

Permalink
Simple client event access / Aspect based data definitions (#12)
Browse files Browse the repository at this point in the history
- Fixes #11 by providing simple access to setting up client events
- Provides support for aspect/reflection based registering of data definitions through the SimProperty attribute.
- WorldManager support class for setting world time.
  • Loading branch information
TimianHeber authored Apr 5, 2021
1 parent 25cc026 commit 6018e0b
Show file tree
Hide file tree
Showing 20 changed files with 849 additions and 109 deletions.
116 changes: 84 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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](
Expand Down Expand Up @@ -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
{
Expand All @@ -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<SimProperty> definition = new List<SimProperty>();

// Consult the SDK for valid sim variable names, units and whether they can be written to.
Expand All @@ -89,12 +93,37 @@ fsConnect.RegisterDataDefinition<PlaneInfoResponse>(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<PlaneInfo>();

```


# Example

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -194,32 +225,26 @@ namespace FsConnectTest
fsConnect.FsDataReceived += HandleReceivedFsData;

List<SimProperty> definition = new List<SimProperty>();

// 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<PlaneInfoResponse>();

// 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<PlaneInfoResponse>(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();
}

private static void HandleReceivedFsData(object sender, FsDataReceivedEventArgs e)
{
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");
}
}
}
Expand All @@ -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<PlaneInfoResponse>(fsConnect, Definitions.PlaneInfo, Requests.AircraftManager);
Expand All @@ -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<PlaneInfoResponse>(_fsConnect, Definitions.PlaneInfo, Requests.SimObjects);

Expand All @@ -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.
46 changes: 20 additions & 26 deletions src/CTrue.FsConnect.ExampleConsole/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@

namespace FsConnectTest
{
public enum Definitions
{
PlaneInfo = 0
}

public enum Requests
{
PlaneInfoRequest = 0
Expand All @@ -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
Expand Down Expand Up @@ -59,23 +60,16 @@ public static void Main(string[] args)

fsConnect.FsDataReceived += HandleReceivedFsData;

List<SimProperty> definition = new List<SimProperty>();
int planeInfoDefinitionId = fsConnect.RegisterDataDefinition<PlaneInfoResponse>();

// 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<PlaneInfoResponse>(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();
}

Expand All @@ -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");
}
}
}
Expand Down
10 changes: 7 additions & 3 deletions src/CTrue.FsConnect.Managers/CTrue.FsConnect.Managers.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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
</Description>
<Authors>C-True</Authors>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageLicenseFile/>
<PackageRequireLicenseAcceptance/>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<RepositoryUrl>https://github.com/c-true/FsConnect</RepositoryUrl>
<PackageTags>msfs flight-simulator simconnect</PackageTags>
<Version>1.2.0</Version>
Expand Down Expand Up @@ -42,4 +42,8 @@
</Reference>
</ItemGroup>

<ItemGroup>
<None Include="licenses\LICENSE.txt" Pack="true" PackagePath="$(PackageLicenseFile)" />
</ItemGroup>

</Project>
38 changes: 38 additions & 0 deletions src/CTrue.FsConnect.Managers/WorldManager.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
9 changes: 9 additions & 0 deletions src/CTrue.FsConnect.Managers/licenses/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -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.
29 changes: 29 additions & 0 deletions src/CTrue.FsConnect.Test/CTrue.FsConnect.Test.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net45</TargetFramework>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NUnit" Version="3.13.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\CTrue.FsConnect\CTrue.FsConnect.csproj" />
</ItemGroup>

<ItemGroup>
<Reference Include="Microsoft.FlightSimulator.SimConnect">
<HintPath>..\Dependencies\SimConnect\lib\net40\Microsoft.FlightSimulator.SimConnect.dll</HintPath>
</Reference>
</ItemGroup>

<ItemGroup>
<None Include="..\Dependencies\SimConnect\build\SimConnect.dll" Visible="false">
<Link>%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Loading

0 comments on commit 6018e0b

Please sign in to comment.