Emesary MFD bridge
This article is a stub. You can help the wiki by expanding it. |
Objective
Document ongoing talks and ideas about using the Emesary framework to provide a sane interfacing mechanism for Canvas based avionics (and lower-level MapStructure building blocks) using Richard's Canvas MFD Framework.
Background
Over time, the NavDisplay code has become a huge mess - it is far from being easily maintainable, yet it is the most widely used Canvas-based MFD in use today, mainly because it is generic and can be easily reused, which is due to it having been designed using just a standalone GUI dialog.
So there are some lessons to be learnt from the whole experience.
Nasal modules
- F-15/Nasal/MPCD/MPCD_main.nas
- F-15/Nasal/MPCD/MPCD_0_0.svg
- fgdata/Nasal/canvas/MFD_Generic.nas
- fgdata/Nasal/emesary.nas
Use cases
Design goals
The NavDisplay code is structured to support:
- GUI-based prototyping and design/testing of layers
- truly indenpendent instances (an arbitrary number of them)
- styling of ND layers/components according to aircraft specifics (think Boeing vs. Airbus ND)
- encapsulate aircraft/system specifics (think differences between fdm, autopilot and route manager)
- encourage a modular and reusable design of additions, so that all aircraft developers using the framework can benefit automatically
Implementation
We will be hooking up the Canvas MFD framework to a conventional Canvas GUI dialog, so that a MFD instrument can be shown in a standalone GUI dialog. The next step will involve using the Emesary framework to create custom events and actions.
Instantiating a MFD dialog
See Howto:Creating a Canvas GUI dialog file for the main article about this subject. |
var (width,height) = (640,480);
var title = 'Canvas MFD/Emesary Bridge';
var window = canvas.Window.new([width,height],"dialog").set('title',title);
##
# the del() function is the destructor of the Window
# which will be called upon termination (dialog closing)
# you can use this to do resource management (clean up timers, listeners or background threads)
#window.del = func()
#{
# print("Cleaning up window:",title,"\n");
# explanation for the call() technique at: http://wiki.flightgear.org/Object_oriented_programming_in_Nasal#Making_safer_base-class_calls
# call(canvas.Window.del, [], me);
#};
# adding a canvas to the new window and setting up background colors/transparency
var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));
# creating the top-level/root group which will contain all other elements/group
var root = myCanvas.createGroup();
var svgURL = "https://sourceforge.net/p/flightgear/fgaddon/HEAD/tree/trunk/Aircraft/F-15/Nasal/MPCD/MPCD_0_0.svg?format=raw";
var svgFile = http.load(svgURL);
svgFile.done( func(request) {
debug.dump( request.response );
});
var MPCD_Device =
{
#
# 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
# Parameters
# - designation - Flightdeck Legend for this
# - model_element - name of the 3d model element that is to be used for drawing
# - model_index - index of the device
new : func(designation, model_element, model_index=0)
{
var obj = {parents : [MPCD_Device] };
obj.designation = designation;
obj.model_element = model_element;
var dev_canvas= canvas.new({
"name": designation,
"size": [1024,1024],
"view": [740,680],
"mipmapping": 1
});
dev_canvas.addPlacement({"node": model_element});
dev_canvas.setColorBackground(0.003921,0.1764,0, 0);
# Create a group for the parsed elements
obj.PFDsvg = dev_canvas.createGroup();
var pres = canvas.parsesvg(obj.PFDsvg, "Nasal/MPCD/MPCD_0_0.svg");
# Parse an SVG file and add the parsed elements to the given group
printf("MPCD : %s Load SVG %s",designation,pres);
obj.PFDsvg.setTranslation (270.0, 197.0);
#
# create the object that will control all of this
obj.num_menu_buttons = 20;
obj.PFD = PFD_Device.new(obj.PFDsvg, obj.num_menu_buttons, "MI_", dev_canvas);
obj.PFD._canvas = dev_canvas;
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
obj.addPages();
return obj;
},
addPages : func
{
me.p1_1 = me.PFD.addPage("Aircraft Menu", "p1_1");
me.p1_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");
if (cdt != nil)
me.page1_1.date.setText(substr(cdt,5,2)~"/"~substr(cdt,8,2)~"/"~substr(cdt,2,2)~"Z");
};
me.pjitds_1 = PFD_NavDisplay.new(me.PFD,"Situation", "mpcd-sit", "pjitds_1", "jtids_main");
# use the radar range as the ND range.
#
# Page 1 is the time display
me.p1_1.update = func
{
var sec = getprop("instrumentation/clock/indicated-sec");
me.time.setText(getprop("sim/time/gmt-string")~"Z");
var cdt = getprop("sim/time/gmt");
if (cdt != nil)
me.date.setText(substr(cdt,5,2)~"/"~substr(cdt,8,2)~"/"~substr(cdt,2,2)~"Z");
};
#
# armament page gun rounds is implemented a little differently as the menu item (1) changes to show
# the contents of the magazine.
me.p1_3.gun_rounds = me.p1_3.addMenuItem(1, sprintf("HIGH\n%dM",getprop("sim/model/f15/systems/gun/rounds")), me.p1_3);
setlistener("sim/model/f15/systems/gun/rounds", func(v)
{
if (v != nil) {
me.p1_3.gun_rounds.title = sprintf("HIGH\n%dM",v.getValue());
me.PFD.updateMenus();
}
}
);
me.PFD.selectPage(me.p1_1);
me.mpcd_button_pushed = 0;
# Connect the buttons - using the provided model index to get the right ones from the model binding
setlistener("sim/model/f15/controls/MPCD/button-pressed", func(v)
{
if (v != nil) {
if (v.getValue())
me.mpcd_button_pushed = v.getValue();
else {
printf("%s: Button %d",me.designation, me.mpcd_button_pushed);
me.PFD.notifyButton(me.mpcd_button_pushed);
me.mpcd_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.mpcd_button_pushed = 0;
me.setupMenus();
me.PFD.selectPage(me.p1_1);
},
# Add the menus to each page.
setupMenus : func
{
},
update : func
{
if (me.mfd_device_status)
me.PFD.update();
},# update
};
#
# Create the MPCD device
var MPCD = MPCD_Device.new("F15-MPCD", "MPCDImage",0);
var updateMPCD = func ()
{
MPCD.update();
}
Related content
References
|