From a903032043933adda1c8a3e911b466e2125f2407 Mon Sep 17 00:00:00 2001 From: Pruznak <pruznak10@gmail.com> Date: Sun, 15 Dec 2024 17:53:55 +0100 Subject: [PATCH 1/6] Fix BARO/RADIO accepts 0, RADIO does not accept valid text entries --- .../src/MFD/pages/FMS/MfdFmsPerf.tsx | 33 ++++++++++--- .../src/MFD/pages/common/DataEntryFormats.tsx | 46 +++++++++++++++++++ 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx index 7baa4383e7d..166de5bafcb 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx @@ -13,6 +13,7 @@ import './MfdFmsPerf.scss'; import { AltitudeFormat, AltitudeOrFlightLevelFormat, + RadioAltitudeFormat, CostIndexFormat, DescentRateFormat, FlightLevelFormat, @@ -400,6 +401,8 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> { private apprVerticalDeviation = Subject.create<string>('+-----'); + private apprRadioInputText = Subject.create<string | number | null>(null); + private readonly apprRadioText = this.precisionApproachSelected.map((v) => (v ? 'RADIO' : '-----')); private missedThrRedAlt = Subject.create<number | null>(null); @@ -2505,9 +2508,13 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> { <div style="display: flex; flex-direction: row;"> <span class="mfd-label mfd-spacing-right perf-appr-weather">BARO</span> <InputField<number> - dataEntryFormat={new AltitudeFormat(Subject.create(0), Subject.create(maxCertifiedAlt))} + dataEntryFormat={new AltitudeFormat(Subject.create(1), Subject.create(maxCertifiedAlt))} dataHandlerDuringValidation={async (v) => { - SimVar.SetSimVarValue('L:AIRLINER_MINIMUM_DESCENT_ALTITUDE', 'feet', v); + if (v === null) { + SimVar.SetSimVarValue('L:AIRLINER_MINIMUM_DESCENT_ALTITUDE', 'feet', 0); + } else { + SimVar.SetSimVarValue('L:AIRLINER_MINIMUM_DESCENT_ALTITUDE', 'feet', v); + } }} mandatory={Subject.create(false)} value={this.props.fmcService.master.fmgc.data.approachBaroMinimum} @@ -2523,19 +2530,33 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> { <ConditionalComponent condition={this.precisionApproachSelected} componentIfTrue={ - <InputField<number> - dataEntryFormat={new AltitudeFormat(Subject.create(0), Subject.create(maxCertifiedAlt))} + <InputField<string | number> + dataEntryFormat={ + new RadioAltitudeFormat(Subject.create(1), Subject.create(maxCertifiedAlt)) + } dataHandlerDuringValidation={async (v) => { if (v === undefined) { SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', -1); - } else if (v === null) { + } else if (v === 'NONE' || v === 'NO' || v === 'NO DH' || v === 'NODH' || v === null) { SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', -2); } else { SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', v); } }} mandatory={Subject.create(false)} - value={this.props.fmcService.master.fmgc.data.approachRadioMinimum} + value={this.apprRadioInputText} + onModified={(v) => { + if (v === null) { + this.apprRadioInputText.set(null); + this.props.fmcService.master?.fmgc.data.approachRadioMinimum.set(null); + } else if (v === 'NONE' || v === 'NO' || v === 'NO DH' || v === 'NODH') { + this.apprRadioInputText.set(v); + this.props.fmcService.master?.fmgc.data.approachRadioMinimum.set(null); + } else { + this.apprRadioInputText.set(Number(v)); + this.props.fmcService.master?.fmgc.data.approachRadioMinimum.set(Number(v)); + } + }} containerStyle="width: 150px;" alignText="flex-end" errorHandler={(e) => this.props.fmcService.master?.showFmsErrorMessage(e)} diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx index 9186ef2654e..e21fb02e301 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx @@ -200,6 +200,52 @@ export class AltitudeFormat implements DataEntryFormat<number> { } } +export class RadioAltitudeFormat implements DataEntryFormat<string | number> { + public placeholder = '-----'; + + public maxDigits = 5; + + private minValue = 1; + + private maxValue = maxCertifiedAlt; + + constructor( + minValue: Subscribable<number> = Subject.create(1), + maxValue: Subscribable<number> = Subject.create(maxCertifiedAlt), + ) { + minValue.sub((val) => (this.minValue = val), true); + maxValue.sub((val) => (this.maxValue = val), true); + } + + public format(value: string | number) { + if (value === null || value === undefined) { + return [this.placeholder, null, 'FT'] as FieldFormatTuple; + } else if (value === 'NONE' || value === 'NO' || value === 'NO DH' || value === 'NODH') { + return [value, null, null] as FieldFormatTuple; + } + return [Number(value).toFixed(0).toString(), null, 'FT'] as FieldFormatTuple; + } + + public async parse(input: string) { + if (input === '') { + return null; + } + if (input === 'NONE' || input === 'NO' || input === 'NO DH' || input === 'NODH') { + return input; + } + + const nbr = Number(input); + if (!Number.isNaN(nbr) && nbr <= this.maxValue && nbr >= this.minValue) { + return nbr; + } + if (nbr > this.maxValue || nbr < this.minValue) { + throw new FmsError(FmsErrorType.EntryOutOfRange); + } else { + throw new FmsError(FmsErrorType.FormatError); + } + } +} + /** * Unit of value: Feet (i.e. FL * 100) */ From d046bd06be89e46a7a0be81808ae2dadb9dd320c Mon Sep 17 00:00:00 2001 From: Pruznak <pruznak10@gmail.com> Date: Mon, 16 Dec 2024 16:38:46 +0100 Subject: [PATCH 2/6] Fix BARO and RADIO can be filled out at the same time, finishing touches --- .../src/MFD/FMC/FmcAircraftInterface.ts | 3 +- .../systems/instruments/src/MFD/FMC/fmgc.ts | 2 +- .../src/MFD/pages/FMS/MfdFmsPerf.tsx | 60 +++++++++++-------- .../src/MFD/pages/common/DataEntryFormats.tsx | 2 +- 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/fbw-a380x/src/systems/instruments/src/MFD/FMC/FmcAircraftInterface.ts b/fbw-a380x/src/systems/instruments/src/MFD/FMC/FmcAircraftInterface.ts index e2d1092e4d3..cf1bf0cfd0b 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/FMC/FmcAircraftInterface.ts +++ b/fbw-a380x/src/systems/instruments/src/MFD/FMC/FmcAircraftInterface.ts @@ -473,7 +473,8 @@ export class FmcAircraftInterface { const inRange = this.shouldTransmitMinimums(distanceToDestination); const mda = this.fmgc.data.approachBaroMinimum.get(); - const dh = this.fmgc.data.approachRadioMinimum.get(); + const dh = + typeof this.fmgc.data.approachRadioMinimum.get() === 'string' ? null : this.fmgc.data.approachRadioMinimum.get(); const mdaValid = inRange && mda !== null; const dhValid = !mdaValid && inRange && typeof dh === 'number'; diff --git a/fbw-a380x/src/systems/instruments/src/MFD/FMC/fmgc.ts b/fbw-a380x/src/systems/instruments/src/MFD/FMC/fmgc.ts index 9b7b741d070..8b3d9a5cf23 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/FMC/fmgc.ts +++ b/fbw-a380x/src/systems/instruments/src/MFD/FMC/fmgc.ts @@ -291,7 +291,7 @@ export class FmgcData { public readonly approachBaroMinimum = Subject.create<number | null>(null); /** in feet. null if not set. */ - public readonly approachRadioMinimum = Subject.create<number | null>(null); + public readonly approachRadioMinimum = Subject.create<string | number | null>(null); public readonly approachVref = Subject.create<Knots>(129); diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx index 166de5bafcb..678109fc4ce 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx @@ -13,7 +13,7 @@ import './MfdFmsPerf.scss'; import { AltitudeFormat, AltitudeOrFlightLevelFormat, - RadioAltitudeFormat, + RadioMinimumFormat, CostIndexFormat, DescentRateFormat, FlightLevelFormat, @@ -401,8 +401,6 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> { private apprVerticalDeviation = Subject.create<string>('+-----'); - private apprRadioInputText = Subject.create<string | number | null>(null); - private readonly apprRadioText = this.precisionApproachSelected.map((v) => (v ? 'RADIO' : '-----')); private missedThrRedAlt = Subject.create<number | null>(null); @@ -2510,14 +2508,21 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> { <InputField<number> dataEntryFormat={new AltitudeFormat(Subject.create(1), Subject.create(maxCertifiedAlt))} dataHandlerDuringValidation={async (v) => { - if (v === null) { - SimVar.SetSimVarValue('L:AIRLINER_MINIMUM_DESCENT_ALTITUDE', 'feet', 0); - } else { - SimVar.SetSimVarValue('L:AIRLINER_MINIMUM_DESCENT_ALTITUDE', 'feet', v); + if (!this.props.fmcService.master?.fmgc.data.approachRadioMinimum.get()) { + if (v === null) { + SimVar.SetSimVarValue('L:AIRLINER_MINIMUM_DESCENT_ALTITUDE', 'feet', 0); + } else { + SimVar.SetSimVarValue('L:AIRLINER_MINIMUM_DESCENT_ALTITUDE', 'feet', v); + } } }} mandatory={Subject.create(false)} value={this.props.fmcService.master.fmgc.data.approachBaroMinimum} + onModified={(v) => { + if (!this.props.fmcService.master?.fmgc.data.approachRadioMinimum.get()) { + this.props.fmcService.master?.fmgc.data.approachBaroMinimum.set(v); + } + }} containerStyle="width: 150px;" alignText="flex-end" errorHandler={(e) => this.props.fmcService.master?.showFmsErrorMessage(e)} @@ -2532,29 +2537,36 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> { componentIfTrue={ <InputField<string | number> dataEntryFormat={ - new RadioAltitudeFormat(Subject.create(1), Subject.create(maxCertifiedAlt)) + new RadioMinimumFormat(Subject.create(1), Subject.create(maxCertifiedAlt)) } dataHandlerDuringValidation={async (v) => { - if (v === undefined) { - SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', -1); - } else if (v === 'NONE' || v === 'NO' || v === 'NO DH' || v === 'NODH' || v === null) { - SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', -2); - } else { - SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', v); + if (!this.props.fmcService.master?.fmgc.data.approachBaroMinimum.get()) { + if (v === undefined) { + SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', -1); + } else if ( + v === 'NONE' || + v === 'NO' || + v === 'NO DH' || + v === 'NODH' || + v === null + ) { + SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', -2); + } else { + SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', v); + } } }} mandatory={Subject.create(false)} - value={this.apprRadioInputText} + value={this.props.fmcService.master?.fmgc.data.approachRadioMinimum} onModified={(v) => { - if (v === null) { - this.apprRadioInputText.set(null); - this.props.fmcService.master?.fmgc.data.approachRadioMinimum.set(null); - } else if (v === 'NONE' || v === 'NO' || v === 'NO DH' || v === 'NODH') { - this.apprRadioInputText.set(v); - this.props.fmcService.master?.fmgc.data.approachRadioMinimum.set(null); - } else { - this.apprRadioInputText.set(Number(v)); - this.props.fmcService.master?.fmgc.data.approachRadioMinimum.set(Number(v)); + if (!this.props.fmcService.master?.fmgc.data.approachBaroMinimum.get()) { + if (v === null) { + this.props.fmcService.master?.fmgc.data.approachRadioMinimum.set(null); + } else if (v === 'NONE' || v === 'NO' || v === 'NO DH' || v === 'NODH') { + this.props.fmcService.master?.fmgc.data.approachRadioMinimum.set(v); + } else { + this.props.fmcService.master?.fmgc.data.approachRadioMinimum.set(Number(v)); + } } }} containerStyle="width: 150px;" diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx index e21fb02e301..586cf81499b 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx @@ -200,7 +200,7 @@ export class AltitudeFormat implements DataEntryFormat<number> { } } -export class RadioAltitudeFormat implements DataEntryFormat<string | number> { +export class RadioMinimumFormat implements DataEntryFormat<string | number> { public placeholder = '-----'; public maxDigits = 5; From bfcdaea7e30642ad838f7293146fefc2fa548714 Mon Sep 17 00:00:00 2001 From: Pruznak <pruznak10@gmail.com> Date: Mon, 16 Dec 2024 17:24:17 +0100 Subject: [PATCH 3/6] More readable ifs --- .../src/MFD/pages/FMS/MfdFmsPerf.tsx | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx index 678109fc4ce..e4fb7971046 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx @@ -2517,7 +2517,7 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> { } }} mandatory={Subject.create(false)} - value={this.props.fmcService.master.fmgc.data.approachBaroMinimum} + value={this.props.fmcService.master?.fmgc.data.approachBaroMinimum} onModified={(v) => { if (!this.props.fmcService.master?.fmgc.data.approachRadioMinimum.get()) { this.props.fmcService.master?.fmgc.data.approachBaroMinimum.set(v); @@ -2541,16 +2541,10 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> { } dataHandlerDuringValidation={async (v) => { if (!this.props.fmcService.master?.fmgc.data.approachBaroMinimum.get()) { - if (v === undefined) { - SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', -1); - } else if ( - v === 'NONE' || - v === 'NO' || - v === 'NO DH' || - v === 'NODH' || - v === null - ) { + if (v === 'NONE' || v === 'NO' || v === 'NO DH' || v === 'NODH' || v === null) { SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', -2); + } else if (v === undefined) { + SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', -1); } else { SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', v); } @@ -2560,9 +2554,7 @@ export class MfdFmsPerf extends FmsPage<MfdFmsPerfProps> { value={this.props.fmcService.master?.fmgc.data.approachRadioMinimum} onModified={(v) => { if (!this.props.fmcService.master?.fmgc.data.approachBaroMinimum.get()) { - if (v === null) { - this.props.fmcService.master?.fmgc.data.approachRadioMinimum.set(null); - } else if (v === 'NONE' || v === 'NO' || v === 'NO DH' || v === 'NODH') { + if (v === 'NONE' || v === 'NO' || v === 'NO DH' || v === 'NODH' || v === null) { this.props.fmcService.master?.fmgc.data.approachRadioMinimum.set(v); } else { this.props.fmcService.master?.fmgc.data.approachRadioMinimum.set(Number(v)); From 1e7ef0dd491468431914fd1c42766f0cf84be8c7 Mon Sep 17 00:00:00 2001 From: Pruznak <pruznak10@gmail.com> Date: Mon, 16 Dec 2024 19:14:18 +0100 Subject: [PATCH 4/6] Add custom value type --- .../instruments/src/MFD/pages/common/DataEntryFormats.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx index 586cf81499b..a82cf9aecdd 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx @@ -3,6 +3,7 @@ import { Subject, Subscribable } from '@microsoft/msfs-sdk'; import { Mmo, maxCertifiedAlt } from '@shared/PerformanceConstants'; type FieldFormatTuple = [value: string | null, unitLeading: string | null, unitTrailing: string | null]; +type RadioMinimumEntry = 'NONE' | 'NO' | 'NO DH' | 'NODH' | number; export interface DataEntryFormat<T> { placeholder: string; maxDigits: number; @@ -217,7 +218,7 @@ export class RadioMinimumFormat implements DataEntryFormat<string | number> { maxValue.sub((val) => (this.maxValue = val), true); } - public format(value: string | number) { + public format(value: RadioMinimumEntry) { if (value === null || value === undefined) { return [this.placeholder, null, 'FT'] as FieldFormatTuple; } else if (value === 'NONE' || value === 'NO' || value === 'NO DH' || value === 'NODH') { From 1a32b7046339494b14ea0b05b7dbb804b31e9296 Mon Sep 17 00:00:00 2001 From: Pruznak <pruznak10@gmail.com> Date: Mon, 16 Dec 2024 19:49:50 +0100 Subject: [PATCH 5/6] Move type declaration --- .../instruments/src/MFD/pages/common/DataEntryFormats.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx index a82cf9aecdd..da63b6889b7 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/DataEntryFormats.tsx @@ -3,7 +3,6 @@ import { Subject, Subscribable } from '@microsoft/msfs-sdk'; import { Mmo, maxCertifiedAlt } from '@shared/PerformanceConstants'; type FieldFormatTuple = [value: string | null, unitLeading: string | null, unitTrailing: string | null]; -type RadioMinimumEntry = 'NONE' | 'NO' | 'NO DH' | 'NODH' | number; export interface DataEntryFormat<T> { placeholder: string; maxDigits: number; @@ -201,6 +200,7 @@ export class AltitudeFormat implements DataEntryFormat<number> { } } +type RadioMinimumEntry = 'NONE' | 'NO' | 'NO DH' | 'NODH' | number; export class RadioMinimumFormat implements DataEntryFormat<string | number> { public placeholder = '-----'; From dbed68fea153d912ef8567bc27a6605abe0b81df Mon Sep 17 00:00:00 2001 From: Pruznak <pruznak10@gmail.com> Date: Mon, 16 Dec 2024 19:57:23 +0100 Subject: [PATCH 6/6] Add CHANGELOG.md entry --- .github/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 83239d20889..94c2b8f2f21 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -92,6 +92,7 @@ 1. [A380X/FCU] Add correct QFE label using the baro preselect display - @heclak (Heclak) 1. [FMS] Move the speed limit data to performance data, so that it is flight-plan-specific - @BlueberryKing (BlueberryKing) 1. [A32NX/FWC] Add FCU faults - @tracernz (Mike) +1. [A380X/MFD] Fix PERF APPR minimums input logic - @Pruznak (Pruznak) ## 0.12.0