From 3d31775ff36bf3aa08a929992b70f32f2a525509 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Fri, 24 Nov 2017 23:04:46 +0000 Subject: [PATCH] Initial commit of FG1000 MFD --- .../FG1000/Models/EIS_c172s.svg | 841 ++++++++ Aircraft/Instruments-3d/FG1000/Models/MFD.svg | 1744 ++++++++++++++++ .../Instruments-3d/FG1000/Models/MFD2.svg | 1745 +++++++++++++++++ .../FG1000/Nasal/Drivers/EISDriver.nas | 68 + Aircraft/Instruments-3d/FG1000/Nasal/EIS.nas | 146 ++ Aircraft/Instruments-3d/FG1000/Nasal/MFD.nas | 85 + .../FG1000/Nasal/NavMap/NavMap.nas | 211 ++ .../FG1000/Nasal/NavMap/NavMapController.nas | 152 ++ .../FG1000/Nasal/NavMap/NavMapOptions.nas | 28 + .../FG1000/Nasal/NavMap/NavMapStyles.nas | 66 + .../FG1000/Nasal/PageGroupController.nas | 195 ++ .../FG1000/Nasal/TrafficMap/TrafficMap.nas | 172 ++ .../Nasal/TrafficMap/TrafficMapController.nas | 91 + .../Nasal/TrafficMap/TrafficMapOptions.nas | 34 + .../Nasal/TrafficMap/TrafficMapStyles.nas | 33 + Nasal/canvas/MFD_Generic.nas | 81 +- Nasal/canvas/map/STAMEN_terrain.lcontroller | 40 + Nasal/canvas/map/TFC.lcontroller | 28 +- Nasal/notifications.nas | 495 ++--- Translations/default/menu.xml | 1 + gui/dialogs/fg1000.xml | 892 +++++++++ gui/menubar.xml | 26 +- 22 files changed, 6899 insertions(+), 275 deletions(-) create mode 100644 Aircraft/Instruments-3d/FG1000/Models/EIS_c172s.svg create mode 100644 Aircraft/Instruments-3d/FG1000/Models/MFD.svg create mode 100644 Aircraft/Instruments-3d/FG1000/Models/MFD2.svg create mode 100644 Aircraft/Instruments-3d/FG1000/Nasal/Drivers/EISDriver.nas create mode 100644 Aircraft/Instruments-3d/FG1000/Nasal/EIS.nas create mode 100644 Aircraft/Instruments-3d/FG1000/Nasal/MFD.nas create mode 100644 Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMap.nas create mode 100644 Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMapController.nas create mode 100644 Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMapOptions.nas create mode 100644 Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMapStyles.nas create mode 100644 Aircraft/Instruments-3d/FG1000/Nasal/PageGroupController.nas create mode 100644 Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMap.nas create mode 100644 Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMapController.nas create mode 100644 Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMapOptions.nas create mode 100644 Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMapStyles.nas create mode 100644 Nasal/canvas/map/STAMEN_terrain.lcontroller create mode 100644 gui/dialogs/fg1000.xml diff --git a/Aircraft/Instruments-3d/FG1000/Models/EIS_c172s.svg b/Aircraft/Instruments-3d/FG1000/Models/EIS_c172s.svg new file mode 100644 index 000000000..e1af03ee8 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Models/EIS_c172s.svg @@ -0,0 +1,841 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + 0 + 3000 + 2200 + RPM + + + + + + + + + + + + + + + + + FFLOW GPH + 0 + 20 + + + + + + + + + OIL PRES + + + + + + + + + + OIL TEMP + + + + + + + + + + + + + + + + + EGT + + 1 + + + + + + + VAC + + + + + + + + + + + + FUEL QTY GAL + 0 + F + + + 10 + 20 + + + R + + + + L + + ENG HRS + XXXX.X + ELECTRICAL + + + M + BUS + E + 32.0 + VOLTS + 32.0 + M + BATT + S + +110 + AMPS + +110 + + + diff --git a/Aircraft/Instruments-3d/FG1000/Models/MFD.svg b/Aircraft/Instruments-3d/FG1000/Models/MFD.svg new file mode 100644 index 000000000..b2660a6ae --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Models/MFD.svg @@ -0,0 +1,1744 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + NAV2 + NAV1 + + COM2 + COM1 + + + + + 123.456 + 123.456 + 123.456 + 123.456 + + + + + + + 123.45 + 123.45 + 123.45 + 123.45 + XXX + XXX + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DIS + --.- + ETE + --:-- + + + GS + DTK + TRK + ETE + 0 + 000° + 000° + --:-- + + + + SoftKey2 + + + SoftKey1 + + + SoftKey3 + + + SoftKey4 + + + SoftKey6 + + + SoftKey5 + + + SoftKey7 + + + SoftKey8 + + + SoftKey10 + + + SoftKey9 + + + SoftKey11 + + + SoftKey0 + + + + + + Zoom nm + + + + + + OPERATING + OPERATING + + UNRESTRICTED + + + + + + 8NM + + + + + + + + + + + + 8NM + + + + + + + NEAREST NDB + NEAREST VOR + NEAREST USER WAYPOINTS + NEAREST FREQUENCIES + NEAREST AIRSPACES + NEAREST AIRPORTS + NEAREST INTERSECTIONS + + + + + TRIP PLANNING + UTILITY + GPS STATUS + XM RADIO + SYSTEM STATUS + + + + + CHECKLIST 1 + CHECKLIST 2 + CHECKLIST 3 + CHECKLIST 4 + CHECKLIST 5 + + + + + AIRPORT INFORMATION + INTERSECTION INFORMATION + NDB INFORMATION + VOR INFORMATION + USER WPT INFORMATION + + + + + NAVIGATION MAP + TRAFFIC MAP + STORMSCOPE + WEATHER DATA LINK + TAWS-B + + + + + ACTIVE FLIGHT PLAN + FLIGHT PLAN CATALOG + STORED FLIGHT PLAN + + MAP + WPT + AUX + FPL + LST + NRST + + diff --git a/Aircraft/Instruments-3d/FG1000/Models/MFD2.svg b/Aircraft/Instruments-3d/FG1000/Models/MFD2.svg new file mode 100644 index 000000000..c9ab3aa49 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Models/MFD2.svg @@ -0,0 +1,1745 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + NAV2 + NAV1 + + COM2 + COM1 + + + + + 123.456 + 123.456 + 123.456 + 123.456 + + + + + + + 123.45 + 123.45 + 123.45 + 123.45 + XXX + XXX + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DIS + --.- + ETE + --:-- + + + GS + DTK + TRK + ETE + 0 + 000° + 000° + --:-- + + + + SoftKey2 + + + SoftKey1 + + + SoftKey3 + + + SoftKey4 + + + SoftKey6 + + + SoftKey5 + + + SoftKey7 + + + SoftKey8 + + + SoftKey10 + + + SoftKey9 + + + SoftKey11 + + + SoftKey0 + + + + + + Zoom nm + + + + + + OPERATING + OPERATING + + UNRESTRICTED + + + + + + 8NM + + + + + + + + + + + + 8NM + + + + + + + NEAREST NDB + NEAREST VOR + NEAREST USER WAYPOINTS + NEAREST FREQUENCIES + NEAREST AIRSPACES + NEAREST AIRPORTS + NEAREST INTERSECTIONS + + + + + TRIP PLANNING + UTILITY + GPS STATUS + XM RADIO + SYSTEM STATUS + + + + + CHECKLIST 1 + CHECKLIST 2 + CHECKLIST 3 + CHECKLIST 4 + CHECKLIST 5 + + + + + AIRPORT INFORMATION + INTERSECTION INFORMATION + NDB INFORMATION + VOR INFORMATION + USER WPT INFORMATION + + + + + NAVIGATION MAP + TRAFFIC MAP + STORMSCOPE + WEATHER DATA LINK + TAWS-B + + + + + ACTIVE FLIGHT PLAN + FLIGHT PLAN CATALOG + STORED FLIGHT PLAN + + MAP + WPT + AUX + FPL + LST + NRST + + diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/Drivers/EISDriver.nas b/Aircraft/Instruments-3d/FG1000/Nasal/Drivers/EISDriver.nas new file mode 100644 index 000000000..5d4803d45 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/Drivers/EISDriver.nas @@ -0,0 +1,68 @@ +# FG1000 Engine Information Display Default Driver +# +# Driver values in the EIS. + +var PropMap = +{ + new : func(name, property) + { + var obj = { parents : [ PropMap ] }; + obj._name = name; + obj._val = 0; + obj._prop = globals.props.getNode(property, 1); + obj._val = obj._prop.getValue(); + return obj; + }, + + update : func() + { + me._val = me._prop.getValue(); + }, + + getValue : func() + { + return me._val; + } + +}; + +var EISDriver = +{ + new : func() + { + var obj = { parents : [ EISDriver ] }; + + # Hack to handle most aircraft not having proper engine hours + setprop("/engines/engine[0]/hours", 157.0); + + obj._propmap = { + "RPM" : PropMap.new("RPM", "/engines/engine[0]/rpm"), + "MBusVolts" : PropMap.new("MBusVolts", "/systems/electrical/volts"), + "EngineHours" : PropMap.new("EngineHours", "/engines/engine[0]/hours"), + "FuelFlowGPH" : PropMap.new("FuelFlowGPH", "/engines/engine[0]/fuel-flow-gph"), + "OilPressurePSI" : PropMap.new("OilPressurePSI", "/engines/engine[0]/oil-pressure-psi"), + "OilTemperatureF" : PropMap.new("OilTemperatureF", "/engines/engine[0]/oil-temperature-degf"), + "EGTNorm" : PropMap.new("EGTNorm", "/engines/engine[0]/egt-norm"), + "VacuumSuctionInHG" : PropMap.new("VacuumSuctionInHG", "/systems/vacuum/suction-inhg"), + + "LeftFuelUSGal" : PropMap.new("LeftFuelUSGal", "/consumables/fuel/tank[0]/indicated-level-gal_us"), + "RightFuelUSGal" : PropMap.new("RightFuelUSGal", "/consumables/fuel/tank[1]/indicated-level-gal_us"), + + + }; + return obj; + }, + + getValue : func(name) + { + if (me._propmap[name] == nil) print("Unknown Driver Key in EISDriver.nas " ~ name); + return me._propmap[name].getValue(); + }, + + update: func () { + + foreach (var tp; keys(me._propmap)) { + me._propmap[tp].update(); + } + } +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/EIS.nas b/Aircraft/Instruments-3d/FG1000/Nasal/EIS.nas new file mode 100644 index 000000000..cd385fc27 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/EIS.nas @@ -0,0 +1,146 @@ +# FG1000 Engine Information Display + +# A mapping from names of elements to specific elements of the SVG. +# These names mapped to values by the provided EISDriver. +var DynamicTextElementMap = +{ + new : func(svg, name, element_id, format) + { + var obj = { parents : [ DynamicTextElementMap ] }; + obj._name = name; + obj._element_id = element_id; + obj._element = svg.getElementById(element_id); + obj._format = format; + return obj; + }, + + update : func(driver) + { + me._element.setText(sprintf(me._format, driver.getValue(me._name))); + }, +}; + +var DynamicSliderElementMap = +{ + new : func(svg, name, element_id, min=0.0, max=1.0) + { + var obj = { parents : [ DynamicSliderElementMap ] }; + obj._name = name; + obj._element_id = element_id; + obj._element = svg.getElementById(element_id); + obj._baseTranslation = obj._element.getTranslation(); + obj._min = min; + obj._max = max; + return obj; + }, + + update : func(driver) + { + var value = driver.getValue(me._name); + if (value == nil) value = 0.0; + + # Bound value to the minimum and maximum values. + value = math.max(me._min, value); + value = math.min(me._max, value); + + # Convert to normalized value + value = (value - me._min) / (me._max - me._min); + + # Simply shift the slider along. All sliders have a 0 value + # at x=3.5, and a maximum value at x=138.0. + me._element.setTranslation([ + me._baseTranslation[0] + (value * (138.0 - 3.5)), + me._baseTranslation[1] + ]); + }, +}; + + +var DynamicRotatingElementMap = +{ + new : func(svg, name, element_id, min=0.0, max=1.0, range=360.0) + { + var obj = { parents : [ DynamicRotatingElementMap ] }; + obj._name = name; + obj._element_id = element_id; + obj._element = svg.getElementById(element_id); + obj._baseTranslation = obj._element.getTranslation(); + obj._min = min; + obj._max = max; + obj._range = range; + return obj; + }, + + update : func(driver) + { + var value = driver.getValue(me._name); + if (value == nil) value = 0.0; + + # Bound value to the minimum and maximum values. + value = math.max(me._min, value); + value = math.min(me._max, value); + + # Convert to normalized value + value = (value - me._min) / (me._max - me._min); + me._element.setRotation(value * me._range * D2R, [100.0, 100.0]); + }, +}; + +var EIS = +{ + new : func (myCanvas, EISDriver) + { + var obj = { parents : [ EIS ] }; + obj._canvas = myCanvas; + obj._EISDriver = EISDriver; + + obj._svg= obj._canvas.createGroup("EIS"); + canvas.parsesvg(obj._svg, '/Aircraft/Instruments-3d/FG1000/Models/EIS_c172s.svg'); + + # Objects to display as straight text + obj._textElements = [ + DynamicTextElementMap.new(obj._svg, "RPM", "RPMDisplay", "%i"), + DynamicTextElementMap.new(obj._svg, "MBusVolts" , "MBusVolts", "%.01f"), + DynamicTextElementMap.new(obj._svg, "MBusVolts" , "EBusVolts", "%.01f"), + DynamicTextElementMap.new(obj._svg, "EngineHours" , "EngineHours", "%.01f"), + ]; + + obj._sliderElements = [ + DynamicSliderElementMap.new(obj._svg, "FuelFlowGPH", "FuelFlowPointer", 0.0, 20.0), + DynamicSliderElementMap.new(obj._svg, "OilPressurePSI", "OilPressurePointer", 0.0, 115.0), + DynamicSliderElementMap.new(obj._svg, "OilTemperatureF", "OilTempPointer", 0.0, 245.0), + DynamicSliderElementMap.new(obj._svg, "EGTNorm", "EGTPointer", 0.0, 1.0), + DynamicSliderElementMap.new(obj._svg, "EGTNorm", "EGTCylinder", 0.0, 1.0), + DynamicSliderElementMap.new(obj._svg, "VacuumSuctionInHG", "VacPointer", 3.0, 7.0), + + DynamicSliderElementMap.new(obj._svg, "LeftFuelUSGal", "LeftFuelPointer", 0.0, 30.0), + DynamicSliderElementMap.new(obj._svg, "RightFuelUSGal", "RightFuelPointer", 0.0, 30.0), + ]; + + obj._rotationElements = [ + DynamicRotatingElementMap.new(obj._svg, "RPM", "RPMPointer", 0.0, 3000.0, 260.0), + ]; + + obj._svg.getElementById("RPMPointer").set("center-offset-x", 150.0); + obj._svg.getElementById("RPMPointer").set("center-offset-y", 100.0); + + return obj; + }, + + update : func() + { + foreach(var te; me._textElements) { + te.update(me._EISDriver); + } + + foreach(var se; me._sliderElements) { + se.update(me._EISDriver); + } + + foreach(var se; me._rotationElements) { + se.update(me._EISDriver); + } + + } + +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/MFD.nas b/Aircraft/Instruments-3d/FG1000/Nasal/MFD.nas new file mode 100644 index 000000000..3d7141927 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/MFD.nas @@ -0,0 +1,85 @@ +# FG1000 MFD + +var nasal_dir = getprop("/sim/fg-root") ~ "/Aircraft/Instruments-3d/FG1000/Nasal/"; +io.load_nasal(nasal_dir ~ 'NavMap/NavMap.nas', "fg1000"); +io.load_nasal(nasal_dir ~ 'NavMap/NavMapStyles.nas', "fg1000"); +io.load_nasal(nasal_dir ~ 'NavMap/NavMapOptions.nas', "fg1000"); +io.load_nasal(nasal_dir ~ 'NavMap/NavMapController.nas', "fg1000"); + +io.load_nasal(nasal_dir ~ 'TrafficMap/TrafficMap.nas', "fg1000"); +io.load_nasal(nasal_dir ~ 'TrafficMap/TrafficMapStyles.nas', "fg1000"); +io.load_nasal(nasal_dir ~ 'TrafficMap/TrafficMapOptions.nas', "fg1000"); +io.load_nasal(nasal_dir ~ 'TrafficMap/TrafficMapController.nas', "fg1000"); + +io.load_nasal(nasal_dir ~ 'EIS.nas', "fg1000"); +io.load_nasal(nasal_dir ~ 'Drivers/EISDriver.nas', "fg1000"); +io.load_nasal(nasal_dir ~ 'PageGroupController.nas', "fg1000"); + + +var MFD = +{ + # Center of any maps + MAP_CENTER : { + X: (1024/2 + 60), + Y: (768/2 + 20) + }, + + # Constants for the hard-buttons on the fascia + FASCIA : { + FMS_OUTER : 0, + FMS_INNER : 1, + RANGE : 2, + }, + + + + new : func (myCanvas) + { + var obj = { parents : [ MFD ] }; + + obj._svg = myCanvas.createGroup("softkeys"); + canvas.parsesvg(obj._svg, '/Aircraft/Instruments-3d/FG1000/Models/MFD.svg'); + + obj._MFDDevice = canvas.PFD_Device.new(obj._svg, 12, "SoftKey", myCanvas, "MFD"); + obj._MFDDevice.RegisterWithEmesary(); + + # Engine Information System + obj._eisDriver = fg1000.EISDriver.new(); + obj._eis = fg1000.EIS.new(myCanvas, obj._eisDriver); + + # Controller for the display on the bottom left which allows selection + # of page groups and individual pages using the FMS controller. + obj._pageGroupController = fg1000.PageGroupController.new(myCanvas, obj._svg, obj._MFDDevice); + obj._pageGroupController.RegisterWithEmesary(); + + obj._pageGroupController.addPage("NavigationMap", fg1000.NavMap.new(myCanvas, obj._MFDDevice, obj._svg)); + obj._pageGroupController.addPage("TrafficMap", fg1000.TrafficMap.new(myCanvas, obj._MFDDevice, obj._svg)); + + # Display the NavMap and the appropriate top level on startup. + obj._MFDDevice.selectPage(obj._pageGroupController.getPage("NavigationMap")); + + # Add a wheel controller., which we will attach to the zoom. + myCanvas.addEventListener("wheel", func(e) + { + if (e.deltaY >0) { + obj._MFDDevice.current_page.controller.zoomIn(); + } else { + obj._MFDDevice.current_page.controller.zoomOut(); + } + }); + + var updateTimer = func() { + obj._eisDriver.update(); + obj._eis.update(); + settimer(updateTimer, 0.1); + }; + + updateTimer(); + + return obj; + }, + del: func() + { + me._pageGroupController.DeRegisterWithEmesary(); + } +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMap.nas b/Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMap.nas new file mode 100644 index 000000000..20e6a9512 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMap.nas @@ -0,0 +1,211 @@ +# Navigation Map +var NavMap = +{ + new : func (myCanvas, device, svg) + { + var obj = { + _group : myCanvas.createGroup("NavigationMapLayer"), + parents : [ NavMap, device.addPage("NavigationMap", "NavigationMapGroup") ] + }; + + obj.Styles = fg1000.NavMapStyles.new(); + obj.Options = fg1000.NavMapOptions.new(); + obj.MFDMap = obj._group.createChild("map"); + + # Need to display this underneath the softkeys, EIS, header. + obj._group.set("z-index", -10.0); + obj._group.setVisible(0); + + obj.device = device; + + # Initialize the controller: + var ctrl_ns = canvas.Map.Controller.get("Aircraft position"); + var source = ctrl_ns.SOURCES["current-pos"]; + if (source == nil) { + # TODO: amend + var source = ctrl_ns.SOURCES["current-pos"] = { + getPosition: func subvec(geo.aircraft_position().latlon(), 0, 2), + getAltitude: func getprop('/position/altitude-ft'), + getHeading: func { + if (me.aircraft_heading) + getprop('/orientation/heading-deg') + else 0 + }, + aircraft_heading: 1, + }; + } + setlistener("/sim/gui/dialogs/map-canvas/aircraft-heading-up", func(n) { + source.aircraft_heading = n.getBoolValue(); + }, 1); + # Make it move with our aircraft: + obj.MFDMap.setController("Aircraft position", "current-pos"); # from aircraftpos.controller + + # Center the map's origin, modified to take into account the surround. + obj.MFDMap.setTranslation( + fg1000.MFD.MAP_CENTER.X, + fg1000.MFD.MAP_CENTER.Y + ); + + var r = func(name,vis=1,zindex=nil) return caller(0)[0]; + # TODO: we'll need some z-indexing here, right now it's just random + foreach(var type; [r('GRID'),r('TFC',0),r('APT'),r('DME'),r('VOR'),r('NDB'),r('FIX',0),r('RTE'),r('WPT'),r('FLT'),r('WXR',0),r('APS')] ) { + obj.MFDMap.addLayer(factory: canvas.SymbolLayer, type_arg: type.name, + visible: type.vis, priority: 4, + style: obj.Styles.getStyle(type.name), + options: obj.Options.getOption(type.name) ); + } + + foreach(var type; [ r('STAMEN_terrain'),r('STAMEN'), r('OpenAIP') ]) { + obj.MFDMap.addLayer(factory: canvas.OverlayLayer, type_arg: type.name, + visible: 0, priority: 1, + style: obj.Styles.getStyle(type.name), + options: obj.Options.getOption(type.name) ); + } + + obj.controller = fg1000.NavMapController.new(obj, svg, "RangeDisplay", 8); + + var topMenu = func(device, pg, menuitem) { + pg.clearMenu(); + resetMenuColors(device); + pg.addMenuItem(0, "ENGINE", pg, engineMenu); + pg.addMenuItem(2, "MAP", pg, mapMenu); + pg.addMenuItem(8, "DCLTR", pg, func(dev, pg, mi) { obj.controller.incrDCLTR(dev, mi); } ); + #pg.addMenuItem(9, "SHW CHRT", pg); # Optional + #pg.addMenuItem(10, "CHKLIST", pg); # Optional + device.updateMenus(); + }; + + var engineMenu = func(device, pg, menuitem) { + pg.clearMenu(); + resetMenuColors(device); + pg.addMenuItem(0, "ENGINE", pg, engineMenu); + pg.addMenuItem(1, "LEAN", pg, leanMenu); + pg.addMenuItem(2, "SYSTEM", pg, systemMenu); + pg.addMenuItem(8, "BACK", pg, topMenu); + device.updateMenus(); + }; + + + # Display map toggle softkeys which change color depending + # on whether a particular layer is enabled or not. + var display_toggle = func(device, svg, mi, layer) { + var bg_name = sprintf("SoftKey%d-bg",mi.menu_id); + if (obj.controller.isEnabled(layer)) { + device.svg.getElementById(bg_name).setColorFill(0.5,0.5,0.5); + svg.setColor(0.0,0.0,0.0); + } else { + device.svg.getElementById(bg_name).setColorFill(0.0,0.0,0.0); + svg.setColor(1.0,1.0,1.0); + } + svg.setText(mi.title); + svg.setVisible(1); # display function + }; + + # Function to undo any colors set by display_toggle when loading a new menu + var resetMenuColors = func(device) { + for(var i = 0; i < 12; i +=1) { + var name = sprintf("SoftKey%d",i); + device.svg.getElementById(name ~ "-bg").setColorFill(0.0,0.0,0.0); + device.svg.getElementById(name).setColor(1.0,1.0,1.0); + } + } + + var mapMenu = func(device, pg, menuitem) { + pg.clearMenu(); + resetMenuColors(device); + pg.addMenuItem(0, "TRAFFIC", pg, + func(dev, pg, mi) { obj.controller.toggleLayer("TFC"); device.updateMenus(); }, # callback + func(svg, mi) { display_toggle(device, svg, mi, "TFC"); } + ); + + pg.addMenuItem(1, "PROFILE", pg); + pg.addMenuItem(2, "TOPO", pg, + func(dev, pg, mi) { obj.controller.toggleLayer("STAMEN"); device.updateMenus(); }, # callback + func(svg, mi) { display_toggle(device, svg, mi, "STAMEN"); } + ); + + pg.addMenuItem(3, "TERRAIN", pg, + func(dev, pg, mi) { obj.controller.toggleLayer("STAMEN_terrain"); device.updateMenus(); }, # callback + func(svg, mi) { display_toggle(device, svg, mi, "STAMEN_terrain"); } + ); + + pg.addMenuItem(4, "AIRWAYS", pg, func(dev, pg, mi) { obj.controller.incrAIRWAYS(dev, mi); } ); + #pg.addMenuItem(5, "STRMSCP", pg); Optional + #pg.addMenuItem(6, "PRECIP", pg); Optional, or NEXRAD + #pg.addMenuItem(7, "XM LTNG", pg); Optional, or DL LTNG + #pg.addMenuItem(8, "METAR", pg); + #pg.addMenuItem(9, "LEGEND", pg); Optional - only available with NEXRAD/XM LTNG/METAR/PROFILE selected + pg.addMenuItem(10, "BACK", pg, topMenu); # Or should this just be the next button? + device.updateMenus(); + }; + + var leanMenu = func(device, pg, menuitem) { + pg.clearMenu(); + resetMenuColors(device); + pg.addMenuItem(0, "ENGINE", pg, engineMenu); + pg.addMenuItem(1, "LEAN", pg, leanMenu); + pg.addMenuItem(2, "SYSTEM", pg, systemMenu); + pg.addMenuItem(3, "CYL SELECT", pg); + pg.addMenuItem(4, "ASSIST", pg); + pg.addMenuItem(9, "BACK", pg, engineMenu); + device.updateMenus(); + }; + + var systemMenu = func(device, pg, menuitem) { + pg.clearMenu(); + resetMenuColors(device); + pg.addMenuItem(0, "ENGINE", pg, engineMenu); + pg.addMenuItem(1, "LEAN", pg, leanMenu); + pg.addMenuItem(2, "SYSTEM", pg, systemMenu); + pg.addMenuItem(3, "RST FUEL", pg); + pg.addMenuItem(4, "GAL REM", pg, galRemMenu); + pg.addMenuItem(5, "BACK", pg, engineMenu); + device.updateMenus(); + }; + + var galRemMenu = func(device, pg, menuitem) { + pg.clearMenu(); + resetMenuColors(device); + pg.addMenuItem(0, "ENGINE", pg, engineMenu); + pg.addMenuItem(1, "LEAN", pg, leanMenu); + pg.addMenuItem(2, "SYSTEM", pg, systemMenu); + pg.addMenuItem(3, "-10 GAL", pg); + pg.addMenuItem(4, "-1 GAL", pg); + pg.addMenuItem(5, "+1 GAL", pg); + pg.addMenuItem(6, "+10 GAL", pg); + pg.addMenuItem(7, "44 GAL", pg); + pg.addMenuItem(8, "BACK", pg, engineMenu); + device.updateMenus(); + }; + + topMenu(device, obj, nil); + + return obj; + }, + toggleLayerVisible : func(name) { + (var l = me.MFDMap.getLayer(name)).setVisible(l.getVisible()); + }, + setLayerVisible : func(name,n=1) { + me.MFDMap.getLayer(name).setVisible(n); + }, + setRange : func(range) { + me.MFDMap.setRange(range); + }, + setScreenRange : func(range) { + me.MFDMap.setScreenRange(range); + }, + offdisplay : func() { + me._group.setVisible(0); + + # Reset the menu colours. Shouldn't have to do this here, but + # there's not currently an obvious other location to do so. + for(var i = 0; i < 12; i +=1) { + var name = sprintf("SoftKey%d",i); + me.device.svg.getElementById(name ~ "-bg").setColorFill(0.0,0.0,0.0); + me.device.svg.getElementById(name).setColor(1.0,1.0,1.0); + } + }, + ondisplay : func() { + me._group.setVisible(1); + }, +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMapController.nas b/Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMapController.nas new file mode 100644 index 000000000..6a339a974 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMapController.nas @@ -0,0 +1,152 @@ +# Navigation Map Controller +var NavMapController = +{ + # Vertical ranges, and labels. + # 28 ranges from 500ft to 2000nm, measuring the vertical map distance. + # Vertical size of the map (once the nav box and softkey area is removed) is 689px. + # 2000nm = 12,152,000ft. + RANGES : [{range: 500/6076.12, label: "500ft"}, + {range: 750/6076.12, label: "750ft"}, + {range: 1000/6076.12, label: "1000ft"}, + {range: 1500/6076.12, label: "1500ft"}, + {range: 2000/6076.12, label: "2000ft"}, + {range: 0.5, label: "0.5nm"}, + {range: 0.75, label: "0.75nm"}, + {range: 1, label: "1nm"}, + {range: 2, label: "2nm"}, + {range: 3, label: "3nm"}, + {range: 4, label: "4nm"}, + {range: 6, label: "6nm"}, + {range: 8, label: "8nm"}, + {range: 10, label: "10nm"}, + {range: 12, label: "12nm"}, + {range: 15, label: "15nm"}, + {range: 20, label: "20nm"}, + {range: 25, label: "25nm"}, + {range: 30, label: "30nm"}, + {range: 40, label: "40nm"}, + {range: 50, label: "50nm"}, + {range: 75, label: "75nm"}, + {range: 100, label: "100nm"}, + {range: 200, label: "200nm"}, + {range: 500, label: "500nm"}, + {range: 1000, label: "1000nm"}, + {range: 1500, label: "1500nm"}, + {range: 2000, label: "2000nm"}, ], + + # Layer display configuration: + # enabled - whether this layer has been enabled by the user + # declutter - the maximum declutter level (0-3) that this layer is visible in + # range - the maximum range this layer is visible (configured by user) + # max_range - the maximum range value that a user can configure for this layer. + LAYER_RANGES : { + GRID : { enabled: 0, declutter: 1, range: 20, max_range: 2000 }, + DME : { enabled: 1, declutter: 1, range: 150, max_range: 300 }, + VOR : { enabled: 1, declutter: 1, range: 150, max_range: 300 }, + NDB : { enabled: 1, declutter: 1, range: 15, max_range: 30 }, + FIX : { enabled: 1, declutter: 1, range: 15, max_range: 30 }, + RTE : { enabled: 1, declutter: 3, range: 2000, max_range: 2000 }, + WPT : { enabled: 1, declutter: 3, range: 2000, max_range: 2000 }, + + APS : { enabled: 1, declutter: 3, range: 2000, max_range: 2000 }, + FLT : { enabled: 1, declutter: 3, range: 2000, max_range: 2000 }, + + WXR : { enabled: 1, declutter: 2, range: 2000, max_range: 2000 }, + + APT : { enabled: 1, declutter: 2, range: 150, max_range: 300 }, + + TFC : { enabled: 0, declutter: 3, range: 150, max_range: 2000}, + + OpenAIP : { enabled: 1, declutter: 1, range: 150, max_range: 300 }, + STAMEN : { enabled: 1, declutter: 3, range: 500, max_range: 2000 }, + STAMEN_terrain : { enabled: 1, declutter: 3, range: 500, max_range: 2000 }, + }, + + # TODO: Add STAMEN topo layer, which is visible on all levels as opposed to + # roads, railways, boundaries, cities which are only visible on declutter 0. + + # Declutter levels. + DCLTR : [ "DCLTR", "DCLTR-1", "DCLTR-2", "DCLTR-3"], + + # Airways levels. + AIRWAYS : [ "AIRWAYS", "AIRWY ON", "AIRWY LO", "AIRWY HI"], + + new : func (navmap, svg, zoom_label, current_zoom) + { + var obj = { parents : [ NavMapController ] }; + obj.current_zoom = current_zoom; + obj.declutter = 0; + obj.airways = 0; + obj.navmap = navmap; + obj.navmap.setScreenRange(689/2.0); + obj.label = svg.getElementById(zoom_label); + obj.setZoom(obj.current_zoom); + return obj; + }, + zoomIn : func() { + me.setZoom(me.current_zoom -1); + }, + zoomOut : func() { + me.setZoom(me.current_zoom +1); + }, + zoom : func(val) + { + var incr_or_decr = (val > 0) ? 1 : -1; + me.setZoom(me.current_zoom + incr_or_decr); + }, + setZoom : func(zoom) { + if ((zoom < 0) or (zoom > (size(me.RANGES) - 1))) return; + me.current_zoom = zoom; + # Ranges above represent vertical ranges, but the display is a rectangle, so + # we need to use the diagonal range of the 1024 x 689, which is 617px. + # 617px is 1.8 x 689/2, so we need to increase the range values by x1.8 + me.navmap.setRange(me.RANGES[zoom].range); + me.label.setText(me.RANGES[zoom].label); + me.updateVisibility(); + }, + updateVisibility : func() { + # Determine which layers should be visible. + foreach (var layer_name; keys(me.LAYER_RANGES)) { + var layer = me.LAYER_RANGES[layer_name]; + if (layer.enabled and + (me.RANGES[me.current_zoom].range <= layer.range) and + (me.declutter <= layer.declutter) ) + { + me.navmap.MFDMap.getLayer(layer_name).setVisible(1); + } else { + me.navmap.MFDMap.getLayer(layer_name).setVisible(0); + } + } + }, + configureLayer : func(layer, enabled, range) { + me.LAYER_RANGES[layer].enabled = enabled; + me.LAYER_RANGES[layer].range = math.min(range, me.LAYER_RANGES[layer].max_range); + }, + isEnabled : func(layer) { + return me.LAYER_RANGES[layer].enabled; + }, + toggleLayer : func(layer) { + me.LAYER_RANGES[layer].enabled = ! me.LAYER_RANGES[layer].enabled; + me.updateVisibility(); + }, + + # Increment through the de-clutter levels, which impact what layers are + # displayed. We also need to update the declutter menu item. + incrDCLTR : func(device, menuItem) { + me.declutter = math.mod(me.declutter +1, 4); + menuItem.title = me.DCLTR[me.declutter]; + device.updateMenus(); + me.updateVisibility(); + }, + + # Increment through the AIRWAYS levels. At present this doesn't do anything + # except change the label. It should enable/disable different airways + # information. + incrAIRWAYS : func(device, menuItem) { + me.airways = math.mod(me.airways +1, 4); + menuItem.title = me.AIRWAYS[me.airways]; + device.updateMenus(); + me.updateVisibility(); + }, + +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMapOptions.nas b/Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMapOptions.nas new file mode 100644 index 000000000..f7f1d4188 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMapOptions.nas @@ -0,0 +1,28 @@ +# Navigation Map Options +var NavMapOptions = +{ + new : func() { + var obj = { parents : [NavMapOptions] }; + obj.Options= {}; + obj.loadOptions(); + return obj; + }, + + getOption : func(type) { + return me.Options[type]; + }, + + setOption : func(type, name, value) { + me.Options[type][name] = value; + }, + + loadOptions : func() { + me.clearOptions(); + me.Options.APS = {}; + }, + + clearOptions : func() { + me.Options = {}; + }, + +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMapStyles.nas b/Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMapStyles.nas new file mode 100644 index 000000000..aa775ee58 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/NavMap/NavMapStyles.nas @@ -0,0 +1,66 @@ +# Navigation Map Styles +var NavMapStyles = +{ + new : func() { + var obj = { parents : [ NavMapStyles ]}; + obj.Styles = {}; + obj.loadStyles(); + return obj; + }, + + getStyle : func(type) { + return me.Styles[type]; + }, + + setStyle : func(type, name, value) { + me.Styles[type][name] = value; + }, + + loadStyles : func() { + me. clearStyles(); + me.Styles.DME = {}; + me.Styles.DME.debug = 1; # HACK for benchmarking/debugging purposes + me.Styles.DME.animation_test = 0; # for prototyping animated symbols + + me.Styles.DME.scale_factor = 0.4; # 40% (applied to whole group) + me.Styles.DME.line_width = 3.0; + me.Styles.DME.color_tuned = [0,1,0]; #rgb + me.Styles.DME.color_default = [1,1,0]; #rgb + + me.Styles.APT = {}; + me.Styles.APT.scale_factor = 0.4; # 40% (applied to whole group) + me.Styles.APT.line_width = 3.0; + me.Styles.APT.color_default = [0,0.6,0.85]; #rgb + me.Styles.APT.label_font_color = me.Styles.APT.color_default; + me.Styles.APT.label_font_size=28; + + me.Styles.TFC = {}; + me.Styles.TFC.scale_factor = 0.4; # 40% (applied to whole group) + + me.Styles.WPT = {}; + me.Styles.WPT.scale_factor = 0.5; # 50% (applied to whole group) + + me.Styles.RTE = {}; + me.Styles.RTE.line_width = 2; + + me.Styles.FLT = {}; + me.Styles.FLT.line_width = 3; + + me.Styles.FIX = {}; + me.Styles.FIX.color = [1,0,0]; + me.Styles.FIX.scale_factor = 0.4; # 40% + + me.Styles.VOR = {}; + me.Styles.VOR.range_line_width = 2; + me.Styles.VOR.radial_line_width = 1; + me.Styles.VOR.scale_factor = 0.6; # 60% + + me.Styles.APS = {}; + me.Styles.APS.scale_factor = 0.25; + }, + + clearStyles : func() { + me.Styles = {}; + }, + +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/PageGroupController.nas b/Aircraft/Instruments-3d/FG1000/Nasal/PageGroupController.nas new file mode 100644 index 000000000..5a25ea714 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/PageGroupController.nas @@ -0,0 +1,195 @@ +# Controller for the PageGroup navigation, displayed in the bottom right of the +# FMS, and controlled by the FMS knob + +# Set of pages, references by SVG ID +var PAGE_GROUPS = [ + + { label: "MapPageGroupLabel", + group: "MapPageGroup", + pages: [ "NavigationMap", "TrafficMap", "Stormscope", "WeatherDataLink", "TAWSB"], + }, + { label: "WPTGroupLabel", + group: "WPTPageGroup", + pages: [ "AirportInformation", "IntersectionInformation", "NDBInformation", "VORInformation", "UserWPTInformation"], + }, + + { label: "AuxGroupLabel", + group: "AuxPageGroup", + pages: [ "TripPlanning", "Utility", "GPSStatus", "XMRadio", "SystemStatus"], + }, + + { label: "FPLGroupLabel", + group: "FPLPageGroup", + pages: [ "ActiveFlightPlan", "FlightPlanCatalog", "StoredFlightPlan"], + }, + + { label: "LstGroupLabel", + group: "LstPageGroup", + pages: [ "Checklist1", "Checklist2", "Checklist3", "Checklist4", "Checklist5"], + }, + + { label: "NrstGroupLabel", + group: "NrstPageGroup", + pages: [ "NearestAirports", "NearestIntersections", "NearestNDB", "NearestVOR", "NearestUserWaypoints", "NearestFrequencies", "NearestAirspaces"], + } +]; + +var PageGroupController = +{ + new : func (myCanvas, svg, device) + { + var obj = { parents : [ PageGroupController ] }; + obj._canvas = myCanvas; + obj._svg = svg; + obj._device = device; + obj._menuVisible = 0; + obj._selectedPageGroup = 0; + obj._selectedPage = 0; + + # List of pages to be controllers. Keys are the pages in PAGE_GROUPS; + obj._pageList = {}; + + # Timers to controll when to hide the menu after inactivity, and when to load + # a new page. + obj._hideMenuTimer = maketimer(5, obj, obj.hideMenu); + obj._hideMenuTimer.singleShot = 1; + + obj._loadPageTimer = maketimer(0.5, obj, obj.loadPage); + obj._loadPageTimer.singleShot = 1; + + # Emesary + obj._recipient = nil; + + obj.hideMenu(); + return obj; + }, + + addPage : func(name, page) + { + me._pageList[name] = page; + }, + + getPage : func(name) + { + return me._pageList[name]; + }, + + hideMenu : func() + { + foreach(var pageGroup; PAGE_GROUPS) + { + me._svg.getElementById(pageGroup.group).setVisible(0); + me._svg.getElementById(pageGroup.label).setVisible(0); + } + me._menuVisible = 0; + }, + + # Function to change a page based on the selection + loadPage : func() + { + var pageToLoad = PAGE_GROUPS[me._selectedPageGroup].pages[me._selectedPage]; + var page = me._pageList[pageToLoad]; + + if (page != nil) { + me._device.selectPage(page); + } else { + printf("Unable to find page " ~ pageToLoad); + } + }, + + showMenu : func() + { + foreach(var pageGroup; PAGE_GROUPS) + { + if (PAGE_GROUPS[me._selectedPageGroup].label == pageGroup.label) + { + # Display the page group and highlight the label + me._svg.getElementById(pageGroup.group).setVisible(1); + me._svg.getElementById(pageGroup.label).setVisible(1); + me._svg.getElementById(pageGroup.label).setColor(0.7,0.7,1.0); + + foreach (var page; pageGroup.pages) + { + # Highlight the current page. + if (pageGroup.pages[me._selectedPage] == page) { + me._svg.getElementById(page).setColor(0.7,0.7,1.0); + } else { + me._svg.getElementById(page).setColor(0.7,0.7,0.7); + } + } + } + else + { + # Hide the pagegroup and unhighlight the label on the bottom + me._svg.getElementById(pageGroup.group).setVisible(0); + me._svg.getElementById(pageGroup.label).setVisible(1); + me._svg.getElementById(pageGroup.label).setColor(0.7,0.7,0.7); + } + } + me._menuVisible = 1; + me._hideMenuTimer.stop(); + me._hideMenuTimer.restart(5); + me._loadPageTimer.stop(); + me._loadPageTimer.restart(0.5); + + }, + + FMSOuter : func(val) + { + if (me._menuVisible == 1) { + # Change page group + var incr_or_decr = (val > 0) ? 1 : -1; + me._selectedPageGroup = math.mod(me._selectedPageGroup + incr_or_decr, size(PAGE_GROUPS)); + me._selectedPage = 0; + } + me.showMenu(); + }, + + FMSInner : func(val) + { + if (me._menuVisible == 1) { + # Change page group + var incr_or_decr = (val > 0) ? 1 : -1; + me._selectedPage = math.mod(me._selectedPage + incr_or_decr, size(PAGE_GROUPS[me._selectedPageGroup].pages)); + } + me.showMenu(); + }, + RegisterWithEmesary : func(transmitter = nil){ + if (transmitter == nil) + transmitter = emesary.GlobalTransmitter; + + if (me._recipient == nil){ + me._recipient = emesary.Recipient.new("PageController_" ~ me._device.designation); + var pfd_obj = me._device; + var controller = me; + me._recipient.Receive = func(notification) + { + if (notification.Device_Id == pfd_obj.device_id + and notification.NotificationType == notifications.PFDEventNotification.DefaultType) { + if (notification.Event_Id == notifications.PFDEventNotification.HardKeyPushed + and notification.EventParameter != nil) + { + var id = notification.EventParameter.Id; + var value = notification.EventParameter.Value; + #printf("Button pressed " ~ id ~ " " ~ value); + if (id == fg1000.MFD.FASCIA.FMS_OUTER) controller.FMSOuter(value); + if (id == fg1000.MFD.FASCIA.FMS_INNER) controller.FMSInner(value); + if (id == fg1000.MFD.FASCIA.RANGE) { if (pfd_obj.current_page.controller.zoom != nil) pfd_obj.current_page.controller.zoom(value); } + } + + return emesary.Transmitter.ReceiptStatus_OK; + } + return emesary.Transmitter.ReceiptStatus_NotProcessed; + }; + transmitter.Register(me._recipient); + me.transmitter = transmitter; + } + }, + DeRegisterWithEmesary : func(transmitter = nil){ + # remove registration from transmitter; but keep the recipient once it is created. + if (me.transmitter != nil) + me.transmitter.DeRegister(me._recipient); + me.transmitter = nil; + }, + +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMap.nas b/Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMap.nas new file mode 100644 index 000000000..3d9a07df5 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMap.nas @@ -0,0 +1,172 @@ +# Traffic Map +# +# Functionally similar to the Garmin GTS 800 Unit +# +var TrafficMap = +{ + new : func (myCanvas, device, svg) + { + var obj = { + _group : myCanvas.createGroup("TrafficMapLayer"), + parents : [ TrafficMap, device.addPage("TrafficMap", "TrafficMapGroup") ] + }; + + obj.Styles = fg1000.TrafficMapStyles.new(); + obj.Options = fg1000.TrafficMapOptions.new(); + obj.mapgroup = obj._group.createChild("map"); + obj.device = device; + + # Dynamic text elements + obj.op_label = svg.getElementById("TrafficMapOpMode"); + obj.alt_label = svg.getElementById("TrafficMapAltMode"); + obj.outer_label = svg.getElementById("TrafficMapOuterRange"); + obj.inner_label = svg.getElementById("TrafficMapInnerRange"); + + # Need to display this underneath the softkeys, EIS, header. + obj._group.set("z-index", -10.0); + obj._group.setVisible(0); + + # Initialize the controller: + var ctrl_ns = canvas.Map.Controller.get("Aircraft position"); + var source = ctrl_ns.SOURCES["current-pos"]; + if (source == nil) { + # TODO: amend + var source = ctrl_ns.SOURCES["current-pos"] = { + getPosition: func subvec(geo.aircraft_position().latlon(), 0, 2), + getAltitude: func getprop('/position/altitude-ft'), + getHeading: func { + if (me.aircraft_heading) + getprop('/orientation/heading-deg') + else 0 + }, + aircraft_heading: 1, + }; + } + setlistener("/sim/gui/dialogs/map-canvas/aircraft-heading-up", func(n) { + source.aircraft_heading = n.getBoolValue(); + }, 1); + # Make it move with our aircraft: + obj.mapgroup.setController("Aircraft position", "current-pos"); # from aircraftpos.controller + + # Center the map's origin, modified to take into account the surround. + obj.mapgroup.setTranslation( + fg1000.MFD.MAP_CENTER.X, + fg1000.MFD.MAP_CENTER.Y + ); + + var r = func(name,vis=1,zindex=nil) return caller(0)[0]; + foreach(var type; [r('TFC',0),r('APS')] ) { + obj.mapgroup.addLayer(canvas.SymbolLayer, + type.name, + 4, + obj.Styles.getStyle(type.name), + obj.Options.getOption(type.name), + type.vis ); + } + + obj.controller = fg1000.TrafficMapController.new(obj, svg); + + var topMenu = func(device, pg, menuitem) { + pg.clearMenu(); + resetMenuColors(device); + pg.addMenuItem(4, "STANDBY", pg, + func(dev, pg, mi) { pg.controller.setOperate(0); device.updateMenus(); }, # callback + func(svg, mi) { display_toggle(device, svg, mi, "STANDBY"); } + ); + + pg.addMenuItem(5, "OPERATE", pg, + func(dev, pg, mi) { pg.controller.setOperate(1); device.updateMenus(); }, # callback + func(svg, mi) { display_toggle(device, svg, mi, "OPERATE"); } + ); + + pg.addMenuItem(6, "TEST", pg, func(dev, pg, mi) { printf("Traffic Map TEST mode not implemented yet."); }, nil); + pg.addMenuItem(7, "FLT ID", pg, + func(dev, pg, mi) { pg.controller.toggleFlightID(); device.updateMenus(); }, # callback + func(svg, mi) { display_toggle(device, svg, mi, "FLT ID"); } + ); + + pg.addMenuItem(8, "ALT MODE", pg, altMenu); + device.updateMenus(); + }; + + var altMenu = func(device, pg, menuitem) { + pg.clearMenu(); + resetMenuColors(device); + pg.addMenuItem(4, "ABOVE", pg, + func(dev, pg, mi) { pg.controller.setAlt("ABOVE"); device.updateMenus(); }, # callback + func(svg, mi) { display_toggle(device, svg, mi, "ABOVE"); } + ); + pg.addMenuItem(5, "NORMAL", pg, + func(dev, pg, mi) { pg.controller.setAlt("NORMAL"); device.updateMenus(); }, # callback + func(svg, mi) { display_toggle(device, svg, mi, "NORMAL"); } + ); + + pg.addMenuItem(6, "BELOW", pg, + func(dev, pg, mi) { pg.controller.setAlt("BELOW"); device.updateMenus(); }, # callback + func(svg, mi) { display_toggle(device, svg, mi, "BELOW"); } + ); + + pg.addMenuItem(7, "UNREST", pg, + func(dev, pg, mi) { pg.controller.setAlt("UNREST"); device.updateMenus(); }, # callback + func(svg, mi) { display_toggle(device, svg, mi, "UNREST"); } + ); + + pg.addMenuItem(8, "BACK", pg, topMenu); + + device.updateMenus(); + }; + + # Display map toggle softkeys which change color depending + # on whether a particular layer is enabled or not. + var display_toggle = func(device, svg, mi, layer) { + var bg_name = sprintf("SoftKey%d-bg",mi.menu_id); + if (obj.controller.isEnabled(layer)) { + device.svg.getElementById(bg_name).setColorFill(0.5,0.5,0.5); + svg.setColor(0.0,0.0,0.0); + } else { + device.svg.getElementById(bg_name).setColorFill(0.0,0.0,0.0); + svg.setColor(1.0,1.0,1.0); + } + svg.setText(mi.title); + svg.setVisible(1); # display function + }; + + # Function to undo any colors set by display_toggle when loading a new menu + var resetMenuColors = func(device) { + for(var i = 0; i < 12; i +=1) { + var name = sprintf("SoftKey%d",i); + device.svg.getElementById(name ~ "-bg").setColorFill(0.0,0.0,0.0); + device.svg.getElementById(name).setColor(1.0,1.0,1.0); + } + } + + topMenu(device, obj, nil); + + return obj; + }, + setLayerVisible : func(name,n=1) { + me.mapgroup.getLayer(name).setVisible(n); + }, + setRange : func(range, inner_label, outer_label) { + me.mapgroup.setRange(range); + me.inner_label.setText(inner_label); + me.outer_label.setText(outer_label); + }, + setScreenRange : func(range) { + me.mapgroup.setScreenRange(range); + }, + offdisplay : func() { + me._group.setVisible(0); + + # Reset the menu colours. Shouldn't have to do this here, but + # there's not currently an obvious other location to do so. + for(var i = 0; i < 12; i +=1) { + var name = sprintf("SoftKey%d",i); + me.device.svg.getElementById(name ~ "-bg").setColorFill(0.0,0.0,0.0); + me.device.svg.getElementById(name).setColor(1.0,1.0,1.0); + } + }, + ondisplay : func() { + me._group.setVisible(1); + }, +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMapController.nas b/Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMapController.nas new file mode 100644 index 000000000..c652cc81d --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMapController.nas @@ -0,0 +1,91 @@ +# Traffic Map Controller +var TrafficMapController = +{ + # Altitude levels levels. + ALTS : { ABOVE : { label: "ABOVE", ceiling_ft: 9000, floor_ft: 2700 }, + NORMAL : { label: "NORMAL", ceiling_ft: 2700, floor_ft: 2700 }, + BELOW : { label: "BELOW", ceiling_ft: 2700, floor_ft: 9000 }, + UNREST : { label: "UNRESTRICTED", ceiling_ft: 9000, floor_ft: 9000 }}, + + + # Three ranges available + # 2nm + # 2nm / 6nm + # 6nm / 12nm + # + # TODO: Currently we simply use the outer range, and display the inner + # range as 1/3 of the outer. Doing this properly, we should display + # different inner rings. + RANGES : [ {range: 2, inner_label: nil, outer_label: "2nm"}, + {range: 6, inner_label: "2nm", outer_label: "6nm"}, + {range: 12, inner_label: "4nm", outer_label: "12nm"} ], + + + new : func (trafficmap, svg) + { + var obj = { parents : [ TrafficMapController ] }; + obj.range = 1; + obj.alt = "NORMAL"; + obj.operating = 0; + obj.flight_id = 0; + obj.trafficmap = trafficmap; + obj.trafficmap.setScreenRange(689/2.0); + + obj.setZoom(obj.range); + return obj; + }, + zoomIn : func() { + me.setZoom(me.current_zoom -1); + }, + zoomOut : func() { + me.setZoom(me.current_zoom +1); + }, + zoom : func(val) + { + var incr_or_decr = (val > 0) ? 1 : -1; + me.setZoom(me.current_zoom + incr_or_decr); + }, + setZoom : func(zoom) { + if ((zoom < 0) or (zoom > (size(me.RANGES) - 1))) return; + me.current_zoom = zoom; + me.trafficmap.setRange( + me.RANGES[zoom].range, + me.RANGES[zoom].inner_label, + me.RANGES[zoom].outer_label); + }, + setAlt : func(alt) { + if (me.ALTS[alt] == nil) return; + me.trafficmap.alt_label.setText(me.ALTS[alt].label); + me.alt = alt; + # Update the TFC controller to filter out the correct targets + me.trafficmap.mapgroup.getLayer("TFC").options.ceiling_ft = me.ALTS[alt].ceiling_ft; + me.trafficmap.mapgroup.getLayer("TFC").options.floor_ft = me.ALTS[alt].floor_ft; + }, + setOperate : func(enabled) { + if (enabled) { + me.trafficmap.op_label.setText("OPERATING"); + me.trafficmap.setLayerVisible("TFC", 1); + me.operating = 1; + } else { + me.trafficmap.op_label.setText("STANDBY"); + me.trafficmap.setLayerVisible("TFC", 0); + me.operating = 0; + } + }, + setFlightID : func(enabled) { + me.flight_id = enabled; + me.trafficmap.Options.setOption("TFC", "display_id", enabled); + }, + toggleFlightID : func() { + me.setFlightID(! me.flight_id); + }, + isEnabled : func(label) { + # Cheeky little function that returns whether the alt or operation mode + # matches the label. Used to highlight current settings in softkeys + if (label == me.alt) return 1; + if (me.operating and label == "OPERATE") return 1; + if (me.operating == 0 and label == "STANDBY") return 1; + if (me.flight_id == 1 and label == "FLT ID") return 1; + return 0; + }, +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMapOptions.nas b/Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMapOptions.nas new file mode 100644 index 000000000..e8aa2d148 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMapOptions.nas @@ -0,0 +1,34 @@ +# Traffic Map Options +var TrafficMapOptions = +{ + new : func() { + var obj = { parents : [TrafficMapOptions] }; + obj.Options= {}; + obj.loadOptions(); + return obj; + }, + + getOption : func(type) { + return me.Options[type]; + }, + + setOption : func(type, name, value) { + me.Options[type][name] = value; + }, + + loadOptions : func() { + me.clearOptions(); + me.Options.APS = {}; + + me.Options.TFC = { + ceiling_ft : 2700, # Display targets up to this height above the aircraft + floor_ft : 2700, # Display target from this height below the aircraft + display_id: 0, + }; + }, + + clearOptions : func() { + me.Options = {}; + }, + +}; diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMapStyles.nas b/Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMapStyles.nas new file mode 100644 index 000000000..c243d2941 --- /dev/null +++ b/Aircraft/Instruments-3d/FG1000/Nasal/TrafficMap/TrafficMapStyles.nas @@ -0,0 +1,33 @@ +# Traffic Map Styles +var TrafficMapStyles = +{ + new : func() { + var obj = { parents : [ TrafficMapStyles ]}; + obj.Styles = {}; + obj.loadStyles(); + return obj; + }, + + getStyle : func(type) { + return me.Styles[type]; + }, + + setStyle : func(type, name, value) { + me.Styles[type][name] = value; + }, + + loadStyles : func() { + me. clearStyles(); + + me.Styles.TFC = {}; + me.Styles.TFC.scale_factor = 1.0; # 40% (applied to whole group) + + me.Styles.APS = {}; + me.Styles.APS.scale_factor = 0.25; + }, + + clearStyles : func() { + me.Styles = {}; + }, + +}; diff --git a/Nasal/canvas/MFD_Generic.nas b/Nasal/canvas/MFD_Generic.nas index c8b27e764..7940fcb78 100644 --- a/Nasal/canvas/MFD_Generic.nas +++ b/Nasal/canvas/MFD_Generic.nas @@ -17,17 +17,22 @@ # # Menu Item. There is a list of these for each page changing button per display page # Parameters: -# menu_id : page change event id for this menu item. e.g. button number -# title : Title Text (for display on the device) -# page : Instance of page usually returned from PFD.addPage +# menu_id : page change event id for this menu item. e.g. button number +# title : Title Text (for display on the device) +# page : Instance of page usually returned from PFD.addPage +# callbackfn : Function to call when menu item is selected +# displayfn : Function to call when the menu item is displayed. Used to enable +# highlighting of menu items, for example. var PFD_MenuItem = { - new : func (menu_id, title, page) + new : func (menu_id, title, page, callbackfn=nil, displayfn=nil) { var obj = {parents : [PFD_MenuItem] }; obj.page = page; obj.menu_id = menu_id; + obj.callbackfn = callbackfn; + obj.displayfn = displayfn; obj.title = title; return obj; }, @@ -63,7 +68,7 @@ var PFD_Page = # make a page that is currenty visible not visible before making a new page visible, # however more than one page could be visible - but only one set of menu buttons can be active # so if two pages are visible (e.g. an overlay) then when the overlay removed it would be necessary - # to call setVisible on the base page to ensure that the menus are seutp + # to call setVisible on the base page to ensure that the menus are setup setVisible : func(vis) { if(me.svg != nil) @@ -75,6 +80,21 @@ var PFD_Page = me.offdisplay(); }, + # Standard callback for buttons, causing the appropriate page to be displayed + std_callbackfn : func (device, me, mi) + { + device.selectPage(mi.page); + }, + + # Standard display function for buttons, displaying the text and making visible + std_displayfn : func(svg_element, menuitem) + { + svg_element.setText(menuitem.title); + svg_element.setVisible(1); + #me.buttons[mi.menu_id].setText(mi.title); + #me.buttons[mi.menu_id].setVisible(1); + }, + # # Perform action when button is pushed notifyButton : func(button_id) @@ -82,7 +102,7 @@ var PFD_Page = { if (mi.menu_id == button_id) { - me.device.selectPage(mi.page); + if (mi.callbackfn != nil) mi.callbackfn(me.device, me, mi); break; } } @@ -96,14 +116,25 @@ var PFD_Page = # page that will be selected when pressed # # The corresponding menu for the selected page will automatically be loaded - addMenuItem : func(menu_id, title, page) + addMenuItem : func(menu_id, title, page, callbackfn=nil, displayfn=nil) { - var nm = PFD_MenuItem.new(menu_id, title, page); + if (callbackfn == nil) callbackfn = me.std_callbackfn; + if (displayfn == nil) displayfn = me.std_displayfn; + var nm = PFD_MenuItem.new(menu_id, title, page, callbackfn, displayfn); append(me.menus, nm); return nm; }, - # base method for update; this can be overriden per page instance to provide update of the + # + # Clear all items from the menu. Use-case is where they may be a hierarchy + # of menus within the same page. + # + clearMenu : func() + { + me.menus = []; + }, + + # base method for update; this can be overridden per page instance to provide update of the # elements on display (e.g. to display updated properties) update : func(notification=nil) { @@ -133,6 +164,7 @@ var PFD_Device = # - num_menu_buttons is the Number of menu buttons; starting from the bottom left then right, then top, then left. # - button prefix (e.g MI_) is the prefix of the labels in the SVG for the menu boxes. # - _canvas is the canvas group. +# - designation (optional) is used for Emesary designation #NOTE: # This does not actually create the canvas elements, or parse the SVG, that would typically be done in # a higher level class that contains an instance of this class. @@ -161,7 +193,7 @@ var PFD_Device = else { obj.buttons[idx] = msvg; - obj.buttons[idx].setText(sprintf("M",idx)); + obj.buttons[idx].setText(sprintf("M%d",idx)); } } obj.Recipient = nil; @@ -187,17 +219,19 @@ var PFD_Device = var pfd_obj = me; me.Recipient.Receive = func(notification) { - if (notification.Device_id == pfd_obj.device_id + if (notification.Device_Id == pfd_obj.device_id and notification.NotificationType == notifications.PFDEventNotification.DefaultType) { if (notification.Event_Id == notifications.PFDEventNotification.SoftKeyPushed and notification.EventParameter != nil) { + #printf("Button pressed " ~ notification.EventParameter); pfd_obj.notifyButton(notification.EventParameter); } else if (notification.Event_Id == notifications.PFDEventNotification.ChangeMenuText and notification.EventParameter != nil) { foreach(var eventMenu; notification.EventParameter) { + #printf("Menu Text changed : " ~ eventMenu.Text); foreach (var mi ; pfd_obj.current_page.menus) { if (pfd_obj.buttons[eventMenu.Id] != nil) { pfd_obj.buttons[eventMenu.Id].setText(eventMenu.Text); @@ -253,6 +287,17 @@ var PFD_Device = return np; }, # + # Get a named page + # + getPage : func(title) + { + foreach(var p; me.pages) { + if (p.title == title) return p; + } + + return nil; + }, + # # manage the update of the currently selected page update : func(notification=nil) { @@ -264,6 +309,8 @@ var PFD_Device = # - the page object method controls the visibility selectPage : func(p) { + if (me.current_page == p) return; + if (me.current_page != nil) me.current_page.setVisible(0); if (me.buttons != nil) @@ -276,8 +323,7 @@ var PFD_Device = { if (me.buttons[mi.menu_id] != nil) { - me.buttons[mi.menu_id].setText(mi.title); - me.buttons[mi.menu_id].setVisible(1); + mi.displayfn(me.buttons[mi.menu_id], mi); } else printf("PFD_device: Menu for button not found. Menu ID '%s'",mi.menu_id); @@ -290,12 +336,17 @@ var PFD_Device = # ensure that the menus are display correctly for the current page. updateMenus : func { + foreach(var mb ; me.buttons) + if (mb != nil) + mb.setVisible(0); + + if (me.current_page == nil) return; + foreach(var mi ; me.current_page.menus) { if (me.buttons[mi.menu_id] != nil) { - me.buttons[mi.menu_id].setText(mi.title); - me.buttons[mi.menu_id].setVisible(1); + mi.displayfn(me.buttons[mi.menu_id], mi); } else printf("No corresponding item '%s'",mi.menu_id); diff --git a/Nasal/canvas/map/STAMEN_terrain.lcontroller b/Nasal/canvas/map/STAMEN_terrain.lcontroller new file mode 100644 index 000000000..061d76f8c --- /dev/null +++ b/Nasal/canvas/map/STAMEN_terrain.lcontroller @@ -0,0 +1,40 @@ +# See: http://wiki.flightgear.org/MapStructure +# Class things: +var name = 'STAMEN_terrain'; +var parents = [OverlayLayer.Controller]; +var __self__ = caller(0)[0]; +OverlayLayer.Controller.add(name, __self__); +TileLayer.add(name, { + parents: [TileLayer], + type: name, # Layer type + df_controller: __self__, # controller to use by default -- this one +}); + +var new = func(layer) { + var m = { + parents: [__self__], + layer: layer, + map: layer.map, + listeners: [], + }; + + layer.makeURL = string.compileTemplate('http://b.tile.stamen.com/terrain-background/{z}/{x}/{y}.png'); + layer.makePath = string.compileTemplate(layer.maps_base ~ '/stamen-terrain-background/{z}/{x}/{y}.png'); + + #layer.makeURL = string.compileTemplate('http://b.tile.stamen.com/toner/{z}/{x}/{y}.png'); + #layer.makePath = string.compileTemplate(layer.maps_base ~ '/stamen-toner/{z}/{x}/{y}.png'); + + + m.addVisibilityListener(); + m.addRangeListener(); + m.addScreenRangeListener(); + return m; +}; + +var updateLayer = func() { +} + +var del = func() { + foreach (var l; me.listeners) + removelistener(l); +}; diff --git a/Nasal/canvas/map/TFC.lcontroller b/Nasal/canvas/map/TFC.lcontroller index 5f96a1263..7666c24e4 100644 --- a/Nasal/canvas/map/TFC.lcontroller +++ b/Nasal/canvas/map/TFC.lcontroller @@ -1,7 +1,7 @@ # See: http://wiki.flightgear.org/MapStructure # FIXME: this needs to be configurable via the ctor to optionally differentiate between MP and AI traffic, and exclude ground/sea traffic # or at least use a different, non-TCAS symbol for those (looking pretty weird ATM) -# +# # Class things: var name = 'TFC'; var parents = [SymbolLayer.Controller]; @@ -11,6 +11,13 @@ SymbolLayer.add(name, { parents: [MultiSymbolLayer], type: name, # Symbol type df_controller: __self__, # controller to use by default -- this one + df_style: { + }, + df_options: { # default configuration options + floor_ft: 120000, # Display target from this height below the aircraft + ceiling_ft: 120000, # Display targets up to this height above the aircraft + display_id: 1, # Display aircraft ID + }, }); var model_root = props.globals.getNode("/ai/models/"); @@ -67,7 +74,7 @@ var get_alt_diff = func(model) { var model_alt = model.get_alt(); var alt = me.map.getAlt(); if (alt == nil or model_alt == nil) return 0; - return alt-model_alt; + return alt - model_alt; }; ## @@ -83,6 +90,12 @@ var searchCmd_default = func { var result = []; var models = 0; + var alt = me.map.getAlt(); + if (alt == nil) alt = 0; + + var min_alt = alt - me.layer.options.floor_ft; + var max_alt = alt + me.layer.options.ceiling_ft; + # AI and Multiplayer traffic foreach (var t; model_root.getChildren()) { if (!t.getValue("valid")) continue; @@ -95,11 +108,16 @@ var searchCmd_default = func { printlog("alert", "lat/lon was nil for AI node "~t.getPath()); continue; } - if (me.map.controller.in_range(lat, lon)) - append(result, TrafficModel.new(t, nil, me.layer)); + + var tm = TrafficModel.new(t, nil, me.layer); + + if ((min_alt < tm.get_alt()) and + (tm.get_alt() < max_alt) and + me.map.controller.in_range(lat, lon) ) { + append(result, tm); + } } #print("Found "~size(result)~" TrafficModel's in range out of "~models~" total."); return result; }; - diff --git a/Nasal/notifications.nas b/Nasal/notifications.nas index 8f56c7a59..813532f0d 100644 --- a/Nasal/notifications.nas +++ b/Nasal/notifications.nas @@ -13,7 +13,7 @@ # # Version : 4.8 # - # Copyright © 2016 Richard Harrison Released under GPL V2 + # Copyright � 2016 Richard Harrison Released under GPL V2 # #---------------------------------------------------------------------------*/ @@ -35,7 +35,7 @@ var PFDEventNotification_Id = 20; # # Usage example - this can all go into one Nasal module somewhere. #----------- -# var PropertySyncNotification = +# var PropertySyncNotification = # { # new: func(_ident="none", _name="", _kind=0, _secondary_kind=0) # { @@ -65,13 +65,13 @@ var PFDEventNotification_Id = 20; # compared to over 1100 bytes with the traditional method. # # The other advantage with this method of transferring data is that the model is in full control of what is -# sent, and also when it is sent. This works on a per notification basis so less important properties could be +# sent, and also when it is sent. This works on a per notification basis so less important properties could be # transmitted on a less frequent schedule; however this will require an instance of the notification for each one. # # PropertySyncNotificationBase is a shortcut notification; as it doesn't need to received and all # of the properties are simply set when the notification is unpacked over MP. # So although the notification will be transmitted -var PropertySyncNotificationBase = +var PropertySyncNotificationBase = { new: func(_ident="none", _name="", _kind=0, _secondary_kind=0) { @@ -87,29 +87,29 @@ var PropertySyncNotificationBase = new_class.addIntProperty = func(variable, property, length) { me[variable] = nil; - append(me._bridgeProperties, + append(me._bridgeProperties, { getValue:func{return emesary.TransferInt.encode(getprop(property) or 0,length);}, - setValue:func(v,bridge,pos){var dv=emesary.TransferInt.decode(v,length,pos);me[variable]=dv.value;setprop(bridge.PropertyRoot~property, me[variable]);return dv;}, + setValue:func(v,bridge,pos){var dv=emesary.TransferInt.decode(v,length,pos);me[variable]=dv.value;setprop(bridge.PropertyRoot~property, me[variable]);return dv;}, }); } new_class.addNormProperty = func(variable, property, length) { me[variable] = nil; - append(me._bridgeProperties, + append(me._bridgeProperties, { getValue:func{return emesary.TransferNorm.encode(getprop(property) or 0,length);}, - setValue:func(v,bridge,pos){var dv=emesary.TransferNorm.decode(v,length,pos);me[variable] = dv.value;setprop(bridge.PropertyRoot~property, me[variable]);return dv;}, + setValue:func(v,bridge,pos){var dv=emesary.TransferNorm.decode(v,length,pos);me[variable] = dv.value;setprop(bridge.PropertyRoot~property, me[variable]);return dv;}, }); } new_class.addStringProperty = func(variable, property) { me[variable] = nil; - append(me._bridgeProperties, + append(me._bridgeProperties, { getValue:func{return emesary.TransferString.encode(getprop(property) or 0);}, - setValue:func(v,bridge,pos){var dv=emesary.TransferString.decode(v,pos);me[variable] = dv.value;setprop(bridge.PropertyRoot~property, me[variable]);return dv;}, + setValue:func(v,bridge,pos){var dv=emesary.TransferString.decode(v,pos);me[variable] = dv.value;setprop(bridge.PropertyRoot~property, me[variable]);return dv;}, }); } @@ -121,9 +121,9 @@ var PropertySyncNotificationBase = } }; # -# Transmit a generic control event. +# Transmit a generic control event. # two parameters - the event Id and the event value which is a 4 byte length (+/- 1,891371.000) -var AircraftControlNotification = +var AircraftControlNotification = { new: func(_ident="none") { @@ -136,15 +136,15 @@ var AircraftControlNotification = new_class.bridgeProperties = func { - return - [ + return + [ { getValue:func{return emesary.TransferInt.encode(new_class.EventType,2);}, - setValue:func(v,bridge,pos){var dv=emesary.TransferInt.decode(v,2,pos);new_class.EventType=dv.value;return dv;}, + setValue:func(v,bridge,pos){var dv=emesary.TransferInt.decode(v,2,pos);new_class.EventType=dv.value;return dv;}, }, { getValue:func{return emesary.TransferFixedDouble.encode(new_class.EventValue,4,1000);}, - setValue:func(v,bridge,pos){var dv=emesary.TransferFixedDouble.decode(v,4,1000,pos);new_class.EventValue=dv.value;print("dec ",dv.value);return dv;}, + setValue:func(v,bridge,pos){var dv=emesary.TransferFixedDouble.decode(v,4,1000,pos);new_class.EventValue=dv.value;print("dec ",dv.value);return dv;}, }, ]; }; @@ -154,9 +154,9 @@ var AircraftControlNotification = # # -# Use to transmit events that happen at a specific place; can be used to make +# Use to transmit events that happen at a specific place; can be used to make # models that are simulated locally (e.g. tankers) appear on other player's MP sessions. -var GeoEventNotification = +var GeoEventNotification = { # new: # _ident - the identifier for the notification. not bridged. @@ -188,43 +188,43 @@ var GeoEventNotification = }; new_class.bridgeProperties = func { - return - [ + return + [ { getValue:func{return emesary.TransferCoord.encode(new_class.Position);}, - setValue:func(v,root,pos){var dv=emesary.TransferCoord.decode(v, pos);new_class.Position=dv.value;return dv}, + setValue:func(v,root,pos){var dv=emesary.TransferCoord.decode(v, pos);new_class.Position=dv.value;return dv}, }, { getValue:func{return emesary.TransferString.encode(new_class.Name);}, - setValue:func(v,root,pos){var dv=emesary.TransferString.decode(v,pos);new_class.Name=dv.value;return dv}, + setValue:func(v,root,pos){var dv=emesary.TransferString.decode(v,pos);new_class.Name=dv.value;return dv}, }, { getValue:func{return emesary.TransferByte.encode(new_class.Kind);}, - setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.Kind=dv.value;return dv}, + setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.Kind=dv.value;return dv}, }, { getValue:func{return emesary.TransferByte.encode(new_class.SecondaryKind);}, - setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.SecondaryKind=dv.value;return dv}, + setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.SecondaryKind=dv.value;return dv}, }, { getValue:func{return emesary.TransferFixedDouble.encode(new_class.u_fps,2,10);}, - setValue:func(v,root,pos){var dv=emesary.TransferFixedDouble.decode(v,2,10,pos);new_class.u_fps=dv.value;return dv}, + setValue:func(v,root,pos){var dv=emesary.TransferFixedDouble.decode(v,2,10,pos);new_class.u_fps=dv.value;return dv}, }, { getValue:func{return emesary.TransferFixedDouble.encode(new_class.v_fps,2,10);}, - setValue:func(v,root,pos){var dv=emesary.TransferFixedDouble.decode(v,2,10,pos);new_class.v_fps=dv.value;return dv}, + setValue:func(v,root,pos){var dv=emesary.TransferFixedDouble.decode(v,2,10,pos);new_class.v_fps=dv.value;return dv}, }, { getValue:func{return emesary.TransferFixedDouble.encode(new_class.w_fps,2,10);}, - setValue:func(v,root,pos){var dv=emesary.TransferFixedDouble.decode(v,2,10,pos);new_class.w_fps=dv.value;return dv}, + setValue:func(v,root,pos){var dv=emesary.TransferFixedDouble.decode(v,2,10,pos);new_class.w_fps=dv.value;return dv}, }, { getValue:func{return emesary.TransferString.encode(new_class.RemoteCallsign);}, - setValue:func(v,root,pos){var dv=emesary.TransferString.decode(v,pos);new_class.RemoteCallsign=dv.value;return dv}, + setValue:func(v,root,pos){var dv=emesary.TransferString.decode(v,pos);new_class.RemoteCallsign=dv.value;return dv}, }, { getValue:func{return emesary.TransferByte.encode(new_class.Flags);}, - setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.Flags=dv.value;return dv}, + setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.Flags=dv.value;return dv}, }, ]; }; @@ -232,7 +232,7 @@ var GeoEventNotification = }, }; # -# Defined kinds: +# Defined kinds: # 1 - Created # 2 - Moved # 3 - Deleted @@ -242,8 +242,8 @@ var GeoEventNotification = # using the first 4 bits as the classification and the second 4 bits as the sub-classification #----------- # Type 0000 : Cargo -# 0 0000 0000 - Vehicle -# 1 0000 0001 - Person +# 0 0000 0000 - Vehicle +# 1 0000 0001 - Person # 2 0000 0010 - 10 kg Item # 3 0000 0011 - 20 kg Item # 4 0000 0100 - 30 kg Item @@ -256,26 +256,26 @@ var GeoEventNotification = # 11 0000 1011 - Chaff # 12 0000 1100 - Flares # 13 0000 1101 - Water (fire fighting) -# 14 0000 1110 - +# 14 0000 1110 - # 15 0000 1111 - Morris Marina #-------- # Type 0001 : Self propelled # 16 0001 0000 - X-2 # 17 0001 0001 - X-15 # 18 0001 0010 - X-24 -# 19 0001 0011 - -# 20 0001 0100 - -# 21 0001 0101 - -# 22 0001 0110 - -# 23 0001 0111 - -# 24 0001 1000 - -# 25 0001 1001 - -# 26 0001 1010 - -# 27 0001 1011 - -# 28 0001 1100 - -# 29 0001 1101 - -# 30 0001 1110 - -# 31 0001 1111 - +# 19 0001 0011 - +# 20 0001 0100 - +# 21 0001 0101 - +# 22 0001 0110 - +# 23 0001 0111 - +# 24 0001 1000 - +# 25 0001 1001 - +# 26 0001 1010 - +# 27 0001 1011 - +# 28 0001 1100 - +# 29 0001 1101 - +# 30 0001 1110 - +# 31 0001 1111 - #-------- # Type 0010 : Aircraft Damage (e.g space shuttle re-entry or during launch) # 32 0010 0000 - Engine 1 @@ -300,36 +300,36 @@ var GeoEventNotification = # 49 0011 0001 - Drop Tank 2 # 50 0011 0010 - Drop Tank 3 # 51 0011 0011 - Drop Tank 4 -# 52 0011 0100 - -# 53 0011 0101 - -# 54 0011 0110 - -# 55 0011 0111 - -# 56 0011 1000 - -# 57 0011 1001 - -# 58 0011 1010 - -# 59 0011 1011 - -# 60 0011 1100 - -# 61 0011 1101 - -# 62 0011 1110 - -# 63 0011 1111 - +# 52 0011 0100 - +# 53 0011 0101 - +# 54 0011 0110 - +# 55 0011 0111 - +# 56 0011 1000 - +# 57 0011 1001 - +# 58 0011 1010 - +# 59 0011 1011 - +# 60 0011 1100 - +# 61 0011 1101 - +# 62 0011 1110 - +# 63 0011 1111 - #-------- -# Type 0100 : -# 64 0100 0000 - -# 65 0100 0001 - -# 66 0100 0010 - -# 67 0100 0011 - -# 68 0100 0100 - -# 69 0100 0101 - -# 70 0100 0110 - -# 71 0100 0111 - -# 72 0100 1000 - -# 73 0100 1001 - -# 74 0100 1010 - -# 75 0100 1011 - -# 76 0100 1100 - -# 77 0100 1101 - -# 78 0100 1110 - -# 79 0100 1111 - +# Type 0100 : +# 64 0100 0000 - +# 65 0100 0001 - +# 66 0100 0010 - +# 67 0100 0011 - +# 68 0100 0100 - +# 69 0100 0101 - +# 70 0100 0110 - +# 71 0100 0111 - +# 72 0100 1000 - +# 73 0100 1001 - +# 74 0100 1010 - +# 75 0100 1011 - +# 76 0100 1100 - +# 77 0100 1101 - +# 78 0100 1110 - +# 79 0100 1111 - #-------- # Type 0101 : Models/Geometry items # 80 0101 0000 - Aim91x.ac @@ -354,199 +354,202 @@ var GeoEventNotification = # 97 0110 0001 - rocket.ac # 98 0110 0010 - tracer.ac # 99 0110 0011 - tracer2.ac -# 100 0110 0100 - -# 101 0110 0101 - -# 102 0110 0110 - -# 103 0110 0111 - -# 104 0110 1000 - -# 105 0110 1001 - -# 106 0110 1010 - -# 107 0110 1011 - -# 108 0110 1100 - -# 109 0110 1101 - -# 110 0110 1110 - -# 111 0110 1111 - +# 100 0110 0100 - +# 101 0110 0101 - +# 102 0110 0110 - +# 103 0110 0111 - +# 104 0110 1000 - +# 105 0110 1001 - +# 106 0110 1010 - +# 107 0110 1011 - +# 108 0110 1100 - +# 109 0110 1101 - +# 110 0110 1110 - +# 111 0110 1111 - #-------- # Type 0111 : Models/Geometry items -# 112 0111 0000 - -# 113 0111 0001 - -# 114 0111 0010 - -# 115 0111 0011 - -# 116 0111 0100 - -# 117 0111 0101 - -# 118 0111 0110 - -# 119 0111 0111 - -# 120 0111 1000 - -# 121 0111 1001 - -# 122 0111 1010 - -# 123 0111 1011 - -# 124 0111 1100 - -# 125 0111 1101 - -# 126 0111 1110 - -# 127 0111 1111 - +# 112 0111 0000 - +# 113 0111 0001 - +# 114 0111 0010 - +# 115 0111 0011 - +# 116 0111 0100 - +# 117 0111 0101 - +# 118 0111 0110 - +# 119 0111 0111 - +# 120 0111 1000 - +# 121 0111 1001 - +# 122 0111 1010 - +# 123 0111 1011 - +# 124 0111 1100 - +# 125 0111 1101 - +# 126 0111 1110 - +# 127 0111 1111 - #-------- # Type 1000 : Models/Geometry items -# 128 1000 0000 - -# 129 1000 0001 - -# 130 1000 0010 - -# 131 1000 0011 - -# 132 1000 0100 - -# 133 1000 0101 - -# 134 1000 0110 - -# 135 1000 0111 - -# 136 1000 1000 - -# 137 1000 1001 - -# 138 1000 1010 - -# 139 1000 1011 - -# 140 1000 1100 - -# 141 1000 1101 - -# 142 1000 1110 - -# 143 1000 1111 - +# 128 1000 0000 - +# 129 1000 0001 - +# 130 1000 0010 - +# 131 1000 0011 - +# 132 1000 0100 - +# 133 1000 0101 - +# 134 1000 0110 - +# 135 1000 0111 - +# 136 1000 1000 - +# 137 1000 1001 - +# 138 1000 1010 - +# 139 1000 1011 - +# 140 1000 1100 - +# 141 1000 1101 - +# 142 1000 1110 - +# 143 1000 1111 - #-------- -# Type 1001 : -# 144 1001 0000 - -# 145 1001 0001 - -# 146 1001 0010 - -# 147 1001 0011 - -# 148 1001 0100 - -# 149 1001 0101 - -# 150 1001 0110 - -# 151 1001 0111 - -# 152 1001 1000 - -# 153 1001 1001 - -# 154 1001 1010 - -# 155 1001 1011 - -# 156 1001 1100 - -# 157 1001 1101 - -# 158 1001 1110 - -# 159 1001 1111 - +# Type 1001 : +# 144 1001 0000 - +# 145 1001 0001 - +# 146 1001 0010 - +# 147 1001 0011 - +# 148 1001 0100 - +# 149 1001 0101 - +# 150 1001 0110 - +# 151 1001 0111 - +# 152 1001 1000 - +# 153 1001 1001 - +# 154 1001 1010 - +# 155 1001 1011 - +# 156 1001 1100 - +# 157 1001 1101 - +# 158 1001 1110 - +# 159 1001 1111 - #-------- -# Type 1010 : -# 160 1010 0000 - -# 161 1010 0001 - -# 162 1010 0010 - -# 163 1010 0011 - -# 164 1010 0100 - -# 165 1010 0101 - -# 166 1010 0110 - -# 167 1010 0111 - -# 168 1010 1000 - -# 169 1010 1001 - -# 170 1010 1010 - -# 171 1010 1011 - -# 172 1010 1100 - -# 173 1010 1101 - -# 174 1010 1110 - -# 175 1010 1111 - +# Type 1010 : +# 160 1010 0000 - +# 161 1010 0001 - +# 162 1010 0010 - +# 163 1010 0011 - +# 164 1010 0100 - +# 165 1010 0101 - +# 166 1010 0110 - +# 167 1010 0111 - +# 168 1010 1000 - +# 169 1010 1001 - +# 170 1010 1010 - +# 171 1010 1011 - +# 172 1010 1100 - +# 173 1010 1101 - +# 174 1010 1110 - +# 175 1010 1111 - #-------- -# Type 1011 : -# 176 1011 0000 - -# 177 1011 0001 - -# 178 1011 0010 - -# 179 1011 0011 - -# 180 1011 0100 - -# 181 1011 0101 - -# 182 1011 0110 - -# 183 1011 0111 - -# 184 1011 1000 - -# 185 1011 1001 - -# 186 1011 1010 - -# 187 1011 1011 - -# 188 1011 1100 - -# 189 1011 1101 - -# 190 1011 1110 - -# 191 1011 1111 - +# Type 1011 : +# 176 1011 0000 - +# 177 1011 0001 - +# 178 1011 0010 - +# 179 1011 0011 - +# 180 1011 0100 - +# 181 1011 0101 - +# 182 1011 0110 - +# 183 1011 0111 - +# 184 1011 1000 - +# 185 1011 1001 - +# 186 1011 1010 - +# 187 1011 1011 - +# 188 1011 1100 - +# 189 1011 1101 - +# 190 1011 1110 - +# 191 1011 1111 - #-------- -# Type 1100 : -# 192 1100 0000 - -# 193 1100 0001 - -# 194 1100 0010 - -# 195 1100 0011 - -# 196 1100 0100 - -# 197 1100 0101 - -# 198 1100 0110 - -# 199 1100 0111 - -# 200 1100 1000 - -# 201 1100 1001 - -# 202 1100 1010 - -# 203 1100 1011 - -# 204 1100 1100 - -# 205 1100 1101 - -# 206 1100 1110 - -# 207 1100 1111 - +# Type 1100 : +# 192 1100 0000 - +# 193 1100 0001 - +# 194 1100 0010 - +# 195 1100 0011 - +# 196 1100 0100 - +# 197 1100 0101 - +# 198 1100 0110 - +# 199 1100 0111 - +# 200 1100 1000 - +# 201 1100 1001 - +# 202 1100 1010 - +# 203 1100 1011 - +# 204 1100 1100 - +# 205 1100 1101 - +# 206 1100 1110 - +# 207 1100 1111 - #-------- -# Type 1101 : -# 208 1101 0000 - -# 209 1101 0001 - -# 210 1101 0010 - -# 211 1101 0011 - -# 212 1101 0100 - -# 213 1101 0101 - -# 214 1101 0110 - -# 215 1101 0111 - -# 216 1101 1000 - -# 217 1101 1001 - -# 218 1101 1010 - -# 219 1101 1011 - -# 220 1101 1100 - -# 221 1101 1101 - -# 222 1101 1110 - -# 223 1101 1111 - +# Type 1101 : +# 208 1101 0000 - +# 209 1101 0001 - +# 210 1101 0010 - +# 211 1101 0011 - +# 212 1101 0100 - +# 213 1101 0101 - +# 214 1101 0110 - +# 215 1101 0111 - +# 216 1101 1000 - +# 217 1101 1001 - +# 218 1101 1010 - +# 219 1101 1011 - +# 220 1101 1100 - +# 221 1101 1101 - +# 222 1101 1110 - +# 223 1101 1111 - #-------- -# Type 1110 : -# 224 1110 0000 - -# 225 1110 0001 - -# 226 1110 0010 - -# 227 1110 0011 - -# 228 1110 0100 - -# 229 1110 0101 - -# 230 1110 0110 - -# 231 1110 0111 - -# 232 1110 1000 - -# 233 1110 1001 - -# 234 1110 1010 - -# 235 1110 1011 - -# 236 1110 1100 - -# 237 1110 1101 - -# 238 1110 1110 - -# 239 1110 1111 - +# Type 1110 : +# 224 1110 0000 - +# 225 1110 0001 - +# 226 1110 0010 - +# 227 1110 0011 - +# 228 1110 0100 - +# 229 1110 0101 - +# 230 1110 0110 - +# 231 1110 0111 - +# 232 1110 1000 - +# 233 1110 1001 - +# 234 1110 1010 - +# 235 1110 1011 - +# 236 1110 1100 - +# 237 1110 1101 - +# 238 1110 1110 - +# 239 1110 1111 - #-------- -# Type 1111 : -# 240 1111 0000 - -# 241 1111 0001 - -# 242 1111 0010 - -# 243 1111 0011 - -# 244 1111 0100 - -# 245 1111 0101 - -# 246 1111 0110 - -# 247 1111 0111 - -# 248 1111 1000 - -# 249 1111 1001 - -# 250 1111 1010 - -# 251 1111 1011 - -# 252 1111 1100 - -# 253 1111 1101 - -# 254 1111 1110 - -# 255 1111 1111 - +# Type 1111 : +# 240 1111 0000 - +# 241 1111 0001 - +# 242 1111 0010 - +# 243 1111 0011 - +# 244 1111 0100 - +# 245 1111 0101 - +# 246 1111 0110 - +# 247 1111 0111 - +# 248 1111 1000 - +# 249 1111 1001 - +# 250 1111 1010 - +# 251 1111 1011 - +# 252 1111 1100 - +# 253 1111 1101 - +# 254 1111 1110 - +# 255 1111 1111 - # # -# Use to transmit events that happen at a specific place; can be used to make +# Use to transmit events that happen at a specific place; can be used to make # models that are simulated locally (e.g. tankers) appear on other player's MP sessions. -var PFDEventNotification = +var PFDEventNotification = { # new: # _ident - the identifier for the notification. not bridged. # _pfd_id - numeric identification of the PFD within the model -# _event_id - event ID. +# _event_id - event ID. # 1 softkey pushed. # 2 select page by ID +# 3 Change softkey button text +# 4 hardkey pushed - i.e. non-soft keys that don't change function based on context. # _event_param - param related to the event ID. implementation specific. ## SoftKeyPushed : 1, SelectPageById : 2, - ChangeMenuText : 3, #event parameter contains hash of { Id: , Text: } + ChangeMenuText : 3, #event parameter contains array of { Id: , Text: } tuples + HardKeyPushed : 4, #event parameter contains single { Id: , Value: } tuple DefaultType : "PFDEventNotification", new: func(_ident, _device_id,_event_id,_event_parameter_id) @@ -560,15 +563,15 @@ var PFDEventNotification = new_class.bridgeProperties = func { - return - [ + return + [ { getValue:func{return emesary.TransferByte.encode(new_class.Event_Id);}, - setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.Event_Id=dv.value;return dv}, + setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.Event_Id=dv.value;return dv}, }, { getValue:func{return emesary.TransferByte.encode(new_class.EventParameter);}, - setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.EventParameter=dv.value;return dv}, + setValue:func(v,root,pos){var dv=emesary.TransferByte.decode(v,pos);new_class.EventParameter=dv.value;return dv}, }, ]; }; diff --git a/Translations/default/menu.xml b/Translations/default/menu.xml index 1ca2a4be7..8fdd8ec29 100644 --- a/Translations/default/menu.xml +++ b/Translations/default/menu.xml @@ -110,6 +110,7 @@ Monitor System Performance Save Video Configuration Browse Internal Properties + FG1000 MFD Logging Local Weather (Test) Print Visible Scene Info diff --git a/gui/dialogs/fg1000.xml b/gui/dialogs/fg1000.xml new file mode 100644 index 000000000..41684afd8 --- /dev/null +++ b/gui/dialogs/fg1000.xml @@ -0,0 +1,892 @@ + + + + fg1000 + vbox + true + 3 + + + 1.0 + + + + + + + + + + hbox + 1 + 1 + + + + + + 1 + + + + + + + + hbox + true + fill + + + + vbox + center + top + + + vol + + 30 + /gui/dialogs/fg1000/nav-vol + + + + + + + + hbox + center + 0 + + + + + + + + + + + + + + hdg + 20 + /gui/dialogs/fg1000/hdg + + + + + + + table + + + + + + + + + + + + + + + + + + + + + + + hbox + center + 0 + + + + + + + + + + + + + + + vbox + true + + + + canvas-map + center + center + false + 1024 + 768 + 1024 + 768 + + + hbox + true + fill + fill + + + + + hbox + true + fill + top + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vbox + center + top + + + com-vol + + 30 + /gui/dialogs/fg1000/com-vol + + + + + + + + hbox + center + 0 + + + + + + + + + + + + + + + baro + 20 + /gui/dialogs/fg1000/baro + + + + + + + + crs + 20 + /gui/dialogs/fg1000/crs + dialog-apply + + + + + + + + + + + hbox + center + 0 + + + + + + + table + center + 0 + + + + + + + + + + table + center + + + + + + + + + + + + + + hbox + center + 0 + + + + + + + + + + + + diff --git a/gui/menubar.xml b/gui/menubar.xml index 5f0368a32..56283a292 100644 --- a/gui/menubar.xml +++ b/gui/menubar.xml @@ -342,21 +342,21 @@ + + fg1000 + + dialog-show + fg1000 + + + logging