Canvas MFD framework: Difference between revisions

Jump to navigation Jump to search
add class to permit multiple instances of an MFD
(Added note about WIP status)
(add class to permit multiple instances of an MFD)
Line 4: Line 4:


== Introduction ==
== Introduction ==
{{Note|This documentation is still a work in progress, as it will be revised to include more explanation and a better way of implementation that will support multiple devices better}}


There is a simple implementation of the basic support for an MFD (or MPCD, or PFD), or any device that is basically a set of pages with buttons around the outside to select the page that is displayed.
There is a simple implementation of the basic support for an MFD (or MPCD, or PFD), or any device that is basically a set of pages with buttons around the outside to select the page that is displayed.
Line 11: Line 9:
This works with a SVG file that defines the menu label locations and has a group for each page. The framework manages the menu hierarchy and displays labels corresponding to the buttons.  
This works with a SVG file that defines the menu label locations and has a group for each page. The framework manages the menu hierarchy and displays labels corresponding to the buttons.  


This is based around the http://wiki.flightgear.org/McDonnell_Douglas_F-15_Eagle#MPCD  
This is based around the SpaceShuttle dispalys which are in turn based on the http://wiki.flightgear.org/McDonnell_Douglas_F-15_Eagle#MPCD  
 
The code as below will allow multiple instances of a particular display to be created. Towards the end of the code an array is populated with one MFD_device instance per display. All use the same SVG and do the same job - but can be on different devices (left, right, etc) in the cockpit. The 3d model must have the frame, bezel and buttons for each display. The model_index selects which buttons to use. The buttons are named sim/model/f15/controls/PFD/button-pressed0 - where this property contains the index of the button, and there is one property per display.


[[File:f-15-cockpit-mpcd-armament.jpg|300px|MCPD Armament Top Level Menu]]
[[File:f-15-cockpit-mpcd-armament.jpg|300px|MCPD Armament Top Level Menu]]
Line 20: Line 20:


<syntaxhighlight lang="nasal">
<syntaxhighlight lang="nasal">
# F-15 Canvas MPCD (Multi-Purpose-Colour-Display)
var MFD_Device =
# ---------------------------
{
# MPCD has many pages; the classes here support multiple pages, menu
#
# operation and the update loop.
# create new MFD device. This is the main interface (from our code) to the MFD device
 
# Each MFD device will contain the underlying PFD device object, the SVG, and the canvas
var MPCDcanvas= canvas.new({
# Parameters
                          "name": "F-15 MPCD",
# - designation - Flightdeck Legend for this
                          "size": [1024,1024],  
# - model_element - name of the 3d model element that is to be used for drawing
                          "view": [740,680],                       
# - model_index - index of the device
                          "mipmapping": 1     
    new : func(designation, model_element, model_index=0)
                          });                           
    {
                         
        var obj = {parents : [MFD_Device] };
MPCDcanvas.addPlacement({"node": "MPCDImage"});
        obj.designation = designation;
MPCDcanvas.setColorBackground(0.003921,0.1764,0, 0);
        obj.model_element = model_element;
        var dev_canvas= canvas.new({
                "name": designation,
                    "size": [1758,1884],  
                    "view": [512,512],                       
                    "mipmapping": 1     
                    });                           


        dev_canvas.addPlacement({"node": model_element});
        dev_canvas.setColorBackground(0,0,0, 0);
# Create a group for the parsed elements
# Create a group for the parsed elements
var MPCDsvg = MPCDcanvas.createGroup();
        obj.PFDsvg = dev_canvas.createGroup();
        var pres = canvas.parsesvg(obj.PFDsvg, "Nasal/PFD/PFD.svg");
# Parse an SVG file and add the parsed elements to the given group
# Parse an SVG file and add the parsed elements to the given group
canvas.parsesvg(MPCDsvg, "Nasal/MPCD/MPCD_0_0.svg");
        printf("MFD : %s Load SVG %s",designation,pres);
        obj.PFDsvg.setTranslation (20, 30);
#
#
# translation may be required to get the image in the right place on the canvas
# create the object that will control all of this
#MPCDsvg.setTranslation (270.0, 197.0);
        obj.PFD = PFD_Device.new(obj.PFDsvg, num_menu_buttons, "MI_");
</syntaxhighlight>
        obj.PFD._canvas = dev_canvas;
        obj.PFD.primary = primary_port;
        obj.PFD.secondary = secondary_port;
        obj.PFD.port_selected = selected_port;
        obj.PFD.dps_page_flag = 0;
        obj.PFD.designation = designation;
        obj.mfd_device_status = 1;
        obj.model_index = model_index; # numeric index (1 to 9, left to right) used to connect the buttons in the cockpit to the display


=== Sample page ===
        obj.addPages();
        return obj;
    },


A typical MFD will have many pages, each page will have its update method called when it is on display, and the ondisplay method when it goes on display, the offdisplay method when it is about to be replaced by another page.
    addPages : func
    {
        me.me.page1_1 = MPCD.addPage("Aircraft Menu", "me.page1_1");


This is a simple page that has a clock display.
        me.page1_1.update = func
        {
            var sec = getprop("instrumentation/clock/indicated-sec");
            me.page1_1.time.setText(getprop("sim/time/gmt-string")~"Z");
            var cdt = getprop("sim/time/gmt");


<syntaxhighlight lang="nasal">
            if (cdt != nil)
                me.page1_1.date.setText(substr(cdt,5,2)~"/"~substr(cdt,8,2)~"/"~substr(cdt,2,2)~"Z");
        };
 
# Connect the buttons - using the provided model index to get the right ones from the model binding
        setlistener("sim/model/f15/controls/PFD/button-pressed"~me.model_index, func(v)
                    {
                        if (v != nil)
                        {
                            if (v.getValue())
                                me.pfd_button_pushed = v.getValue();
                            else
                            {
                                printf("%s: Button %d",me.designation, me.pfd_button_pushed);
                                me.PFD.notifyButton(me.pfd_button_pushed);
                                me.pfd_button_pushed = 0;
                            }
                        }
                    });
 
# Set listener on the PFD mode button; this could be an on off switch or by convention
# it will also act as brightness; so 0 is off and anything greater is brightness.
# ranges are not pre-defined; it is probably sensible to use 0..10 as an brightness rather
# than 0..1 as a floating value; but that's just my view.
        setlistener("sim/model/f15/controls/PFD/mode"~me.model_index, func(v)
                    {
                        if (v != nil)
                        {
                            me.mfd_device_status = v.getValue();
                            print("MFD Mode ",me.designation," ",me.mfd_device_status);
                            if (!me.mfd_device_status)
                                me.PFDsvg.setVisible(0);
                            else
                                me.PFDsvg.setVisible(1);
                        }
                    });
 
        me.PFD.pfd_button_pushed = 0;
        me.setupMenus();
        me.selectPage(p1_1);
    },


var p1_1 = MPCD.addPage("Aircraft Menu", "p1_1");
    # Add the menus to each page.  
    setupMenus : func
    {
        me.page1_1.addMenuItem(0, "ARMT", p1_2);
        me.page1_1.addMenuItem(1, "BIT", p1_2);
        me.page1_1.addMenuItem(2, "SIT", pjitds_1);
        me.page1_1.addMenuItem(3, "WPN", p1_2);
        me.page1_1.addMenuItem(4, "DTM", p1_2);


p1_1.update = func
        me.page1_1.date = MPCDsvg.getElementById("page1_1_date");
{
        me.page1_1.time = MPCDsvg.getElementById("page1_1_time");
    var sec = getprop("instrumentation/clock/indicated-sec");
     },
    p1_1.time.setText(getprop("sim/time/gmt-string")~"Z");
     var cdt = getprop("sim/time/gmt");


     if (cdt != nil)
     update : func
        p1_1.date.setText(substr(cdt,5,2)~"/"~substr(cdt,8,2)~"/"~substr(cdt,2,2)~"Z");
    {
        if(me.mfd_device_status)
            me.PFD.update();
    },
};
};


p1_1.addMenuItem(0, "ARMT", p1_2);
# the PFD object really should be called an MFD - we attach the port connections to the IDPs and the selection
p1_1.addMenuItem(1, "BIT", p1_2);
p1_1.addMenuItem(2, "SIT", pjitds_1);
p1_1.addMenuItem(3, "WPN", p1_2);
p1_1.addMenuItem(4, "DTM", p1_2);


p1_1.date = MPCDsvg.getElementById("p1_1_date");
var MFD_array = [];
p1_1.time = MPCDsvg.getElementById("p1_1_time");
</syntaxhighlight>


In the above menu structure the p1_2 (second parameters) refer to other pages, created just like the first one.
#
#
# Create and append all of the MFDs in the cockpit.
# - MFD_Device.new( Identity, Canvas3dSurface, model index)
#               
append(MFD_array, MFD_Device.new("MFD-1", "MPCDImage",0));


=== Supporting logic ===
# update only one display per frame to reduce load. This can easily be changed
# to update all by looping around all of the displays in the MFD_array
var frame_device_update_id = 0;


<syntaxhighlight lang="nasal">
var MFD_rtExec_update = func
#
{
# Select the default first page. Something needs to be selected otherwise the menus
    if (frame_device_update_id >= size(MDU_array))
# will not be correct.
        frame_device_update_id = 0;
MPCD.selectPage(p1_1);


#
    if (frame_device_update_id < size(MDU_array))
# Connect the buttons
        MDU_array[frame_device_update_id].update();
setlistener("sim/model/f15/controls/MPCD/button-pressed", func(v)
            {
                if (v != nil)
                {
                    if (v.getValue())
                        mpcd_button_pushed = v.getValue();
                    else
                    {
                        MPCD.notifyButton(mpcd_button_pushed);
                        mpcd_button_pushed = 0;
                    }
                }
            });


#
    frame_device_update_id += 1;
# called from the aircraft main loop, or via a maketimer() call
     MFD_rtExec_timer.restart(0.02);
var updateMPCD = func ()
     if(mpcd_mode)
        MPCD.update();
}
}
var MFD_rtExec_timer = maketimer(6, MFD_rtExec_update);
MFD_rtExec_timer.restart(6);
</syntaxhighlight>
</syntaxhighlight>


308

edits

Navigation menu