Jump to content
The simFlight Network Forums

Added Support for LVars/HVars Access in MSFS via John Dowson's WASM Module.


Recommended Posts

MSFSVariableServices

Background

A new class called MSFSVariableServices has been added to the FSUIPCClient dll from Version 3.2.1-Beta. 

This module allows direct access to LVars and HVars for MSFS. Other Flight Sims (e.g. P3D) are NOT supported. 

It uses John Dowson's WASM module for MSFS which must be installed in your MSFS and will also need to be installed in your users' MSFS. This module is installed by the FSUIPC7 installer.

This facility is completely independent of FSUIPC. When using this feature in the FSUIPCClient dll you will not need a connection to FSUIPC. It works separately.

Note that the FSUIPCConnection.ReadLVar() and WriteLVar() are NOT using this new WASM module. These still use the legacy LVAR access provided by the FSUIPC Offset 0x0D70. You can still use these methods when FSUIPC7 supports this, however this method of LVAR access has historically been slow (read one variable per Process()) and will continue to be.

The new MSFSVariableServices offers significant performance advantages over the old ReadLVar() method.


Requirements

As the developer you will also need John's FSUIPC_WAPID.dll. This can be found in latest the WASM package which can be downloaded from this thread:

https://forum.simflight.com/topic/92031-wasm-module-client-api-for-msfs-fsuipc7-now-available-for-developers-and-advanced-users-only/

e.g. the latest version at the time of this post is FSUIPC-WASMv0.4.10.zip.

Inside the Zip file, the DLL can be found in \FSUIPC_WAPI\dll

This dll must be distributed with your application and should be located in the same folder as your .exe and FSUIPCClient.dll.

In Visual Studio you can add this DLL to your project as a normal project item and set the property "Copy to Output Directory" to "Copy if Newer".

You CANNOT add this dll as a reference to your project as it's an unmanged C-style DLL.


How to Use:

Setup:

First create an instance of the MSFSVariableServices class:

MSFSVariableServices VS = new MSFSVariableServices();

I recommend handling the OnLogEntryReceived event so you can receive the log messages from the module. This will give you status updates, errors etc. What you do with the messages is up to you. In the example below I'm writing the log entries to a listbox, but you could show them in the Debug output window or write to your own log.

VS.OnLogEntryReceived += VS_OnLogEntryReceived;

private void VS_OnLogEntryReceived(object sender, LogEventArgs e)
{
    this.lstLog.Items.Add(e.LogEntry);
}

Next you need to call Init(). This will attempt a connection to the flight sim via SimConnect. The result of this will be returned as a log entry. You need to pass in the handle of the main window in your application:

e.g: Assuming the code is in the code behind the main form/window:

WinForms:

VS.Init(this.Handle);

WPF:

VS.Init(new WindowInteropHelper(this).Handle);

Configuration:

There are a number of properties to control how the module behaves. The default values for these properties are good for most cases.

VS.LogLevel 

Defines what kinds of events (if any) are logged via the logging system. Values can be:

  •         DISABLE_LOG
  •         LOG_LEVEL_INFO
  •         LOG_LEVEL_BUFFER
  •         LOG_LEVEL_DEBUG
  •         LOG_LEVEL_TRACE
  •         ENABLE_LOG

 

VS.LVARUpdateFrequency

This is the number of times per second (Hz) that the WASM Module will get the latest LVar values. This can be used to limit the impact of the WASM module if fast updates are not required. The default is 6Hz. You can slow this down or speed it up depending on how frequently your application will be requesting updates. 

 

VS.SimConfigConnection

By default the MSFSVariableServices will connect to the local instance of SimConnect. If you have other instances configured on your machine (e.g. connections to remote PCs running MSFS) you can change this property to use them.

 

Starting the module:

After iniializing and setting your configuration, you must then call Start() to tell the WASM module to start receiving data.

VS.Start();

NOTE: After calling Start() it will take the WASM module a few seconds to collect the data from the flight sim and do its housekeeping. This means that LVars and HVars may not be instantly available, so don't attempt to read variables until a few seconds have passed.

If you ever want to pause the data transfer you can call 

VS.Stop();

No more data updates will be received by the WASM module until you call Start() again.

 

Accessing LVars:

The module automatically discovers the loaded LVars. These are located in the LVars property and are stored as a collection of FsLVar objects.

You can access the individual FsLVar objects in three ways:

1. Iterate through all of them:

foreach (FsLVar lvar in VS.LVars)
{
   // Do something with each LVar... e.g.
   CheckLVar(lvar);
}

2. Get a specific LVar by name:

FsLVar myLVar = VS.LVars["B747_8_MFD_Range"];

3. Get a specific LVar by ID

FsLVar myLVar = VS.LVars[26];

Getting names of all LVars

You can find a List<string> of all known LVar names at:

VS.LVars.Names

Checking if LVars exist

To find if an LVar exists by ID or Name use:

if (VS.LVars.Exists(26))
{
    // Exists
}

if (VS.LVars.Exists("B747_8_MFD_Range"))
{
    // Exists
}

Counting number of LVars

You can get the number of LVars with:

VA.LVars.Count

 


Accessing HVars

HVars are stored as a collection in the property

VS.HVars

This collection works in exactly the same way as LVars above.


Refreshing the LVAR values:

To get the latest values call:

VS.RefreshData();

 


Using the LVar values:

Each FsLVar object has a Value property. This is a double type as all LVars are stored as doubles within the Flight Sim.

e.g. To get the value for B747_8_MFD_Range use:

double range = VS.LVars["B747_8_MFD_Range"].Value;

Advanced use:

You can also keep a reference to the LVar which will persist between calls to RefreshData():

FsLVar mfdRange = VS.LVars["B747_8_MFD_Range"];

VS.RefreshData();
double range = mfdRange.Value;


Updating the LVar values:

To write a new value for an LVar call the SetValue() method. This will sent the new value to be written to the Flight Sim. 

e.g. 

VS.LVars["B747_8_MFD_Range"].SetValue(2);

NOTE: This will NOT update the local Value property of the FsLVar. The Value property will be updated after the Sim has received the new value and it's sent back to your application during the normal RefreshData() call. The OnPropertyChanged event will also fire at this time.


Setting HVars:

To set an HVar, call the Set() method of the FsHVar object:

VS.HVars["AS1000_PFD_NAV_Switch"].Set();

This will cause the new HVar to be set in the Flight Sim immediately. 


Checking for changes in LVars:

There are a number of ways to know if LVar values have changed after calling RefreshData():

1. Check the ValueChanged property of the FsLVar object:

if (VS.LVars["B747_8_MFD_Range"].ValueChanged)
{
    // do something with new range value
}


2. Get a collection of all the changed LVars from the LVarsChanged property:

foreach (FsLVar lvar in VS.LVarsChanged)
{
        // Do something with each changed LVar... e.g.
        NotifyChangesTo(lvar);
}

Note that the LVarChanged collection works the same way as the normal LVars collection.

3. You can handle the OnValueChanged event of an FsLVar:

e.g. to be notified of changed to B747_8_MFD_Range:

 VS.LVars["B747_8_MFD_Range"].OnValueChanged += MFDRange_OnValueChanged;

 private void MFDRange_OnValueChanged(object sender, LVarEvent e)
 {
     double newSetting = e.LVar.Value;
     MessageBox.Show("MFD Range Changed to " + newSetting.ToString());
 }

Logging LVars and HVars:

List of LVars and HVars can sent to the log by calling:

VS.LogLVars();
VS.LogHVars();

Creating a new LVar:

Call CreateNewLVar passing in the name of the new LVar and its initial value.

VS.CreateLVar("My_New_LVar", 100);

Executing Calculator Code:

VS.ExecuteCalculatorCode(myCode);

where myCode is a string containing the code to execute. 

 

This is very much a Beta at this point as I don't have MSFS so I can't test any of this here.

Paul

Link to post
Share on other sites

Make sure your exe is compiling and running as a 64 bit process. The FSUIPC_WASPID.dll is 64bit so cannot be loaded from a 32-bit process.

In your exe project properties, go to the Build tab. Make sure your platform target is either x64 or "any cpu". If it's "any" make sure the box for 'prefer 32bit" is NOT checked.

Paul

Link to post
Share on other sites

Thank you, sir!

I'm able to instantiate the object now; however, I'm still getting a object reference not set to an instance error...even when initializing it first.  Below is just a simple method to obtain A320 battery state:

            MSFSVariableServices VS = new MSFSVariableServices();
            VS.OnLogEntryReceived += VS_OnLogEntryReceived;
            VS.Init(this.Handle);
            VS.Start();
            Thread.Sleep(2000);//gives a few seconds to collect the data from wasm module
            double batt = VS.LVars["A320_Neo_BAT_State"].Value;
            MessageBox.Show(batt.ToString()); 

Link to post
Share on other sites

You'll need to add a call to VS.RefreshData() after the sleep, and before you try to read the LVAR.

If it still doesn't work try the following:

1. Waiting longer than 2 seconds.

2. Check the list of LVar names at VS.LVars.Names to see all the LVars that have been found. (If you have logging set up you could also use VS.LogLVars())

Paul

Link to post
Share on other sites

Hey Paul!

 

            MSFSVariableServices VS = new MSFSVariableServices();
            VS.OnLogEntryReceived += VS_OnLogEntryReceived;
            VS.Init(this.Handle);
            VS.LogLVars();
            VS.Start();
            Thread.Sleep(5000);//gives a few seconds to collect the data
            VS.RefreshData();

            try
            {
                MessageBox.Show(log);
            }
            catch(Exception ex) { MessageBox.Show(ex.ToString()); }

Apparently I'm only logging 000 LVARS.  Any other ideas, my friend? 

Link to post
Share on other sites

Sorry to be nausaunse, Paul; however, I'm still unable to read any LVARS.  I continue to get this:

    public partial class Dashboard : Form
    {
        MSFSVariableServices VS = new MSFSVariableServices();

      .....

        public Dashboard()
        {
            try
            {
                InitializeComponent();
                Connect.Click += Button_Click;

                VS.OnLogEntryReceived += VS_OnLogEntryReceived;
            }
            catch (Exception) { }
        }

        private void debugToolStripMenuItem_Click(object sender, EventArgs e)
        {
            VS.Init(this.Handle);            
            VS.Start();
            Thread.Sleep(5000);//gives a few seconds to collect the data
            VS.RefreshData();
            VS.LogLVars();
            Thread.Sleep(2000);//gives a few seconds to collect the data
            try
            {
                MessageBox.Show(log);
            }
            catch(Exception ex) { MessageBox.Show(ex.ToString()); }
        }

 

 

image.png.b37e7bb327fa666923f2692b1bf2aadf.png

Link to post
Share on other sites

I have written a small class that handles the connection starting, connection error handling etc.

Reason is my app is constantly running and making requests and should not be impacted if the sim is not running or starting or shut down in between.

With the FSUIPCConnection I could just check if the connection is open, if not try to open it and if there were an error (e.g. FSUIPC was not running) I would get some kind of exception and the app would wait a bit and try again.
With the new MSFSVariableServices there seems to be no immediate failure (e.g. when start fails), instead the information is only then received in the log entry message events.

Hence why I wrote this handler class.
Usage is quite simple, when you run code through the "Try"-methods it will make sure the connection is available, start the connection if it's not and give a result if your action was successful. In case of an error it will try to stop the connection so on the next request it will try to start it again.
Examples:

// Initialize the wrapper. You still need your handle (e.g. this.Handle from Windows.Forms
// or from a custom Messaging component in a console app) and optional a handler for messages
VariableHandler variables = new VariableHandler(handle, msg => Console.WriteLine(msg));

// As a connection start is implicitly ensured before each action this is enough to perform
// an initial start (e.g. if you want to allow for time to gather the data before your first
// real request):
variables.Try(vs => {});

// Reading all LVars:
variables.Try(vs =>
{
	vs.RefreshData();
	Console.WriteLine(vs.LVars.Count + " LVars:");
	foreach (FsLVar lvar in vs.LVars)
	{
		Console.WriteLine(lvar.Name + " = " + lvar.Value);
	}
});

// Do a refresh:
variables.Try(vs => vs.RefreshData());

// Reading a single LVar:
if (variables.Try(vs => vs.LVars["LVAR_NAME"], out FsLVar lvar))
{
	if (lvar != null)
	{
		Console.WriteLine(lvar.Name + " = " + lvar.Value);
	}
}

// Force a connection stop (something that should not be necessary usually):
variables.Try(vs => vs.Stop());

This is not a higher level abstraction layer, as you still work with the normal MSFSVariableServices object through the "Try"-methods, but you don't have to care about managing the connection or handling an Exception that occurred because you sent a request after the sim has shut down etc.

It's not much but I think it could help someone having similar problems.

VariableHandler.cs

Link to post
Share on other sites
1 hour ago, kingm56 said:

Sorry to be nausaunse, Paul; however, I'm still unable to read any LVARS.  I continue to get this:

Have you tried not using LogLVars and instead simply accessing the LVars directly? Very simple WindowsForms example:

MSFSVariableServices VS;
private void Form1_Load(object sender, EventArgs e)
{
	VS = new MSFSVariableServices();
	VS.OnLogEntryReceived += VS_OnLogEntryReceived;
	VS.Init(this.Handle);
	VS.Start();
}

private void Button1_Click(object sender, EventArgs e)
{
	VS.RefreshData();
	string text = VS.LVars.Count + " LVARS\r\n";
	foreach (FsLVar lvar in VS.LVars)
	{
		text += lvar.Name + " = " + lvar.Value + "\r\n";
	}
	MessageBox.Show(text);
}

private void VS_OnLogEntryReceived(object sender, LogEventArgs e)
{}

 

Link to post
Share on other sites
4 hours ago, Paul Henty said:

Your code looks okay now.

Can you please try running John's WASMClient.exe program to check if that is returning any LVars. You can find it in the FSUIPC-WASMv0.4.10.Zip file.

Paul

Hey Paul! I tried running WASMClient.exe; the WASM Client connected, but does not collect any LVARS, which appears to be my culprit.  Here's the log entried:

 

Sun May 09 2021 17:17:35.385    [INFO]: Connected to MSFS
Sun May 09 2021 17:25:24.355    [INFO]: SimConnect_Close done

Link to post
Share on other sites
3 hours ago, jaxx said:

Have you tried not using LogLVars and instead simply accessing the LVars directly? Very simple WindowsForms example:


MSFSVariableServices VS;
private void Form1_Load(object sender, EventArgs e)
{
	VS = new MSFSVariableServices();
	VS.OnLogEntryReceived += VS_OnLogEntryReceived;
	VS.Init(this.Handle);
	VS.Start();
}

private void Button1_Click(object sender, EventArgs e)
{
	VS.RefreshData();
	string text = VS.LVars.Count + " LVARS\r\n";
	foreach (FsLVar lvar in VS.LVars)
	{
		text += lvar.Name + " = " + lvar.Value + "\r\n";
	}
	MessageBox.Show(text);
}

private void VS_OnLogEntryReceived(object sender, LogEventArgs e)
{}

 

Hey Jaxx!

It appears WASMClient isn't collect any LVARs.  Any thoughts, my friend? 

Link to post
Share on other sites
Quote

the WASM Client connected, but does not collect any LVARS,

There seems to be a problem somewhere other than your code/my dll then. The best thing to do would be to ask John about this in the MSFS support forum under a new topic.

If you tell him the WASMClient.exe is connecting but not finding any LVARs he may be able to suggest things to check,

Paul

Link to post
Share on other sites

Hi everyone, I developed MSFS software with C #, I tried following the instructions in this post and I was able to set HVars, but after I fixed it, now it doesn't work and it crashes as pictured.

I tried WASMClient and it still works fine. Now i use

-FSUIPC 7.10

-fsuipcClient 3.2.2.364

-FSUIPC-WASMv0.4.10 And use FSUIPC_WAPID.DLL from it.

 

Can anyone advise me to fix this issue?

Best regards,

patcharapol Phophun

Error1.jpg

Link to post
Share on other sites
Quote

now it doesn't work and it crashes as pictured.

It crashes because that HVar doesn't exist. You can see the list of LVARs and HVARs found by using LogHVars() and LogLVars(). What do they show?

Remember that you need to wait a few seconds after Start() before the WASM module is ready.

Paul

Link to post
Share on other sites
8 hours ago, Paul Henty said:

It crashes because that HVar doesn't exist. You can see the list of LVARs and HVARs found by using LogHVars() and LogLVars(). What do they show?

Remember that you need to wait a few seconds after Start() before the WASM module is ready.

Paul

Thank you very much for your advice.

Best regards,

patcharapol Phophun

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use. Guidelines Privacy Policy We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.