Skip to content

Commit

Permalink
feat(efb): Simrate and Pause at T/D in EFB Quick Settings (#8641)
Browse files Browse the repository at this point in the history
* feat: display simrate in status bar

* feat/refactor: pause at tod and simrate controller in quick settings

* fix: css

* chore: rename elements

* revert: use current bg-fill styling

* refactor: first draft tod changes

* refactor: pause at tod armed indicator simvar

* refactor: use LocalSimVar

* refactor: NXDataStore.getAndSubscribe

* revert: package.json

* fix: use getVar for checking armed status + small refactor

---------

Co-authored-by: Saschl <[email protected]>
Co-authored-by: Saschl <[email protected]>
Co-authored-by: alepouna <[email protected]>
  • Loading branch information
4 people authored Jul 8, 2024
1 parent 74e195a commit 8d341ea
Show file tree
Hide file tree
Showing 9 changed files with 338 additions and 96 deletions.
4 changes: 3 additions & 1 deletion .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,12 @@
1. [GENERAL] Added environ AIRCRAFT_PROJECT_PREFIX to allow for custom aircraft project prefixes - @2hwk (2Cas)
1. [EFB] Read from VFS JSON5 markup for Ground > Payload page - @2hwk (2Cas)
1. [FADEC] Fadec rewrite/cleanup/commenting using cpp framework - @frankkopp (Frank Kopp)
1. [EFB] Added warning for not compatible aircraft type to simbrief import - @2hwk (2Cas)
1. [EFCS] Fix ground spoiler retraction after increasing TLA slightly above 0 - @lukecologne (luke)
1. [FWC] Improved LDG LT memo to take into account light position - @BravoMike99 (bruno_pt99)
1. [PRESS] Add pressurization system failures - @mjuhe (Miquel Juhe)
1. [EFB] Added simrate indicator to status bar - @2hwk (2Cas)
1. [EFB] Added simrate and pause on TOD controls to control center - @2hwk (2Cas)
1. [FMS] Implement missed approach route - @Benjozork (Benjamin Dupont), @tracernz (Mike), @saschl (saschl#9432), @BlueberryKing (BlueberryKing)
1. [AUTOFLIGHT] Implement NAV IN GA - @lukecologne (luke)
1. [FMS] Implement ability to program alternate routing - @Benjozork (Benjamin Dupont), @tracernz (Mike), @saschl (saschl#9432), @BlueberryKing (BlueberryKing)
Expand Down Expand Up @@ -156,7 +159,6 @@
1. [EFB/ATSU] Use MSFS METAR data rather than FSX cloud data from FBW API - @tracernz (Mike)
1. [APU] Added xfeed APU fuel capabilities - @Taz5150 (TazX [Z+1]#0405)
1. [ATSU] Fix ATIS Auto Update LSK6L to ATIS page instead of ATC MENU - @BravoMike99 (bruno_pt99)
1. [EFB] Added warning for not compatible aircraft type to simbrief import - @2hwk (2Cas)

## 0.10.0

Expand Down
121 changes: 79 additions & 42 deletions fbw-a32nx/src/systems/fmgc/src/guidance/vnav/descent/TodGuidance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { AircraftToDescentProfileRelation } from '@fmgc/guidance/vnav/descent/Ai
import { VerticalProfileComputationParametersObserver } from '@fmgc/guidance/vnav/VerticalProfileComputationParameters';
import { LateralMode } from '@shared/autopilot';
import { FmgcFlightPhase } from '@shared/flightphase';
import { NXDataStore, PopUpDialog } from '@flybywiresim/fbw-sdk';
import { LocalSimVar, NXDataStore, PopUpDialog } from '@flybywiresim/fbw-sdk';

const TIMEOUT = 10_000;

Expand All @@ -16,6 +16,12 @@ export class TodGuidance {

private tdPaused: boolean;

private tdArmed: LocalSimVar<boolean>;

private pauseAtTodDistance: number;

private tdPauseEnabled: boolean;

private apEngaged: boolean;

private cooldown: number;
Expand All @@ -29,6 +35,33 @@ export class TodGuidance {
this.apEngaged = false;
this.tdReached = false;
this.tdPaused = false;
this.tdPauseEnabled = false;
this.tdArmed = new LocalSimVar('L:A32NX_PAUSE_AT_TOD_ARMED', 'bool');

NXDataStore.getAndSubscribe(
'PAUSE_AT_TOD_DISTANCE',
(_, value: string) => {
const pF = parseFloat(value);
if (isNaN(pF)) {
this.pauseAtTodDistance = 0;
} else {
this.pauseAtTodDistance = pF;
}
},
'10',
);

NXDataStore.getAndSubscribe(
'PAUSE_AT_TOD',
(_, value: string) => {
if (value === 'ENABLED') {
this.tdPauseEnabled = true;
} else {
this.tdPauseEnabled = false;
}
},
'DISABLED',
);
}

showPausePopup(title: string, message: string) {
Expand All @@ -44,61 +77,65 @@ export class TodGuidance {

update(deltaTime: number) {
this.updateTdReached(deltaTime);
this.updateTdPause(deltaTime);
if (this.tdPauseEnabled) {
this.updateTdPause(deltaTime);
}
}

updateTdPause(deltaTime: number) {
if (this.cooldown <= 0 && NXDataStore.get('PAUSE_AT_TOD', 'DISABLED') === 'ENABLED') {
// Only watching if T/D pause untriggered + between flight phase CLB and CRZ
if (
// Only armed if all conditions met
this.tdArmed.setVar(
this.cooldown <= 0 &&
!this.tdPaused &&
this.observer.get().flightPhase >= FmgcFlightPhase.Climb &&
this.observer.get().flightPhase <= FmgcFlightPhase.Cruise &&
Simplane.getAutoPilotAirspeedManaged()
Simplane.getAutoPilotAirspeedManaged(),
);

if (this.tdArmed.getVar()) {
// Check T/D pause first
if (
(this.aircraftToDescentProfileRelation.distanceToTopOfDescent() ?? Number.POSITIVE_INFINITY) <
this.pauseAtTodDistance
) {
this.tdPaused = true;
this.showPausePopup(
'TOP OF DESCENT',
`Paused before the calculated top of descent. System Time was ${new Date().toLocaleTimeString()}.`,
);
// Check A/P mode reversion
} else if (
// Only guard A/P above transitional altitude
this.atmosphericConditions.currentAltitude
? this.atmosphericConditions.currentAltitude > this.observer.get().originTransitionAltitude
: false
) {
// Check T/D pause first, then AP mode reversion
if (
(this.aircraftToDescentProfileRelation.distanceToTopOfDescent() ?? Number.POSITIVE_INFINITY) <
parseFloat(NXDataStore.get('PAUSE_AT_TOD_DISTANCE', '10'))
) {
this.tdPaused = true;
const apActive =
SimVar.GetSimVarValue('L:A32NX_AUTOPILOT_ACTIVE', 'boolean') &&
SimVar.GetSimVarValue('L:A32NX_FMA_LATERAL_MODE', 'Enum') === LateralMode.NAV;

if (this.apEngaged && !apActive) {
this.showPausePopup(
'TOP OF DESCENT',
`Paused before the calculated top of descent. System Time was ${new Date().toLocaleTimeString()}.`,
'AP PROTECTION',
`Autopilot or lateral guidance disengaged before the calculated top of descent. System Time was ${new Date().toLocaleTimeString()}.`,
);
// Only guard AP above transitional altitude
} else if (
this.atmosphericConditions.currentAltitude
? this.atmosphericConditions.currentAltitude > this.observer.get().originTransitionAltitude
: false
) {
const apActive =
SimVar.GetSimVarValue('L:A32NX_AUTOPILOT_ACTIVE', 'boolean') &&
SimVar.GetSimVarValue('L:A32NX_FMA_LATERAL_MODE', 'Enum') === LateralMode.NAV;

if (this.apEngaged && !apActive) {
this.showPausePopup(
'AP PROTECTION',
`Autopilot or lateral guidance disengaged before the calculated top of descent. System Time was ${new Date().toLocaleTimeString()}.`,
);
}

if (this.apEngaged !== apActive) {
this.apEngaged = apActive;
}
}
}

// Reset flags on turnaround
if (
this.observer.get().flightPhase === FmgcFlightPhase.Done ||
this.observer.get().flightPhase === FmgcFlightPhase.Preflight
) {
this.tdPaused = false;
this.apEngaged = false;
if (this.apEngaged !== apActive) {
this.apEngaged = apActive;
}
}
}

// Reset flags on turnaround
if (
this.observer.get().flightPhase === FmgcFlightPhase.Done ||
this.observer.get().flightPhase === FmgcFlightPhase.Preflight
) {
this.tdPaused = false;
this.apEngaged = false;
}
// Iterate backoff timer
if (this.cooldown > 0) {
this.cooldown = Math.max(0, this.cooldown - deltaTime);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,11 @@
"SimBridgeConnecting": "Connecting...",
"SimBridgeOff": "Off",
"SimBridgeOffline": "Not Available",
"Simrate": "Simrate",
"PauseAtTod": "Pause at Top of Descent",
"PauseAtTodArmed": "Armed",
"PauseAtTodStandby": "Standby",
"PauseAtTodInactive": "Inactive",
"TT": {
"AlignAdirs": "Instantly align ADIRS",
"AutoBrightness": "Turn Auto Brightness On/Off",
Expand All @@ -555,6 +560,8 @@
"PowerButton": "Power Off",
"Settings": "Go to flyPad Settings page",
"SimBridge": "Control SimBridge Connection",
"PauseAtTod": "Arm or Disarm pause at Top of Descent",
"Simrate": "Increase or Decrease Simulation Rate",
"Sleep": "Standby"
},
"Title": "Quick Controls"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
// SPDX-License-Identifier: GPL-3.0

import React from 'react';
import { usePersistentNumberProperty, usePersistentProperty, useSimVar } from '@flybywiresim/fbw-sdk';
import {
usePersistentBooleanProperty,
usePersistentNumberProperty,
usePersistentProperty,
useSimVar,
} from '@flybywiresim/fbw-sdk';

import { t } from '../../Localization/translation';
import { Toggle } from '../../UtilComponents/Form/Toggle';
Expand All @@ -20,9 +25,9 @@ export const RealismPage = () => {
const [, setAdirsAlignTimeSimVar] = useSimVar('L:A32NX_CONFIG_ADIRS_IR_ALIGN_TIME', 'Enum', Number.MAX_SAFE_INTEGER);
const [dmcSelfTestTime, setDmcSelfTestTime] = usePersistentProperty('CONFIG_SELF_TEST_TIME', '12');
const [boardingRate, setBoardingRate] = usePersistentProperty('CONFIG_BOARDING_RATE', 'REAL');
const [mcduInput, setMcduInput] = usePersistentProperty('MCDU_KB_INPUT', 'DISABLED');
const [mcduInput, setMcduInput] = usePersistentBooleanProperty('MCDU_KB_INPUT', false);
const [mcduTimeout, setMcduTimeout] = usePersistentProperty('CONFIG_MCDU_KB_TIMEOUT', '60');
const [pauseAtTod, setPauseAtTod] = usePersistentProperty('PAUSE_AT_TOD', 'DISABLED');
const [pauseAtTod, setPauseAtTod] = usePersistentBooleanProperty('PAUSE_AT_TOD', false);
const [todOffset, setTodOffset] = usePersistentNumberProperty('PAUSE_AT_TOD_DISTANCE', 10);
const [realisticTiller, setRealisticTiller] = usePersistentNumberProperty('REALISTIC_TILLER_ENABLED', 0);
const [autoFillChecklists, setAutoFillChecklists] = usePersistentNumberProperty('EFB_AUTOFILL_CHECKLISTS', 0);
Expand Down Expand Up @@ -108,17 +113,17 @@ export const RealismPage = () => {

<SettingGroup>
<SettingItem name={t('Settings.Realism.McduKeyboardInput')} unrealistic groupType="parent">
<Toggle value={mcduInput === 'ENABLED'} onToggle={(value) => setMcduInput(value ? 'ENABLED' : 'DISABLED')} />
<Toggle value={mcduInput} onToggle={(value) => setMcduInput(value)} />
</SettingItem>

{mcduInput === 'ENABLED' && (
{mcduInput && (
<SettingItem name={t('Settings.Realism.McduFocusTimeout')} groupType="sub">
<SimpleInput
className="w-30 text-center"
value={mcduTimeout}
min={5}
max={120}
disabled={mcduInput !== 'ENABLED'}
disabled={!mcduInput}
onChange={(event) => {
if (!Number.isNaN(event) && parseInt(event) >= 5 && parseInt(event) <= 120) {
setMcduTimeout(event.trim());
Expand All @@ -143,19 +148,16 @@ export const RealismPage = () => {

<SettingGroup>
<SettingItem name={t('Settings.Realism.PauseAtTod')} unrealistic groupType="parent">
<Toggle
value={pauseAtTod === 'ENABLED'}
onToggle={(value) => setPauseAtTod(value ? 'ENABLED' : 'DISABLED')}
/>
<Toggle value={pauseAtTod} onToggle={(value) => setPauseAtTod(value)} />
</SettingItem>
{pauseAtTod === 'ENABLED' && (
{pauseAtTod && (
<SettingItem name={t('Settings.Realism.PauseAtTodDistance')} groupType="sub">
<SimpleInput
className="w-30 text-center"
value={todOffset}
min={0}
max={50.0}
disabled={pauseAtTod !== 'ENABLED'}
disabled={!pauseAtTod}
onChange={(event) => {
if (!Number.isNaN(event) && parseInt(event) >= 0 && parseInt(event) <= 50.0) {
setTodOffset(parseFloat(event.trim()));
Expand Down
Loading

0 comments on commit 8d341ea

Please sign in to comment.