• Which the release of FS2020 we see an explosition of activity on the forun and of course we are very happy to see this. But having all questions about FS2020 in one forum becomes a bit messy. So therefore we would like to ask you all to use the following guidelines when posting your questions:

    • Tag FS2020 specific questions with the MSFS2020 tag.
    • Questions about making 3D assets can be posted in the 3D asset design forum. Either post them in the subforum of the modelling tool you use or in the general forum if they are general.
    • Questions about aircraft design can be posted in the Aircraft design forum
    • Questions about airport design can be posted in the FS2020 airport design forum. Once airport development tools have been updated for FS2020 you can post tool speciifc questions in the subforums of those tools as well of course.
    • Questions about terrain design can be posted in the FS2020 terrain design forum.
    • Questions about SimConnect can be posted in the SimConnect forum.

    Any other question that is not specific to an aspect of development or tool can be posted in the General chat forum.

    By following these guidelines we make sure that the forums remain easy to read for everybody and also that the right people can find your post to answer it.

MSFS20 C++ gauges nightmare

Messages
162
Country
italy
Someone like me is fighting pointlessly to try to run gauges in C++ ?
it’s a nightmare.
For instance:
Code:
PCSTRINGZ custom_event_id_name = "custom_event_id"; 
struct CustomEvent {    ID id;    ENUM value; }; 
CustomEvent custom_event_id = { 0,0 }; 
// Callbacks 
extern "C" { 
   MSFS_CALLBACK bool custom_event_gauge_callback(FsContext ctx, int service_id, void* pData)    { 
       ID id = 0;
        ID check_id = 0;
        switch (service_id)        { 
       case PANEL_SERVICE_PRE_INSTALL:        {
            ID check_id = check_named_variable(custom_event_id_name);
            if (check_id < 0) {
                id = register_named_variable(custom_event_id_name);
                custom_event_id.id = id; 
           }  
          return true; 
       }
none of above works.
register_named_variable(custom_event_id_name) does not register anything and return always 0
consequently check_named_variable(custom_event_id_name); fails always.
any clues ?
tia
Mario
 
Someone like me is fighting pointlessly to try to run gauges in C++ ?
it’s a nightmare.
For instance:
Code:
PCSTRINGZ custom_event_id_name = "custom_event_id";
struct CustomEvent {    ID id;    ENUM value; };
CustomEvent custom_event_id = { 0,0 };
// Callbacks
extern "C" {
   MSFS_CALLBACK bool custom_event_gauge_callback(FsContext ctx, int service_id, void* pData)    {
       ID id = 0;
        ID check_id = 0;
        switch (service_id)        {
       case PANEL_SERVICE_PRE_INSTALL:        {
            ID check_id = check_named_variable(custom_event_id_name);
            if (check_id < 0) {
                id = register_named_variable(custom_event_id_name);
                custom_event_id.id = id;
           } 
          return true;
       }
none of above works.
register_named_variable(custom_event_id_name) does not register anything and return always 0
consequently check_named_variable(custom_event_id_name); fails always.
any clues ?
tia
Mario
Maybe it's a double declaration of check_id?
 
That definately won't help. Also, I'd put the code in PRE_UPDATE because you can't guarantee that PRE_INSTALL will update when you want it too.
 
That definately won't help. Also, I'd put the code in PRE_UPDATE because you can't guarantee that PRE_INSTALL will update when you want it too.
I can debug the code, PRE_INSTALL is called several times, not a big issue, however
Code:
register_named_variable(custom_event_id_name);
i'm stuck here.
Someone has tried to make a working C++ gauge ? (hope yes) :-(
 
Hi,

You should try:

Code:
       case PANEL_SERVICE_CONNECT_TO_WINDOW:        {
            ID check_id = register_named_variable(custom_event_id_name);
             custom_event_id.id = id; 
           }  
          return true;

With that snippet you are registering an LVAR only once. No need to check whether or not it is already registered.

Tomas
 
In Tomas' response, the return is assigned to check_id... and the value of id (which is assigned to custom_event_id.id is undefined. So... I'd change = id to = check_id. I would also avoid declaring variables in the middle of code. I can't count the number of times I've found bugs in people's code because of that one.
 
I'm with Ed on variable declarations. I truly dislike Microsoft for promoting the use of variable declaration anywhere in dotNet. Declare everything at the top of the code and assign it an initial variable. Failure to assign an initial value will lead to what is euphemistically refered to as 'undefined behaviour' because you will have no idea what crap the operating system has dumped in the variable when you come to use it.
 
If it's a local stack variable (one only used in a function), declare it at the beginning of the function.
 
You should try:

Code:
       case PANEL_SERVICE_CONNECT_TO_WINDOW:        {
            ID check_id = register_named_variable(custom_event_id_name);
             custom_event_id.id = id;
           } 
          return true;
How and when you got PANEL_SERVICE_CONNECT_TO_WINDOW for WASM gauge callback function in MSFS?
In SDK 0.6.0 (C Gauges -> Events received by the gauge callback) [Why the hell they doesn't publish SDK on web for hyperlinks? or I have missed something]
there is no mention about it in service_id. By the way in gauges.h there are more PANEL_SERVICE_xxx, but in my test gauge I have only received and logged service_id = 2, 3, 4, 5, [6, 7, 10, 11], 12, 13.
 
Someone has tried to make a working C++ gauge ? (hope yes) :-(
Yes. This quick example works fine.
C++:
// My demo of wasm gauge

#include <MSFS/MSFS.h>
#include <MSFS/MSFS_Render.h>
#include <MSFS/Render/nanovg.h>
#include <MSFS/Legacy/gauges.h>

#include <map>
#include <cstring>
#include <string>
#include <sstream>
#include <fstream>

// Names for my variables
PCSTRINGZ custom_var_name = "var1";
PCSTRINGZ custom_var_name2 = "var2";

struct sGaugeVars
{
    int iFont;
    ID id_var_1;
    ID id_var_2;
};

std::stringstream g_Log;

std::map<FsContext, sGaugeVars> g_GaugeVars;
std::map<FsContext, NVGcontext*> g_GaugeNVGcontext;

extern "C" MSFS_CALLBACK bool DemoGauge1_gauge_callback(FsContext ctx, int service_id, void* pData)
{
    // local variables for debug view
    ID a = 0, b = 0;

    switch (service_id)
    {
    case PANEL_SERVICE_PRE_INSTALL:
    {
        sGaugeInstallData* p_install_data = (sGaugeInstallData*)pData;
        g_GaugeVars[ctx].iFont = -1;
        // Register variables names
        g_GaugeVars[ctx].id_var_1 = register_named_variable(custom_var_name);
        g_GaugeVars[ctx].id_var_2 = register_named_variable(custom_var_name2);
        
        // Check received IDs in debugger's Local window
        a = g_GaugeVars[ctx].id_var_1;
        b = g_GaugeVars[ctx].id_var_2;

        g_Log << "Registered variables: "
            << custom_var_name << " with ID: " << a << " "
            << custom_var_name2 << " with ID: " << b << std::endl;

        return true;
    }
    break;
    case PANEL_SERVICE_POST_INSTALL:
    {
        NVGparams params;
        params.edgeAntiAlias = true;
        params.userPtr = ctx;
        g_GaugeNVGcontext[ctx] = nvgCreateInternal(&params);
        NVGcontext* nvgctx = g_GaugeNVGcontext[ctx];
        g_GaugeVars[ctx].iFont = nvgCreateFont(nvgctx, "serif", "./data/Roboto-Regular.ttf");
        return true;
    }
    break;
    case PANEL_SERVICE_PRE_INITIALIZE: break;
    case PANEL_SERVICE_POST_INITIALIZE:
    {
        // set default values for variables
        if (check_named_variable(custom_var_name) != -1 )
            set_named_variable_value(g_GaugeVars[ctx].id_var_1, 0.);
        if (check_named_variable(custom_var_name2) != -1)
            set_named_variable_value(g_GaugeVars[ctx].id_var_2, 9999.);
    }
    break;
    case PANEL_SERVICE_PRE_UPDATE:
    {
        // change variables values
        set_named_variable_value(g_GaugeVars[ctx].id_var_1, get_named_variable_value(g_GaugeVars[ctx].id_var_1) + 0.1);
        set_named_variable_value(g_GaugeVars[ctx].id_var_2, get_named_variable_value(g_GaugeVars[ctx].id_var_2) - 0.1);
    }
    break;
    case PANEL_SERVICE_POST_UPDATE: break;
    case PANEL_SERVICE_PRE_DRAW:
    {
        sGaugeDrawData* p_draw_data = (sGaugeDrawData*)pData;
        float pxRatio = (float)p_draw_data->fbWidth / (float)p_draw_data->winWidth;
        NVGcontext* nvgctx = g_GaugeNVGcontext[ctx];
        nvgBeginFrame(nvgctx, p_draw_data->winWidth, p_draw_data->winHeight, pxRatio);
        {
            // Black background
            nvgFillColor(nvgctx, nvgRGB(0, 0, 0));
            nvgBeginPath(nvgctx);
            nvgRect(nvgctx, 0, 0, p_draw_data->winWidth, p_draw_data->winHeight);
            nvgFill(nvgctx);

            /// Output variables values
            if (g_GaugeVars[ctx].iFont != -1)
            {
                char str[50] = "";

                nvgFontFaceId(nvgctx, g_GaugeVars[ctx].iFont);
                nvgFontSize(nvgctx, 0.1f * p_draw_data->winHeight);
                nvgTextAlign(nvgctx, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
                nvgFillColor(nvgctx, nvgRGB(250, 250, 250));

                sprintf(str, "var1 ID: %d Value: %5.1f", g_GaugeVars[ctx].id_var_1, get_named_variable_value(g_GaugeVars[ctx].id_var_1));
                nvgText(nvgctx, 0, 0.2f * p_draw_data->winHeight, str, (const char*)NULL);
                g_Log << str << " ";

                sprintf(str, "var2 ID: %d Value: %5.1f", g_GaugeVars[ctx].id_var_2, get_named_variable_value(g_GaugeVars[ctx].id_var_2));
                nvgText(nvgctx, 0, 0.4f * p_draw_data->winHeight, str, (const char*)NULL);
                g_Log << str << std::endl;
            }
        }
        nvgEndFrame(nvgctx);
        return true;
    }
    break;
    case PANEL_SERVICE_POST_DRAW: break;
    case PANEL_SERVICE_PRE_KILL:
    {
        NVGcontext* nvgctx = g_GaugeNVGcontext[ctx];
        nvgDeleteInternal(nvgctx);
        g_GaugeNVGcontext.erase(ctx);

        return true;
    }
    break;
    case PANEL_SERVICE_POST_KILL:
    {
        // Save log to text file
        // %LOCALAPPDATA%\Packages\Microsoft.FlightSimulator_8wekyb3d8bbwe\LocalState\packages\mycompany-aircraft-wasm-gauge\work\gauge_log.txt
        std::ofstream out("\\work\\gauge_log.txt", std::ios::app);
        if (out.is_open())
        {
            out << g_Log.rdbuf();
            out.close();
        }
        g_GaugeVars.erase(ctx);
        g_Log.clear();
        return true;
    }
    break;
    }
    return false;
}

Screenshot 2020-10-06 012014.png
Screenshot 2020-10-06 012322.png
 
  • Like
Reactions: tml
Yes. This quick example works fine.
C++:
// My demo of wasm gauge

#include <MSFS/MSFS.h>
#include <MSFS/MSFS_Render.h>
#include <MSFS/Render/nanovg.h>
#include <MSFS/Legacy/gauges.h>

#include <map>
#include <cstring>
#include <string>
#include <sstream>
#include <fstream>

// Names for my variables
PCSTRINGZ custom_var_name = "var1";
PCSTRINGZ custom_var_name2 = "var2";

struct sGaugeVars
{
    int iFont;
    ID id_var_1;
    ID id_var_2;
};

std::stringstream g_Log;

std::map<FsContext, sGaugeVars> g_GaugeVars;
std::map<FsContext, NVGcontext*> g_GaugeNVGcontext;

extern "C" MSFS_CALLBACK bool DemoGauge1_gauge_callback(FsContext ctx, int service_id, void* pData)
{
    // local variables for debug view
    ID a = 0, b = 0;

    switch (service_id)
    {
    case PANEL_SERVICE_PRE_INSTALL:
    {
        sGaugeInstallData* p_install_data = (sGaugeInstallData*)pData;
        g_GaugeVars[ctx].iFont = -1;
        // Register variables names
        g_GaugeVars[ctx].id_var_1 = register_named_variable(custom_var_name);
        g_GaugeVars[ctx].id_var_2 = register_named_variable(custom_var_name2);
    
        // Check received IDs in debugger's Local window
        a = g_GaugeVars[ctx].id_var_1;
        b = g_GaugeVars[ctx].id_var_2;

        g_Log << "Registered variables: "
            << custom_var_name << " with ID: " << a << " "
            << custom_var_name2 << " with ID: " << b << std::endl;

        return true;
    }
    break;
    case PANEL_SERVICE_POST_INSTALL:
    {
        NVGparams params;
        params.edgeAntiAlias = true;
        params.userPtr = ctx;
        g_GaugeNVGcontext[ctx] = nvgCreateInternal(&params);
        NVGcontext* nvgctx = g_GaugeNVGcontext[ctx];
        g_GaugeVars[ctx].iFont = nvgCreateFont(nvgctx, "serif", "./data/Roboto-Regular.ttf");
        return true;
    }
    break;
    case PANEL_SERVICE_PRE_INITIALIZE: break;
    case PANEL_SERVICE_POST_INITIALIZE:
    {
        // set default values for variables
        if (check_named_variable(custom_var_name) != -1 )
            set_named_variable_value(g_GaugeVars[ctx].id_var_1, 0.);
        if (check_named_variable(custom_var_name2) != -1)
            set_named_variable_value(g_GaugeVars[ctx].id_var_2, 9999.);
    }
    break;
    case PANEL_SERVICE_PRE_UPDATE:
    {
        // change variables values
        set_named_variable_value(g_GaugeVars[ctx].id_var_1, get_named_variable_value(g_GaugeVars[ctx].id_var_1) + 0.1);
        set_named_variable_value(g_GaugeVars[ctx].id_var_2, get_named_variable_value(g_GaugeVars[ctx].id_var_2) - 0.1);
    }
    break;
    case PANEL_SERVICE_POST_UPDATE: break;
    case PANEL_SERVICE_PRE_DRAW:
    {
        sGaugeDrawData* p_draw_data = (sGaugeDrawData*)pData;
        float pxRatio = (float)p_draw_data->fbWidth / (float)p_draw_data->winWidth;
        NVGcontext* nvgctx = g_GaugeNVGcontext[ctx];
        nvgBeginFrame(nvgctx, p_draw_data->winWidth, p_draw_data->winHeight, pxRatio);
        {
            // Black background
            nvgFillColor(nvgctx, nvgRGB(0, 0, 0));
            nvgBeginPath(nvgctx);
            nvgRect(nvgctx, 0, 0, p_draw_data->winWidth, p_draw_data->winHeight);
            nvgFill(nvgctx);

            /// Output variables values
            if (g_GaugeVars[ctx].iFont != -1)
            {
                char str[50] = "";

                nvgFontFaceId(nvgctx, g_GaugeVars[ctx].iFont);
                nvgFontSize(nvgctx, 0.1f * p_draw_data->winHeight);
                nvgTextAlign(nvgctx, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
                nvgFillColor(nvgctx, nvgRGB(250, 250, 250));

                sprintf(str, "var1 ID: %d Value: %5.1f", g_GaugeVars[ctx].id_var_1, get_named_variable_value(g_GaugeVars[ctx].id_var_1));
                nvgText(nvgctx, 0, 0.2f * p_draw_data->winHeight, str, (const char*)NULL);
                g_Log << str << " ";

                sprintf(str, "var2 ID: %d Value: %5.1f", g_GaugeVars[ctx].id_var_2, get_named_variable_value(g_GaugeVars[ctx].id_var_2));
                nvgText(nvgctx, 0, 0.4f * p_draw_data->winHeight, str, (const char*)NULL);
                g_Log << str << std::endl;
            }
        }
        nvgEndFrame(nvgctx);
        return true;
    }
    break;
    case PANEL_SERVICE_POST_DRAW: break;
    case PANEL_SERVICE_PRE_KILL:
    {
        NVGcontext* nvgctx = g_GaugeNVGcontext[ctx];
        nvgDeleteInternal(nvgctx);
        g_GaugeNVGcontext.erase(ctx);

        return true;
    }
    break;
    case PANEL_SERVICE_POST_KILL:
    {
        // Save log to text file
        // %LOCALAPPDATA%\Packages\Microsoft.FlightSimulator_8wekyb3d8bbwe\LocalState\packages\mycompany-aircraft-wasm-gauge\work\gauge_log.txt
        std::ofstream out("\\work\\gauge_log.txt", std::ios::app);
        if (out.is_open())
        {
            out << g_Log.rdbuf();
            out.close();
        }
        g_GaugeVars.erase(ctx);
        g_Log.clear();
        return true;
    }
    break;
    }
    return false;
}

View attachment 63214View attachment 63215
Thanks a lot, will give a try.
/Mario
just checked, it compiles nicely however watching in the MSFS console window i see it's not initialized nor loaded for some reason, so won't work on my side, sorry.
my call in panel.cfg
Code:
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=demo.wasm&wasm_gauge=DemoGauge1, 0,0,1024,76
and console window says:
WASM: Failed to load module (not found in the VFS)
thanks for help.
/Mario
 
Last edited:
In Tomas' response, the return is assigned to check_id... and the value of id (which is assigned to custom_event_id.id is undefined. So... I'd change = id to = check_id. I would also avoid declaring variables in the middle of code. I can't count the number of times I've found bugs in people's code because of that one.
thanks.
no conflict because the 2 check_id are not declared "in the middle of code" but in different scopes, compiler won't be confused, simply the outer check_id is initialzed at 0 and remains unused, while the inner check_id should have the right id returned from register function, which is not, and will disappear out of his scope..
simply
 
I said nothing about the compiler being confused. I am stating that it is bad practice because declaring a variable on the fly like that can lead to serious issues. I have seen this many, many times in people's code. I simply can not stress strongly enough that this is a practice that should be shunned. It's bad, bad... really bad. Over 40 years here in writing software... and I'll say it again... just because you can, doesn't mean you should. It's a bad coding method. There is quite literally no valid reason to declare a variable on the fly like that.
 
thanks.
no conflict because the 2 check_id are not declared "in the middle of code" but in different scopes, compiler won't be confused, simply the outer check_id is initialzed at 0 and remains unused, while the inner check_id should have the right id returned from register function, which is not, and will disappear out of his scope..
simply
Im a bit late, but im getting the same "WASM: Failed to load module (not found in the VFS)". Have you found any fixes for this?
 
Hello,
yes, was my fault, most of time it means that C++ -> WASM compiler found erros on your code, opening console on the game and looking at the errors should reveal what the problem is.
/Mario
 
with SDK 0.9 it not quite hard to do WASM using C++.
I feel, it more simple than FSX C++ GDI+. I just spend 10 days just porting over P3D aircraft code to native fs2020.
 
It's not really clear in what circumstances C++ gauge development makes sense over javascript.

I don't think there's a practical speed difference within the sim given the coding practices common in gauges (I have a PhD in computer science so I understand the diffference in execution model and that trivial instruction sequences execute quicker in WASM than JS) . My guess is C++ -> WASM gauge code will be deprecated in due course (shock, horror). Of course those with 'legacy' C++ gauge code will want to migrate rather then re-write but I don't think I would use C++ otherwise.

While I'm on a rant, the naming of the most basic calls used in gauge code in C++ and JS make me smile, i.e "get_named_variable_value()" and "SimVars.GetSimVarValue()". No-one thought just "SimVars.get()" ?
 
Back
Top