Jump to content
The simFlight Network Forums
Firefly

Best way to make browser interface with FSUIPC?

Recommended Posts

What is the best way to make a browser web page interface with FSUIPC? So that we can easily design our own panels with buttons and information from the aircraft/simulator using HTML/CSS/Javascript? How could that be done?

It seems like it should utilize websockets since that provides two-way instant communication and the WebSocket API is supported by any modern browser. But FSUIPC doesn't have a websocket server in-built I presume... Plain requests via the HTTP protocol could be an alternative but would only be optimal for button presses...

I'm aware of WideFS button capabilities - it would be some similar to that but just done in the browser instead so that it will have many more options for layout and information. Seems with today's browsers it should be doable performance-wise and therefore an obvious feature to have.

Share this post


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

What is the best way to make a browser web page interface with FSUIPC?

There is no web browser interface to FSUIPC as far as I'm aware. Please check the SDK for the available interfaces.

I guess, for example, you could write a java plug-in that runs in the browser and communicates to FSUIPC via one of the Java SDKs. Or one of the other SDKs (e.g. python) if that is supported by the browser.

There are no plans to add a HTTP or websockets interface directly to FSUIPC. 

Share this post


Link to post
Share on other sites

Actually, I'll put this on my list to investigate at some point. i.e add a http (or maybe websockets) interface to FSUIPC. However, I don't expect to have time to look into this for quite some time. Any such interface would only expose reading and writing to the offset data area, as is supported by the current SDK.

John

Share this post


Link to post
Share on other sites

I've been thinking about this a bit and it should actually be pretty simple to implement, so I'll add it to my 'to be implemented' list. However, with FSUIPC7 for MSFS being the priority at the moment, it will take some time before I can get around to looking at this in detail. I will implement as a plug-in dll, so I can add extra features not available in the SDK, such as a direct 'control/event' interface, not via offsets.

Also, it should be reasonably straightforward to implement as a separate process,  using one of the provided SDKs. All that is needed is to add an embedded http/websockets server, and then exposed the FSUIPC SDK functions as endpoints. The most developed/used SDK at the moment seems to be Paul Henty's .net client. Maybe @Paul Henty has already considered this?

John

Share this post


Link to post
Share on other sites

Hi John,

Yes I've thought about it as I've seen some other requests for this in past but I didn't have the time for new project.

As you say, a small application that runs in the system tray that provides an http interface should be fairly straight forward. As well as the basic offset read/write I could also expose the helper classes in the DLL like accessing weather and AI traffic.

I guess the main advantage of a separate program is that it'll work with all version of FSUIPC/Flight Sims.

I'll investigate his week as I don't think it'll be too big a project and I'll report back.

Paul

Share this post


Link to post
Share on other sites

Guys, that's great to hear! I really think this could be very useful and open up for a lot of possibilities - as there are a lot of people who know how to work with the web technologies but don't how to use lower level languages, do compiling and all that stuff. But simple web APIs is what is tying systems together nowadays.

Looking forward to see what you can come up with!

Share this post


Link to post
Share on other sites

I have a proof of concept working.

It uses HTTP requests. I can't see any advantage in using WebSockets as FSUIPC itself does not have duplex communication. It's only one-way poll & response from client to FSUIPC.

Just to make sure I'm on the right track, and check if this this would be useful, I've posted some javascript below that I've used for testing. This runs directly in a browser, completely client-side.

It talks to the HTTP server program that is located on the flight sim PC (or can be on a different PC if connecting to the flight sim over WideFS).

The URL consists of the IP of the server and port number (setup in the server program) followed by /fsuipc/ and then the type of request you are making. e.g. to request offset data it might look like this:

 

http://localhost:2048/fsuipc/offsets

You'll make an AJAX POST request to this URL, passing a JSON stringified array of offset requests (details in the code below). Then you'll get back another JSON string containing the values which you can turn into a Javascript object to use.

Here's the Javascript for reading three offsets:

var fsuipcURL = "http://localhost:2048/fsuipc/";

function btnReadOffsetsClick(e) {
    // make a request to the sever to read offset data
    // First setup a data structure containing the offset requests

    // This is an array of offset objects. Each object contains:
    // name: used to identify the value when the response comes back (can be anything)
    // address: Hexadecimal address of the offset
    // type: the type of data in the offset. Must be:
    //       'int'   (signed integer)
    //       'uint'  (unsigned integer)
    //       'string'
    //       'float'
    // size: The size of the offset in bytes 

    offsets = [
        { name: 'avionicsMaster', address: '2E80', type: 'uint', size: 4},
        { name: 'heading', address: '0580', type: 'uint', size: 4},
        { name: 'aircraftName', address: '3D00', type: 'string', size: 256 },
    ];

    // create the URL
    var offsetsURL = fsuipcURL + "offsets";
    // create a new AJAX request object
    var xhttp = new XMLHttpRequest();

    // setup a function to execute when the data is returned
    xhttp.onreadystatechange = function () {
        showOffsetData(this);
    };
    // make the request
    xhttp.open("POST", offsetsURL, true);
    xhttp.send(JSON.stringify(offsets));
}

function showOffsetData(e) {
    // clear error display
    document.getElementById('fsuipcReadOffsets').innerHTML = '';
    var displayHTML = "";
    if (e.readyState >= 3 && e.status == 200) {
        // decode the JSON response 
        var response = JSON.parse(e.responseText);
        if (response.Success) {
            var data = response.Data;
            // display each offset value on the page
            document.getElementById('aircraftName').innerText = data['aircraftName'];
            document.getElementById('avionicsMaster').innerText = data['avionicsMaster'] > 0 ? 'ON' : 'OFF';
            // convert heading from FS units to degrees
            var headingDegrees = data['heading'] * 360 / (65536 * 65536);
            document.getElementById('headingTrue').innerText = headingDegrees.toFixed(2);
        }
        else {
            displayHTML = "Request failed with error code: " + response.ErrorCode + " " + response.ErrorMessage;
        }
    }
    else {
        displayHTML = "Error communicating with FSUIPC Web Services. ";
    }
    // set any error HTML into the div
    document.getElementById('fsuipcReadOffsets').innerHTML = displayHTML;
}

This is the result:

image.png.4259b5ec955d99146b6145035a7f3e02.png

Do you think this would work?

Thanks,

Paul

Share this post


Link to post
Share on other sites

Hi Paul,

You're fast! This is great - it's exactly what I had in mind for HTTP communication. It can certainly work in a lot of scenarios - I'm just thinking that in case you for example wanted to do a gauge you would want very frequent updates, at least every 50-100 ms, and in that case there would be quite a bit of overhead in constantly opening/closing the HTTP connection and passing headers. At least theoretically that would be a concern... so that's why I was thinking of websockets. I was imagining that you could subscribe to a set of offsets which would then continually be sent back to the client at a given interval (maybe even user desired interval?). Either all the subscribed offsets could be returned, or even better, only those that have changed since last time.

I understand that FSUIPC in itself is stateless - poll and response - so this would of course require a running websocket server with some logic in it. But due to the overhead of HTTP I believe it would perform substantially better to have that websocket server continually poll FSUIPC internally than doing the polling over the HTTP protocol? Or is there something I'm missing?

Thanks,

Allan

Share this post


Link to post
Share on other sites

I've got WebSockets working. Here's a quick video of real time updates every 100ms.

https://youtu.be/E6RR94O8nfs

This is the JavaScript for this page:

var fsuipcURL = "ws://127.0.0.1:2048/fsuipc";
var ws;

function btnConnectClick(e) {
    if (!ws) {
        // create a new WebSocket using the URL with the 'fsuipc' protocol.
        ws = new WebSocket(fsuipcURL, "fsuipc");

        ws.onopen = function () {
            alert("Socket Open...");
        };

        ws.onmessage = function (evt) {
            // Find out which request was responded to and process it
            var response = JSON.parse(evt.data);
            switch (response.name) {
                case 'info':
                    showInfo(response);
                    break;
                case 'offsetsTest':
                    showOffsetData(response);
                    break;
            }
        };

        ws.onclose = function () {

            // websocket is closed.
            alert("Connection is closed...");
            ws = null;
        };
    }
}

function btnInfoClick(e) {
    // make a request to the sever for info.
    request = {
        request: 'info',
        name: 'info'
    };

    if (ws) {
        ws.send(JSON.stringify(request));
    }
}

function btnReadOffsetsClick(e) {
    // make a request to the sever to read offset data
    // First setup a data structure containing the offset requests

    // This contains an array of ofset objects. Each object contains:
    // name: used to identify the value when the response comes back (can be anything)
    // address: Hexadecimal address of the offset
    // type: the type of data in the offset. Must be:
    //       'int'   (signed integer)
    //       'uint'  (unsigned integer)
    //       'string'
    //       'float'
    // size: The size of the offset in bytes 
    request = {
        request: 'offsets.read', // Tell the server to read offsets
        name: 'offsetsTest', // An ID so we can match the response
        interval: 100, // Send every 100ms - specfiy 0 for read once (no repeat).
        offsets: [
            { name: 'altitude', address: '0570', type: 'int', size: 8 },
            { name: 'avionicsMaster', address: '2E80', type: 'uint', size: 4 },
            { name: 'heading', address: '0580', type: 'uint', size: 4 },
            { name: 'aircraftName', address: '3D00', type: 'string', size: 256 },
        ]
    };

    if (ws) {
        ws.send(JSON.stringify(request));
    }
}

function showInfo(response) {
    if (response.success) {
        var data = response.data;
        // Create a table to display the data
        displayHTML = '<table>';
        // enumerate each returned data item and add as a row to the table
        for (var propertyName in data) {
            displayHTML += '<tr>';
            displayHTML += '<td>' + propertyName + '</td><td>' + data[propertyName] + '</td>';
            displayHTML += '</tr>';
        }
        displayHTML += "</table>";
    }
    else {
        displayHTML = "Request failed with error code: " + response.errorCode + "&nbsp;" + response.errorMessage;
    }
    document.getElementById('fsuipcInfo').innerHTML = displayHTML;
}

function showOffsetData(response) {
    // clear error display
    document.getElementById('fsuipcReadOffsets').innerHTML = '';
    var displayHTML = "";
    
    if (response.success) {
        var data = response.data;
        // display each offset value on the page
        document.getElementById('aircraftName').innerText = data['aircraftName'];
        document.getElementById('avionicsMaster').innerText = data['avionicsMaster'] > 0 ? 'ON' : 'OFF';
        // convert heading from FS units to degrees
        var headingDegrees = data['heading'] * 360 / (65536 * 65536);
        document.getElementById('headingTrue').innerText = headingDegrees.toFixed(0);
        // convert altitude metres to feet
        var altitudeFeet = data['altitude'] / (65535 * 65535) * 3.28084;
        document.getElementById('altitude').innerText = altitudeFeet.toFixed(0);
    }
    else {
        displayHTML = "Request failed with error code: " + response.errorCode + "&nbsp;" + response.errorMessage;
    }

    // set any error HTML into the div
    document.getElementById('fsuipcReadOffsets').innerHTML = displayHTML;
}

Is that more like what you had in mind?

If so, I'll get it to a stable version with offset read and write so you can test it. 

Let me know if you have any other comments or suggestions.

Paul 

  • Like 2

Share this post


Link to post
Share on other sites

I have reviewed the code now - it looks terrific! Good to see you have the "name" ID in there so that we can match the response in case we need to use it a-la-HTTP. Will it only send a message with offset values if there is a change in at least one of them, or what do you think about that? Could save a lot of network traffic for values that rarely change... eg. if you wanted to monitor taxi lights for example...

Will you be able to subscribe to multiple offsets.read requests with different intervals or only one? Just curious as I'm not sure if that would be needed... I guess the same reasoning goes for whether or not one can stop receiving messages for something you have previously subscribed to...

Share this post


Link to post
Share on other sites
Quote

Will it only send a message with offset values if there is a change in at least one of them, or what do you think about that?

That's a good idea. I'll put that in.

Quote

Will you be able to subscribe to multiple offsets.read requests ...

Yes, you can set up multiple read requests each with their own interval. I think this is useful as not all information would need to be read at the same rate. It also makes sense to split the offsets up into  logical groups, e.g. lights, engines etc. especially if the request only sends data if one of the offsets has changed.

I'll be adding an 'offset.stop' so you can stop the request if you don't need it anymore.

Paul

  • Like 1

Share this post


Link to post
Share on other sites

Excellent stuff.

I assume you are thinking of sending all values when at least one changes... Wondering if it will make sense to even only send the values that have actually changed. Would save some bandwidth but would require client-side logic to check which ones are present.

Share this post


Link to post
Share on other sites

Yeah I was thinking about that.

Probably the best thing is to make it an option on the request. That way the user can decide if they want to check which values are present or not.

It would also be a good way of knowing which values have changed. Some applications like flight recorders are mainly based on logging values changing. You would need to test for the change anyway so checking for the value to be present in the return message would be no less inconvenient.

For people writing gauge type applications, they just wouldn't enable the option and get all value from the request back all the time (if at least one has changed).

Paul 

  • Like 1

Share this post


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.