Canvas EFIS framework: Difference between revisions
mNo edit summary |
mNo edit summary |
||
(One intermediate revision by the same user not shown) | |||
Line 75: | Line 75: | ||
* If an animation is triggered only by one property and that property is unlikely to change often, remove the prop / animation from the update loop and use a listener instead. The listener may read other props as needed. A listener does not consume any CPU unless the property it listens to has changed, in which case the listener function is run. | * If an animation is triggered only by one property and that property is unlikely to change often, remove the prop / animation from the update loop and use a listener instead. The listener may read other props as needed. A listener does not consume any CPU unless the property it listens to has changed, in which case the listener function is run. | ||
== Framework documentation == | == Framework API documentation == | ||
The following sections document the framework classes and their methods. | |||
== Class EFIS == | |||
This is the top level class which ties together the different parts like display units, sources and cockpit controls. | This is the top level class which ties together the different parts like display units, sources and cockpit controls. | ||
=== new() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = new(display_names, object_names, canvas_settings=nil); | |syntax = new(display_names, object_names, canvas_settings=nil); | ||
Line 93: | Line 93: | ||
}} | }} | ||
=== setPowerProp() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = setPowerProp(path); | |syntax = setPowerProp(path); | ||
Line 101: | Line 101: | ||
}} | }} | ||
=== setWindowSize() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = setWindowSize(window_size); | |syntax = setWindowSize(window_size); | ||
Line 109: | Line 109: | ||
}} | }} | ||
=== setDUPowerProps() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = setDUPowerProps(power_props, minimum_power=0); | |syntax = setDUPowerProps(power_props, minimum_power=0); | ||
Line 119: | Line 119: | ||
}} | }} | ||
=== addSource() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = addSource(efis_canvas); | |syntax = addSource(efis_canvas); | ||
Line 127: | Line 127: | ||
}} | }} | ||
=== addDisplaySwapControl() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = addDisplaySwapControl(ctrl, mappings, callback=nil); | |syntax = addDisplaySwapControl(ctrl, mappings, callback=nil); | ||
Line 139: | Line 139: | ||
}} | }} | ||
=== addSourceSelector() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = addSourceSelector(selected, target, sources=nil); | |syntax = addSourceSelector(selected, target, sources=nil); | ||
Line 151: | Line 151: | ||
}} | }} | ||
=== setDefaultMapping() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = setDefaultMapping(mapping); | |syntax = setDefaultMapping(mapping); | ||
Line 159: | Line 159: | ||
}} | }} | ||
=== displayWindow() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = displayWindow(id); | |syntax = displayWindow(id); | ||
Line 168: | Line 168: | ||
== Class EFISCanvas == | |||
EFISCanvas manages a single canvas for you. | EFISCanvas manages a single canvas for you. | ||
It offers a bunch of little helpers to make the live of an aircraft developer easier. | It offers a bunch of little helpers to make the live of an aircraft developer easier. | ||
These helpers mostly do animation and update stuff on canvas elements in an efficient way. | These helpers mostly do animation and update stuff on canvas elements in an efficient way. | ||
=== new() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = new(name, svgfile); | |syntax = new(name, svgfile); | ||
Line 183: | Line 183: | ||
}} | }} | ||
=== getPath() === | |||
returns path of canvas | returns path of canvas | ||
=== getCanvas() === | |||
returns canvas instance | returns canvas instance | ||
=== getRoot() === | |||
returns root group of the canvas | returns root group of the canvas | ||
=== setUpdateN() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = setUpdateN(node); | |syntax = setUpdateN(node); | ||
Line 200: | Line 200: | ||
}} | }} | ||
=== loadsvg() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = loadsvg(file); | |syntax = loadsvg(file); | ||
Line 208: | Line 208: | ||
}} | }} | ||
=== addUpdateFunction() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = addUpdateFunction(f, interval); | |syntax = addUpdateFunction(f, interval); | ||
Line 218: | Line 218: | ||
}} | }} | ||
=== startUpdates() === | |||
Start all update timers. | Start all update timers. | ||
=== stopUpdates() === | |||
Stop all update timers. | Stop all update timers. | ||
=== getInstr() === | |||
When updating EFIS displays, there is a need to access properties under /instrumentation very often. | When updating EFIS displays, there is a need to access properties under /instrumentation very often. | ||
To avoid the rather expensive append operator ("~") and property path lookups, you should use this little helper. | To avoid the rather expensive append operator ("~") and property path lookups, you should use this little helper. | ||
Line 242: | Line 242: | ||
}} | }} | ||
=== updateTextElement() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = updateTextElement(svgkey, text, color=nil); | |syntax = updateTextElement(svgkey, text, color=nil); | ||
Line 254: | Line 254: | ||
}} | }} | ||
=== _updateClip() === | |||
private function that sets clipping for an element, if there is another element with the same name extended by "_clip". | private function that sets clipping for an element, if there is another element with the same name extended by "_clip". | ||
=== Listener factories === | |||
{{note| | {{note| | ||
The following listener factories will return a function. The general usage is | The following listener factories will return a function. The general usage is | ||
Line 269: | Line 269: | ||
==== _makeListener_showHide() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = _makeListener_showHide(svgkey, value=nil); | |syntax = _makeListener_showHide(svgkey, value=nil); | ||
Line 279: | Line 279: | ||
}} | }} | ||
==== _makeListener_rotate() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = _makeListener_rotate(svgkey, factors=nil); | |syntax = _makeListener_rotate(svgkey, factors=nil); | ||
Line 289: | Line 289: | ||
}} | }} | ||
==== _makeListener_translate() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = _makeListener_translate(svgkey, fx, fy); | |syntax = _makeListener_translate(svgkey, fx, fy); | ||
Line 301: | Line 301: | ||
}} | }} | ||
==== _makeListener_setColor() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = _makeListener_setColor(svgkey, color_true, color_false); | |syntax = _makeListener_setColor(svgkey, color_true, color_false); | ||
Line 313: | Line 313: | ||
}} | }} | ||
==== _makeListener_updateText() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = _makeListener_updateText(svgkey, format="%s", default=""); | |syntax = _makeListener_updateText(svgkey, format="%s", default=""); | ||
Line 326: | Line 326: | ||
== | == Class DisplayUnit == | ||
This class is used by the EFIS class. DisplayUnit creates and places a canvas to a 3D object specified in new(). | This class is used by the EFIS class. DisplayUnit creates and places a canvas to a 3D object specified in new(). | ||
=== new() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = new(name, canvas_settings, screen_obj, parent_obj = nil); | |syntax = new(name, canvas_settings, screen_obj, parent_obj = nil); | ||
Line 343: | Line 343: | ||
}} | }} | ||
=== setSource() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = display_unit.setSource(path); | |syntax = display_unit.setSource(path); | ||
Line 351: | Line 351: | ||
}} | }} | ||
=== setPowerSource() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = display_unit.setPowerSource(prop, min); | |syntax = display_unit.setPowerSource(prop, min); | ||
Line 365: | Line 365: | ||
}} | }} | ||
=== asWindow() === | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = display_unit.asWindow(window_size); | |syntax = display_unit.asWindow(window_size); | ||
Line 374: | Line 374: | ||
== EICAS message system == | |||
The message system decouples the update of canvas text elements (e.g. message lines) from the handling of messages. | The message system decouples the update of canvas text elements (e.g. message lines) from the handling of messages. | ||
It is configured with the necessary message classes (e.g. warnings, caution messages, status messages) and a list of all possible messages. | It is configured with the necessary message classes (e.g. warnings, caution messages, status messages) and a list of all possible messages. | ||
Line 380: | Line 380: | ||
Alternatively, a message can be shown or hidden via the Nasal methods (see also the HOWTO section for examples and best practices). | Alternatively, a message can be shown or hidden via the Nasal methods (see also the HOWTO section for examples and best practices). | ||
=== | === Class MessageSystem === | ||
==== new() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = new(page_length, prop_path); | |syntax = new(page_length, prop_path); | ||
Line 391: | Line 391: | ||
}} | }} | ||
==== setPowerProp() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = setPowerProp(prop_path); | |syntax = setPowerProp(prop_path); | ||
Line 399: | Line 399: | ||
}} | }} | ||
==== addMessageClass() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = addMessageClass(class_name, paging, color = nil); | |syntax = addMessageClass(class_name, paging, color = nil); | ||
Line 407: | Line 407: | ||
}} | }} | ||
==== addMessages() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = addMessages(class, messages); | |syntax = addMessages(class, messages); | ||
Line 417: | Line 417: | ||
}} | }} | ||
==== addAuralAlerts() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = addAuralAlerts(alert_hash); | |syntax = addAuralAlerts(alert_hash); | ||
Line 425: | Line 425: | ||
}} | }} | ||
==== setMessage() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = setMessage(class, msg_id, visible=1); | |syntax = setMessage(class, msg_id, visible=1); | ||
Line 437: | Line 437: | ||
}} | }} | ||
==== init() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = init(); | |syntax = init(); | ||
Line 443: | Line 443: | ||
}} | }} | ||
==== hasUpdate() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = hasUpdate(); | |syntax = hasUpdate(); | ||
Line 449: | Line 449: | ||
}} | }} | ||
==== setPageLength() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = setPageLength(length); | |syntax = setPageLength(length); | ||
Line 457: | Line 457: | ||
}} | }} | ||
==== getPageLength() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = getPageLength(); | |syntax = getPageLength(); | ||
Line 463: | Line 463: | ||
}} | }} | ||
==== getFirstUpdateIndex() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = getFirstUpdateIndex(); | |syntax = getFirstUpdateIndex(); | ||
Line 469: | Line 469: | ||
}} | }} | ||
==== getActiveMessages() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = getActiveMessages(); | |syntax = getActiveMessages(); | ||
Line 475: | Line 475: | ||
}} | }} | ||
==== getMessageID() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = getMessageID(class, msg_text); | |syntax = getMessageID(class, msg_text); | ||
Line 485: | Line 485: | ||
}} | }} | ||
==== disableMessage() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = disableMessage(class, msg_id); | |syntax = disableMessage(class, msg_id); | ||
Line 495: | Line 495: | ||
}} | }} | ||
==== enableMessage() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = enableMessage(class, msg_id); | |syntax = enableMessage(class, msg_id); | ||
Line 507: | Line 507: | ||
'''-- following methods are for message output on a canvas --''' | '''-- following methods are for message output on a canvas --''' | ||
==== setCanvasGroup() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = setCanvasGroup(group); | |syntax = setCanvasGroup(group); | ||
Line 515: | Line 515: | ||
}} | }} | ||
==== createCanvasTextLines() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = createCanvasTextLines(left, top, line_spacing, font_size); | |syntax = createCanvasTextLines(left, top, line_spacing, font_size); | ||
Line 527: | Line 527: | ||
}} | }} | ||
==== createPageIndicator() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = createPageIndicator(left, top, font_size, format_string = nil); | |syntax = createPageIndicator(left, top, font_size, format_string = nil); | ||
Line 539: | Line 539: | ||
}} | }} | ||
==== updateCanvas() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = updateCanvas(); | |syntax = updateCanvas(); | ||
Line 545: | Line 545: | ||
}} | }} | ||
==== updatePageIndicator() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = updatePageIndicator(current, total); | |syntax = updatePageIndicator(current, total); | ||
Line 557: | Line 557: | ||
=== Format for message definitions === | |||
MessageSystem.addMessages() expects a vector of message definitions. A message definition is a hash with the keys ''msg'', ''prop'', ''aural'', ''condition'' (only msg is required, the rest is optional). | MessageSystem.addMessages() expects a vector of message definitions. A message definition is a hash with the keys ''msg'', ''prop'', ''aural'', ''condition'' (only msg is required, the rest is optional). | ||
Line 570: | Line 570: | ||
The optional ''aural'' element can be any key of the aural configuration hash passed to MessageSystem.addAuralAlerts(). If the | The optional ''aural'' element can be any key of the aural configuration hash passed to MessageSystem.addAuralAlerts(). If the | ||
{{#tag:syntaxhighlight| | |||
{ | { | ||
msg: "Message Text", | msg: "Message Text", | ||
Line 585: | Line 585: | ||
var EICASMessages = [ | var EICASMessages = [ | ||
{msg: "APU FIRE", prop: "engines/engine[2]/on-fire", aural: "firebell"}, | {msg: "APU FIRE", prop: "engines/engine[2]/on-fire", aural: "firebell"}, | ||
{msg: "AC 1 AUTOXFER", prop: "systems/AC/system[1]/serviceable", condition: {eq: 0}}, | {msg: "AC 1 AUTOXFER", prop: "systems/AC/system[1]/serviceable", condition: {eq: 0} }, | ||
{msg: "AC BUS 1", prop: "systems/AC/outputs/bus1", condition: {lt: 100}}, | {msg: "AC BUS 1", prop: "systems/AC/outputs/bus1", condition: {lt: 100} }, | ||
{msg: "ELT ON", prop: ""}, | {msg: "ELT ON", prop: ""}, | ||
]; | ]; | ||
|lang="nasal"}} | |||
=== Class MessageClass === | |||
Helper class for MessageSystem. Stores configuration data for a message class. | Helper class for MessageSystem. Stores configuration data for a message class. | ||
=== Class Pager === | |||
Helper class for MessageSystem. | Helper class for MessageSystem. | ||
The pager class takes a vector of messages and splits it into pages (sub vectors of defined length). | The pager class takes a vector of messages and splits it into pages (sub vectors of defined length). | ||
Line 612: | Line 611: | ||
|} | |} | ||
==== new() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = new(page_length, prop_path); | |syntax = new(page_length, prop_path); | ||
Line 622: | Line 621: | ||
}} | }} | ||
==== page() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = page(lines); | |syntax = page(lines); | ||
Line 630: | Line 629: | ||
}} | }} | ||
==== pageChanged() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = pageChanged(); | |syntax = pageChanged(); | ||
Line 636: | Line 635: | ||
}} | }} | ||
==== setPageLength() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = setPageLength(lines); | |syntax = setPageLength(lines); | ||
Line 644: | Line 643: | ||
}} | }} | ||
==== setPage() ==== | |||
{{Nasal doc | {{Nasal doc | ||
|syntax = setPage(page); | |syntax = setPage(page); | ||
Line 652: | Line 651: | ||
}} | }} | ||
==== getPageCount() ==== | |||
Returns total number of pages, according to last run of page() | Returns total number of pages, according to last run of page() | ||
==== getCurrentPage() ==== | |||
Returns current page, according to last run of page() | Returns current page, according to last run of page() | ||
== HOWTO and Examples == | == HOWTO and Examples == | ||
to be written / reference implementation in CRJ700 familiy to be published after framework is published. | to be written / reference implementation in CRJ700 familiy to be published after framework is published. |
Revision as of 20:04, 13 February 2020
The FlightGear forum has a subforum related to: Canvas |
Foreword
The canvas EFIS framework (created by jsb) is by-product of the EFIS development done for the CRJ700 family, which uses Rockwell Collins Proline. It is published as a separate framework in the hope that it is useful for other aircraft as well, however some features may be rather specific. The biggest motivation and primary design goal was to support "near optimal" performance but that does not come automatically of course.
For the bigger picture see also Howto:Add an EFIS to your aircraft.
Introduction and Overview
An EFIS usually comprises multiple display units (DU for short; CRT or LCD screens) to show all the information relevant to the pilot (e.g. 2x PFD, 2x MFD, 2x EICAS in the CRJ700). Some displays like an EICAS / ECAM may show different pages for the systems of the aircraft, they can be select by the pilot via a corresponding control panel. Furthermore, displays can be swapped in case of a DU fault. In the CRJ700 the pilot can select display sources by turning the corresponding knobs.
The EFIS framework provides Nasal classes to handle the following:
Display unit
- reference to a 3D object in the cockpit ("display unit") on which the canvas texture shall be displayed
- power switch property
- image source (canvas reference)
Note: Dimming of display can be done by defining an animation in the XML going with the 3D model of the DU. See example below.
Image source (EFISCanvas)
- base class for EFIS/EICAS pages handling some SVG stuff
- can load a SVG file and create clipping from <name>_clip elements
- allows to register multiple update functions with individual update intervals
- update functions can be en-/disabled by a single property that should reflect the visibility of the canvas (e.g. do not waste CPU with updating pages that are not currently displayed)
- several listener factories for common animations like show/hide, translation, rotation
Display selectors / routing
- Handle input events from cockpit knobs to switch displays / pages
EICAS Message system
- Handle EICAS messages efficiently without eating up to much frame rate
- Message queue: decouple canvas updates from message arrival
Thoughts on implementation
Design goal: EFIS framework should support best performance by offering different update methods and update rates for efficency.
Update rates
The EFIS/EICAS basically monitors the whole aircraft so it will have to read lots of properties. For efficiency the properties must be grouped into different classes already in the design phase, that is, the aircraft developer shall carefully think about how often a property changes. Some props are written to at frame rate by property rules but that does not mean the value changes every time. Furthermore, not every change has the same relevance, not everything needs to be displayed real time (= frame rate).
update rate | Interval | comment |
---|---|---|
2 | 0.500s | low performance needs |
10 | 0.100s | low to med. performance but no fluent animation |
20 | 0.050s | |
30 | 0.033s | smooth canvas animation but possibly high performance need |
60 | 0.017s |
Some examples:
- PFD contains many "real-time" elements like attitude indicator, speed, altitude etc. They should be updated probably at frame rate (min. 25 Hz) Other elements like radio frequency may be ok to update after 100ms or even 0.5s
- An EICAS page displaying the door status maybe just fine with one update per second as doors move slowly and you won't see the door from outside and EICAS display inside the cockpit at the same time so a delay of one second does not matter
Update methods
- Simple approach is to write an update loop that reads all relevant props, calculates the needed updates and updates the canvas elements (which in the end means writing properties) -> tends to be inefficient.
- Property read/write is somehow "expensive" and can cost you a lot of frame rate. So the next better idea could be to limit the rate at which the update function is called - usually by a timer with some sensible interval.
- If an animation is triggered only by one property and that property is unlikely to change often, remove the prop / animation from the update loop and use a listener instead. The listener may read other props as needed. A listener does not consume any CPU unless the property it listens to has changed, in which case the listener function is run.
Framework API documentation
The following sections document the framework classes and their methods.
Class EFIS
This is the top level class which ties together the different parts like display units, sources and cockpit controls.
new()
new(display_names, object_names, canvas_settings=nil);
Returns a new EFIS object
- display_names
- Vector of display names
- object_names
- Vector of 3D object names for canvas placement.
- canvas_settings
- Optional config hash for canvas that is merged with default settings of EFIS class
setPowerProp()
setPowerProp(path);
Creates listener for property path. Power on will start all display update functions, power of will stop them.
- path
- Property path to EFIS power property (bool).
setWindowSize()
setWindowSize(window_size);
Sets default size for canvas windows
- window_size
- Vector [size_x, size_y]
setDUPowerProps()
setDUPowerProps(power_props, minimum_power=0);
Sets default size for canvas windows
- power_props
- Vector of property paths, must be of same size as display_names given to new().
- minimum_power
- Passed to DisplayUnit.setPowerSource()
addSource()
addSource(efis_canvas);
Adds a EFISCanvas as display source, returns source ID to be used in mappings.
- efis_canvas
- A EFISCanvas instance created with EFISCanvas.new()
addDisplaySwapControl()
addDisplaySwapControl(ctrl, mappings, callback=nil);
Adds a listener for "ctrl", activates new source-display-mapping on change. Use this to implement a display swap knob in the cockpit.
- callback
- optional function that will be called with current ctrl value
addSourceSelector()
addSourceSelector(selected, target, sources=nil);
Adds a listener for "selected" which will read the target DU from "target" and sets its source according to "selected". Use this e.g. for a EICAS / ECAM page select panel.
- sources
- optional vector; selected -> source ID (as returned by addSource), defaults to all registered sources
setDefaultMapping()
setDefaultMapping(mapping);
Sets and activates a default mapping of sources to display units.
- mapping
- Vector [source_id1, source_id2, ...] or hash {DisplayName: source_id}
displayWindow()
displayWindow(id);
Opens display unit as canvas window.
- id
- Index of display
Class EFISCanvas
EFISCanvas manages a single canvas for you. It offers a bunch of little helpers to make the live of an aircraft developer easier. These helpers mostly do animation and update stuff on canvas elements in an efficient way.
new()
new(name, svgfile);
Returns a new EFISCanvas
- name
- Used e.g. in window title (see below)
- svgfile
- Filename of a SVG to load. (see also: loadsvg)
getPath()
returns path of canvas
getCanvas()
returns canvas instance
getRoot()
returns root group of the canvas
setUpdateN()
setUpdateN(node);
set a node that is checked in all registered update functions, they will only be run, if the value of this node is true
- node
- props.node object
loadsvg()
loadsvg(file);
Parses the given SVG file into the root group of the EFISCanvas. Clipping is setup for elements that have a corresponding element with the same name extended by "_clip".
- file
- filepath passed to canvas.parsesvg
addUpdateFunction()
addUpdateFunction(f, interval);
Creates a timer with given interval that runs the given function if the value of the update node is true.
- f
- Nasal function to run
- interval
- Interval in seconds
startUpdates()
Start all update timers.
stopUpdates()
Stop all update timers.
getInstr()
When updating EFIS displays, there is a need to access properties under /instrumentation very often. To avoid the rather expensive append operator ("~") and property path lookups, you should use this little helper. It creates node objects (only once) to access properties like "/instrumentation/"~sys~"["~id~"]/"~prop,
getInstr(sys, prop, default=0, id=nil);
Helper to quickly access instrumentation properties with this name schema: "/instrumentation/"~sys~"["~id~"]/"~prop
- sys
- the system name under /instrumentation
- prop
- default
- Optional default value to return, if getValue() returns nil
- id
- Optional override the id of the current EFISCanvas obj (me.id)
updateTextElement()
updateTextElement(svgkey, text, color=nil);
Updates a single text element and optionally sets its color.
- svgkey
- Name of the text element to update.
- text
- new text, passed to setText()
- color
- Vector [r,g,b] or valid color name from EFISCanvas.colors
_updateClip()
private function that sets clipping for an element, if there is another element with the same name extended by "_clip".
Listener factories
Note
The following listener factories will return a function. The general usage is setlistener("/some/property/name", _makeListener_xxx(parameters), 1, 0); As usual, the listened-to node is passed into the listener function as first argument and the function will getValue() and do something meaningful with the value. |
The first argument to the listener factory functions is always svgkey which can be either a string naming one canvas element or a vector of such strings.
_makeListener_showHide()
_makeListener_showHide(svgkey, value=nil);
Returns listener to show/hide element(s).
- svgkey
- String or vector; the element(s) to update.
- value
- optional value to trigger show(); otherwise node.value will be implicitly treated as bool.
_makeListener_rotate()
_makeListener_rotate(svgkey, factors=nil);
Returns listener to rotate one or more canvas element(s) by property value. If svgkey is a vector, factors must be a hash having each elemnent in svgkey as a key and the corresponding factor.
- svgkey
- String or vector; the element(s) to update.
- factors=nil
- Factor for setRotation(value * factor); defaults to 1 if not given
_makeListener_translate()
_makeListener_translate(svgkey, fx, fy);
Returns listener to translate one or more canvas element(s) by property value. If svgkey is a vector, fx and fy must be hashes having each elemnent in svgkey as a key and the corresponding factor for translation.
- svgkey
- String or vector; the element(s) to update.
- fx
- Factor for translation in x direction
- fy
- Factor for translation in y direction
_makeListener_setColor()
_makeListener_setColor(svgkey, color_true, color_false);
Returns generic listener to change element color. Both color arguments can be either a vector [r,g,b] or a valid name (hash key) for EFISCanvas.colors;
- svgkey
- String or vector; element(s) on which to call setColor().
- color_true
- Color to use, if getValue() is true.
- color_false
- Color to use, if getValue() is false.
_makeListener_updateText()
_makeListener_updateText(svgkey, format="%s", default="");
Returns listener to update a canvas text element from property value.
- svgkey
- The element to update.
- format
- A valid format string for printf()
- default
- Value to use, if getValue() returns nil or false.
Class DisplayUnit
This class is used by the EFIS class. DisplayUnit creates and places a canvas to a 3D object specified in new().
new()
new(name, canvas_settings, screen_obj, parent_obj = nil);
Configure the nasal file to load.
- name
- Used e.g. in window title (see below)
- canvas_settings
- passed to canvas.new()
- screen_obj
- used for canvas placement
- parent_obj
- used for canvas placement
setSource()
display_unit.setSource(path);
Select a canvas to show on the display unit.
- path
- A valid canvas path as returned by the canvas.getPath() method.
setPowerSource()
display_unit.setPowerSource(prop, min);
Sets a listener to prop. Display will show only if property value >= min
- prop
- Path to property that represents display power supply.
- min
- Minimum value of property to show display.
Example
var my_display_unit = DisplayUnit.new("PFD1");
# display turns on when DC power is >= 24 volts
my_display_unit.setPowerSource("/systems/DC/outputs/pfd1", 24);
asWindow()
display_unit.asWindow(window_size);
Opens the display in a canvas window.
- window_size
- Vector [size_x, size_y]; passed to canvas.Window.new().
EICAS message system
The message system decouples the update of canvas text elements (e.g. message lines) from the handling of messages. It is configured with the necessary message classes (e.g. warnings, caution messages, status messages) and a list of all possible messages. For each message a corresponding property can be defined that controls whether or not the message is shown. Alternatively, a message can be shown or hidden via the Nasal methods (see also the HOWTO section for examples and best practices).
Class MessageSystem
new()
new(page_length, prop_path);
Creates a new message system.
- page_length
- Maximum page length. Page length can be changed at runtime but not exceed this initial value.
- prop_path
- Property path, some config and data of the message system will be put here.
setPowerProp()
setPowerProp(prop_path);
Sets a listener to the "power switch" property. Will call init() if property changes to true.
- prop_path
- Path to a bool property representing the EFIS power state.
addMessageClass()
addMessageClass(class_name, paging, color = nil);
Sets the number of lines per page.
- lines
- Number of lines per page
addMessages()
addMessages(class, messages);
Add message definitions to a class. Setup listeners according to message definitions that call setMessage() accordingly.
- class
- Class identifier returned by addMessageClass()
- messages
- Vector of message definitions. See below for format.
addAuralAlerts()
addAuralAlerts(alert_hash);
Set aural alert configuration hash
- alert_hash
- Hash; aural_name: {path: "myAircraft/Sounds", file: "sound.wav", volume: 1.0 }
setMessage()
setMessage(class, msg_id, visible=1);
Show or hide a message, optional trigger aural alert on show. Does nothing if message is already in desired state.
- class
- Class identifier returned by addMessageClass()
- msg_id
- see getMessageID()
- visible
- bool; 0 = hide, otherwise show
init()
init();
Used on power up to check for active messages. Enables sound queue for aural alerts.
hasUpdate()
hasUpdate();
Returns update flag.
setPageLength()
setPageLength(length);
Change page length. Length cannot exceed initial page length (see new())
- length
- New number of lines per page.
getPageLength()
getPageLength();
Returns current page length.
getFirstUpdateIndex()
getFirstUpdateIndex();
Returns the index of the first line that has changed.
getActiveMessages()
getActiveMessages();
Returns message queue and clears the hasUpdate flag.
getMessageID()
getMessageID(class, msg_text);
Returns numeric id for a message in given class.
- class
- Class identifier returned by addMessageClass()
- msg_text
- Message string to search for.
disableMessage()
disableMessage(class, msg_id);
Inhibit message (or all messages in class if no msg_id is given).
- class
- Class identifier returned by addMessageClass()
- msg_id
- see getMessageID()
enableMessage()
enableMessage(class, msg_id);
Re-enable message (or all messages in class if no id is given).
- class
- Class identifier returned by addMessageClass()
- msg_id
- see getMessageID()
-- following methods are for message output on a canvas --
setCanvasGroup()
setCanvasGroup(group);
Set an existing canvas group to create text elements on.
- group
- A valid canvas group element
createCanvasTextLines()
createCanvasTextLines(left, top, line_spacing, font_size);
Show or hide a message, optional trigger aural alert on show. Does nothing if message is already in desired state.
- left, top
- Position of text element.
- line_spacing
- see getMessageID()
- font_size
- Font size.
createPageIndicator()
createPageIndicator(left, top, font_size, format_string = nil);
Creates a text element for a page indicator on the canvas group set with setCanvasGroup().
- left, top
- Position of text element
- font_size
- Font size.
- format_string
- Optional printf format string, defaults to "Page %2d/%2d"
updateCanvas()
updateCanvas();
Call this regularly to update text lines on canvas with the content of the message queue.
updatePageIndicator()
updatePageIndicator(current, total);
Updates the page indicator text element.
- current
- Currently displayed page.
- total
- Total number of pages.
Format for message definitions
MessageSystem.addMessages() expects a vector of message definitions. A message definition is a hash with the keys msg, prop, aural, condition (only msg is required, the rest is optional).
prop shall be used if
- the visibility of a message depends on only one property
- this property does NOT change frequently
The optional condition element reqires prop and is again a hash where you can define up to four compare values. For each existing key a listener to prop will be created performing the selected comparision. It is up to the developer to provide a reasonable configuration here.
The optional aural element can be any key of the aural configuration hash passed to MessageSystem.addAuralAlerts(). If the
{
msg: "Message Text",
prop: "/some/bool/trigger", # optional
aural: "sound_name", # optional
condition: { # optional
eq: "foo",
ne: "bar",
lt: 21, # "less than",
gt: 42, # "greater than",
},
}
# example:
var EICASMessages = [
{msg: "APU FIRE", prop: "engines/engine[2]/on-fire", aural: "firebell"},
{msg: "AC 1 AUTOXFER", prop: "systems/AC/system[1]/serviceable", condition: {eq: 0} },
{msg: "AC BUS 1", prop: "systems/AC/outputs/bus1", condition: {lt: 100} },
{msg: "ELT ON", prop: ""},
];
Class MessageClass
Helper class for MessageSystem. Stores configuration data for a message class.
Class Pager
Helper class for MessageSystem. The pager class takes a vector of messages and splits it into pages (sub vectors of defined length). It can be configured via the Nasal methods described below or via some properties on the fly.
Config nodes
property name | Type and access | Description |
---|---|---|
page_length | (INT, r/w) | maximum number of lines per page. |
page | (INT, r/w) | currently selected page. |
pages | (INT, r/o) | number of pages according to last run of the page() method. |
new()
new(page_length, prop_path);
Configure the nasal file to load.
- page_length
- Maximum lines per page.
- prop_path
- Property base path; pager configuration nodes will be created under this path.
page()
page(lines);
Return lines of current page; sticky messages will not be paged.
- lines
- Vector of messages
pageChanged()
pageChanged();
Return the page-changed flag and resets it to false, so calling this method again will return false unless another page has been selected in the meantime.
setPageLength()
setPageLength(lines);
Update the value of the page_length node (the number of lines per page).
- lines
- Number of lines per page
setPage()
setPage(page);
Update the value of page node used by page() to return the right lines.
- page
- Selected page
getPageCount()
Returns total number of pages, according to last run of page()
getCurrentPage()
Returns current page, according to last run of page()
HOWTO and Examples
to be written / reference implementation in CRJ700 familiy to be published after framework is published.