forked from theomessin/jetbridge
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
9 changed files
with
697 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 16 | ||
VisualStudioVersion = 16.0.31129.286 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TheoMessin.JetBridge.Client", "TheoMessin.JetBridge.Client\TheoMessin.JetBridge.Client.csproj", "{966BB3B6-3B9A-4FDF-A794-AEE6F63FE7AF}" | ||
EndProject | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TheoMessin.JetBridge.Example", "TheoMessin.JetBridge.Example\TheoMessin.JetBridge.Example.csproj", "{C006ED21-7035-4092-9A95-1B1777BE48B8}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|x64 = Debug|x64 | ||
Release|x64 = Release|x64 | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{966BB3B6-3B9A-4FDF-A794-AEE6F63FE7AF}.Debug|x64.ActiveCfg = Debug|x64 | ||
{966BB3B6-3B9A-4FDF-A794-AEE6F63FE7AF}.Debug|x64.Build.0 = Debug|x64 | ||
{966BB3B6-3B9A-4FDF-A794-AEE6F63FE7AF}.Release|x64.ActiveCfg = Release|x64 | ||
{966BB3B6-3B9A-4FDF-A794-AEE6F63FE7AF}.Release|x64.Build.0 = Release|x64 | ||
{C006ED21-7035-4092-9A95-1B1777BE48B8}.Debug|x64.ActiveCfg = Debug|x64 | ||
{C006ED21-7035-4092-9A95-1B1777BE48B8}.Debug|x64.Build.0 = Debug|x64 | ||
{C006ED21-7035-4092-9A95-1B1777BE48B8}.Release|x64.ActiveCfg = Release|Any CPU | ||
{C006ED21-7035-4092-9A95-1B1777BE48B8}.Release|x64.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {EBDF73D0-D7A6-4049-9DAB-B71F544C6D80} | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> | ||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Downlink/@EntryIndexedValue">True</s:Boolean> | ||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Messin/@EntryIndexedValue">True</s:Boolean> | ||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Theo/@EntryIndexedValue">True</s:Boolean> | ||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Uplink/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,268 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Runtime.InteropServices; | ||
using System.Threading; | ||
using Microsoft.FlightSimulator.SimConnect; | ||
|
||
namespace TheoMessin.JetBridge.Client | ||
{ | ||
/// <summary> | ||
/// Client for JetBridge | ||
/// </summary> | ||
public class JetBridgeClient | ||
{ | ||
#region Fields | ||
|
||
/// <summary> | ||
/// SimConnect instance | ||
/// </summary> | ||
readonly SimConnect _simConnect; | ||
|
||
/// <summary> | ||
/// Generate random IDs | ||
/// </summary> | ||
readonly Random _idGenerator = new Random(); | ||
|
||
/// <summary> | ||
/// Map of request id to response | ||
/// </summary> | ||
readonly Dictionary<int, PacketResponse> _responses = new Dictionary<int, PacketResponse>(); | ||
|
||
#endregion | ||
|
||
#region Constructors / Finalizers | ||
|
||
/// <summary> | ||
/// Constructor | ||
/// </summary> | ||
/// <param name="simConnect">SimConnect instance to communicate with</param> | ||
public JetBridgeClient(SimConnect simConnect) | ||
{ | ||
_simConnect = simConnect; | ||
} | ||
|
||
#endregion | ||
|
||
#region Enums | ||
|
||
/// <summary> | ||
/// Client data areas | ||
/// </summary> | ||
enum ClientDataAreas | ||
{ | ||
/// <summary> | ||
/// Data returned from JetBridge | ||
/// </summary> | ||
PublicDownlinkArea = 5321, | ||
|
||
/// <summary> | ||
/// Data sent to JetBridge | ||
/// </summary> | ||
PublicUplinkArea = 5322, | ||
}; | ||
|
||
/// <summary> | ||
/// Data definitions | ||
/// </summary> | ||
enum ClientDataDefinitions | ||
{ | ||
/// <summary> | ||
/// <see cref="Packet"/> data type | ||
/// </summary> | ||
PacketDefinition = 5321, | ||
}; | ||
|
||
/// <summary> | ||
/// Request identifiers | ||
/// </summary> | ||
enum DataRequest | ||
{ | ||
/// <summary> | ||
/// Not currently used | ||
/// </summary> | ||
// ReSharper disable once UnusedMember.Local | ||
UplinkRequest = 5321, | ||
|
||
/// <summary> | ||
/// Return data from JetBridge | ||
/// </summary> | ||
DownlinkRequest = 5322, | ||
}; | ||
|
||
#endregion | ||
|
||
#region Methods | ||
|
||
/// <summary> | ||
/// Connect to SimConnect | ||
/// </summary> | ||
/// <remarks> | ||
/// Call this after SimConnect has connected to the simulator | ||
/// </remarks> | ||
public void Connect() | ||
{ | ||
_simConnect.OnRecvClientData += OnRecvClientData; | ||
_simConnect.AddToClientDataDefinition(ClientDataDefinitions.PacketDefinition, 0, (uint)Marshal.SizeOf<Packet>(), 0, 0); | ||
_simConnect.RegisterStruct<SIMCONNECT_RECV_CLIENT_DATA, Packet>(ClientDataDefinitions.PacketDefinition); | ||
_simConnect.MapClientDataNameToID(ChannelNames.PublicDownlinkChannel, ClientDataAreas.PublicDownlinkArea); | ||
_simConnect.MapClientDataNameToID(ChannelNames.PublicUplinkChannel, ClientDataAreas.PublicUplinkArea); | ||
_simConnect.RequestClientData(ClientDataAreas.PublicDownlinkArea, DataRequest.DownlinkRequest, ClientDataDefinitions.PacketDefinition, SIMCONNECT_CLIENT_DATA_PERIOD.ON_SET, SIMCONNECT_CLIENT_DATA_REQUEST_FLAG.CHANGED, 0, 0, 0); | ||
} | ||
|
||
|
||
/// <summary> | ||
/// Send a command to JetBridge | ||
/// </summary> | ||
/// <param name="command">Command to send</param> | ||
/// <returns>Response to the command, or <c>null</c> if no response was received</returns> | ||
public string SendCommand(string command) | ||
{ | ||
return SendCommand(command, TimeSpan.FromSeconds(1)); | ||
} | ||
|
||
/// <summary> | ||
/// Send a command to JetBridge | ||
/// </summary> | ||
/// <param name="command">Command to send</param> | ||
/// <param name="timeout">Time to wait for a response</param> | ||
/// <returns>Response to the command, or <c>null</c> if no response was received</returns> | ||
public string SendCommand(string command, TimeSpan timeout) | ||
{ | ||
var p = new Packet | ||
{ | ||
id = _idGenerator.Next(), | ||
data = command | ||
}; | ||
|
||
using (var response = new PacketResponse()) | ||
{ | ||
_responses.Add(p.id, response); | ||
_simConnect.SetClientData(ClientDataAreas.PublicUplinkArea, ClientDataDefinitions.PacketDefinition, 0, 0, p); | ||
var received = response.Received.WaitOne(timeout); | ||
_responses.Remove(p.id); | ||
return received ? response.Response : null; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Handle received data | ||
/// </summary> | ||
/// <param name="sender">SimConnect client</param> | ||
/// <param name="data">Received data</param> | ||
void OnRecvClientData(SimConnect sender, SIMCONNECT_RECV_CLIENT_DATA data) | ||
{ | ||
if (data.dwDefineID == (uint)ClientDataDefinitions.PacketDefinition) | ||
{ | ||
if (data.dwData[0] is Packet p) | ||
{ | ||
if (_responses.TryGetValue(p.id, out var response)) | ||
{ | ||
response.Response = p.data; | ||
} | ||
} | ||
} | ||
} | ||
|
||
#endregion | ||
|
||
#region Structs | ||
|
||
/// <summary> | ||
/// Wire format of packet data | ||
/// </summary> | ||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] | ||
public struct Packet | ||
{ | ||
/// <summary> | ||
/// Packet Id | ||
/// </summary> | ||
public int id; | ||
|
||
/// <summary> | ||
/// Packet data | ||
/// </summary> | ||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] | ||
public string data; | ||
} | ||
|
||
#endregion | ||
|
||
#region Nested Types | ||
|
||
/// <summary> | ||
/// Registered channel names | ||
/// </summary> | ||
static class ChannelNames | ||
{ | ||
#region Fields | ||
|
||
/// <summary> | ||
/// Receive data from JetBridge | ||
/// </summary> | ||
public const string PublicDownlinkChannel = "theomessin.jetbridge.downlink"; | ||
|
||
/// <summary> | ||
/// Send data to JetBridge | ||
/// </summary> | ||
public const string PublicUplinkChannel = "theomessin.jetbridge.uplink"; | ||
|
||
#endregion | ||
} | ||
|
||
/// <summary> | ||
/// Hold a response from JetBridge | ||
/// </summary> | ||
class PacketResponse : IDisposable | ||
{ | ||
#region Fields | ||
|
||
/// <summary> | ||
/// Set when <see cref="Response"/> is set | ||
/// </summary> | ||
readonly ManualResetEvent _done = new ManualResetEvent(false); | ||
|
||
/// <summary> | ||
/// Backing field for <see cref="Response"/> | ||
/// </summary> | ||
string _response; | ||
|
||
#endregion | ||
|
||
#region Properties | ||
|
||
/// <summary> | ||
/// Response from JetBridge | ||
/// </summary> | ||
public string Response | ||
{ | ||
get => _response; | ||
set | ||
{ | ||
_response = value; | ||
_done.Set(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Handle that is set when <see cref="Response"/> is set | ||
/// </summary> | ||
public WaitHandle Received => _done; | ||
|
||
#endregion | ||
|
||
#region Methods | ||
|
||
/// <summary> | ||
/// Free up resources | ||
/// </summary> | ||
public void Dispose() | ||
{ | ||
_done?.Dispose(); | ||
} | ||
|
||
#endregion | ||
} | ||
|
||
#endregion | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
CSharp/TheoMessin.JetBridge.Client/TheoMessin.JetBridge.Client.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netstandard2.0</TargetFramework> | ||
<Platforms>x64</Platforms> | ||
<GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
<Description> | ||
<![CDATA[ JetBridge Client | ||
.NET Client to use SimConnect with the JetBridge module for Microsoft Flight Simulator | ||
]]> | ||
</Description> | ||
<Version>0.1.0</Version> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup> | ||
<MsfsSdkLocation Condition="'$(MsfsSdkLocation)'==''">C:\MSFS SDK</MsfsSdkLocation> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Reference Include="Microsoft.FlightSimulator.SimConnect"> | ||
<HintPath>$(MsfsSdkLocation)\SimConnect SDK\lib\managed\Microsoft.FlightSimulator.SimConnect.dll</HintPath> | ||
</Reference> | ||
</ItemGroup> | ||
|
||
</Project> |
Oops, something went wrong.