Using the SimConnect SDK - A SimVar Request handler (and potential SDK replacement)

Master branch updated to set the Data Type to String (if not already defined) when Units are declared as a String (Units are set to NULL for Strings earlier in the code - patch just checks for that).

                default:
                    if (string.IsNullOrEmpty(unit))
                        simConnect.RegisterDataDefineStruct<SimVarString>(simReq.DefID);
                    else
                        simConnect.RegisterDataDefineStruct<double>(simReq.DefID); // We'll presume default values being requested are numeric
                    break;

hI, good to know you have the global solution, not a workaround as I used. Meanwhile I will add these lines you posted. Thks

If you used the Git Clone or Visual Studio download options from GitHub, you can use the Pull feature of Git in Visual Studio to pull down the latest changes, so you don’t need to apply the changes locally yourself

Not experience with the Github pull but it seems easy.
Meanwhile here my code with you help.

foreach (var toConnect in simConnectProperties)
{

            values = toConnect.Value.Split(new char[] { ',' });
            if (values[1] != "string")
            {
                sim.AddToDataDefinition((DUMMYENUM)toConnect.Key, values[0], values[1], SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                sim.RegisterDataDefineStruct<double>((DUMMYENUM)toConnect.Key);
            }
            else
            {
                // neste momento apanha as strings porque são contveridas em null
                sim.AddToDataDefinition((DUMMYENUM)toConnect.Key, values[0], null, SIMCONNECT_DATATYPE.STRINGV, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                sim.RegisterDataDefineStruct<string256Struct>((DUMMYENUM)toConnect.Key);
            }
        }

here I receive the string
case (8):
Var8 = ((double)data.dwData[0]).ToString();
break;
case (9):
Var9 = ((string256Struct)data.dwData[0]).strVal;
break;

finaly the struct

private struct string256Struct
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string strVal;
}

It was quite helpful your comments
Thks
Note - I don’t understand yet this FS Editor

That looks fine if you’re only going to fetch numeric and string values.

Just wrote a Unit Test to fetch every SimVar and discovered around 10% (90 SimVars) fail, mainly because they expect a non-standard result to be registered, such as a structure.

Yes, you are right! As I am beginning to work with the variables I will find those gaps now or later. 90 is too much but the structures will be a few, I think. Interesting your tests!

Hi DragonIard,
it would help me a lot, if you could kindly provide a small simple programme which just uses your SimConnectHandler_Test and these three methods:
ConnectUseLocalServerConfig_Test
RequestSimVar_Test_Once
Disconnect_Test
Thanks in advance!

Hi @SteinGerste1348 ,

I’m a little busy with work just now, I’ll try and create a small project to consume the helper as an example nearer to, or over the weekend.

Dragonlaird

Hi DragonIard,
thanks for answering!
No hurry at all. I continued to dive into Visual Studio and have already found out the implicit “Test”-category of Visual Studio. I am getting closer to not just understand your code but also will be able to get parts of it running in own mini test programmes :slight_smile:

I’ve just updated the code with a breaking change. I’ve renamed the SimConnectHandler to SimConnectHelper, as it makes more sense when writing your own code to use it as follows:

SimConnectHelper.Connect();

I’ve also added the initial code to disable AI for submitted values, although this isn’t fully tested yet as I’m simply too busy for the next few days.

For info, there are 3 projects in the solution:

  1. SimConnectHelper - This is the actual code you will reference/use in your own projects
  2. SimConnectHandler_Demo - A simple form that uses the SimConnectHelper as an example
  3. SimConnectHandler_Tests - A collection of Unit Tests intended to automate various test scenarios using the SimConnectHelper
2 Likes

Hi @SteinGerste1348 ,

As I suspect you’ve realised, you wouldn’t call the methods you mentioned as they are Unit Tests, used for automated testing of the main project (SimConnectHelper).

To use the project yourself in your own code, you would need a copy of the SimConnectHelper project (the other 2 projects are just for testing and demo’ing the main project).

Within your own project, create a reference to the SimConnectHelper project (so your code can find and use it), then all you need to do is call the relevant methods of the helper in your code.

You don’t need to create an instance of the helper - it’s always available.

So, to connect to MSFS 2020 running on the same PC, you would just write in your own code:

SimConnectHelper.Connect();

There’s a lot you can do with the helper, so please read the full tutorial (above), review the documentation (I will be adding more to the project Wiki), follow the examples (in both the Demo and Unit Test projects) and experiment yourself, I often find learning is best achieved by experimenting.

Dragonlaird

Hello Dragonlaird :grinning:
Im using VS2019 C#.
I think I need the last little kick to understand how your SimconnecHelper is working.
I have added SimconnectHelper to my proj. and added “using SimConnectHelper;”
and “using SimConnectHelper.Common;” to my MainWindow.cs file - so far so good…
Now… can you support me with the function() that set my public variable “public double planeHeading;” to the heading of my user plane in FS2020 ??

Be safe and thanks
FAR

Apologies for not responding sooner, I’ve been away, with no access to Internet. I’m currently at work but I’ll look into this as soon as I finish tonight

No Rush… this is a hobby :wink: - thanks

I’ve not tested this as I’m having to download a 40Gb update - but the code compiles without error:

using SimConnectHelper.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SimConnectHandler_Demo
{
    public class FARyoufly_Sample
    {
		private IDictionary latestResults = new Dictionary();

		public void Main()
		{
			SimConnectHelper.SimConnectHelper.SimError += SimConnect_Error;
			SimConnectHelper.SimConnectHelper.SimConnected += SimConnect_Connection;
			SimConnectHelper.SimConnectHelper.SimData += SimConnect_DataReceived;

			SimConnectHelper.SimConnectHelper.Connect();

			var simVarName = "GPS GROUND TRUE HEADING"; // Or "HEADING INDICATOR", "PLANE HEADING DEGREES GYRO", "INDUCTOR COMPASS HEADING REF", "PLANE HEADING DEGREES MAGNETIC", "PLANE HEADING DEGREES TRUE"

			var variable = new SimConnectVariable
			{
				Name = simVarName,
				Unit = "radians"
			};
			var requestID = SimConnectHelper.SimConnectHelper.GetSimVar(variable, 50); // Ask SimConnect to send the value every 50ms

			for (var i = 0; i < 100; i++)
			{
				// Latest value of the SimVar can be retrieved by calling method below
				var planeHeading = GetSimVarValue(simVarName);
				// If value hasn't been returned yet, it will default to zero
				Console.WriteLine($"{i:000}: Heading: {planeHeading}");
				Thread.Sleep(10); // Wait for 10ms before checking again
			}
		}

		private double GetSimVarValue(string simVarName)
        {
			if (latestResults.ContainsKey(simVarName))
				return (double)latestResults[simVarName].Value;
			return 0;
        }

		private void SimConnect_DataReceived(object sender, SimConnectVariableValue e)
		{
			lock (latestResults)
			{
				if (latestResults.ContainsKey(e.Request.Name))
					latestResults[e.Request.Name] = e;
				else
					latestResults.Add(e.Request.Name, e);
			}
		}

		private void SimConnect_Error(object sender, ExternalException e)
		{
			throw new NotImplementedException();
		}

		private void SimConnect_Connection(object sender, bool e)
		{
            if (e)
            {
				// SimConnect has connected successfully
            }
            else
            {
				// SimConnect has disconnected
			}
		}
	}
}

Thanks Dragonlaird…
So the “variable” is my variable that now hold the heading - Right ?

Do i need wo make this code for every variable I want ? …

Thanks a lot

No, variable is the SimVar Request you want to fetch the value for, it can be re-used after you’ve submitted the request, MSFS will remember which SimVars you have asked for.

No, in fact really, you don’t need to create any variables, as the Dictionary:

latestResults

Contains the values for any SimVar you request (assuming the values you request are all of type double - most are).

So for every SimVar you want to request, set up “variable” as shown in the code above, then call:

SimConnectHelper.SimConnectHelper.GetSimVar(variable, 50);

The request will be processed by MSFS and the value returned (every 50ms, or whatever speed you want the value to update).

You can then access the latest returned values anywhere in your code by calling:

myValue = GetSimVarValue(simVarName);

Where simVarName is a string containing the name of the SimVar you originally requested (e.g. “GPS GROUND TRUE HEADING”) and myValue will contain the latest value received from MSFS.

A simple way to use this would be to create a method that runs at startup and sends all your SimVar requests as soon as your app loads, then in your code where you need the value (e.g. the heading), you would simply write something like:

var heading = GetSimVarValue("GPS GROUND TRUE HEADING");

Great - thanks…
I played a little with you suggested code…
But my Code comes up with a error… I have installed requested NuGet packed.
In this part it comes with an error… IDictionary does not contain a definition for “ContainsKey”.
if (latestResults.ContainsKey(simVarName))
return (double)latestResults[simVarName].Value;

I noob but learning fast :wink:
Appreciate your help
Frank

That’s a simple fix - In the line:

private IDictionary latestResults = new Dictionary();

just change it to:

private Dictionary latestResults = new Dictionary();

Hi Dragon !
I can’t make this line work… ‘Dictionary does not contain a definition for ContainsKey’

Best
Frank