Demo: LVAR write access for any aircraft control

Hi Maurice,

Ok, thanks for the info. I was thinking in terms of FSUIPC. I need to be able to show what lvars are available for a particular aircraft, and also get values back from the WASM module to FSUIPC.
Currently you can integrate the MobiFlight WASM module to use their events via FSUIPC, but I need to add lvar support (read, write, list).
I will investigate the use of the simConnect Client Data area for the data transfer, but would need to be able to get a list of available lvars and access their values from within the WASM module, hence the question.
Anyway thanks for your work on this. I’ll take a look at your github repo and see if I can use this in FSUIPC. I’ve a few things to finish first, but hopefully I can start on this in a day or two.
And sorry for the late response - didn’t get a notification on this for some reason.

John

Thanks, yes I am very interested in the B747 LVars. How and where did you find the
“H:A320_Neo_CDU_1_BTN_0)”,
“(>H:A320_Neo_CDU_1_BTN_1)”,
“(>H:A320_Neo_CDU_1_BTN_2)”,
LVars?

And where did the event number from 0x11000 through 0x1FFFF came from?

Thanks for all your inputs!

To find the >H: functions, in the Developer mode, select Windows > Model Behaviors. Then make sure to select the airplane interior file, for instance A320_NEO_INTERIOR.XML or 747-8I_INTERIOR.XML. After that, try figuring out the child elements that may be related to the component of your interest. For instance, for the B747 MCDU buttons, you need to look here:

  • Root compenents: PEDESTAL_FWD
  • Childs.0: CDUS
  • Childs.1: CDU_1
  • Childs.2: BTN_CDU1_INIT
    This allows you to find for instance (>H:B747_8_FMC_1_BTN_INIT).

Later I’ll take a look at adding more LVARs and H/K buttons. It must be noted that LVAR updates do not always propagate through the simulator, so whenever possible it seems preferable to send calculator_codes rather than directly writing to LVARS.

Keep you posted!
Maurice

And where did the event number from 0x11000 through 0x1FFFF came from?

I forgot to reply on this, it is in th gauge.h file (in your MSFS SDK, for me <SDK root>/WASM/include/MSFS/Legacy/gauges.h, at line 3191, and 3192:

#define THIRD_PARTY_EVENT_ID_MIN 0x00011000
#define THIRD_PARTY_EVENT_ID_MAX 0x0001FFFF

Regards,

Thanks again for your info.

Any idea how to get the local variables state, e.g. mcp annunciators from a wasm?

A good wasm should have the function of both raising events and reading variables.

And also, what is the difference between events starting with H:, A:, K:, L:.?

Is there a definition somewhere?

Great that this is of help to you, for sure you can also retrieve LVAR values, using get_named_variable(), similar to how it is set in the code I provided. Make sure to check the methods available in the Gauges API (it’s both included in the MSFS SK docs, and online from P3D: Gauges API. The alphabet functions deal with different types of variables, some info is available here (Variables Overview). It seems like MSFS has introduced some new Variable types, but I have perhaps missed the documentation on that so far?

Variables and events are very different things:

Variables

L: are the usual LVar from FSX. The “L” naming might be misleading, since it sounds a bit like “Local” vars, when in fact they are really global user variables.

A: are “Airplane” variables, however they work from other Simobjects like Ground Vehicles too, but not all kind of objects have the same variables available, the User airplane has all of them, AI airplanes have less, Ground Vehicles even less and SimpleObject have less than Ground vehicles. They are the “simulation” variables which you usually read only, because they are handled by the simulation so, even if some of them can be written, you’ll likely “fight” against the simulation which will write to them too. You usually change their values indirectly: by sending an EVENT which result in the simulation changing its state.

H: are new “Html” variables used by the new Html5/Javascript gauge system

O: variables are local variables that store some states of simobjects with an XML behavior

Events

K: are not variables, they are Events ( K as in “keyboard”, even if they might not be assigned to the keyboard at all ), so you SEND them to the sim to perform an action, which usually results in a related A: variable to change. So, for example, if you send the K: PARKING BRAKE event, it will affect the PARKING BRAKE POSITION A: variable.

Right now, you can access A: variables with Simconnect, even from an external .EXE written in any language that supports it ( usually C# or C++ ), but to access L: variables you must use a WASM module or gauge that will call the Panels interface which maps 1:1 the old Gauges SDK.

H: and O: variables are usually accessed from Html/JS code, there’s no documented way to access them with an API.

You can send Events both from Simconnect or from the Panels interface in WASM modules/gauges.

8 Likes

Thanks, learning something new every day.

Super! This was probably the most informative reading I’ve found on this subject. Thank you for taking the time to share!

1 Like

So, I downloaded your code and added a PCSTRINGZ array with the H events that Working Title published for the FMS:

PCSTRINGZ FMS_BUTTONS[67] = {
  "(>H:CJ4_FMC_1_BTN_IDX)",
  ...
};

In the switch statement I added:

case 0x11010: {
  execute_calculator_code(FMS_BUTTONS[evdata], nullptr, nullptr, nullptr);
  break;
}

Visual Studio complained about the size of MCDU_BUTTONS[71] but after changing that to 72 it compiled without problem. I added the .wasm file to the community folder with the required .json files as your instructions:

  • WASM_lvar_access
    • module
      - WASM_lvar_access.wasm
    • layout.json
    • manifest.json

I already had the MobiFlight wasm module so I removed that to avoid any conflicts.

Finally I made an .evt file for FSUIPC with all the FMS events and mapped my keyboard numpad to the FMS buttons for 0-9. Nothing happened.

Obviously I’m missing something here but I don’t know what. Perhaps you or anyone else here could nudge me in the right direction :slightly_smiling_face:.

Hi @andhog, I gave this a quick try by only adding the following two lines to my local copy of the code:

PCSTRINGZ CJ4_BUTTONS[] = {
	"(>H:CJ4_FMC_1_BTN_A)",
	"(>H:CJ4_FMC_1_BTN_IDX)",
};

And using the case structure as before, with your event ID 0x11010. This works fine for me, at least when I directly interface through my SimConnect methods, thus Event 0x11010 with evdata 0x0 pushes button A, and Event ID with evdata 0x1 pushes IDX.

Have you checked that your layout.json properly reflects the new file size of the wasm module after compilation? I am unfamiliar with .evt files, so I am afraid I can’t help you there.

PS: thanks for noting the wrong index count in the GitHub copy of the source file.

1 Like

There’s most likely something wrong with my FSUIPC settings. Thank you for checking it out though, at least I know where to look now!

Still struggling to be able to read an LVAR with the get_named_varable().

If you could give me some more hints to make the code I would appreciate it a lot.

Any idea what the difference is between a >L: and L: before the var name?

1 Like

To read the value of an LVar that has been defined elsewhere:

int id = check_named_variable( "MyVariable" );

if ( id != -1 ))
  double value = get_named_variable_value( id );

To write the value of an LVar that has been defined elsewhere:

double value = 1234

int id = check_named_variable( "MyVariable" );
if ( id != -1 ))
 set_named_variable_value( id, value );

To define a new LVar:

int id = check_named_variable( "MyVariable" );

if ( id == -1 ))
  int id = register_named_variable( "MyVariable" );

the > sign in used in XML expressions to indicate the expression will write to the LVar, otherwise it’s a read.

1 Like

Thanks a lot, I have got it to work now.

Now hopefully the last issue is:

How to read those Lvars values that are read as described above from an external Simconnect client (exe)?

How to read those Lvars values that are read as described above from an external Simconnect client (exe)?

You can’t directly.

You must read them from a WASM module, then use the Simconnect data tagging functions to send them to a Simconnect client that will use the matching function to “listen” for your data.

Unfortunately, there’s no sample in the SDK so, you will have to figure it out only from the documentation.

Does anyone has an example of how to set up the “Simconnect data tagging functions”?
What type of Simconnect functions are tagging functions?

1 Like

Thanks everyone in this thread. I’ve been eyeing this for a while and appreciate all the information shared.

I was wondering the same, so I implemented GetClientData/SetClientData: GitHub - davuxcom/fs-gauge-bridge

BridgeGauge.wasm communicates with BridgeClient, an external SimConnect app in C#.

I did not use the data tagging as I don’t think it’s necessary, but optimizing this into a batch set of operations could benefit from that. Ultimately you have a data region and can do as you wish, however there is functionality to get updates only on part of the data changing.

It creates two client data areas, one that is used to send a string into a wasm, and one that is used to read a double from the wasm. There is a simple UI that lets you enter a value like (A:LIGHT LANDING,Bool) or 1 (>A:LIGHT LANDING,Bool) and then the value will be continually monitored. In the wasm the last query is checked and SetClientData is called on every frame.

Some additional notes:

  • Might not be optimal for performance. I see 60fps in all my testing though. The obvious next step is to read 10 strings at a time and output 10 values.
  • Not sure about memory leaks, but looks okay. Using shared_ptr and string should help… obviously one string is held until gauge exit.
  • All work is done via execute_calculator_code. The named value stuff didn’t work for me and also doesn’t seem at all necessary unless I need to register them myself.
  • Possibly only start the BridgeClient app after the gauge is loaded, but I think this isn’t necessary. The client data regions are owned by the gauge but the client shouldn’t have a problem linking up the strings to IDs and declaring sizes up front. I saw something during development around this though.
  • Note that the default value is 0 and when a variable doesn’t change the value, no update will be triggered. Also make sure to hit send on the command in the window, that first Got data: 0 is reading the default/empty state of the client data region.
  • Should prefer sizeof and Marshal.SizeOf for struct sizes, but this makes it clear what is what.
1 Like

I suggest using the register/set/get variable functions instead, because with execute_calculator code you are unnecessarily forcing the sim to send the expression through the XML evaluator every time, which is not needed if you don’t really need an “expression”, the whole point of this would be that external clients in C++/C# would create their own logic, and they surely could do it so much faster and better and with way more complex logic that the XML expression evaluator could possibly do.