Canvas MFD framework: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
m (→‎Background: http://forum.flightgear.org/viewtopic.php?p=198150#p198150)
(33 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{infobox subsystem
{{Stub}}
|image =777-display-selector.png
 
|name =Canvas MFD Framework
{{Template:Canvas Navigation}}
|started= 02/2014
 
|description = MFD Framework
== Introduction ==
|status = Under active development as of 02/2014
{{See also|Canvas EFIS Framework}}
|maintainers  = F-JYL, 5H1N0B1, Hooray
 
|developers = F-JYL (since 02/2014),
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.
<!--
 
|folders = [http://gitorious.org/fg/flightgear/trees/next/src/Canvas $FG_SRC/Canvas]
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.
          [http://gitorious.org/fg/simgear/trees/next/simgear/canvas $SG_SRC/simgear/canvas]
 
|topic-fgdata= (main repository, master branch; https://gitorious.org/~tomprogs/fg/toms-fgdata/commits/canvas-gui-demo)
This is based around the SpaceShuttle displays which are in turn based on the http://wiki.flightgear.org/McDonnell_Douglas_F-15_Eagle#MPCD.
-->
 
|subforum= http://forum.flightgear.org/viewforum.php?f=71
For a straightforward example of this in use, see https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Aircraft/F-15/Nasal/MPCD/MPCD_main.nas
}}
 
For a more complex example as part of the FG1000 glass cockpit, see https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Aircraft/Instruments-3d/FG1000/Nasal/MFD.nas
 
[[File:f-15-cockpit-mpcd-armament.jpg|300px|MCPD Armament Top Level Menu]]
 
== Design ==
 
The PFD/MFD contains multiple pages, each of which can be referenced by buttons and displayed.
 
Emesary is used to pass input information from the buttons into the MFD.  This allows MP support hardware interfaces.
 
To make it easier to use SVG files for UI elements, a number of UI primitives are supported which support highlighting, editing in a generic way (see https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Nasal/canvas/PFD/).
 
== UI Primitives ==


===UIElement===
Abstract element, all other elements inherit from this and have the following methods:
* getName : func() { return me._name; },
* setValue : func(value) { me._value = value; },
* getValue : func() { return me._value; },
* highlightElement : func() { },
* unhighlightElement : func() { },
* isEditable : func () { return 0; },
* isInEdit : func() { return me._edit; },
* enterElement : func() { me._edit = 0; return me._value; },
* clearElement : func() { me._edit = 0; },
* editElement : func()  { me._edit = 1; },
* setVisible : func(vis) { },
* incrSmall : func(value) { },
* incrLarge : func(value) { },


{{Stub}}
{{Template:Canvas Navigation}}


== Background ==
===TextElement===
Canvas MFD displays are just canvas textures, which are just represented as "properties", or rather, "property branches" - in the form of /canvas/by-index/texture[x] - you can use the property tree browser to check how these work - but basically, each canvas texture can have multiple "placements", such as "aircraft", "scenery" or GUI (dialog/window) - these placements maintain a reference count internally.
Simple text element, can have values set (setValue) and retrieved (getValue).


Toggling between different image sources is also accomplished by supporting recursion through "nested canvases" - i.e. a canvas can "include" (reference) another canvas and use it as the image source (raster image).
In the SVG file, this will have the name [pagename][name].


Thus, technically, the only thing involved would be coming up with two classes for "displays" and "image generators" - where each would be internally mapped to a canvas texture, the image generator would be "black box" where rendering takes place - while the "display" would be just a simple canvas that references the proper canvas texture, based on the currently selected switch/mode.
Constructor:  new : func (pagename, svg, name, value="", style=nil)
Meanwhile, I would prefer coming up with a real MFD framework that manages different displays/screens and image sources.
* pagename - the name of the page this belongs to, as referenced by the MFD.  This is the prefix for the SVG elements themselves.
* svg - reference the svg Group element that contains the element.
* name - name of the element.  In the SVG file this will be [pagename][name].
* value - initial value
* style - style hash


That should then also help with PFD/ND/CDU and EICAS/EFIS stuff.
===HighlightElement===
An SVG element that is made visible or hidden to indicate that an element is selected, rather than highlighting the text.


Philosopher's MapStructure framework has been specifically designed to support the notion of controllers for these things, so need to add any heavy hacks to the code - we should better work together and ensure that MapStructure ends up in fgdata soon enough ...
In the SVG file, this will have the name [pagename][name].
Gijs already started working on a MFD creation framework for the 744, and as previously mentioned, certain features are going to be identical - regardless of aircraft, i.e. bizjet, boeing, airbus etc - most MFDs will have knobs to adjust brightness or change video sources - so I'd rather keep the general design in mind here, and not implement such things specifically for a certain aircraft. Ultimately, it really just boils down to mapping a few properties to the corresponding canvas properties.


Constructor:  new : func (pagename, svg, name, value="", style=nil)
* pagename - the name of the page this belongs to, as referenced by the MFD.  This is the prefix for the SVG elements themselves.
* svg - reference the svg Group element that contains the element.
* name - name of the element.  In the SVG file this will be [pagename][name].
* value - initial value
* style - style hash


===ScrollElement===
Element that can take a number of pre-defined values. 


{{cquote|I also need a better way to switch pages on the lower EICAS. Right now I delete/re-create the Canvas with this code. It doesn't work well though; at times no page is loaded at all. Of course I cannot delete a Canvas when I have it displayed in a dialog, so this method is probably doomed...
In addition to the [PageName][name] SVG Text element, [PageName][name]Left and [PageName][name]Right SVG elements used to indicate if there are any further values to scroll through.
<ref>{{cite web |url=http://forum.flightgear.org/viewtopic.php?f=19&t=7867&hilit=switch+mfd&start=75#p191702
|title=The making of the Queen
|author=Gijs |date= Sun Oct 13, 2013 9:04 am}}</ref>|Gijs}}


Constructor:  new : func (pageName, svg, name, values, initialIndex=0, style=nil)
* pagename - the name of the page this belongs to, as referenced by the MFD.  This is the prefix for the SVG elements themselves.
* svg - reference the svg Group element that contains the element.
* name - name of the element.  In the SVG file this will be [pagename][name].
* values - array of values that this scroll element can scroll through
* initialIndex - index into the array of values.
* style - style hash


{{cquote|I'm also doing some work on my C-130J cockpit and therefore have got nearly the same problems^^ There are currently five screens with a lot of pages which can be freely placed on any of the screens. I'm not yet sure on how to setup this system in detail. If displays/windows/etc. show exactly the same thing they should also use the same canvas. One approach would be to use a canvas for each page and add one ore more placements to it depending on where it should be displayed.
Additional methods:
Another approach would be to use a canvas for each screen and either reload each page on switching or after loading once hide the according group.
* setValues(values_array) sets the values of the element to values_array
A completely different approach (which probably also will require some core changes) is to allow moving groups between different canvasses and also just to a storage location to move pages around as needed.


<ref>{{cite web |url=http://forum.flightgear.org/viewtopic.php?f=19&t=7867&hilit=switch+mfd&start=90#p191764
===PointerElement===
|title=The making of the Queen
An SVG element that moves horizontally or vertically depending on the value set.
|author=TheTom |date= Mon Oct 14, 2013 5:02 am}}</ref>|TheTom}}


In the SVG file, this will have the name [pagename][name].  [pagename][name] will be shifted linearly in the x axis (or y axis if vertical=1) such that when value >= maxVal it is shifted by scalePx.


{{cquote|It would probably be a good idea to look at existing airliners in FG, such as the 744, 777 and then come up with a simple Nasal-space framework to manage image sources and screens, so that a screen selector would ideally only manage placements, while supporting different MFDs for each pilot - analogous to how A661 has the concept of an image generator (IG) and a cockpit display system (CDS).
Constructor:  new : func (pagename, svg, name, minVal, maxVal, scalePx, vertical=0, value=0, style=nil)
* pagename - the name of the page this belongs to, as referenced by the MFD.  This is the prefix for the SVG elements themselves.
* svg - reference the svg Group element that contains the element.
* name - name of the element.  In the SVG file this will be [pagename][name].
* minVal - minimum value to clamp to
* maxVal - maximum value to clamp to
* scalePx - scale. 
* vertical - whether the element should be shifted horizontally or vertically.
* value - initial value
* style - style hash


For most modern jets it would make sense to introduce some intermediate layer that wraps the main canvas system, so that different displays (PFD, ND, EICAS, M/CDU etc) can be conveniently managed.
===RotatingElement===
An SVG element that rotates around an axis depending on the value set.


Basically, we only need to add a handful of Nasal wrapper classes that provide the building blocks for any kind of EFIS, i.e. generic components such as:
In the SVG file, this will have the name [pagename][name]. [pagename][name] will be rotated around the centerOffset point.


* display (CRT/LCD)
Constructor:  new : func (pagename, svg, name, minVal, maxVal, rangeDeg, centerOffset, value=0, style=nil)
* source selector: http://www.meriweather.com/flightdeck/747/ctr-747.html
* pagename - the name of the page this belongs to, as referenced by the MFD.  This is the prefix for the SVG elements themselves.
* image source (a Nasal class that simply wraps a canvas)
* svg - reference the svg Group element that contains the element.
* display settings (brightness etc)
* name - name of the element.  In the SVG file this will be [pagename][name].
* minVal - minimum value to clamp to
* maxVal - maximum value to clamp to
* rangeDeg - degrees to rotate clockwise when value >= maxVal. 
* centerOffset - Array [x,y] indicating the offset for the rotation.
* value - initial value
* style - style hash


cockpit developers would then ideally use existing components or add new ones as required, for different types of EFIS (777, 747, A320, A380).


PFD/ND and EICAS/ECAM or MCDUs would be built on top of these.<ref>{{cite web |url=http://forum.flightgear.org/viewtopic.php?f=19&t=7867&hilit=switch+mfd&start=90#p191764
===DataEntryElement===
|title=The making of the Queen
Element that allows the user to enter input, e.g. entering an ICAO airport name to search for.  incrLarge() is used to move the cursor between characters, incrSmall() is used to change the current character, or if the DataEntryElement is not being edited, to start editing it.
|author=Hooray |date= Mon Oct 14, 2013 5:02 am}}</ref>|Hooray}}


<references/>
In addition to a [PageName][name] SVG Text element containing the string, there must also be a set of [PageName][name][0...size] elements, each representing a single character for data entry.


== Design ==
Constructor: new : func (pagename, svg, name, value, size, charSet, style=nil)
* Screen
* pagename - the name of the page this belongs to, as referenced by the MFD.  This is the prefix for the SVG elements themselves.
* Image Source
* svg - reference the svg Group element that contains the element.
* Switch/Selector
* name - name of the element.  In the SVG file this will be [pagename][name].
* Placement Manager
* value - initial value
* size - the number of characters in the DataEntryElement
* charSet - string containing the set of valid characters, e.g. "ABDCEFG...."
* style - style hash


<syntaxhighlight lang="nasal">


# wrapper for a cockpit placement
===GroupElement===
var Screen = {
A set of elements used to display a paged list containing lines of elements.  incrSmall() is used to scroll through the list, one line at a time.  Pagination happens automatically.
};


# wrapper for any Nasal class managing a canvas
If there are more values than space to display them, then a scrollbar can be displayed.
var ImageSource = {
};


var SourceSelector = {
The SVG file will contain
};
* [pageName][elementNames ....][0 .... size] elements for the lines of elements to display
* optionally, [pageName][scrollTroughElement] and [pageName][scrollThumbElement] to display a scrollbar.


</syntaxhighlight>
Constructor: new : func (pageName, svg, elementNames, size, highlightElement, arrow=0, scrollTroughElement=nil, scrollThumbElement=nil, scrollHeight=0, style=nil)
* pagename - the name of the page this belongs to, as referenced by the MFD.  This is the prefix for the SVG elements themselves.
* svg - reference the svg Group element that contains the element.
* elementNames - a list of element names that define a single line within the group. 
* size - the number of elements that are displayed on each page.
* highlightElement - the name of the element (in elementNames) that will be highlighted to indicate the selected line.
* arrow - whether the highlightElement is a TextElement (and will be highlighted normally) or an ArrowElement (and will be shown or hidden)
* scrollTroughElement/scrollThumbElement, the names (without pagename prefix) that are used to scroll through the list.  If neither are defined, then no scrollbar will be displayed, though the list can still scroll.
* scrollHeight - the total height through which the scrollThumbElement will move.  Typically this will be the height of the scrollTroughElement minus the height of the scrollThumbElement.
* style - style hash

Revision as of 17:33, 10 February 2020

This article is a stub. You can help the wiki by expanding it.


Introduction

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.

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 SpaceShuttle displays which are in turn based on the http://wiki.flightgear.org/McDonnell_Douglas_F-15_Eagle#MPCD.

For a straightforward example of this in use, see https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Aircraft/F-15/Nasal/MPCD/MPCD_main.nas

For a more complex example as part of the FG1000 glass cockpit, see https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Aircraft/Instruments-3d/FG1000/Nasal/MFD.nas

MCPD Armament Top Level Menu

Design

The PFD/MFD contains multiple pages, each of which can be referenced by buttons and displayed.

Emesary is used to pass input information from the buttons into the MFD. This allows MP support hardware interfaces.

To make it easier to use SVG files for UI elements, a number of UI primitives are supported which support highlighting, editing in a generic way (see https://sourceforge.net/p/flightgear/fgdata/ci/next/tree/Nasal/canvas/PFD/).

UI Primitives

UIElement

Abstract element, all other elements inherit from this and have the following methods:

  • getName : func() { return me._name; },
  • setValue : func(value) { me._value = value; },
  • getValue : func() { return me._value; },
  • highlightElement : func() { },
  • unhighlightElement : func() { },
  • isEditable : func () { return 0; },
  • isInEdit : func() { return me._edit; },
  • enterElement : func() { me._edit = 0; return me._value; },
  • clearElement : func() { me._edit = 0; },
  • editElement : func() { me._edit = 1; },
  • setVisible : func(vis) { },
  • incrSmall : func(value) { },
  • incrLarge : func(value) { },


TextElement

Simple text element, can have values set (setValue) and retrieved (getValue).

In the SVG file, this will have the name [pagename][name].

Constructor: new : func (pagename, svg, name, value="", style=nil)

  • pagename - the name of the page this belongs to, as referenced by the MFD. This is the prefix for the SVG elements themselves.
  • svg - reference the svg Group element that contains the element.
  • name - name of the element. In the SVG file this will be [pagename][name].
  • value - initial value
  • style - style hash

HighlightElement

An SVG element that is made visible or hidden to indicate that an element is selected, rather than highlighting the text.

In the SVG file, this will have the name [pagename][name].

Constructor: new : func (pagename, svg, name, value="", style=nil)

  • pagename - the name of the page this belongs to, as referenced by the MFD. This is the prefix for the SVG elements themselves.
  • svg - reference the svg Group element that contains the element.
  • name - name of the element. In the SVG file this will be [pagename][name].
  • value - initial value
  • style - style hash

ScrollElement

Element that can take a number of pre-defined values.

In addition to the [PageName][name] SVG Text element, [PageName][name]Left and [PageName][name]Right SVG elements used to indicate if there are any further values to scroll through.

Constructor: new : func (pageName, svg, name, values, initialIndex=0, style=nil)

  • pagename - the name of the page this belongs to, as referenced by the MFD. This is the prefix for the SVG elements themselves.
  • svg - reference the svg Group element that contains the element.
  • name - name of the element. In the SVG file this will be [pagename][name].
  • values - array of values that this scroll element can scroll through
  • initialIndex - index into the array of values.
  • style - style hash

Additional methods:

  • setValues(values_array) sets the values of the element to values_array

PointerElement

An SVG element that moves horizontally or vertically depending on the value set.

In the SVG file, this will have the name [pagename][name]. [pagename][name] will be shifted linearly in the x axis (or y axis if vertical=1) such that when value >= maxVal it is shifted by scalePx.

Constructor: new : func (pagename, svg, name, minVal, maxVal, scalePx, vertical=0, value=0, style=nil)

  • pagename - the name of the page this belongs to, as referenced by the MFD. This is the prefix for the SVG elements themselves.
  • svg - reference the svg Group element that contains the element.
  • name - name of the element. In the SVG file this will be [pagename][name].
  • minVal - minimum value to clamp to
  • maxVal - maximum value to clamp to
  • scalePx - scale.
  • vertical - whether the element should be shifted horizontally or vertically.
  • value - initial value
  • style - style hash

RotatingElement

An SVG element that rotates around an axis depending on the value set.

In the SVG file, this will have the name [pagename][name]. [pagename][name] will be rotated around the centerOffset point.

Constructor: new : func (pagename, svg, name, minVal, maxVal, rangeDeg, centerOffset, value=0, style=nil)

  • pagename - the name of the page this belongs to, as referenced by the MFD. This is the prefix for the SVG elements themselves.
  • svg - reference the svg Group element that contains the element.
  • name - name of the element. In the SVG file this will be [pagename][name].
  • minVal - minimum value to clamp to
  • maxVal - maximum value to clamp to
  • rangeDeg - degrees to rotate clockwise when value >= maxVal.
  • centerOffset - Array [x,y] indicating the offset for the rotation.
  • value - initial value
  • style - style hash


DataEntryElement

Element that allows the user to enter input, e.g. entering an ICAO airport name to search for. incrLarge() is used to move the cursor between characters, incrSmall() is used to change the current character, or if the DataEntryElement is not being edited, to start editing it.

In addition to a [PageName][name] SVG Text element containing the string, there must also be a set of [PageName][name][0...size] elements, each representing a single character for data entry.

Constructor: new : func (pagename, svg, name, value, size, charSet, style=nil)

  • pagename - the name of the page this belongs to, as referenced by the MFD. This is the prefix for the SVG elements themselves.
  • svg - reference the svg Group element that contains the element.
  • name - name of the element. In the SVG file this will be [pagename][name].
  • value - initial value
  • size - the number of characters in the DataEntryElement
  • charSet - string containing the set of valid characters, e.g. "ABDCEFG...."
  • style - style hash


GroupElement

A set of elements used to display a paged list containing lines of elements. incrSmall() is used to scroll through the list, one line at a time. Pagination happens automatically.

If there are more values than space to display them, then a scrollbar can be displayed.

The SVG file will contain

  • [pageName][elementNames ....][0 .... size] elements for the lines of elements to display
  • optionally, [pageName][scrollTroughElement] and [pageName][scrollThumbElement] to display a scrollbar.

Constructor: new : func (pageName, svg, elementNames, size, highlightElement, arrow=0, scrollTroughElement=nil, scrollThumbElement=nil, scrollHeight=0, style=nil)

  • pagename - the name of the page this belongs to, as referenced by the MFD. This is the prefix for the SVG elements themselves.
  • svg - reference the svg Group element that contains the element.
  • elementNames - a list of element names that define a single line within the group.
  • size - the number of elements that are displayed on each page.
  • highlightElement - the name of the element (in elementNames) that will be highlighted to indicate the selected line.
  • arrow - whether the highlightElement is a TextElement (and will be highlighted normally) or an ArrowElement (and will be shown or hidden)
  • scrollTroughElement/scrollThumbElement, the names (without pagename prefix) that are used to scroll through the list. If neither are defined, then no scrollbar will be displayed, though the list can still scroll.
  • scrollHeight - the total height through which the scrollThumbElement will move. Typically this will be the height of the scrollTroughElement minus the height of the scrollThumbElement.
  • style - style hash