diff --git a/README.md b/README.md index 06f2c0c..1776443 100644 --- a/README.md +++ b/README.md @@ -378,6 +378,19 @@ worldManager.SetTime(new DateTime(year, month, day, hour, minute, 0)); ``` +### Radio Manager + +The radio manager currently supports setting and getting COM1 and COM2 standby and active frequencies. + +```csharp + +FsConnect fsConnect = new FsConnect(); +fsConnect.Connect("RadioManagerTest", 0); +RadioManager radioManager = new RadioManager(_fsConnect); +radioManager.SetCom1ActiveFrequency(freq); + +``` + ## 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) @@ -387,6 +400,10 @@ worldManager.SetTime(new DateTime(year, month, day, hour, minute, 0)); ## Change log +## 1.3.2 + +* Added RadioManager, with support for setting and getting COM1 and COM2 standby and active frequencies. + ## 1.3.1 * [Breaking] SimProperty renamed to SimVar, minor other renames. diff --git a/src/CTrue.FsConnect.ExampleConsole/Program.cs b/src/CTrue.FsConnect.ExampleConsole/Program.cs index 78d363c..6a698d8 100644 --- a/src/CTrue.FsConnect.ExampleConsole/Program.cs +++ b/src/CTrue.FsConnect.ExampleConsole/Program.cs @@ -66,7 +66,7 @@ public static void Main(string[] args) do { - fsConnect.RequestData(Requests.PlaneInfoRequest, planeInfoDefinitionId); + fsConnect.RequestData((int)Requests.PlaneInfoRequest, planeInfoDefinitionId); cki = Console.ReadKey(); } while (cki.Key != ConsoleKey.Escape); diff --git a/src/CTrue.FsConnect.Managers.Test/AircraftManagersIntegrationTests.cs b/src/CTrue.FsConnect.Managers.Test/AircraftManagersIntegrationTests.cs new file mode 100644 index 0000000..d2ca8b4 --- /dev/null +++ b/src/CTrue.FsConnect.Managers.Test/AircraftManagersIntegrationTests.cs @@ -0,0 +1,76 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using NUnit.Framework; + +namespace CTrue.FsConnect.Managers.Test +{ + /// + /// World manager integration tests. + /// + /// + /// Load MSFS and an aircraft before running. + /// Observe that the time changes in the simulator. + /// + [TestFixture(Explicit = true)] + public class AircraftManagersIntegrationTests + { + [Test] + public void GetAircraftData() + { + // Arrange + AutoResetEvent resetEvent = new AutoResetEvent(false); + int errorCount = 0; + + FsConnect fsConnect = new FsConnect(); + fsConnect.ConnectionChanged += (sender, b) => + { + if (b) resetEvent.Set(); + }; + fsConnect.FsError += (sender, args) => + { + errorCount++; + Console.WriteLine($"Error: {args.ExceptionDescription}"); + }; + + fsConnect.Connect("AircraftManagersIntegrationTests", 0); + + bool res = resetEvent.WaitOne(2000); + if(!res) Assert.Fail("Not connected to MSFS within timeout"); + + var defId = fsConnect.RegisterDataDefinition(); + + AircraftManager aircraftManager = new AircraftManager(fsConnect, defId); + + // Act + var info = aircraftManager.Get(); + + // Assert + Assert.That(errorCount, Is.Zero); + Assert.That(info.Title, Is.Not.Empty); + Assert.That(info.Latitude, Is.Not.EqualTo(0)); + Assert.That(info.Longitude, Is.Not.EqualTo(0)); + } + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] + public struct AircraftInfo + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public String Title; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public String Category; + [SimVar(NameId = FsSimVar.PlaneLatitude, UnitId = FsUnit.Radian)] + public double Latitude; + [SimVar(NameId = FsSimVar.PlaneLongitude, UnitId = FsUnit.Radian)] + public double Longitude; + [SimVar(NameId = FsSimVar.PlaneAltitudeAboveGround, UnitId = FsUnit.Feet)] + public double AltitudeAboveGround; + [SimVar(NameId = FsSimVar.PlaneAltitude, UnitId = FsUnit.Feet)] + public double Altitude; + [SimVar(NameId = FsSimVar.PlaneHeadingDegreesTrue, UnitId = FsUnit.Degree)] + public double Heading; + [SimVar(NameId = FsSimVar.AirspeedTrue, UnitId = FsUnit.Knot)] + public double Speed; + }; +} diff --git a/src/CTrue.FsConnect.Managers.Test/RadioManagerTests.cs b/src/CTrue.FsConnect.Managers.Test/RadioManagerTests.cs new file mode 100644 index 0000000..cfd6850 --- /dev/null +++ b/src/CTrue.FsConnect.Managers.Test/RadioManagerTests.cs @@ -0,0 +1,130 @@ +using System; +using System.Threading; +using NUnit.Framework; + +namespace CTrue.FsConnect.Managers.Test +{ + /// + /// Radio manager integration tests. + /// + /// + /// Load MSFS and an aircraft before running. + /// + [TestFixture(Explicit = true)] + public class RadioManagerTests + { + private FsConnect _fsConnect; + private RadioManager _manager; + + [SetUp] + public void SetUp() + { + AutoResetEvent resetEvent = new AutoResetEvent(false); + + _fsConnect = new FsConnect(); + _fsConnect.ConnectionChanged += (sender, b) => + { + if (b) resetEvent.Set(); + }; + _fsConnect.FsError += (sender, args) => + { + Assert.Fail($"MSFS Error: {args.ExceptionDescription}"); + }; + + _fsConnect.Connect("RadioManagerIntegrationTest", 0); + + bool res = resetEvent.WaitOne(2000); + if (!res) Assert.Fail("Not connected to MSFS within timeout"); + + _manager = new RadioManager(_fsConnect); + } + + [Test] + public void SetCom1StandbyFrequency() + { + // Arrange + double freq = 124.774d; + + // Act + _manager.SetCom1StandbyFrequency(freq); + + // Assert + _manager.Update(); + Assert.That(_manager.Com1StandbyFrequency, Is.EqualTo(freq)); + } + + [Test] + public void SetCom1ActiveFrequency() + { + // Arrange + double freq = 124.774d; + + // Act + _manager.SetCom1ActiveFrequency(freq); + + // Assert + _manager.Update(); + Assert.That(_manager.Com1ActiveFrequency, Is.EqualTo(freq)); + } + + [Test] + public void SetCom2StandbyFrequency() + { + // Arrange + double freq = 124.774d; + + // Act + _manager.SetCom2StandbyFrequency(freq); + + // Assert + _manager.Update(); + Assert.That(_manager.Com2StandbyFrequency, Is.EqualTo(freq)); + } + + [Test] + public void SetCom2ActiveFrequency() + { + // Arrange + double freq = 124.773d; + + // Act + _manager.SetCom2ActiveFrequency(freq); + + // Assert + _manager.Update(); + Assert.That(_manager.Com2ActiveFrequency, Is.EqualTo(freq)); + } + + [Test] + public void SwapCom1() + { + // Arrange + double freq = 124.773d; + _manager.SetCom1StandbyFrequency(freq); + _manager.SetCom1ActiveFrequency(125.000d); + + // Act + _manager.Com1Swap(); + + // Assert + _manager.Update(); + Assert.That(_manager.Com1ActiveFrequency, Is.EqualTo(freq)); + } + + [Test] + public void SwapCom2() + { + // Arrange + double freq = 124.773d; + _manager.SetCom2StandbyFrequency(freq); + _manager.SetCom2ActiveFrequency(125.000d); + + // Act + _manager.Com2Swap(); + + // Assert + _manager.Update(); + Assert.That(_manager.Com2ActiveFrequency, Is.EqualTo(freq)); + } + } +} diff --git a/src/CTrue.FsConnect.Managers.Test/FsConnectManagersIntegrationTests.cs b/src/CTrue.FsConnect.Managers.Test/WorldManagerIntegrationTests.cs similarity index 93% rename from src/CTrue.FsConnect.Managers.Test/FsConnectManagersIntegrationTests.cs rename to src/CTrue.FsConnect.Managers.Test/WorldManagerIntegrationTests.cs index 1ecf109..831fe22 100644 --- a/src/CTrue.FsConnect.Managers.Test/FsConnectManagersIntegrationTests.cs +++ b/src/CTrue.FsConnect.Managers.Test/WorldManagerIntegrationTests.cs @@ -12,7 +12,7 @@ namespace CTrue.FsConnect.Managers.Test /// Observe that the time changes in the simulator. /// [TestFixture(Explicit = true)] - public class FsConnectManagersIntegrationTests + public class WorldManagerIntegrationTests { [Test] public void SetTime_SetsTimeInMSFS() @@ -32,7 +32,7 @@ public void SetTime_SetsTimeInMSFS() Console.WriteLine($"Error: {args.ExceptionDescription}"); }; - fsConnect.Connect("FsConnectManagersIntegrationTest", 0); + fsConnect.Connect("WorldManagerIntegrationTests", 0); bool res = resetEvent.WaitOne(2000); if(!res) Assert.Fail("Not connected to MSFS within timeout"); diff --git a/src/CTrue.FsConnect.Managers/AircraftManager.cs b/src/CTrue.FsConnect.Managers/AircraftManager.cs index cfa0109..221a608 100644 --- a/src/CTrue.FsConnect.Managers/AircraftManager.cs +++ b/src/CTrue.FsConnect.Managers/AircraftManager.cs @@ -73,6 +73,26 @@ public RequestMethod RequestMethod } } + /// + /// Creates an instance of the class. + /// + /// + public AircraftManager(IFsConnect fsConnect, int defineId) + : this(fsConnect, defineId, fsConnect.GetNextId()) + { + } + + /// + /// Creates an instance of the class. + /// + /// + /// The definition used when registering the aircraft information structure. + /// The request Id to use when requesting data using the manager. + public AircraftManager(IFsConnect fsConnect, int defineId, int requestId) + : this(fsConnect, (FsConnectEnum)defineId, (FsConnectEnum)requestId) + { + } + /// /// Creates an instance of the class. /// diff --git a/src/CTrue.FsConnect.Managers/CTrue.FsConnect.Managers.csproj b/src/CTrue.FsConnect.Managers/CTrue.FsConnect.Managers.csproj index fd34e45..18abcf0 100644 --- a/src/CTrue.FsConnect.Managers/CTrue.FsConnect.Managers.csproj +++ b/src/CTrue.FsConnect.Managers/CTrue.FsConnect.Managers.csproj @@ -11,6 +11,7 @@ - Aircraft information - Retrieving sim objects - Setting world time + - Controlling COM radios C-True true @@ -18,11 +19,11 @@ true https://github.com/c-true/FsConnect msfs flight-simulator simconnect - 1.3.1 + 1.3.2 true - - 1.3.1.0 - 1.3.1.0 + Added RadioManager for controlling COM radios. + 1.3.2.0 + 1.3.2.0 diff --git a/src/CTrue.FsConnect.Managers/RadioManager.cs b/src/CTrue.FsConnect.Managers/RadioManager.cs new file mode 100644 index 0000000..30dc2df --- /dev/null +++ b/src/CTrue.FsConnect.Managers/RadioManager.cs @@ -0,0 +1,249 @@ +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; + +namespace CTrue.FsConnect.Managers +{ + /// + /// The controls the navigation and communication radios in the current aircraft. + /// + /// + /// Note: Currently only 2 decimal frequencies, e.g. 120.25 are supported, not 3 decimal frequencies such as 128.725 + /// + public interface IRadioManager + { + /// + /// Gets the COM1 Active frequency as returned from the last call with . + /// + /// + /// The active frequency is changed by setting the COM1 standby frequency and calling + /// + double Com1ActiveFrequency { get; } + + /// + /// Gets the COM1 Standby frequency as returned from the last call with . + /// + double Com1StandbyFrequency { get; } + + /// + /// Gets the COM2 Active frequency as returned from the last call with . + /// + /// + /// The active frequency is changed by setting the COM2 standby frequency and calling + /// + double Com2ActiveFrequency { get; } + + /// + /// Gets the COM2 Standby frequency as returned from the last call with . + /// + double Com2StandbyFrequency { get; } + + /// + /// Sets the COM1 standby frequency. + /// + /// The frequency, in MHz, e.g. 124.100 + /// + /// Range: 118.000 to 135.975Mhz + /// + void SetCom1StandbyFrequency(double frequency); + + /// + /// Sets the COM1 active frequency. + /// + /// The frequency, in MHz, e.g. 124.100 + /// + /// Range: 118.000 to 135.975Mhz + /// + void SetCom1ActiveFrequency(double frequency); + + /// + /// Sets the COM2 standby frequency. + /// + /// The frequency, in MHz, e.g. 124.100 + /// + /// Range: 118.000 to 135.975Mhz + /// + void SetCom2StandbyFrequency(double frequency); + + /// + /// Sets the COM2 active frequency. + /// + /// The frequency, in MHz, e.g. 124.10 + /// + /// Range: 118.000 to 135.975Mhz + /// + void SetCom2ActiveFrequency(double frequency); + + /// + /// Swaps COMS1 active and standby frequency. + /// + void Com1Swap(); + + /// + /// Swaps COMS2 active and standby frequency. + /// + void Com2Swap(); + + /// + /// Request new radio data from MSFS. + /// + /// + /// The call is blocked until an update is returned. + /// + void Update(); + } + + /// + public class RadioManager : IRadioManager + { + private readonly IFsConnect _fsConnect; + private AutoResetEvent _resetEvent = new AutoResetEvent(false); + private int _groupId; + + private int _com1StbyRadioSetHzEventId; + private int _com1ActiveRadioSetHzEventId; + private int _com1StbySwapEventId; + + private int _com2StbyRadioSetHzEventId; + private int _com2ActiveRadioSetHzEventId; + private int _com2StbySwapEventId; + + private RadioManagerSimVars _radioManagerSimVars = new RadioManagerSimVars(); + private int _radioManagerSimVarsReqId; + private int _radioManagerSimVarsDefId; + + /// + public double Com1StandbyFrequency { get; private set; } + + /// + public double Com1ActiveFrequency { get; private set; } + + /// + public double Com2StandbyFrequency { get; private set; } + + /// + public double Com2ActiveFrequency { get; private set; } + + /// + /// Creates a new instance. + /// + /// + public RadioManager(IFsConnect fsConnect) + { + _fsConnect = fsConnect; + + RegisterEvents(); + } + + /// + public void Com1Swap() + { + _fsConnect.TransmitClientEvent(_com1StbySwapEventId, 0, _groupId); + } + + /// + public void Com2Swap() + { + _fsConnect.TransmitClientEvent(_com2StbySwapEventId, 0, _groupId); + } + + private void RegisterEvents() + { + _fsConnect.FsDataReceived += OnFsDataReceived; + _groupId = _fsConnect.GetNextId(); + + _com1StbyRadioSetHzEventId = _fsConnect.GetNextId(); + _fsConnect.MapClientEventToSimEvent(_groupId, _com1StbyRadioSetHzEventId, FsEventNameId.ComStbyRadioSetHz); + + _com1ActiveRadioSetHzEventId = _fsConnect.GetNextId(); + _fsConnect.MapClientEventToSimEvent(_groupId, _com1ActiveRadioSetHzEventId, FsEventNameId.ComRadioSetHz); + + _com1StbySwapEventId = _fsConnect.GetNextId(); + _fsConnect.MapClientEventToSimEvent(_groupId, _com1StbySwapEventId, FsEventNameId.ComStbyRadioSwitchTo); + + _com2StbyRadioSetHzEventId = _fsConnect.GetNextId(); + _fsConnect.MapClientEventToSimEvent(_groupId, _com2StbyRadioSetHzEventId, FsEventNameId.Com2StbyRadioSetHz); + + _com2ActiveRadioSetHzEventId = _fsConnect.GetNextId(); + _fsConnect.MapClientEventToSimEvent(_groupId, _com2ActiveRadioSetHzEventId, FsEventNameId.Com2RadioSetHz); + + _com2StbySwapEventId = _fsConnect.GetNextId(); + _fsConnect.MapClientEventToSimEvent(_groupId, _com2StbySwapEventId, FsEventNameId.Com2RadioSwap); + + _fsConnect.SetNotificationGroupPriority(_groupId); + + _radioManagerSimVarsReqId = _fsConnect.GetNextId(); + _radioManagerSimVarsDefId = _fsConnect.RegisterDataDefinition(); + } + + private void OnFsDataReceived(object sender, FsDataReceivedEventArgs e) + { + if (e.Data.Count == 0) return; + if (!(e.Data[0] is RadioManagerSimVars)) return; + + _radioManagerSimVars = (RadioManagerSimVars) e.Data[0]; + _resetEvent.Set(); + } + + /// + public void Update() + { + _fsConnect.RequestData(_radioManagerSimVarsReqId, _radioManagerSimVarsDefId); + bool resetRes =_resetEvent.WaitOne(10000); + + if(!resetRes) + throw new TimeoutException("Radio Manager data was not returned from MSFS within timeout"); + + Com1StandbyFrequency = new FrequencyBcd(_radioManagerSimVars.Com1StandbyFrequency).Value; + Com1ActiveFrequency = new FrequencyBcd(_radioManagerSimVars.Com1ActiveFrequency).Value; + Com2ActiveFrequency = new FrequencyBcd(_radioManagerSimVars.Com2ActiveFrequency).Value; + Com2StandbyFrequency = new FrequencyBcd(_radioManagerSimVars.Com2StandbyFrequency).Value; + } + + /// + public void SetCom1StandbyFrequency(double frequency) + { + FrequencyBcd freqBcd = new FrequencyBcd(frequency); + _fsConnect.TransmitClientEvent(_com1StbyRadioSetHzEventId, freqBcd.Bcd32Value, _groupId); + } + + /// + public void SetCom1ActiveFrequency(double frequency) + { + FrequencyBcd freqBcd = new FrequencyBcd(frequency); + _fsConnect.TransmitClientEvent(_com1ActiveRadioSetHzEventId, freqBcd.Bcd32Value, _groupId); + } + + /// + public void SetCom2StandbyFrequency(double frequency) + { + FrequencyBcd freqBcd = new FrequencyBcd(frequency); + _fsConnect.TransmitClientEvent(_com2StbyRadioSetHzEventId, freqBcd.Bcd32Value, _groupId); + } + + /// + public void SetCom2ActiveFrequency(double frequency) + { + FrequencyBcd freqBcd = new FrequencyBcd(frequency); + _fsConnect.TransmitClientEvent(_com2ActiveRadioSetHzEventId, freqBcd.Bcd32Value, _groupId); + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] + public struct RadioManagerSimVars + { + [SimVar(NameId = FsSimVar.ComActiveFrequency, UnitId = FsUnit.FrequencyBcd32, Instance = 1)] + public uint Com1ActiveFrequency; + + [SimVar(NameId = FsSimVar.ComStandbyFrequency, UnitId = FsUnit.FrequencyBcd32, Instance = 1)] + public uint Com1StandbyFrequency; + + [SimVar(NameId = FsSimVar.ComActiveFrequency, UnitId = FsUnit.FrequencyBcd32, Instance = 2)] + public uint Com2ActiveFrequency; + + [SimVar(NameId = FsSimVar.ComStandbyFrequency, UnitId = FsUnit.FrequencyBcd32, Instance = 2)] + public uint Com2StandbyFrequency; + + } + } +} \ No newline at end of file diff --git a/src/CTrue.FsConnect.Test/BcdTests.cs b/src/CTrue.FsConnect.Test/BcdTests.cs new file mode 100644 index 0000000..54e3545 --- /dev/null +++ b/src/CTrue.FsConnect.Test/BcdTests.cs @@ -0,0 +1,46 @@ +using System; +using NUnit.Framework; + +namespace CTrue.FsConnect.Test +{ + [TestFixture] + public class BcdTests + { + [Test] + public void Test() + { + /* + def to_radio_bcd16(val): + encodable = int(val * 100) + remainder = ((val * 100) - encodable) / 100.0 + return int(str(encodable), 16), round(remainder,3), val + */ + double freq = 128.775; + uint freq1 = (uint)(freq * 100); + double remainder = ((freq * 100) - freq1) / 100.0; + var bcd = Bcd.Dec2Bcd(freq1); + var freq2 = Bcd.Bcd2Dec(bcd); + + + Assert.That(freq2, Is.EqualTo(freq1)); + + //Assert.That(ToRadioBcd(128.775), Is.EqualTo(75895)); + + } + + [Test] + public void Test2() + { + double freq = 128.775; + + // Act + var bcd = Bcd.Dec2Bcd(freq); + + // Assert + var freqOutUint = Bcd.Bcd2Dec(bcd); + var freqOutDouble = (double)freqOutUint / 100; + + Assert.That(freqOutDouble, Is.EqualTo(freq)); + } + } +} \ No newline at end of file diff --git a/src/CTrue.FsConnect.Test/FrequencyBcdTest.cs b/src/CTrue.FsConnect.Test/FrequencyBcdTest.cs new file mode 100644 index 0000000..4032d5d --- /dev/null +++ b/src/CTrue.FsConnect.Test/FrequencyBcdTest.cs @@ -0,0 +1,31 @@ +using NUnit.Framework; + +namespace CTrue.FsConnect.Test +{ + [TestFixture] + public class FrequencyBcdTest + { + [Test] + public void Test() + { + FrequencyBcd freq = new FrequencyBcd(128.775); + + Assert.That(freq.Bcd16Value, Is.EqualTo(75895)); + + FrequencyBcd freq2 = new FrequencyBcd(freq.Bcd16Value); + + Assert.That(freq2.Value, Is.EqualTo(128.75)); + } + + [Test] + public void Test2([Values(124.00, 127.90)] double frequency) + { + // Act + FrequencyBcd freq = new FrequencyBcd(frequency); + FrequencyBcd freq2 = new FrequencyBcd(freq.Bcd16Value); + + // Assert + Assert.That(freq2.Value, Is.EqualTo(frequency)); + } + } +} \ No newline at end of file diff --git a/src/CTrue.FsConnect.Test/FsConnectIntegrationTest.cs b/src/CTrue.FsConnect.Test/FsConnectIntegrationTest.cs new file mode 100644 index 0000000..cbdc48d --- /dev/null +++ b/src/CTrue.FsConnect.Test/FsConnectIntegrationTest.cs @@ -0,0 +1,81 @@ +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using Microsoft.FlightSimulator.SimConnect; +using NUnit.Framework; +namespace CTrue.FsConnect.Test +{ + [TestFixture, Explicit] + public class FsConnectIntegrationTest + { + enum TestEnums + { + GroupId = 1234, + EventId = 1235, + RequestId + } + + [Test] + public void TransmitClientEvent_HeadingBugDirectionSet_SetsHeadingBugDirection() + { + // Arrange + AutoResetEvent resetEvent = new AutoResetEvent(false); + int errorCount = 0; + + FsConnect fsConnect = new FsConnect(); + fsConnect.ConnectionChanged += (sender, b) => + { + if (b) resetEvent.Set(); + }; + + fsConnect.FsError += (sender, args) => + { + errorCount++; + Console.WriteLine($"Error: {args.ExceptionDescription}"); + }; + + fsConnect.Connect("FsConnectIntegrationTest", 0); + bool res = resetEvent.WaitOne(2000); + if (!res) Assert.Fail("Not connected to MSFS within timeout"); + + var hbDef = fsConnect.RegisterDataDefinition(); + HeadingBugTest headingBugData = default; + + // Act + fsConnect.MapClientEventToSimEvent(TestEnums.GroupId, TestEnums.EventId, FsEventNameId.HeadingBugSet); + fsConnect.SetNotificationGroupPriority(TestEnums.GroupId); + uint headingValue = (uint)DateTime.Now.Second * 6; + fsConnect.TransmitClientEvent(TestEnums.EventId, (uint)headingValue, TestEnums.GroupId); + + // Assert + Assert.That(errorCount, Is.Zero, "MSFS returned errors. Check console output."); + + fsConnect.FsDataReceived += (sender, args) => + { + var data = args.Data.FirstOrDefault(); + + headingBugData = data is HeadingBugTest ? (HeadingBugTest) data : default; + + resetEvent.Set(); + }; + + fsConnect.RequestData((int)TestEnums.RequestId, hbDef); + res = resetEvent.WaitOne(2000); + if (!res) Assert.Fail("Data not returned from MSFS within timeout"); + + Assert.That(headingBugData, Is.Not.Null); + Assert.That(headingBugData.HeadingBug, Is.EqualTo(headingValue)); + + // Teardown + fsConnect?.Disconnect(); + } + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] + public struct HeadingBugTest + { + [SimVar(NameId = FsSimVar.AutopilotHeadingLockDir, UnitId = FsUnit.Degree)] + public double HeadingBug; + } +} \ No newline at end of file diff --git a/src/CTrue.FsConnect/Bcd.cs b/src/CTrue.FsConnect/Bcd.cs new file mode 100644 index 0000000..c7f1a04 --- /dev/null +++ b/src/CTrue.FsConnect/Bcd.cs @@ -0,0 +1,45 @@ +namespace CTrue.FsConnect +{ + /// + /// Handles the BCD format + /// + public static class Bcd + { + /// + /// Converts from binary coded decimal to integer + /// + /// + /// + public static uint Bcd2Dec(uint num) + { + return HornerScheme(num, 0x10, 10); + } + + /// + /// Converts from integer to binary coded decimal + /// + /// + /// + public static uint Dec2Bcd(uint num) + { + return HornerScheme(num, 10, 0x10); + } + + public static uint Dec2Bcd(double num) + { + return Dec2Bcd((uint) (num * 100)); + } + + private static uint HornerScheme(uint Num, uint Divider, uint Factor) + { + uint Remainder = 0, Quotient = 0, Result = 0; + Remainder = Num % Divider; + Quotient = Num / Divider; + + if (!(Quotient == 0 && Remainder == 0)) + Result += HornerScheme(Quotient, Divider, Factor) * Factor + Remainder; + + return Result; + } + } +} \ No newline at end of file diff --git a/src/CTrue.FsConnect/CTrue.FsConnect.csproj b/src/CTrue.FsConnect/CTrue.FsConnect.csproj index 3e1ec47..9c6f291 100644 --- a/src/CTrue.FsConnect/CTrue.FsConnect.csproj +++ b/src/CTrue.FsConnect/CTrue.FsConnect.csproj @@ -13,11 +13,11 @@ Contains SimConnect binaries, as distributed by the Flight Simulator 20202 SDK 0 true https://github.com/c-true/FsConnect msfs flight-simulator simconnect - 1.3.1 + 1.3.2 true - Support for SimVar reflection, client event enum - 1.3.1.0 - 1.3.1.0 + Added and updated some method signatures. * Added event ids for setting COM and NAV frequencies. + 1.3.2.0 + 1.3.2.0 diff --git a/src/CTrue.FsConnect/FrequencyBcd.cs b/src/CTrue.FsConnect/FrequencyBcd.cs new file mode 100644 index 0000000..5b4816b --- /dev/null +++ b/src/CTrue.FsConnect/FrequencyBcd.cs @@ -0,0 +1,52 @@ +using System; + +namespace CTrue.FsConnect +{ + public class FrequencyBcd + { + private double _value = 0; + private uint _bcd16Value = 0; + private uint _bcd32Value = 0; + + public double Value => _value; + + /// + /// Gets the BCD encoded as 32 bit. + /// + public uint Bcd32Value => _bcd32Value; + + /// + /// Gets the BCD encoded as 16 bit. + /// + public uint Bcd16Value => _bcd16Value; + + public FrequencyBcd(double freqValue) + { + _value = freqValue; + + uint encodable = (uint)((_value - 100) * 100); + double remainder = ((_value * 100) - encodable) / 100.0; + + _bcd16Value = Bcd.Dec2Bcd(encodable); + + Byte[] bytes = BitConverter.GetBytes(Convert.ToInt32(freqValue * 1000000)); + _bcd32Value = BitConverter.ToUInt32(bytes, 0); + } + + public FrequencyBcd(uint bcd32Value) + { + _bcd32Value = bcd32Value; + var freqOutUint = Bcd.Bcd2Dec(_bcd32Value); + _value = (double)freqOutUint / 10000; + } + + public FrequencyBcd(ushort bcd16Value) + { + _bcd16Value = bcd16Value; + var freqOutUint = Bcd.Bcd2Dec(_bcd16Value); + var tmp = freqOutUint + 10000; + _value = (double)tmp / 100; + //_value = Math.Round(_value * 4, MidpointRounding.ToEven) / 4; + } + } +} \ No newline at end of file diff --git a/src/CTrue.FsConnect/FsConnect.cs b/src/CTrue.FsConnect/FsConnect.cs index 2543ade..9425aa0 100644 --- a/src/CTrue.FsConnect/FsConnect.cs +++ b/src/CTrue.FsConnect/FsConnect.cs @@ -268,9 +268,9 @@ public void RequestData(Enum requestId, Enum defineId, uint radius = 0, FsConnec } /// - public void RequestData(Enum requestId, int defineId, uint radius = 0, FsConnectSimobjectType type = FsConnectSimobjectType.User) + public void RequestData(int requestId, int defineId, uint radius = 0, FsConnectSimobjectType type = FsConnectSimobjectType.User) { - _simConnect?.RequestDataOnSimObjectType(requestId, (FsConnectEnum)defineId, radius, (SIMCONNECT_SIMOBJECT_TYPE)type); + _simConnect?.RequestDataOnSimObjectType((FsConnectEnum)requestId, (FsConnectEnum)defineId, radius, (SIMCONNECT_SIMOBJECT_TYPE)type); } #endregion @@ -341,6 +341,12 @@ public void TransmitClientEvent(Enum eventId, uint dwData, Enum groupId) _simConnect.TransmitClientEvent((uint)SIMCONNECT_SIMOBJECT_TYPE.USER, eventId, dwData, groupId, SIMCONNECT_EVENT_FLAG.DEFAULT); } + /// + public void TransmitClientEvent(int eventId, uint dwData, int groupId) + { + _simConnect.TransmitClientEvent((uint)SIMCONNECT_SIMOBJECT_TYPE.USER, (FsConnectEnum)eventId, dwData, (FsConnectEnum)groupId, SIMCONNECT_EVENT_FLAG.DEFAULT); + } + /// public void SetText(string text, int duration) { diff --git a/src/CTrue.FsConnect/FsEventNameId.cs b/src/CTrue.FsConnect/FsEventNameId.cs index 3f2821a..9f67a07 100644 --- a/src/CTrue.FsConnect/FsEventNameId.cs +++ b/src/CTrue.FsConnect/FsEventNameId.cs @@ -3957,6 +3957,38 @@ public enum FsEventNameId /// Show or hide multi-player race results. /// ToggleRaceresultsWindow, + /// + /// Set COM1 Standby frequency. + /// + ComRadioSetHz, + /// + /// Set COM2 Standby frequency. + /// + Com2RadioSetHz, + /// + /// Set NAV1 Standby frequency. + /// + Nav1RadioSetHz, + /// + /// Set NAV2 Standby frequency. + /// + Nav2RadioSetHz, + /// + /// Set COM1 Standby frequency. + /// + ComStbyRadioSetHz, + /// + /// Set COM2 Standby frequency. + /// + Com2StbyRadioSetHz, + /// + /// Set NAV1 Standby frequency. + /// + Nav1StbyRadioSetHz, + /// + /// Set NAV2 Standby frequency. + /// + Nav2StbyRadioSetHz, }; /// @@ -4484,7 +4516,7 @@ internal static class FsEventNameLookup "AVIONICS_MASTER_SET", "TOGGLE_AVIONICS_MASTER", "COM_STBY_RADIO_SET", - "COM_STBY_RADIO_SWITCH_TO", + "COM_STBY_RADIO_SWAP", "COM_RADIO_FRACT_DEC_CARRY", "COM_RADIO_FRACT_INC_CARRY", "COM2_RADIO_WHOLE_DEC", @@ -4952,6 +4984,14 @@ internal static class FsEventNameLookup "MULTIPLAYER_VOICE_CAPTURE_STOP", "MULTIPLAYER_BROADCAST_VOICE_", "TOGGLE_RACERESULTS_WINDOW", + "COM_RADIO_SET_HZ", + "COM2_RADIO_SET_HZ", + "NAV1_RADIO_SET_HZ", + "NAV2_RADIO_SET_HZ", + "COM_STBY_RADIO_SET_HZ", + "COM2_STBY_RADIO_SET_HZ", + "NAV1_STBY_RADIO_SET_HZ", + "NAV2_STBY_RADIO_SET_HZ" }; /// diff --git a/src/CTrue.FsConnect/FsSimVar.cs b/src/CTrue.FsConnect/FsSimVar.cs index c03f3fc..458f5ef 100644 --- a/src/CTrue.FsConnect/FsSimVar.cs +++ b/src/CTrue.FsConnect/FsSimVar.cs @@ -298,7 +298,13 @@ public enum FsSimVar AutopilotMaster, AutopilotWingLeveler, AutopilotNav1Lock, + /// + /// Heading mode active + /// AutopilotHeadingLock, + /// + /// Selected heading + /// AutopilotHeadingLockDir, AutopilotAltitudeLock, AutopilotAltitudeLockVar, diff --git a/src/CTrue.FsConnect/IFsConnect.cs b/src/CTrue.FsConnect/IFsConnect.cs index 13a0e0b..854964b 100644 --- a/src/CTrue.FsConnect/IFsConnect.cs +++ b/src/CTrue.FsConnect/IFsConnect.cs @@ -236,7 +236,7 @@ public interface IFsConnect : IDisposable /// 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); + void RequestData(int requestId, int defineId, uint radius = 0, FsConnectSimobjectType type = FsConnectSimobjectType.User); /// /// Updates a sim object in the flight simulator. @@ -276,6 +276,14 @@ public interface IFsConnect : IDisposable /// void TransmitClientEvent(Enum eventId, uint dwData, Enum groupId); + /// + /// Sends a client event to flight simulator. + /// + /// + /// + /// + void TransmitClientEvent(int eventId, uint dwData, int groupId); + /// /// Maps a client event to a sim event. /// diff --git a/src/CTrue.FsConnect/SimVarReflector.cs b/src/CTrue.FsConnect/SimVarReflector.cs index 8ace400..651f0da 100644 --- a/src/CTrue.FsConnect/SimVarReflector.cs +++ b/src/CTrue.FsConnect/SimVarReflector.cs @@ -110,6 +110,7 @@ private string GetUnit(FieldInfo fieldInfo, SimVarAttribute attr) private SIMCONNECT_DATATYPE GetDataType(FieldInfo field, SimVarAttribute attr) { + // Always return the specified data type if provided if (attr != null && attr.DataType != SIMCONNECT_DATATYPE.INVALID) { return attr.DataType; @@ -148,6 +149,12 @@ private SIMCONNECT_DATATYPE GetDataType(FieldInfo field, SimVarAttribute attr) if (field.FieldType == typeof(bool)) return SIMCONNECT_DATATYPE.INT32; + if (field.FieldType == typeof(uint) || field.FieldType == typeof(int)) + return SIMCONNECT_DATATYPE.INT32; + + if (field.FieldType == typeof(ulong) || field.FieldType == typeof(long)) + return SIMCONNECT_DATATYPE.INT64; + // Default data type return SIMCONNECT_DATATYPE.FLOAT64; }