From 1471f043eac5a5ad19624c7dfd2e391a851873de Mon Sep 17 00:00:00 2001 From: Michael Corcoran Date: Sat, 18 Jan 2025 00:05:28 +1300 Subject: [PATCH 1/3] feat(a32nx/fms): database holds --- .github/CHANGELOG.md | 2 +- .../CDU/A320_Neo_CDU_HoldAtPage.js | 100 +++++++++++++++++- 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index a92b729492d..0277bca358b 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -125,7 +125,7 @@ 1. [A380X/MFD] Add airport data page into the MFD (DATA > AIRPORT) - @bulenteroglu (senolitam) 1. [A380X/EFB] Adds PRIM/SEC/FCDC failures to EFB - @flogross89 (floridude) 1. [A380X/PFD] Fix precision of pitch trim indicator - @flogross89 (floridude) - +1. [A32NX/FMS] Add terminal area database holds for MSFS2024 - @tracernz (Mike) ## 0.12.0 diff --git a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/CDU/A320_Neo_CDU_HoldAtPage.js b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/CDU/A320_Neo_CDU_HoldAtPage.js index 05bee15af19..36211f7fc1d 100644 --- a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/CDU/A320_Neo_CDU_HoldAtPage.js +++ b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/CDU/A320_Neo_CDU_HoldAtPage.js @@ -1,4 +1,4 @@ -// Copyright (c) 2021-2023 FlyByWire Simulations +// Copyright (c) 2021-2023, 2025 FlyByWire Simulations // // SPDX-License-Identifier: GPL-3.0 @@ -55,9 +55,60 @@ class CDUHoldAtPage { type: HoldType.Computed, }; modifiedHold = {}; + + const fix = waypoint.terminationWaypoint(); + if (fix && fix.area === 1 /* WaypointArea.Terminal */ && fix.airportIdent && fix.airportIdent.length === 4) { + Fmgc.NavigationDatabaseService.activeDatabase.getHolds(fix.ident, fix.airportIdent).then((holds) => { + // Pick a hold based on altitude suitability and inbound course + // Missing in the navdata is the duplicate indicator that would help us pick the right area/airspace type. + holds + .filter((v) => v.waypoint.databaseId === fix.databaseId) + .sort((a, b) => { + let ret = CDUHoldAtPage.holdVerticalDistanceFromAlt(alt, a) - CDUHoldAtPage.holdVerticalDistanceFromAlt(alt, b); + if (ret === 0) { + ret = Math.abs(a.magneticCourse - inboundMagneticCourse) - Math.abs(b.magneticCourse - inboundMagneticCourse); + } + return ret; + }); + + if (holds[0]) { + defaultHold = { + inboundMagneticCourse: holds[0].magneticCourse, + turnDirection: holds[0].turnDirection, + time: holds[0].lengthTime ? holds[0].lengthTime : undefined, + distance: holds[0].length ? holds[0].length : undefined, + type: HoldType.Database, + }; + } + + CDUHoldAtPage.addOrEditManualHold( + mcdu, + waypointIndexFP, + // eslint-disable-next-line prefer-object-spread + Object.assign({}, defaultHold), + modifiedHold, + defaultHold, + forPlan, + inAlternate, + ); + }).catch(() => { + CDUHoldAtPage.addOrEditManualHold( + mcdu, + waypointIndexFP, + // eslint-disable-next-line prefer-object-spread + Object.assign({}, defaultHold), + modifiedHold, + defaultHold, + forPlan, + inAlternate, + ); + }); + return; + } } - mcdu.flightPlanService.addOrEditManualHold( + CDUHoldAtPage.addOrEditManualHold( + mcdu, waypointIndexFP, // eslint-disable-next-line prefer-object-spread Object.assign({}, defaultHold), @@ -65,12 +116,51 @@ class CDUHoldAtPage { defaultHold, forPlan, inAlternate, - ).then((holdIndex) => { - CDUHoldAtPage.DrawPage(mcdu, holdIndex, waypointIndexFP, forPlan, inAlternate); - }); + ); + } + } + + static holdVerticalDistanceFromAlt(altitude, hold) { + switch (hold.altitudeDescriptor) { + case "B": + if (altitude <= hold.altitude1 && altitude >= hold.altitude2) { + return 0; + } + if (altitude > hold.altitude1) { + return altitude - hold.altitude1; + } + return hold.altitude2 - altitude; + case "+": + return altitude >= hold.altitude1 ? 0 : hold.altitude1 - altitude; + case "-": + return altitude <= hold.altitude1 ? 0 : altitude - hold.altitude1; + default: + // no restriction, so always suitable + return 0; } } + static addOrEditManualHold( + mcdu, + atIndex, + desiredHold, + modifiedHold, + defaultHold, + planIndex, + alternate, + ) { + mcdu.flightPlanService.addOrEditManualHold( + atIndex, + desiredHold, + modifiedHold, + defaultHold, + planIndex, + alternate, + ).then((holdIndex) => { + CDUHoldAtPage.DrawPage(mcdu, holdIndex, atIndex, planIndex, alternate); + }); + } + static DrawPage(mcdu, waypointIndexFP, originalFpIndex, forPlan, inAlternate) { mcdu.clearDisplay(); mcdu.page.Current = mcdu.page.HoldAtPage; From 880fe4d6ca8f9d75c6595a43bda1fa4157f8075e Mon Sep 17 00:00:00 2001 From: Michael Corcoran Date: Thu, 23 Jan 2025 17:44:56 +1300 Subject: [PATCH 2/3] fix(a32nx): look for more holds --- .../CDU/A320_Neo_CDU_HoldAtPage.js | 106 ++++++++++-------- 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/CDU/A320_Neo_CDU_HoldAtPage.js b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/CDU/A320_Neo_CDU_HoldAtPage.js index 36211f7fc1d..f52bd27c101 100644 --- a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/CDU/A320_Neo_CDU_HoldAtPage.js +++ b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/CDU/A320_Neo_CDU_HoldAtPage.js @@ -57,54 +57,66 @@ class CDUHoldAtPage { modifiedHold = {}; const fix = waypoint.terminationWaypoint(); - if (fix && fix.area === 1 /* WaypointArea.Terminal */ && fix.airportIdent && fix.airportIdent.length === 4) { - Fmgc.NavigationDatabaseService.activeDatabase.getHolds(fix.ident, fix.airportIdent).then((holds) => { - // Pick a hold based on altitude suitability and inbound course - // Missing in the navdata is the duplicate indicator that would help us pick the right area/airspace type. - holds - .filter((v) => v.waypoint.databaseId === fix.databaseId) - .sort((a, b) => { - let ret = CDUHoldAtPage.holdVerticalDistanceFromAlt(alt, a) - CDUHoldAtPage.holdVerticalDistanceFromAlt(alt, b); - if (ret === 0) { - ret = Math.abs(a.magneticCourse - inboundMagneticCourse) - Math.abs(b.magneticCourse - inboundMagneticCourse); - } - return ret; - }); - - if (holds[0]) { - defaultHold = { - inboundMagneticCourse: holds[0].magneticCourse, - turnDirection: holds[0].turnDirection, - time: holds[0].lengthTime ? holds[0].lengthTime : undefined, - distance: holds[0].length ? holds[0].length : undefined, - type: HoldType.Database, - }; - } - - CDUHoldAtPage.addOrEditManualHold( - mcdu, - waypointIndexFP, - // eslint-disable-next-line prefer-object-spread - Object.assign({}, defaultHold), - modifiedHold, - defaultHold, - forPlan, - inAlternate, - ); - }).catch(() => { - CDUHoldAtPage.addOrEditManualHold( - mcdu, - waypointIndexFP, - // eslint-disable-next-line prefer-object-spread - Object.assign({}, defaultHold), - modifiedHold, - defaultHold, - forPlan, - inAlternate, - ); - }); - return; + const promises = []; + // Due to the way MSFS stores holds, we have to try all these possibilities. + if (targetPlan.originAirport) { + promises.push(Fmgc.NavigationDatabaseService.activeDatabase.getHolds(fix.ident, targetPlan.originAirport.ident)); } + if (targetPlan.destinationAirport) { + promises.push(Fmgc.NavigationDatabaseService.activeDatabase.getHolds(fix.ident, targetPlan.destinationAirport.ident)); + } + if (fix && fix.area === 1 /* WaypointArea.Terminal */ && fix.airportIdent && fix.airportIdent.length > 0) { + promises.push(Fmgc.NavigationDatabaseService.activeDatabase.getHolds(fix.ident, fix.airportIdent)); + } + Promise.all(promises).then((resolvedPromises) => { + // Pick a hold based on altitude suitability and inbound course + // Missing in the navdata is the duplicate indicator that would help us pick the right area/airspace type. + const holds = resolvedPromises + .reduce((allHolds, holds) => { + allHolds.push(...holds); return allHolds; + }) + .filter((v) => v.waypoint.databaseId === fix.databaseId) + .sort((a, b) => { + let ret = CDUHoldAtPage.holdVerticalDistanceFromAlt(alt, a) - CDUHoldAtPage.holdVerticalDistanceFromAlt(alt, b); + if (ret === 0) { + ret = Math.abs(a.magneticCourse - inboundMagneticCourse) - Math.abs(b.magneticCourse - inboundMagneticCourse); + } + return ret; + }); + + if (holds[0]) { + defaultHold = { + inboundMagneticCourse: holds[0].magneticCourse, + turnDirection: holds[0].turnDirection, + time: holds[0].lengthTime ? holds[0].lengthTime : undefined, + distance: holds[0].length ? holds[0].length : undefined, + type: HoldType.Database, + }; + } + + CDUHoldAtPage.addOrEditManualHold( + mcdu, + waypointIndexFP, + // eslint-disable-next-line prefer-object-spread + Object.assign({}, defaultHold), + modifiedHold, + defaultHold, + forPlan, + inAlternate, + ); + }).catch(() => { + CDUHoldAtPage.addOrEditManualHold( + mcdu, + waypointIndexFP, + // eslint-disable-next-line prefer-object-spread + Object.assign({}, defaultHold), + modifiedHold, + defaultHold, + forPlan, + inAlternate, + ); + }); + return; } CDUHoldAtPage.addOrEditManualHold( From 66fcf9be9c84939dadd1060615ad563663e5b59e Mon Sep 17 00:00:00 2001 From: Michael Corcoran Date: Fri, 24 Jan 2025 21:59:53 +1300 Subject: [PATCH 3/3] refactor: smol fix Co-authored-by: BBK <22713769+BlueberryKing@users.noreply.github.com> --- .../FlyByWire_A320_Neo/CDU/A320_Neo_CDU_HoldAtPage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/CDU/A320_Neo_CDU_HoldAtPage.js b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/CDU/A320_Neo_CDU_HoldAtPage.js index f52bd27c101..b77c34e3a0f 100644 --- a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/CDU/A320_Neo_CDU_HoldAtPage.js +++ b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/html_ui/Pages/VCockpit/Instruments/Airliners/FlyByWire_A320_Neo/CDU/A320_Neo_CDU_HoldAtPage.js @@ -143,9 +143,9 @@ class CDUHoldAtPage { } return hold.altitude2 - altitude; case "+": - return altitude >= hold.altitude1 ? 0 : hold.altitude1 - altitude; + return Math.max(0, hold.altitude1 - altitude); case "-": - return altitude <= hold.altitude1 ? 0 : altitude - hold.altitude1; + return Math.max(0, altitude - hold.altitude1); default: // no restriction, so always suitable return 0;