359 lines
12 KiB
Text
359 lines
12 KiB
Text
# Copyright 2018 Stuart Buchanan
|
|
# This file is part of FlightGear.
|
|
#
|
|
# FlightGear is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# FlightGear is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with FlightGear. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
# DirectTo Controller
|
|
var DirectToController =
|
|
{
|
|
new : func (page, svg)
|
|
{
|
|
var obj = { parents : [ DirectToController, MFDPageController.new(page)] };
|
|
obj.id = "";
|
|
obj.page = page;
|
|
obj.dto_displayed = 0;
|
|
|
|
obj._cursorElements = [
|
|
obj.page.IDEntry,
|
|
obj.page.VNVAltEntry,
|
|
obj.page.VNVOffsetEntry,
|
|
obj.page.CourseEntry,
|
|
obj.page.Activate
|
|
];
|
|
|
|
obj._activateIndex = size(obj._cursorElements) - 1;
|
|
|
|
obj._selectedElement = 0;
|
|
|
|
# Whether the WaypointSubmenuGroup is enabled
|
|
obj._waypointSubmenuVisible = 0;
|
|
|
|
return obj;
|
|
},
|
|
|
|
setCursorElement : func(value) {
|
|
|
|
for (var i = 0; i < size(me._cursorElements); i = i+1) {
|
|
me._cursorElements[i].unhighlightElement();
|
|
}
|
|
|
|
if (value < 0) value = 0;
|
|
if (value > (size(me._cursorElements) -1)) value = size(me._cursorElements) -1;
|
|
me._selectedElement = value;
|
|
me._cursorElements[me._selectedElement].highlightElement();
|
|
},
|
|
|
|
nextCursorElement : func(value) {
|
|
var incr_or_decr = (value > 0) ? 1 : -1;
|
|
me.setCursorElement(me._selectedElement + incr_or_decr);
|
|
},
|
|
|
|
handleCRSR : func() {
|
|
if (! me.dto_displayed) return emesary.Transmitter.ReceiptStatus_NotProcessed;
|
|
|
|
# No effect, but shouldn't be passed to underlying page?
|
|
return emesary.Transmitter.ReceiptStatus_Finished;
|
|
},
|
|
updateWaypointSubmenu : func() {
|
|
var type = me.page.WaypointSubmenuSelect.getValue();
|
|
|
|
var items = [];
|
|
|
|
if (type == "FPL") {
|
|
# Get the contents of the flightplan and display the list of waypoints.
|
|
var fp = me.getNavData("Flightplan");
|
|
|
|
for (var i = 0; i < fp.getPlanSize(); i = i + 1) {
|
|
var wp = fp.getWP(i);
|
|
if (wp.wp_name != nil) append(items, wp.wp_name);
|
|
}
|
|
}
|
|
|
|
if (type == "NRST") {
|
|
# Get the nearest airports
|
|
var apts = me.getNavData("NearestAirports");
|
|
|
|
for (var i = 0; i < size(apts); i = i + 1) {
|
|
var apt = apts[i];
|
|
if (apt.id != nil) append(items, apt.id);
|
|
}
|
|
}
|
|
|
|
if (type == "RECENT") {
|
|
# Get the set of recent waypoints
|
|
items = me.getNavData("RecentWaypoints");
|
|
}
|
|
|
|
if (type == "USER") {
|
|
items = me.getNavData("UserWaypoints");
|
|
}
|
|
|
|
if (type == "AIRWAY") {
|
|
var fp = me.getNavData("AirwayWaypoints");
|
|
if (fp != nil) {
|
|
for (var i = 0; i < fp.getPlanSize(); i = i + 1) {
|
|
var wp = fp.getWP(i);
|
|
if (wp.wp_name != nil) append(items, wp.wp_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((items != nil) and (size(items) > 0)) {
|
|
# At this point we have a vector of waypoint names. We need to convert
|
|
# this into a vector of { "WaypointSubmenuScroll" : [name] } hashes for consumption by the
|
|
# list of waypoints
|
|
var groupitems = [];
|
|
foreach (var item; items) {
|
|
append(groupitems, { "WaypointSubmenuScroll" : item } );
|
|
}
|
|
|
|
# Now display them!
|
|
me.page.WaypointSubmenuScroll.setValues(groupitems);
|
|
} else {
|
|
# Nothing to display
|
|
me.page.WaypointSubmenuScroll.setValues([]);
|
|
}
|
|
},
|
|
|
|
getNavData : func(type, value=nil) {
|
|
# Use Emesary to get a piece from the NavData system, using the provided
|
|
# type and value;
|
|
var notification = notifications.PFDEventNotification.new(
|
|
"MFD",
|
|
me.getDeviceID(),
|
|
notifications.PFDEventNotification.NavData,
|
|
{Id: type, Value: value});
|
|
|
|
var response = me._transmitter.NotifyAll(notification);
|
|
|
|
if (! me._transmitter.IsFailed(response)) {
|
|
return notification.EventParameter.Value;
|
|
} else {
|
|
return nil;
|
|
}
|
|
},
|
|
|
|
setNavData : func(type, value=nil) {
|
|
# Use Emesary to set a piece of data in the NavData system, using the provided
|
|
# type and value;
|
|
var notification = notifications.PFDEventNotification.new(
|
|
"MFD",
|
|
me.getDeviceID(),
|
|
notifications.PFDEventNotification.NavData,
|
|
{Id: type, Value: value});
|
|
|
|
var response = me._transmitter.NotifyAll(notification);
|
|
|
|
if (me._transmitter.IsFailed(response)) {
|
|
print("DirectToController.setNavData() : Failed to set Nav Data " ~ value);
|
|
debug.dump(value);
|
|
}
|
|
},
|
|
|
|
handleDTO : func(value) {
|
|
if (me.dto_displayed == 0) {
|
|
# Display the DTO page.
|
|
me.page.ondisplay();
|
|
me.dto_displayed = 1;
|
|
} else {
|
|
# Hide the DTO page.
|
|
me.page.offdisplay();
|
|
me.dto_displayed = 0;
|
|
}
|
|
|
|
return emesary.Transmitter.ReceiptStatus_Finished;
|
|
},
|
|
|
|
handleRange : func(val)
|
|
{
|
|
if (! me.dto_displayed) return emesary.Transmitter.ReceiptStatus_NotProcessed;
|
|
|
|
if (me.page.DirectToChart != nil) {
|
|
return me.page.DirectToChart.handleRange(val);
|
|
} else {
|
|
return emesary.Transmitter.ReceiptStatus_NotProcessed;
|
|
}
|
|
},
|
|
|
|
handleFMSInner : func(value) {
|
|
if (! me.dto_displayed) return emesary.Transmitter.ReceiptStatus_NotProcessed;
|
|
|
|
if (me._waypointSubmenuVisible) {
|
|
# We're in the Waypoint Submenu, in which case the inner FMS knob
|
|
# selects between the different waypoint types.
|
|
me.page.WaypointSubmenuSelect.highlightElement();
|
|
me.page.WaypointSubmenuSelect.incrSmall(value);
|
|
# Now update the Scroll group with the new type of waypoints
|
|
me.updateWaypointSubmenu();
|
|
} else if ((me._selectedElement == 0) and (! me.page.IDEntry.isInEdit()) and (value == -1)) {
|
|
# The WaypointSubmenuGroup group is displayed if the small FMS knob is rotated
|
|
# anti-clockwise as an initial rotation where the ID Entry is not being editted.
|
|
me._cursorElements[0].unhighlightElement();
|
|
|
|
me.page.WaypointSubmenuGroup.setVisible(1);
|
|
me.page.WaypointSubmenuSelect.highlightElement();
|
|
me._waypointSubmenuVisible = 1;
|
|
me.updateWaypointSubmenu();
|
|
} else {
|
|
# We've already got something selected, and we're not in the
|
|
# WaypointSubmenuGroup, so increment it.
|
|
me._cursorElements[me._selectedElement].incrSmall(value);
|
|
}
|
|
|
|
return emesary.Transmitter.ReceiptStatus_Finished;
|
|
},
|
|
|
|
handleFMSOuter : func(value) {
|
|
if (! me.dto_displayed) return emesary.Transmitter.ReceiptStatus_NotProcessed;
|
|
|
|
if (me._waypointSubmenuVisible) {
|
|
# We're in the Waypoint Submenu, in which case the outer FMS knob
|
|
# selects between the different waypoints in the Waypoint Submenu.
|
|
me.page.WaypointSubmenuSelect.unhighlightElement();
|
|
me.page.WaypointSubmenuScroll.showCRSR();
|
|
me.page.WaypointSubmenuScroll.incrLarge(value);
|
|
} else if (me._cursorElements[me._selectedElement].isInEdit()) {
|
|
# If we're editing an element, then get on with it!
|
|
me._cursorElements[me._selectedElement].incrLarge(value);
|
|
} else {
|
|
me.nextCursorElement(value);
|
|
}
|
|
|
|
return emesary.Transmitter.ReceiptStatus_Finished;
|
|
},
|
|
|
|
handleEnter : func(value) {
|
|
if (! me.dto_displayed) return emesary.Transmitter.ReceiptStatus_NotProcessed;
|
|
|
|
if (me._waypointSubmenuVisible) {
|
|
# If we're in the Waypoint Submenu, then take whatever is highlighted
|
|
# in the scroll list, load it and hide the Waypoint submenu
|
|
var id = me.page.WaypointSubmenuScroll.getValue();
|
|
if (id != nil) me.loadDestination(id);
|
|
me.page.WaypointSubmenuGroup.setVisible(0);
|
|
me._waypointSubmenuVisible = 0;
|
|
|
|
# Select the activate ACTIVATE item.
|
|
me.setCursorElement(me._activateIndex);
|
|
} else {
|
|
if (me._selectedElement == me._activateIndex) {
|
|
# If we're on the Activate button, then set up the DirectTo and hide the
|
|
# page. We're finished
|
|
var params = {};
|
|
params.id = me.page.IDEntry.getValue();
|
|
params.alt_ft = me.page.VNVAltEntry.getValue();
|
|
params.offset_nm = me.page.VNVOffsetEntry.getValue();
|
|
me.setNavData("SetDirectTo", params);
|
|
|
|
me.page.offdisplay();
|
|
me.dto_displayed = 0;
|
|
|
|
return emesary.Transmitter.ReceiptStatus_Finished;
|
|
}
|
|
|
|
# If we're editing an element, complete the data entry.
|
|
if (me._cursorElements[me._selectedElement].isInEdit()) {
|
|
me._cursorElements[me._selectedElement].enterElement();
|
|
}
|
|
|
|
if (me._cursorElements[me._selectedElement] == me.page.IDEntry) {
|
|
# We've finished entering an ID, so load it.
|
|
me.loadDestination(me.page.IDEntry.getValue());
|
|
}
|
|
|
|
# Determine where to highlight next. In most cases, we go straight to ACTIVATE.
|
|
# The exception is the VNV Alt field which goes to the VNV Distance field;
|
|
if (me._cursorElements[me._selectedElement] == me.page.VNVAltEntry) {
|
|
# VNV DIS entry is the next element
|
|
me.nextCursorElement(1);
|
|
} else {
|
|
# ACTIVATE is the last element of the group
|
|
me.setCursorElement(me._activateIndex);
|
|
}
|
|
}
|
|
|
|
return emesary.Transmitter.ReceiptStatus_Finished;
|
|
},
|
|
|
|
handleClear : func(value) {
|
|
if (! me.dto_displayed) return emesary.Transmitter.ReceiptStatus_NotProcessed;
|
|
|
|
if (me._waypointSubmenuVisible) {
|
|
# If we're in the Waypoint Submenu, then this clears it.
|
|
me.page.WaypointSubmenuGroup.setVisible(0);
|
|
me._waypointSubmenuVisible = 0;
|
|
} else if (me._cursorElements[me._selectedElement].isInEdit()) {
|
|
me._cursorElements[me._selectedElement].clearElement();
|
|
} else {
|
|
# Cancel the entire Direct To page.
|
|
me.page.offdisplay();
|
|
me.dto_displayed = 0;
|
|
}
|
|
return emesary.Transmitter.ReceiptStatus_Finished;
|
|
},
|
|
|
|
# Reset controller if required when the page is displayed or hidden
|
|
# Note that we explicitly do NOT RegisterWithEmesary/DeRegisterWithEmesary!
|
|
# This page should RegisterWithEmesary at start of day instead.
|
|
ondisplay : func() {
|
|
# Find the current DTO target, which may have been set by the page the
|
|
# use was on.
|
|
var id = me.getNavData("CurrentDTO");
|
|
me.page.IDEntry.setValue(id);
|
|
me.loadDestination(id);
|
|
me.setCursorElement(0);
|
|
},
|
|
|
|
offdisplay : func() {
|
|
},
|
|
|
|
loadDestination : func(id) {
|
|
if ((id == nil) or (id == "")) {
|
|
me.page.displayDestination(nil);
|
|
} else {
|
|
# Use Emesary to get the destination
|
|
var notification = notifications.PFDEventNotification.new(
|
|
"MFD",
|
|
me.getDeviceID(),
|
|
notifications.PFDEventNotification.NavData,
|
|
{Id: "NavDataByID", Value: id});
|
|
|
|
var response = me._transmitter.NotifyAll(notification);
|
|
var retval = notification.EventParameter.Value;
|
|
|
|
if ((! me._transmitter.IsFailed(response)) and (size(retval) > 0)) {
|
|
var destination = retval[0];
|
|
# set the course and distance to the destination if required
|
|
|
|
# Some elements don't have names
|
|
var name = destination.id;
|
|
if (defined("destination.name")) name = destination.name;
|
|
|
|
var point = { lat: destination.lat, lon: destination.lon };
|
|
var (course, dist) = courseAndDistance(point);
|
|
|
|
var d = {
|
|
id: destination.id,
|
|
name: name,
|
|
lat: destination.lat,
|
|
lon: destination.lon,
|
|
course : course,
|
|
range_nm : dist,
|
|
};
|
|
|
|
me.page.displayDestination(d);
|
|
}
|
|
}
|
|
},
|
|
};
|