From 0f13a86049734e9078567f43a13d89ad227c94d0 Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Sat, 18 Jan 2025 00:20:09 +0200 Subject: [PATCH 01/23] OIT initial commit --- fbw-a380x/mach.config.js | 2 +- .../FlyByWire_A380_842/panel/panel.cfg | 18 ++- .../systems/instruments/src/OIT/.eslintrc.js | 13 ++ .../src/systems/instruments/src/OIT/OIT.tsx | 82 ++++++++++++ .../systems/instruments/src/OIT/OitHeader.tsx | 72 +++++++++++ .../instruments/src/OIT/OitPageDirectory.tsx | 19 +++ .../src/OIT/OitSimvarPublisher.tsx | 34 +++++ .../instruments/src/OIT/OitUiService.tsx | 91 ++++++++++++++ .../instruments/src/OIT/Pages/OitNotFound.tsx | 35 ++++++ .../OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx | 35 ++++++ .../systems/instruments/src/OIT/config.json | 9 +- .../instruments/src/OIT/instrument.tsx | 119 ++++++++++++++++++ .../systems/instruments/src/OIT/style.scss | 62 +++++++++ .../systems/instruments/src/OIT/tsconfig.json | 36 +++++- .../{OIT => OITlegacy}/Components/Button.tsx | 0 .../Components/Dropdown.tsx | 0 .../{OIT => OITlegacy}/Components/Layer.tsx | 0 .../OnboardInformationTerminal.tsx | 0 .../Pages/FlightOps/LoadBox.tsx | 0 .../Pages/FlightOps/Login.tsx | 0 .../Pages/FlightOps/Menu.tsx | 0 .../Pages/FlightOps/STS.tsx | 0 .../src/{OIT => OITlegacy}/Pages/index.tsx | 0 .../instruments/src/OITlegacy/config.json | 8 ++ .../src/{OIT => OITlegacy}/index.scss | 0 .../src/{OIT => OITlegacy}/index.tsx | 0 .../instruments/src/OITlegacy/tsconfig.json | 3 + 27 files changed, 626 insertions(+), 12 deletions(-) create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/.eslintrc.js create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/OitPageDirectory.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/OitUiService.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/Pages/OitNotFound.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/instrument.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/style.scss rename fbw-a380x/src/systems/instruments/src/{OIT => OITlegacy}/Components/Button.tsx (100%) rename fbw-a380x/src/systems/instruments/src/{OIT => OITlegacy}/Components/Dropdown.tsx (100%) rename fbw-a380x/src/systems/instruments/src/{OIT => OITlegacy}/Components/Layer.tsx (100%) rename fbw-a380x/src/systems/instruments/src/{OIT => OITlegacy}/OnboardInformationTerminal.tsx (100%) rename fbw-a380x/src/systems/instruments/src/{OIT => OITlegacy}/Pages/FlightOps/LoadBox.tsx (100%) rename fbw-a380x/src/systems/instruments/src/{OIT => OITlegacy}/Pages/FlightOps/Login.tsx (100%) rename fbw-a380x/src/systems/instruments/src/{OIT => OITlegacy}/Pages/FlightOps/Menu.tsx (100%) rename fbw-a380x/src/systems/instruments/src/{OIT => OITlegacy}/Pages/FlightOps/STS.tsx (100%) rename fbw-a380x/src/systems/instruments/src/{OIT => OITlegacy}/Pages/index.tsx (100%) create mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/config.json rename fbw-a380x/src/systems/instruments/src/{OIT => OITlegacy}/index.scss (100%) rename fbw-a380x/src/systems/instruments/src/{OIT => OITlegacy}/index.tsx (100%) create mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/tsconfig.json diff --git a/fbw-a380x/mach.config.js b/fbw-a380x/mach.config.js index 1a751a10370..6b3d4bfd318 100644 --- a/fbw-a380x/mach.config.js +++ b/fbw-a380x/mach.config.js @@ -36,11 +36,11 @@ module.exports = { msfsAvionicsInstrument('ND'), msfsAvionicsInstrument('PFD'), msfsAvionicsInstrument('RMP'), + msfsAvionicsInstrument('OIT'), reactInstrument('BAT'), reactInstrument('EFB', ['/Pages/VCockpit/Instruments/Shared/Map/MapInstrument.html']), reactInstrument('ISISlegacy'), - reactInstrument('OIT'), reactInstrument('RTPI'), reactInstrument('SD'), ], diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg index 60212725b7b..904ee683606 100644 --- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg +++ b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg @@ -150,6 +150,20 @@ texture=SCREEN_DU_RMP_3 htmlgauge00=A380X/RMP/rmp.html?Index=3, 0,0,1664,1024 [VCockpit19] +size_mm=1333,1000 +pixel_size=1333,1000 +texture=$SCREEN_OIT_LEFT + +htmlgauge00=A380X/OIT/oit.html?Index=1, 0,0,1333,1000 + +[VCockpit20] +size_mm=1333,1000 +pixel_size=1333,1000 +texture=$SCREEN_OIT_RIGHT + +htmlgauge00=A380X/OIT/oit.html?Index=2, 0,0,1333,1000 + +[VCockpit21] size_mm=0,0 pixel_size=0,0 texture=NO_TEXTURE @@ -160,7 +174,7 @@ htmlgauge01=WasmInstrument/WasmInstrument.html?wasm_module=fbw.wasm&wasm_gauge=f htmlgauge02=WasmInstrument/WasmInstrument.html?wasm_module=fadec-a380x.wasm&wasm_gauge=Gauge_Fadec,0,0,1,1 htmlgauge03=WasmInstrument/WasmInstrument.html?wasm_module=extra-backend-a380x.wasm&wasm_gauge=Gauge_Extra_Backend,0,0,1,1 -[VCockpit20] +[VCockpit22] size_mm=0,0 pixel_size=0,0 texture=NO_TEXTURE @@ -168,7 +182,7 @@ background_color=0,0,0 htmlgauge00=A380X/SystemsHost/systems-host.html,0,0,1,1 -[VCockpit21] +[VCockpit23] size_mm=0,0 pixel_size=0,0 texture=NO_TEXTURE diff --git a/fbw-a380x/src/systems/instruments/src/OIT/.eslintrc.js b/fbw-a380x/src/systems/instruments/src/OIT/.eslintrc.js new file mode 100644 index 00000000000..49eb6e1a6ed --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/.eslintrc.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = { + extends: ['../../../../../../.eslintrc.js', 'plugin:jsdoc/recommended-typescript-error'], + + // overrides airbnb, use sparingly + rules: { + 'react/no-unknown-property': 'off', + 'react/style-prop-object': 'off', + 'arrow-body-style': 'off', + camelcase: 'off', + }, +}; diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx new file mode 100644 index 00000000000..4372fb1a675 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx @@ -0,0 +1,82 @@ +// Copyright (c) 2025 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { ClockEvents, ComponentProps, DisplayComponent, EventBus, FSComponent, VNode } from '@microsoft/msfs-sdk'; + +import './style.scss'; +import { OitSimvars } from 'instruments/src/OIT/OitSimvarPublisher'; +import { OitUiService, OitUriInformation } from 'instruments/src/OIT/OitUiService'; +import { OitNotFound } from 'instruments/src/OIT/pages/OitNotFound'; +import { pageForUrl } from 'instruments/src/OIT/OitPageDirectory'; +import { OitHeader } from 'instruments/src/OIT/OitHeader'; + +export interface AbstractOitPageProps extends ComponentProps { + bus: EventBus; + oit: OIT; +} + +export interface OitProps { + readonly bus: EventBus; + readonly instrument: BaseInstrument; + captOrFo: 'CAPT' | 'FO'; +} + +export class OIT extends DisplayComponent { + private readonly sub = this.props.bus.getSubscriber(); + + #uiService = new OitUiService(this.props.captOrFo, this.props.bus); + + get uiService() { + return this.#uiService; + } + + private readonly topRef = FSComponent.createRef(); + + private readonly activePageRef = FSComponent.createRef(); + + private activePage: VNode = (); + + public onAfterRender(node: VNode): void { + super.onAfterRender(node); + + this.uiService.activeUri.sub((uri) => { + this.activeUriChanged(uri); + }); + + this.uiService.navigateTo('flt-ops'); // should be /sts + } + + private activeUriChanged(uri: OitUriInformation) { + // Remove and destroy old OIT page + if (this.activePageRef.getOrDefault()) { + while (this.activePageRef.instance.firstChild) { + this.activePageRef.instance.removeChild(this.activePageRef.instance.firstChild); + } + } + if (this.activePage && this.activePage.instance instanceof DisplayComponent) { + this.activePage.instance.destroy(); + } + + // Mapping from URL to page component + if (uri.page) { + this.activePage = pageForUrl(`${uri.sys}/${uri.page}`, this.props.bus, this); + } else { + this.activePage = pageForUrl(`${uri.sys}`, this.props.bus, this); + } + + FSComponent.render(this.activePage, this.activePageRef?.getOrDefault()); + } + + destroy(): void { + super.destroy(); + } + + render(): VNode | null { + return ( +
+ +
+
+ ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx new file mode 100644 index 00000000000..51a5672f610 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx @@ -0,0 +1,72 @@ +import { DisplayComponent, FSComponent, SubscribableUtils, Subscription, VNode } from '@microsoft/msfs-sdk'; +import { PageSelectorDropdownMenu } from 'instruments/src/MFD/pages/common/PageSelectorDropdownMenu'; +import { OIT } from 'instruments/src/OIT/OIT'; +import { OitUiService } from 'instruments/src/OIT/OitUiService'; + +interface OitHeaderHeaderProps { + uiService: OitUiService; + oit: OIT; +} + +/* + * Complete header for the ATCCOM system + */ +export abstract class OitHeader extends DisplayComponent { + // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. + protected subs = [] as Subscription[]; + + public onAfterRender(node: VNode): void { + super.onAfterRender(node); + } + + public destroy(): void { + // Destroy all subscriptions to remove all references to this instance. + this.subs.forEach((x) => x.destroy()); + + super.destroy(); + } + + render(): VNode { + return ( +
+ this.props.uiService.navigateTo('flt-ops') }, + { label: 'FLT FOLDER', action: () => this.props.uiService.navigateTo('flt-ops/flt-folder') }, + { label: 'TERML CHART', action: () => this.props.uiService.navigateTo('flt-ops/terml-chart') }, + { label: 'OPS LIBRARY', action: () => this.props.uiService.navigateTo('flt-ops/ops-library') }, + { label: 'T.O PERF', action: () => this.props.uiService.navigateTo('flt-ops/to-perf') }, + { label: 'LOADSHEET', action: () => this.props.uiService.navigateTo('flt-ops/loadsheet') }, + { label: 'LDG PERF', action: () => this.props.uiService.navigateTo('flt-ops/ldg-perf') }, + { label: 'IN-FLT PERF', action: () => this.props.uiService.navigateTo('flt-ops/in-flt-perf') }, + { label: 'FLT OPS STS', action: () => this.props.uiService.navigateTo('flt-ops/sts') }, + { label: 'LOAD BOX', action: () => this.props.uiService.navigateTo('flt-ops/load-box') }, + { label: 'EXPORT BOX', action: () => this.props.uiService.navigateTo('flt-ops/export-box') }, + { label: 'EXIT SESSION', action: () => this.props.uiService.navigateTo('flt-ops/exit-session') }, + ]} + idPrefix={`${this.props.uiService.captOrFo}_OIT_menu`} + /> +
+ {this.props.uiService.activeUri.map((uri) => heading[`${uri.sys}/${uri.page}`] ?? '')} +
+ this.props.uiService.navigateTo('flt-ops'), disabled: true }]} + idPrefix={`${this.props.uiService.captOrFo}_OIT_menu`} + /> +
0 MSG
+
+
+ ); + } +} + +const heading: Record = { + 'flt-ops': 'FLT OPS MENU', + 'flt-ops/sts': 'STATUS PAGE', + 'flt-ops/load-box': 'LOAD BOX', + 'flt-ops/export-box': 'EXPORT BOX', +}; diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitPageDirectory.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitPageDirectory.tsx new file mode 100644 index 00000000000..c3e9891e5fb --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitPageDirectory.tsx @@ -0,0 +1,19 @@ +// Copyright (c) 2025 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, FSComponent, VNode } from '@microsoft/msfs-sdk'; +import { OIT } from 'instruments/src/OIT/OIT'; +import { OitFltOpsMenuPage } from 'instruments/src/OIT/pages/flt-ops/OitFltOpsMenuPage'; +import { OitNotFound } from 'instruments/src/OIT/pages/OitNotFound'; + +// Page imports +// eslint-disable-next-line jsdoc/require-jsdoc +export function pageForUrl(url: string, bus: EventBus, oit: OIT): VNode { + switch (url) { + case 'flt-ops': + return ; + + default: + return ; + } +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx new file mode 100644 index 00000000000..839c2fc5213 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx @@ -0,0 +1,34 @@ +// Copyright (c) 2025 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, SimVarDefinition, SimVarPublisher, SimVarValueType } from '@microsoft/msfs-sdk'; + +export type OitSimvars = { + coldDark: number; + elec: boolean; + elecFo: boolean; + potentiometerCaptain: number; + potentiometerFo: number; +}; + +export enum OitVars { + coldDark = 'L:A32NX_COLD_AND_DARK_SPAWN', + elec = 'L:A32NX_ELEC_AC_2_BUS_IS_POWERED', + elecFo = 'L:A32NX_ELEC_AC_ESS_BUS_IS_POWERED', + potentiometerCaptain = 'LIGHT POTENTIOMETER:88', // FIXME poti + potentiometerFo = 'LIGHT POTENTIOMETER:90', // FIXME poti +} + +/** A publisher to poll and publish nav/com simvars. */ +export class OitSimvarPublisher extends SimVarPublisher { + private static simvars = new Map([ + ['elec', { name: OitVars.elec, type: SimVarValueType.Bool }], + ['elecFo', { name: OitVars.elecFo, type: SimVarValueType.Bool }], + ['potentiometerCaptain', { name: OitVars.potentiometerCaptain, type: SimVarValueType.Number }], + ['potentiometerFo', { name: OitVars.potentiometerFo, type: SimVarValueType.Number }], + ]); + + public constructor(bus: EventBus) { + super(OitSimvarPublisher.simvars, bus); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitUiService.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitUiService.tsx new file mode 100644 index 00000000000..eeddeee4bf8 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitUiService.tsx @@ -0,0 +1,91 @@ +// Copyright (c) 2025 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, Subject } from '@microsoft/msfs-sdk'; + +export enum OitSystem { + None = '', + Nss = 'nss', + FltOps = 'flt-ops', +} + +/* + * Hierarchy of URIs: sys / category / page / extra + */ +export interface OitUriInformation { + uri: string; // Full URI to OIT page, e.g. FLT-OPS/TO-PERF + sys: OitSystem; // 1st part of URI (system), can be nss or flt-ops + page: string; // page within system + extra?: string; // Can be used for deep-linking within a page +} + +/* + * Handles navigation (and potentially other aspects) for OIT pages + */ +export class OitUiService { + constructor( + public captOrFo: 'CAPT' | 'FO', + private readonly bus: EventBus, + ) {} + + public readonly activeUri = Subject.create({ + uri: '', + sys: OitSystem.None, + page: '', + extra: '', + }); + + private navigationStack: string[] = []; + + public parseUri(uri: string): OitUriInformation { + const uriParts = uri.split('/'); + return { + uri, + sys: uriParts[0] as OitSystem, + page: uriParts[1], + extra: uriParts.slice(2).join('/'), + }; + } + + /** + * Navigate to OIT page. + * @param uri The URI to navigate to. Format: sys/category/page, e.g. fms/active/init represents ACTIVE/INIT page from the FMS. Use URI 'back' for returning to previous page. + * In theory, one can use anything after a third slash for intra-page deep linking: fms/active/perf/appr could link to the approach PERF page. + */ + public navigateTo(uri: string): void { + let nextUri: string; + + const forceReloadUrls: string[] = []; + if (uri === this.activeUri.get().uri && !forceReloadUrls.includes(uri)) { + // Same URL, don't navigate. Except for some URLs defined in forceReloadUrls + console.info('Navigate to same URL, ignored.'); + return; + } + + // Before navigating, make sure that all input fields are un-focused + Coherent.trigger('UNFOCUS_INPUT_FIELD'); + + if (uri === 'back') { + if (this.navigationStack.length < 2) { + return; + } + console.info('Navigate back'); + this.navigationStack.pop(); + nextUri = this.navigationStack[this.navigationStack.length - 1]; + } else { + console.info(`Navigate to ${uri}`); + this.navigationStack.push(uri); + nextUri = uri; + } + + const parsedUri = this.parseUri(nextUri); + this.activeUri.set(parsedUri); + } + + /* + * Whether one can navigate back + */ + public canGoBack() { + return this.navigationStack.length > 1; + } +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/OitNotFound.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/OitNotFound.tsx new file mode 100644 index 00000000000..41eacea6e8e --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/OitNotFound.tsx @@ -0,0 +1,35 @@ +// Copyright (c) 2025 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { DisplayComponent, FSComponent, Subscription, VNode } from '@microsoft/msfs-sdk'; +import { AbstractOitPageProps } from 'instruments/src/OIT/OIT'; + +interface OitNotFoundProps extends AbstractOitPageProps {} + +export class OitNotFound extends DisplayComponent { + // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. + private subs = [] as Subscription[]; + + public onAfterRender(node: VNode): void { + super.onAfterRender(node); + + new Promise((resolve) => setTimeout(resolve, 500)).then(() => this.props.oit.uiService.navigateTo('back')); + } + + public destroy(): void { + // Destroy all subscriptions to remove all references to this instance. + this.subs.forEach((x) => x.destroy()); + + super.destroy(); + } + + render(): VNode { + return ( + <> + {/* begin page content */} +
+ {/* end page content */} + + ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx new file mode 100644 index 00000000000..2a9f948fefd --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx @@ -0,0 +1,35 @@ +// Copyright (c) 2025 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { DisplayComponent, FSComponent, Subscription, VNode } from '@microsoft/msfs-sdk'; +import { AbstractOitPageProps } from 'instruments/src/OIT/OIT'; + +interface OitFltOpsMenuPageProps extends AbstractOitPageProps {} + +export class OitFltOpsMenuPage extends DisplayComponent { + // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. + private subs = [] as Subscription[]; + + public onAfterRender(node: VNode): void { + super.onAfterRender(node); + + new Promise((resolve) => setTimeout(resolve, 500)).then(() => this.props.oit.uiService.navigateTo('back')); + } + + public destroy(): void { + // Destroy all subscriptions to remove all references to this instance. + this.subs.forEach((x) => x.destroy()); + + super.destroy(); + } + + render(): VNode { + return ( + <> + {/* begin page content */} +
+ {/* end page content */} + + ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/config.json b/fbw-a380x/src/systems/instruments/src/OIT/config.json index dd22b8a2bdb..df67f160af9 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/config.json +++ b/fbw-a380x/src/systems/instruments/src/OIT/config.json @@ -1,8 +1,5 @@ { - "index": "./index.tsx", - "isInteractive": true, - "dimensions": { - "width": 1024, - "height": 768 - } + "index": "./instrument.tsx", + "name": "OIT", + "isInteractive": true } diff --git a/fbw-a380x/src/systems/instruments/src/OIT/instrument.tsx b/fbw-a380x/src/systems/instruments/src/OIT/instrument.tsx new file mode 100644 index 00000000000..aed7c50728c --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/instrument.tsx @@ -0,0 +1,119 @@ +// Copyright (c) 2025 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { + FSComponent, + EventBus, + HEventPublisher, + InstrumentBackplane, + FsInstrument, + FsBaseInstrument, + ClockPublisher, +} from '@microsoft/msfs-sdk'; +import { FailuresConsumer } from '@flybywiresim/fbw-sdk'; +import { OIT } from 'instruments/src/OIT/OIT'; +import { OitSimvarPublisher } from 'instruments/src/OIT/OitSimvarPublisher'; + +class OitInstrument implements FsInstrument { + private readonly bus = new EventBus(); + + private readonly backplane = new InstrumentBackplane(); + + private readonly clockPublisher = new ClockPublisher(this.bus); + + private readonly oitSimvarPublisher = new OitSimvarPublisher(this.bus); + + private readonly hEventPublisher = new HEventPublisher(this.bus); + + private readonly failuresConsumer = new FailuresConsumer('A32NX'); + + constructor(public readonly instrument: BaseInstrument) { + this.hEventPublisher = new HEventPublisher(this.bus); + + this.backplane.addPublisher('hEvent', this.hEventPublisher); + this.backplane.addPublisher('clock', this.clockPublisher); + this.backplane.addPublisher('oitSimvar', this.oitSimvarPublisher); + + this.doInit(); + } + + public doInit(): void { + this.backplane.init(); + + const oit = document.getElementById('OIT_CONTENT'); + + FSComponent.render( + , + document.getElementById('OIT_CONTENT'), + ); + + // Remove "instrument didn't load" text + oit?.querySelector(':scope > h1')?.remove(); + } + + /** + * A callback called when the instrument gets a frame update. + */ + public Update(): void { + this.backplane.onUpdate(); + this.failuresConsumer.update(); + } + + public onInteractionEvent(args: string[]): void { + this.hEventPublisher.dispatchHEvent(args[0]); + } + + public onGameStateChanged(_oldState: GameState, _newState: GameState): void { + // noop + } + + public onFlightStart(): void { + // noop + } + + public onSoundEnd(_soundEventId: Name_Z): void { + // noop + } + + public onPowerOn(): void { + // noop + } + + public onPowerOff(): void { + // noop + } +} + +class A380X_OIT extends FsBaseInstrument { + public constructInstrument(): OitInstrument { + return new OitInstrument(this); + } + + public get isInteractive(): boolean { + return true; + } + + public get templateID(): string { + return 'A380X_OIT'; + } + + /** @inheritdoc */ + public onPowerOn(): void { + super.onPowerOn(); + + this.fsInstrument.onPowerOn(); + } + + /** @inheritdoc */ + public onShutDown(): void { + super.onShutDown(); + + this.fsInstrument.onPowerOff(); + } +} + +registerInstrument('a380x-oit', A380X_OIT); diff --git a/fbw-a380x/src/systems/instruments/src/OIT/style.scss b/fbw-a380x/src/systems/instruments/src/OIT/style.scss new file mode 100644 index 00000000000..244e482087e --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/style.scss @@ -0,0 +1,62 @@ +// Copyright (c) 2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +@import "../MsfsAvionicsCommon/definitions.scss"; + +@font-face { + font-family: "Ecam"; + //noinspection CssUnknownTarget + src: url("/Fonts/fbw-a380x/FBW-Display-EIS-A380.ttf") format("truetype"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "FBW-Display-EIS-A380-SlashedZero"; + //noinspection CssUnknownTarget + src: url("/Fonts/fbw-a380x/FBW-Display-EIS-A380-SlashedZero.ttf") format("truetype"); + font-weight: normal; + font-style: normal; +} + +.oit-main { + position: absolute; + width: 1333px; + height: 1000px; /* 1:1.33 W:H */ + background: $display-background; + font-family: "Ecam", monospace; + display: flex; + flex-direction: column; +} + +.oit-label { + font-size: 20px; + color: $display-white; + font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; +} + +.oit-label.green { + color: $display-green; +} + +.oit-label.amber { + color: $display-amber; +} + +.oit-label.cyan { + color: $display-cyan; +} + +.oit-label.bigger { + font-size: 24px; +} + +.oit-label.biggest { + font-size: 28px; +} + +.oit-header-row { + display: flex; + flex-direction: row; + justify-content: space-between; +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/tsconfig.json b/fbw-a380x/src/systems/instruments/src/OIT/tsconfig.json index befb760d707..b051de3a024 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/tsconfig.json +++ b/fbw-a380x/src/systems/instruments/src/OIT/tsconfig.json @@ -1,3 +1,33 @@ - { - "extends": "../../tsconfig.json" - } \ No newline at end of file +{ + "extends": "../../../tsconfig.json", + + "compilerOptions": { + "incremental": false /* Enables incremental builds */, + "target": "es2017" /* Specifies the ES2017 target, compatible with Coherent GT */, + "module": "es2015" /* Ensures that modules are at least es2015 */, + "strict": true /* Enables strict type checking, highly recommended but optional */, + "esModuleInterop": true /* Emits additional JS to work with CommonJS modules */, + "skipLibCheck": true /* Skip type checking on library .d.ts files */, + "forceConsistentCasingInFileNames": true /* Ensures correct import casing */, + "moduleResolution": "node" /* Enables compatibility with MSFS SDK bare global imports */, + "jsxFactory": "FSComponent.buildComponent" /* Required for FSComponent framework JSX */, + "jsxFragmentFactory": "FSComponent.Fragment" /* Required for FSComponent framework JSX */, + "jsx": "react", /* Required for FSComponent framework JSX */ + "paths": { + "@datalink/aoc": ["../../../fbw-common/src/systems/datalink/aoc/src/index.ts"], + "@datalink/atc": ["../../../fbw-common/src/systems/datalink/atc/src/index.ts"], + "@datalink/common": ["../../../fbw-common/src/systems/datalink/common/src/index.ts"], + "@datalink/router": ["../../../fbw-common/src/systems/datalink/router/src/index.ts"], + "@failures": ["./failures/src/index.ts"], + "@fmgc/*": ["./fmgc/src/*"], + "@instruments/common/*": ["./instruments/src/Common/*"], + "@localization/*": ["../localization/*"], + "@sentry/*": ["./sentry-client/src/*"], + "@simbridge/*": ["./simbridge-client/src/*"], + "@shared/*": ["./shared/src/*"], + "@tcas/*": ["./tcas/src/*"], + "@typings/*": ["../../../fbw-common/src/typings/*"], + "@flybywiresim/fbw-sdk": ["../../../fbw-common/src/systems/index-no-react.ts"], + } + } +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Components/Button.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Button.tsx similarity index 100% rename from fbw-a380x/src/systems/instruments/src/OIT/Components/Button.tsx rename to fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Button.tsx diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Components/Dropdown.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Dropdown.tsx similarity index 100% rename from fbw-a380x/src/systems/instruments/src/OIT/Components/Dropdown.tsx rename to fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Dropdown.tsx diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Components/Layer.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Layer.tsx similarity index 100% rename from fbw-a380x/src/systems/instruments/src/OIT/Components/Layer.tsx rename to fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Layer.tsx diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OnboardInformationTerminal.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/OnboardInformationTerminal.tsx similarity index 100% rename from fbw-a380x/src/systems/instruments/src/OIT/OnboardInformationTerminal.tsx rename to fbw-a380x/src/systems/instruments/src/OITlegacy/OnboardInformationTerminal.tsx diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/FlightOps/LoadBox.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/LoadBox.tsx similarity index 100% rename from fbw-a380x/src/systems/instruments/src/OIT/Pages/FlightOps/LoadBox.tsx rename to fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/LoadBox.tsx diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/FlightOps/Login.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/Login.tsx similarity index 100% rename from fbw-a380x/src/systems/instruments/src/OIT/Pages/FlightOps/Login.tsx rename to fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/Login.tsx diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/FlightOps/Menu.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/Menu.tsx similarity index 100% rename from fbw-a380x/src/systems/instruments/src/OIT/Pages/FlightOps/Menu.tsx rename to fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/Menu.tsx diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/FlightOps/STS.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/STS.tsx similarity index 100% rename from fbw-a380x/src/systems/instruments/src/OIT/Pages/FlightOps/STS.tsx rename to fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/STS.tsx diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/index.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/index.tsx similarity index 100% rename from fbw-a380x/src/systems/instruments/src/OIT/Pages/index.tsx rename to fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/index.tsx diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/config.json b/fbw-a380x/src/systems/instruments/src/OITlegacy/config.json new file mode 100644 index 00000000000..dd22b8a2bdb --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OITlegacy/config.json @@ -0,0 +1,8 @@ +{ + "index": "./index.tsx", + "isInteractive": true, + "dimensions": { + "width": 1024, + "height": 768 + } +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/index.scss b/fbw-a380x/src/systems/instruments/src/OITlegacy/index.scss similarity index 100% rename from fbw-a380x/src/systems/instruments/src/OIT/index.scss rename to fbw-a380x/src/systems/instruments/src/OITlegacy/index.scss diff --git a/fbw-a380x/src/systems/instruments/src/OIT/index.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/index.tsx similarity index 100% rename from fbw-a380x/src/systems/instruments/src/OIT/index.tsx rename to fbw-a380x/src/systems/instruments/src/OITlegacy/index.tsx diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/tsconfig.json b/fbw-a380x/src/systems/instruments/src/OITlegacy/tsconfig.json new file mode 100644 index 00000000000..befb760d707 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OITlegacy/tsconfig.json @@ -0,0 +1,3 @@ + { + "extends": "../../tsconfig.json" + } \ No newline at end of file From 39e85c537a5b0cee06fb2eba65cc9939cdce3f4b Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Sat, 18 Jan 2025 02:44:35 +0200 Subject: [PATCH 02/23] first stub --- .../FlyByWire_A380_842/model/A380_COCKPIT.xml | 41 ++++++++-- .../src/MFD/pages/common/IconButton.tsx | 32 ++++++++ .../src/systems/instruments/src/OIT/OIT.tsx | 2 + .../systems/instruments/src/OIT/OitFooter.tsx | 46 +++++++++++ .../systems/instruments/src/OIT/OitHeader.tsx | 26 +++++-- .../OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx | 57 +++++++++++++- .../systems/instruments/src/OIT/style.scss | 78 +++++++++++++++++-- 7 files changed, 261 insertions(+), 21 deletions(-) create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/A380_COCKPIT.xml b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/A380_COCKPIT.xml index 1e1b9771bb1..1dabff7dddf 100644 --- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/A380_COCKPIT.xml +++ b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/A380_COCKPIT.xml @@ -357,14 +357,26 @@ - - + - - EFB - 0.75 + + SCREEN_OIT_LEFT + 81 + (L:A32NX_ELEC_AC_2_BUS_IS_POWERED, Bool) + + + + + + + + + + SCREEN_OIT_RIGHT + 82 + (L:A32NX_ELEC_AC_ESS_BUS_IS_POWERED, Bool) @@ -388,7 +400,6 @@ - @@ -1245,11 +1256,18 @@ KNOB_EFIS_CS_MFD - ND_Brightness TT:COCKPIT.TOOLTIPS.LIGHTING_KNOB_L_MFD_DECREASE TT:COCKPIT.TOOLTIPS.LIGHTING_KNOB_L_MFD_INCREASE 98 + + + + KNOB_EFIS_CS_OIT + TT:COCKPIT.TOOLTIPS.LIGHTING_KNOB_L_OIT_DECREASE + TT:COCKPIT.TOOLTIPS.LIGHTING_KNOB_L_OIT_INCREASE + 81 + @@ -1392,6 +1410,15 @@ TT:COCKPIT.TOOLTIPS.LIGHTING_KNOB_R_MFD_INCREASE 99 + + + + KNOB_EFIS_FO_OIT + OIT_Brightness_FO + TT:COCKPIT.TOOLTIPS.LIGHTING_KNOB_L_OIT_DECREASE + TT:COCKPIT.TOOLTIPS.LIGHTING_KNOB_L_OIT_INCREASE + 82 + diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/IconButton.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/IconButton.tsx index d9f47b4262a..31fe13cf5b4 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/IconButton.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/IconButton.tsx @@ -16,6 +16,10 @@ interface IconButtonProps extends ComponentProps { | 'double-down' | 'double-left' | 'double-right' + | 'single-up' + | 'single-down' + | 'single-left' + | 'single-right' | 'ecl-single-up' | 'ecl-single-down' | 'ecl-check' @@ -116,6 +120,34 @@ export class IconButton extends DisplayComponent { )} + {this.props.icon === 'single-up' && ( + + + + + + )} + {this.props.icon === 'single-down' && ( + + + + + + )} + {this.props.icon === 'single-left' && ( + + + + + + )} + {this.props.icon === 'single-right' && ( + + + + + + )} {this.props.icon === 'ecl-single-up' && ( diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx index 4372fb1a675..94b7dabbbfe 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx @@ -9,6 +9,7 @@ import { OitUiService, OitUriInformation } from 'instruments/src/OIT/OitUiServic import { OitNotFound } from 'instruments/src/OIT/pages/OitNotFound'; import { pageForUrl } from 'instruments/src/OIT/OitPageDirectory'; import { OitHeader } from 'instruments/src/OIT/OitHeader'; +import { OitFooter } from 'instruments/src/OIT/OitFooter'; export interface AbstractOitPageProps extends ComponentProps { bus: EventBus; @@ -76,6 +77,7 @@ export class OIT extends DisplayComponent {
+
); } diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx new file mode 100644 index 00000000000..7e930e747ab --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx @@ -0,0 +1,46 @@ +import { DisplayComponent, FSComponent, Subscription, VNode } from '@microsoft/msfs-sdk'; +import { Button } from 'instruments/src/MFD/pages/common/Button'; +import { IconButton } from 'instruments/src/MFD/pages/common/IconButton'; +import { OIT } from 'instruments/src/OIT/OIT'; +import { OitUiService } from 'instruments/src/OIT/OitUiService'; + +interface OiFooterHeaderProps { + uiService: OitUiService; + oit: OIT; +} + +/* + * Complete header for the ATCCOM system + */ +export abstract class OitFooter extends DisplayComponent { + // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. + protected subs = [] as Subscription[]; + + public onAfterRender(node: VNode): void { + super.onAfterRender(node); + } + + public destroy(): void { + // Destroy all subscriptions to remove all references to this instance. + this.subs.forEach((x) => x.destroy()); + + super.destroy(); + } + + render(): VNode { + return ( +
+ + {['FLT OPS STS', 'CHARTS', 'OPS LIBRARY'].map((s) => ( +
); } diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx index 2a9f948fefd..ce5ce5a1bc7 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0 import { DisplayComponent, FSComponent, Subscription, VNode } from '@microsoft/msfs-sdk'; +import { Button } from 'instruments/src/MFD/pages/common/Button'; import { AbstractOitPageProps } from 'instruments/src/OIT/OIT'; interface OitFltOpsMenuPageProps extends AbstractOitPageProps {} @@ -27,7 +28,61 @@ export class OitFltOpsMenuPage extends DisplayComponent return ( <> {/* begin page content */} -
+
+
+
+
MISSION
+ {['CHARTS', 'FLT FOLDER'].map((s) => ( +
+
+
DOCUMENTATION
+ {['OPS LIBRARY'].map((s) => ( +
+
+
PERFORMANCE
+ {['T.O PERF', 'LOADSHEET', 'LDG PERF', 'IN-FLT PERF'].map((s) => ( +
+
+
UTILITIES
+ {['FLT OPS STS', 'LOAD BOX', 'EXPORT BOX'].map((s) => ( +
+
+
+
{/* end page content */} ); diff --git a/fbw-a380x/src/systems/instruments/src/OIT/style.scss b/fbw-a380x/src/systems/instruments/src/OIT/style.scss index 244e482087e..31071c25ed8 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/style.scss +++ b/fbw-a380x/src/systems/instruments/src/OIT/style.scss @@ -19,20 +19,64 @@ font-style: normal; } +@font-face { + font-family: "OIT"; + //noinspection CssUnknownTarget + src: url("/Fonts/fbw-a380x/EFB/Inter-Regular.ttf") format("truetype"); + font-weight: normal; + font-style: normal; +} + .oit-main { position: absolute; width: 1333px; height: 1000px; /* 1:1.33 W:H */ background: $display-background; - font-family: "Ecam", monospace; + font-family: "OIT"; display: flex; flex-direction: column; + padding: 10px; +} + +.oit-page-container { + display: flex; + flex: 1 1 auto; + flex-direction: column; +} + + +.oit-heading { + font-size: 28px; + color: $display-cyan; + width: 245px; + padding: 13px; + align-items: center; + text-align: center; +} + +.oit-msg-header { + font-size: 28px; + color: $display-white; + width: 125px; + padding: 13px; + align-items: center; + text-align: center; +} + +.oit-msg-box { + font-size: 28px; + color: $display-white; + width: 430px; + padding: 13px; + align-items: center; + text-align: left; + border: 4px outset $display-light-grey; + background-color: $display-mfd-darker-grey; } .oit-label { - font-size: 20px; + font-size: 24px; color: $display-white; - font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; } .oit-label.green { @@ -48,11 +92,11 @@ } .oit-label.bigger { - font-size: 24px; + font-size: 28px; } .oit-label.biggest { - font-size: 28px; + font-size: 32px; } .oit-header-row { @@ -60,3 +104,27 @@ flex-direction: row; justify-content: space-between; } + +.fr { + display: flex; + flex-direction: row; +} + +.fc { + display: flex; + flex-direction: column; +} + +.oit-flt-ops-menu-column { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; +} + +.oit-flt-ops-menu-column-title { + font-size: 32px; + color: $display-white; + margin-top: 60px; + margin-bottom: 30px; +} From c3ff935b37b3bcaeee8762712029a84144309a11 Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Mon, 20 Jan 2025 16:59:10 +0200 Subject: [PATCH 03/23] update paths --- .../systems/instruments/src/OIT/OitFooter.tsx | 4 +- .../systems/instruments/src/OIT/OitHeader.tsx | 6 +- .../src/OIT/OitSimvarPublisher.tsx | 4 +- .../OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx | 2 +- .../systems/instruments/src/OIT/style.scss | 1 + .../instruments/src/OIT/widget-style.scss | 733 ++++++++++++++++++ 6 files changed, 742 insertions(+), 8 deletions(-) create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx index 7e930e747ab..a7b45bf39ea 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx @@ -1,6 +1,6 @@ import { DisplayComponent, FSComponent, Subscription, VNode } from '@microsoft/msfs-sdk'; -import { Button } from 'instruments/src/MFD/pages/common/Button'; -import { IconButton } from 'instruments/src/MFD/pages/common/IconButton'; +import { Button } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/Button'; +import { IconButton } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/IconButton'; import { OIT } from 'instruments/src/OIT/OIT'; import { OitUiService } from 'instruments/src/OIT/OitUiService'; diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx index ff6b249e20e..88c18f44e28 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx @@ -1,7 +1,7 @@ import { DisplayComponent, FSComponent, SubscribableUtils, Subscription, VNode } from '@microsoft/msfs-sdk'; -import { PageSelectorDropdownMenu } from 'instruments/src/MFD/pages/common/PageSelectorDropdownMenu'; -import { Button } from 'instruments/src/MFD/pages/common/Button'; -import { IconButton } from 'instruments/src/MFD/pages/common/IconButton'; +import { PageSelectorDropdownMenu } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/PageSelectorDropdownMenu'; +import { Button } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/Button'; +import { IconButton } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/IconButton'; import { OIT } from 'instruments/src/OIT/OIT'; import { OitUiService } from 'instruments/src/OIT/OitUiService'; diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx index 839c2fc5213..9f095f73eb4 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx @@ -15,8 +15,8 @@ export enum OitVars { coldDark = 'L:A32NX_COLD_AND_DARK_SPAWN', elec = 'L:A32NX_ELEC_AC_2_BUS_IS_POWERED', elecFo = 'L:A32NX_ELEC_AC_ESS_BUS_IS_POWERED', - potentiometerCaptain = 'LIGHT POTENTIOMETER:88', // FIXME poti - potentiometerFo = 'LIGHT POTENTIOMETER:90', // FIXME poti + potentiometerCaptain = 'LIGHT POTENTIOMETER:81', // FIXME poti + potentiometerFo = 'LIGHT POTENTIOMETER:82', // FIXME poti } /** A publisher to poll and publish nav/com simvars. */ diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx index ce5ce5a1bc7..704caf895ac 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0 import { DisplayComponent, FSComponent, Subscription, VNode } from '@microsoft/msfs-sdk'; -import { Button } from 'instruments/src/MFD/pages/common/Button'; +import { Button } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/Button'; import { AbstractOitPageProps } from 'instruments/src/OIT/OIT'; interface OitFltOpsMenuPageProps extends AbstractOitPageProps {} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/style.scss b/fbw-a380x/src/systems/instruments/src/OIT/style.scss index 31071c25ed8..f5cce134025 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/style.scss +++ b/fbw-a380x/src/systems/instruments/src/OIT/style.scss @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0 @import "../MsfsAvionicsCommon/definitions.scss"; +@import "./widget-style.scss"; @font-face { font-family: "Ecam"; diff --git a/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss b/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss new file mode 100644 index 00000000000..ea72f521f6c --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss @@ -0,0 +1,733 @@ +@import "../../../MsfsAvionicsCommon/definitions.scss"; + +.mfd-label { + font-size: 20px; + color: $display-white; + font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; +} + +.mfd-label.green { + color: $display-green; +} + +.mfd-label.amber { + color: $display-amber; +} + +.mfd-label.magenta { + color: $display-magenta; +} + +.mfd-label.bigger { + font-size: 24px; +} + +.mfd-label.biggest { + font-size: 28px; +} + +.mfd-label-unit { + color: $display-dark-blue; + font-size: 20px; +} + +.mfd-value { + color: $display-green; + font-size: 25px; + font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; +} + +.mfd-value.amber { + color: $display-amber; +} + +.mfd-value.cyan { + color: $display-cyan; +} + +.mfd-value.magenta { + color: $display-magenta; + font-size: 25px; +} + +.mfd-value.tmpy { + color: $display-yellow; + font-size: 25px; +} + +.mfd-value.sec { + color: $display-white; + font-size: 25px; +} + +.mfd-value.smaller { + font-size: 20px; +} + +.mfd-value.bigger { + font-size: 30px; +} + +.mfd-button { + display: flex; + font-size: 22px; + background-color: $display-mfd-darker-grey; + color: $display-white; + font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; + text-align: center; + align-items: center; + justify-content: center; + border: 2px outset $display-light-grey; + padding: 9px 12px 5px 12px; +} + +.mfd-button.opened { + background-color: $display-dark-grey; + border: 2px inset $display-light-grey; +} + +.mfd-button:hover { + border-color: $display-cyan; +} + +.mfd-button.disabled { + color: $display-dark-grey; +} + +.mfd-button.selected { + background-color: $display-dark-grey; + border: 2px inset $display-light-grey; +} + +.mfd-button.disabled:hover { + border-color: $display-light-grey; +} + +.mfd-icon-button { + display: flex; + flex-direction: row; + background-color: $display-mfd-darker-grey; + color: $display-white; + border: 2px outset $display-light-grey; + justify-content: center; + align-items: center; +} + +.mfd-icon-button:hover { + border: 2px outset $display-cyan; +} + +.mfd-dropdown-container { + position: relative; +} + +.mfd-dropdown-outer { + display: flex; + flex-direction: row; + justify-content: space-between; + background-color: $display-mfd-darker-grey; + border: 2px outset $display-light-grey; +} + +.mfd-dropdown-outer.inactive { + background-color: $display-background; + border: 2px outset transparent !important; +} + +.mfd-dropdown-outer:hover { + border-color: $display-cyan; +} + +.mfd-dropdown-inner { + background-color: $display-background; + display: flex; + flex: 1; + justify-content: center; + align-items: center; + height: 40px; +} + +.mfd-dropdown-arrow { + display: flex; + justify-content: center; + align-items: center; + width: 25px; +} + +.mfd-dropdown-arrow.inactive { + visibility: hidden; +} + +.mfd-dropdown-menu::-webkit-scrollbar { + width: 20px; +} + +.mfd-dropdown-menu::-webkit-scrollbar-track { + background: $display-mfd-darker-grey; +} + +.mfd-dropdown-menu::-webkit-scrollbar-thumb { + background: $display-light-grey; +} + +.mfd-dropdown-menu { + background-color: $display-mfd-darker-grey; + min-width: 100%; + position: absolute; + z-index: 60; + overflow: auto; + max-height: 275px; + display: none; + border: 2px outset $display-light-grey; +} + +.mfd-dropdown-menu-element { + color: $display-white; + font-size: 20px; + padding: 12px 16px; + display: block; + border: 3px solid transparent; + white-space: nowrap; + overflow: hidden; + text-align: left; + padding: 5px 16px; +} + +.mfd-dropdown-menu-element:hover { + border: 3px solid $display-cyan; +} + +.mfd-dropdown-menu-element.disabled { + color: $display-dark-grey; +} + +.mfd-page-selector-label { + font-size: 22px; + color: $display-white; + font-family: "Ecam", monospace; + padding: 2px; + text-align: center; + display: inline; + margin: 10px; + padding: 3px; + border: 1px solid transparent; +} + +.mfd-page-selector-label.active { + border: 1px solid $display-white; +} + +.mfd-page-selector-outer { + display: flex; + flex-direction: row; + align-items: center; + background-color: $display-mfd-darker-grey; + border-top: 2px solid $display-light-grey; + border-left: 2px solid $display-light-grey; + border-bottom: 2px solid rgba(211, 211, 211, 0.432); + border-right: 2px solid rgba(211, 211, 211, 0.432); +} + +.mfd-page-selector-outer:hover { + border-color: $display-cyan; +} + +.mfd-page-selector-label-container { + display: flex; + flex: 8; + justify-content: center; +} + +.mfd-page-selector-label-triangle { + display: flex; +} + +.mfd-page-selector-label-triangle > span { + padding: 8px; +} + +.mfd-page-container { + display: flex; + flex: 1 1 auto; + flex-direction: column; +} + +.mfd-spacing-right { + margin-right: 15px; +} + +.mfd-unit-leading { + margin-right: 6px; +} + +.mfd-unit-trailing { + margin-left: 6px; +} + +.mfd-top-tab-navigator-container { + display: flex; + flex-direction: column; + flex: 1 1 auto; + margin: 2px; +} + +.mfd-navigator-container { + display: flex; + flex-direction: column; + flex: 1; +} + +.mfd-navigator-content { + display: flex; + flex-direction: column; + flex: 1; +} + +.mfd-top-tab-navigator-bar { + display: flex; +} + +.mfd-top-tab-navigator-bar-element-outer { + display: flex; + flex: 1; + border-bottom: 2px solid $display-light-grey; +} + +.mfd-top-tab-navigator-bar-element-label { + display: flex; + flex: 1; + justify-content: center; + align-items: center; + background-color: $display-mfd-darker-grey; + border-top: 2px solid $display-light-grey; + padding: 6px 0px 2px 0px; +} + +.mfd-top-tab-navigator-bar-element-label.active { + background-color: $display-background; +} + +.mfd-top-tab-navigator-bar-element-label:hover { + border: 2px outset $display-cyan; +} + +.mfd-top-tab-navigator-bar-element-outer.active { + background-color: $display-background; + border-bottom-style: none; +} + +.mfd-top-tab-navigator-tab-content { + flex: 1; + display: flex; + flex-direction: column; + border: 2px outset $display-light-grey; + border-top-style: none; + padding: 10px; + overflow: hidden; +} + +.mfd-label-value-container { + padding: 7px; + display: flex; + flex-direction: row; + align-items: center; +} + +.mfd-radio-button{ + -webkit-appearance: none; /* WebKit */ + -moz-appearance: none; /* Mozilla */ + -o-appearance: none; /* Opera */ + -ms-appearance: none; /* Internet Explorer */ + appearance: none; /* CSS3 */ + font-size: 26px; + display: flex; + align-items: center; + border: 2px solid transparent; +} + +.mfd-radio-button + .mfd-radio-button { + margin-top: 5px; +} + +.mfd-radio-button input[type="radio"] { + appearance: none; /* CSS3 */ + background-color: $display-background; + width: 30px; + height: 30px; + border: 2px inset $display-light-grey; + border-radius: 50%; + margin-right: 15px; + display: grid; + place-content: center; +} + +.mfd-radio-button input[type="radio"]::after { + display: none; +} + + +.mfd-radio-button input[type="radio"]:checked { + box-shadow: inset 0 0 0px 4px $display-background; +} + +.mfd-radio-button.yellow input[type="radio"]:checked { + background: $display-yellow; +} + +.mfd-radio-button.green input[type="radio"]:checked { + background: $display-green; +} + +.mfd-radio-button.white input[type="radio"]:checked { + background: $display-white; +} + +.mfd-radio-button.cyan input[type="radio"]:checked { + background: $display-cyan; +} + +.mfd-radio-button input[type="radio"]:disabled { + background: $display-dark-grey !important; +} + + +.mfd-radio-button input[type="radio"]:checked { + background: $display-cyan; + box-shadow: inset 0 0 0px 4px $display-background; +} + +.mfd-radio-button input[type="radio"]:checked ~ * { + color: $display-cyan; +} + +.mfd-radio-button.yellow input[type="radio"]:checked ~ * { + color: $display-yellow; +} + +.mfd-radio-button.green input[type="radio"]:checked ~ * { + color: $display-green; +} + +.mfd-radio-button.white input[type="radio"]:checked ~ * { + color: $display-white; +} + +.mfd-radio-button.cyan input[type="radio"]:checked ~ * { + color: $display-cyan; +} + +.mfd-radio-button input[type="radio"]:disabled ~ * { + color: $display-dark-grey !important; +} + +.mfd-radio-button:hover { + border: 2px outset $display-cyan; +} + +.mfd-context-menu { + font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; + background-color: $display-mfd-darker-grey; + border: 2px outset $display-light-grey; + position: absolute; + z-index: 10; + overflow: auto; + display: none; +} + +.mfd-context-menu-element { + color: $display-white; + font-size: 22px; + text-align: left; + padding: 7px 2px; + display: block; + border: 3px solid transparent; +} + +.mfd-context-menu-element:hover { + border: 3px solid $display-cyan; +} + +.mfd-context-menu-element.disabled { + color: $display-dark-grey; +} + +.mfd-context-menu-element.disabled:hover { + border: 3px solid transparent; +} + +.mfd-input-field-root { + display: flex; + flex-direction: row; + justify-items: flex-start; + align-items: baseline; +} + +.mfd-input-field-root.overflow { + position: relative; + top: 0px; +} + +.mfd-input-field-container { + display: flex; + flex-direction: row; + justify-items: center; + align-items: baseline; + padding: 4px 2px; + background-color: $display-background; + border: 2px inset $display-light-grey; + overflow: visible; + height: 37px; +} + +.mfd-input-field-container.inactive { + border: 2px inset transparent !important; + background-color: $display-background !important; +} + +.mfd-input-field-container.disabled { + background-color: $display-mfd-darker-grey; +} + +.mfd-input-field-container:hover { + border: 2px inset $display-cyan; +} + +.mfd-input-field-container.overflow { + position: absolute; + top: -18px; + z-index: 5; + border: 1px solid $display-dark-grey !important; +} + +.mfd-input-field-text-input { + background-color: $display-background; + border: none; + font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; + font-size: 27px; + line-height: 27px; + color: $display-cyan; + padding: 0px; +} + +.mfd-input-field-text-input.inactive { + background-color: $display-background !important; + color: $display-green !important; +} + +.mfd-input-field-text-input.tmpy { + color: $display-yellow; +} + +.mfd-input-field-text-input.validating { + color: $display-light-grey; +} + +.mfd-input-field-text-input.disabled { + background-color: $display-mfd-darker-grey; + color: $display-light-grey; +} + +.mfd-input-field-text-input.mandatory { + color: $display-amber; +} + +.mfd-input-field-text-input.valueSelected { + background-color: $display-cyan; + color: $display-background; +} + +.mfd-input-field-text-input.tmpy.valueSelected { + background-color: $display-yellow; + color: $display-background; +} + +.mfd-input-field-text-input.editing { + font-size: 27px !important; +} + +.mfd-input-field-text-input.computedByFms { + font-size: 22px; +} + +.mfd-input-field-caret { + animation: blinking 1s step-start infinite; + height: 27px; + width: 18px; + background-color: $display-cyan; + color: $display-background; + font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; + font-size: 27px; + text-align: center; + vertical-align: center; +} + +@keyframes blinking { + 50% { + background-color: $display-background; + color: $display-cyan; + } +} + +.mfd-input-field-unit { + align-self: center; +} + +.mfd-input-field-text-input-container { + display: flex; + flex: 1; + flex-direction: row; + align-self: center; + align-items: center; +} + +.mfd-mouse-cursor { + pointer-events: none; + width: 80px; + height: 80px; + position: absolute; + z-index: 999; +} + +.mfd-dialog { + display: flex; + flex-direction: column; + justify-content: space-between; + z-index: 10; + overflow: auto; + position: absolute; + background-color: $display-background; + border: 2px outset $display-light-grey; + padding: 5px; + margin: 5px; +} + +.mfd-dialog-title { + display: flex; + justify-content: center; + align-items: flex-start; + padding-top: 20px; +} + +.mfd-dialog-buttons { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.fr { + display: flex; + flex-direction: row; +} + +.fc { + display: flex; + flex-direction: column; +} + +.aic { + align-items: center; +} + +.jcc { + justify-content: center; +} + +.mfd-surv-button { + border: 4px inset $display-light-grey; + display: flex; + flex-direction: column; + width: 100px; + justify-content: center; + align-items: center; + padding: 5px; + height: 65px; +} + +.mfd-surv-button.disabled { + background-color: $display-dark-grey; + border: none; +} + +.mfd-surv-label { + font-size: 20px; + color: $display-white; + font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; + text-align: center; + margin-bottom: 2px; +} + +.mfd-surv-status-button { + background-color: #3c3c3c; + height: 60px; + width: 90px; + position: absolute; + top: -35px; + left: 0px; + right: 0px; + margin: auto; + border: 2px solid $display-light-grey; +} + +.mfd-surv-status-button:hover { + border-color: $display-cyan; +} + +.mfd-surv-status-button-label { + text-align: center; + font-size: 22px; + padding-top: 3px; + color: $display-white; + font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; +} + +.mfd-surv-status-indicator { + width: 100%; + height: 14.3%; + background-color: $display-light-grey; +} + +.mfd-surv-status-indicator.active { + background-color: $display-green; +} + +.mfd-surv-status-item { + font-size: 24px; +} + +.mfd-surv-status-item.active { + color: $display-green; +} + +.mfd-surv-status-item.failed { + color: $display-amber; +} + +.hidden { + display: none; +} + +.invisible { + visibility: hidden; +} + +.mfd-adsc-button { + border: 4px inset $display-light-grey; + display: flex; + flex-direction: column; + width: 100px; + justify-content: center; + align-items: center; + padding: 5px; + height: 65px; +} + +.mfd-adsc-button.disabled { + background-color: $display-dark-grey; + border: none; +} + +.mfd-adsc-label-off { + font-size: 20px; + color: $display-dark-grey; + font-family: "Ecam", monospace; +} From 6e2bec065b9c9f38043d9d5db42d5ee1afc0c38b Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Mon, 20 Jan 2025 21:22:27 +0200 Subject: [PATCH 04/23] styles, failures --- fbw-a380x/src/systems/failures/src/a380.ts | 16 ++ .../UiWidgets/PageSelectorDropdownMenu.tsx | 14 +- .../src/systems/instruments/src/OIT/OIT.tsx | 36 ++- .../systems/instruments/src/OIT/OisLaptop.ts | 49 ++++ .../instruments/src/OIT/OitDisplayUnit.tsx | 259 ++++++++++++++++++ .../systems/instruments/src/OIT/OitFooter.tsx | 6 +- .../systems/instruments/src/OIT/OitHeader.tsx | 37 ++- .../src/OIT/OitSimvarPublisher.tsx | 19 +- .../OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx | 31 +-- .../instruments/src/OIT/instrument.tsx | 10 + .../instruments/src/OIT/oit-display-unit.scss | 56 ++++ .../instruments/src/OIT/widget-style.scss | 38 ++- .../src/systems/shared/src/electrical.ts | 9 + fbw-a380x/src/systems/systems-host/index.ts | 15 + .../AircraftNetworkServerUnit.ts | 70 +++++ fbw-common/src/systems/shared/src/ata.ts | 1 + 16 files changed, 598 insertions(+), 68 deletions(-) create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/OisLaptop.ts create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/OitDisplayUnit.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/oit-display-unit.scss create mode 100644 fbw-a380x/src/systems/systems-host/systems/InformationSystems/AircraftNetworkServerUnit.ts diff --git a/fbw-a380x/src/systems/failures/src/a380.ts b/fbw-a380x/src/systems/failures/src/a380.ts index e30eab60051..65ad58070c8 100644 --- a/fbw-a380x/src/systems/failures/src/a380.ts +++ b/fbw-a380x/src/systems/failures/src/a380.ts @@ -167,6 +167,14 @@ export const A380Failure = Object.freeze({ Transponder1: 34003, Transponder2: 34004, + + NssAnsu1: 46001, + NssAnsu2: 46002, + FltOpsAnsu1: 46003, + CaptainLaptop: 46004, + FirstOfficerLaptop: 46005, + CaptainOit: 46006, + FirstOfficerOit: 46007, }); export const A380FailureDefinitions: FailureDefinition[] = [ @@ -329,4 +337,12 @@ export const A380FailureDefinitions: FailureDefinition[] = [ [34, A380Failure.RadioAntennaDirectCoupling3, 'RA SYS C Direct Coupling'], [34, A380Failure.Transponder1, 'XPDR 1'], [34, A380Failure.Transponder2, 'XPDR 2'], + + [46, A380Failure.NssAnsu1, 'NSS AVNCS ANSU 1'], + [46, A380Failure.NssAnsu2, 'NSS AVNCS ANSU 2'], + [46, A380Failure.FltOpsAnsu1, 'FLT OPS ANSU 1'], + [46, A380Failure.CaptainLaptop, 'Captain Laptop'], + [46, A380Failure.FirstOfficerLaptop, 'F/O Laptop'], + [46, A380Failure.CaptainOit, 'Captain OIT'], + [46, A380Failure.FirstOfficerOit, 'F/O OIT'], ]; diff --git a/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/PageSelectorDropdownMenu.tsx b/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/PageSelectorDropdownMenu.tsx index 4a832fba9c2..88ecaa13a10 100644 --- a/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/PageSelectorDropdownMenu.tsx +++ b/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/PageSelectorDropdownMenu.tsx @@ -16,6 +16,7 @@ type PageSelectorMenuItem = { label: string; action(): void; disabled?: boolean; + separatorBelow?: boolean; }; interface PageSelectorDropdownMenuProps extends ComponentProps { @@ -25,6 +26,7 @@ interface PageSelectorDropdownMenuProps extends ComponentProps { idPrefix: string; containerStyle?: string; labelStyle?: string; + dropdownMenuStyle?: string; } export class PageSelectorDropdownMenu extends DisplayComponent { // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. @@ -108,10 +110,14 @@ export class PageSelectorDropdownMenu extends DisplayComponent +
- + {this.props.label}
@@ -126,13 +132,13 @@ export class PageSelectorDropdownMenu extends DisplayComponent {this.props.menuItems.map( (el, idx) => ( {el.label} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx index 94b7dabbbfe..1b73bd8320f 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx @@ -1,7 +1,15 @@ // Copyright (c) 2025 FlyByWire Simulations // SPDX-License-Identifier: GPL-3.0 -import { ClockEvents, ComponentProps, DisplayComponent, EventBus, FSComponent, VNode } from '@microsoft/msfs-sdk'; +import { + ClockEvents, + ComponentProps, + DisplayComponent, + EventBus, + FSComponent, + Subject, + VNode, +} from '@microsoft/msfs-sdk'; import './style.scss'; import { OitSimvars } from 'instruments/src/OIT/OitSimvarPublisher'; @@ -10,16 +18,23 @@ import { OitNotFound } from 'instruments/src/OIT/pages/OitNotFound'; import { pageForUrl } from 'instruments/src/OIT/OitPageDirectory'; import { OitHeader } from 'instruments/src/OIT/OitHeader'; import { OitFooter } from 'instruments/src/OIT/OitFooter'; +import { OitDisplayUnit, OitDisplayUnitID } from 'instruments/src/OIT/OitDisplayUnit'; +import { FailuresConsumer } from '@flybywiresim/fbw-sdk'; +import { OisLaptop } from 'instruments/src/OIT/OisLaptop'; export interface AbstractOitPageProps extends ComponentProps { bus: EventBus; oit: OIT; } +export type OisOperationMode = 'nss' | 'flt-ops'; + export interface OitProps { readonly bus: EventBus; readonly instrument: BaseInstrument; - captOrFo: 'CAPT' | 'FO'; + readonly captOrFo: 'CAPT' | 'FO'; + readonly failuresConsumer: FailuresConsumer; + readonly laptop: OisLaptop; } export class OIT extends DisplayComponent { @@ -74,11 +89,18 @@ export class OIT extends DisplayComponent { render(): VNode | null { return ( -
- -
- -
+ ('flt-ops')} + > +
+ +
+ +
+ ); } } diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OisLaptop.ts b/fbw-a380x/src/systems/instruments/src/OIT/OisLaptop.ts new file mode 100644 index 00000000000..88fac4d57b1 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/OisLaptop.ts @@ -0,0 +1,49 @@ +// Copyright (c) 2025 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, Instrument, SimVarValueType, Subject, Subscribable } from '@microsoft/msfs-sdk'; +import { FailuresConsumer } from '@flybywiresim/fbw-sdk'; +import { A380Failure } from '@failures'; + +type LaptopIndex = 1 | 2; + +export class OisLaptop implements Instrument { + private readonly failureKey = this.index === 1 ? A380Failure.CaptainLaptop : A380Failure.FirstOfficerLaptop; + + private readonly powered = Subject.create(false); + + private readonly _isHealthy = Subject.create(false); + public readonly isHealthy = this._isHealthy as Subscribable; + + constructor( + private readonly bus: EventBus, + private readonly index: LaptopIndex, + private readonly failuresConsumer: FailuresConsumer, + ) {} + + /** @inheritdoc */ + init(): void { + this.failuresConsumer.register(this.failureKey); + } + + /** @inheritdoc */ + onUpdate(): void { + const failed = this.failuresConsumer.isActive(this.failureKey); + + if (this.index === 1) { + this.powered.set(SimVar.GetSimVarValue('L:A32NX_ELEC_DC_1_BUS_IS_POWERED', SimVarValueType.Bool)); + } else { + this.powered.set( + SimVar.GetSimVarValue('L:A32NX_ELEC_DC_2_BUS_IS_POWERED', SimVarValueType.Bool) || + SimVar.GetSimVarValue('L:A32NX_ELEC_DC_HOT_2_BUS_IS_POWERED', SimVarValueType.Bool), + ); + } + + this._isHealthy.set(!failed && this.powered.get()); + SimVar.SetSimVarValue( + `L:A32NX_FLTOPS_LAPTOP_${this.index.toFixed(0)}_IS_HEALTHY`, + SimVarValueType.Bool, + this._isHealthy.get(), + ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitDisplayUnit.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitDisplayUnit.tsx new file mode 100644 index 00000000000..c73cf65e2bf --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitDisplayUnit.tsx @@ -0,0 +1,259 @@ +// Copyright (c) 2021-2023 FlyByWire Simulations +// +// SPDX-License-Identifier: GPL-3.0 + +import { + ClockEvents, + ConsumerSubject, + DisplayComponent, + EventBus, + FSComponent, + MappedSubject, + Subject, + Subscribable, + SubscribableMapFunctions, + VNode, +} from '@microsoft/msfs-sdk'; +import { FailuresConsumer, NXDataStore } from '@flybywiresim/fbw-sdk'; +import { AcElectricalBus, DcElectricalBus } from '@shared/electrical'; + +import './oit-display-unit.scss'; +import { OitSimvars } from 'instruments/src/OIT/OitSimvarPublisher'; +import { A380Failure } from '@failures'; +import { OisOperationMode } from 'instruments/src/OIT/OIT'; + +export const getDisplayIndex = () => { + const url = Array.from(document.querySelectorAll('vcockpit-panel > *')) + ?.find((it) => it.tagName.toLowerCase() !== 'wasm-instrument') + ?.getAttribute('url'); + + return url ? parseInt(url.substring(url.length - 1), 10) : 0; +}; + +export enum OitDisplayUnitID { + CaptOit, + FoOit, +} + +const DisplayUnitToDCBus: { [k in OitDisplayUnitID]: (DcElectricalBus | AcElectricalBus)[] } = { + [OitDisplayUnitID.CaptOit]: [AcElectricalBus.Ac2, DcElectricalBus.Dc2], + [OitDisplayUnitID.FoOit]: [AcElectricalBus.AcEss, DcElectricalBus.DcEssInFlight, DcElectricalBus.Dc1], +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const DisplayUnitToPotentiometer: { [k in OitDisplayUnitID]: number } = { + [OitDisplayUnitID.CaptOit]: 81, + [OitDisplayUnitID.FoOit]: 82, +}; + +interface DisplayUnitProps { + readonly bus: EventBus; + readonly displayUnitId: OitDisplayUnitID; + readonly failuresConsumer: FailuresConsumer; + readonly nssOrFltOps: Subscribable; + readonly test?: Subscribable; +} + +enum DisplayUnitState { + On, + Off, + Bootup, + Selftest, + Standby, + Failed, +} + +export class OitDisplayUnit extends DisplayComponent { + private readonly sub = this.props.bus.getSubscriber(); + + private readonly state = Subject.create( + SimVar.GetSimVarValue('L:A32NX_COLD_AND_DARK_SPAWN', 'Bool') ? DisplayUnitState.Off : DisplayUnitState.Standby, + ); + + private readonly failureKey = + this.props.displayUnitId === OitDisplayUnitID.CaptOit ? A380Failure.CaptainOit : A380Failure.FirstOfficerOit; + + private timeOut: number = 0; + + private selfTestRef = FSComponent.createRef(); + + private thalesBootupRef = FSComponent.createRef(); + + private oitRef = FSComponent.createRef(); + + private readonly powered = Subject.create(false); + + private readonly brightness = ConsumerSubject.create( + this.sub.on(this.props.displayUnitId === OitDisplayUnitID.CaptOit ? 'potentiometerCaptain' : 'potentiometerFo'), + 75, + ); + + private readonly nssAnsu1Failed = ConsumerSubject.create(this.sub.on('nssAnsu1Healthy'), true).map( + SubscribableMapFunctions.not(), + ); + + private readonly nssAnsu2Failed = ConsumerSubject.create(this.sub.on('nssAnsu2Healthy'), true).map( + SubscribableMapFunctions.not(), + ); + + private readonly allNssAnsuFailed = MappedSubject.create( + SubscribableMapFunctions.and(), + this.nssAnsu1Failed, + this.nssAnsu2Failed, + ); + + private readonly fltOpsAnsuFailed = ConsumerSubject.create(this.sub.on('fltOpsAnsu1Healthy'), true).map( + SubscribableMapFunctions.not(), + ); + + private readonly fltOpsLaptopFailed = ConsumerSubject.create( + this.sub.on(this.props.displayUnitId === OitDisplayUnitID.CaptOit ? 'laptopCaptHealthy' : 'laptopFoHealthy'), + true, + ).map(SubscribableMapFunctions.not()); + + private readonly failed = MappedSubject.create( + ([opMode, state, nssFail, fltOpsFail]) => + state === DisplayUnitState.On && ((opMode === 'nss' && nssFail) || (opMode === 'flt-ops' && fltOpsFail)), + this.props.nssOrFltOps, + this.state, + this.allNssAnsuFailed, + this.fltOpsLaptopFailed, + ); + + public onAfterRender(node: VNode): void { + super.onAfterRender(node); + + this.props.failuresConsumer.register(this.failureKey); + + this.sub.on('realTime').handle(() => this.update()); + this.sub + .on('realTime') + .atFrequency(1) + .handle((_t) => { + // override MSFS menu animations setting for this instrument + if (!document.documentElement.classList.contains('animationsEnabled')) { + document.documentElement.classList.add('animationsEnabled'); + } + }); + + MappedSubject.create( + () => { + this.updateState(); + }, + this.brightness, + this.powered, + this.failed, + ); + } + + setTimer(time: number) { + this.timeOut = window.setTimeout(() => { + if (this.state.get() === DisplayUnitState.Standby) { + this.state.set(DisplayUnitState.Off); + } + if (this.state.get() === DisplayUnitState.Selftest) { + this.state.set(DisplayUnitState.On); + } + this.updateState(); + }, time * 1000); + } + + public update() { + const poweredByBus1 = DisplayUnitToDCBus[this.props.displayUnitId][0] + ? SimVar.GetSimVarValue(`L:A32NX_ELEC_${DisplayUnitToDCBus[this.props.displayUnitId][0]}_BUS_IS_POWERED`, 'Bool') + : true; + const poweredByBus2 = DisplayUnitToDCBus[this.props.displayUnitId][1] + ? SimVar.GetSimVarValue(`L:A32NX_ELEC_${DisplayUnitToDCBus[this.props.displayUnitId][1]}_BUS_IS_POWERED`, 'Bool') + : true; + const poweredByBus3 = DisplayUnitToDCBus[this.props.displayUnitId][2] + ? SimVar.GetSimVarValue(`L:A32NX_ELEC_${DisplayUnitToDCBus[this.props.displayUnitId][2]}_BUS_IS_POWERED`, 'Bool') + : true; + this.powered.set( + (poweredByBus1 || poweredByBus2 || poweredByBus3) && !this.props.failuresConsumer.isActive(this.failureKey), + ); + } + + updateState() { + if (this.state.get() === DisplayUnitState.On && (this.brightness.get() === 0 || !this.powered.get())) { + this.state.set(DisplayUnitState.Standby); + this.setTimer(10); + } else if (this.state.get() === DisplayUnitState.Standby && this.brightness.get() !== 0 && this.powered.get()) { + this.state.set(DisplayUnitState.On); + clearTimeout(this.timeOut); + } else if (this.state.get() === DisplayUnitState.Off && this.brightness.get() !== 0 && this.powered.get()) { + this.state.set(DisplayUnitState.Bootup); + this.setTimer(0.25 + Math.random() * 0.2); + } else if (this.state.get() === DisplayUnitState.Bootup && this.brightness.get() !== 0 && this.powered.get()) { + this.state.set(DisplayUnitState.Selftest); + this.setTimer(parseInt(NXDataStore.get('CONFIG_SELF_TEST_TIME', '15'))); + } else if ( + (this.state.get() === DisplayUnitState.Selftest || this.state.get() === DisplayUnitState.Bootup) && + (this.brightness.get() === 0 || !this.powered.get()) + ) { + this.state.set(DisplayUnitState.Off); + clearTimeout(this.timeOut); + } + + if (this.state.get() === DisplayUnitState.Selftest) { + this.selfTestRef.instance.style.display = 'block'; + this.thalesBootupRef.instance.style.display = 'none'; + this.oitRef.instance.style.display = 'none'; + } else if (this.state.get() === DisplayUnitState.Bootup) { + this.selfTestRef.instance.style.display = 'none'; + this.thalesBootupRef.instance.style.display = 'block'; + this.oitRef.instance.style.display = 'none'; + } else if (this.state.get() === DisplayUnitState.On) { + this.selfTestRef.instance.style.display = 'none'; + this.thalesBootupRef.instance.style.display = 'none'; + this.oitRef.instance.style.display = 'block'; + } else if (this.state.get() === DisplayUnitState.Failed) { + this.selfTestRef.instance.style.display = 'none'; + this.thalesBootupRef.instance.style.display = 'none'; + this.oitRef.instance.style.display = 'none'; + } else { + this.selfTestRef.instance.style.display = 'none'; + this.thalesBootupRef.instance.style.display = 'none'; + this.oitRef.instance.style.display = 'none'; + } + } + + render(): VNode { + return ( + <> + + + + +
+ {this.props.children} +
+ + (v ? 'block' : 'none')), + }} + class="AnsuFailed" + viewBox="0 0 1333 1000" + > + + Not available. + + + + ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx index a7b45bf39ea..cd9ff1fdfeb 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx @@ -32,11 +32,7 @@ export abstract class OitFooter extends DisplayComponent {
{['FLT OPS STS', 'CHARTS', 'OPS LIBRARY'].map((s) => ( -
); } diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx index 9f095f73eb4..e50f81b8a2e 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx @@ -9,14 +9,24 @@ export type OitSimvars = { elecFo: boolean; potentiometerCaptain: number; potentiometerFo: number; + nssAnsu1Healthy: boolean; + nssAnsu2Healthy: boolean; + fltOpsAnsu1Healthy: boolean; + laptopCaptHealthy: boolean; + laptopFoHealthy: boolean; }; export enum OitVars { coldDark = 'L:A32NX_COLD_AND_DARK_SPAWN', elec = 'L:A32NX_ELEC_AC_2_BUS_IS_POWERED', elecFo = 'L:A32NX_ELEC_AC_ESS_BUS_IS_POWERED', - potentiometerCaptain = 'LIGHT POTENTIOMETER:81', // FIXME poti - potentiometerFo = 'LIGHT POTENTIOMETER:82', // FIXME poti + potentiometerCaptain = 'LIGHT POTENTIOMETER:81', + potentiometerFo = 'LIGHT POTENTIOMETER:82', + nssAnsu1Healthy = 'L:A32NX_NSS_ANSU_1_IS_HEALTHY', + nssAnsu2Healthy = 'L:A32NX_NSS_ANSU_2_IS_HEALTHY', + fltOpsAnsu1Healthy = 'L:A32NX_FLTOPS_ANSU_1_IS_HEALTHY', + laptopCaptHealthy = 'L:A32NX_FLTOPS_LAPTOP_1_IS_HEALTHY', + laptopFoHealthy = 'L:A32NX_FLTOPS_LAPTOP_2_IS_HEALTHY', } /** A publisher to poll and publish nav/com simvars. */ @@ -26,6 +36,11 @@ export class OitSimvarPublisher extends SimVarPublisher { ['elecFo', { name: OitVars.elecFo, type: SimVarValueType.Bool }], ['potentiometerCaptain', { name: OitVars.potentiometerCaptain, type: SimVarValueType.Number }], ['potentiometerFo', { name: OitVars.potentiometerFo, type: SimVarValueType.Number }], + ['nssAnsu1Healthy', { name: OitVars.nssAnsu1Healthy, type: SimVarValueType.Bool }], + ['nssAnsu2Healthy', { name: OitVars.nssAnsu2Healthy, type: SimVarValueType.Bool }], + ['fltOpsAnsu1Healthy', { name: OitVars.fltOpsAnsu1Healthy, type: SimVarValueType.Bool }], + ['laptopCaptHealthy', { name: OitVars.laptopCaptHealthy, type: SimVarValueType.Bool }], + ['laptopFoHealthy', { name: OitVars.laptopFoHealthy, type: SimVarValueType.Bool }], ]); public constructor(bus: EventBus) { diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx index 704caf895ac..bc040f73a8a 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx @@ -32,46 +32,26 @@ export class OitFltOpsMenuPage extends DisplayComponent
MISSION
- {['CHARTS', 'FLT FOLDER'].map((s) => ( -
DOCUMENTATION
{['OPS LIBRARY'].map((s) => ( -
PERFORMANCE
{['T.O PERF', 'LOADSHEET', 'LDG PERF', 'IN-FLT PERF'].map((s) => ( -
UTILITIES
{['FLT OPS STS', 'LOAD BOX', 'EXPORT BOX'].map((s) => ( -
@@ -79,7 +59,6 @@ export class OitFltOpsMenuPage extends DisplayComponent
diff --git a/fbw-a380x/src/systems/instruments/src/OIT/instrument.tsx b/fbw-a380x/src/systems/instruments/src/OIT/instrument.tsx index aed7c50728c..16630640f88 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/instrument.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/instrument.tsx @@ -13,6 +13,7 @@ import { import { FailuresConsumer } from '@flybywiresim/fbw-sdk'; import { OIT } from 'instruments/src/OIT/OIT'; import { OitSimvarPublisher } from 'instruments/src/OIT/OitSimvarPublisher'; +import { OisLaptop } from 'instruments/src/OIT/OisLaptop'; class OitInstrument implements FsInstrument { private readonly bus = new EventBus(); @@ -27,12 +28,19 @@ class OitInstrument implements FsInstrument { private readonly failuresConsumer = new FailuresConsumer('A32NX'); + private readonly laptop = new OisLaptop( + this.bus, + this.instrument.instrumentIndex === 1 ? 1 : 2, + this.failuresConsumer, + ); + constructor(public readonly instrument: BaseInstrument) { this.hEventPublisher = new HEventPublisher(this.bus); this.backplane.addPublisher('hEvent', this.hEventPublisher); this.backplane.addPublisher('clock', this.clockPublisher); this.backplane.addPublisher('oitSimvar', this.oitSimvarPublisher); + this.backplane.addInstrument('Laptop', this.laptop); this.doInit(); } @@ -47,6 +55,8 @@ class OitInstrument implements FsInstrument { bus={this.bus} instrument={this.instrument} captOrFo={this.instrument.instrumentIndex === 1 ? 'CAPT' : 'FO'} + failuresConsumer={this.failuresConsumer} + laptop={this.laptop} />, document.getElementById('OIT_CONTENT'), ); diff --git a/fbw-a380x/src/systems/instruments/src/OIT/oit-display-unit.scss b/fbw-a380x/src/systems/instruments/src/OIT/oit-display-unit.scss new file mode 100644 index 00000000000..d7b71b65de5 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/oit-display-unit.scss @@ -0,0 +1,56 @@ + +@import "../MsfsAvionicsCommon/definitions.scss"; + +@font-face { + font-family: "OIT"; + //noinspection CssUnknownTarget + src: url("/Fonts/fbw-a380x/EFB/Inter-Regular.ttf") format("truetype"); + font-weight: normal; + font-style: normal; +} + +.SelfTest { + position: absolute; + left: 0%; + top: 0%; + width: 100%; + height: 100%; + border: none; +} + +.SelfTestBackground { + fill: $display-background; +} + +.SelfTestText { + font-size: 24px; + fill: $display-green; + visibility: visible !important; + + text-anchor: middle; + font-family: "OIT", monospace; +} + +.EngineeringTestModeText { + font-size: 21px; + color: black; + font-family: "OIT", monospace; +} + +.AnsuFailed { + position: absolute; + left: 0%; + top: 0%; + width: 100%; + height: 100%; + border: none; + background-color: $display-background; +} + +.AnsuFailedText { + font-size: 32px; + fill: $display-amber; + visibility: visible !important; + text-anchor: middle; + font-family: "OIT", monospace; +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss b/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss index ea72f521f6c..39c0531100e 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss +++ b/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss @@ -1,9 +1,9 @@ -@import "../../../MsfsAvionicsCommon/definitions.scss"; +@import "../MsfsAvionicsCommon/definitions.scss"; .mfd-label { font-size: 20px; color: $display-white; - font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; + font-family: "OIT", monospace; } .mfd-label.green { @@ -73,12 +73,12 @@ font-size: 22px; background-color: $display-mfd-darker-grey; color: $display-white; - font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; + font-family: "OIT", monospace; text-align: center; align-items: center; justify-content: center; border: 2px outset $display-light-grey; - padding: 9px 12px 5px 12px; + padding: 10px 12px 10px 12px; } .mfd-button.opened { @@ -171,12 +171,12 @@ } .mfd-dropdown-menu { - background-color: $display-mfd-darker-grey; + background-color: $display-background; min-width: 100%; position: absolute; z-index: 60; overflow: auto; - max-height: 275px; + max-height: 600px; display: none; border: 2px outset $display-light-grey; } @@ -201,14 +201,22 @@ color: $display-dark-grey; } +.mfd-dropdown-menu-element.separator { + border-bottom: 3px solid $display-light-grey; +} + +.mfd-dropdown-menu-element.separator:hover { + border: 3px solid $display-cyan; +} + .mfd-page-selector-label { - font-size: 22px; + font-size: 28px; color: $display-white; - font-family: "Ecam", monospace; + font-family: "OIT", monospace; padding: 2px; text-align: center; display: inline; - margin: 10px; + margin: 7px 0px 7px 0px; padding: 3px; border: 1px solid transparent; } @@ -425,7 +433,7 @@ } .mfd-context-menu { - font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; + font-family: "OIT", monospace; background-color: $display-mfd-darker-grey; border: 2px outset $display-light-grey; position: absolute; @@ -502,7 +510,7 @@ .mfd-input-field-text-input { background-color: $display-background; border: none; - font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; + font-family: "OIT", monospace; font-size: 27px; line-height: 27px; color: $display-cyan; @@ -555,7 +563,7 @@ width: 18px; background-color: $display-cyan; color: $display-background; - font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; + font-family: "OIT", monospace; font-size: 27px; text-align: center; vertical-align: center; @@ -651,7 +659,7 @@ .mfd-surv-label { font-size: 20px; color: $display-white; - font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; + font-family: "OIT", monospace; text-align: center; margin-bottom: 2px; } @@ -677,7 +685,7 @@ font-size: 22px; padding-top: 3px; color: $display-white; - font-family: "FBW-Display-EIS-A380-SlashedZero", monospace; + font-family: "OIT", monospace; } .mfd-surv-status-indicator { @@ -729,5 +737,5 @@ .mfd-adsc-label-off { font-size: 20px; color: $display-dark-grey; - font-family: "Ecam", monospace; + font-family: "OIT", monospace; } diff --git a/fbw-a380x/src/systems/shared/src/electrical.ts b/fbw-a380x/src/systems/shared/src/electrical.ts index 74671dff4b3..f45f9a03ebe 100644 --- a/fbw-a380x/src/systems/shared/src/electrical.ts +++ b/fbw-a380x/src/systems/shared/src/electrical.ts @@ -4,3 +4,12 @@ export enum DcElectricalBus { DcEss = 'DC_ESS', DcEssInFlight = '108PH', } + +export enum AcElectricalBus { + Ac1 = 'AC_1', + Ac2 = 'AC_2', + Ac3 = 'AC_3', + Ac4 = 'AC_4', + AcEss = 'AC_ESS', + AcEmer = 'AC_EMER', +} diff --git a/fbw-a380x/src/systems/systems-host/index.ts b/fbw-a380x/src/systems/systems-host/index.ts index b8ebac5a4de..0d3c6c65e28 100644 --- a/fbw-a380x/src/systems/systems-host/index.ts +++ b/fbw-a380x/src/systems/systems-host/index.ts @@ -35,6 +35,7 @@ import { FwsCore } from 'systems-host/systems/FlightWarningSystem/FwsCore'; import { FuelSystemPublisher } from 'systems-host/systems/FuelSystemPublisher'; import { BrakeToVacateDistanceUpdater } from 'systems-host/systems/BrakeToVacateDistanceUpdater'; import { PseudoFwcSimvarPublisher } from 'instruments/src/MsfsAvionicsCommon/providers/PseudoFwcPublisher'; +import { AircraftNetworkServerUnit } from 'systems-host/systems/InformationSystems/AircraftNetworkServerUnit'; class SystemsHost extends BaseInstrument { private readonly bus = new ArincEventBus(); @@ -104,6 +105,17 @@ class SystemsHost extends BaseInstrument { //FIXME add some deltatime functionality to backplane instruments so we dont have to pass SystemHost private readonly legacyFuel = new LegacyFuel(this.bus, this); + // For now, pass ATSU to the ANSUs. In our target architecture, there should be no ATSU + private readonly nssAnsu1 = new AircraftNetworkServerUnit(this.bus, 1, 'nss', this.failuresConsumer, this.atsu); + private readonly nssAnsu2 = new AircraftNetworkServerUnit(this.bus, 2, 'nss', this.failuresConsumer, this.atsu); + private readonly fltOpsAnsu1 = new AircraftNetworkServerUnit( + this.bus, + 1, + 'flt-ops', + this.failuresConsumer, + this.atsu, + ); + /** * "mainmenu" = 0 * "loading" = 1 @@ -134,6 +146,9 @@ class SystemsHost extends BaseInstrument { this.backplane.addPublisher('StallWarning', this.stallWarningPublisher); this.backplane.addPublisher('PseudoFwc', this.pseudoFwcPublisher); this.backplane.addInstrument('LegacyFuel', this.legacyFuel); + this.backplane.addInstrument('nssAnsu1', this.nssAnsu1, true); + this.backplane.addInstrument('nssAnsu2', this.nssAnsu2, true); + this.backplane.addInstrument('fltOpsAnsu1', this.fltOpsAnsu1, true); this.hEventPublisher = new HEventPublisher(this.bus); this.soundManager = new LegacySoundManager(); diff --git a/fbw-a380x/src/systems/systems-host/systems/InformationSystems/AircraftNetworkServerUnit.ts b/fbw-a380x/src/systems/systems-host/systems/InformationSystems/AircraftNetworkServerUnit.ts new file mode 100644 index 00000000000..3724391141d --- /dev/null +++ b/fbw-a380x/src/systems/systems-host/systems/InformationSystems/AircraftNetworkServerUnit.ts @@ -0,0 +1,70 @@ +// Copyright (c) 2025 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, Instrument, SimVarValueType, Subject, Subscribable } from '@microsoft/msfs-sdk'; +import { FailuresConsumer } from '@flybywiresim/fbw-sdk'; +import { A380Failure } from '@failures'; +import { AtsuSystem } from 'systems-host/systems/atsu'; + +type AnsuIndex = 1 | 2; + +type AnsuType = 'nss' | 'flt-ops'; + +export class AircraftNetworkServerUnit implements Instrument { + private readonly failureKey = + this.type === 'flt-ops' && this.index === 1 + ? A380Failure.FltOpsAnsu1 + : this.index === 1 + ? A380Failure.NssAnsu1 + : A380Failure.NssAnsu2; + + private readonly powered = Subject.create(false); + + private readonly _isHealthy = Subject.create(false); + public readonly isHealthy = this._isHealthy as Subscribable; + + constructor( + private readonly bus: EventBus, + private readonly index: AnsuIndex, + private readonly type: AnsuType, + private readonly failuresConsumer: FailuresConsumer, + private readonly atsu: AtsuSystem, + ) {} + + /** @inheritdoc */ + init(): void { + this.failuresConsumer.register(this.failureKey); + } + + /** @inheritdoc */ + onUpdate(): void { + const failed = this.failuresConsumer.isActive(this.failureKey); + + if (this.type === 'nss') { + if (this.index === 1) { + this.powered.set( + SimVar.GetSimVarValue('L:A32NX_ELEC_AC_2_BUS_IS_POWERED', SimVarValueType.Bool) || + SimVar.GetSimVarValue('L:A32NX_ELEC_DC_HOT_2_BUS_IS_POWERED', SimVarValueType.Bool), + ); + } else { + this.powered.set( + SimVar.GetSimVarValue('L:A32NX_ELEC_AC_1_BUS_IS_POWERED', SimVarValueType.Bool) || + SimVar.GetSimVarValue('L:A32NX_ELEC_AC_EMER_BUS_IS_POWERED', SimVarValueType.Bool) || + SimVar.GetSimVarValue('L:A32NX_ELEC_DC_HOT_1_BUS_IS_POWERED', SimVarValueType.Bool), + ); + } + } else if (this.type === 'flt-ops') { + this.powered.set( + SimVar.GetSimVarValue('L:A32NX_ELEC_AC_ESS_BUS_IS_POWERED', SimVarValueType.Bool) || + SimVar.GetSimVarValue('L:A32NX_ELEC_DC_HOT_1_BUS_IS_POWERED', SimVarValueType.Bool), + ); + } + + this._isHealthy.set(!failed && this.powered.get()); + SimVar.SetSimVarValue( + `L:A32NX_${this.type === 'nss' ? 'NSS' : 'FLTOPS'}_ANSU_${this.index.toFixed(0)}_IS_HEALTHY`, + SimVarValueType.Bool, + this._isHealthy.get(), + ); + } +} diff --git a/fbw-common/src/systems/shared/src/ata.ts b/fbw-common/src/systems/shared/src/ata.ts index 36672356ec1..2ea19792ea0 100644 --- a/fbw-common/src/systems/shared/src/ata.ts +++ b/fbw-common/src/systems/shared/src/ata.ts @@ -103,6 +103,7 @@ export const AtaChaptersDescription = Object.freeze({ 31: 'The cockpit displays give critical flight information to the pilots. In a failure where displays are lost, the pilots must deal with a lack of flight data given to them.', 32: 'The landing gear components are responsible for supporting and steering the aircraft on the ground, and make it possible to retract and store the landing gear in flight. Includes the functioning and maintenance aspects of the landing gear doors.', 34: 'The navigation systems provide data about the position, speed, heading, and altitude of the aircraft. Failures in a system such as the ADIRS can cause a loss of data sent to instrumentation.', + 46: 'Information systems provide means of communication between Airline Operational Control (AOC), Air Traffic Control (ATC), and various applications around the organization of on-board information (e.g. via the OIS)', }); export type AtaChapterNumber = keyof typeof AtaChaptersTitle; From de89893af4ef3466e4475048bf78531d8e0fea1d Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:59:29 +0200 Subject: [PATCH 05/23] menu revamp, introduce performance pages --- .../systems/instruments/src/OIT/OitFooter.tsx | 2 +- .../systems/instruments/src/OIT/OitHeader.tsx | 30 ++++++-- .../instruments/src/OIT/OitPageDirectory.tsx | 5 ++ .../OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx | 72 +++++++++++++++---- .../Pages/flt-ops/OitFltOpsPerformance.tsx | 40 +++++++++++ 5 files changed, 128 insertions(+), 21 deletions(-) create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsPerformance.tsx diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx index cd9ff1fdfeb..020b6e197d4 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx @@ -31,7 +31,7 @@ export abstract class OitFooter extends DisplayComponent { return (
- {['FLT OPS STS', 'CHARTS', 'OPS LIBRARY'].map((s) => ( + {['FLT OPS STS', 'CHARTS', 'FLT FOLDER'].map((s) => (
DOCUMENTATION
- {['OPS LIBRARY'].map((s) => ( -
PERFORMANCE
- {['T.O PERF', 'LOADSHEET', 'LDG PERF', 'IN-FLT PERF'].map((s) => ( -
UTILITIES
- {['FLT OPS STS', 'LOAD BOX', 'EXPORT BOX'].map((s) => ( -
@@ -60,6 +101,7 @@ export class OitFltOpsMenuPage extends DisplayComponent label={'EXIT SESSION'} containerStyle="width: 300px; margin-bottom: 20px; align-self: flex-end;" onClick={() => {}} + disabled={Subject.create(true)} />
{/* end page content */} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsPerformance.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsPerformance.tsx new file mode 100644 index 00000000000..e6f8bb09070 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsPerformance.tsx @@ -0,0 +1,40 @@ +// Copyright (c) 2025 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { DisplayComponent, FSComponent, Subscription, VNode } from '@microsoft/msfs-sdk'; +import { AbstractOitPageProps } from 'instruments/src/OIT/OIT'; + +interface OitFltOpsMenuPageProps extends AbstractOitPageProps {} + +export class OitFltOpsPerformance extends DisplayComponent { + // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. + private subs = [] as Subscription[]; + + public onAfterRender(node: VNode): void { + super.onAfterRender(node); + } + + public destroy(): void { + // Destroy all subscriptions to remove all references to this instance. + this.subs.forEach((x) => x.destroy()); + + super.destroy(); + } + + render(): VNode { + return ( + <> + {/* begin page content */} +
+
+ NOT IMPLEMENTED +
+
+ Performance calculations are not yet implemented. We suggest to use Navigraph's performance calculators. +
+
+ {/* end page content */} + + ); + } +} From b88f8016cee9122639c533fc3a2c51e6d7efd801 Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Mon, 20 Jan 2025 23:58:51 +0200 Subject: [PATCH 06/23] STS page first implementation --- .../src/systems/instruments/src/MFD/MFD.tsx | 3 +- .../src/MFD/shared/MFDSimvarPublisher.tsx | 5 - .../UiWidgets/DropdownMenu.tsx | 3 +- .../UiWidgets/InputField.tsx | 6 +- .../instruments/src/ND/OansControlPanel.tsx | 4 +- .../src/systems/instruments/src/OIT/OIT.tsx | 9 +- .../instruments/src/OIT/OitPageDirectory.tsx | 3 + .../src/OIT/OitSimvarPublisher.tsx | 4 + .../Pages/flt-ops/OitFltOpsPerformance.tsx | 4 +- .../src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx | 118 ++++++++++++++++++ .../instruments/src/OIT/oit-display-unit.scss | 3 - .../systems/instruments/src/OIT/style.scss | 43 +++++++ 12 files changed, 187 insertions(+), 18 deletions(-) create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx diff --git a/fbw-a380x/src/systems/instruments/src/MFD/MFD.tsx b/fbw-a380x/src/systems/instruments/src/MFD/MFD.tsx index cd60b114481..d3cb80b7de3 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/MFD.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/MFD.tsx @@ -27,10 +27,11 @@ import { DisplayInterface } from '@fmgc/flightplanning/interface/DisplayInterfac import { FmsErrorType } from '@fmgc/FmsError'; import { FmcServiceInterface } from 'instruments/src/MFD/FMC/FmcServiceInterface'; import { CdsDisplayUnit, DisplayUnitID } from '../MsfsAvionicsCommon/CdsDisplayUnit'; -import { InteractionMode, InternalKccuKeyEvent, MfdSimvars } from './shared/MFDSimvarPublisher'; +import { InternalKccuKeyEvent, MfdSimvars } from './shared/MFDSimvarPublisher'; import { MfdFmsPageNotAvail } from 'instruments/src/MFD/pages/FMS/MfdFmsPageNotAvail'; import './pages/common/style.scss'; +import { InteractionMode } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/InputField'; export const getDisplayIndex = () => { const url = document.getElementsByTagName('a380x-mfd')[0].getAttribute('url'); diff --git a/fbw-a380x/src/systems/instruments/src/MFD/shared/MFDSimvarPublisher.tsx b/fbw-a380x/src/systems/instruments/src/MFD/shared/MFDSimvarPublisher.tsx index ec0095b8be4..e4915ad4d04 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/shared/MFDSimvarPublisher.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/shared/MFDSimvarPublisher.tsx @@ -38,11 +38,6 @@ export type InternalKccuKeyEvent = { kccuKeyEvent: string; }; -export enum InteractionMode { - Touchscreen, - Kccu, -} - export enum MfdVars { coldDark = 'L:A32NX_COLD_AND_DARK_SPAWN', elec = 'L:A32NX_ELEC_AC_ESS_BUS_IS_POWERED', diff --git a/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/DropdownMenu.tsx b/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/DropdownMenu.tsx index dcc75a188be..8f4a4f8e8f9 100644 --- a/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/DropdownMenu.tsx +++ b/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/DropdownMenu.tsx @@ -14,9 +14,8 @@ import { Subscription, VNode, } from '@microsoft/msfs-sdk'; -import { InputField } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/InputField'; +import { InputField, InteractionMode } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/InputField'; import { DropdownFieldFormat } from 'instruments/src/MFD/pages/common/DataEntryFormats'; -import { InteractionMode } from 'instruments/src/MFD/shared/MFDSimvarPublisher'; interface DropdownMenuProps extends ComponentProps { values: SubscribableArray; diff --git a/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/InputField.tsx b/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/InputField.tsx index 5dbba669b24..474723e4f04 100644 --- a/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/InputField.tsx +++ b/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/InputField.tsx @@ -14,7 +14,11 @@ import { } from '@microsoft/msfs-sdk'; import { DataEntryFormat } from 'instruments/src/MFD/pages/common/DataEntryFormats'; import { FmsError, FmsErrorType } from '@fmgc/FmsError'; -import { InteractionMode } from 'instruments/src/MFD/shared/MFDSimvarPublisher'; + +export enum InteractionMode { + Touchscreen, + Kccu, +} interface InputFieldProps extends ComponentProps { dataEntryFormat: DataEntryFormat; diff --git a/fbw-a380x/src/systems/instruments/src/ND/OansControlPanel.tsx b/fbw-a380x/src/systems/instruments/src/ND/OansControlPanel.tsx index bf8db36a391..2c62f78ca39 100644 --- a/fbw-a380x/src/systems/instruments/src/ND/OansControlPanel.tsx +++ b/fbw-a380x/src/systems/instruments/src/ND/OansControlPanel.tsx @@ -48,14 +48,14 @@ import { Button } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/Button'; import { OansRunwayInfoBox } from './OANSRunwayInfoBox'; import { DropdownMenu } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/DropdownMenu'; import { RadioButtonGroup } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/RadioButtonGroup'; -import { InputField } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/InputField'; +import { InputField, InteractionMode } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/InputField'; import { LengthFormat } from 'instruments/src/MFD/pages/common/DataEntryFormats'; import { IconButton } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/IconButton'; import { TopTabNavigator, TopTabNavigatorPage } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/TopTabNavigator'; import { Coordinates, distanceTo, placeBearingDistance } from 'msfs-geo'; import { AdirsSimVars } from 'instruments/src/MsfsAvionicsCommon/SimVarTypes'; import { NavigationDatabase, NavigationDatabaseBackend, NavigationDatabaseService } from '@fmgc/index'; -import { InteractionMode, InternalKccuKeyEvent } from 'instruments/src/MFD/shared/MFDSimvarPublisher'; +import { InternalKccuKeyEvent } from 'instruments/src/MFD/shared/MFDSimvarPublisher'; import { NDSimvars } from 'instruments/src/ND/NDSimvarPublisher'; import { Position } from '@turf/turf'; diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx index 1b73bd8320f..6b60e9f066f 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx @@ -12,7 +12,7 @@ import { } from '@microsoft/msfs-sdk'; import './style.scss'; -import { OitSimvars } from 'instruments/src/OIT/OitSimvarPublisher'; +import { InternalKbdKeyEvent, OitSimvars } from 'instruments/src/OIT/OitSimvarPublisher'; import { OitUiService, OitUriInformation } from 'instruments/src/OIT/OitUiService'; import { OitNotFound } from 'instruments/src/OIT/pages/OitNotFound'; import { pageForUrl } from 'instruments/src/OIT/OitPageDirectory'; @@ -21,6 +21,7 @@ import { OitFooter } from 'instruments/src/OIT/OitFooter'; import { OitDisplayUnit, OitDisplayUnitID } from 'instruments/src/OIT/OitDisplayUnit'; import { FailuresConsumer } from '@flybywiresim/fbw-sdk'; import { OisLaptop } from 'instruments/src/OIT/OisLaptop'; +import { InteractionMode } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/InputField'; export interface AbstractOitPageProps extends ComponentProps { bus: EventBus; @@ -46,6 +47,10 @@ export class OIT extends DisplayComponent { return this.#uiService; } + public readonly hEventConsumer = this.props.bus.getSubscriber().on('kbdKeyEvent'); + + public readonly interactionMode = Subject.create(InteractionMode.Touchscreen); + private readonly topRef = FSComponent.createRef(); private readonly activePageRef = FSComponent.createRef(); @@ -59,7 +64,7 @@ export class OIT extends DisplayComponent { this.activeUriChanged(uri); }); - this.uiService.navigateTo('flt-ops'); // should be /sts + this.uiService.navigateTo('flt-ops/sts'); // should be /sts } private activeUriChanged(uri: OitUriInformation) { diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitPageDirectory.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitPageDirectory.tsx index 563fd95088b..477f220e3d4 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitPageDirectory.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitPageDirectory.tsx @@ -5,6 +5,7 @@ import { EventBus, FSComponent, VNode } from '@microsoft/msfs-sdk'; import { OIT } from 'instruments/src/OIT/OIT'; import { OitFltOpsMenuPage } from 'instruments/src/OIT/pages/flt-ops/OitFltOpsMenuPage'; import { OitFltOpsPerformance } from 'instruments/src/OIT/pages/flt-ops/OitFltOpsPerformance'; +import { OitFltOpsStatus } from 'instruments/src/OIT/pages/flt-ops/OitFltOpsStatus'; import { OitNotFound } from 'instruments/src/OIT/pages/OitNotFound'; // Page imports @@ -13,6 +14,8 @@ export function pageForUrl(url: string, bus: EventBus, oit: OIT): VNode { switch (url) { case 'flt-ops': return ; + case 'flt-ops/sts': + return ; case 'flt-ops/to-perf': return ; case 'flt-ops/ldg-perf': diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx index e50f81b8a2e..e2e9f5def6e 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitSimvarPublisher.tsx @@ -16,6 +16,10 @@ export type OitSimvars = { laptopFoHealthy: boolean; }; +export type InternalKbdKeyEvent = { + kbdKeyEvent: string; +}; + export enum OitVars { coldDark = 'L:A32NX_COLD_AND_DARK_SPAWN', elec = 'L:A32NX_ELEC_AC_2_BUS_IS_POWERED', diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsPerformance.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsPerformance.tsx index e6f8bb09070..14ebc082edb 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsPerformance.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsPerformance.tsx @@ -4,9 +4,9 @@ import { DisplayComponent, FSComponent, Subscription, VNode } from '@microsoft/msfs-sdk'; import { AbstractOitPageProps } from 'instruments/src/OIT/OIT'; -interface OitFltOpsMenuPageProps extends AbstractOitPageProps {} +interface OitFltOpsPerformancePageProps extends AbstractOitPageProps {} -export class OitFltOpsPerformance extends DisplayComponent { +export class OitFltOpsPerformance extends DisplayComponent { // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. private subs = [] as Subscription[]; diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx new file mode 100644 index 00000000000..cc20c7d108d --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx @@ -0,0 +1,118 @@ +// Copyright (c) 2025 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { ArraySubject, DisplayComponent, FSComponent, Subject, Subscription, VNode } from '@microsoft/msfs-sdk'; +import { AbstractOitPageProps } from 'instruments/src/OIT/OIT'; +import { DropdownMenu } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/DropdownMenu'; +import { InputField } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/InputField'; +import { LongAlphanumericFormat } from 'instruments/src/MFD/pages/common/DataEntryFormats'; +import { Button } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/Button'; + +interface OitFltOpsStatusPageProps extends AbstractOitPageProps {} + +export class OitFltOpsStatus extends DisplayComponent { + // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. + private subs = [] as Subscription[]; + + public onAfterRender(node: VNode): void { + super.onAfterRender(node); + } + + public destroy(): void { + // Destroy all subscriptions to remove all references to this instance. + this.subs.forEach((x) => x.destroy()); + + super.destroy(); + } + + render(): VNode { + return ( + <> + {/* begin page content */} +
+
+
+
ACFT REGISTRATION
+
+ (0)} + values={ArraySubject.create(['F-FBWA'])} + freeTextAllowed={false} + containerStyle="width: 600px;" + alignLabels="center" + numberOfDigitsForInputField={6} + tmpyActive={Subject.create(false)} + hEventConsumer={this.props.oit.hEventConsumer} + interactionMode={this.props.oit.interactionMode} + /> +
+
+
+
MSN
+
225
+
+
+
OIS VERSION
+
20-JAN 25 V1.0
+
+
+
+
+
FLT NUMBER
+
+ + dataEntryFormat={new LongAlphanumericFormat()} + mandatory={Subject.create(true)} + value={Subject.create(null)} + containerStyle="width: 600px; margin-right: 5px;" + alignText="center" + hEventConsumer={this.props.oit.hEventConsumer} + interactionMode={this.props.oit.interactionMode} + /> +
+
+
+
+
FROM
+
+ (0)} + values={ArraySubject.create(['----'])} + freeTextAllowed={false} + containerStyle="width: 250px;" + alignLabels="center" + numberOfDigitsForInputField={6} + tmpyActive={Subject.create(false)} + hEventConsumer={this.props.oit.hEventConsumer} + interactionMode={this.props.oit.interactionMode} + /> +
+ TO +
+ (0)} + values={ArraySubject.create(['----'])} + freeTextAllowed={false} + containerStyle="width: 250px;" + alignLabels="center" + numberOfDigitsForInputField={6} + tmpyActive={Subject.create(false)} + hEventConsumer={this.props.oit.hEventConsumer} + interactionMode={this.props.oit.interactionMode} + /> +
+
+
+
+
+ {/* end page content */} + + ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/oit-display-unit.scss b/fbw-a380x/src/systems/instruments/src/OIT/oit-display-unit.scss index d7b71b65de5..0e771e4b38f 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/oit-display-unit.scss +++ b/fbw-a380x/src/systems/instruments/src/OIT/oit-display-unit.scss @@ -28,13 +28,11 @@ visibility: visible !important; text-anchor: middle; - font-family: "OIT", monospace; } .EngineeringTestModeText { font-size: 21px; color: black; - font-family: "OIT", monospace; } .AnsuFailed { @@ -52,5 +50,4 @@ fill: $display-amber; visibility: visible !important; text-anchor: middle; - font-family: "OIT", monospace; } diff --git a/fbw-a380x/src/systems/instruments/src/OIT/style.scss b/fbw-a380x/src/systems/instruments/src/OIT/style.scss index f5cce134025..0cdcd479d3f 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/style.scss +++ b/fbw-a380x/src/systems/instruments/src/OIT/style.scss @@ -43,8 +43,14 @@ display: flex; flex: 1 1 auto; flex-direction: column; + } +.oit-page-container.framed { + border: 3px solid $display-light-grey; + padding: 10px; + margin: 10px; +} .oit-heading { font-size: 28px; @@ -129,3 +135,40 @@ margin-top: 60px; margin-bottom: 30px; } + +.oit-flt-ops-sts-upper { + flex: 2; + display: flex; + flex-direction: column; + justify-content: center; + border-bottom: 3px solid $display-light-grey; +} + +.oit-flt-ops-sts-line { + display: flex; + flex-direction: row; + margin-top: 40px; + margin-bottom: 40px; +} + +.oit-flt-ops-sts-lower { + flex: 1; + display: flex; + flex-direction: column; +} + +.oit-flt-ops-sts-line-left { + flex: 2; + display: flex; + justify-content: flex-end; + align-items: center; + margin-right: 20px; +} + +.oit-flt-ops-sts-line-right { + flex: 5; + display: flex; + justify-content: center; + align-items: center; + flex-direction: row; +} From eb3f6cca28a16ddcb563883a8e99f9db4f86c9db Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Tue, 21 Jan 2025 00:46:24 +0200 Subject: [PATCH 07/23] SYNCHRO AVIONICS --- .../src/MFD/FMC/FmcAircraftInterface.ts | 60 ++++++++++++------- .../MFD/pages/FMS/F-PLN/DestinationWindow.tsx | 2 +- .../MFD/pages/FMS/F-PLN/FplnRevisionsMenu.tsx | 2 +- .../src/MFD/pages/FMS/MfdFmsInit.tsx | 8 ++- .../src/MFD/pages/common/FmsPage.tsx | 2 +- .../src/systems/instruments/src/OIT/OIT.tsx | 8 +++ .../systems/instruments/src/OIT/OisLaptop.ts | 24 +++++++- .../systems/instruments/src/OIT/OitFooter.tsx | 18 +++++- .../src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx | 40 ++++++------- .../instruments/src/OIT/oit-display-unit.scss | 3 + .../instruments/src/OIT/widget-style.scss | 18 +++--- .../src/ND/shared/BtvRunwayInfo.tsx | 4 +- .../src/OANC/OancControlPanelUtils.ts | 4 +- .../publishers/OansBtv/FmsOansPublisher.ts | 10 ---- .../src/publishers/OansBtv/FmsPublisher.ts | 20 +++++++ .../shared/src/publishers/OansBtv/index.ts | 1 + 16 files changed, 149 insertions(+), 75 deletions(-) create mode 100644 fbw-common/src/systems/shared/src/publishers/OansBtv/FmsPublisher.ts 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 d2e09fa33e5..dcd46ac1763 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/FMC/FmcAircraftInterface.ts +++ b/fbw-a380x/src/systems/instruments/src/MFD/FMC/FmcAircraftInterface.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0 import { ConsumerValue, EventBus, GameStateProvider, SimVarValueType, Subject, UnitType } from '@microsoft/msfs-sdk'; -import { Arinc429SignStatusMatrix, Arinc429Word, FmsOansData, MathUtils } from '@flybywiresim/fbw-sdk'; +import { Arinc429SignStatusMatrix, Arinc429Word, FmsData, MathUtils } from '@flybywiresim/fbw-sdk'; import { FlapConf } from '@fmgc/guidance/vnav/common'; import { FlightPlanService } from '@fmgc/index'; import { MmrRadioTuningStatus } from '@fmgc/navigation/NavaidTuner'; @@ -495,29 +495,49 @@ export class FmcAircraftInterface { return phase > FmgcFlightPhase.Cruise || (phase === FmgcFlightPhase.Cruise && isCloseToDestination); } - updateOansAirports() { - if (this.flightPlanService.hasActive) { - const pub = this.bus.getPublisher(); - if (this.flightPlanService.active?.originAirport?.ident) { - pub.pub('fmsOrigin', this.flightPlanService.active.originAirport.ident, true); - } + updateFmsData() { + const pub = this.bus.getPublisher(); + pub.pub( + 'fmsOrigin', + this.flightPlanService.hasActive && this.flightPlanService.active?.originAirport?.ident + ? this.flightPlanService.active.originAirport.ident + : null, + true, + ); - if (this.flightPlanService.active?.originRunway?.ident) { - pub.pub('fmsDepartureRunway', this.flightPlanService.active.originRunway.ident, true); - } + pub.pub( + 'fmsDepartureRunway', + this.flightPlanService.hasActive && this.flightPlanService.active?.originRunway?.ident + ? this.flightPlanService.active.originRunway.ident + : null, + true, + ); - if (this.flightPlanService.active?.destinationAirport?.ident) { - pub.pub('fmsDestination', this.flightPlanService.active.destinationAirport.ident, true); - } + pub.pub( + 'fmsDestination', + this.flightPlanService.hasActive && this.flightPlanService.active?.destinationAirport?.ident + ? this.flightPlanService.active.destinationAirport.ident + : null, + true, + ); - if (this.flightPlanService.active?.destinationRunway?.ident) { - pub.pub('fmsLandingRunway', this.flightPlanService.active.destinationRunway.ident, true); - } + pub.pub( + 'fmsLandingRunway', + this.flightPlanService.hasActive && this.flightPlanService.active?.destinationRunway?.ident + ? this.flightPlanService.active.destinationRunway.ident + : null, + true, + ); - if (this.flightPlanService.active?.alternateDestinationAirport?.ident) { - pub.pub('fmsAlternate', this.flightPlanService.active.alternateDestinationAirport.ident, true); - } - } + pub.pub( + 'fmsAlternate', + this.flightPlanService.hasActive && this.flightPlanService.active?.alternateDestinationAirport?.ident + ? this.flightPlanService.active.alternateDestinationAirport.ident + : null, + true, + ); + + pub.pub('fmsFlightNumber', this.fmgc.data.atcCallsign.get(), true); } activatePreSelSpeedMach(preSel: number) { diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/F-PLN/DestinationWindow.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/F-PLN/DestinationWindow.tsx index 87bd8027230..9fa5367af43 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/F-PLN/DestinationWindow.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/F-PLN/DestinationWindow.tsx @@ -32,7 +32,7 @@ export class DestinationWindow extends DisplayComponent this.props.fmcService.master.revisedWaypointPlanIndex.get() ?? undefined, this.props.fmcService.master.revisedWaypointIsAltn.get() ?? undefined, ); - this.props.fmcService.master?.acInterface.updateOansAirports(); + this.props.fmcService.master?.acInterface.updateFmsData(); } this.props.visible.set(false); this.newDest.set(''); diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/F-PLN/FplnRevisionsMenu.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/F-PLN/FplnRevisionsMenu.tsx index 2ddda72d32a..218d3bf21cb 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/F-PLN/FplnRevisionsMenu.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/F-PLN/FplnRevisionsMenu.tsx @@ -156,7 +156,7 @@ export function getRevisionsMenu(fpln: MfdFmsFpln, type: FplnRevisionsMenuType): disabled: false, onPressed: () => { fpln.props.fmcService.master?.flightPlanService.enableAltn(legIndex, planIndex); - fpln.props.fmcService.master?.acInterface.updateOansAirports(); + fpln.props.fmcService.master?.acInterface.updateFmsData(); }, }, { diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsInit.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsInit.tsx index cea64df01a4..c452c762d70 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsInit.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/FMS/MfdFmsInit.tsx @@ -144,6 +144,8 @@ export class MfdFmsInit extends FmsPage { } else { this.disconnectFromNetworks(); } + this.props.fmcService.master?.acInterface.updateFmsData(); + this.loadedFlightPlan?.setFlightNumber(c); }); } @@ -235,7 +237,7 @@ export class MfdFmsInit extends FmsPage { toIcao, this.altnIcao.get() ?? undefined, ); - this.props.fmcService.master?.acInterface.updateOansAirports(); + this.props.fmcService.master?.acInterface.updateFmsData(); } } @@ -250,7 +252,7 @@ export class MfdFmsInit extends FmsPage { console.error(e); logTroubleshootingError(this.props.bus, e); } - this.props.fmcService.master?.acInterface.updateOansAirports(); + this.props.fmcService.master?.acInterface.updateFmsData(); this.props.fmcService.master.fmgc.data.atcCallsign.set(this.simBriefOfp?.callsign ?? '----------'); // Don't insert weights for now, something seems broken here @@ -387,7 +389,7 @@ export class MfdFmsInit extends FmsPage { this.altnIcao.set(v); if (v) { await this.props.fmcService.master?.flightPlanService.setAlternate(v); - this.props.fmcService.master?.acInterface.updateOansAirports(); + this.props.fmcService.master?.acInterface.updateFmsData(); } }} mandatory={Subject.create(true)} diff --git a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/FmsPage.tsx b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/FmsPage.tsx index 70918485d4a..579210e5b3e 100644 --- a/fbw-a380x/src/systems/instruments/src/MFD/pages/common/FmsPage.tsx +++ b/fbw-a380x/src/systems/instruments/src/MFD/pages/common/FmsPage.tsx @@ -188,7 +188,7 @@ export abstract class FmsPage extends DisplayCom } } - this.props.fmcService.master?.acInterface.updateOansAirports(); + this.props.fmcService.master?.acInterface.updateFmsData(); } public destroy(): void { diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx index 6b60e9f066f..b949010f64e 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx @@ -47,6 +47,14 @@ export class OIT extends DisplayComponent { return this.#uiService; } + get laptop() { + return this.props.laptop; + } + + get laptopData() { + return this.props.laptop.data; + } + public readonly hEventConsumer = this.props.bus.getSubscriber().on('kbdKeyEvent'); public readonly interactionMode = Subject.create(InteractionMode.Touchscreen); diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OisLaptop.ts b/fbw-a380x/src/systems/instruments/src/OIT/OisLaptop.ts index 88fac4d57b1..2b1cf0495cf 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OisLaptop.ts +++ b/fbw-a380x/src/systems/instruments/src/OIT/OisLaptop.ts @@ -1,13 +1,15 @@ // Copyright (c) 2025 FlyByWire Simulations // SPDX-License-Identifier: GPL-3.0 -import { EventBus, Instrument, SimVarValueType, Subject, Subscribable } from '@microsoft/msfs-sdk'; -import { FailuresConsumer } from '@flybywiresim/fbw-sdk'; +import { ConsumerSubject, EventBus, Instrument, SimVarValueType, Subject, Subscribable } from '@microsoft/msfs-sdk'; +import { FailuresConsumer, FmsData } from '@flybywiresim/fbw-sdk'; import { A380Failure } from '@failures'; type LaptopIndex = 1 | 2; export class OisLaptop implements Instrument { + public readonly data = new OisLaptopData(); + private readonly failureKey = this.index === 1 ? A380Failure.CaptainLaptop : A380Failure.FirstOfficerLaptop; private readonly powered = Subject.create(false); @@ -15,6 +17,18 @@ export class OisLaptop implements Instrument { private readonly _isHealthy = Subject.create(false); public readonly isHealthy = this._isHealthy as Subscribable; + private readonly sub = this.bus.getSubscriber(); + + private readonly fltNumberBus = ConsumerSubject.create(this.sub.on('fmsFlightNumber'), null); + private readonly fromAirportBus = ConsumerSubject.create(this.sub.on('fmsOrigin'), null); + private readonly toAirportBus = ConsumerSubject.create(this.sub.on('fmsDestination'), null); + + public synchroAvionics() { + this.data.fltNumber.set(this.fltNumberBus.get()); + this.data.fromAirport.set(this.fromAirportBus.get()); + this.data.toAirport.set(this.toAirportBus.get()); + } + constructor( private readonly bus: EventBus, private readonly index: LaptopIndex, @@ -47,3 +61,9 @@ export class OisLaptop implements Instrument { ); } } + +export class OisLaptopData { + public readonly fltNumber = Subject.create(null); + public readonly fromAirport = Subject.create(null); + public readonly toAirport = Subject.create(null); +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx index 020b6e197d4..8f3f9ccb86d 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitFooter.tsx @@ -31,9 +31,21 @@ export abstract class OitFooter extends DisplayComponent { return (
- {['FLT OPS STS', 'CHARTS', 'FLT FOLDER'].map((s) => ( -
@@ -67,7 +71,7 @@ export class OitFltOpsStatus extends DisplayComponent dataEntryFormat={new LongAlphanumericFormat()} mandatory={Subject.create(true)} - value={Subject.create(null)} + value={this.props.oit.laptopData.fltNumber} containerStyle="width: 600px; margin-right: 5px;" alignText="center" hEventConsumer={this.props.oit.hEventConsumer} @@ -79,30 +83,24 @@ export class OitFltOpsStatus extends DisplayComponent
FROM
- (0)} - values={ArraySubject.create(['----'])} - freeTextAllowed={false} - containerStyle="width: 250px;" - alignLabels="center" - numberOfDigitsForInputField={6} - tmpyActive={Subject.create(false)} + + dataEntryFormat={new AirportFormat()} + mandatory={Subject.create(true)} + value={this.props.oit.laptopData.fromAirport} + containerStyle="width: 250px; margin-right: 5px;" + alignText="center" hEventConsumer={this.props.oit.hEventConsumer} interactionMode={this.props.oit.interactionMode} />
TO
- (0)} - values={ArraySubject.create(['----'])} - freeTextAllowed={false} - containerStyle="width: 250px;" - alignLabels="center" - numberOfDigitsForInputField={6} - tmpyActive={Subject.create(false)} + + dataEntryFormat={new AirportFormat()} + mandatory={Subject.create(true)} + value={this.props.oit.laptopData.toAirport} + containerStyle="width: 250px; margin-right: 5px;" + alignText="center" hEventConsumer={this.props.oit.hEventConsumer} interactionMode={this.props.oit.interactionMode} /> diff --git a/fbw-a380x/src/systems/instruments/src/OIT/oit-display-unit.scss b/fbw-a380x/src/systems/instruments/src/OIT/oit-display-unit.scss index 0e771e4b38f..87a6c92c357 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/oit-display-unit.scss +++ b/fbw-a380x/src/systems/instruments/src/OIT/oit-display-unit.scss @@ -28,11 +28,13 @@ visibility: visible !important; text-anchor: middle; + font-family: "OIT"; } .EngineeringTestModeText { font-size: 21px; color: black; + font-family: "OIT"; } .AnsuFailed { @@ -50,4 +52,5 @@ fill: $display-amber; visibility: visible !important; text-anchor: middle; + font-family: "OIT"; } diff --git a/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss b/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss index 39c0531100e..9dbebf3de89 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss +++ b/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss @@ -3,7 +3,7 @@ .mfd-label { font-size: 20px; color: $display-white; - font-family: "OIT", monospace; + font-family: "OIT"; } .mfd-label.green { @@ -73,7 +73,7 @@ font-size: 22px; background-color: $display-mfd-darker-grey; color: $display-white; - font-family: "OIT", monospace; + font-family: "OIT"; text-align: center; align-items: center; justify-content: center; @@ -212,7 +212,7 @@ .mfd-page-selector-label { font-size: 28px; color: $display-white; - font-family: "OIT", monospace; + font-family: "OIT"; padding: 2px; text-align: center; display: inline; @@ -433,7 +433,7 @@ } .mfd-context-menu { - font-family: "OIT", monospace; + font-family: "OIT"; background-color: $display-mfd-darker-grey; border: 2px outset $display-light-grey; position: absolute; @@ -510,7 +510,7 @@ .mfd-input-field-text-input { background-color: $display-background; border: none; - font-family: "OIT", monospace; + font-family: "OIT"; font-size: 27px; line-height: 27px; color: $display-cyan; @@ -563,7 +563,7 @@ width: 18px; background-color: $display-cyan; color: $display-background; - font-family: "OIT", monospace; + font-family: "OIT"; font-size: 27px; text-align: center; vertical-align: center; @@ -659,7 +659,7 @@ .mfd-surv-label { font-size: 20px; color: $display-white; - font-family: "OIT", monospace; + font-family: "OIT"; text-align: center; margin-bottom: 2px; } @@ -685,7 +685,7 @@ font-size: 22px; padding-top: 3px; color: $display-white; - font-family: "OIT", monospace; + font-family: "OIT"; } .mfd-surv-status-indicator { @@ -737,5 +737,5 @@ .mfd-adsc-label-off { font-size: 20px; color: $display-dark-grey; - font-family: "OIT", monospace; + font-family: "OIT"; } diff --git a/fbw-common/src/systems/instruments/src/ND/shared/BtvRunwayInfo.tsx b/fbw-common/src/systems/instruments/src/ND/shared/BtvRunwayInfo.tsx index a370e6675f0..7f8ea8c7467 100644 --- a/fbw-common/src/systems/instruments/src/ND/shared/BtvRunwayInfo.tsx +++ b/fbw-common/src/systems/instruments/src/ND/shared/BtvRunwayInfo.tsx @@ -3,11 +3,11 @@ import { FSComponent, DisplayComponent, VNode, MappedSubject, ConsumerSubject } from '@microsoft/msfs-sdk'; -import { Arinc429LocalVarConsumerSubject, ArincEventBus, BtvData, FmsOansData } from '@flybywiresim/fbw-sdk'; +import { Arinc429LocalVarConsumerSubject, ArincEventBus, BtvData, FmsOansData, FmsData } from '@flybywiresim/fbw-sdk'; import { Layer } from '../../MsfsAvionicsCommon/Layer'; export class BtvRunwayInfo extends DisplayComponent<{ bus: ArincEventBus }> { - private readonly sub = this.props.bus.getArincSubscriber(); + private readonly sub = this.props.bus.getArincSubscriber(); private readonly fmsRwyIdent = ConsumerSubject.create(this.sub.on('fmsLandingRunway'), null); diff --git a/fbw-common/src/systems/instruments/src/OANC/OancControlPanelUtils.ts b/fbw-common/src/systems/instruments/src/OANC/OancControlPanelUtils.ts index b60886419ea..82305fa4bc5 100644 --- a/fbw-common/src/systems/instruments/src/OANC/OancControlPanelUtils.ts +++ b/fbw-common/src/systems/instruments/src/OANC/OancControlPanelUtils.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: GPL-3.0 import { ArraySubject, ConsumerSubject, DmsFormatter2, EventBus, Subject, UnitType } from '@microsoft/msfs-sdk'; -import { AmdbAirportSearchResult, FmsOansData } from '@flybywiresim/fbw-sdk'; +import { AmdbAirportSearchResult, FmsData } from '@flybywiresim/fbw-sdk'; export enum ControlPanelAirportSearchMode { Icao, @@ -57,7 +57,7 @@ export class ControlPanelStore { export class FmsDataStore { constructor(private bus: EventBus) { - const sub = this.bus.getSubscriber(); + const sub = this.bus.getSubscriber(); this.origin.setConsumer(sub.on('fmsOrigin')); this.destination.setConsumer(sub.on('fmsDestination')); this.alternate.setConsumer(sub.on('fmsAlternate')); diff --git a/fbw-common/src/systems/shared/src/publishers/OansBtv/FmsOansPublisher.ts b/fbw-common/src/systems/shared/src/publishers/OansBtv/FmsOansPublisher.ts index 316cb85bc10..28b15275f1d 100644 --- a/fbw-common/src/systems/shared/src/publishers/OansBtv/FmsOansPublisher.ts +++ b/fbw-common/src/systems/shared/src/publishers/OansBtv/FmsOansPublisher.ts @@ -9,16 +9,6 @@ import { Position } from '@turf/turf'; * Transmitted from FMS to OANS */ export interface FmsOansData { - /** (FMS -> OANS) Selected origin airport. */ - fmsOrigin: string; - /** (FMS -> OANS) Selected destination airport. */ - fmsDestination: string; - /** (FMS -> OANS) Selected alternate airport. */ - fmsAlternate: string; - /** (FMS -> OANS) Identifier of departure runway. */ - fmsDepartureRunway: string; - /** (FMS -> OANS) Identifier of landing runway selected through FMS. */ - fmsLandingRunway: string; /** Identifier of landing runway selected for BTV through OANS. */ oansSelectedLandingRunway: string; /** Arinc429: Length of landing runway selected for BTV through OANS, in meters. */ diff --git a/fbw-common/src/systems/shared/src/publishers/OansBtv/FmsPublisher.ts b/fbw-common/src/systems/shared/src/publishers/OansBtv/FmsPublisher.ts new file mode 100644 index 00000000000..5c08daf04a2 --- /dev/null +++ b/fbw-common/src/systems/shared/src/publishers/OansBtv/FmsPublisher.ts @@ -0,0 +1,20 @@ +// Copyright (c) 2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +/** + * Transmitted from FMS + */ +export interface FmsData { + /** Selected origin airport. */ + fmsOrigin: string | null; + /** Selected destination airport. */ + fmsDestination: string | null; + /** Selected alternate airport. */ + fmsAlternate: string | null; + /** Identifier of departure runway. */ + fmsDepartureRunway: string | null; + /** Identifier of landing runway selected through FMS. */ + fmsLandingRunway: string | null; + /** Flight number entered on INIT page */ + fmsFlightNumber: string | null; +} diff --git a/fbw-common/src/systems/shared/src/publishers/OansBtv/index.ts b/fbw-common/src/systems/shared/src/publishers/OansBtv/index.ts index d649b32cf33..350dce3ae7b 100644 --- a/fbw-common/src/systems/shared/src/publishers/OansBtv/index.ts +++ b/fbw-common/src/systems/shared/src/publishers/OansBtv/index.ts @@ -1,2 +1,3 @@ export * from './BtvPublisher'; +export * from './FmsPublisher'; export * from './FmsOansPublisher'; From 3b4d0c458923429c367686bc940421bf71fb4cc0 Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Tue, 21 Jan 2025 00:54:48 +0200 Subject: [PATCH 08/23] style tweaks --- .../src/MsfsAvionicsCommon/UiWidgets/InputField.tsx | 5 ++++- fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx | 2 -- .../instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx | 3 +++ fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/InputField.tsx b/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/InputField.tsx index 474723e4f04..ff3bfeb7c29 100644 --- a/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/InputField.tsx +++ b/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/UiWidgets/InputField.tsx @@ -52,6 +52,8 @@ interface InputFieldProps extends ComponentProps { hEventConsumer: Consumer; /** Kccu uses the HW keys, and doesn't focus input fields */ interactionMode: Subscribable; + /** Used for OIT, where placeholders are [] for mandatory fields */ + overrideEmptyMandatoryPlaceholder?: string; // inViewEvent?: Consumer; // Consider activating when we have a larger collision mesh for the screens } @@ -312,7 +314,8 @@ export class InputField extends DisplayComponent> { this.trailingUnit.set(unitTrailing ?? ''); if (this.props.mandatory?.get() && !this.props.inactive?.get() && !this.props.disabled?.get()) { - this.textInputRef.instance.innerHTML = formatted?.replace(/-/gi, '\u25AF') ?? ''; + this.textInputRef.instance.innerHTML = + formatted?.replace(/-/gi, this.props.overrideEmptyMandatoryPlaceholder ?? '\u25AF') ?? ''; } else { this.textInputRef.instance.innerText = formatted ?? ''; } diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx index d6c7d5463a1..78bbb66bc03 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx @@ -75,7 +75,6 @@ export abstract class OitHeader extends DisplayComponent { }, ]} idPrefix={`${this.props.uiService.captOrFo}_OIT_menu_menu`} - containerStyle="height: 60px;" dropdownMenuStyle="width: 300px;" />
{this.props.uiService.activeUri.map((uri) => heading[uri.uri] ?? 'FIXME')}
@@ -97,7 +96,6 @@ export abstract class OitHeader extends DisplayComponent { { label: 'CLOSE APPLICATION', action: () => {}, disabled: true, separatorBelow: true }, ]} idPrefix={`${this.props.uiService.captOrFo}_OIT_menu_functions`} - containerStyle="height: 60px" dropdownMenuStyle="width: 300px;" />
0 MSG
diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx index 74492f469e9..2db4b2efb53 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx @@ -76,6 +76,7 @@ export class OitFltOpsStatus extends DisplayComponent alignText="center" hEventConsumer={this.props.oit.hEventConsumer} interactionMode={this.props.oit.interactionMode} + overrideEmptyMandatoryPlaceholder="[]" />
@@ -91,6 +92,7 @@ export class OitFltOpsStatus extends DisplayComponent alignText="center" hEventConsumer={this.props.oit.hEventConsumer} interactionMode={this.props.oit.interactionMode} + overrideEmptyMandatoryPlaceholder="[]" />
TO @@ -103,6 +105,7 @@ export class OitFltOpsStatus extends DisplayComponent alignText="center" hEventConsumer={this.props.oit.hEventConsumer} interactionMode={this.props.oit.interactionMode} + overrideEmptyMandatoryPlaceholder="[]" />
diff --git a/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss b/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss index 9dbebf3de89..ab0d20f397b 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss +++ b/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss @@ -144,7 +144,7 @@ flex: 1; justify-content: center; align-items: center; - height: 40px; + height: 60px; } .mfd-dropdown-arrow { From 64646d4e429ec94a5d2249590b46b5ad23f65484 Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Tue, 21 Jan 2025 01:04:57 +0200 Subject: [PATCH 09/23] update PERF pages --- fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx | 4 ++-- .../instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx | 2 ++ .../src/OIT/Pages/flt-ops/OitFltOpsPerformance.tsx | 5 +---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx index 78bbb66bc03..2384dc25444 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx @@ -51,9 +51,9 @@ export abstract class OitHeader extends DisplayComponent { disabled: true, separatorBelow: true, }, - { label: 'T.O PERF', action: () => this.props.uiService.navigateTo('flt-ops/to-perf') }, + { label: 'T.O PERF', action: () => this.props.uiService.navigateTo('flt-ops/to-perf'), disabled: true }, { label: 'LOADSHEET', action: () => this.props.uiService.navigateTo('flt-ops/loadsheet'), disabled: true }, - { label: 'LDG PERF', action: () => this.props.uiService.navigateTo('flt-ops/ldg-perf') }, + { label: 'LDG PERF', action: () => this.props.uiService.navigateTo('flt-ops/ldg-perf'), disabled: true }, { label: 'IN-FLT PERF', action: () => this.props.uiService.navigateTo('flt-ops/in-flt-perf'), diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx index b06067074ce..3352ad21dc6 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx @@ -56,6 +56,7 @@ export class OitFltOpsMenuPage extends DisplayComponent label={'T.O PERF'} containerStyle="width: 300px; margin-bottom: 20px" onClick={() => this.props.oit.uiService.navigateTo('flt-ops/to-perf')} + disabled={Subject.create(true)} />
{/* end page content */} From 958d84a8d09d3a2aaf50110e74f80f1b2c4a4862 Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Tue, 21 Jan 2025 02:53:34 +0200 Subject: [PATCH 10/23] first try at charts --- fbw-a380x/mach.config.js | 1 + .../FlyByWire_A380_842/panel/panel.cfg | 2 + .../src/systems/instruments/src/OIT/OIT.tsx | 15 +- .../systems/instruments/src/OIT/OitHeader.tsx | 2 +- .../instruments/src/OIT/OitPageDirectory.tsx | 5 + .../OIT/Pages/flt-ops/OitFltOpsEfbOverlay.tsx | 37 +++ .../src/OITlegacy/Components/Button.tsx | 90 ------ .../src/OITlegacy/Components/Dropdown.tsx | 296 ------------------ .../src/OITlegacy/Components/Layer.tsx | 7 - .../instruments/src/OITlegacy/Efb.scss | 10 + .../instruments/src/OITlegacy/OitLegacy.tsx | 146 +++++++++ .../OITlegacy/OnboardInformationTerminal.tsx | 180 ----------- .../src/OITlegacy/Pages/FlightOps/LoadBox.tsx | 11 - .../src/OITlegacy/Pages/FlightOps/Login.tsx | 30 -- .../src/OITlegacy/Pages/FlightOps/Menu.tsx | 11 - .../src/OITlegacy/Pages/FlightOps/STS.tsx | 91 ------ .../instruments/src/OITlegacy/Pages/index.tsx | 6 - .../instruments/src/OITlegacy/config.json | 13 +- .../instruments/src/OITlegacy/index.tsx | 10 +- .../instruments/src/OITlegacy/tsconfig.json | 15 +- 20 files changed, 241 insertions(+), 737 deletions(-) create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsEfbOverlay.tsx delete mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Button.tsx delete mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Dropdown.tsx delete mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Layer.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/Efb.scss create mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx delete mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/OnboardInformationTerminal.tsx delete mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/LoadBox.tsx delete mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/Login.tsx delete mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/Menu.tsx delete mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/STS.tsx delete mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/index.tsx diff --git a/fbw-a380x/mach.config.js b/fbw-a380x/mach.config.js index 6b3d4bfd318..25792b37d9a 100644 --- a/fbw-a380x/mach.config.js +++ b/fbw-a380x/mach.config.js @@ -41,6 +41,7 @@ module.exports = { reactInstrument('BAT'), reactInstrument('EFB', ['/Pages/VCockpit/Instruments/Shared/Map/MapInstrument.html']), reactInstrument('ISISlegacy'), + reactInstrument('OITlegacy'), reactInstrument('RTPI'), reactInstrument('SD'), ], diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg index 904ee683606..6ae64bf92cd 100644 --- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg +++ b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg @@ -155,6 +155,7 @@ pixel_size=1333,1000 texture=$SCREEN_OIT_LEFT htmlgauge00=A380X/OIT/oit.html?Index=1, 0,0,1333,1000 +htmlgauge01=A380X/OITlegacy/oitlegacy.html, 0,0,1333,1000 [VCockpit20] size_mm=1333,1000 @@ -162,6 +163,7 @@ pixel_size=1333,1000 texture=$SCREEN_OIT_RIGHT htmlgauge00=A380X/OIT/oit.html?Index=2, 0,0,1333,1000 +htmlgauge01=A380X/OITlegacy/oitlegacy.html, 0,0,1333,1000 [VCockpit21] size_mm=0,0 diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx index b949010f64e..3ab586e10e0 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx @@ -7,6 +7,7 @@ import { DisplayComponent, EventBus, FSComponent, + SimVarValueType, Subject, VNode, } from '@microsoft/msfs-sdk'; @@ -18,7 +19,7 @@ import { OitNotFound } from 'instruments/src/OIT/pages/OitNotFound'; import { pageForUrl } from 'instruments/src/OIT/OitPageDirectory'; import { OitHeader } from 'instruments/src/OIT/OitHeader'; import { OitFooter } from 'instruments/src/OIT/OitFooter'; -import { OitDisplayUnit, OitDisplayUnitID } from 'instruments/src/OIT/OitDisplayUnit'; +import { getDisplayIndex, OitDisplayUnit, OitDisplayUnitID } from 'instruments/src/OIT/OitDisplayUnit'; import { FailuresConsumer } from '@flybywiresim/fbw-sdk'; import { OisLaptop } from 'instruments/src/OIT/OisLaptop'; import { InteractionMode } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/InputField'; @@ -70,6 +71,18 @@ export class OIT extends DisplayComponent { this.uiService.activeUri.sub((uri) => { this.activeUriChanged(uri); + + // Activate EFB overlay if on charts or flt-folder page + SimVar.SetSimVarValue( + `L:A32NX_OIS_${getDisplayIndex()}_SHOW_CHARTS`, + SimVarValueType.Bool, + uri.uri === 'flt-ops/charts', // uri.uri === 'flt-ops/charts' + ); + SimVar.SetSimVarValue( + `L:A32NX_OIS_${getDisplayIndex()}_SHOW_OFP`, + SimVarValueType.Bool, + uri.uri === 'flt-ops/flt-folder', + ); }); this.uiService.navigateTo('flt-ops/sts'); // should be /sts diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx index 2384dc25444..7f8ded8c33f 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitHeader.tsx @@ -42,7 +42,7 @@ export abstract class OitHeader extends DisplayComponent { }, { label: 'TERML CHART', - action: () => this.props.uiService.navigateTo('flt-ops/terml-chart'), + action: () => this.props.uiService.navigateTo('flt-ops/charts'), separatorBelow: true, }, { diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitPageDirectory.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitPageDirectory.tsx index 477f220e3d4..d57b927d610 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitPageDirectory.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitPageDirectory.tsx @@ -3,6 +3,7 @@ import { EventBus, FSComponent, VNode } from '@microsoft/msfs-sdk'; import { OIT } from 'instruments/src/OIT/OIT'; +import { OitFltOpsEfbOverlay } from 'instruments/src/OIT/pages/flt-ops/OitFltOpsEfbOverlay'; import { OitFltOpsMenuPage } from 'instruments/src/OIT/pages/flt-ops/OitFltOpsMenuPage'; import { OitFltOpsPerformance } from 'instruments/src/OIT/pages/flt-ops/OitFltOpsPerformance'; import { OitFltOpsStatus } from 'instruments/src/OIT/pages/flt-ops/OitFltOpsStatus'; @@ -20,6 +21,10 @@ export function pageForUrl(url: string, bus: EventBus, oit: OIT): VNode { return ; case 'flt-ops/ldg-perf': return ; + case 'flt-ops/charts': + return ; + case 'flt-ops/flt-folder': + return ; default: return ; diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsEfbOverlay.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsEfbOverlay.tsx new file mode 100644 index 00000000000..1207e2d33cc --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsEfbOverlay.tsx @@ -0,0 +1,37 @@ +// Copyright (c) 2025 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { DisplayComponent, FSComponent, Subscription, VNode } from '@microsoft/msfs-sdk'; +import { AbstractOitPageProps } from 'instruments/src/OIT/OIT'; + +interface OitFltOpsEfbOverlayPageProps extends AbstractOitPageProps {} + +export class OitFltOpsEfbOverlay extends DisplayComponent { + // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. + private subs = [] as Subscription[]; + + public onAfterRender(node: VNode): void { + super.onAfterRender(node); + } + + public destroy(): void { + // Destroy all subscriptions to remove all references to this instance. + this.subs.forEach((x) => x.destroy()); + + super.destroy(); + } + + render(): VNode { + return ( + <> + {/* begin page content */} +
+
+ EFB OVERLAY FAILED +
+
+ {/* end page content */} + + ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Button.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Button.tsx deleted file mode 100644 index 137983c78a5..00000000000 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Button.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import React, { Children, FC, isValidElement } from 'react'; -import { useHover } from 'use-events'; -import { Layer } from '@instruments/common/utils'; - -type ButtonProps = { - x?: number; - y?: number; - width?: number; - height?: number; - onClick?: () => void; - fill?: string; - disabled?: boolean; - highlighted?: boolean; - gradient?: boolean; - forceHover?: boolean; -}; -const strokeWidth = 3; -export const Button: FC = ({ - x = 0, - y = 0, - width = 0, - height = 41, - children, - onClick, - disabled, - fill, - highlighted, - gradient, - forceHover, -}) => { - const [hovered, hoverProps] = useHover(); - - return ( - - - - - {gradient && ( - <> - - - - - )} - - - {Children.map(children, (child) => { - if (isValidElement(child) && child.type !== 'tspan') { - return child; - } - return ( - - {child} - - ); - })} - - ); -}; diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Dropdown.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Dropdown.tsx deleted file mode 100644 index 4f18f0bd8cc..00000000000 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Dropdown.tsx +++ /dev/null @@ -1,296 +0,0 @@ -import React, { - Children, - isValidElement, - ReactNode, - useState, - FC, - useRef, - useEffect, - Dispatch, - SetStateAction, -} from 'react'; -import { useHover } from 'use-events'; -import { useHistory, useRouteMatch } from 'react-router-dom'; -import { useInputManager } from '@instruments/common/input'; -import { Layer } from './Layer'; -import { Button } from './Button'; - -type DropdownProps = { - x: number; - y?: number; - width?: number; - height?: number; - fill?: string; - selectable?: boolean; - title: string | ReactNode; - dropDownWidth?: number; - active?: boolean; - disabled?: boolean; - scrollable?: boolean; - clipItems?: boolean; - maxHeight?: number; -}; -const scrollBarWidth = 15; -let lastMousePosition = 0; - -// eslint-disable-next-line max-len -export const Dropdown: FC = ({ - x, - y = 0, - width = 192, - height = 60, - dropDownWidth = 192, - fill, - selectable, - title, - children, - active, - disabled, - scrollable, - clipItems = true, - maxHeight = Infinity, -}) => { - const [open, setOpen] = useState(false); - const textRef = useRef(null); - const [textBbox, setTextBbox] = useState(); - const [scrollPosition, setScrollPosition] = useState(0); - const [clipPathId] = useState((Math.random() * 1000).toString()); - const [hovered, hoverProps] = useHover(); - const childYPositions = useRef([2]); - - Children.forEach(children, (child, index) => { - if (isValidElement(child)) { - console.log(childYPositions.current); - childYPositions.current[index + 1] = child.props.height + childYPositions.current[index]; - } - }); - - useEffect(() => setTextBbox(textRef.current?.getBBox()), [textRef]); - if (open && disabled) setOpen(false); - - let totalHeight = 1; - Children.forEach(children, (child: React.ReactElement) => { - totalHeight += child.props.height ?? 0; - }); - - return ( - - - )} - - - - - {open && ( - setOpen(false)}> - - - {Children.map(children, (child, index) => { - if (isValidElement(child)) { - return React.cloneElement(child, { - y: height + childYPositions.current[index], - width: child.props.width - ? child.props.width - : (dropDownWidth ?? width) - (scrollable ? scrollBarWidth + 3 : 0), - centered: child.props.centered ?? !selectable, - }); - } - return <>; - })} - - {scrollable && ( - - )} - - )} - - ); -}; - -export type ScrollBarProps = { - x: number; - y: number; - width?: number; - maxHeight: number; - totalChildHeight: number; - scrollPosition: number; - setScrollPosition: Dispatch>; -}; -export const ScrollBar: FC = ({ - x, - y, - width = 48, - maxHeight, - totalChildHeight, - scrollPosition, - setScrollPosition, -}) => { - const inputManager = useInputManager(); - const [dragging, setDragging] = useState(false); - const [hovered, hoverRef] = useHover(); - const handleMouseDown = (e: any) => { - inputManager.setMouseUpHandler(handleMouseUp); - inputManager.setMouseMoveHandler(handleMouseMove); - lastMousePosition = e.pageY; - }; - - const handleMouseUp = () => { - setDragging(false); - inputManager.clearHandlers(); - }; - - const handleMouseMove = (e: MouseEvent) => { - const delta = e.pageY - lastMousePosition; - setScrollPosition((p) => { - let newPos = p + delta; - newPos = Math.max(0, newPos); - newPos = Math.min(newPos, totalChildHeight - maxHeight); - return newPos; - }); - lastMousePosition = e.pageY; - }; - - return ( - - ); -}; - -type DropdownItemProps = { - y?: number; - onSelect?: () => void; - width?: number; - height?: number; - centered?: boolean; -}; - -export const DropdownItem: FC = ({ y = 0, onSelect, width = 0, height = 0, centered, children }) => { - const [hovered, hoverProps] = useHover(); - - return ( - - - - {children} - - - ); -}; - -export const DropdownLink: FC = (props) => { - const history = useHistory(); - const { path } = useRouteMatch(); - return ( - history.push(path + props.link)}> - {props.children} - - ); -}; - -export const DropdownDivider = ({ y = 0, width = 0, height = 1 }: DropdownItemProps) => ( - - - -); diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Layer.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Layer.tsx deleted file mode 100644 index c1fa1f25d54..00000000000 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/Components/Layer.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import React, { SVGProps, FC } from 'react'; - -export const Layer: FC & { angle?: number }> = (props) => ( - - {props.children} - -); diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/Efb.scss b/fbw-a380x/src/systems/instruments/src/OITlegacy/Efb.scss new file mode 100644 index 00000000000..7eba995af69 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OITlegacy/Efb.scss @@ -0,0 +1,10 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +$font-file-path: '/Fonts/fbw-a380x/EFB'; + +@import "../../../../../../fbw-common/src/systems/instruments/src/EFB/Assets/Efb.scss"; + +.nopointer { + pointer-events: none; +} diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx new file mode 100644 index 00000000000..afbdf6aed6d --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx @@ -0,0 +1,146 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import React, { useEffect, useState } from 'react'; +import { + AircraftContext, + ErrorFallback, + EventBusContextProvider, + FailuresOrchestratorProvider, + ModalProvider, + PowerContext, + PowerStates, + store, +} from '@flybywiresim/flypad'; + +import './Efb.scss'; +import { EventBus } from '@microsoft/msfs-sdk'; +import { NavigraphAuthProvider } from '../../../../../../fbw-common/src/systems/instruments/src/react/navigraph'; +import { Navigation } from '../../../../../../fbw-common/src/systems/instruments/src/EFB/Navigation/Navigation'; +import { Provider } from 'react-redux'; +import { TroubleshootingContextProvider } from '../../../../../../fbw-common/src/systems/instruments/src/EFB/TroubleshootingContext'; +import { ErrorBoundary } from 'react-error-boundary'; +import { MemoryRouter as Router } from 'react-router'; +import { useSimVar } from '@flybywiresim/fbw-sdk'; + +export const getDisplayIndex = () => { + const url = Array.from(document.querySelectorAll('vcockpit-panel > *')) + ?.find((it) => it.tagName.toLowerCase() !== 'wasm-instrument') + ?.getAttribute('url'); + + return url ? parseInt(url.substring(url.length - 1), 10) : 0; +}; + +export interface OitEfbWrapperProps { + eventBus: EventBus; +} + +export const OitEfbWrapper: React.FC = ({ eventBus }) => { + const [powerState, setPowerState] = useState(PowerStates.LOADED); + + const [showCharts] = useSimVar(`L:A32NX_OIS_${getDisplayIndex()}_SHOW_CHARTS`, 'Bool', 100); + const [showOfp] = useSimVar(`L:A32NX_OIS_${getDisplayIndex()}_SHOW_OFP`, 'Bool', 100); + + const showEfbOverlay = showCharts === 1 || showOfp === 1; + document.getElementsByTagName('a380x-oitlegacy')[0].classList.toggle('nopointer', !showEfbOverlay); + + const [err, setErr] = useState(false); + + useEffect(() => { + document + .getElementsByTagName('a380x-oitlegacy')[0] + .setAttribute('style', 'position: absolute; left: 10px; top: 75px; width: 1313px; height: 860px;'); + }, []); + + return ( + + + + + setErr(false)} resetKeys={[err]}> + + + + + + {showEfbOverlay && ( +
+
+
+ {showCharts === 1 && } +
+
+
+ )} +
+
+
+
+
+
+
+
+
+
+ ); +}; diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/OnboardInformationTerminal.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/OnboardInformationTerminal.tsx deleted file mode 100644 index 762bd7a5c3c..00000000000 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/OnboardInformationTerminal.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import React, { createContext, useContext } from 'react'; -import { useHistory, useRouteMatch, Redirect, Route, Switch } from 'react-router-dom'; -import { Pages } from './Pages'; - -import './index.scss'; -import { Dropdown, DropdownDivider, DropdownItem, DropdownLink } from './Components/Dropdown'; -import { Button } from './Components/Button'; - -enum OITDisplayPosition { - Captain = 'CAPTAIN', - FO = 'F/O', - Backup = 'BACKUP', -} -type OITContextType = { - displayPosition: OITDisplayPosition; -}; - -export const OITContext = createContext(undefined as any); - -export const useOITContext = () => useContext(OITContext); - -export const OnboardInformationTerminal: React.FC = () => { - const history = useHistory(); - const { path } = useRouteMatch(); - return ( - - - - - {/* fix prefixes for this */} - - {history.location.pathname.toUpperCase().substring(path.length)} - - - - - - - ); -}; - -const PagesContainer: React.FC = () => ( - - - - - - - - - - - -); - -const MenuDropdown: React.FC = () => ( - <> - - - FLT OPS MENU - - - - - - FLT FOLDER - - - TERML CHART - - - OPS LIBRARY - - - - - - T.O PERF - - - LOADSHEET - - - LDG PERF - - - IN-FLT PERF - - - - - - FLT OPS STS - - - LOAD BOX - - - EXPORT BOX - - - - - - EXIT SESSION - - - -); - -const FunctionDropdown: React.FC = () => ( - <> - - - HOME - - - PREVIOUS - - - NEXT - - - - - - PRINT - - - STORE - - - UPDATE - - - - - - UNDO - - - REDO - - - - - - HELP - - - - - - HIDE SWITCHING BAR - - - - - - CLOSE APPLICATION - - - -); - -const MessageDropdown: React.FC = () => ( - <> - - 0 MSG - - - - MESSAGE - - - - -); diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/LoadBox.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/LoadBox.tsx deleted file mode 100644 index 42c5b5297d6..00000000000 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/LoadBox.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React, { FC } from 'react'; - -export const LoadBoxPage: FC = () => { - return ( - <> - - LOAD BOX - - - ); -}; diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/Login.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/Login.tsx deleted file mode 100644 index 4b85242516c..00000000000 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/Login.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React, { FC } from 'react'; -import { useOITContext } from '../../OnboardInformationTerminal'; -import { Button } from '../../Components/Button'; - -export const LoginPage: FC = () => { - const { displayPosition } = useOITContext(); - return ( - <> - - FLT OPS Domain - - - Login Page - - - {displayPosition} - - - - - - ); -}; diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/Menu.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/Menu.tsx deleted file mode 100644 index 25b4b3125f4..00000000000 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/Menu.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React, { FC } from 'react'; - -export const MenuPage: FC = () => { - return ( - <> - - FLT OPS MENU - - - ); -}; diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/STS.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/STS.tsx deleted file mode 100644 index e9e37164cbe..00000000000 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/FlightOps/STS.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import React, { FC } from 'react'; -import { Button } from '../../Components/Button'; -import { Dropdown, DropdownItem } from '../../Components/Dropdown'; - -export const STSPage: FC = () => { - return ( - <> - {/* Main Section */} - - - - - - - - - - - - ); -}; - -const AircraftInfo: React.FC = () => ( - <> - - ACFT REGISTRATION - - - - - idk what goes here - - - - - - - MSN - - - 9804 - - - - OIS VERSION - - - 14-JUL-08 V2.0 - - -); - -const NavInfo: React.FC = () => ( - <> - - ACTIVE - - - CHARTS - - - 07-AUG-08 27-AUG-08 - - -); - -const FlightInfo: React.FC = () => ( - <> - - FLT NBR - - - - AIB123 - - - {/* The style for this dropdown is not finished, don't have enough references to be able to correctly stylize it */} - - FROM - - - - - TO - - - -); diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/index.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/index.tsx deleted file mode 100644 index 0c2aef562df..00000000000 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/Pages/index.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { LoginPage } from './FlightOps/Login'; -import { STSPage } from './FlightOps/STS'; -import { MenuPage } from './FlightOps/Menu'; -import { LoadBoxPage } from './FlightOps/LoadBox'; - -export const Pages = { LoginPage, STSPage, MenuPage, LoadBoxPage }; diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/config.json b/fbw-a380x/src/systems/instruments/src/OITlegacy/config.json index dd22b8a2bdb..4a92b27227a 100644 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/config.json +++ b/fbw-a380x/src/systems/instruments/src/OITlegacy/config.json @@ -1,8 +1,15 @@ { "index": "./index.tsx", + "name": "OITlegacy", "isInteractive": true, "dimensions": { - "width": 1024, - "height": 768 - } + "width": 1313, + "height": 860 + }, + "additionalImports": [ + "/Pages/VCockpit/Instruments/Shared/Map/MapInstrument.html" + ], + "extraDeps": [ + "/fbw-common/src/systems/instruments/src/EFB" + ] } diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/index.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/index.tsx index a196268e2fd..d9df60824d6 100644 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/index.tsx +++ b/fbw-a380x/src/systems/instruments/src/OITlegacy/index.tsx @@ -1,17 +1,13 @@ import React from 'react'; import { getRootElement } from '@instruments/common/defaults'; -import { HashRouter as Router } from 'react-router-dom'; import ReactDOM from 'react-dom'; import { renderTarget } from '../util.js'; -import { OnboardInformationTerminal } from './OnboardInformationTerminal'; import { render } from '../Common'; +import { OitEfbWrapper } from 'instruments/src/OITlegacy/OitLegacy.js'; +import { EventBus } from '@microsoft/msfs-sdk'; if (renderTarget) { - render( - - - , - ); + render(); } getRootElement().addEventListener('unload', () => { diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/tsconfig.json b/fbw-a380x/src/systems/instruments/src/OITlegacy/tsconfig.json index befb760d707..269eff19f0d 100644 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/tsconfig.json +++ b/fbw-a380x/src/systems/instruments/src/OITlegacy/tsconfig.json @@ -1,3 +1,12 @@ - { - "extends": "../../tsconfig.json" - } \ No newline at end of file +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "resolveJsonModule" : true, + "paths": { + "@instruments/common/*": ["./instruments/src/Common/*"], + "@flybywiresim/fbw-sdk": ["../../../fbw-common/src/systems/index.ts"], + "@flybywiresim/flypad": ["../../../fbw-common/src/systems/instruments/src/EFB/index.ts"], + "@flybywiresim/checklists": ["../../../fbw-common/src/systems/shared/src/checklists/index.ts"] + } + } +} From 6579f63a2bb38d04e7d2bc591e200c96968c73bb Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Tue, 21 Jan 2025 03:05:41 +0200 Subject: [PATCH 11/23] squeeze space --- fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx index afbdf6aed6d..8d90fa598c1 100644 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx +++ b/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx @@ -126,9 +126,7 @@ export const OitEfbWrapper: React.FC = ({ eventBus }) => { }} >
-
- {showCharts === 1 && } -
+
{showCharts === 1 && }
)} From 555aa69a2ae2fdabbd97e0c68add9d725ab5279c Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Tue, 21 Jan 2025 03:47:05 +0200 Subject: [PATCH 12/23] weird style import to override EFB styles in OIT + OFP page --- .../systems/instruments/src/OIT/style.scss | 8 ++++++ .../instruments/src/OIT/widget-style.scss | 4 ++- .../instruments/src/OITlegacy/Efb.scss | 8 ------ .../instruments/src/OITlegacy/OitLegacy.tsx | 25 +++++++++++++++++-- .../instruments/src/OITlegacy/index.scss | 3 --- 5 files changed, 34 insertions(+), 14 deletions(-) delete mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/index.scss diff --git a/fbw-a380x/src/systems/instruments/src/OIT/style.scss b/fbw-a380x/src/systems/instruments/src/OIT/style.scss index 0cdcd479d3f..fc689940b39 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/style.scss +++ b/fbw-a380x/src/systems/instruments/src/OIT/style.scss @@ -172,3 +172,11 @@ align-items: center; flex-direction: row; } + +$font-file-path: '/Fonts/fbw-a380x/EFB'; + +@import "../../../../../../fbw-common/src/systems/instruments/src/EFB/Assets/Efb.scss"; + +.nopointer { + pointer-events: none; +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss b/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss index ab0d20f397b..2a7b36d8e6d 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss +++ b/fbw-a380x/src/systems/instruments/src/OIT/widget-style.scss @@ -91,7 +91,9 @@ } .mfd-button.disabled { - color: $display-dark-grey; + color: $display-dark-grey !important; + --color-text: $display-dark-grey; + color: var(--color-text); } .mfd-button.selected { diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/Efb.scss b/fbw-a380x/src/systems/instruments/src/OITlegacy/Efb.scss index 7eba995af69..2242d35cf30 100644 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/Efb.scss +++ b/fbw-a380x/src/systems/instruments/src/OITlegacy/Efb.scss @@ -1,10 +1,2 @@ // Copyright (c) 2023-2024 FlyByWire Simulations // SPDX-License-Identifier: GPL-3.0 - -$font-file-path: '/Fonts/fbw-a380x/EFB'; - -@import "../../../../../../fbw-common/src/systems/instruments/src/EFB/Assets/Efb.scss"; - -.nopointer { - pointer-events: none; -} diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx index 8d90fa598c1..f8eff1cf166 100644 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx +++ b/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx @@ -10,6 +10,9 @@ import { ModalProvider, PowerContext, PowerStates, + setAirframeInfo, + setCabinInfo, + setFlypadInfo, store, } from '@flybywiresim/flypad'; @@ -21,7 +24,8 @@ import { Provider } from 'react-redux'; import { TroubleshootingContextProvider } from '../../../../../../fbw-common/src/systems/instruments/src/EFB/TroubleshootingContext'; import { ErrorBoundary } from 'react-error-boundary'; import { MemoryRouter as Router } from 'react-router'; -import { useSimVar } from '@flybywiresim/fbw-sdk'; +import { UniversalConfigProvider, useSimVar } from '@flybywiresim/fbw-sdk'; +import { Dispatch } from '../../../../../../fbw-common/src/systems/instruments/src/EFB/Dispatch/Dispatch'; export const getDisplayIndex = () => { const url = Array.from(document.querySelectorAll('vcockpit-panel > *')) @@ -46,6 +50,20 @@ export const OitEfbWrapper: React.FC = ({ eventBus }) => { const [err, setErr] = useState(false); + useEffect(() => { + UniversalConfigProvider.fetchAirframeInfo(process.env.AIRCRAFT_PROJECT_PREFIX, process.env.AIRCRAFT_VARIANT).then( + (info) => store.dispatch(setAirframeInfo(info)), + ); + + UniversalConfigProvider.fetchFlypadInfo(process.env.AIRCRAFT_PROJECT_PREFIX, process.env.AIRCRAFT_VARIANT).then( + (info) => store.dispatch(setFlypadInfo(info)), + ); + + UniversalConfigProvider.fetchCabinInfo(process.env.AIRCRAFT_PROJECT_PREFIX, process.env.AIRCRAFT_VARIANT).then( + (info) => store.dispatch(setCabinInfo(info)), + ); + }, []); + useEffect(() => { document .getElementsByTagName('a380x-oitlegacy')[0] @@ -126,7 +144,10 @@ export const OitEfbWrapper: React.FC = ({ eventBus }) => { }} >
-
{showCharts === 1 && }
+
+ {showCharts === 1 && } + {showOfp === 1 && } +
)} diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/index.scss b/fbw-a380x/src/systems/instruments/src/OITlegacy/index.scss deleted file mode 100644 index 6f83e3816b4..00000000000 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/index.scss +++ /dev/null @@ -1,3 +0,0 @@ -text { - font-family: "Helvetica"; -} From 6a3284ead38abde2720ca8decd9f8f3c4b69640b Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Tue, 21 Jan 2025 17:39:53 +0200 Subject: [PATCH 13/23] fix exports --- .../src/systems/instruments/src/OITlegacy/OitLegacy.tsx | 9 ++++----- fbw-common/src/systems/instruments/src/EFB/index.ts | 3 +++ fbw-common/src/systems/instruments/src/react/index.ts | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx index f8eff1cf166..ad808705b7b 100644 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx +++ b/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx @@ -4,28 +4,27 @@ import React, { useEffect, useState } from 'react'; import { AircraftContext, + Dispatch, ErrorFallback, EventBusContextProvider, FailuresOrchestratorProvider, ModalProvider, + Navigation, PowerContext, PowerStates, setAirframeInfo, setCabinInfo, setFlypadInfo, store, + TroubleshootingContextProvider, } from '@flybywiresim/flypad'; import './Efb.scss'; import { EventBus } from '@microsoft/msfs-sdk'; -import { NavigraphAuthProvider } from '../../../../../../fbw-common/src/systems/instruments/src/react/navigraph'; -import { Navigation } from '../../../../../../fbw-common/src/systems/instruments/src/EFB/Navigation/Navigation'; import { Provider } from 'react-redux'; -import { TroubleshootingContextProvider } from '../../../../../../fbw-common/src/systems/instruments/src/EFB/TroubleshootingContext'; import { ErrorBoundary } from 'react-error-boundary'; import { MemoryRouter as Router } from 'react-router'; -import { UniversalConfigProvider, useSimVar } from '@flybywiresim/fbw-sdk'; -import { Dispatch } from '../../../../../../fbw-common/src/systems/instruments/src/EFB/Dispatch/Dispatch'; +import { NavigraphAuthProvider, UniversalConfigProvider, useSimVar } from '@flybywiresim/fbw-sdk'; export const getDisplayIndex = () => { const url = Array.from(document.querySelectorAll('vcockpit-panel > *')) diff --git a/fbw-common/src/systems/instruments/src/EFB/index.ts b/fbw-common/src/systems/instruments/src/EFB/index.ts index 9b6f797f90e..0377cb85e02 100644 --- a/fbw-common/src/systems/instruments/src/EFB/index.ts +++ b/fbw-common/src/systems/instruments/src/EFB/index.ts @@ -11,7 +11,9 @@ export * from './Assets/NoseOutline'; export * from './Assets/FuelOutline'; export * from './Assets/OverWingOutline'; export * from './Assets/A380SeatOutlineBg'; +export * from './Dispatch/Dispatch'; export * from './Localization/translation'; +export * from './Navigation/Navigation'; export * from './Settings/Migration'; export * from './Settings/Settings'; export * from './Settings/sync'; @@ -21,6 +23,7 @@ export * from './Store/features/simBrief'; export * from './Store/features/payload'; export * from './Store/features/config'; export * from './Store/store'; +export * from './TroubleshootingContext'; export * from './UtilComponents/BingMap'; export * from './UtilComponents/Card/Card'; export * from './UtilComponents/Form/Select'; diff --git a/fbw-common/src/systems/instruments/src/react/index.ts b/fbw-common/src/systems/instruments/src/react/index.ts index deb10379af0..6b8197f23d2 100644 --- a/fbw-common/src/systems/instruments/src/react/index.ts +++ b/fbw-common/src/systems/instruments/src/react/index.ts @@ -1,6 +1,7 @@ export * from './arinc429'; export * from './bitFlags'; export * from './hooks'; +export * from './navigraph'; export * from './persistence'; export * from './simVars'; export * from './useInterval'; From 560d6691141610d991ccc1d2ffb54eea82c75fdc Mon Sep 17 00:00:00 2001 From: Florian Gross <63071941+flogross89@users.noreply.github.com> Date: Wed, 22 Jan 2025 02:35:05 +0200 Subject: [PATCH 14/23] FMS/simbrief sync --- .../src/systems/instruments/src/OIT/OIT.tsx | 40 +++-- .../src/OIT/OisInternalPublisher.ts | 7 + .../systems/instruments/src/OIT/OisLaptop.ts | 8 +- .../instruments/src/OIT/OitDisplayUnit.tsx | 15 +- .../src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx | 3 +- .../instruments/src/OITlegacy/OitLegacy.tsx | 138 +++++++++++++++--- .../instruments/src/OITlegacy/utils.ts | 15 ++ .../src/systems/instruments/src/EFB/index.ts | 1 + 8 files changed, 185 insertions(+), 42 deletions(-) create mode 100644 fbw-a380x/src/systems/instruments/src/OIT/OisInternalPublisher.ts create mode 100644 fbw-a380x/src/systems/instruments/src/OITlegacy/utils.ts diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx index 3ab586e10e0..ebcc10c5166 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx @@ -7,6 +7,7 @@ import { DisplayComponent, EventBus, FSComponent, + MappedSubject, SimVarValueType, Subject, VNode, @@ -56,12 +57,16 @@ export class OIT extends DisplayComponent { return this.props.laptop.data; } + private readonly operationMode = Subject.create('flt-ops'); + public readonly hEventConsumer = this.props.bus.getSubscriber().on('kbdKeyEvent'); public readonly interactionMode = Subject.create(InteractionMode.Touchscreen); private readonly topRef = FSComponent.createRef(); + private readonly displayUnitRef = FSComponent.createRef(); + private readonly activePageRef = FSComponent.createRef(); private activePage: VNode = (); @@ -71,20 +76,28 @@ export class OIT extends DisplayComponent { this.uiService.activeUri.sub((uri) => { this.activeUriChanged(uri); - - // Activate EFB overlay if on charts or flt-folder page - SimVar.SetSimVarValue( - `L:A32NX_OIS_${getDisplayIndex()}_SHOW_CHARTS`, - SimVarValueType.Bool, - uri.uri === 'flt-ops/charts', // uri.uri === 'flt-ops/charts' - ); - SimVar.SetSimVarValue( - `L:A32NX_OIS_${getDisplayIndex()}_SHOW_OFP`, - SimVarValueType.Bool, - uri.uri === 'flt-ops/flt-folder', - ); }); + MappedSubject.create( + ([uri, displayFailed, displayPowered, operationMode]) => { + // Activate EFB overlay if on charts or flt-folder page + SimVar.SetSimVarValue( + `L:A32NX_OIS_${getDisplayIndex()}_SHOW_CHARTS`, + SimVarValueType.Bool, + uri.uri === 'flt-ops/charts' && operationMode === 'flt-ops' && !displayFailed && displayPowered, + ); + SimVar.SetSimVarValue( + `L:A32NX_OIS_${getDisplayIndex()}_SHOW_OFP`, + SimVarValueType.Bool, + uri.uri === 'flt-ops/flt-folder' && operationMode === 'flt-ops' && !displayFailed && displayPowered, + ); + }, + this.uiService.activeUri, + this.displayUnitRef.instance.failed, + this.displayUnitRef.instance.powered, + this.operationMode, + ); + this.uiService.navigateTo('flt-ops/sts'); // should be /sts } @@ -116,10 +129,11 @@ export class OIT extends DisplayComponent { render(): VNode | null { return ( ('flt-ops')} + nssOrFltOps={this.operationMode} >
diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OisInternalPublisher.ts b/fbw-a380x/src/systems/instruments/src/OIT/OisInternalPublisher.ts new file mode 100644 index 00000000000..1a942276c93 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/OIT/OisInternalPublisher.ts @@ -0,0 +1,7 @@ +// Copyright (c) 2025 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +export interface OisInternalData { + /** SYNCHRO AVIONICS is pressed (transmitted to EFB overlay) */ + synchroAvncs: boolean; +} diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OisLaptop.ts b/fbw-a380x/src/systems/instruments/src/OIT/OisLaptop.ts index 2b1cf0495cf..4897adf8e08 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OisLaptop.ts +++ b/fbw-a380x/src/systems/instruments/src/OIT/OisLaptop.ts @@ -4,6 +4,7 @@ import { ConsumerSubject, EventBus, Instrument, SimVarValueType, Subject, Subscribable } from '@microsoft/msfs-sdk'; import { FailuresConsumer, FmsData } from '@flybywiresim/fbw-sdk'; import { A380Failure } from '@failures'; +import { OisInternalData } from 'instruments/src/OIT/OisInternalPublisher'; type LaptopIndex = 1 | 2; @@ -17,7 +18,7 @@ export class OisLaptop implements Instrument { private readonly _isHealthy = Subject.create(false); public readonly isHealthy = this._isHealthy as Subscribable; - private readonly sub = this.bus.getSubscriber(); + private readonly sub = this.bus.getSubscriber(); private readonly fltNumberBus = ConsumerSubject.create(this.sub.on('fmsFlightNumber'), null); private readonly fromAirportBus = ConsumerSubject.create(this.sub.on('fmsOrigin'), null); @@ -27,6 +28,9 @@ export class OisLaptop implements Instrument { this.data.fltNumber.set(this.fltNumberBus.get()); this.data.fromAirport.set(this.fromAirportBus.get()); this.data.toAirport.set(this.toAirportBus.get()); + + // Workaround since synced messages in the same VCockpit don't work + SimVar.SetSimVarValue('L:A32NX_OIS_SYNCHRO_AVIONICS', SimVarValueType.Number, Math.random()); } constructor( @@ -38,6 +42,8 @@ export class OisLaptop implements Instrument { /** @inheritdoc */ init(): void { this.failuresConsumer.register(this.failureKey); + + this.sub.on('synchroAvncs').handle(() => this.synchroAvionics()); } /** @inheritdoc */ diff --git a/fbw-a380x/src/systems/instruments/src/OIT/OitDisplayUnit.tsx b/fbw-a380x/src/systems/instruments/src/OIT/OitDisplayUnit.tsx index c73cf65e2bf..9fa2cc0706f 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/OitDisplayUnit.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/OitDisplayUnit.tsx @@ -4,6 +4,7 @@ import { ClockEvents, + ComponentProps, ConsumerSubject, DisplayComponent, EventBus, @@ -63,7 +64,7 @@ enum DisplayUnitState { Failed, } -export class OitDisplayUnit extends DisplayComponent { +export class OitDisplayUnit extends DisplayComponent { private readonly sub = this.props.bus.getSubscriber(); private readonly state = Subject.create( @@ -81,7 +82,7 @@ export class OitDisplayUnit extends DisplayComponent { private oitRef = FSComponent.createRef(); - private readonly powered = Subject.create(false); + public readonly powered = Subject.create(false); private readonly brightness = ConsumerSubject.create( this.sub.on(this.props.displayUnitId === OitDisplayUnitID.CaptOit ? 'potentiometerCaptain' : 'potentiometerFo'), @@ -111,13 +112,19 @@ export class OitDisplayUnit extends DisplayComponent { true, ).map(SubscribableMapFunctions.not()); - private readonly failed = MappedSubject.create( + private readonly fltOpsFailed = MappedSubject.create( + SubscribableMapFunctions.or(), + this.fltOpsAnsuFailed, + this.fltOpsLaptopFailed, + ); + + public readonly failed = MappedSubject.create( ([opMode, state, nssFail, fltOpsFail]) => state === DisplayUnitState.On && ((opMode === 'nss' && nssFail) || (opMode === 'flt-ops' && fltOpsFail)), this.props.nssOrFltOps, this.state, this.allNssAnsuFailed, - this.fltOpsLaptopFailed, + this.fltOpsFailed, ); public onAfterRender(node: VNode): void { diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx index 2db4b2efb53..57989cbf2bd 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx @@ -7,6 +7,7 @@ import { DropdownMenu } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/Dropd import { InputField } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/InputField'; import { AirportFormat, LongAlphanumericFormat } from 'instruments/src/MFD/pages/common/DataEntryFormats'; import { Button } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/Button'; +import { OisInternalData } from 'instruments/src/OIT/OisInternalPublisher'; interface OitFltOpsStatusPageProps extends AbstractOitPageProps {} @@ -49,7 +50,7 @@ export class OitFltOpsStatus extends DisplayComponent
); } diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/OitNotFound.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/OitNotFound.tsx index f530a42a0c2..825fee52e93 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/Pages/OitNotFound.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/OitNotFound.tsx @@ -8,7 +8,7 @@ interface OitNotFoundProps extends AbstractOitPageProps {} export class OitNotFound extends DisplayComponent { // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. - private subs = [] as Subscription[]; + private readonly subs = [] as Subscription[]; public onAfterRender(node: VNode): void { super.onAfterRender(node); @@ -18,7 +18,9 @@ export class OitNotFound extends DisplayComponent { public destroy(): void { // Destroy all subscriptions to remove all references to this instance. - this.subs.forEach((x) => x.destroy()); + for (const s of this.subs) { + s.destroy(); + } super.destroy(); } diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsEfbOverlay.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsEfbOverlay.tsx index 3b484b926c7..9dee4f667ce 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsEfbOverlay.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsEfbOverlay.tsx @@ -8,7 +8,7 @@ interface OitFltOpsEfbOverlayPageProps extends AbstractOitPageProps {} export class OitFltOpsEfbOverlay extends DisplayComponent { // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. - private subs = [] as Subscription[]; + private readonly subs = [] as Subscription[]; public onAfterRender(node: VNode): void { super.onAfterRender(node); @@ -16,7 +16,9 @@ export class OitFltOpsEfbOverlay extends DisplayComponent x.destroy()); + for (const s of this.subs) { + s.destroy(); + } super.destroy(); } diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx index 8b581699f2c..98b7e838288 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsMenuPage.tsx @@ -9,7 +9,7 @@ interface OitFltOpsMenuPageProps extends AbstractOitPageProps {} export class OitFltOpsMenuPage extends DisplayComponent { // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. - private subs = [] as Subscription[]; + private readonly subs = [] as Subscription[]; public onAfterRender(node: VNode): void { super.onAfterRender(node); @@ -17,7 +17,9 @@ export class OitFltOpsMenuPage extends DisplayComponent public destroy(): void { // Destroy all subscriptions to remove all references to this instance. - this.subs.forEach((x) => x.destroy()); + for (const s of this.subs) { + s.destroy(); + } super.destroy(); } diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsPerformance.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsPerformance.tsx index eeb9fb17c1d..9eb27362f85 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsPerformance.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsPerformance.tsx @@ -8,7 +8,7 @@ interface OitFltOpsPerformancePageProps extends AbstractOitPageProps {} export class OitFltOpsPerformance extends DisplayComponent { // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. - private subs = [] as Subscription[]; + private readonly subs = [] as Subscription[]; public onAfterRender(node: VNode): void { super.onAfterRender(node); @@ -16,7 +16,9 @@ export class OitFltOpsPerformance extends DisplayComponent x.destroy()); + for (const s of this.subs) { + s.destroy(); + } super.destroy(); } diff --git a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx index e390ea730a0..faa52569972 100644 --- a/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx +++ b/fbw-a380x/src/systems/instruments/src/OIT/Pages/flt-ops/OitFltOpsStatus.tsx @@ -13,7 +13,7 @@ interface OitFltOpsStatusPageProps extends AbstractOitPageProps {} export class OitFltOpsStatus extends DisplayComponent { // Make sure to collect all subscriptions here, otherwise page navigation doesn't work. - private subs = [] as Subscription[]; + private readonly subs = [] as Subscription[]; public onAfterRender(node: VNode): void { super.onAfterRender(node); @@ -21,7 +21,9 @@ export class OitFltOpsStatus extends DisplayComponent public destroy(): void { // Destroy all subscriptions to remove all references to this instance. - this.subs.forEach((x) => x.destroy()); + for (const s of this.subs) { + s.destroy(); + } super.destroy(); } diff --git a/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx b/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx index 452ed8e1002..0d36d4c159d 100644 --- a/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx +++ b/fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx @@ -204,7 +204,6 @@ export const OitEfbPageWrapper: React.FC = () => { }; useEffect(() => { - console.log('navigraphAuthInfo.loggedIn, synchroAvionics', navigraphAuthInfo); updateSimBriefInfo(); }, [navigraphAuthInfo, synchroAvionics]);