diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 5089d1fc1a9..8319354b6a8 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -43,6 +43,7 @@ 1. [A380X/WING_FLEX] Reduced stiffness of wings for more tip up bend - @Crocket63 (crocket) 1. [A380X/FWS] Add V1 callout - @flogross89 (floridude) 1. [FMS] Fix existing T-P moving when inserting temporary flight plan - @Benjozork (Benjamin Dupont) +1. [OANS] Display correct flags when IRS position is not available - @Nufflee (nufflee) 1. [FMS] Use station declination for PBX/PBD waypoints - @BlueberryKing (BlueberryKing) 1. [RA] Add direct coupling and interrupted antenna cable failures to RAs - @beheh (Benedict Etzel) 1. [A32NX/TELEX] Keep flight number set even if already taken - @BenJuan26 (BenJuan26) diff --git a/fbw-a32nx/src/systems/instruments/src/OANC/instrument.tsx b/fbw-a32nx/src/systems/instruments/src/OANC/instrument.tsx index 6ee14483fd4..01334cc6bc8 100644 --- a/fbw-a32nx/src/systems/instruments/src/OANC/instrument.tsx +++ b/fbw-a32nx/src/systems/instruments/src/OANC/instrument.tsx @@ -87,8 +87,6 @@ class A32NX_OANC extends BaseInstrument { private readonly controlPanelVisible = Subject.create(false); - private readonly oansMessageScreenRef = FSComponent.createRef(); - constructor() { super(); this.efisSide = getDisplayIndex() === 1 ? 'L' : 'R'; @@ -122,9 +120,6 @@ class A32NX_OANC extends BaseInstrument { class="oanc-container" style={`width: ${OANC_RENDER_WIDTH}px; height: ${OANC_RENDER_HEIGHT}px; overflow: hidden`} > -
- PLEASE WAIT -
{ private manualAirportSelection = false; - private readonly pposLatWord = Arinc429RegisterSubject.createEmpty(); + // TODO: Should be using GPS position interpolated with IRS velocity data + private readonly pposLatWord = Arinc429LocalVarConsumerSubject.create(this.sub.on('latitude')); - private readonly pposLonWord = Arinc429RegisterSubject.createEmpty(); + private readonly pposLongWord = Arinc429LocalVarConsumerSubject.create(this.sub.on('longitude')); private presentPos = MappedSubject.create( ([lat, lon]) => { return { lat: lat.value, long: lon.value } as Coordinates; }, this.pposLatWord, - this.pposLonWord, + this.pposLongWord, + ); + + private presentPosNotAvailable = MappedSubject.create( + ([lat, long]) => !lat.isNormalOperation() || !long.isNormalOperation(), + this.pposLatWord, + this.pposLongWord, ); private readonly fmsDataStore = new FmsDataStore(this.props.bus); @@ -208,13 +214,13 @@ export class OansControlPanel extends DisplayComponent { NavigationDatabaseService.activeDatabase.getDatabaseIdent().then((db) => { const from = new Date(db.effectiveFrom); const to = new Date(db.effectiveTo); - this.activeDatabase.set(`${from.getDay()}${months[from.getMonth()]}-${to.getDay()}${months[to.getMonth()]}`); + this.activeDatabase.set(`${from.getDate()}${months[from.getMonth()]}-${to.getDate()}${months[to.getMonth()]}`); }); NXDataStore.getAndSubscribe('NAVIGRAPH_ACCESS_TOKEN', () => this.loadOansDb()); this.subs.push( - this.props.isVisible.sub((it) => this.style.setValue('visibility', it ? 'visible' : 'hidden'), true), + this.props.isVisible.sub((it) => this.style.setValue('visibility', it ? 'inherit' : 'hidden'), true), ); this.subs.push( @@ -246,20 +252,6 @@ export class OansControlPanel extends DisplayComponent { }, true), ); - this.sub - .on('latitude') - .whenChanged() - .handle((value) => { - this.pposLatWord.setWord(value); - }); - - this.sub - .on('longitude') - .whenChanged() - .handle((value) => { - this.pposLonWord.setWord(value); - }); - this.fmsDataStore.landingRunway.sub(async (it) => { // Set control panel display if (it) { @@ -301,12 +293,8 @@ export class OansControlPanel extends DisplayComponent { .on('realTime') .atFrequency(5) .handle((_) => { - const ppos: Coordinates = { lat: 0, long: 0 }; - ppos.lat = SimVar.GetSimVarValue('PLANE LATITUDE', 'Degrees'); - ppos.long = SimVar.GetSimVarValue('PLANE LONGITUDE', 'Degrees'); - - if (this.arpCoordinates && ppos.lat && this.navigraphAvailable.get() === false) { - globalToAirportCoordinates(this.arpCoordinates, ppos, this.localPpos); + if (this.arpCoordinates && this.navigraphAvailable.get() === false) { + globalToAirportCoordinates(this.arpCoordinates, this.presentPos.get(), this.localPpos); this.props.bus.getPublisher().pub('oansAirportLocalCoordinates', this.localPpos, true); } }); @@ -411,6 +399,11 @@ export class OansControlPanel extends DisplayComponent { }; private autoLoadAirport() { + // If we don't have ppos, do not try to auto load + if (this.presentPosNotAvailable.get()) { + return; + } + // If airport has been manually selected, do not auto load. if ( this.manualAirportSelection === true || diff --git a/fbw-a380x/src/systems/instruments/src/ND/instrument.tsx b/fbw-a380x/src/systems/instruments/src/ND/instrument.tsx index 75d192bede6..286a01720a1 100644 --- a/fbw-a380x/src/systems/instruments/src/ND/instrument.tsx +++ b/fbw-a380x/src/systems/instruments/src/ND/instrument.tsx @@ -4,6 +4,7 @@ import { Clock, + ConsumerSubject, FsBaseInstrument, FSComponent, FsInstrument, @@ -29,8 +30,6 @@ import { a380EfisZoomRangeSettings, A380EfisZoomRangeValue, Oanc, - OANC_RENDER_HEIGHT, - OANC_RENDER_WIDTH, OansControlEvents, ZOOM_TRANSITION_TIME_MS, } from '@flybywiresim/oanc'; @@ -115,8 +114,6 @@ class NDInstrument implements FsInstrument { private readonly controlPanelVisible = Subject.create(false); - private readonly oansMessageScreenRef = FSComponent.createRef(); - private oansContextMenuItems: Subscribable = Subject.create([ { name: 'ADD CROSS', @@ -184,11 +181,11 @@ class NDInstrument implements FsInstrument { private oansRef = FSComponent.createRef>(); - private oansContainerRef = FSComponent.createRef(); + private cursorVisible = Subject.create(true); - private oansControlPanelContainerRef = FSComponent.createRef(); + private readonly oansNotAvailable = ConsumerSubject.create(null, true); - private cursorVisible = Subject.create(true); + private readonly oansShown = Subject.create(false); constructor() { const side: EfisSide = getDisplayIndex() === 1 ? 'L' : 'R'; @@ -252,23 +249,36 @@ class NDInstrument implements FsInstrument { failed={Subject.create(false)} >
(v ? 'block' : 'none')), + }} > - -
+
(v ? 'block' : 'none')), + }} + > (); + this.oansNotAvailable.setConsumer(sub.on('oansNotAvail')); + sub .on('ndMode') .whenChanged() @@ -346,16 +362,12 @@ class NDInstrument implements FsInstrument { } private updateNdOansVisibility() { - if (this.oansContainerRef.getOrDefault()) { - if (this.efisCpRange === -1 && [EfisNdMode.PLAN, EfisNdMode.ARC, EfisNdMode.ROSE_NAV].includes(this.efisNdMode)) { - this.bus.getPublisher().pub('ndShowOans', true); - this.oansContainerRef.instance.style.display = 'block'; - this.oansControlPanelContainerRef.instance.style.display = 'block'; - } else { - this.bus.getPublisher().pub('ndShowOans', false); - this.oansContainerRef.instance.style.display = 'none'; - this.oansControlPanelContainerRef.instance.style.display = 'none'; - } + if (this.efisCpRange === -1 && [EfisNdMode.PLAN, EfisNdMode.ARC, EfisNdMode.ROSE_NAV].includes(this.efisNdMode)) { + this.bus.getPublisher().pub('ndShowOans', true); + this.oansShown.set(true); + } else { + this.bus.getPublisher().pub('ndShowOans', false); + this.oansShown.set(false); } } diff --git a/fbw-a380x/src/systems/instruments/src/ND/oans-style.scss b/fbw-a380x/src/systems/instruments/src/ND/oans-style.scss index f6910135e09..9330f4215cb 100644 --- a/fbw-a380x/src/systems/instruments/src/ND/oans-style.scss +++ b/fbw-a380x/src/systems/instruments/src/ND/oans-style.scss @@ -21,13 +21,14 @@ .oanc-container { width: 768px; height: 768px; + overflow: hidden; background-color: $display-background; font-family: "Ecam", monospace !important; } -.oanc-message-screen { +.oanc-flag-container { position: absolute; width: 768px; @@ -37,14 +38,10 @@ justify-content: center; align-items: center; - font-size: 24px; - - background-color: $display-background; color: $display-white; - - z-index: 9999; } -.oanc-message-screen.amber { + +.oanc-flag-container.amber { color: $display-amber; } @@ -241,6 +238,12 @@ font-size: 24px; } +.oanc-bottom-flag { + padding: 1px; + padding-left: 2px; + color: $display-amber; +} + .oanc-speed-info { position: absolute; top: 0; diff --git a/fbw-common/src/systems/instruments/src/ND/ND.tsx b/fbw-common/src/systems/instruments/src/ND/ND.tsx index d0ca819acea..8aaf198761c 100644 --- a/fbw-common/src/systems/instruments/src/ND/ND.tsx +++ b/fbw-common/src/systems/instruments/src/ND/ND.tsx @@ -14,6 +14,8 @@ import { VNode, } from '@microsoft/msfs-sdk'; +import { GenericAdirsEvents } from '@flybywiresim/fbw-sdk'; + import { clampAngle } from 'msfs-geo'; import { BtvRunwayInfo } from './shared/BtvRunwayInfo'; import { RwyAheadAdvisory } from './shared/RwyAheadAdvisory'; @@ -23,7 +25,6 @@ import { LnavStatus } from './shared/LnavStatus'; import { CrossTrackError } from './shared/CrossTrackError'; import { RadioNeedle } from './shared/RadioNeedle'; import { GenericFmsEvents } from './types/GenericFmsEvents'; -import { GenericAdirsEvents } from './types/GenericAdirsEvents'; import { NDSimvars } from './NDSimvarPublisher'; import { ArcModePage } from './pages/arc'; import { Layer } from '../MsfsAvionicsCommon/Layer'; diff --git a/fbw-common/src/systems/instruments/src/ND/NDSimvarPublisher.tsx b/fbw-common/src/systems/instruments/src/ND/NDSimvarPublisher.tsx index 8d1ccb4c7f9..c526535203e 100644 --- a/fbw-common/src/systems/instruments/src/ND/NDSimvarPublisher.tsx +++ b/fbw-common/src/systems/instruments/src/ND/NDSimvarPublisher.tsx @@ -1,4 +1,4 @@ -import { GenericAdirsEvents } from './types/GenericAdirsEvents'; +import { GenericAdirsEvents } from '@flybywiresim/fbw-sdk'; import { GenericSwitchingPanelEvents } from './types/GenericSwitchingPanelEvents'; export type NDSimvars = GenericAdirsEvents & diff --git a/fbw-common/src/systems/instruments/src/ND/pages/arc/index.tsx b/fbw-common/src/systems/instruments/src/ND/pages/arc/index.tsx index 43f76607ffc..36d4cf3aae1 100644 --- a/fbw-common/src/systems/instruments/src/ND/pages/arc/index.tsx +++ b/fbw-common/src/systems/instruments/src/ND/pages/arc/index.tsx @@ -12,7 +12,14 @@ import { VNode, } from '@microsoft/msfs-sdk'; -import { ArincEventBus, Arinc429WordData, Arinc429RegisterSubject, EfisNdMode, MathUtils } from '@flybywiresim/fbw-sdk'; +import { + ArincEventBus, + Arinc429WordData, + Arinc429RegisterSubject, + EfisNdMode, + MathUtils, + GenericAdirsEvents, +} from '@flybywiresim/fbw-sdk'; import { LsCourseBug } from './LsCourseBug'; import { ArcModeUnderlay } from './ArcModeUnderlay'; @@ -20,7 +27,6 @@ import { Flag } from '../../shared/Flag'; import { NDPage } from '../NDPage'; import { NDControlEvents } from '../../NDControlEvents'; import { GenericFcuEvents } from '../../types/GenericFcuEvents'; -import { GenericAdirsEvents } from '../../types/GenericAdirsEvents'; export interface ArcModePageProps extends ComponentProps { bus: ArincEventBus; diff --git a/fbw-common/src/systems/instruments/src/ND/pages/plan/index.tsx b/fbw-common/src/systems/instruments/src/ND/pages/plan/index.tsx index dae0cf45a77..27fb80ffd80 100644 --- a/fbw-common/src/systems/instruments/src/ND/pages/plan/index.tsx +++ b/fbw-common/src/systems/instruments/src/ND/pages/plan/index.tsx @@ -12,13 +12,12 @@ import { ConsumerSubject, } from '@microsoft/msfs-sdk'; -import { Arinc429Register, Arinc429WordData, EfisNdMode } from '@flybywiresim/fbw-sdk'; +import { Arinc429Register, Arinc429WordData, EfisNdMode, GenericAdirsEvents } from '@flybywiresim/fbw-sdk'; import { PlanModeUnderlay } from './PlanModeUnderlay'; import { MapParameters } from '../../shared/utils/MapParameters'; import { NDPage } from '../NDPage'; import { NDControlEvents } from '../../NDControlEvents'; -import { GenericAdirsEvents } from '../../types/GenericAdirsEvents'; import { GenericFcuEvents } from '../../types/GenericFcuEvents'; import { GenericFmsEvents } from '../../types/GenericFmsEvents'; diff --git a/fbw-common/src/systems/instruments/src/ND/pages/rose/RoseLSPage.tsx b/fbw-common/src/systems/instruments/src/ND/pages/rose/RoseLSPage.tsx index fb538210b55..555b0025efe 100644 --- a/fbw-common/src/systems/instruments/src/ND/pages/rose/RoseLSPage.tsx +++ b/fbw-common/src/systems/instruments/src/ND/pages/rose/RoseLSPage.tsx @@ -12,14 +12,13 @@ import { Subscribable, VNode, } from '@microsoft/msfs-sdk'; -import { Arinc429WordData, MathUtils } from '@flybywiresim/fbw-sdk'; +import { Arinc429WordData, MathUtils, GenericAdirsEvents } from '@flybywiresim/fbw-sdk'; import { RoseMode, RoseModeProps } from './RoseMode'; import { RoseModeUnderlay } from './RoseModeUnderlay'; import { NDControlEvents } from '../../NDControlEvents'; import { IlsInfoIndicator } from './IlsInfoIndicator'; import { GlideSlope } from './Glideslope'; -import { GenericAdirsEvents } from '../../types/GenericAdirsEvents'; import { GenericDisplayManagementEvents } from '../../types/GenericDisplayManagementEvents'; import { GenericVorEvents } from '../../types/GenericVorEvents'; import { GenericFlightManagementBusEvents } from '../../types/GenericFlightManagementBusEvents'; diff --git a/fbw-common/src/systems/instruments/src/ND/pages/rose/RoseNavPage.tsx b/fbw-common/src/systems/instruments/src/ND/pages/rose/RoseNavPage.tsx index 87f7cd1dc3b..5262443af2e 100644 --- a/fbw-common/src/systems/instruments/src/ND/pages/rose/RoseNavPage.tsx +++ b/fbw-common/src/systems/instruments/src/ND/pages/rose/RoseNavPage.tsx @@ -4,7 +4,7 @@ import { FSComponent, ConsumerSubject, MappedSubject, Subject, VNode } from '@microsoft/msfs-sdk'; -import { Arinc429RegisterSubject, EfisNdMode, MathUtils } from '@flybywiresim/fbw-sdk'; +import { Arinc429RegisterSubject, EfisNdMode, MathUtils, GenericAdirsEvents } from '@flybywiresim/fbw-sdk'; import { LsCourseBug } from '../arc/LsCourseBug'; import { Flag } from '../../shared/Flag'; @@ -12,7 +12,6 @@ import { RoseMode } from './RoseMode'; import { RoseModeUnderlay } from './RoseModeUnderlay'; import { NDControlEvents } from '../../NDControlEvents'; import { GenericFcuEvents } from '../../types/GenericFcuEvents'; -import { GenericAdirsEvents } from '../../types/GenericAdirsEvents'; export class RoseNavPage extends RoseMode { private readonly pposLatWord = Arinc429RegisterSubject.createEmpty(); diff --git a/fbw-common/src/systems/instruments/src/ND/pages/rose/RoseVorPage.tsx b/fbw-common/src/systems/instruments/src/ND/pages/rose/RoseVorPage.tsx index 940517d908d..9371a2b7fc2 100644 --- a/fbw-common/src/systems/instruments/src/ND/pages/rose/RoseVorPage.tsx +++ b/fbw-common/src/systems/instruments/src/ND/pages/rose/RoseVorPage.tsx @@ -12,14 +12,13 @@ import { VNode, } from '@microsoft/msfs-sdk'; -import { Arinc429WordData, Arinc429ConsumerSubject } from '@flybywiresim/fbw-sdk'; +import { Arinc429WordData, Arinc429ConsumerSubject, GenericAdirsEvents } from '@flybywiresim/fbw-sdk'; import { RoseMode, RoseModeProps } from './RoseMode'; import { RoseModeUnderlay } from './RoseModeUnderlay'; import { Flag } from '../../shared/Flag'; import { NDControlEvents } from '../../NDControlEvents'; import { VorInfoIndicator } from './VorInfoIndicator'; -import { GenericAdirsEvents } from '../../types/GenericAdirsEvents'; import { GenericDisplayManagementEvents } from '../../types/GenericDisplayManagementEvents'; import { GenericVorEvents } from '../../types/GenericVorEvents'; diff --git a/fbw-common/src/systems/instruments/src/ND/shared/Airplane.tsx b/fbw-common/src/systems/instruments/src/ND/shared/Airplane.tsx index 356f9da9848..c1de925de99 100644 --- a/fbw-common/src/systems/instruments/src/ND/shared/Airplane.tsx +++ b/fbw-common/src/systems/instruments/src/ND/shared/Airplane.tsx @@ -11,11 +11,10 @@ import { MappedSubject, Subscribable, } from '@microsoft/msfs-sdk'; -import { EfisNdMode, Arinc429ConsumerSubject } from '@flybywiresim/fbw-sdk'; +import { EfisNdMode, Arinc429ConsumerSubject, GenericAdirsEvents } from '@flybywiresim/fbw-sdk'; import { NDControlEvents } from '../NDControlEvents'; import { LubberLine } from '../pages/arc/LubberLine'; -import { GenericAdirsEvents } from '../types/GenericAdirsEvents'; import { GenericDisplayManagementEvents } from '../types/GenericDisplayManagementEvents'; import { Layer } from '../../MsfsAvionicsCommon/Layer'; diff --git a/fbw-common/src/systems/instruments/src/ND/shared/RadioNeedle.tsx b/fbw-common/src/systems/instruments/src/ND/shared/RadioNeedle.tsx index 30d1210eb7c..0093a78c800 100644 --- a/fbw-common/src/systems/instruments/src/ND/shared/RadioNeedle.tsx +++ b/fbw-common/src/systems/instruments/src/ND/shared/RadioNeedle.tsx @@ -12,11 +12,10 @@ import { MappedSubject, ClockEvents, } from '@microsoft/msfs-sdk'; -import { EfisNdMode, MathUtils, NavAidMode, Arinc429WordData } from '@flybywiresim/fbw-sdk'; +import { EfisNdMode, MathUtils, NavAidMode, Arinc429WordData, GenericAdirsEvents } from '@flybywiresim/fbw-sdk'; import { diffAngle } from 'msfs-geo'; import { GenericFcuEvents } from '../types/GenericFcuEvents'; -import { GenericAdirsEvents } from '../types/GenericAdirsEvents'; import { GenericDisplayManagementEvents } from '../types/GenericDisplayManagementEvents'; import { GenericVorEvents } from '../types/GenericVorEvents'; diff --git a/fbw-common/src/systems/instruments/src/ND/shared/WindIndicator.tsx b/fbw-common/src/systems/instruments/src/ND/shared/WindIndicator.tsx index 6c18ba7603f..989b993a318 100644 --- a/fbw-common/src/systems/instruments/src/ND/shared/WindIndicator.tsx +++ b/fbw-common/src/systems/instruments/src/ND/shared/WindIndicator.tsx @@ -3,9 +3,8 @@ // SPDX-License-Identifier: GPL-3.0 import { FSComponent, DisplayComponent, EventBus, Subject, VNode } from '@microsoft/msfs-sdk'; -import { Arinc429RegisterSubject } from '@flybywiresim/fbw-sdk'; +import { Arinc429RegisterSubject, GenericAdirsEvents } from '@flybywiresim/fbw-sdk'; -import { GenericAdirsEvents } from '../types/GenericAdirsEvents'; import { GenericDisplayManagementEvents } from '../types/GenericDisplayManagementEvents'; import { Layer } from '../../MsfsAvionicsCommon/Layer'; diff --git a/fbw-common/src/systems/instruments/src/OANC/Oanc.tsx b/fbw-common/src/systems/instruments/src/OANC/Oanc.tsx index a38305af5a5..0e193a4807a 100644 --- a/fbw-common/src/systems/instruments/src/OANC/Oanc.tsx +++ b/fbw-common/src/systems/instruments/src/OANC/Oanc.tsx @@ -9,7 +9,6 @@ import { EventBus, FSComponent, MappedSubject, - NodeReference, SimVarValueType, Subject, Subscribable, @@ -32,6 +31,8 @@ import { EfisSide, FmsOansData, FcuSimVars, + GenericAdirsEvents, + Arinc429LocalVarConsumerSubject, } from '@flybywiresim/fbw-sdk'; import { BBox, @@ -141,12 +142,13 @@ export interface OancProps extends ComponentProps { contextMenuX?: Subject; contextMenuY?: Subject; contextMenuItems?: ContextMenuItemData[]; - messageScreenRef: NodeReference; zoomValues: T[]; } export class Oanc extends DisplayComponent> { - private readonly sub = this.props.bus.getSubscriber(); + private readonly sub = this.props.bus.getSubscriber< + FcuSimVars & OansControlEvents & FmsOansData & GenericAdirsEvents + >(); private readonly animationContainerRef = [ FSComponent.createRef(), @@ -179,13 +181,11 @@ export class Oanc extends DisplayComponent> { public labelContainerRef = FSComponent.createRef(); - private readonly positionTextRef = FSComponent.createRef(); - public data: AmdbFeatureCollection | undefined; private dataBbox: BBox | undefined; - private arpCoordinates: Coordinates | undefined; + private arpCoordinates: Subject = Subject.create(undefined); private canvasCenterCoordinates: Coordinates | undefined; @@ -270,9 +270,20 @@ export class Oanc extends DisplayComponent> { private canvasCentreY = Subject.create(0); - public readonly ppos: Coordinates = { lat: 0, long: 0 }; + // TODO: Should be using GPS position interpolated with IRS velocity data + private readonly pposLatWord = Arinc429LocalVarConsumerSubject.create(this.sub.on('latitude')); + + private readonly pposLongWord = Arinc429LocalVarConsumerSubject.create(this.sub.on('longitude')); + + public readonly ppos = MappedSubject.create( + ([latWord, longWord]) => ({ lat: latWord.value, long: longWord.value }) as Coordinates, + this.pposLatWord, + this.pposLongWord, + ); + + private readonly trueHeadingWord = Arinc429LocalVarConsumerSubject.create(this.sub.on('trueHeadingRaw')); - public readonly referencePos: Coordinates = { lat: 0, long: 0 }; + public referencePos: Coordinates = { lat: 0, long: 0 }; public readonly aircraftWithinAirport = Subject.create(false); @@ -280,11 +291,19 @@ export class Oanc extends DisplayComponent> { private readonly airportBearing = Subject.create(0); - public readonly projectedPpos: Position = [0, 0]; + public readonly projectedPpos = MappedSubject.create( + ([ppos, arpCoordinates], previous: Position) => { + if (arpCoordinates) { + return globalToAirportCoordinates(arpCoordinates, ppos, [0, 0]); + } - private readonly aircraftOnGround = Subject.create(true); + return previous; + }, + this.ppos, + this.arpCoordinates, + ); - private readonly planeTrueHeading = Subject.create(0); + private readonly aircraftOnGround = Subject.create(true); private readonly mapHeading = Subject.create(0); @@ -331,6 +350,14 @@ export class Oanc extends DisplayComponent> { this.fmsDataStore.alternate, ); + private readonly pposNotAvailable = MappedSubject.create( + ([lat, long, trueHeading]) => + !lat.isNormalOperation() || !long.isNormalOperation() || !trueHeading.isNormalOperation(), + this.pposLatWord, + this.pposLongWord, + this.trueHeadingWord, + ); + // eslint-disable-next-line arrow-body-style public usingPposAsReference = MappedSubject.create( ([overlayNDMode, aircraftOnGround, aircraftWithinAirport]) => { @@ -356,7 +383,27 @@ export class Oanc extends DisplayComponent> { private readonly zoomLevelScales: number[] = this.props.zoomValues.map((it) => 1 / ((it * 2) / DEFAULT_SCALE_NM)); - private readonly oansNotAvailable = ConsumerSubject.create(this.sub.on('oansNotAvail'), true); + private readonly airportLoading = Subject.create(false); + + private readonly arptNavPosLostFlagVisible = MappedSubject.create( + ([pposNotAvailable, efisNDModeSub]) => pposNotAvailable && efisNDModeSub !== EfisNdMode.PLAN, + this.pposNotAvailable, + this.overlayNDModeSub, + ); + + private readonly pleaseWaitFlagVisible = MappedSubject.create( + ([arptNavPosLostFlagVisible, airportLoading]) => !arptNavPosLostFlagVisible && airportLoading, + this.arptNavPosLostFlagVisible, + this.airportLoading, + ); + + private readonly oansNotAvailable = ConsumerSubject.create(null, false); + + private readonly anyFlagVisible = MappedSubject.create( + ([arptNavPosLostFlagVisible, pleaseWaitFlagVisible]) => arptNavPosLostFlagVisible || pleaseWaitFlagVisible, + this.arptNavPosLostFlagVisible, + this.pleaseWaitFlagVisible, + ); public getZoomLevelInverseScale() { const multiplier = this.overlayNDModeSub.get() === EfisNdMode.ROSE_NAV ? 0.5 : 1; @@ -372,7 +419,7 @@ export class Oanc extends DisplayComponent> { this.labelContainerRef.instance.addEventListener('mouseup', this.handleCursorPanStop.bind(this)); this.oansVisible.setConsumer(this.sub.on('ndShowOans')); - + this.oansNotAvailable.setConsumer(this.sub.on('oansNotAvail')); this.efisNDModeSub.setConsumer(this.sub.on('ndMode')); this.efisNDModeSub.sub((mode) => { @@ -391,20 +438,6 @@ export class Oanc extends DisplayComponent> { this.loadAirportMap(airport); }); - this.oansNotAvailable.sub((na) => { - if (this.props.messageScreenRef.getOrDefault()) { - if (na) { - this.props.messageScreenRef.instance.style.visibility = 'visible'; - this.props.messageScreenRef.instance.innerText = 'NOT AVAIL'; - this.props.messageScreenRef.instance.classList.add('amber'); - } else if (this.props.messageScreenRef.instance.innerText === 'NOT AVAIL') { - this.props.messageScreenRef.instance.style.visibility = 'hidden'; - this.props.messageScreenRef.instance.innerText = ''; - this.props.messageScreenRef.instance.classList.remove('amber'); - } - } - }, true); - this.fmsDataStore.origin.sub(() => this.updateLabelClasses()); this.fmsDataStore.departureRunway.sub(() => this.updateLabelClasses()); this.fmsDataStore.destination.sub(() => this.updateLabelClasses()); @@ -475,10 +508,6 @@ export class Oanc extends DisplayComponent> { this.modeAnimationOffsetX, this.modeAnimationOffsetY, ); - - this.positionVisible.sub( - (visible) => (this.positionTextRef.instance.style.visibility = visible ? 'inherit' : 'hidden'), - ); } private handleLabelFilter() { @@ -516,11 +545,7 @@ export class Oanc extends DisplayComponent> { public async loadAirportMap(icao: string) { this.dataLoading = true; - if (this.props.messageScreenRef.getOrDefault()) { - this.props.messageScreenRef.instance.style.visibility = 'visible'; - this.props.messageScreenRef.instance.innerText = 'PLEASE WAIT'; - this.props.messageScreenRef.instance.classList.remove('amber'); - } + this.airportLoading.set(true); this.clearData(); this.clearMap(); @@ -572,7 +597,7 @@ export class Oanc extends DisplayComponent> { ); return; } - this.arpCoordinates = { lat: refPointLat, long: refPointLong }; + this.arpCoordinates.set({ lat: refPointLat, long: refPointLong }); this.data = airportMap; @@ -583,8 +608,11 @@ export class Oanc extends DisplayComponent> { // Figure out the boundaries of the map data const dataBbox = bbox(airportMap); - this.updatePosition(); - this.aircraftWithinAirport.set(booleanPointInPolygon(this.projectedPpos, bboxPolygon(dataBbox))); + if (!this.pposNotAvailable.get()) { + this.aircraftWithinAirport.set(booleanPointInPolygon(this.projectedPpos.get(), bboxPolygon(dataBbox))); + } else { + this.aircraftWithinAirport.set(false); + } const width = (dataBbox[2] - dataBbox[0]) * 1; const height = (dataBbox[3] - dataBbox[1]) * 1; @@ -619,7 +647,7 @@ export class Oanc extends DisplayComponent> { ) + 90, ); - return placeBearingDistance(this.arpCoordinates, reciprocal(angleToCanvasCentre), nmDistanceToCanvasCentre); + return placeBearingDistance(this.arpCoordinates.get(), reciprocal(angleToCanvasCentre), nmDistanceToCanvasCentre); } private createLabelElement(label: Label): HTMLDivElement { @@ -910,77 +938,73 @@ export class Oanc extends DisplayComponent> { private lastTime = 0; - /** - * - * @param coordinates coordinates to be transformed - * @param out Output argument: Write projected coordinates here - */ - private projectCoordinates(coordinates: Coordinates, out: Position): Position { - globalToAirportCoordinates(this.arpCoordinates, coordinates, out); - return out; - } - public Update() { const now = Date.now(); const deltaTime = (now - this.lastTime) / 1_000; this.lastTime = now; - this.updatePosition(); - if (!this.data || this.dataLoading) return; this.aircraftOnGround.set( ![6, 7, 8, 9].includes(SimVar.GetSimVarValue('L:A32NX_FWC_FLIGHT_PHASE', SimVarValueType.Number)), ); - this.aircraftWithinAirport.set(booleanPointInPolygon(this.projectedPpos, bboxPolygon(bbox(this.data)))); + // This will always be false without ppos, otherwise it will be updated below + let airportTooFarAwayAndInArcMode = false; - const distToArpt = this.ppos && this.arpCoordinates ? distanceTo(this.ppos, this.arpCoordinates) : 9999; + if (!this.pposNotAvailable.get()) { + this.aircraftWithinAirport.set(booleanPointInPolygon(this.projectedPpos.get(), bboxPolygon(bbox(this.data)))); - // If in ARC mode and airport more than 30nm away, apply a hack to not create a huge canvas (only shift airport a little bit out of view with a static offset) - const airportTooFarAwayAndInArcMode = this.usingPposAsReference.get() && distToArpt > 30; + const distToArpt = this.arpCoordinates.get() ? distanceTo(this.ppos.get(), this.arpCoordinates.get()) : 9999; - if (this.arpCoordinates) { - this.airportWithinRange.set(distToArpt < this.props.zoomValues[this.zoomLevelIndex.get()] + 3); // Add 3nm for airport dimension, FIXME better estimation - this.airportBearing.set(bearingTo(this.ppos, this.arpCoordinates)); + // If in ARC mode and airport more than 30nm away, apply a hack to not create a huge canvas (only shift airport a little bit out of view with a static offset) + airportTooFarAwayAndInArcMode = this.usingPposAsReference.get() && distToArpt > 30; + + if (this.arpCoordinates.get()) { + this.airportWithinRange.set(distToArpt < this.props.zoomValues[this.zoomLevelIndex.get()] + 3); // Add 3nm for airport dimension, FIXME better estimation + this.airportBearing.set(bearingTo(this.ppos.get(), this.arpCoordinates.get())); + } else { + this.airportWithinRange.set(true); + this.airportBearing.set(0); + } } else { + this.aircraftWithinAirport.set(false); this.airportWithinRange.set(true); - this.airportBearing.set(0); } - if (this.usingPposAsReference.get() || !this.arpCoordinates) { - this.referencePos.lat = this.ppos.lat; - this.referencePos.long = this.ppos.long; + if (this.usingPposAsReference.get() || !this.arpCoordinates.get()) { + this.referencePos = this.ppos.get(); } else { - this.referencePos.lat = this.arpCoordinates.lat; - this.referencePos.long = this.arpCoordinates.long; + this.referencePos = this.arpCoordinates.get(); } - const position = this.positionComputer.computePosition(); + if (!this.pposNotAvailable.get()) { + const position = this.positionComputer.computePosition(); + + if (position) { + this.positionVisible.set(true); + this.positionString.set(position); + } else { + this.positionVisible.set(false); + } - if (position) { - this.positionVisible.set(true); - this.positionString.set(position); + this.props.bus.getPublisher().pub('oansAirportLocalCoordinates', this.projectedPpos.get(), true); + this.btvUtils.updateRwyAheadAdvisory( + this.ppos.get(), + this.arpCoordinates.get(), + this.trueHeadingWord.get().value, + this.layerFeatures[2], + ); } else { this.positionVisible.set(false); } - this.projectCoordinates(this.ppos, this.projectedPpos); - - this.props.bus.getPublisher().pub('oansAirportLocalCoordinates', this.projectedPpos, true); - this.btvUtils.updateRwyAheadAdvisory( - this.ppos, - this.arpCoordinates, - this.planeTrueHeading.get(), - this.layerFeatures[2], - ); - // If OANS is not visible on this side (i.e. range selector is not on ZOOM), don't continue here to save runtime if (!this.oansVisible.get()) { return; } - const mapTargetHeading = this.modeAnimationMapNorthUp.get() ? 0 : this.planeTrueHeading.get(); + const mapTargetHeading = this.modeAnimationMapNorthUp.get() ? 0 : this.trueHeadingWord.get().value; this.mapHeading.set(mapTargetHeading); const interpolatedMapHeading = this.interpolatedMapHeading.get(); @@ -1006,7 +1030,7 @@ export class Oanc extends DisplayComponent> { const mapCurrentHeading = this.interpolatedMapHeading.get(); this.canvasCentreReferencedMapParams.compute(this.canvasCenterCoordinates, 0, 0.539957, 1_000, mapCurrentHeading); - this.arpReferencedMapParams.compute(this.arpCoordinates, 0, 0.539957, 1_000, mapCurrentHeading); + this.arpReferencedMapParams.compute(this.arpCoordinates.get(), 0, 0.539957, 1_000, mapCurrentHeading); let [offsetX, offsetY]: [number, number] = [0, 0]; if (airportTooFarAwayAndInArcMode) { @@ -1047,7 +1071,7 @@ export class Oanc extends DisplayComponent> { // Transform airplane this.aircraftX.set(384); this.aircraftY.set(384); - this.aircraftRotation.set(this.planeTrueHeading.get() - mapCurrentHeading); + this.aircraftRotation.set(this.trueHeadingWord.get().value - mapCurrentHeading); // FIXME Use this to update pan offset when zooming /* if (this.previousZoomLevelIndex.get() !== this.zoomLevelIndex.get()) { @@ -1062,10 +1086,7 @@ export class Oanc extends DisplayComponent> { if (this.lastLayerDrawnIndex > this.layerCanvasRefs.length - 1) { this.doneDrawing = true; - if (this.props.messageScreenRef.getOrDefault()) { - this.props.messageScreenRef.instance.style.visibility = 'hidden'; - this.props.messageScreenRef.instance.innerText = ''; - } + this.airportLoading.set(false); this.labelManager.reflowLabels( this.fmsDataStore.departureRunway.get(), @@ -1096,16 +1117,6 @@ export class Oanc extends DisplayComponent> { } } - private updatePosition(): void { - this.ppos.lat = SimVar.GetSimVarValue('PLANE LATITUDE', 'Degrees'); - this.ppos.long = SimVar.GetSimVarValue('PLANE LONGITUDE', 'Degrees'); - this.planeTrueHeading.set(SimVar.GetSimVarValue('PLANE HEADING DEGREES TRUE', 'Degrees')); - - if (this.arpCoordinates) { - this.projectCoordinates(this.ppos, this.projectedPpos); - } - } - private updateLabelClasses() { this.labelManager.updateLabelClasses( this.fmsDataStore, @@ -1306,163 +1317,192 @@ export class Oanc extends DisplayComponent> { render(): VNode | null { return ( <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(v ? 'inherit' : 'hidden')) }} > -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
+ PLEASE WAIT +
+
(v ? 'inherit' : 'hidden')) }} + > + ARPT NAV POS LOST +
- +
(v ? 'none' : 'block')) }}> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + +
-
-
+
-
-
- this.props.zoomValues[it])} - ndMode={this.overlayNDModeSub} - rotation={this.interpolatedMapHeading} - isMapPanned={this.isMapPanned} - airportWithinRange={this.airportWithinRange} - airportBearing={this.airportBearing} - airportIcao={this.dataAirportIcao} - /> +
+
+ this.props.zoomValues[it])} + ndMode={this.overlayNDModeSub} + rotation={this.interpolatedMapHeading} + isMapPanned={this.isMapPanned} + airportWithinRange={this.airportWithinRange} + airportBearing={this.airportBearing} + airportIcao={this.dataAirportIcao} + /> +
-
-
-
-
- - {this.positionString} +
+
+
+ (it ? 'block' : 'none')), + }} + > + {this.positionString} + + + (it ? 'block' : 'none')), + }} + > + ARPT NAV POS LOST + +
+ + + {this.airportInfoLine1} + + + {this.airportInfoLine2} + + (it ? 'inherit' : 'none')) }} + > + ARPT NOT IN +
+ ACTIVE F/PLN
- - {this.airportInfoLine1} - - - {this.airportInfoLine2} - - (it ? 'inherit' : 'none')) }} - > - ARPT NOT IN -
- ACTIVE F/PLN -
+ this.props.zoomValues[it])} + ndMode={this.overlayNDModeSub} + rotation={this.interpolatedMapHeading} + isMapPanned={this.isMapPanned} + airportWithinRange={this.airportWithinRange} + airportBearing={this.airportBearing} + airportIcao={this.dataAirportIcao} + />
- - this.props.zoomValues[it])} - ndMode={this.overlayNDModeSub} - rotation={this.interpolatedMapHeading} - isMapPanned={this.isMapPanned} - airportWithinRange={this.airportWithinRange} - airportBearing={this.airportBearing} - airportIcao={this.dataAirportIcao} - /> ); } diff --git a/fbw-common/src/systems/instruments/src/OANC/OancAircraftIcon.tsx b/fbw-common/src/systems/instruments/src/OANC/OancAircraftIcon.tsx index 90ee6eb776c..da4b230cf3f 100644 --- a/fbw-common/src/systems/instruments/src/OANC/OancAircraftIcon.tsx +++ b/fbw-common/src/systems/instruments/src/OANC/OancAircraftIcon.tsx @@ -26,7 +26,7 @@ export class OancAircraftIcon extends DisplayComponent { onAfterRender() { this.subscriptions.push( this.props.isVisible.sub((isVisible) => { - this.svgRef.instance.style.visibility = isVisible ? 'visible' : 'hidden'; + this.svgRef.instance.style.visibility = isVisible ? 'inherit' : 'hidden'; }, true), MappedSubject.create(this.props.x, this.props.y, this.props.rotation).sub(([x, y, rotation]) => { this.svgRef.instance.style.transform = `translate(${x - 45}px, ${y - 39.625}px) rotate(${rotation}deg)`; diff --git a/fbw-common/src/systems/instruments/src/OANC/OancLabelManager.ts b/fbw-common/src/systems/instruments/src/OANC/OancLabelManager.ts index 0aa71c8f2d7..f24fccdc05e 100644 --- a/fbw-common/src/systems/instruments/src/OANC/OancLabelManager.ts +++ b/fbw-common/src/systems/instruments/src/OANC/OancLabelManager.ts @@ -40,7 +40,7 @@ export class OancLabelManager { const rotate = -mapCurrentHeading; if (this.oanc.doneDrawing && this.showLabels && LABEL_VISIBILITY_RULES[this.oanc.zoomLevelIndex.get()]) { - this.oanc.labelContainerRef.instance.style.visibility = 'visible'; + this.oanc.labelContainerRef.instance.style.visibility = 'inherit'; for (const label of this.visibleLabels.getArray()) { const element = this.visibleLabelElements.get(label); @@ -65,7 +65,7 @@ export class OancLabelManager { element.style.visibility = 'hidden'; continue; } else if ([LabelStyle.BtvSelectedRunwayArrow, LabelStyle.FmsSelectedRunwayEnd].includes(label.style)) { - element.style.visibility = 'visible'; + element.style.visibility = 'inherit'; } const [labelX, labelY] = label.position; diff --git a/fbw-common/src/systems/instruments/src/OANC/OancPositionComputer.ts b/fbw-common/src/systems/instruments/src/OANC/OancPositionComputer.ts index 03e4bc8ff85..1dd8629865e 100644 --- a/fbw-common/src/systems/instruments/src/OANC/OancPositionComputer.ts +++ b/fbw-common/src/systems/instruments/src/OANC/OancPositionComputer.ts @@ -31,7 +31,7 @@ export class OancPositionComputer { const polygon = feature.geometry as Polygon; - if (booleanPointInPolygon(this.oanc.projectedPpos, polygon)) { + if (booleanPointInPolygon(this.oanc.projectedPpos.get(), polygon)) { switch (feature.properties.feattype) { case FeatureType.ParkingStandArea: return feature.properties.idstd; diff --git a/fbw-common/src/systems/instruments/src/OANC/OansBrakeToVacateSelection.ts b/fbw-common/src/systems/instruments/src/OANC/OansBrakeToVacateSelection.ts index aee1a7cc47c..c5ffdacca57 100644 --- a/fbw-common/src/systems/instruments/src/OANC/OansBrakeToVacateSelection.ts +++ b/fbw-common/src/systems/instruments/src/OANC/OansBrakeToVacateSelection.ts @@ -49,7 +49,7 @@ export class OansBrakeToVacateSelection { private readonly bus: EventBus, private readonly labelManager?: OancLabelManager, private readonly aircraftOnGround?: Subscribable, - private readonly aircraftPpos?: Position, + private readonly aircraftPpos?: Subscribable, private readonly canvasRef?: NodeReference, private readonly canvasCentreX?: Subscribable, private readonly canvasCentreY?: Subscribable, @@ -435,14 +435,14 @@ export class OansBrakeToVacateSelection { const aircraftDistFromThreshold = pointDistance( this.btvThresholdPosition[0], this.btvThresholdPosition[1], - this.aircraftPpos[0], - this.aircraftPpos[1], + this.aircraftPpos.get()[0], + this.aircraftPpos.get()[1], ); const aircraftDistFromRunwayEnd = pointDistance( this.btvOppositeThresholdPosition[0], this.btvOppositeThresholdPosition[1], - this.aircraftPpos[0], - this.aircraftPpos[1], + this.aircraftPpos.get()[0], + this.aircraftPpos.get()[1], ); const isPastThreshold = aircraftDistFromRunwayEnd < this.btvRunwayLda.get(); // As soon as aircraft passes the touchdown zone distance, draw DRY and WET stop bars from there @@ -749,7 +749,7 @@ export class OansBrakeToVacateSelection { const insideRunways = []; runwayFeatures.features.forEach((feat) => { if (feat.properties.idrwy) { - const inside = booleanContains(feat.geometry as Polygon, point(this.aircraftPpos)); + const inside = booleanContains(feat.geometry as Polygon, point(this.aircraftPpos.get())); if (inside) { insideRunways.push(feat.properties.idrwy.replace('.', ' - ')); } diff --git a/fbw-common/src/systems/instruments/src/OANC/styles.scss b/fbw-common/src/systems/instruments/src/OANC/styles.scss index feced820c13..c5a0c3d89b7 100644 --- a/fbw-common/src/systems/instruments/src/OANC/styles.scss +++ b/fbw-common/src/systems/instruments/src/OANC/styles.scss @@ -15,13 +15,14 @@ .oanc-container { width: 768px; height: 768px; + overflow: hidden; background-color: $display-background; font-family: "Ecam", monospace !important; } -.oanc-message-screen { +.oanc-flag-container { position: absolute; width: 768px; @@ -31,12 +32,11 @@ justify-content: center; align-items: center; - font-size: 24px; - - background-color: $display-background; - color: white; + color: $display-white; +} - z-index: 9999; +.oanc-flag-container.amber { + color: $display-amber; } .oanc-label { @@ -232,6 +232,12 @@ font-size: 24px; } +.oanc-bottom-flag { + padding: 1px; + padding-left: 2px; + color: $display-amber; +} + .oanc-speed-info { position: absolute; top: 0; diff --git a/fbw-common/src/systems/shared/src/index.ts b/fbw-common/src/systems/shared/src/index.ts index 36b435b4daa..50e2e8a48d6 100644 --- a/fbw-common/src/systems/shared/src/index.ts +++ b/fbw-common/src/systems/shared/src/index.ts @@ -41,3 +41,4 @@ export * from './popup'; export * from './simbridge'; export * from './simvar'; export * from './units'; +export * from './types'; diff --git a/fbw-common/src/systems/instruments/src/ND/types/GenericAdirsEvents.ts b/fbw-common/src/systems/shared/src/types/GenericAdirsEvents.ts similarity index 85% rename from fbw-common/src/systems/instruments/src/ND/types/GenericAdirsEvents.ts rename to fbw-common/src/systems/shared/src/types/GenericAdirsEvents.ts index 84be6256cc0..28ec77a5490 100644 --- a/fbw-common/src/systems/instruments/src/ND/types/GenericAdirsEvents.ts +++ b/fbw-common/src/systems/shared/src/types/GenericAdirsEvents.ts @@ -1,3 +1,6 @@ +// Copyright (c) 2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + export interface GenericAdirsEvents { pitch: number; roll: number; diff --git a/fbw-common/src/systems/shared/src/types/index.ts b/fbw-common/src/systems/shared/src/types/index.ts new file mode 100644 index 00000000000..bea48047998 --- /dev/null +++ b/fbw-common/src/systems/shared/src/types/index.ts @@ -0,0 +1,6 @@ +// Copyright (c) 2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { GenericAdirsEvents } from './GenericAdirsEvents'; + +export { GenericAdirsEvents };