# Copyright 2018 Stuart Buchanan # This file is part of FlightGear. # # Foobar 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 . # # 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); } } }, };