From d301492df68270fa66ec63f6aa64600faebde766 Mon Sep 17 00:00:00 2001 From: Frank Kopp Date: Fri, 19 Jan 2024 23:07:03 +0100 Subject: [PATCH] feat(A380X): Added systems-host and extras-host to A380X (#8418) * Initial add of extras-host to A380X * Move setting of A32NX_IS_READY to extras-host * Added systems-host from A32NX to A380X * Removed commented part in build script --- .../FlyByWire_A380_842/panel/panel.cfg | 18 ++- .../A380X/ExtrasHost/extras-host.html | 7 + .../A380X/SystemsHost/systems-host.html | 4 + fbw-a380x/src/systems/extras-host/README.md | 24 ++++ fbw-a380x/src/systems/extras-host/build.js | 13 ++ fbw-a380x/src/systems/extras-host/index.ts | 110 +++++++++++++++ .../modules/common/AircraftPresetsList.ts | 25 ++++ .../modules/common/ExtrasSimVarPublisher.ts | 24 ++++ .../modules/key_interceptor/KeyInterceptor.ts | 124 ++++++++++++++++ .../pushbutton_check/PushbuttonCheck.ts | 52 +++++++ .../modules/version_check/VersionCheck.ts | 21 +++ .../src/systems/extras-host/tsconfig.json | 21 +++ .../fmgc/src/components/ReadySignal.ts | 23 --- .../src/systems/fmgc/src/components/index.ts | 3 +- fbw-a380x/src/systems/systems-host/README.md | 25 ++++ fbw-a380x/src/systems/systems-host/build.js | 13 ++ fbw-a380x/src/systems/systems-host/index.ts | 133 ++++++++++++++++++ .../src/systems/systems-host/systems/atsu.ts | 54 +++++++ .../systems-host/systems/powersupply.ts | 86 +++++++++++ .../src/systems/systems-host/tsconfig.json | 21 +++ igniter.config.mjs | 16 +++ package.json | 2 + 22 files changed, 792 insertions(+), 27 deletions(-) create mode 100644 fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/A380X/ExtrasHost/extras-host.html create mode 100644 fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/A380X/SystemsHost/systems-host.html create mode 100644 fbw-a380x/src/systems/extras-host/README.md create mode 100644 fbw-a380x/src/systems/extras-host/build.js create mode 100644 fbw-a380x/src/systems/extras-host/index.ts create mode 100644 fbw-a380x/src/systems/extras-host/modules/common/AircraftPresetsList.ts create mode 100644 fbw-a380x/src/systems/extras-host/modules/common/ExtrasSimVarPublisher.ts create mode 100644 fbw-a380x/src/systems/extras-host/modules/key_interceptor/KeyInterceptor.ts create mode 100644 fbw-a380x/src/systems/extras-host/modules/pushbutton_check/PushbuttonCheck.ts create mode 100644 fbw-a380x/src/systems/extras-host/modules/version_check/VersionCheck.ts create mode 100644 fbw-a380x/src/systems/extras-host/tsconfig.json delete mode 100644 fbw-a380x/src/systems/fmgc/src/components/ReadySignal.ts create mode 100644 fbw-a380x/src/systems/systems-host/README.md create mode 100644 fbw-a380x/src/systems/systems-host/build.js create mode 100644 fbw-a380x/src/systems/systems-host/index.ts create mode 100644 fbw-a380x/src/systems/systems-host/systems/atsu.ts create mode 100644 fbw-a380x/src/systems/systems-host/systems/powersupply.ts create mode 100644 fbw-a380x/src/systems/systems-host/tsconfig.json 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 f82a1be107c..a2fecb14aba 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 @@ -154,5 +154,19 @@ htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=systems.wasm&wasm_gau htmlgauge01=WasmInstrument/WasmInstrument.html?wasm_module=fbw.wasm&wasm_gauge=fbw, 0,0,1,1 htmlgauge02=WasmInstrument/WasmInstrument.html?wasm_module=fadec.wasm&wasm_gauge=FadecGauge, 0,0,1,1 htmlgauge03=WasmInstrument/WasmInstrument.html?wasm_module=extra-backend-a380x.wasm&wasm_gauge=Gauge_Extra_Backend,0,0,1,1 -# Uncomment when A380 ExtrasHost exists in monorepo -#htmlgauge04=A380X/ExtrasHost/extras-host.html,0,0,1,1 + +[VCockpit20] +size_mm = 0,0 +pixel_size = 0,0 +texture = NO_TEXTURE +background_color = 0,0,0 + +htmlgauge00 = A380X/SystemsHost/systems-host.html,0,0,1,1 + +[VCockpit21] +size_mm=0,0 +pixel_size=0,0 +texture=NO_TEXTURE +background_color=0,0,0 + +htmlgauge04=A380X/ExtrasHost/extras-host.html,0,0,1,1 diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/A380X/ExtrasHost/extras-host.html b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/A380X/ExtrasHost/extras-host.html new file mode 100644 index 00000000000..6b6a3e3959c --- /dev/null +++ b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/A380X/ExtrasHost/extras-host.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/A380X/SystemsHost/systems-host.html b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/A380X/SystemsHost/systems-host.html new file mode 100644 index 00000000000..fe337fe7fa6 --- /dev/null +++ b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/A380X/SystemsHost/systems-host.html @@ -0,0 +1,4 @@ + + + + diff --git a/fbw-a380x/src/systems/extras-host/README.md b/fbw-a380x/src/systems/extras-host/README.md new file mode 100644 index 00000000000..071f8f82493 --- /dev/null +++ b/fbw-a380x/src/systems/extras-host/README.md @@ -0,0 +1,24 @@ +# FBW Extras Host + +The ExtrasHost instrument is used to provide non-aircraft and non-wasm related functionality an +environment to run as single instrument without rendering or connection to the MCDU, or the EFB, +where some of these functionalities have been hosted in the past. + +The ExtrasHost inherits from the BaseInstruments class that is managed by the simulator. + +It uses the msfssdk library for using the EventBus and HEventPublisher and pot. other classes. + +## System interfaces + +Every module class has to implement the following functions: + +- `constructor` to get access to the system-wide EventBus +- `connectedCallback` which is called after the simulator set up everything. These functions will also add the subscribtion to special events. +- `startPublish` which is called as soon as the simulator starts running. It will also start publishing the simulator variables onto the EventBus +- `update` is called in every update call of the simulator, but only after `startPublish` is called + +## Examples + +See the modules folder for examples on how to implement a module. + + diff --git a/fbw-a380x/src/systems/extras-host/build.js b/fbw-a380x/src/systems/extras-host/build.js new file mode 100644 index 00000000000..63ccbff4374 --- /dev/null +++ b/fbw-a380x/src/systems/extras-host/build.js @@ -0,0 +1,13 @@ +// Copyright (c) 2021-2023 FlyByWire Simulations +// +// SPDX-License-Identifier: GPL-3.0 + +'use strict'; + +const esbuild = require('esbuild'); +const path = require('path'); +const { createModuleBuild } = require('#build-utils'); + +const outFile = 'fbw-a380x/out/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/A380X/ExtrasHost/index.js'; + +esbuild.build(createModuleBuild('fbw-a380x', undefined, path.join(__dirname, './index.ts'), outFile, __dirname)); diff --git a/fbw-a380x/src/systems/extras-host/index.ts b/fbw-a380x/src/systems/extras-host/index.ts new file mode 100644 index 00000000000..1ac80686e76 --- /dev/null +++ b/fbw-a380x/src/systems/extras-host/index.ts @@ -0,0 +1,110 @@ +// Copyright (c) 2022 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, HEventPublisher } from '@microsoft/msfs-sdk'; +import { NotificationManager } from '@flybywiresim/fbw-sdk'; +import { ExtrasSimVarPublisher } from 'extras-host/modules/common/ExtrasSimVarPublisher'; +import { PushbuttonCheck } from 'extras-host/modules/pushbutton_check/PushbuttonCheck'; +import { KeyInterceptor } from './modules/key_interceptor/KeyInterceptor'; +import { VersionCheck } from './modules/version_check/VersionCheck'; + +/** + * This is the main class for the extras-host instrument. + * + * It provides an environment for non-aircraft non-wasm systems/modules to run in. + * + * Usage: + * - Add new modules as private readonly members of this class. + * - Add the modules to the constructor. + * - Add the modules to the connectedCallback() method. + * - Add the modules to the Update() method. + * + * Each module must implement the following methods: + * - `constructor` to get access to the system-wide EventBus + * - `connectedCallback` which is called after the simulator set up everything. These functions will also add the subscribtion to special events. + * - `startPublish` which is called as soon as the simulator starts running. It will also start publishing the simulator variables onto the EventBus + * - `update` is called in every update call of the simulator, but only after `startPublish` is called + */ +class ExtrasHost extends BaseInstrument { + private readonly bus: EventBus; + + private readonly notificationManager: NotificationManager; + + private readonly hEventPublisher: HEventPublisher; + + private readonly simVarPublisher: ExtrasSimVarPublisher; + + private readonly pushbuttonCheck: PushbuttonCheck; + + private readonly versionCheck: VersionCheck; + + private readonly keyInterceptor: KeyInterceptor; + + /** + * "mainmenu" = 0 + * "loading" = 1 + * "briefing" = 2 + * "ingame" = 3 + */ + private gameState = 0; + + constructor() { + super(); + + this.bus = new EventBus(); + this.hEventPublisher = new HEventPublisher(this.bus); + this.simVarPublisher = new ExtrasSimVarPublisher(this.bus); + + this.notificationManager = new NotificationManager(); + + this.pushbuttonCheck = new PushbuttonCheck(this.bus, this.notificationManager); + this.versionCheck = new VersionCheck(this.bus); + this.keyInterceptor = new KeyInterceptor(this.bus, this.notificationManager); + + console.log('A380X_EXTRASHOST: Created'); + } + + get templateID(): string { + return 'A380X_EXTRASHOST'; + } + + public getDeltaTime() { + return this.deltaTime; + } + + public onInteractionEvent(args: string[]): void { + this.hEventPublisher.dispatchHEvent(args[0]); + } + + public connectedCallback(): void { + super.connectedCallback(); + + this.pushbuttonCheck.connectedCallback(); + } + + public Update(): void { + super.Update(); + + if (this.gameState !== GameState.ingame) { + const gs = this.getGameState(); + if (gs === GameState.ingame) { + // Start the modules + this.hEventPublisher.startPublish(); + this.versionCheck.startPublish(); + this.keyInterceptor.startPublish(); + this.simVarPublisher.startPublish(); + + // Signal that the aircraft is ready via L:A32NX_IS_READY + SimVar.SetSimVarValue('L:A32NX_IS_READY', 'number', 1); + console.log('A380X_EXTRASHOST: Aircraft is ready'); + } + this.gameState = gs; + } else { + this.simVarPublisher.onUpdate(); + } + + // Call module update() methods here if they have one + } +} + +registerInstrument('extras-host', ExtrasHost); diff --git a/fbw-a380x/src/systems/extras-host/modules/common/AircraftPresetsList.ts b/fbw-a380x/src/systems/extras-host/modules/common/AircraftPresetsList.ts new file mode 100644 index 00000000000..791088404ae --- /dev/null +++ b/fbw-a380x/src/systems/extras-host/modules/common/AircraftPresetsList.ts @@ -0,0 +1,25 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +/** + * The AircraftPresetsList class is used to get the name of a preset from the preset ID. + * These need to align with the IDs in the Presets C++ WASM and the AircraftPresets.tsx in the EFB. + * WASM: src/presets/src/Aircraft/AircraftProcedures.h + */ +export class AircraftPresetsList { + private static list: { index: number, name: string }[] = [ + { index: 1, name: 'Cold & Dark' }, + { index: 2, name: 'Powered' }, + { index: 3, name: 'Ready for Pushback' }, + { index: 4, name: 'Ready for Taxi' }, + { index: 5, name: 'Ready for Takeoff' }, + ]; + + public static getPresetName(presetID: number): string { + const index = presetID - 1; + if (index < 0 || index > AircraftPresetsList.list.length) { + return ''; + } + return AircraftPresetsList.list[index].name; + } +} diff --git a/fbw-a380x/src/systems/extras-host/modules/common/ExtrasSimVarPublisher.ts b/fbw-a380x/src/systems/extras-host/modules/common/ExtrasSimVarPublisher.ts new file mode 100644 index 00000000000..65cd25c7296 --- /dev/null +++ b/fbw-a380x/src/systems/extras-host/modules/common/ExtrasSimVarPublisher.ts @@ -0,0 +1,24 @@ +// Copyright (c) 2023 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, PublishPacer, SimVarDefinition, SimVarPublisher, SimVarValueType } from '@microsoft/msfs-sdk'; + +/* eslint-disable camelcase */ + +export interface ExtrasSimVarEvents { + /** ECP TO CONF pushbutton state */ + ecp_to_config_pushbutton: boolean, + /** FWC flight phase from 1 - 10 */ + fwc_flight_phase: number, +} + +export class ExtrasSimVarPublisher extends SimVarPublisher { + private static readonly simVars = new Map([ + ['ecp_to_config_pushbutton', { name: 'L:A32NX_BTN_TOCONFIG', type: SimVarValueType.Bool }], + ['fwc_flight_phase', { name: 'L:A32NX_FWC_FLIGHT_PHASE', type: SimVarValueType.Number }], + ]); + + constructor(bus: EventBus, pacer?: PublishPacer) { + super(ExtrasSimVarPublisher.simVars, bus, pacer); + } +} diff --git a/fbw-a380x/src/systems/extras-host/modules/key_interceptor/KeyInterceptor.ts b/fbw-a380x/src/systems/extras-host/modules/key_interceptor/KeyInterceptor.ts new file mode 100644 index 00000000000..acb85300eee --- /dev/null +++ b/fbw-a380x/src/systems/extras-host/modules/key_interceptor/KeyInterceptor.ts @@ -0,0 +1,124 @@ +// Copyright (c) 2022 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, KeyEvents, KeyEventManager } from '@microsoft/msfs-sdk'; +import { NotificationManager, NotificationType, PopUpDialog } from '@flybywiresim/fbw-sdk'; +import { AircraftPresetsList } from '../common/AircraftPresetsList'; + +/** + * This class is used to intercept the key events for the engine auto start and engine auto shutdown. + * + * Additional key events can be added in the registerIntercepts() method. + */ +export class KeyInterceptor { + private eventBus: EventBus; + + private keyInterceptManager: KeyEventManager; + + private dialogVisible = false; + + constructor(private readonly bus: EventBus, private readonly notification: NotificationManager) { + this.eventBus = bus; + KeyEventManager.getManager(this.eventBus).then((manager) => { + this.keyInterceptManager = manager; + this.registerIntercepts(); + }); + console.log('KeyInterceptor: Created'); + } + + public startPublish(): void { + console.log('KeyInterceptor: startPublish()'); + } + + private registerIntercepts() { + this.keyInterceptManager.interceptKey('ENGINE_AUTO_START', false); + this.keyInterceptManager.interceptKey('ENGINE_AUTO_SHUTDOWN', false); + + const subscriber = this.eventBus.getSubscriber(); + subscriber.on('key_intercept').handle((keyData) => { + switch (keyData.key) { + case 'ENGINE_AUTO_START': + console.log('KeyInterceptor: ENGINE_AUTO_START'); + this.engineAutoStartAction(); + break; + case 'ENGINE_AUTO_SHUTDOWN': + console.log('KeyInterceptor: ENGINE_AUTO_SHUTDOWN'); + this.engineAutoStopAction(); + break; + default: + break; + } + }); + } + + private engineAutoStartAction() { + if (!this.dialogVisible) { + // If loading already in progress show a notification and return + if (this.isAlreadyLoading()) return; + // Show a dialog to ask user to load a preset or cancel + this.dialogVisible = true; + const dialog = new PopUpDialog(); + const presetID = 4; // "Ready for Taxi" + dialog.showPopUp( + 'Ctrl+E Not supported', + `
+ Engine Auto Start is not supported by the A380X.
+
+ Do you want to you use the flyPad's Aircraft Presets to set the aircraft to + "${AircraftPresetsList.getPresetName(presetID)}"? +
`, + 'small', + () => this.loadPreset(presetID), + () => this.dialogVisible = false, + ); + } + } + + private engineAutoStopAction() { + if (this.isAlreadyLoading()) return; + // If engines are running show a dialog to ask user to load a preset or cancel + if (!this.dialogVisible && this.isOneEngineRunning()) { + this.dialogVisible = true; + const dialog = new PopUpDialog(); + const presetID = 2; + dialog.showPopUp( + 'Shift+Ctrl+E Not supported', + `
+ Engine Auto Shutdown is not supported by the A380X.
+
+ Do you want to you use the flyPad's Aircraft Presets to set the aircraft to + "${AircraftPresetsList.getPresetName(presetID)}"? +
`, + 'small', + () => this.loadPreset(presetID), + () => this.dialogVisible = false, + ); + } + } + + private isAlreadyLoading() { + const loadingInProgress = SimVar.GetSimVarValue('L:A32NX_AIRCRAFT_PRESET_LOAD', 'Number'); + if (loadingInProgress > 0) { + this.notification.showNotification({ + title: 'Aircraft Presets', + message: `Loading Preset is already in progress "${(AircraftPresetsList.getPresetName(loadingInProgress))}"`, + type: NotificationType.Message, + timeout: 1500, + }); + return true; + } + return false; + } + + private isOneEngineRunning() { + const engine1N1 = SimVar.GetSimVarValue('L:A32NX_ENGINE_N1:1', 'Number'); + const engine2N1 = SimVar.GetSimVarValue('L:A32NX_ENGINE_N1:2', 'Number'); + return engine1N1 > 0.1 || engine2N1 > 0.1; + } + + private loadPreset(presetID: number) { + console.log(`Setting aircraft preset to ${AircraftPresetsList.getPresetName(presetID)}`); + SimVar.SetSimVarValue('L:A32NX_AIRCRAFT_PRESET_LOAD', 'Number', presetID); + this.dialogVisible = false; + } +} diff --git a/fbw-a380x/src/systems/extras-host/modules/pushbutton_check/PushbuttonCheck.ts b/fbw-a380x/src/systems/extras-host/modules/pushbutton_check/PushbuttonCheck.ts new file mode 100644 index 00000000000..f0934441161 --- /dev/null +++ b/fbw-a380x/src/systems/extras-host/modules/pushbutton_check/PushbuttonCheck.ts @@ -0,0 +1,52 @@ +// Copyright (c) 2023 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { ConsumerSubject, DebounceTimer, EventBus, MappedSubject } from '@microsoft/msfs-sdk'; +import { NotificationManager, NotificationTheme } from '@flybywiresim/fbw-sdk'; +import { ExtrasSimVarEvents } from 'extras-host/modules/common/ExtrasSimVarPublisher'; + +/** + * Monitors cockpit pushbuttons that may be written externally to ensure they are not "stuck" because + * the external writer failed to set them back to 0. + */ +export class PushbuttonCheck { + /** Maximum time in ms that TO CONF can be pressed before it is considered "stuck" */ + private static readonly TO_CONFIG_MAX_PRESS_TIME = 30000; + + private readonly sub = this.bus.getSubscriber(); + + private readonly fwcFlightPhase = ConsumerSubject.create(null, 1); + + private readonly toConfButton = ConsumerSubject.create(null, false); + + private readonly toConfTimer = new DebounceTimer(); + + private readonly toConfButtonInCruise = MappedSubject.create(([toConf, phase]) => toConf && phase === 6, this.toConfButton, this.fwcFlightPhase) + + private toConfMessageShown = false; + + constructor(private readonly bus: EventBus, private readonly notification: NotificationManager) {} + + public connectedCallback(): void { + this.toConfButtonInCruise.sub(this.onToConfigPushbutton.bind(this)); + + this.fwcFlightPhase.setConsumer(this.sub.on('fwc_flight_phase')); + this.toConfButton.setConsumer(this.sub.on('ecp_to_config_pushbutton')); + } + + private onToConfigPushbutton(pressed: boolean): void { + if (pressed && !this.toConfTimer.isPending() && !this.toConfMessageShown) { + this.toConfTimer.schedule(() => { + this.toConfMessageShown = true; + this.notification.showNotification({ + title: 'ECP Pushbutton Held', + // eslint-disable-next-line max-len + message: 'The TO CONF pushbutton has been held for a long time!\n\nIf you have external hardware or software controlling this variable (L:A32NX_BTN_TOCONFIG), please check that it is setup to write the variable to 0 when the button is released.', + theme: NotificationTheme.Tips, + }); + }, PushbuttonCheck.TO_CONFIG_MAX_PRESS_TIME); + } else if (!pressed) { + this.toConfTimer.clear(); + } + } +} diff --git a/fbw-a380x/src/systems/extras-host/modules/version_check/VersionCheck.ts b/fbw-a380x/src/systems/extras-host/modules/version_check/VersionCheck.ts new file mode 100644 index 00000000000..e4f28374938 --- /dev/null +++ b/fbw-a380x/src/systems/extras-host/modules/version_check/VersionCheck.ts @@ -0,0 +1,21 @@ +// Copyright (c) 2022 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus } from '@microsoft/msfs-sdk'; + +/** + * This class is used to check the version of the aircraft and display a warning if it is too old. + */ +export class VersionCheck { + constructor(private readonly bus: EventBus) { + console.log('VersionCheck: Created'); + } + + public startPublish(): void { + console.log('VersionCheck: startPublish()'); + + // TODO: Implement version check which is dependent on the API to change and handling + // of github version for the monorepo needs to be changed. + console.warn('VersionCheck for the A380X is not yet implemented.'); + } +} diff --git a/fbw-a380x/src/systems/extras-host/tsconfig.json b/fbw-a380x/src/systems/extras-host/tsconfig.json new file mode 100644 index 00000000000..4e2b403b503 --- /dev/null +++ b/fbw-a380x/src/systems/extras-host/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "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/fmgc/src/components/ReadySignal.ts b/fbw-a380x/src/systems/fmgc/src/components/ReadySignal.ts deleted file mode 100644 index f561251cd55..00000000000 --- a/fbw-a380x/src/systems/fmgc/src/components/ReadySignal.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { FlightPlanManager } from '@fmgc/wtsdk'; -import { UpdateThrottler } from '@shared/UpdateThrottler'; -import { FmgcComponent } from './FmgcComponent'; - -export class ReadySignal implements FmgcComponent { - private baseInstrument: BaseInstrument = null; - - private updateThrottler = new UpdateThrottler(1000); - - init(baseInstrument: BaseInstrument, _flightPlanManager: FlightPlanManager): void { - this.baseInstrument = baseInstrument; - } - - update(deltaTime: number): void { - if (this.updateThrottler.canUpdate(deltaTime) !== -1 - && this.baseInstrument.getGameState() === GameState.ingame - && SimVar.GetSimVarValue('L:A32NX_IS_READY', 'number') !== 1) { - // set ready signal that JS code is initialized and flight is actually started - // -> user pressed 'READY TO FLY' button - SimVar.SetSimVarValue('L:A32NX_IS_READY', 'number', 1); - } - } -} diff --git a/fbw-a380x/src/systems/fmgc/src/components/index.ts b/fbw-a380x/src/systems/fmgc/src/components/index.ts index 15fab9a004e..2aed1347afb 100644 --- a/fbw-a380x/src/systems/fmgc/src/components/index.ts +++ b/fbw-a380x/src/systems/fmgc/src/components/index.ts @@ -1,4 +1,3 @@ -import { ReadySignal } from '@fmgc/components/ReadySignal'; import { FlightPlanManager } from '@fmgc/wtsdk'; import { EfisLabels } from './EfisLabels'; import { FmgcComponent } from './FmgcComponent'; @@ -9,7 +8,7 @@ const fmsMessages = new FmsMessages(); const components: FmgcComponent[] = [ fmsMessages, new EfisLabels(), - new ReadySignal(), + // new ReadySignal(), // moved to extras-host ]; export function initComponents(baseInstrument: BaseInstrument, flightPlanManager: FlightPlanManager): void { diff --git a/fbw-a380x/src/systems/systems-host/README.md b/fbw-a380x/src/systems/systems-host/README.md new file mode 100644 index 00000000000..0d8d1481496 --- /dev/null +++ b/fbw-a380x/src/systems/systems-host/README.md @@ -0,0 +1,25 @@ +# FBW Systems Host + +The systems host is used to provide non-Rust systems of the A380X an environment to run as single +instrument without rendering or connection to the MCDU, etc. The system inherits from the +BaseInstruments class that is managed by the simulator. + +It manages the different power supplies, systems and handles the simulator states. + +## Communication stack + +The systems and the power supply communicate via the MSFS EventBus. The power supply class +translates the simulator variables into EventBus events. + +## System interfaces + +Every system has to implement the following functions: + +- `constructor` to get access to the system-wide EventBus +- `connectedCallback` which is called after the simulator set up everything. These functions will also add the subscribtion to special events. +- `startPublish` which is called as soon as the simulator starts running. It will also start publishing the simulator variables onto the EventBus +- `update` is called in every update call of the simulator, but only after `startPublish` is called + +## Example +(TODO: update for A380X's first system) +~~The `systems/atsu.ts` is a first example how to implement a system inside the host environment.~~ diff --git a/fbw-a380x/src/systems/systems-host/build.js b/fbw-a380x/src/systems/systems-host/build.js new file mode 100644 index 00000000000..b61c31aea8d --- /dev/null +++ b/fbw-a380x/src/systems/systems-host/build.js @@ -0,0 +1,13 @@ +// Copyright (c) 2021-2023 FlyByWire Simulations +// +// SPDX-License-Identifier: GPL-3.0 + +'use strict'; + +const esbuild = require('esbuild'); +const path = require('path'); +const { createModuleBuild } = require('#build-utils'); + +const outFile = 'fbw-a380x/out/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/A380X/SystemsHost/index.js'; + +esbuild.build(createModuleBuild('fbw-a380x', undefined, path.join(__dirname, './index.ts'), outFile, __dirname)); diff --git a/fbw-a380x/src/systems/systems-host/index.ts b/fbw-a380x/src/systems/systems-host/index.ts new file mode 100644 index 00000000000..52aab2f732e --- /dev/null +++ b/fbw-a380x/src/systems/systems-host/index.ts @@ -0,0 +1,133 @@ +// Copyright (c) 2021-2023 FlyByWire Simulations +// +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, HEventPublisher, KeyEventManager, Wait, GameStateProvider } from '@microsoft/msfs-sdk'; +import { AtsuSystem } from './systems/atsu'; +import { PowerSupplyBusses } from './systems/powersupply'; + +class SystemsHost extends BaseInstrument { + private readonly bus: EventBus; + + private readonly hEventPublisher: HEventPublisher; + + // Uncomment once migrated from the A32NX to the A380X + // private readonly powerSupply: PowerSupplyBusses; + // private readonly atsu: AtsuSystem; + + private keyInterceptManager: KeyEventManager; + + /** + * "mainmenu" = 0 + * "loading" = 1 + * "briefing" = 2 + * "ingame" = 3 + */ + private gameState = 0; + + constructor() { + super(); + + this.bus = new EventBus(); + this.hEventPublisher = new HEventPublisher(this.bus); + // this.powerSupply = new PowerSupplyBusses(this.bus); + // this.atsu = new AtsuSystem(this.bus); + Promise.all([ + KeyEventManager.getManager(this.bus), + Wait.awaitSubscribable(GameStateProvider.get(), (state) => state === GameState.ingame, true), + ]).then(([keyEventManager]) => { + this.keyInterceptManager = keyEventManager; + this.initLighting(); + }); + } + + get templateID(): string { + return 'A380X_SYSTEMSHOST'; + } + + public getDeltaTime() { + return this.deltaTime; + } + + public onInteractionEvent(args: string[]): void { + this.hEventPublisher.dispatchHEvent(args[0]); + } + + public connectedCallback(): void { + super.connectedCallback(); + + // this.powerSupply.connectedCallback(); + // this.atsu.connectedCallback(); + + // Needed to fetch METARs from the sim + RegisterViewListener('JS_LISTENER_FACILITY', () => { + console.log('JS_LISTENER_FACILITY registered.'); + }, true); + } + + public Update(): void { + super.Update(); + + if (this.gameState !== 3) { + const gamestate = this.getGameState(); + if (gamestate === 3) { + this.hEventPublisher.startPublish(); + // this.powerSupply.startPublish(); + // this.atsu.startPublish(); + } + this.gameState = gamestate; + } + + // this.powerSupply.update(); + // this.atsu.update(); + } + + private initLighting() { + console.log('[systems-host] initializing lighting to defaults'); + + /** automatic brightness based on ambient light, [0, 1] scale */ + const autoBrightness = Math.max(15, Math.min(85, SimVar.GetSimVarValue('GLASSCOCKPIT AUTOMATIC BRIGHTNESS', 'percent'))); + + // OVHD Reading Lights + this.setPotentiometer(96, 0); // Capt + this.setPotentiometer(97, 0); // F/O + + // Glareshield + this.setPotentiometer(84, autoBrightness < 50 ? 1.5 * autoBrightness : 0); // Int Lt + this.setPotentiometer(87, autoBrightness); // Lcd Brt + this.setPotentiometer(10, 0); // table Cpt + this.setPotentiometer(11, 0); // table F/O + + // Instruments Cpt + this.setPotentiometer(88, autoBrightness); // PFD + this.setPotentiometer(89, autoBrightness); // ND + this.setPotentiometer(94, autoBrightness/2); // wxRadar + this.setPotentiometer(98, autoBrightness); // MFD + this.setPotentiometer(8, autoBrightness < 50 ? 20 : 0); // console light + + // Instruments F/O + this.setPotentiometer(90, autoBrightness); // PFD + this.setPotentiometer(91, autoBrightness); // ND + this.setPotentiometer(95, autoBrightness/2); // wxRadar + this.setPotentiometer(99, autoBrightness); // MFD + this.setPotentiometer(9, autoBrightness < 50 ? 20 : 0); // console light + + // Pedestal + this.setPotentiometer(80, autoBrightness); // rmpCptLightLevel + this.setPotentiometer(81, autoBrightness); // rmpFoLightLevel + this.setPotentiometer(82, autoBrightness); // rmpOvhdLightLevel + this.setPotentiometer(92, autoBrightness); // ecamUpperLightLevel + this.setPotentiometer(93, autoBrightness); // ecamLowerLightLevel + this.setPotentiometer(76, autoBrightness); // pedFloodLightLevel + this.setPotentiometer(83, autoBrightness); // mainPnlFloodLightLevel + this.setPotentiometer(85, autoBrightness); // integralLightLevel + this.setPotentiometer(7, autoBrightness); // ambientLightLevel + + } + + private setPotentiometer(potentiometer: number, brightness: number) { + this.keyInterceptManager.triggerKey('LIGHT_POTENTIOMETER_SET', false, potentiometer, brightness); + } +} + +registerInstrument('systems-host', SystemsHost); diff --git a/fbw-a380x/src/systems/systems-host/systems/atsu.ts b/fbw-a380x/src/systems/systems-host/systems/atsu.ts new file mode 100644 index 00000000000..2cbbb78b54f --- /dev/null +++ b/fbw-a380x/src/systems/systems-host/systems/atsu.ts @@ -0,0 +1,54 @@ +import { Atc } from '@datalink/atc'; +import { Aoc } from '@datalink/aoc'; +import { SimVarHandling } from '@datalink/common'; +import { Router } from '@datalink/router'; +import { EventBus, EventSubscriber } from '@microsoft/msfs-sdk'; +import { PowerSupplyBusTypes } from 'systems-host/systems/powersupply'; + +export class AtsuSystem { + private readonly simVarHandling: SimVarHandling; + + private readonly powerSupply: EventSubscriber; + + private readonly atc: Atc; + + private readonly aoc: Aoc; + + private readonly router: Router; + + constructor(private readonly bus: EventBus) { + this.simVarHandling = new SimVarHandling(this.bus); + this.router = new Router(this.bus, false, false); + this.atc = new Atc(this.bus, false, false); + this.aoc = new Aoc(this.bus, false); + + this.powerSupply = this.bus.getSubscriber(); + this.powerSupply.on('acBus1').handle((powered: boolean) => { + if (powered) { + this.router.powerUp(); + this.atc.powerUp(); + this.aoc.powerUp(); + } else { + this.aoc.powerDown(); + this.atc.powerDown(); + this.router.powerDown(); + } + }); + } + + public connectedCallback(): void { + this.simVarHandling.initialize(); + this.router.initialize(); + this.atc.initialize(); + this.aoc.initialize(); + } + + public startPublish(): void { + this.simVarHandling.startPublish(); + } + + public update(): void { + this.simVarHandling.update(); + this.router.update(); + } +} diff --git a/fbw-a380x/src/systems/systems-host/systems/powersupply.ts b/fbw-a380x/src/systems/systems-host/systems/powersupply.ts new file mode 100644 index 00000000000..f4b1e8e834e --- /dev/null +++ b/fbw-a380x/src/systems/systems-host/systems/powersupply.ts @@ -0,0 +1,86 @@ +import { EventBus, EventSubscriber, Publisher, SimVarDefinition, SimVarPublisher, SimVarValueType } from '@microsoft/msfs-sdk'; + +interface PowerSupplySimvars { + msfsAcBus1: number, + msfsAcBus2: number, + msfsAcBusEss: number, + msfsDcBus1: number, + msfsDcBus2: number, + msfsDcBusEss: number, +} + +enum PowerSupplySimvarSources { + acBus1 = 'L:A32NX_ELEC_AC_1_BUS_IS_POWERED', + acBus2 = 'L:A32NX_ELEC_AC_2_BUS_IS_POWERED', + acBusEss = 'L:A32NX_ELEC_AC_ESS_BUS_IS_POWERED', + dcBus1 = 'L:A32NX_ELEC_DC_1_BUS_IS_POWERED', + dcBus2 = 'L:A32NX_ELEC_DC_2_BUS_IS_POWERED', + dcBusEss = 'L:A32NX_ELEC_DC_ESS_BUS_IS_POWERED', +} + +export class PowerSupplySimvarPublisher extends SimVarPublisher { + private static simvars = new Map([ + ['msfsAcBus1', { name: PowerSupplySimvarSources.acBus1, type: SimVarValueType.Number }], + ['msfsAcBus2', { name: PowerSupplySimvarSources.acBus2, type: SimVarValueType.Number }], + ['msfsAcBusEss', { name: PowerSupplySimvarSources.acBusEss, type: SimVarValueType.Number }], + ['msfsDcBus1', { name: PowerSupplySimvarSources.dcBus1, type: SimVarValueType.Number }], + ['msfsDcBus2', { name: PowerSupplySimvarSources.dcBus2, type: SimVarValueType.Number }], + ['msfsDcBusEss', { name: PowerSupplySimvarSources.dcBusEss, type: SimVarValueType.Number }], + ]); + + public constructor(bus: EventBus) { + super(PowerSupplySimvarPublisher.simvars, bus); + } +} + +export interface PowerSupplyBusTypes { + acBus1: boolean, + acBus2: boolean, + acBusEss: boolean, + dcBus1: boolean, + dcBus2: boolean, + dcBusEss: boolean, +} + +export class PowerSupplyBusses { + private simVarPublisher: PowerSupplySimvarPublisher = null; + + private subscriber: EventSubscriber = null; + + private publisher: Publisher = null; + + constructor(private readonly bus: EventBus) { + this.simVarPublisher = new PowerSupplySimvarPublisher(this.bus); + } + + private initialize(): void { + this.publisher = this.bus.getPublisher(); + this.subscriber = this.bus.getSubscriber(); + + this.subscriber.on('msfsAcBus1').whenChanged().handle((powered: number) => this.publisher.pub('acBus1', powered !== 0, false, false)); + this.subscriber.on('msfsAcBus2').whenChanged().handle((powered: number) => this.publisher.pub('acBus2', powered !== 0, false, false)); + this.subscriber.on('msfsAcBusEss').whenChanged().handle((powered: number) => this.publisher.pub('acBusEss', powered !== 0, false, false)); + this.subscriber.on('msfsDcBus1').whenChanged().handle((powered: number) => this.publisher.pub('dcBus1', powered !== 0, false, false)); + this.subscriber.on('msfsDcBus2').whenChanged().handle((powered: number) => this.publisher.pub('dcBus2', powered !== 0, false, false)); + this.subscriber.on('msfsDcBusEss').whenChanged().handle((powered: number) => this.publisher.pub('dcBusEss', powered !== 0, false, false)); + } + + public connectedCallback(): void { + this.initialize(); + + this.simVarPublisher.subscribe('msfsAcBus1'); + this.simVarPublisher.subscribe('msfsAcBus2'); + this.simVarPublisher.subscribe('msfsAcBusEss'); + this.simVarPublisher.subscribe('msfsDcBus1'); + this.simVarPublisher.subscribe('msfsDcBus2'); + this.simVarPublisher.subscribe('msfsDcBusEss'); + } + + public startPublish(): void { + this.simVarPublisher.startPublish(); + } + + public update(): void { + this.simVarPublisher.onUpdate(); + } +} diff --git a/fbw-a380x/src/systems/systems-host/tsconfig.json b/fbw-a380x/src/systems/systems-host/tsconfig.json new file mode 100644 index 00000000000..cf30496d09a --- /dev/null +++ b/fbw-a380x/src/systems/systems-host/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "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/igniter.config.mjs b/igniter.config.mjs index f811a7ca441..352aac55173 100644 --- a/igniter.config.mjs +++ b/igniter.config.mjs @@ -163,6 +163,22 @@ export default new TaskOfTasks("all", [ // Group all typescript and react build tasks together. new TaskOfTasks("build", [ + new ExecTask( + 'extras-host', + 'npm run build-a380x:extras-host', + [ + 'fbw-a380x/src/systems/extras-host', + 'fbw-a380x/out/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/A380X/ExtrasHost' + ] + ), + new ExecTask( + 'systems-host', + 'npm run build-a380x:systems-host', + [ + 'fbw-a380x/src/systems/systems-host', + 'fbw-a380x/out/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/A380X/SystemsHost' + ] + ), new TaskOfTasks("instruments", getA380InstrumentsIgniterTasks(), true), ], true), diff --git a/package.json b/package.json index fcaac3613aa..cfa439f39d5 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,9 @@ "build-a380x:copy-base-files": "mkdir -p fbw-a380x/out/flybywire-aircraft-a380-842 && (rsync -a fbw-a380x/src/base/flybywire-aircraft-a380-842 fbw-a380x/out/ || cp -a -u fbw-a380x/src/base/flybywire-aircraft-a380-842 fbw-a380x/out/)", + "build-a380x:extras-host": "node fbw-a380x/src/systems/extras-host/build.js", "build-a380x:instruments": "mach build --config fbw-a380x/mach.config.js --work-in-config-dir", + "build-a380x:systems-host": "node fbw-a380x/src/systems/systems-host/build.js", "build-a380x:systems": "cargo build -p a380_systems_wasm --target wasm32-wasi --release && wasm-opt -O1 --signext-lowering --enable-bulk-memory -o /external/fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/systems.wasm /external/target/wasm32-wasi/release/a380_systems_wasm.wasm", "build-a380x:fadec": "cd fbw-a380x/src/wasm/fadec_a380 && ./build.sh && wasm-opt -O1 --signext-lowering -o /external/fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/fadec.wasm /external/fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/fadec.wasm",