Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(a380x/mfd): PERF APPR minimums input logic #9666

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
2 changes: 1 addition & 1 deletion fbw-a380x/src/systems/instruments/src/MFD/FMC/fmgc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
49 changes: 37 additions & 12 deletions fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsPerf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import './MfdFmsPerf.scss';
import {
AltitudeFormat,
AltitudeOrFlightLevelFormat,
RadioMinimumFormat,
CostIndexFormat,
DescentRateFormat,
FlightLevelFormat,
Expand Down Expand Up @@ -2505,12 +2506,23 @@ 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 (!this.props.fmcService.master?.fmgc.data.approachRadioMinimum.get()) {
if (v === null) {
SimVar.SetSimVarValue('L:AIRLINER_MINIMUM_DESCENT_ALTITUDE', 'feet', 0);
Benjozork marked this conversation as resolved.
Show resolved Hide resolved
} else {
SimVar.SetSimVarValue('L:AIRLINER_MINIMUM_DESCENT_ALTITUDE', 'feet', v);
}
}
}}
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);
}
}}
containerStyle="width: 150px;"
alignText="flex-end"
errorHandler={(e) => this.props.fmcService.master?.showFmsErrorMessage(e)}
Expand All @@ -2523,19 +2535,32 @@ 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 RadioMinimumFormat(Subject.create(1), Subject.create(maxCertifiedAlt))
}
dataHandlerDuringValidation={async (v) => {
if (v === undefined) {
SimVar.SetSimVarValue('L:AIRLINER_DECISION_HEIGHT', 'feet', -1);
} else if (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 === '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);
}
}
}}
mandatory={Subject.create(false)}
value={this.props.fmcService.master.fmgc.data.approachRadioMinimum}
value={this.props.fmcService.master?.fmgc.data.approachRadioMinimum}
onModified={(v) => {
if (!this.props.fmcService.master?.fmgc.data.approachBaroMinimum.get()) {
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));
}
}
}}
containerStyle="width: 150px;"
alignText="flex-end"
errorHandler={(e) => this.props.fmcService.master?.showFmsErrorMessage(e)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Pruznak marked this conversation as resolved.
Show resolved Hide resolved
export interface DataEntryFormat<T> {
placeholder: string;
maxDigits: number;
Expand Down Expand Up @@ -200,6 +201,52 @@ export class AltitudeFormat implements DataEntryFormat<number> {
}
}

export class RadioMinimumFormat 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: RadioMinimumEntry) {
if (value === null || value === undefined) {
return [this.placeholder, null, 'FT'] as FieldFormatTuple;
} else if (value === 'NONE' || value === 'NO' || value === 'NO DH' || value === 'NODH') {
Pruznak marked this conversation as resolved.
Show resolved Hide resolved
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;
Copy link
Member

@tracernz tracernz Dec 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems weird to not normalise them in the parser. Why not do that and avoid any other code having to deal with multiple different values that have the same meaning?

Copy link
Member

@Benjozork Benjozork Dec 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was gonna comment the same but assumed it needed to keep displaying the same text.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also wanted to do that and return null, but when I did so, I came across a problem - when entering a value into the RADIO field, going to any other MFD page and then back to PERF APPR, the value go deleted, both from the field and the actual minimum variable. That's because InputField.ts sets the actual text value of the field from the specified variable of the component. The value of those strings in minimums altitude is basically null, so when rendering the page again, it would fall to the if which populates placeholders in the field, because the variable would be null. That's why it's done like this, to keep even the string value in the input field. If there's a better way to do this, I'm of course open to suggestions, it's just that I didn't figure out a better way to work with this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems weird to not normalise them in the parser. Why not do that and avoid any other code having to deal with multiple different values that have the same meaning?

So what's the verdict on this one ?

}

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)
*/
Expand Down
Loading