From 7c6f3d2ee3b03b92d44b44b27f9761965a0fb4d2 Mon Sep 17 00:00:00 2001 From: Michael Corcoran Date: Sat, 12 Oct 2024 17:06:36 +1300 Subject: [PATCH] chore(a380x): port fcu and make fixes (#8919) * chore: straight port of fcu * chore: add actual type information The type errors are now all real. * chore: clean up css vars * chore: missed one * chore: fix erroneous typings * feat: port altitude * feat: port baro and fix presel * doc: fixme * feat(fcu): pointers * fix: add radio nav publisher since it's referenced already * fix: publisher * fix: eslintrc * chore: fix lint (except bugs from old fcu) * fix: allow element to be undefined * fix: logic errors in vs manager (from old code) * fix: logic errors in hdg manager (from old code) * fix: accept undefined element * fix: comment for 2cas * fix: type error in mode manager * fix: type errors in speed manager * fix: add some fixmes --- fbw-a32nx/mach.config.js | 4 +- fbw-a380x/mach.config.js | 5 +- .../legacy/generated/A32NX_Interior_FCU.xml | 50 +- .../FlyByWire_A380_842/panel/panel.cfg | 2 +- .../FlyByWire_A380_842/panel/panel.xml | 12 +- .../html_ui/Images/fbw-a380x/fcu/ADF1.png | Bin 0 -> 3254 bytes .../html_ui/Images/fbw-a380x/fcu/ADF2.png | Bin 0 -> 3228 bytes .../Images => Images/fbw-a380x/fcu}/ARPT.png | Bin .../Images => Images/fbw-a380x/fcu}/Blank.png | Bin .../Images => Images/fbw-a380x/fcu}/CSTR.png | Bin .../Images => Images/fbw-a380x/fcu}/NDB.png | Bin .../fbw-a380x/fcu}/POINTER1.png | Bin .../fbw-a380x/fcu}/POINTER2.png | Bin .../Images => Images/fbw-a380x/fcu}/TERR.png | Bin .../Images => Images/fbw-a380x/fcu}/TRAF.png | Bin .../html_ui/Images/fbw-a380x/fcu/VOR1.png | Bin 0 -> 3257 bytes .../html_ui/Images/fbw-a380x/fcu/VOR2.png | Bin 0 -> 3228 bytes .../Images => Images/fbw-a380x/fcu}/VORD.png | Bin .../Images => Images/fbw-a380x/fcu}/WPT.png | Bin .../Images => Images/fbw-a380x/fcu}/WX.png | Bin .../Instruments/LegacyA380X/FCU/A380_FCU.html | 113 - .../Instruments/LegacyA380X/FCU/A380_FCU.js | 1424 -------- .../FCU/legacy/A32NX_BaseAirliners.js | 1258 ------- .../LegacyA380X/FCU/legacy/A32NX_NavSystem.js | 2927 ----------------- .../systems/instruments/src/FCU/.eslintrc.js | 8 + .../src/FCU/Components/Altitude.tsx | 45 + .../instruments/src/FCU/Components/Baro.tsx | 123 + .../src/FCU/Components/FcuDisplay.tsx | 43 + .../src/FCU/Components/Heading.tsx | 29 + .../instruments/src/FCU/Components/NdData.tsx | 70 + .../instruments/src/FCU/Components/Speed.tsx | 29 + .../src/FCU/Components/VerticalSpeed.tsx | 26 + .../instruments/src/FCU/FcuBaseInstrument.ts | 41 + .../instruments/src/FCU/FcuFsInstrument.tsx | 151 + .../src/FCU/Managers/AltitudeManager.ts | 76 + .../src/FCU/Managers/AutopilotManager.ts | 34 + .../src/FCU/Managers/BaroManager.ts | 98 + .../src/FCU/Managers/HeadingManager.ts | 350 ++ .../src/FCU/Managers/ModeManager.ts | 56 + .../src/FCU/Managers/SpeedManager.ts | 389 +++ .../src/FCU/Managers/TemporaryHax.ts | 72 + .../src/FCU/Managers/VerticalSpeedManager.ts | 337 ++ .../src/FCU/Publishers/FcuPublisher.ts | 43 + .../systems/instruments/src/FCU/config.json | 3 + .../instruments/src/FCU/definitions.scss | 20 + .../instruments/src/FCU/style.scss} | 107 +- .../systems/instruments/src/FCU/tsconfig.json | 17 + .../providers/FGDataPublisher.ts | 25 +- .../providers/OverheadPublisher.ts | 30 + fbw-common/src/systems/shared/src/index.ts | 1 + .../Msfs/MsfsAutopilotAssistancePublisher.ts | 51 + .../Msfs/MsfsRadioNavigationPublisher.ts | 46 + .../shared/src/publishers/Msfs/index.ts | 2 + .../systems/shared/src/publishers/index.ts | 1 + .../fs-base-ui/html_ui/JS/SimPlane.d.ts | 18 +- 55 files changed, 2316 insertions(+), 5820 deletions(-) create mode 100644 fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/ADF1.png create mode 100644 fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/ADF2.png rename fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/{Pages/VCockpit/Instruments/LegacyA380X/FCU/Images => Images/fbw-a380x/fcu}/ARPT.png (100%) rename fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/{Pages/VCockpit/Instruments/LegacyA380X/FCU/Images => Images/fbw-a380x/fcu}/Blank.png (100%) rename fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/{Pages/VCockpit/Instruments/LegacyA380X/FCU/Images => Images/fbw-a380x/fcu}/CSTR.png (100%) rename fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/{Pages/VCockpit/Instruments/LegacyA380X/FCU/Images => Images/fbw-a380x/fcu}/NDB.png (100%) rename fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/{Pages/VCockpit/Instruments/LegacyA380X/FCU/Images => Images/fbw-a380x/fcu}/POINTER1.png (100%) rename fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/{Pages/VCockpit/Instruments/LegacyA380X/FCU/Images => Images/fbw-a380x/fcu}/POINTER2.png (100%) rename fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/{Pages/VCockpit/Instruments/LegacyA380X/FCU/Images => Images/fbw-a380x/fcu}/TERR.png (100%) rename fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/{Pages/VCockpit/Instruments/LegacyA380X/FCU/Images => Images/fbw-a380x/fcu}/TRAF.png (100%) create mode 100644 fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/VOR1.png create mode 100644 fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/VOR2.png rename fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/{Pages/VCockpit/Instruments/LegacyA380X/FCU/Images => Images/fbw-a380x/fcu}/VORD.png (100%) rename fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/{Pages/VCockpit/Instruments/LegacyA380X/FCU/Images => Images/fbw-a380x/fcu}/WPT.png (100%) rename fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/{Pages/VCockpit/Instruments/LegacyA380X/FCU/Images => Images/fbw-a380x/fcu}/WX.png (100%) delete mode 100644 fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/A380_FCU.html delete mode 100644 fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/A380_FCU.js delete mode 100644 fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/legacy/A32NX_BaseAirliners.js delete mode 100644 fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/legacy/A32NX_NavSystem.js create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/.eslintrc.js create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Components/Altitude.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Components/Baro.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Components/FcuDisplay.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Components/Heading.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Components/NdData.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Components/Speed.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Components/VerticalSpeed.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/FcuBaseInstrument.ts create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/FcuFsInstrument.tsx create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Managers/AltitudeManager.ts create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Managers/AutopilotManager.ts create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Managers/BaroManager.ts create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Managers/HeadingManager.ts create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Managers/ModeManager.ts create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Managers/SpeedManager.ts create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Managers/TemporaryHax.ts create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Managers/VerticalSpeedManager.ts create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/Publishers/FcuPublisher.ts create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/config.json create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/definitions.scss rename fbw-a380x/src/{base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/A380_FCU.css => systems/instruments/src/FCU/style.scss} (56%) create mode 100644 fbw-a380x/src/systems/instruments/src/FCU/tsconfig.json create mode 100644 fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/providers/OverheadPublisher.ts create mode 100644 fbw-common/src/systems/shared/src/publishers/Msfs/MsfsAutopilotAssistancePublisher.ts create mode 100644 fbw-common/src/systems/shared/src/publishers/Msfs/MsfsRadioNavigationPublisher.ts create mode 100644 fbw-common/src/systems/shared/src/publishers/Msfs/index.ts create mode 100644 fbw-common/src/systems/shared/src/publishers/index.ts diff --git a/fbw-a32nx/mach.config.js b/fbw-a32nx/mach.config.js index 479d2770dbb..ea8bac3beea 100644 --- a/fbw-a32nx/mach.config.js +++ b/fbw-a32nx/mach.config.js @@ -47,10 +47,10 @@ module.exports = { ], }; -function msfsAvionicsInstrument(name, folder = name) { +function msfsAvionicsInstrument(name, index = 'instrument.tsx') { return { name, - index: `src/systems/instruments/src/${folder}/instrument.tsx`, + index: `src/systems/instruments/src/${name}/${index}`, simulatorPackage: { type: 'baseInstrument', templateId: `A32NX_${name}`, diff --git a/fbw-a380x/mach.config.js b/fbw-a380x/mach.config.js index d78f3610a3a..0f53acc371e 100644 --- a/fbw-a380x/mach.config.js +++ b/fbw-a380x/mach.config.js @@ -30,6 +30,7 @@ module.exports = { instruments: [ msfsAvionicsInstrument('Clock'), msfsAvionicsInstrument('EWD'), + msfsAvionicsInstrument('FCU', 'FcuBaseInstrument.ts'), msfsAvionicsInstrument('MFD'), msfsAvionicsInstrument('ND'), msfsAvionicsInstrument('PFD'), @@ -44,10 +45,10 @@ module.exports = { ], }; -function msfsAvionicsInstrument(name, folder = name) { +function msfsAvionicsInstrument(name, index = 'instrument.tsx') { return { name, - index: `src/systems/instruments/src/${folder}/instrument.tsx`, + index: `src/systems/instruments/src/${name}/${index}`, simulatorPackage: { type: 'baseInstrument', templateId: `A380X_${name}`, diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/behaviour/legacy/generated/A32NX_Interior_FCU.xml b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/behaviour/legacy/generated/A32NX_Interior_FCU.xml index c3fd7624ebe..080e0690448 100644 --- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/behaviour/legacy/generated/A32NX_Interior_FCU.xml +++ b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/model/behaviour/legacy/generated/A32NX_Interior_FCU.xml @@ -35,9 +35,9 @@ TT:COCKPIT.TOOLTIPS.AUTOPILOT_PANEL_BARO_KNOB_DECREASE TurnLeft #ID# - 0 + 0 1 - 2 + 2 3 AUTOPILOT_Knob_Baro_#ID##SUFFIX_ID# AUTOPILOT_Knob_Baro @@ -46,73 +46,41 @@ - (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_STD_BARO# != (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_STD_QNH# != and if{ + (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_STD_QFE# != (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_STD_QNH# != and if{ (L:XMLVAR_Baro_Selector_HPA_#BARO_ID#) if{ #BARO_ID# (A:KOHLSMAN SETTING MB:1, mbars) -- 16 * (>K:2:KOHLSMAN_SET) } els{ #BARO_ID# (>K:KOHLSMAN_DEC) } } els{ - (L:XMLVAR_Baro_Selector_HPA_#BARO_ID#) if{ - (L:A380X_EFIS_L_BARO_PRESELECTED) 1 - (>L:A380X_EFIS_L_BARO_PRESELECTED) - } els{ - (L:A380X_EFIS_L_BARO_PRESELECTED) 0.01 - (>L:A380X_EFIS_L_BARO_PRESELECTED) - } + (>H:A380X_FCU_BARO_PRESEL_DEC) } - (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_STD_BARO# != (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_STD_QNH# != and if{ + (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_STD_QFE# != (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_STD_QNH# != and if{ (L:XMLVAR_Baro_Selector_HPA_#BARO_ID#) if{ #BARO_ID# (A:KOHLSMAN SETTING MB:1, mbars) ++ 16 * (>K:2:KOHLSMAN_SET) } els{ #BARO_ID# (>K:KOHLSMAN_INC) } } els{ - (L:XMLVAR_Baro_Selector_HPA_#BARO_ID#) if{ - (L:A380X_EFIS_L_BARO_PRESELECTED) 1 + (>L:A380X_EFIS_L_BARO_PRESELECTED) - } els{ - (L:A380X_EFIS_L_BARO_PRESELECTED) 0.01 + (>L:A380X_EFIS_L_BARO_PRESELECTED) - } + (>H:A380X_FCU_BARO_PRESEL_INC) } - (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_BARO# == if{ - #MODE_STD_BARO# (>L:XMLVAR_Baro#BARO_ID#_Mode) - } els{ - (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_QNH# == if{ - #MODE_STD_QNH# (>L:XMLVAR_Baro#BARO_ID#_Mode) - (L:XMLVAR_Baro_Selector_HPA_#BARO_ID#) if{ - 1013.25 (>L:A380X_EFIS_L_BARO_PRESELECTED) - } els{ - 29.92 (>L:A380X_EFIS_L_BARO_PRESELECTED) - } - } - } + #MODE_STD_QNH# (>L:XMLVAR_Baro#BARO_ID#_Mode) - (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_STD_BARO# == if{ - #MODE_BARO# (>L:XMLVAR_Baro#BARO_ID#_Mode) - } els{ - (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_STD_QNH# == if{ - (L:XMLVAR_Baro_Selector_HPA_#BARO_ID#) if{ - #BARO_ID# (L:A380X_EFIS_L_BARO_PRESELECTED) 16 * (>K:2:KOHLSMAN_SET) - } els{ - #BARO_ID# (L:A380X_EFIS_L_BARO_PRESELECTED) 541.822 * (>K:2:KOHLSMAN_SET) - } - #MODE_QNH# (>L:XMLVAR_Baro#BARO_ID#_Mode) - } els{ - (L:XMLVAR_Baro#BARO_ID#_Mode) ! (>L:XMLVAR_Baro#BARO_ID#_Mode) - } - } + #MODE_QNH# (>L:XMLVAR_Baro#BARO_ID#_Mode) 0 36 0 100 - (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_STD_BARO# == + (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_STD_QFE# == (L:XMLVAR_Baro#BARO_ID#_Mode) #MODE_STD_QNH# == or ? 50 (O:_PushAnimVar) ? 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 b88735a1b17..bf497acb6f7 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 @@ -82,7 +82,7 @@ size_mm=5120,5120 pixel_size=5120,5120 texture=$FCU -htmlgauge00=LegacyA380X/FCU/A380_FCU.html, 0,0,5120,5120 +htmlgauge00=A380X/FCU/FCU.html, 0,0,5120,5120 [VCockpit10] size_mm=512,512 diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.xml b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.xml index c309cfde6f0..d437a138669 100644 --- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.xml +++ b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.xml @@ -1,8 +1,14 @@ - - - + + A380X_FCU + + + + + + + diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/ADF1.png b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/ADF1.png new file mode 100644 index 0000000000000000000000000000000000000000..1b89e950647f4b8a7b9052566096c6d2cc70da82 GIT binary patch literal 3254 zcmeAS@N?(olHy`uVBq!ia0y~yV7bG{uhv}ide^R#B z+w2?8kwSLD8yvBD(dPW8Jx@;MJh#1FYjS?`zFN6;bD#fw_4(emx7RAqo1NdJv%YS= z?&+e}wPv8aDHlHi@)_w-8f6bIu~zgN+U;e1(EaleRST@Y(#bvk`HxQRTK9VHS75RG z=V~0+?|uJK3fptl+kR)vORtP8O5OI^3>1;`ZqEB3V}1Vf)#rPGrR?W|AcTW|M1_jVqzP6f4pUVXp0 zNoV`L^v!e6-+um)x>fryX(KKCdKuK>X}Bfv_;NJEz8U&dZTelO-mu>(zZ-LF&yOn9X?PF%Y-2;rf_nUVJ!k(!?4MwNME1w=g@c3VxzF$W{e7?Q zOWfnp6@6r2Vj0;eC*Bx zd8M}Aw(HNfFNxWi*L!H)wRQPdQg4qMz=!swF=x6WgY?o7%Z}83$faYiGAKM<6(lVp z#0f`}yX97lJuFkGbY(%N1{)Vn*62ALqXeY|6t;z|F|r!WKK4~OSQTtIy`uT4GDxT< zM;rqkuAyp2C8h}vCp$<|nHz$R2Y@9uYc`T|H$U;G2CLrr@v`gua$YXXiR5LT_bFZ$ zE=Jb_VC9cx;jwAdfJDUP>L%b$Z^`5zPnV^Eic1x8mf7ql$O5F zwB%IuU={_w6>2%0!U`#YMJH>qFJS74?x}HSyP*5D#2R>3veB?vE(vTN9fFXZ+}l7{ zYt179LnB!nG6UHB`TSMDBUbiPFF?#+=P6fS|G<%nv*_iZMna$PWOH!GE`}zK(0U=Z0L0K zP~*%OcH+Iev7A9m(JkQCd^iiCd=!EHx%XRt_M%5C!&D@{fj=ATG{Z*Lsds(r68lhE z&LqfG$y!XTmTj!n*&t;apsb_%WSK&_mEju9I{L1)w!~Q_N4*C+*-4`vK6SX#L@(?v zovj$t4EoNvLgSX*Z*6b5-xrZQ*Ne)@S|BlcHoAuB0BK*tm9XJDO&IBLerFHL)}N%t zqu6@XMWaZ{w1BZ_X@rfg&TVPj#*B(-9IeR>FwH(4r`0a?Cf>^_9`0iQNW~o!-g@GH UXLYhBln>VU-HEZ9T`A{(05{7f2mk;8 literal 0 HcmV?d00001 diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/ARPT.png b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/ARPT.png similarity index 100% rename from fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/ARPT.png rename to fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/ARPT.png diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/Blank.png b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/Blank.png similarity index 100% rename from fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/Blank.png rename to fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/Blank.png diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/CSTR.png b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/CSTR.png similarity index 100% rename from fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/CSTR.png rename to fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/CSTR.png diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/NDB.png b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/NDB.png similarity index 100% rename from fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/NDB.png rename to fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/NDB.png diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/POINTER1.png b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/POINTER1.png similarity index 100% rename from fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/POINTER1.png rename to fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/POINTER1.png diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/POINTER2.png b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/POINTER2.png similarity index 100% rename from fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/POINTER2.png rename to fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/POINTER2.png diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/TERR.png b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/TERR.png similarity index 100% rename from fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/TERR.png rename to fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/TERR.png diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/TRAF.png b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/TRAF.png similarity index 100% rename from fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/TRAF.png rename to fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/TRAF.png diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/VOR1.png b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/VOR1.png new file mode 100644 index 0000000000000000000000000000000000000000..ff68e7cb9c1b22206f88ca6fbb9b3573f2690f14 GIT binary patch literal 3257 zcmeH}Z%7ky7{`CB2xkdpfo7ol!xaWC(&-eSO zJdt!Xz(2$v01%Lv5SI+VnhyYQGX2&vZ!$e++Zf^IB}gs-z!S3%=YEv-Apq;M6XRk| zU9Q9WKUOw(ghZc=vOGDqImCK||GF#a87HPsCU&Rxrgt5tIv$S{sQKEUwB7t25WHgks0k3z;n}fSXzsDYEa21p@x{uwPLMx=3IOFvE^G?saO-qiXZm^D;oltiU8QYtCc+) zoshShkt;U`*b2dpCHdibYHAfz+yGamKE9VT6Q~;WX|diL{Fn7X`SR1f?oQ%WzQ@y@ z>QCBe=d4ozw;zIYh-=wKjL0G03gF4~i*ggC!-UL8onk#ZOx&zM8gVnv(Ez&)ydv0j z3hyX%I)$2yEKhuoxr=V&CD)rClbkKJR5nDhVUv4vrG-542x2&9K#yqmN# zCQIoQI=LjBO5fwSIBKm^l&zTTB8c#>O_mAC(&CLN(@L6QpkNsrnDblOB4U{57XTve ze$UQ)6I9odHA@=`bZ7%nf|$}67Zx}SmN}(F`}qd|h1!<^%q(oBe^wPeV-l<=}Y=7I~C>*l43Dl%?BTnwFd9 z9hhBCvjA_A&^oN#TWMVqjG{3s$R#*~(XhyiQ zyrUl~LawO1+h}fPvWT2ZtgX7T34zx%z3I(ex7*!1SAP^?|5iWBABP9t!+~?o^FGh> zRv(v$gM-!u0RVy%6XH(*uw)PbK~}&rSjqZ!Mh_vdAR)B~0O3)737Ypdv;nX}k{Ev= z`NG}t5B;*LU7>LYQkol*1#b_n$upnoI&%Md-{PJ-?&0S#X36;;#2h|w#X=)$R7v2@ zW9`ikHDk`(q@}3+F>M(CR?y2{Y=2BWKsD3jCiQL?&e##V$Au26)YDJwIZj4QF^`0~ z44b-t$hU{})uWn{e5_TizBG{mZwG*(dWgR**zzoxhl7>tdAPbHhKGQ_b3CjHfzJY9 z^ERCTLR><@VhES7|C0x!ot^w!b=~0~Cn7**cbH#)U?jIDY9gbj9>vI=RypBf+d4vR zC0s0RfV`Aq+NMploidZNbfcvxl8F#IaHl-BZdl*I-tbM1a9GQyH%f~2j?g;ct2X*9 zCT(iUEuV2uR{iIs&D)sw+!IS7lU-}f;bkJhOo-9HZM#3arjXfM+xvTIewMKu&aIx^|BF@n6^@~NL`7X3dOo0qQn4aia^;^nB>Vb)y? z>{5O4Z>M|KX4gCoD?Edd?p~UJ6y--&m8ARn5IHX`Qbu0OmJ^h=Nld4`QoO9!j%#_J zg}&=0-x;Yo2bGg5;cHO3d5s$tnUaCxQDcS2JJY>jrjWH?0^@ZBvj|p)g5amCKt*7t zU*qJSItup4YVk0NSrLC`ika^b_Pcca{CmMBmFV;^pk~+ElK30jLpWR?gdryUiz0+DI=5= zoOot!)rX_z@Q+E-?X#)rm9aFA&Z&|+jY$ gL?gErB67Z1;g-AHVK*~I<7PJ#4@=@T2eT@_1LFZM#sB~S literal 0 HcmV?d00001 diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/VORD.png b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/VORD.png similarity index 100% rename from fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/VORD.png rename to fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/VORD.png diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/WPT.png b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/WPT.png similarity index 100% rename from fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/WPT.png rename to fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/WPT.png diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/WX.png b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/WX.png similarity index 100% rename from fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/Images/WX.png rename to fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Images/fbw-a380x/fcu/WX.png diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/A380_FCU.html b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/A380_FCU.html deleted file mode 100644 index 1c74c89b6d0..00000000000 --- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/A380_FCU.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/A380_FCU.js b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/A380_FCU.js deleted file mode 100644 index e870d952c4f..00000000000 --- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/A380_FCU.js +++ /dev/null @@ -1,1424 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -class A320_Neo_FCU extends BaseAirliners { - constructor() { - super(); - this.initDuration = 3000; - this.electricity = document.querySelector('#Electricity'); - } - - get templateID() { - return 'A320_Neo_FCU'; - } - - connectedCallback() { - super.connectedCallback(); - RegisterViewListener('JS_LISTENER_KEYEVENT', this.onListenerRegistered.bind(this)); - this.maxUpdateBudget = 12; - } - - disconnectedCallback() { - super.disconnectedCallback(); - } - - onListenerRegistered() { - this.mainPage = new A320_Neo_FCU_MainPage(); - this.pageGroups = [ - new NavSystemPageGroup('Main', this, [ - this.mainPage, - ]), - ]; - } - - reboot() { - super.reboot(); - this.mainPage.reboot(); - } - - onUpdate(_deltaTime) { - super.onUpdate(_deltaTime); - - const newStyle = SimVar.GetSimVarValue('L:A32NX_ELEC_DC_ESS_BUS_IS_POWERED', 'Bool') - || SimVar.GetSimVarValue('L:A32NX_ELEC_DC_2_BUS_IS_POWERED', 'Bool') ? 'block' : 'none'; - if (newStyle === 'block' && newStyle !== this.electricity.style.display) { - if (!SimVar.GetSimVarValue('AUTOPILOT FLIGHT DIRECTOR ACTIVE:1', 'bool')) { - SimVar.SetSimVarValue('K:TOGGLE_FLIGHT_DIRECTOR', 'number', 1); - } - if (!SimVar.GetSimVarValue('AUTOPILOT FLIGHT DIRECTOR ACTIVE:2', 'bool')) { - SimVar.SetSimVarValue('K:TOGGLE_FLIGHT_DIRECTOR', 'number', 2); - } - } - this.electricity.style.display = newStyle; - } - - onEvent(_event) { - } -} - -class A320_Neo_FCU_MainElement extends NavSystemElement { - init(root) { - } - - onEnter() { - } - - onUpdate(_deltaTime) { - } - - onExit() { - } - - onEvent(_event) { - } -} - -class A320_Neo_FCU_MainPage extends NavSystemPage { - constructor() { - super('Main', 'Mainframe', new A320_Neo_FCU_MainElement()); - this.largeScreen = new A320_Neo_FCU_LargeScreen(); - this.smallScreen = new A320_Neo_FCU_SmallScreen(); - this.element = new NavSystemElementGroup([ - this.largeScreen, - this.smallScreen, - ]); - } - - init() { - super.init(); - } - - onEvent(_event) { - this.largeScreen.onEvent(_event); - } - - reboot() { - this.largeScreen.reboot(); - this.smallScreen.reboot(); - } -} - -class A320_Neo_FCU_Component { - getDivElement(_name) { - if (this.divRef != null) { - return this.divRef.querySelector(`#${_name}`); - } - } - - set textValueContent(_textContent) { - if (this.textValue != null) { - this.textValue.textContent = _textContent; - this.textValue.innerHTML = this.textValue.innerHTML.replace('{sp}', ' '); - } - } - - getElement(_type, _name) { - if (this.divRef != null) { - const allText = this.divRef.getElementsByTagName(_type); - if (allText != null) { - for (let i = 0; i < allText.length; ++i) { - if (allText[i].id == _name) { - return allText[i]; - } - } - } - } - return null; - } - - getTextElement(_name) { - return this.getElement('text', _name); - } - - setTextElementActive(_text, _active, _baro) { - if (_text != null) { - _text.setAttribute('class', `Common ${_active ? 'Active' : 'Inactive'} ${_baro ? 'BaroValue' : ''}`); - } - } - - setElementVisibility(_element, _show) { - if (_element != null) { - _element.style.display = _show ? 'block' : 'none'; - } - } - - constructor(_gps, _divName) { - this.gps = _gps; - this.divRef = _gps.getChildById(_divName); - this.textValue = this.getTextElement('Value'); - } - - reboot() { - this.init(); - } -} - -class A320_Neo_FCU_Speed extends A320_Neo_FCU_Component { - constructor() { - super(...arguments); - - this.backToIdleTimeout = 10000; - this.MIN_SPEED = 100; - this.MAX_SPEED = 399; - this.MIN_MACH = 0.10; - this.MAX_MACH = 0.99; - - this.isActive = false; - this.isManaged = false; - this.showSelectedSpeed = true; - this.currentValue = this.MIN_SPEED; - this.selectedValue = this.MIN_SPEED; - this.isMachActive = false; - this.inSelection = false; - this.isSelectedValueActive = false; - this.isValidV2 = false; - this.isVerticalModeSRS = false; - this.isTargetManaged = false; - - this._rotaryEncoderCurrentSpeed = 1; - this._rotaryEncoderMaximumSpeed = 10; - this._rotaryEncoderTimeout = 300; - this._rotaryEncoderIncrement = 0.15; - this._rotaryEncoderPreviousTimestamp = 0; - this.init(); - this.update(0); - } - - init() { - this.isValidV2 = false; - this.isVerticalModeSRS = false; - this.selectedValue = this.MIN_SPEED; - this.currentValue = this.MIN_SPEED; - this.targetSpeed = this.MIN_SPEED; - this.isTargetManaged = false; - this.isMachActive = false; - this.textSPD = this.getTextElement('SPD'); - this.textMACH = this.getTextElement('MACH'); - this.textKNOTS = this.getTextElement('KNOTS'); - Coherent.call('AP_SPD_VAR_SET', 0, this.MIN_SPEED).catch(console.error); - SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_SPEED_SELECTED', 'number', this.MIN_SPEED); - SimVar.SetSimVarValue('K:AP_MANAGED_SPEED_IN_MACH_OFF', 'number', 0); - this.onPull(); - } - - update(_deltaTime) { - const isManaged = Simplane.getAutoPilotAirspeedManaged() && this.isTargetManaged; - const showSelectedSpeed = this.inSelection || !isManaged; - const isMachActive = SimVar.GetSimVarValue('AUTOPILOT MANAGED SPEED IN MACH', 'bool'); - const isExpedModeOn = SimVar.GetSimVarValue('L:A32NX_FMA_EXPEDITE_MODE', 'number') === 1; - const isManagedSpeedAvailable = this.isManagedSpeedAvailable(); - - // detect if managed speed should engage due to V2 entry or SRS mode - if (this.shouldEngageManagedSpeed()) { - this.onPush(); - } - // detect if EXPED mode was engaged - if (!isManaged && isExpedModeOn && isManagedSpeedAvailable) { - this.onPush(); - } - // when both AP and FD off -> revert to selected - if (isManaged && !isManagedSpeedAvailable) { - this.onPull(); - } - - // update speed - if (!isManaged && this.selectedValue > 0) { - // mach mode was switched - if (isMachActive != this.isMachActive) { - if (isMachActive || this.selectedValue > 1) { - // KIAS -> Mach - this.selectedValue = this.clampMach( - Math.round(SimVar.GetGameVarValue('FROM KIAS TO MACH', 'number', this.selectedValue) * 100) / 100, - ); - } else { - // Mach -> KIAS - this.selectedValue = this.clampSpeed( - Math.round(SimVar.GetGameVarValue('FROM MACH TO KIAS', 'number', this.selectedValue)), - ); - } - } - // get current target speed - let targetSpeed = (isMachActive || this.selectedValue < 1) - ? SimVar.GetGameVarValue('FROM MACH TO KIAS', 'number', this.selectedValue) - : this.selectedValue; - // clamp speed into valid range - targetSpeed = this.clampSpeed(targetSpeed); - // set target speed - if (targetSpeed !== this.targetSpeed) { - Coherent.call('AP_SPD_VAR_SET', 0, targetSpeed).catch(console.error); - this.targetSpeed = targetSpeed; - } - // detect mismatch - if (Simplane.getAutoPilotAirspeedHoldValue() !== this.targetSpeed) { - Coherent.call('AP_SPD_VAR_SET', 0, targetSpeed).catch(console.error); - } - } else { - this.targetSpeed = -1; - } - - this.refresh( - true, - isManaged, - showSelectedSpeed, - isMachActive, - this.selectedValue, - SimVar.GetSimVarValue('L:A32NX_OVHD_INTLT_ANN', 'number') == 0, - ); - } - - shouldEngageManagedSpeed() { - const managedSpeedTarget = SimVar.GetSimVarValue('L:A32NX_SPEEDS_MANAGED_PFD', 'knots'); - const isValidV2 = SimVar.GetSimVarValue('L:AIRLINER_V2_SPEED', 'knots') >= 90; - const isVerticalModeSRS = SimVar.GetSimVarValue('L:A32NX_FMA_VERTICAL_MODE', 'enum') === 40; - - // V2 is entered into MCDU (was not set -> set) - // SRS mode engages (SRS no engaged -> engaged) - let shouldEngage = false; - if ((!this.isValidV2 && isValidV2) || (!this.isVerticalModeSRS && isVerticalModeSRS)) { - shouldEngage = true; - } - - // store state - if (!isValidV2 || managedSpeedTarget >= 90) { - // store V2 state only if managed speed target is valid (to debounce) - this.isValidV2 = isValidV2; - } - this.isVerticalModeSRS = isVerticalModeSRS; - - return shouldEngage; - } - - isManagedSpeedAvailable() { - // managed speed is available when flight director or autopilot is engaged, or in approach phase (FMGC flight phase) - return (Simplane.getAutoPilotFlightDirectorActive(1) - || Simplane.getAutoPilotFlightDirectorActive(2) - || SimVar.GetSimVarValue('L:A32NX_AUTOPILOT_ACTIVE', 'number') === 1 - || SimVar.GetSimVarValue('L:A32NX_FMGC_FLIGHT_PHASE', 'number') === 5) - && SimVar.GetSimVarValue('L:A32NX_SPEEDS_MANAGED_PFD', 'knots') >= 90; - } - - refresh(_isActive, _isManaged, _showSelectedSpeed, _machActive, _value, _lightsTest, _force = false) { - if ((_isActive != this.isActive) - || (_isManaged != this.isManaged) - || (_showSelectedSpeed != this.showSelectedSpeed) - || (_machActive != this.isMachActive) - || (_value != this.currentValue) - || (_lightsTest !== this.lightsTest) - || _force) { - this.isActive = _isActive; - if (_isManaged !== this.isManaged && _isManaged) { - this.inSelection = false; - this.isSelectedValueActive = false; - this.selectedValue = -1; - console.warn('reset due to _isManaged == true'); - } - this.isManaged = _isManaged; - SimVar.SetSimVarValue("L:A32NX_FCU_SPD_MANAGED_DOT", "boolean", this.isManaged); - if (_showSelectedSpeed !== this.showSelectedSpeed && !_showSelectedSpeed) { - this.inSelection = false; - this.isSelectedValueActive = false; - this.selectedValue = -1; - console.warn('reset due to _showSelectedSpeed == false'); - } - this.showSelectedSpeed = _showSelectedSpeed; - SimVar.SetSimVarValue('L:A32NX_FCU_SPD_MANAGED_DASHES', 'boolean', this.isManaged && !this.showSelectedSpeed); - if (this.currentValue != _value) { - SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_SPEED_SELECTED', 'number', _value); - } - this.currentValue = _machActive ? _value * 100 : _value; - this.isMachActive = _machActive; - this.setTextElementActive(this.textSPD, !_machActive); - this.setTextElementActive(this.textMACH, _machActive); - if (this.isMachActive) { - this.setTextElementActive(this.textKNOTS, false); - } else { - this.setTextElementActive(this.textKNOTS, !(this.isManaged && !this.showSelectedSpeed)); - } - this.lightsTest = _lightsTest; - if (this.lightsTest) { - this.textValueContent = '.8.8.8'; - this.setTextElementActive(this.textSPD, true); - this.setTextElementActive(this.textMACH, true); - this.setTextElementActive(this.textKNOTS, true); - return; - } - let value = _machActive ? Math.max(this.currentValue, 0) : Math.max(this.currentValue, 100); - value = Math.round(value).toString().padStart(3, '0'); - if (!_isManaged && this.currentValue > 0) { - if (_machActive) { - value = `${value.substring(0, 1)}.${value.substring(1)}`; - } - this.textValueContent = value; - } else if (_isManaged || this.currentValue < 0) { - if (_showSelectedSpeed) { - if (_machActive) { - value = `${value.substring(0, 1)}.${value.substring(1)}`; - } - this.textValueContent = value; - } else { - this.textValueContent = '---'; - } - } - } - } - - clampSpeed(value) { - return Utils.Clamp(value, this.MIN_SPEED, this.MAX_SPEED); - } - - clampMach(value) { - return Utils.Clamp(value, this.MIN_MACH, this.MAX_MACH); - } - - getCurrentSpeed() { - return this.clampSpeed(Math.round(Simplane.getIndicatedSpeed())); - } - - getCurrentMach() { - return this.clampMach(Math.round(Simplane.getMachSpeed() * 100) / 100); - } - - onRotate() { - clearTimeout(this._resetSelectionTimeout); - if (!this.inSelection && this.isManaged) { - this.inSelection = true; - if (!this.isSelectedValueActive) { - if (this.isMachActive) { - this.selectedValue = this.getCurrentMach(); - } else { - this.selectedValue = this.getCurrentSpeed(); - } - } - } - this.isSelectedValueActive = true; - if (this.inSelection) { - this._resetSelectionTimeout = setTimeout(() => { - this.selectedValue = -1; - this.isSelectedValueActive = false; - this.inSelection = false; - }, this.backToIdleTimeout); - } - } - - onPush() { - if (!this.isManagedSpeedAvailable()) { - return; - } - clearTimeout(this._resetSelectionTimeout); - SimVar.SetSimVarValue('K:SPEED_SLOT_INDEX_SET', 'number', 2); - this.inSelection = false; - this.isSelectedValueActive = false; - this.isTargetManaged = true; - } - - onPull() { - clearTimeout(this._resetSelectionTimeout); - if (!this.isSelectedValueActive) { - if (this.isMachActive) { - this.selectedValue = this.getCurrentMach(); - } else { - this.selectedValue = this.getCurrentSpeed(); - } - } - SimVar.SetSimVarValue('K:SPEED_SLOT_INDEX_SET', 'number', 1); - this.inSelection = false; - this.isSelectedValueActive = false; - this.isTargetManaged = false; - } - - onSwitchSpeedMach() { - clearTimeout(this._resetSelectionTimeout); - this.inSelection = false; - this.isSelectedValueActive = false; - if (this.isMachActive) { - SimVar.SetSimVarValue('K:AP_MANAGED_SPEED_IN_MACH_OFF', 'number', 0); - } else { - SimVar.SetSimVarValue('K:AP_MANAGED_SPEED_IN_MACH_ON', 'number', 0); - } - } - - onPreSelSpeed(isMach) { - clearTimeout(this._resetSelectionTimeout); - SimVar.SetSimVarValue('K:SPEED_SLOT_INDEX_SET', 'number', 1); - this.inSelection = false; - this.isSelectedValueActive = false; - this.isTargetManaged = false; - this.isMachActive = isMach; - if (isMach) { - this.selectedValue = SimVar.GetSimVarValue('L:A32NX_MachPreselVal', 'mach'); - SimVar.SetSimVarValue('K:AP_MANAGED_SPEED_IN_MACH_ON', 'number', 1); - } else { - this.selectedValue = SimVar.GetSimVarValue('L:A32NX_SpeedPreselVal', 'knots'); - SimVar.SetSimVarValue('K:AP_MANAGED_SPEED_IN_MACH_OFF', 'number', 1); - } - } - - getRotationSpeed() { - if (this._rotaryEncoderCurrentSpeed < 1 - || (Date.now() - this._rotaryEncoderPreviousTimestamp) > this._rotaryEncoderTimeout) { - this._rotaryEncoderCurrentSpeed = 1; - } else { - this._rotaryEncoderCurrentSpeed += this._rotaryEncoderIncrement; - } - this._rotaryEncoderPreviousTimestamp = Date.now(); - return Math.min(this._rotaryEncoderMaximumSpeed, Math.floor(this._rotaryEncoderCurrentSpeed)); - } - - onEvent(_event) { - if (_event === 'SPEED_INC') { - // use rotary encoder to speed dialing up / down - if (this.isMachActive) { - this.selectedValue = this.clampMach(this.selectedValue + 0.01); - } else { - this.selectedValue = this.clampSpeed(this.selectedValue + this.getRotationSpeed()); - } - this.onRotate(); - } else if (_event === 'SPEED_DEC') { - // use rotary encoder to speed dialing up / down - if (this.isMachActive) { - this.selectedValue = this.clampMach(this.selectedValue - 0.01); - } else { - this.selectedValue = this.clampSpeed(this.selectedValue - this.getRotationSpeed()); - } - this.onRotate(); - } else if (_event === 'SPEED_PUSH') { - this.onPush(); - } else if (_event === 'SPEED_PULL') { - this.onPull(); - } else if (_event === 'SPEED_SET') { - const value = SimVar.GetSimVarValue('L:A320_Neo_FCU_SPEED_SET_DATA', 'number'); - if (this.isMachActive) { - this.selectedValue = this.clampMach(value / 100.0); - } else { - this.selectedValue = this.clampSpeed(value); - } - this.isSelectedValueActive = true; - this.onRotate(); - } else if (_event === 'SPEED_TOGGLE_SPEED_MACH') { - this.onSwitchSpeedMach(); - } else if (_event === 'USE_PRE_SEL_SPEED') { - this.onPreSelSpeed(false); - } else if (_event === 'USE_PRE_SEL_MACH') { - this.onPreSelSpeed(true); - } else if (_event === 'SPEED_TCAS') { - this.onPull(); - if (this.isMachActive) { - this.selectedValue = this.getCurrentMach(); - } else { - this.selectedValue = this.getCurrentSpeed(); - } - } - } -} - -class A320_Neo_FCU_Autopilot extends A320_Neo_FCU_Component { - constructor() { - super(...arguments); - this.init(); - this.update(0); - } - - init() { - } - - onEvent(_event) { - if (_event === 'AP_1_PUSH') { - SimVar.SetSimVarValue('K:A32NX.FCU_AP_1_PUSH', 'number', 0); - } else if (_event === 'AP_2_PUSH') { - SimVar.SetSimVarValue('K:A32NX.FCU_AP_2_PUSH', 'number', 0); - } else if (_event === 'LOC_PUSH') { - SimVar.SetSimVarValue('K:A32NX.FCU_LOC_PUSH', 'number', 0); - } else if (_event === 'APPR_PUSH') { - SimVar.SetSimVarValue('K:A32NX.FCU_APPR_PUSH', 'number', 0); - } else if (_event === 'EXPED_PUSH') { - SimVar.SetSimVarValue('K:A32NX.FCU_EXPED_PUSH', 'number', 0); - } else if (_event === 'TRUEMAG_PUSH') { - SimVar.SetSimVarValue('L:A32NX_PUSH_TRUE_REF', 'bool', !SimVar.GetSimVarValue('L:A32NX_PUSH_TRUE_REF', 'bool')); - } - } - - update(_deltaTime) { - } -} - -class A320_Neo_FCU_Heading extends A320_Neo_FCU_Component { - constructor() { - super(...arguments); - this.backToIdleTimeout = 45000; - this.inSelection = false; - - this._rotaryEncoderCurrentSpeed = 1; - this._rotaryEncoderMaximumSpeed = 5; - this._rotaryEncoderTimeout = 350; - this._rotaryEncoderIncrement = 0.1; - this._rotaryEncoderPreviousTimestamp = 0; - this.init(); - this.update(0); - } - - init() { - this.textHDG = this.getTextElement('HDG'); - this.textTRK = this.getTextElement('TRK'); - this.signDegrees = this.getTextElement('DEGREES'); - this.currentValue = -1; - this.selectedValue = Simplane.getAltitudeAboveGround() > 1000 ? this.getCurrentHeading() : 0; - this.isSelectedValueActive = true; - this.isPreselectionModeActive = false; - this.wasHeadingSync = false; - this.refresh(true, false, false, false, true, 0, false, true); - } - - onRotate() { - const lateralMode = SimVar.GetSimVarValue('L:A32NX_FMA_LATERAL_MODE', 'Number'); - const lateralArmed = SimVar.GetSimVarValue('L:A32NX_FMA_LATERAL_ARMED', 'Number'); - const isTRKMode = SimVar.GetSimVarValue('L:A32NX_TRK_FPA_MODE_ACTIVE', 'Bool'); - const radioHeight = SimVar.GetSimVarValue('RADIO HEIGHT', 'feet'); - - if (!this.inSelection - && (this.isManagedModeActive(lateralMode) - || this.isPreselectionAvailable(radioHeight, lateralMode))) { - this.inSelection = true; - if (!this.isSelectedValueActive) { - if (isTRKMode) { - this.selectedValue = this.getCurrentTrack(); - } else { - this.selectedValue = this.getCurrentHeading(); - } - } - } - - this.isSelectedValueActive = true; - - if (this.inSelection && !this.isPreselectionAvailable(radioHeight, lateralMode)) { - this.isPreselectionModeActive = false; - clearTimeout(this._resetSelectionTimeout); - this._resetSelectionTimeout = setTimeout(() => { - this.selectedValue = -1; - this.isSelectedValueActive = false; - this.inSelection = false; - }, this.backToIdleTimeout); - } else { - this.isPreselectionModeActive = true; - } - } - - getCurrentHeading() { - return ((Math.round(SimVar.GetSimVarValue('PLANE HEADING DEGREES MAGNETIC', 'degree')) % 360) + 360) % 360; - } - - getCurrentTrack() { - return ((Math.round(SimVar.GetSimVarValue('GPS GROUND MAGNETIC TRACK', 'degree')) % 360) + 360) % 360; - } - - onPush() { - clearTimeout(this._resetSelectionTimeout); - this.isPreselectionModeActive = false; - this.inSelection = false; - SimVar.SetSimVarValue('K:A32NX.FCU_TO_AP_HDG_PUSH', 'number', 0); - SimVar.SetSimVarValue('K:HEADING_SLOT_INDEX_SET', 'number', 2); - } - - onPull() { - clearTimeout(this._resetSelectionTimeout); - const isTRKMode = SimVar.GetSimVarValue('L:A32NX_TRK_FPA_MODE_ACTIVE', 'Bool'); - if (!this.isSelectedValueActive) { - if (isTRKMode) { - this.selectedValue = this.getCurrentTrack(); - } else { - this.selectedValue = this.getCurrentHeading(); - } - } - this.inSelection = false; - this.isSelectedValueActive = true; - this.isPreselectionModeActive = false; - SimVar.SetSimVarValue('K:A32NX.FCU_TO_AP_HDG_PULL', 'number', 0); - SimVar.SetSimVarValue('K:HEADING_SLOT_INDEX_SET', 'number', 1); - } - - update(_deltaTime) { - const lateralMode = SimVar.GetSimVarValue('L:A32NX_FMA_LATERAL_MODE', 'Number'); - const lateralArmed = SimVar.GetSimVarValue('L:A32NX_FMA_LATERAL_ARMED', 'Number'); - const isTRKMode = SimVar.GetSimVarValue('L:A32NX_TRK_FPA_MODE_ACTIVE', 'Bool'); - const lightsTest = SimVar.GetSimVarValue('L:A32NX_OVHD_INTLT_ANN', 'number') == 0; - const isManagedActive = this.isManagedModeActive(lateralMode); - const isManagedArmed = this.isManagedModeArmed(lateralArmed); - const showSelectedValue = (this.isSelectedValueActive || this.inSelection || this.isPreselectionModeActive); - - const isHeadingSync = SimVar.GetSimVarValue('L:A32NX_FCU_HEADING_SYNC', 'Number'); - if (!this.wasHeadingSync && isHeadingSync) { - if (isTRKMode) { - this.selectedValue = this.getCurrentTrack(); - } else { - this.selectedValue = this.getCurrentHeading(); - } - this.isSelectedValueActive = true; - this.onRotate(); - } - this.wasHeadingSync = isHeadingSync; - - this.refresh(true, isManagedArmed, isManagedActive, isTRKMode, showSelectedValue, this.selectedValue, lightsTest); - } - - refresh(_isActive, _isManagedArmed, _isManagedActive, _isTRKMode, _showSelectedHeading, _value, _lightsTest, _force = false) { - if ((_isActive != this.isActive) - || (_isManagedArmed != this.isManagedArmed) - || (_isManagedActive != this.isManagedActive) - || (_isTRKMode != this.isTRKMode) - || (_showSelectedHeading != this.showSelectedHeading) - || (_value != this.currentValue) - || (_lightsTest !== this.lightsTest) - || _force) { - if (_isTRKMode != this.isTRKMode) { - this.onTRKModeChanged(_isTRKMode); - } - if (_isManagedArmed - && _isManagedArmed !== this.isManagedArmed - && SimVar.GetSimVarValue('RADIO HEIGHT', 'feet') < 30) { - _value = -1; - _showSelectedHeading = false; - this.selectedValue = _value; - this.isSelectedValueActive = false; - this.isPreselectionModeActive = false; - SimVar.SetSimVarValue('K:HEADING_SLOT_INDEX_SET', 'number', 2); - } - if (_isManagedActive !== this.isManagedActive) { - if (_isManagedActive) { - _value = -1; - _showSelectedHeading = false; - this.selectedValue = _value; - this.isSelectedValueActive = false; - this.isPreselectionModeActive = false; - } else { - _showSelectedHeading = true; - if (!this.isSelectedValueActive) { - this.isSelectedValueActive = true; - if (_isTRKMode) { - _value = this.getCurrentTrack(); - this.selectedValue = _value; - } else { - _value = this.getCurrentHeading(); - this.selectedValue = _value; - } - } - } - } - - // ugly hack because the FG doesn't understand true heading - // FIXME teach the FG about true/mag - const correctedHeading = this.trueRef ? (_value - SimVar.GetSimVarValue('MAGVAR', 'degree')) % 360 : _value; - - SimVar.SetSimVarValue("L:A320_FCU_SHOW_SELECTED_HEADING", "number", _showSelectedHeading == true ? 1 : 0); - if (_value !== this.currentValue) { - SimVar.SetSimVarValue("L:A32NX_FCU_HEADING_SELECTED", "Degrees", _value); - SimVar.SetSimVarValue("L:A32NX_AUTOPILOT_HEADING_SELECTED", "Degrees", correctedHeading); - Coherent.call("HEADING_BUG_SET", 1, Math.max(0, correctedHeading)).catch(console.error); - } else if (this.trueRef) { - SimVar.SetSimVarValue("L:A32NX_AUTOPILOT_HEADING_SELECTED", "Degrees", correctedHeading); - Coherent.call("HEADING_BUG_SET", 1, Math.max(0, correctedHeading)).catch(console.error); - } - - this.isActive = _isActive; - this.isManagedActive = _isManagedActive; - this.isManagedArmed = _isManagedArmed; - this.isTRKMode = _isTRKMode; - this.showSelectedHeading = _showSelectedHeading; - this.currentValue = _value; - this.setTextElementActive(this.textHDG, !this.isTRKMode); - this.setTextElementActive(this.textTRK, this.isTRKMode); - this.lightsTest = _lightsTest; - if (this.lightsTest) { - this.setTextElementActive(this.textHDG, true); - this.setTextElementActive(this.textTRK, true); - this.setElementVisibility(this.signDegrees, true); - this.textValueContent = '.8.8.8'; - return; - } - if ((this.isManagedArmed || this.isManagedActive) && !this.showSelectedHeading) { - this.textValueContent = '---'; - this.setElementVisibility(this.signDegrees, false); - } else { - const value = Math.round(Math.max(this.currentValue, 0)) % 360; - this.textValueContent = value.toString().padStart(3, '0'); - this.setElementVisibility(this.signDegrees, true); - } - - SimVar.SetSimVarValue( - 'L:A32NX_FCU_HDG_MANAGED_DASHES', - 'boolean', - (this.isManagedArmed || this.isManagedActive) && !this.showSelectedHeading, - ); - - } - } - - isManagedModeActive(_mode) { - return (_mode !== 0 && _mode !== 10 && _mode !== 11 && _mode !== 40 && _mode !== 41); - } - - isManagedModeArmed(_armed) { - return (_armed > 0); - } - - isPreselectionAvailable(_radioHeight, _mode) { - return ( - _radioHeight < 30 - || ((_mode >= 30 && _mode <= 34) || _mode === 50) - ); - } - - onTRKModeChanged(_newValue) { - if (_newValue) { - this.selectedValue = this.calculateTrackForHeading(this.selectedValue); - } else { - this.selectedValue = this.calculateHeadingForTrack(this.selectedValue); - } - } - - /** - * Calculates the corresponding track for a given heading, assuming it is flown in the current conditions (TAS + wind). - * @param {number} _heading The heading in degrees. - * @returns {number} The corresponding track in degrees. - */ - calculateTrackForHeading(_heading) { - const trueAirspeed = SimVar.GetSimVarValue('AIRSPEED TRUE', 'Knots'); - if (trueAirspeed < 50) { - return _heading; - } - - const heading = _heading * Math.PI / 180; - const windVelocity = SimVar.GetSimVarValue('AMBIENT WIND VELOCITY', 'Knots'); - const windDirection = SimVar.GetSimVarValue('AMBIENT WIND DIRECTION', 'Degrees') * Math.PI / 180; - // https://web.archive.org/web/20160302090326/http://williams.best.vwh.net/avform.htm#Wind - const wca = Math.atan2(windVelocity * Math.sin(heading - windDirection), trueAirspeed - windVelocity * Math.cos(heading - windDirection)); - const track = heading + wca % (2 * Math.PI); - return (((track * 180 / Math.PI) % 360) + 360) % 360; - } - - /** - * Calculates the heading needed to fly a given track in the current conditions (TAS + wind). - * @param {number} _track The track in degrees. - * @returns {number} The corresponding heading in degrees. - */ - calculateHeadingForTrack(_track) { - const trueAirspeed = SimVar.GetSimVarValue('AIRSPEED TRUE', 'Knots'); - if (trueAirspeed < 50) { - return _track; - } - - const track = _track * Math.PI / 180; - const windVelocity = SimVar.GetSimVarValue('AMBIENT WIND VELOCITY', 'Knots'); - const windDirection = SimVar.GetSimVarValue('AMBIENT WIND DIRECTION', 'Degrees') * Math.PI / 180; - // https://web.archive.org/web/20160302090326/http://williams.best.vwh.net/avform.htm#Wind - const swc = (windVelocity / trueAirspeed) * Math.sin(windDirection - track); - const heading = track + Math.asin(swc) % (2 * Math.PI); - const _heading = (((heading * 180 / Math.PI) % 360) + 360) % 360; - return _heading == NaN ? _track : _heading; - } - - getRotationSpeed() { - if (this._rotaryEncoderCurrentSpeed < 1 - || (Date.now() - this._rotaryEncoderPreviousTimestamp) > this._rotaryEncoderTimeout) { - this._rotaryEncoderCurrentSpeed = 1; - } else { - this._rotaryEncoderCurrentSpeed += this._rotaryEncoderIncrement; - } - this._rotaryEncoderPreviousTimestamp = Date.now(); - return Math.min(this._rotaryEncoderMaximumSpeed, Math.floor(this._rotaryEncoderCurrentSpeed)); - } - - onEvent(_event) { - if (_event === 'HDG_INC_HEADING') { - this.selectedValue = ((Math.round(this.selectedValue + this.getRotationSpeed()) % 360) + 360) % 360; - this.onRotate(); - } else if (_event === 'HDG_DEC_HEADING') { - this.selectedValue = ((Math.round(this.selectedValue - this.getRotationSpeed()) % 360) + 360) % 360; - this.onRotate(); - } else if (_event === 'HDG_INC_TRACK') { - this.selectedValue = ((Math.round(this.selectedValue + this.getRotationSpeed()) % 360) + 360) % 360; - this.onRotate(); - } else if (_event === 'HDG_DEC_TRACK') { - this.selectedValue = ((Math.round(this.selectedValue - this.getRotationSpeed()) % 360) + 360) % 360; - this.onRotate(); - } else if (_event === 'HDG_PUSH') { - this.onPush(); - } else if (_event === 'HDG_PULL') { - this.onPull(); - } else if (_event === 'HDG_SET') { - this.selectedValue = Math.round(SimVar.GetSimVarValue('L:A320_Neo_FCU_HDG_SET_DATA', 'number') % 360); - this.isSelectedValueActive = true; - this.onRotate(); - } - } -} - -class A320_Neo_FCU_Mode extends A320_Neo_FCU_Component { - constructor() { - super(...arguments); - this.init(); - this.update(0); - } - - init() { - this.textHDG = this.getTextElement('HDG'); - this.textVS = this.getTextElement('VS'); - this.textTRK = this.getTextElement('TRK'); - this.textFPA = this.getTextElement('FPA'); - this.refresh(false, 0, true); - } - - update(_deltaTime) { - if (SimVar.GetSimVarValue('L:A32NX_FCU_MODE_REVERSION_TRK_FPA_ACTIVE', 'Bool')) { - SimVar.SetSimVarValue('L:A32NX_TRK_FPA_MODE_ACTIVE', 'Bool', 0); - } - const _isTRKFPADisplayMode = SimVar.GetSimVarValue('L:A32NX_TRK_FPA_MODE_ACTIVE', 'Bool'); - this.refresh(_isTRKFPADisplayMode, SimVar.GetSimVarValue('L:A32NX_OVHD_INTLT_ANN', 'number') == 0); - } - - refresh(_isTRKFPADisplayMode, _lightsTest, _force = false) { - if ((_isTRKFPADisplayMode != this.isTRKFPADisplayMode) || (_lightsTest !== this.lightsTest) || _force) { - this.isTRKFPADisplayMode = _isTRKFPADisplayMode; - this.lightsTest = _lightsTest; - if (this.lightsTest) { - this.setTextElementActive(this.textHDG, true); - this.setTextElementActive(this.textVS, true); - this.setTextElementActive(this.textTRK, true); - this.setTextElementActive(this.textFPA, true); - return; - } - this.setTextElementActive(this.textHDG, !this.isTRKFPADisplayMode); - this.setTextElementActive(this.textVS, !this.isTRKFPADisplayMode); - this.setTextElementActive(this.textTRK, this.isTRKFPADisplayMode); - this.setTextElementActive(this.textFPA, this.isTRKFPADisplayMode); - } - } -} - -class A320_Neo_FCU_Altitude extends A320_Neo_FCU_Component { - constructor() { - super(...arguments); - this.init(); - this.update(0); - } - - init() { - this.isActive = false; - this.isManaged = false; - this.currentValue = 0; - let initValue = 100; - if (Simplane.getAltitudeAboveGround() > 1000) { - initValue = Math.min(49000, Math.max(100, Math.round(Simplane.getAltitude() / 100) * 100)); - } - Coherent.call('AP_ALT_VAR_SET_ENGLISH', 3, initValue, true).catch(console.error); - this.refresh(false, false, initValue, 0, true); - } - - reboot() { - this.init(); - } - - isManagedModeActiveOrArmed(_mode, _armed) { - return ( - (_mode >= 20 && _mode <= 34) - || (_armed >> 1 & 1 - || _armed >> 2 & 1 - || _armed >> 3 & 1 - || _armed >> 4 & 1 - ) - ); - } - - update(_deltaTime) { - const verticalMode = SimVar.GetSimVarValue('L:A32NX_FMA_VERTICAL_MODE', 'Number'); - const verticalArmed = SimVar.GetSimVarValue('L:A32NX_FMA_VERTICAL_ARMED', 'Number'); - const isManaged = this.isManagedModeActiveOrArmed(verticalMode, verticalArmed); - - this.refresh(Simplane.getAutoPilotActive(), isManaged, Simplane.getAutoPilotDisplayedAltitudeLockValue(Simplane.getAutoPilotAltitudeLockUnits()), SimVar.GetSimVarValue('L:A32NX_OVHD_INTLT_ANN', 'number') == 0); - } - - refresh(_isActive, _isManaged, _value, _lightsTest, _force = false) { - if ((_isActive != this.isActive) || (_isManaged != this.isManaged) || (_value != this.currentValue) || (_lightsTest !== this.lightsTest) || _force) { - this.isActive = _isActive; - this.isManaged = _isManaged; - this.currentValue = _value; - this.lightsTest = _lightsTest; - if (this.lightsTest) { - this.textValueContent = '88888'; - return; - } - const value = Math.floor(Math.max(this.currentValue, 100)); - this.textValueContent = value.toString().padStart(5, '0'); - SimVar.SetSimVarValue('L:A32NX_FCU_ALT_MANAGED', 'boolean', this.isManaged); - } - } - - onEvent(_event) { - if (_event === 'ALT_PUSH') { - SimVar.SetSimVarValue('K:A32NX.FCU_ALT_PUSH', 'number', 0); - SimVar.SetSimVarValue('K:ALTITUDE_SLOT_INDEX_SET', 'number', 2); - } else if (_event === 'ALT_PULL') { - SimVar.SetSimVarValue('K:A32NX.FCU_ALT_PULL', 'number', 0); - SimVar.SetSimVarValue('K:ALTITUDE_SLOT_INDEX_SET', 'number', 1); - } - } -} - -let A320_Neo_FCU_VSpeed_State; -(function (A320_Neo_FCU_VSpeed_State) { - A320_Neo_FCU_VSpeed_State[A320_Neo_FCU_VSpeed_State.Idle = 0] = 'Idle'; - A320_Neo_FCU_VSpeed_State[A320_Neo_FCU_VSpeed_State.Zeroing = 1] = 'Zeroing'; - A320_Neo_FCU_VSpeed_State[A320_Neo_FCU_VSpeed_State.Selecting = 2] = 'Selecting'; - A320_Neo_FCU_VSpeed_State[A320_Neo_FCU_VSpeed_State.Flying = 3] = 'Flying'; -}(A320_Neo_FCU_VSpeed_State || (A320_Neo_FCU_VSpeed_State = {}))); -class A320_Neo_FCU_VerticalSpeed extends A320_Neo_FCU_Component { - constructor() { - super(...arguments); - this.forceUpdate = true; - this.ABS_MINMAX_FPA = 9.9; - this.ABS_MINMAX_VS = 6000; - this.backToIdleTimeout = 45000; - this.previousVerticalMode = 0; - this.init(); - this.update(0); - } - - get currentState() { - return this._currentState; - } - - set currentState(v) { - this._currentState = v; - SimVar.SetSimVarValue('L:A320_NE0_FCU_STATE', 'number', this.currentState); - } - - init() { - this.textVS = this.getTextElement('VS'); - this.textFPA = this.getTextElement('FPA'); - this.isActive = false; - this.isFPAMode = false; - this._enterIdleState(); - this.selectedVs = 0; - this.selectedFpa = 0; - this.refresh(false, false, 0, 0, true); - } - - onPush() { - const mode = SimVar.GetSimVarValue('L:A32NX_FMA_VERTICAL_MODE', 'Number'); - if (mode >= 32 && mode <= 34) { - return; - } - clearTimeout(this._resetSelectionTimeout); - this.forceUpdate = true; - - this.currentState = A320_Neo_FCU_VSpeed_State.Zeroing; - - this.selectedVs = 0; - this.selectedFpa = 0; - - SimVar.SetSimVarValue('K:A32NX.FCU_TO_AP_VS_PUSH', 'number', 0); - } - - onRotate() { - if (this.currentState === A320_Neo_FCU_VSpeed_State.Idle || this.currentState === A320_Neo_FCU_VSpeed_State.Selecting) { - clearTimeout(this._resetSelectionTimeout); - this.forceUpdate = true; - - if (this.currentState === A320_Neo_FCU_VSpeed_State.Idle) { - this.selectedVs = this.getCurrentVerticalSpeed(); - this.selectedFpa = this.getCurrentFlightPathAngle(); - } - - this.currentState = A320_Neo_FCU_VSpeed_State.Selecting; - - this._resetSelectionTimeout = setTimeout(() => { - this.selectedVs = 0; - this.selectedFpa = 0; - this.currentState = A320_Neo_FCU_VSpeed_State.Idle; - this.forceUpdate = true; - }, this.backToIdleTimeout); - } else if (this.currentState === A320_Neo_FCU_VSpeed_State.Zeroing) { - this.currentState = A320_Neo_FCU_VSpeed_State.Flying; - this.forceUpdate = true; - } - } - - onPull() { - clearTimeout(this._resetSelectionTimeout); - this.forceUpdate = true; - - if (this.currentState === A320_Neo_FCU_VSpeed_State.Idle) { - if (this.isFPAMode) { - this.selectedFpa = this.getCurrentFlightPathAngle(); - } else { - this.selectedVs = this.getCurrentVerticalSpeed(); - } - } - - SimVar.SetSimVarValue('K:A32NX.FCU_TO_AP_VS_PULL', 'number', 0); - } - - getCurrentFlightPathAngle() { - return this.calculateAngleForVerticalSpeed(Simplane.getVerticalSpeed()); - } - - getCurrentVerticalSpeed() { - return Utils.Clamp(Math.round(Simplane.getVerticalSpeed() / 100) * 100, -this.ABS_MINMAX_VS, this.ABS_MINMAX_VS); - } - - _enterIdleState(idleVSpeed) { - this.selectedVs = 0; - this.selectedFpa = 0; - this.currentState = A320_Neo_FCU_VSpeed_State.Idle; - this.forceUpdate = true; - } - - update(_deltaTime) { - const lightsTest = SimVar.GetSimVarValue('L:A32NX_OVHD_INTLT_ANN', 'number') == 0; - const isFPAMode = SimVar.GetSimVarValue('L:A32NX_TRK_FPA_MODE_ACTIVE', 'Bool'); - const verticalMode = SimVar.GetSimVarValue('L:A32NX_FMA_VERTICAL_MODE', 'Number'); - - if ((this.previousVerticalMode != verticalMode) - && (verticalMode !== 14 && verticalMode !== 15)) { - clearTimeout(this._resetSelectionTimeout); - this._enterIdleState(); - } - - if (this.currentState !== A320_Neo_FCU_VSpeed_State.Flying - && this.currentState !== A320_Neo_FCU_VSpeed_State.Zeroing - && (verticalMode === 14 || verticalMode === 15)) { - clearTimeout(this._resetSelectionTimeout); - this.forceUpdate = true; - const isModeReversion = SimVar.GetSimVarValue('L:A32NX_FCU_MODE_REVERSION_ACTIVE', 'Number'); - const modeReversionTargetFpm = SimVar.GetSimVarValue('L:A32NX_FCU_MODE_REVERSION_TARGET_FPM', 'Number'); - if (isFPAMode) { - if (isModeReversion === 1) { - this.currentState = A320_Neo_FCU_VSpeed_State.Flying; - const modeReversionTargetFpa = this.calculateAngleForVerticalSpeed(modeReversionTargetFpm); - this.selectedFpa = Utils.Clamp(Math.round(modeReversionTargetFpa * 10) / 10, -this.ABS_MINMAX_FPA, this.ABS_MINMAX_FPA); - } else if (this.selectedFpa !== 0) { - this.currentState = A320_Neo_FCU_VSpeed_State.Flying; - } else { - this.currentState = A320_Neo_FCU_VSpeed_State.Zeroing; - } - } else if (isModeReversion === 1) { - this.currentState = A320_Neo_FCU_VSpeed_State.Flying; - this.selectedVs = Utils.Clamp(Math.round(modeReversionTargetFpm / 100) * 100, -this.ABS_MINMAX_VS, this.ABS_MINMAX_VS); - } else if (this.currentVs !== 0) { - this.currentState = A320_Neo_FCU_VSpeed_State.Flying; - } else { - this.currentState = A320_Neo_FCU_VSpeed_State.Zeroing; - } - } - - if (isFPAMode) { - this.refresh(true, true, this.selectedFpa, lightsTest, this.forceUpdate); - } else { - this.refresh(true, false, this.selectedVs, lightsTest, this.forceUpdate); - } - - this.forceUpdate = false; - this.previousVerticalMode = verticalMode; - } - - refresh(_isActive, _isFPAMode, _value, _lightsTest, _force = false) { - if ((_isActive != this.isActive) || (_isFPAMode != this.isFPAMode) || (_value != this.currentValue) || (_lightsTest !== this.lightsTest) || _force) { - if (this.isFPAMode != _isFPAMode) { - this.onFPAModeChanged(_isFPAMode); - } - if (this.currentValue !== _value) { - if (_isFPAMode) { - SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_FPA_SELECTED', 'Degree', this.selectedFpa); - SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_VS_SELECTED', 'feet per minute', 0); - } else { - SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_FPA_SELECTED', 'Degree', 0); - SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_VS_SELECTED', 'feet per minute', this.selectedVs); - } - } - this.isActive = _isActive; - this.isFPAMode = _isFPAMode; - this.currentValue = _value; - this.lightsTest = _lightsTest; - if (this.lightsTest) { - this.setTextElementActive(this.textVS, true); - this.setTextElementActive(this.textFPA, true); - this.textValueContent = '+8.888'; - return; - } - this.setTextElementActive(this.textVS, !this.isFPAMode); - this.setTextElementActive(this.textFPA, this.isFPAMode); - if (this.isActive && this.currentState != A320_Neo_FCU_VSpeed_State.Idle) { - const sign = (this.currentValue < 0) ? '-' : '+'; - if (this.isFPAMode) { - let value = Math.abs(this.currentValue); - value = Math.round(value * 10).toString().padStart(2, '0'); - value = `${value.substring(0, 1)}.${value.substring(1)}`; - this.textValueContent = sign + value; - } else if (this.currentState === A320_Neo_FCU_VSpeed_State.Zeroing) { - this.textValueContent = ('~00oo'); - } else { - let value = Math.floor(this.currentValue); - value = Math.abs(value); - this.textValueContent = `${sign + (Math.floor(value * 0.01).toString().padStart(2, '0'))}oo`; - } - SimVar.SetSimVarValue('L:A32NX_FCU_VS_MANAGED', 'boolean', false); - } else { - this.textValueContent = '~----'; - SimVar.SetSimVarValue('L:A32NX_FCU_VS_MANAGED', 'boolean', true); - } - } - } - - onEvent(_event) { - if (_event === 'VS_INC_VS') { - this.selectedVs = Utils.Clamp(Math.round(this.selectedVs + 100), -this.ABS_MINMAX_VS, this.ABS_MINMAX_VS); - this.onRotate(); - } else if (_event === 'VS_DEC_VS') { - this.selectedVs = Utils.Clamp(Math.round(this.selectedVs - 100), -this.ABS_MINMAX_VS, this.ABS_MINMAX_VS); - this.onRotate(); - } else if (_event === 'VS_INC_FPA') { - this.selectedFpa = Utils.Clamp(Math.round((this.selectedFpa + 0.1) * 10) / 10, -this.ABS_MINMAX_FPA, this.ABS_MINMAX_FPA); - this.onRotate(); - } else if (_event === 'VS_DEC_FPA') { - this.selectedFpa = Utils.Clamp(Math.round((this.selectedFpa - 0.1) * 10) / 10, -this.ABS_MINMAX_FPA, this.ABS_MINMAX_FPA); - this.onRotate(); - } else if (_event === 'VS_PUSH') { - this.onPush(); - } else if (_event === 'VS_PULL') { - this.onPull(); - } else if (_event === 'VS_SET') { - const value = SimVar.GetSimVarValue('L:A320_Neo_FCU_VS_SET_DATA', 'number'); - if (this.isFPAMode) { - if (Math.abs(value) < 100 || value == 0) { - this.selectedFpa = Utils.Clamp(Math.round(value) / 10, -this.ABS_MINMAX_FPA, this.ABS_MINMAX_FPA); - this.currentState = A320_Neo_FCU_VSpeed_State.Selecting; - this.onRotate(); - } - } else if (Math.abs(value) >= 100 || value == 0) { - this.selectedVs = Utils.Clamp(Math.round(value), -this.ABS_MINMAX_VS, this.ABS_MINMAX_VS); - this.currentState = A320_Neo_FCU_VSpeed_State.Selecting; - this.onRotate(); - } - } - } - - onFPAModeChanged(_newValue) { - if (_newValue) { - this.selectedFpa = this.calculateAngleForVerticalSpeed(this.selectedVs); - } else { - this.selectedVs = this.calculateVerticalSpeedForAngle(this.selectedFpa); - } - } - - /** - * Calculates the vertical speed needed to fly a flight path angle at the current ground speed. - * @param {number} _angle The flight path angle in degrees. - * @returns {number} The corresponding vertical speed in feet per minute. - */ - calculateVerticalSpeedForAngle(_angle) { - if (_angle == 0) { - return 0; - } - const _groundSpeed = SimVar.GetSimVarValue('GPS GROUND SPEED', 'Meters per second'); - const groundSpeed = _groundSpeed * 3.28084 * 60; // Now in feet per minute. - const angle = _angle * Math.PI / 180; // Now in radians. - const verticalSpeed = Math.tan(angle) * groundSpeed; - return Utils.Clamp(Math.round(verticalSpeed / 100) * 100, -this.ABS_MINMAX_VS, this.ABS_MINMAX_VS); - } - - /** - * Calculates the flight path angle for a given vertical speed, assuming it is flown at the current ground speed. - * @param {number} verticalSpeed The flight path angle in feet per minute. - * @returns {number} The corresponding flight path angle in degrees. - */ - calculateAngleForVerticalSpeed(verticalSpeed) { - if (Math.abs(verticalSpeed) < 10) { - return 0; - } - const _groundSpeed = SimVar.GetSimVarValue('GPS GROUND SPEED', 'Meters per second'); - const groundSpeed = _groundSpeed * 3.28084 * 60; // Now in feet per minute. - const angle = Math.atan(verticalSpeed / groundSpeed); - const _angle = angle * 180 / Math.PI; - return Utils.Clamp(Math.round(_angle * 10) / 10, -this.ABS_MINMAX_FPA, this.ABS_MINMAX_FPA); - } -} - -class A320_Neo_FCU_LargeScreen extends NavSystemElement { - init(root) { - if (this.components == null) { - this.components = new Array(); - this.speedDisplay = new A320_Neo_FCU_Speed(this.gps, 'Speed'); - this.components.push(this.speedDisplay); - this.headingDisplay = new A320_Neo_FCU_Heading(this.gps, 'Heading'); - this.components.push(this.headingDisplay); - this.components.push(new A320_Neo_FCU_Mode(this.gps, 'Mode')); - this.altitudeDisplay = new A320_Neo_FCU_Altitude(this.gps, 'Altitude'); - this.components.push(this.altitudeDisplay); - this.verticalSpeedDisplay = new A320_Neo_FCU_VerticalSpeed(this.gps, 'VerticalSpeed'); - this.components.push(this.verticalSpeedDisplay); - this.autopilotInterface = new A320_Neo_FCU_Autopilot(this.gps, 'Autopilot'); - } - } - - onEnter() { - } - - reboot() { - if (this.components != null) { - for (let i = 0; i < this.components.length; ++i) { - if (this.components[i] != null) { - this.components[i].reboot(); - } - } - } - } - - onUpdate(_deltaTime) { - if (this.components != null) { - for (let i = 0; i < this.components.length; ++i) { - if (this.components[i] != null) { - this.components[i].update(_deltaTime); - } - } - } - } - - onExit() { - } - - onEvent(_event) { - this.autopilotInterface.onEvent(_event); - this.speedDisplay.onEvent(_event); - this.headingDisplay.onEvent(_event); - this.altitudeDisplay.onEvent(_event); - this.verticalSpeedDisplay.onEvent(_event); - } -} - -class A320_Neo_FCU_Pressure extends A320_Neo_FCU_Component { - constructor() { - super(...arguments); - this.init(); - this.update(0); - } - - init() { - this.selectedElem = this.getDivElement('Selected'); - this.standardElem = this.getDivElement('Standard'); - this.textQFE = this.getTextElement('QFE'); - this.textQNH = this.getTextElement('QNH'); - this.textPreSelBaro = this.getTextElement('PreSelBaroValue'); - this.currentPreSelValue = null; - this.resetPreSelectionTimeout = null; - this.refresh('QFE', true, 0, 0, true); - } - - update(_deltaTime) { - const units = Simplane.getPressureSelectedUnits(); - const mode = Simplane.getPressureSelectedMode(Aircraft.A320_NEO); - this.refresh(mode, (units != 'millibar'), Simplane.getPressureValue(units), SimVar.GetSimVarValue('L:A32NX_OVHD_INTLT_ANN', 'number') == 0); - } - - refresh(_mode, _isHGUnit, _value, _lightsTest, _force = false) { - let preSelValue = SimVar.GetSimVarValue('L:A380X_EFIS_L_BARO_PRESELECTED', 'number'); - // Conversion of baro selection SimVar. I tried using a standard altimeter, didn't work. - if (preSelValue < 1) { - preSelValue = _isHGUnit ? 29.92 : 1013.25; - SimVar.SetSimVarValue('L:A380X_EFIS_L_BARO_PRESELECTED', 'number', preSelValue); - } - if (preSelValue < 800 && !_isHGUnit) { - preSelValue = preSelValue / 0.02953; - SimVar.SetSimVarValue('L:A380X_EFIS_L_BARO_PRESELECTED', 'number', preSelValue); - } else if (preSelValue > 800 && _isHGUnit) { - preSelValue = Math.round(preSelValue * 0.02953 * 100) / 100; - SimVar.SetSimVarValue('L:A380X_EFIS_L_BARO_PRESELECTED', 'number', preSelValue); - } - - // Display pre-selected value for only 4 seconds, then reset and hide - if (preSelValue !== this.currentPreSelValue || (_isHGUnit != this.isHGUnit)) { - clearTimeout(this.resetPreSelectionTimeout); - this.resetPreSelectionTimeout = setTimeout(() => { - this.currentPreSelValue = Simplane.getPressureSelectedUnits() === 'millibar' ? 1013.25 : 29.92; - SimVar.SetSimVarValue('L:A380X_EFIS_L_BARO_PRESELECTED', 'number', this.currentPreSelValue); - this.setTextElementActive(this.textPreSelBaro, false, true); - }, 4000); - } - - if ((_mode != this.currentMode) || (_isHGUnit != this.isHGUnit) || (_value != this.currentValue) || (preSelValue != this.currentPreSelValue) || (_lightsTest !== this.lightsTest) || _force) { - const wasStd = this.currentMode == 'STD' && _mode != 'STD'; - this.currentMode = _mode; - this.isHGUnit = _isHGUnit; - this.currentValue = _value; - this.currentPreSelValue = preSelValue; - this.lightsTest = _lightsTest; - if (this.lightsTest) { - this.standardElem.style.display = 'none'; - this.selectedElem.style.display = 'block'; - this.setTextElementActive(this.textQFE, true, true); - this.setTextElementActive(this.textQNH, true, true); - this.setTextElementActive(this.textPreSelBaro, true, true); - this.textValueContent = '88.88'; - this.textPreSelBaro.textContent = '88.88'; - return; - } - if (this.currentMode == 'STD') { - this.standardElem.style.display = 'block'; - this.selectedElem.style.display = 'none'; - SimVar.SetSimVarValue('KOHLSMAN SETTING STD', 'Bool', 1); - const hPa = SimVar.GetSimVarValue('L:XMLVAR_Baro_Selector_HPA_1', 'boolean'); - const preSelRender = Math.round(Math.max(!hPa ? (preSelValue * 100) : preSelValue, 0)); - this.textPreSelBaro.textContent = !hPa ? `${preSelRender.toFixed(0).substring(0, 2)}.${preSelRender.toFixed(0).substring(2)}` : preSelRender.toFixed(0).padStart(4, '0'); - if (Math.abs(preSelValue - 1013) < 0.5 || Math.abs(preSelValue - 29.92) < 0.005) { - this.setTextElementActive(this.textPreSelBaro, false, true); - } else { - this.setTextElementActive(this.textPreSelBaro, true, true); - } - } else { - this.standardElem.style.display = 'none'; - this.selectedElem.style.display = 'block'; - SimVar.SetSimVarValue('KOHLSMAN SETTING STD', 'Bool', 0); - const isQFE = (this.currentMode == 'QFE'); - this.setTextElementActive(this.textQFE, isQFE, true); - this.setTextElementActive(this.textQNH, !isQFE, true); - this.setTextElementActive(this.textPreSelBaro, false, true); - let value = Math.round(Math.max(this.isHGUnit ? (this.currentValue * 100) : this.currentValue, 0)); - if (!wasStd) { - value = value.toString().padStart(4, '0'); - if (this.isHGUnit) { - value = `${value.substring(0, 2)}.${value.substring(2)}`; - } - this.textValueContent = value; - } - } - } - } -} - -class A320_Neo_FCU_SmallScreen extends NavSystemElement { - init(root) { - if (this.pressure == null) { - this.pressure = new A320_Neo_FCU_Pressure(this.gps, 'SmallScreen'); - } - } - - onEnter() { - } - - onUpdate(_deltaTime) { - if (this.pressure != null) { - this.pressure.update(_deltaTime); - } - } - - onExit() { - } - - onEvent(_event) { - } - - reboot() { - if (this.pressure) { - this.pressure.reboot(); - } - } -} - -registerInstrument('a380-fcu-element', A320_Neo_FCU); diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/legacy/A32NX_BaseAirliners.js b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/legacy/A32NX_BaseAirliners.js deleted file mode 100644 index 93734ac5571..00000000000 --- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/legacy/A32NX_BaseAirliners.js +++ /dev/null @@ -1,1258 +0,0 @@ -class BaseAirliners extends NavSystem { - constructor() { - super(...arguments); - this.isMachActive = false; - this.machTransition = 0; - } - connectedCallback() { - super.connectedCallback(); - this.preserveAspectRatio("Mainframe"); - this.addEventAlias("FMS_Upper_INC", "NavigationSmallInc"); - this.addEventAlias("FMS_Upper_DEC", "NavigationSmallDec"); - this.addEventAlias("FMS_Lower_INC", "NavigationLargeInc"); - this.addEventAlias("FMS_Lower_DEC", "NavigationLargeDec"); - this.addEventAlias("FMS_Upper_PUSH", "NavigationPush"); - } - onUpdate(_deltaTime) { - super.onUpdate(_deltaTime); - BaseAirliners.isMetric = Simplane.getUnitIsMetric(); - } - static unitIsMetric(_plane) { - switch (_plane) { - case Aircraft.A320_NEO: - return true; - case Aircraft.B747_8: - case Aircraft.AS01B: - return false; - } - return BaseAirliners.isMetric; - } -} -BaseAirliners.isMetric = false; -var Airliners; -(function (Airliners) { - class BaseEICAS extends BaseAirliners { - constructor() { - super(); - this.lowerScreenPages = new Array(); - } - connectedCallback() { - super.connectedCallback(); - if (this.urlConfig.index) { - this.setAttribute("index", this.urlConfig.index.toString()); - } - this.createLowerScreenPages(); - this.pageGroups = [new NavSystemPageGroup(BaseEICAS.LOWER_SCREEN_GROUP_NAME, this, this.lowerScreenPages)]; - } - disconnectedCallback() { - } - onEvent(_event) { - super.onEvent(_event); - const prefix = this.getLowerScreenChangeEventNamePrefix(); - if (_event.indexOf(prefix) >= 0) { - const pageName = _event.replace(prefix, ""); - this.changePage(pageName); - } else { - for (let i = 0; i < this.lowerScreenPages.length; i++) { - this.lowerScreenPages[i].onEvent(_event); - } - } - } - - createLowerScreenPage(_name, _htmlElemId, _eicasPageSelector) { - this.lowerScreenPages.push(new NavSystemPage(_name.toUpperCase(), _htmlElemId, new Airliners.EICASPage(_eicasPageSelector))); - } - changePage(_pageName) { - const pageName = _pageName.toUpperCase(); - this.SwitchToPageName(BaseEICAS.LOWER_SCREEN_GROUP_NAME, pageName); - for (let i = 0; i < this.lowerScreenPages.length; i++) { - if (this.lowerScreenPages[i].name == pageName) { - // SimVar.SetSimVarValue("L:A32NX_ECAM_SD_CURRENT_PAGE_INDEX", "number", i); - break; - } - } - } - } - BaseEICAS.LOWER_SCREEN_GROUP_NAME = "LowerScreenGroup"; - BaseEICAS.LOWER_SCREEN_CHANGE_EVENT_NAME = "ChangeLowerScreenPage"; - Airliners.BaseEICAS = BaseEICAS; - let EICAS_INFO_PANEL_ID; - (function (EICAS_INFO_PANEL_ID) { - EICAS_INFO_PANEL_ID[EICAS_INFO_PANEL_ID["PRIMARY"] = 0] = "PRIMARY"; - EICAS_INFO_PANEL_ID[EICAS_INFO_PANEL_ID["SECONDARY"] = 1] = "SECONDARY"; - })(EICAS_INFO_PANEL_ID = Airliners.EICAS_INFO_PANEL_ID || (Airliners.EICAS_INFO_PANEL_ID = {})); - let EICAS_INFO_PANEL_EVENT_TYPE; - (function (EICAS_INFO_PANEL_EVENT_TYPE) { - EICAS_INFO_PANEL_EVENT_TYPE[EICAS_INFO_PANEL_EVENT_TYPE["ADD_MESSAGE"] = 0] = "ADD_MESSAGE"; - EICAS_INFO_PANEL_EVENT_TYPE[EICAS_INFO_PANEL_EVENT_TYPE["REMOVE_MESSAGE"] = 1] = "REMOVE_MESSAGE"; - EICAS_INFO_PANEL_EVENT_TYPE[EICAS_INFO_PANEL_EVENT_TYPE["MODIFY_MESSAGE"] = 2] = "MODIFY_MESSAGE"; - EICAS_INFO_PANEL_EVENT_TYPE[EICAS_INFO_PANEL_EVENT_TYPE["CLEAR_SCREEN"] = 3] = "CLEAR_SCREEN"; - })(EICAS_INFO_PANEL_EVENT_TYPE = Airliners.EICAS_INFO_PANEL_EVENT_TYPE || (Airliners.EICAS_INFO_PANEL_EVENT_TYPE = {})); - let EICAS_INFO_PANEL_MESSAGE_STYLE; - (function (EICAS_INFO_PANEL_MESSAGE_STYLE) { - EICAS_INFO_PANEL_MESSAGE_STYLE[EICAS_INFO_PANEL_MESSAGE_STYLE["INDICATION"] = 0] = "INDICATION"; - EICAS_INFO_PANEL_MESSAGE_STYLE[EICAS_INFO_PANEL_MESSAGE_STYLE["CAUTION"] = 1] = "CAUTION"; - EICAS_INFO_PANEL_MESSAGE_STYLE[EICAS_INFO_PANEL_MESSAGE_STYLE["WARNING"] = 2] = "WARNING"; - EICAS_INFO_PANEL_MESSAGE_STYLE[EICAS_INFO_PANEL_MESSAGE_STYLE["TITLE"] = 3] = "TITLE"; - EICAS_INFO_PANEL_MESSAGE_STYLE[EICAS_INFO_PANEL_MESSAGE_STYLE["OPTION"] = 4] = "OPTION"; - })(EICAS_INFO_PANEL_MESSAGE_STYLE = Airliners.EICAS_INFO_PANEL_MESSAGE_STYLE || (Airliners.EICAS_INFO_PANEL_MESSAGE_STYLE = {})); - class EICASInfoPanelManager { - } - Airliners.EICASInfoPanelManager = EICASInfoPanelManager; - class EICASTemplateElement extends TemplateElement { - onEvent(_event) { } - ; - getInfoPanelManager() { - return null; - } - ; - } - Airliners.EICASTemplateElement = EICASTemplateElement; - class EICASScreen extends NavSystemElementContainer { - constructor(_name, _root, _selector) { - super(_name, _root, null); - this.selector = ""; - this.screen = null; - this.IndependentElements = new NavSystemElementGroup([]); - this.selector = _selector; - this.element = this.IndependentElements; - this.getDeltaTime = A32NX_Util.createDeltaTimeCalculator(this._lastTime); - } - init() { - super.init(); - const root = this.gps.getChildById(this.htmlElemId); - if (root != null) { - this.screen = root.querySelector(this.selector); - } - } - onUpdate() { - const _deltaTime = this.getDeltaTime(); - super.onUpdate(_deltaTime); - if (this.screen != null) { - this.screen.update(_deltaTime); - } - } - addIndependentElement(_element) { - this.IndependentElements.addElement(_element); - } - getInfoPanelManager() { - if (this.screen) { - return this.screen.getInfoPanelManager(); - } - return null; - } - ; - } - Airliners.EICASScreen = EICASScreen; - class EICASPage extends NavSystemElement { - constructor(_selector) { - super(); - this.selector = ""; - this.selector = _selector; - } - init() { - const root = this.gps.getChildById(this.container.htmlElemId); - if (root != null) { - this.page = root.querySelector(this.selector); - } - this.getDeltaTime = A32NX_Util.createDeltaTimeCalculator(this._lastTime); - } - onEnter() { - if (this.page != null) { - this.page.style.display = "block"; - } - } - onUpdate() { - const _deltaTime = this.getDeltaTime(); - if (this.page != null) { - this.page.update(_deltaTime); - } - } - onEvent(_event) { - if (this.page) { - this.page.onEvent(_event); - } - } - onExit() { - if (this.page != null) { - this.page.style.display = "none"; - } - } - } - Airliners.EICASPage = EICASPage; - class DynamicValueComponent { - constructor(_text, _getValueFunction, _roundToDP = 0, _formatFunction = null) { - this.visible = true; - this.text = null; - this.getValue = null; - this.roundToDP = 0; - this.format = null; - this.currentValue = 0; - this.text = _text; - this.getValue = _getValueFunction; - this.roundToDP = _roundToDP; - this.format = _formatFunction; - this.trySetValue(0, true); - } - set isVisible(_visible) { - this.visible = _visible; - if (this.visible) { - if (this.getValue != null) { - this.trySetValue(this.getValue(), true); - } - } else { - if (this.text != null) { - this.text.textContent = ""; - } - } - } - refresh() { - if (this.visible) { - if (this.getValue != null) { - this.trySetValue(this.getValue()); - } - } - } - trySetValue(_value, _force = false) { - if ((_value != this.currentValue) || _force) { - this.currentValue = _value; - if (this.text != null) { - if (this.format != null) { - this.text.textContent = this.format(_value, this.roundToDP); - } else { - this.text.textContent = DynamicValueComponent.formatValueToString(_value, this.roundToDP); - } - } - } - } - static formatValueToString(_value, _dp = 0) { - return _value.toFixed(_dp); - } - static formatValueToPosNegString(_value, _dp = 0) { - return ((_value > 0) ? "+" : "") + _value.toFixed(_dp); - } - static formatValueToPosNegTemperature(_value, _dp = 0) { - return (DynamicValueComponent.formatValueToPosNegString(_value, _dp) + "c"); - } - static formatValueToThrottleDisplay(_value, _dp = 0) { - if (_value < 0) { - return "REV"; - } else { - return _value.toFixed(_dp); - } - } - } - Airliners.DynamicValueComponent = DynamicValueComponent; - class BaseATC extends BaseAirliners { - constructor() { - super(); - this.currentDigits = [0, 0, 0, 0]; - this.currentCode = 0; - this.bLastInputIsCLR = false; - this.emptySlot = "-"; - } - connectedCallback() { - super.connectedCallback(); - this.valueText = this.querySelector("text"); - } - disconnectedCallback() { - super.disconnectedCallback(); - } - Init() { - super.Init(); - console.log("ATC Init"); - } - onUpdate(_deltaTime) { - super.onUpdate(_deltaTime); - - const lightsTest = SimVar.GetSimVarValue("L:A32NX_OVHD_INTLT_ANN", "number") == 0 && SimVar.GetSimVarValue("L:A32NX_ELEC_DC_2_BUS_IS_POWERED", "Bool"); - const lightsTestChanged = lightsTest !== this.lightsTest; - this.lightsTest = lightsTest; - - if (lightsTest) { - if (lightsTestChanged && this.valueText != null) { - this.valueText.textContent = "8888"; - } - return; - } - - const code = SimVar.GetSimVarValue("TRANSPONDER CODE:1", "number"); - if (code != this.currentCode || lightsTestChanged) { - this.currentCode = code; - this.currentDigits = [Math.floor(code / 1000), Math.floor((code % 1000) / 100), Math.floor((code % 100) / 10), code % 10]; - this.refreshValue(); - } - } - refreshValue() { - let code = ""; - for (let i = 0; i <= 3; i++) { - if (this.currentDigits[i] >= 0) { - code += this.currentDigits[i].toString(); - } else { - code += this.emptySlot; - } - } - if (this.valueText != null) { - this.valueText.textContent = code; - } - } - onEvent(_event) { - if (_event.indexOf("BTN_") >= 0) { - const buttonSuffix = _event.replace("BTN_", ""); - if (buttonSuffix.charAt(0) == 'C') { - if (this.currentDigits[0] >= 0) { - if (this.bLastInputIsCLR) { - for (var i = 3; i >= 0; i--) { - this.currentDigits[i] = -1; - } - } else { - for (var i = 3; i >= 0; i--) { - if (this.currentDigits[i] >= 0) { - this.currentDigits[i] = -1; - this.bLastInputIsCLR = true; - break; - } - } - } - this.refreshValue(); - } - } else if (buttonSuffix.charAt(0) == 'I') { - return; - } else { - let slot = -1; - { - for (var i = 0; i <= 3; i++) { - if (this.currentDigits[i] < 0) { - slot = i; - break; - } - } - } - if (slot < 0) { - for (var i = 0; i <= 3; i++) { - this.currentDigits[i] = -1; - } - slot = 0; - } - const buttonNumber = parseInt(buttonSuffix); - this.currentDigits[slot] = buttonNumber; - this.refreshValue(); - if (slot == 3 && this.currentDigits[3] >= 0) { - const code = (this.currentDigits[0] * 4096) + (this.currentDigits[1] * 256) + (this.currentDigits[2] * 16) + this.currentDigits[3]; - SimVar.SetSimVarValue("K:XPNDR_SET", "Bco16", code); - } - this.bLastInputIsCLR = false; - } - } - } - } - Airliners.BaseATC = BaseATC; - let PopupMenu_ItemType; - (function (PopupMenu_ItemType) { - PopupMenu_ItemType[PopupMenu_ItemType["TITLE"] = 0] = "TITLE"; - PopupMenu_ItemType[PopupMenu_ItemType["LIST"] = 1] = "LIST"; - PopupMenu_ItemType[PopupMenu_ItemType["RANGE"] = 2] = "RANGE"; - PopupMenu_ItemType[PopupMenu_ItemType["RADIO"] = 3] = "RADIO"; - PopupMenu_ItemType[PopupMenu_ItemType["RADIO_LIST"] = 4] = "RADIO_LIST"; - PopupMenu_ItemType[PopupMenu_ItemType["RADIO_RANGE"] = 5] = "RADIO_RANGE"; - PopupMenu_ItemType[PopupMenu_ItemType["SUBMENU"] = 6] = "SUBMENU"; - PopupMenu_ItemType[PopupMenu_ItemType["CHECKBOX"] = 7] = "CHECKBOX"; - })(PopupMenu_ItemType || (PopupMenu_ItemType = {})); - class PopupMenu_Item { - constructor(_type, _section, _y, _height) { - this.y = 0; - this.height = 0; - this.listVal = 0; - this.rangeMin = 0; - this.rangeMax = 0; - this.rangeStep = 0; - this.rangeDecimals = 0; - this.rangeVal = 0; - this.radioVal = false; - this.checkboxVal = false; - this.type = _type; - this.section = _section; - this.y = _y; - this.height = _height; - } - get interactive() { - if (this.type != PopupMenu_ItemType.TITLE) { - return true; - } - return false; - } - get enabled() { - if (this.dictKeys != null || this.subMenu) { - return true; - } - return false; - } - } - class PopupMenu_Section { - constructor() { - this.items = new Array(); - this.startY = 0; - this.endY = 0; - this.interactionColor = ""; - this.defaultRadio = true; - } - } - class PopupMenu_Handler { - constructor() { - this.menuLeft = 0; - this.menuTop = 0; - this.menuWidth = 0; - this.columnLeft1 = 3; - this.columnLeft2 = 20; - this.columnLeft3 = 90; - this.lineHeight = 18; - this.sectionBorderSize = 1; - this.textStyle = "Roboto-Regular"; - this.textMarginX = 3; - this.highlightColor = "cyan"; - this.interactionColor = "cyan"; - this.disabledColor = "grey"; - this.shapeFillColor = "none"; - this.shapeFillIfDisabled = true; - this.shape3D = false; - this.shape3DBorderSize = 3; - this.shape3DBorderLeft = "rgb(100, 100, 100)"; - this.shape3DBorderRight = "rgb(30, 30, 30)"; - this.highlightId = 0; - this.speedInc = 1.0; - this.speedInc_UpFactor = 0.25; - this.speedInc_DownFactor = 0.075; - this.speedInc_PowFactor = 0.9; - } - get height() { - let height = 0; - for (let i = 0; i < this.allSections.length; i++) { - height += this.allSections[i].endY - this.allSections[i].startY; - } - return height; - } - highlight(_index) { - if (_index >= 0) { - this.highlightId = _index; - } - } - reset() { - } - onUpdate(_dTime) { - this.updateHighlight(); - this.updateSpeedInc(); - } - onActivate() { - if (this.highlightItem && this.highlightItem.enabled) { - switch (this.highlightItem.type) { - case PopupMenu_ItemType.RADIO: - case PopupMenu_ItemType.RADIO_LIST: - case PopupMenu_ItemType.RADIO_RANGE: - let changed = false; - const section = this.highlightItem.section; - for (let i = 0; i < section.items.length; i++) { - const item = section.items[i]; - if (item.radioElem) { - if (item == this.highlightItem) { - if (!item.radioVal) { - this.activateItem(item, true); - changed = true; - } else if (!section.defaultRadio) { - this.activateItem(item, false); - changed = true; - } - } else if (item.radioVal) { - this.activateItem(item, false); - } - } - } - if (changed) { - this.onChanged(this.highlightItem); - } - break; - case PopupMenu_ItemType.SUBMENU: - this.highlightItem.subMenu(); - break; - case PopupMenu_ItemType.CHECKBOX: - if (!this.highlightItem.checkboxVal) { - this.activateItem(this.highlightItem, true); - } else { - this.activateItem(this.highlightItem, false); - this.highlightItem.checkboxVal = false; - } - this.onChanged(this.highlightItem); - break; - } - } - } - onDataDec() { - if (this.highlightItem && this.highlightItem.enabled) { - switch (this.highlightItem.type) { - case PopupMenu_ItemType.LIST: - case PopupMenu_ItemType.RADIO_LIST: - if (this.highlightItem.listVal > 0) { - this.highlightItem.listVal--; - this.highlightItem.listElem.textContent = this.highlightItem.listValues[this.highlightItem.listVal]; - this.onChanged(this.highlightItem); - } - break; - case PopupMenu_ItemType.RANGE: - case PopupMenu_ItemType.RADIO_RANGE: - if (this.highlightItem.rangeVal > this.highlightItem.rangeMin) { - this.highlightItem.rangeVal -= this.highlightItem.rangeStep * this.getSpeedAccel(); - this.highlightItem.rangeVal = Math.max(this.highlightItem.rangeVal, this.highlightItem.rangeMin); - this.highlightItem.rangeElem.textContent = this.highlightItem.rangeVal.toFixed(this.highlightItem.rangeDecimals); - this.onChanged(this.highlightItem); - this.speedInc += this.speedInc_UpFactor; - } - break; - } - } - } - onDataInc() { - if (this.highlightItem && this.highlightItem.enabled) { - switch (this.highlightItem.type) { - case PopupMenu_ItemType.LIST: - case PopupMenu_ItemType.RADIO_LIST: - if (this.highlightItem.listVal < this.highlightItem.listValues.length - 1) { - this.highlightItem.listVal++; - this.highlightItem.listElem.textContent = this.highlightItem.listValues[this.highlightItem.listVal]; - this.onChanged(this.highlightItem); - } - break; - case PopupMenu_ItemType.RANGE: - case PopupMenu_ItemType.RADIO_RANGE: - if (this.highlightItem.rangeVal < this.highlightItem.rangeMax) { - this.highlightItem.rangeVal += this.highlightItem.rangeStep * this.getSpeedAccel(); - this.highlightItem.rangeVal = Math.min(this.highlightItem.rangeVal, this.highlightItem.rangeMax); - this.highlightItem.rangeElem.textContent = this.highlightItem.rangeVal.toFixed(this.highlightItem.rangeDecimals); - this.onChanged(this.highlightItem); - this.speedInc += this.speedInc_UpFactor; - } - break; - } - } - } - onMenuDec() { - if (this.highlightId > 0) { - this.highlightId--; - } - } - onMenuInc() { - this.highlightId++; - } - onEscape() { - if (this.escapeCbk) { - this.escapeCbk(); - } - } - openMenu() { - this.allSections = []; - this.sectionRoot = null; - this.highlightItem = null; - this.highlightId = 0; - this.escapeCbk = null; - this.sectionRoot = document.createElementNS(Avionics.SVG.NS, "g"); - this.sectionRoot.setAttribute("transform", "translate(" + this.menuLeft + " " + this.menuTop + ")"); - return this.sectionRoot; - } - closeMenu() { - const bg = document.createElementNS(Avionics.SVG.NS, "rect"); - bg.setAttribute("x", "0"); - bg.setAttribute("y", "0"); - bg.setAttribute("width", this.menuWidth.toString()); - bg.setAttribute("height", this.height.toString()); - bg.setAttribute("fill", "black"); - this.sectionRoot.insertBefore(bg, this.sectionRoot.firstChild); - this.highlightElem = document.createElementNS(Avionics.SVG.NS, "rect"); - this.highlightElem.setAttribute("x", "0"); - this.highlightElem.setAttribute("y", "0"); - this.highlightElem.setAttribute("width", this.menuWidth.toString()); - this.highlightElem.setAttribute("height", this.lineHeight.toString()); - this.highlightElem.setAttribute("fill", "none"); - this.highlightElem.setAttribute("stroke", this.highlightColor); - this.highlightElem.setAttribute("stroke-width", (this.sectionBorderSize + 1).toString()); - this.sectionRoot.appendChild(this.highlightElem); - if (this.dictionary) { - this.dictionary.changed = false; - } - } - beginSection(_defaultRadio = true) { - this.section = new PopupMenu_Section(); - this.section.interactionColor = this.interactionColor; - this.section.defaultRadio = _defaultRadio; - if (this.allSections.length > 0) { - this.section.startY = this.allSections[this.allSections.length - 1].endY; - this.section.endY = this.section.startY; - } - } - endSection() { - const stroke = document.createElementNS(Avionics.SVG.NS, "rect"); - stroke.setAttribute("x", "0"); - stroke.setAttribute("y", this.section.startY.toString()); - stroke.setAttribute("width", this.menuWidth.toString()); - stroke.setAttribute("height", (this.section.endY - this.section.startY).toString()); - stroke.setAttribute("fill", "none"); - stroke.setAttribute("stroke", "white"); - stroke.setAttribute("stroke-width", this.sectionBorderSize.toString()); - this.sectionRoot.appendChild(stroke); - let defaultRadio = null; - for (let i = 0; i < this.section.items.length; i++) { - const item = this.section.items[i]; - if (item.radioElem) { - if (this.dictionary && item.dictKeys && this.dictionary.exists(item.dictKeys[0])) { - if (this.dictionary.get(item.dictKeys[0]) == item.radioName) { - defaultRadio = item; - break; - } - } else if (!defaultRadio && this.section.defaultRadio) { - defaultRadio = item; - } - } - } - for (let i = 0; i < this.section.items.length; i++) { - const item = this.section.items[i]; - let dictIndex = 0; - let changed = false; - if (item.radioElem) { - if (item == defaultRadio) { - this.activateItem(item, true); - changed = true; - } - dictIndex++; - } - if (item.listElem) { - item.listVal = 0; - if (this.dictionary && item.dictKeys && this.dictionary.exists(item.dictKeys[dictIndex])) { - const value = this.dictionary.get(item.dictKeys[dictIndex]); - for (let j = 0; j < item.listValues.length; j++) { - if (item.listValues[j] == value) { - item.listVal = j; - break; - } - } - } - item.listElem.textContent = item.listValues[item.listVal]; - changed = true; - } - if (item.rangeElem) { - item.rangeVal = item.rangeMin; - if (this.dictionary && item.dictKeys && this.dictionary.exists(item.dictKeys[dictIndex])) { - item.rangeVal = parseFloat(this.dictionary.get(item.dictKeys[dictIndex])); - item.rangeVal = Math.max(item.rangeMin, Math.min(item.rangeVal, item.rangeMax)); - } - item.rangeElem.textContent = item.rangeVal.toFixed(item.rangeDecimals); - changed = true; - } - if (item.checkboxElem) { - if (this.dictionary && item.dictKeys && this.dictionary.exists(item.dictKeys[dictIndex])) { - if (this.dictionary.get(item.dictKeys[0]) == "ON") { - this.activateItem(item, true); - changed = true; - } - } - } - if (changed) { - this.onChanged(item); - } - } - this.allSections.push(this.section); - this.section = null; - } - addTitle(_text, _textSize, _bgFactor, _bgColor = "blue", _showEscapeIcon = false) { - const bg = document.createElementNS(Avionics.SVG.NS, "rect"); - bg.setAttribute("x", "0"); - bg.setAttribute("y", this.section.endY.toString()); - bg.setAttribute("width", (this.menuWidth * _bgFactor).toString()); - bg.setAttribute("height", this.lineHeight.toString()); - bg.setAttribute("fill", _bgColor); - this.sectionRoot.appendChild(bg); - let posX = this.columnLeft1; - if (_showEscapeIcon) { - const arrow = document.createElementNS(Avionics.SVG.NS, "path"); - arrow.setAttribute("d", "M" + posX + " " + (this.section.endY + 2) + " l0 " + (this.lineHeight * 0.38) + " l13 0 l0 -2 l2 2 l-2 2 l0 -2"); - arrow.setAttribute("fill", "none"); - arrow.setAttribute("stroke", "white"); - arrow.setAttribute("stroke-width", "1.5"); - this.sectionRoot.appendChild(arrow); - posX += 20; - } - const text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = _text; - text.setAttribute("x", posX.toString()); - text.setAttribute("y", (this.section.endY + this.lineHeight * 0.5).toString()); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", _textSize.toString()); - text.setAttribute("font-family", this.textStyle); - text.setAttribute("alignment-baseline", "central"); - this.sectionRoot.appendChild(text); - const item = new PopupMenu_Item(PopupMenu_ItemType.TITLE, this.section, this.section.endY, this.lineHeight); - this.section.items.push(item); - this.section.endY += this.lineHeight; - } - addList(_text, _textSize, _values, _dictKeys) { - const enabled = (_dictKeys != null) ? true : false; - const text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = _text; - text.setAttribute("x", (this.columnLeft2 + this.textMarginX).toString()); - text.setAttribute("y", (this.section.endY + this.lineHeight * 0.5).toString()); - text.setAttribute("fill", (enabled) ? "white" : this.disabledColor); - text.setAttribute("font-size", _textSize.toString()); - text.setAttribute("font-family", this.textStyle); - text.setAttribute("alignment-baseline", "central"); - this.sectionRoot.appendChild(text); - const hl = document.createElementNS(Avionics.SVG.NS, "rect"); - hl.setAttribute("x", (this.columnLeft3 - 2).toString()); - hl.setAttribute("y", (this.section.endY + 2).toString()); - hl.setAttribute("width", (this.menuWidth - 2 - (this.columnLeft3 - 2)).toString()); - hl.setAttribute("height", ((this.section.endY + this.lineHeight - 2) - (this.section.endY + 2)).toString()); - hl.setAttribute("fill", (enabled) ? this.interactionColor : this.disabledColor); - hl.setAttribute("visibility", "hidden"); - this.sectionRoot.appendChild(hl); - const choice = document.createElementNS(Avionics.SVG.NS, "text"); - choice.textContent = _values[0]; - choice.setAttribute("x", this.columnLeft3.toString()); - choice.setAttribute("y", (this.section.endY + this.lineHeight * 0.5).toString()); - choice.setAttribute("fill", (enabled) ? this.interactionColor : this.disabledColor); - choice.setAttribute("font-size", _textSize.toString()); - choice.setAttribute("font-family", this.textStyle); - choice.setAttribute("alignment-baseline", "central"); - this.sectionRoot.appendChild(choice); - const item = new PopupMenu_Item(PopupMenu_ItemType.LIST, this.section, this.section.endY, this.lineHeight); - item.dictKeys = _dictKeys; - item.listElem = choice; - item.listValues = _values; - item.listHLElem = hl; - this.section.items.push(item); - this.section.endY += this.lineHeight; - } - addRange(_text, _textSize, _min, _max, _step, _dictKeys) { - const enabled = (_dictKeys != null) ? true : false; - const text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = _text; - text.setAttribute("x", (this.columnLeft2 + this.textMarginX).toString()); - text.setAttribute("y", (this.section.endY + this.lineHeight * 0.5).toString()); - text.setAttribute("fill", (enabled) ? "white" : this.disabledColor); - text.setAttribute("font-size", _textSize.toString()); - text.setAttribute("font-family", this.textStyle); - text.setAttribute("alignment-baseline", "central"); - this.sectionRoot.appendChild(text); - const hl = document.createElementNS(Avionics.SVG.NS, "rect"); - hl.setAttribute("x", (this.columnLeft3 - 2).toString()); - hl.setAttribute("y", (this.section.endY + 2).toString()); - hl.setAttribute("width", (this.menuWidth - 2 - (this.columnLeft3 - 2)).toString()); - hl.setAttribute("height", ((this.section.endY + this.lineHeight - 2) - (this.section.endY + 2)).toString()); - hl.setAttribute("fill", (enabled) ? this.interactionColor : this.disabledColor); - hl.setAttribute("visibility", "hidden"); - this.sectionRoot.appendChild(hl); - const range = document.createElementNS(Avionics.SVG.NS, "text"); - range.setAttribute("x", this.columnLeft3.toString()); - range.setAttribute("y", (this.section.endY + this.lineHeight * 0.5).toString()); - range.setAttribute("fill", (enabled) ? this.interactionColor : this.disabledColor); - range.setAttribute("font-size", _textSize.toString()); - range.setAttribute("font-family", this.textStyle); - range.setAttribute("alignment-baseline", "central"); - this.sectionRoot.appendChild(range); - const item = new PopupMenu_Item(PopupMenu_ItemType.RANGE, this.section, this.section.endY, this.lineHeight); - item.dictKeys = _dictKeys; - item.rangeElem = range; - item.rangeHLElem = hl; - item.rangeMin = _min; - item.rangeMax = _max; - item.rangeVal = _min; - item.rangeStep = _step; - item.rangeDecimals = Utils.countDecimals(_step); - this.section.items.push(item); - this.section.endY += this.lineHeight; - } - addRadio(_text, _textSize, _dictKeys) { - const enabled = (_dictKeys != null) ? true : false; - const cx = this.columnLeft1 + (this.columnLeft2 - this.columnLeft1) * 0.5; - const cy = this.section.endY + this.lineHeight * 0.5; - let shape; - if (this.shape3D) { - const b = this.shape3DBorderSize; - const h = Math.min(this.lineHeight, this.columnLeft2) * 0.8; - const w = h * 0.75; - if (enabled) { - const leftBorder = document.createElementNS(Avionics.SVG.NS, "path"); - leftBorder.setAttribute("d", "M" + (cx) + " " + (cy - h * 0.5) + " l" + (-w * 0.5) + " " + (h * 0.5) + " l" + (w * 0.5) + " " + (h * 0.5) + " l0" + (-b) + " l" + (-w * 0.5 + b) + " " + (-h * 0.5 + b) + " l" + (w * 0.5 - b) + " " + (-h * 0.5 + b) + " Z"); - leftBorder.setAttribute("fill", this.shape3DBorderLeft); - this.sectionRoot.appendChild(leftBorder); - const rightBorder = document.createElementNS(Avionics.SVG.NS, "path"); - rightBorder.setAttribute("d", "M" + (cx) + " " + (cy - h * 0.5) + " l" + (w * 0.5) + " " + (h * 0.5) + " l" + (-w * 0.5) + " " + (h * 0.5) + " l0" + (-b) + " l" + (w * 0.5 - b) + " " + (-h * 0.5 + b) + " l" + (-w * 0.5 + b) + " " + (-h * 0.5 + b) + " Z"); - rightBorder.setAttribute("fill", this.shape3DBorderRight); - this.sectionRoot.appendChild(rightBorder); - } - shape = document.createElementNS(Avionics.SVG.NS, "path"); - shape.setAttribute("d", "M" + (cx) + " " + (cy - h * 0.5 + b) + " L" + (cx - w * 0.5 + b) + " " + (cy) + " L" + (cx) + " " + (cy + h * 0.5 - b) + " L" + (cx + w * 0.5 - b) + " " + (cy) + " Z"); - shape.setAttribute("fill", (enabled) ? this.shapeFillColor : ((this.shapeFillIfDisabled) ? this.disabledColor : "none")); - if (!enabled) { - shape.setAttribute("stroke", this.disabledColor); - shape.setAttribute("stroke-width", "1"); - } - this.sectionRoot.appendChild(shape); - } else { - const size = Math.min(this.lineHeight, this.columnLeft2) * 0.33; - shape = document.createElementNS(Avionics.SVG.NS, "circle"); - shape.setAttribute("cx", cx.toString()); - shape.setAttribute("cy", cy.toString()); - shape.setAttribute("r", size.toString()); - shape.setAttribute("fill", (enabled) ? this.shapeFillColor : ((this.shapeFillIfDisabled) ? this.disabledColor : "none")); - shape.setAttribute("stroke", (enabled) ? "white" : this.disabledColor); - shape.setAttribute("stroke-width", "1"); - this.sectionRoot.appendChild(shape); - } - const text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = _text; - text.setAttribute("x", (this.columnLeft2 + this.textMarginX).toString()); - text.setAttribute("y", (this.section.endY + this.lineHeight * 0.5).toString()); - text.setAttribute("fill", (enabled) ? "white" : this.disabledColor); - text.setAttribute("font-size", _textSize.toString()); - text.setAttribute("font-family", this.textStyle); - text.setAttribute("alignment-baseline", "central"); - this.sectionRoot.appendChild(text); - const item = new PopupMenu_Item(PopupMenu_ItemType.RADIO, this.section, this.section.endY, this.lineHeight); - item.dictKeys = _dictKeys; - item.radioElem = shape; - item.radioName = _text; - this.section.items.push(item); - this.registerWithMouse(item); - this.section.endY += this.lineHeight; - } - addRadioList(_text, _textSize, _values, _dictKeys) { - const enabled = (_dictKeys != null) ? true : false; - const cx = this.columnLeft1 + (this.columnLeft2 - this.columnLeft1) * 0.5; - const cy = this.section.endY + this.lineHeight * 0.5; - let shape; - if (this.shape3D) { - const b = this.shape3DBorderSize; - const h = Math.min(this.lineHeight, this.columnLeft2) * 0.8; - const w = h * 0.75; - if (enabled) { - const leftBorder = document.createElementNS(Avionics.SVG.NS, "path"); - leftBorder.setAttribute("d", "M" + (cx) + " " + (cy - h * 0.5) + " l" + (-w * 0.5) + " " + (h * 0.5) + " l" + (w * 0.5) + " " + (h * 0.5) + " l0" + (-b) + " l" + (-w * 0.5 + b) + " " + (-h * 0.5 + b) + " l" + (w * 0.5 - b) + " " + (-h * 0.5 + b) + " Z"); - leftBorder.setAttribute("fill", this.shape3DBorderLeft); - this.sectionRoot.appendChild(leftBorder); - const rightBorder = document.createElementNS(Avionics.SVG.NS, "path"); - rightBorder.setAttribute("d", "M" + (cx) + " " + (cy - h * 0.5) + " l" + (w * 0.5) + " " + (h * 0.5) + " l" + (-w * 0.5) + " " + (h * 0.5) + " l0" + (-b) + " l" + (w * 0.5 - b) + " " + (-h * 0.5 + b) + " l" + (-w * 0.5 + b) + " " + (-h * 0.5 + b) + " Z"); - rightBorder.setAttribute("fill", this.shape3DBorderRight); - this.sectionRoot.appendChild(rightBorder); - } - shape = document.createElementNS(Avionics.SVG.NS, "path"); - shape.setAttribute("d", "M" + (cx) + " " + (cy - h * 0.5 + b) + " L" + (cx - w * 0.5 + b) + " " + (cy) + " L" + (cx) + " " + (cy + h * 0.5 - b) + " L" + (cx + w * 0.5 - b) + " " + (cy) + " Z"); - shape.setAttribute("fill", (enabled) ? this.shapeFillColor : ((this.shapeFillIfDisabled) ? this.disabledColor : "none")); - if (!enabled) { - shape.setAttribute("stroke", this.disabledColor); - shape.setAttribute("stroke-width", "1"); - } - this.sectionRoot.appendChild(shape); - } else { - const size = Math.min(this.lineHeight, this.columnLeft2) * 0.33; - shape = document.createElementNS(Avionics.SVG.NS, "circle"); - shape.setAttribute("cx", cx.toString()); - shape.setAttribute("cy", cy.toString()); - shape.setAttribute("r", size.toString()); - shape.setAttribute("fill", (enabled) ? this.shapeFillColor : ((this.shapeFillIfDisabled) ? this.disabledColor : "none")); - shape.setAttribute("stroke", (enabled) ? "white" : this.disabledColor); - shape.setAttribute("stroke-width", "1"); - this.sectionRoot.appendChild(shape); - } - const text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = _text; - text.setAttribute("x", (this.columnLeft2 + this.textMarginX).toString()); - text.setAttribute("y", (this.section.endY + this.lineHeight * 0.5).toString()); - text.setAttribute("fill", (enabled) ? "white" : this.disabledColor); - text.setAttribute("font-size", _textSize.toString()); - text.setAttribute("font-family", this.textStyle); - text.setAttribute("alignment-baseline", "central"); - this.sectionRoot.appendChild(text); - const hl = document.createElementNS(Avionics.SVG.NS, "rect"); - hl.setAttribute("x", (this.columnLeft3 - 2).toString()); - hl.setAttribute("y", (this.section.endY + 2).toString()); - hl.setAttribute("width", (this.menuWidth - 2 - (this.columnLeft3 - 2)).toString()); - hl.setAttribute("height", ((this.section.endY + this.lineHeight - 2) - (this.section.endY + 2)).toString()); - hl.setAttribute("fill", this.interactionColor); - hl.setAttribute("visibility", "hidden"); - this.sectionRoot.appendChild(hl); - const choice = document.createElementNS(Avionics.SVG.NS, "text"); - choice.textContent = _values[0]; - choice.setAttribute("x", this.columnLeft3.toString()); - choice.setAttribute("y", (this.section.endY + this.lineHeight * 0.5).toString()); - choice.setAttribute("fill", (enabled) ? this.interactionColor : this.disabledColor); - choice.setAttribute("font-size", _textSize.toString()); - choice.setAttribute("font-family", this.textStyle); - choice.setAttribute("alignment-baseline", "central"); - this.sectionRoot.appendChild(choice); - const item = new PopupMenu_Item(PopupMenu_ItemType.RADIO_LIST, this.section, this.section.endY, this.lineHeight); - item.dictKeys = _dictKeys; - item.radioElem = shape; - item.radioName = _text; - item.listElem = choice; - item.listHLElem = hl; - item.listValues = _values; - this.section.items.push(item); - this.registerWithMouse(item); - this.section.endY += this.lineHeight; - } - addRadioRange(_text, _textSize, _min, _max, _step, _dictKeys) { - const enabled = (_dictKeys != null) ? true : false; - const cx = this.columnLeft1 + (this.columnLeft2 - this.columnLeft1) * 0.5; - const cy = this.section.endY + this.lineHeight * 0.5; - let shape; - if (this.shape3D) { - const b = this.shape3DBorderSize; - const h = Math.min(this.lineHeight, this.columnLeft2) * 0.8; - const w = h * 0.75; - if (enabled) { - const leftBorder = document.createElementNS(Avionics.SVG.NS, "path"); - leftBorder.setAttribute("d", "M" + (cx) + " " + (cy - h * 0.5) + " l" + (-w * 0.5) + " " + (h * 0.5) + " l" + (w * 0.5) + " " + (h * 0.5) + " l0" + (-b) + " l" + (-w * 0.5 + b) + " " + (-h * 0.5 + b) + " l" + (w * 0.5 - b) + " " + (-h * 0.5 + b) + " Z"); - leftBorder.setAttribute("fill", this.shape3DBorderLeft); - this.sectionRoot.appendChild(leftBorder); - const rightBorder = document.createElementNS(Avionics.SVG.NS, "path"); - rightBorder.setAttribute("d", "M" + (cx) + " " + (cy - h * 0.5) + " l" + (w * 0.5) + " " + (h * 0.5) + " l" + (-w * 0.5) + " " + (h * 0.5) + " l0" + (-b) + " l" + (w * 0.5 - b) + " " + (-h * 0.5 + b) + " l" + (-w * 0.5 + b) + " " + (-h * 0.5 + b) + " Z"); - rightBorder.setAttribute("fill", this.shape3DBorderRight); - this.sectionRoot.appendChild(rightBorder); - } - shape = document.createElementNS(Avionics.SVG.NS, "path"); - shape.setAttribute("d", "M" + (cx) + " " + (cy - h * 0.5 + b) + " L" + (cx - w * 0.5 + b) + " " + (cy) + " L" + (cx) + " " + (cy + h * 0.5 - b) + " L" + (cx + w * 0.5 - b) + " " + (cy) + " Z"); - shape.setAttribute("fill", (enabled) ? this.shapeFillColor : ((this.shapeFillIfDisabled) ? this.disabledColor : "none")); - if (!enabled) { - shape.setAttribute("stroke", this.disabledColor); - shape.setAttribute("stroke-width", "1"); - } - this.sectionRoot.appendChild(shape); - } else { - const size = Math.min(this.lineHeight, this.columnLeft2) * 0.33; - shape = document.createElementNS(Avionics.SVG.NS, "circle"); - shape.setAttribute("cx", cx.toString()); - shape.setAttribute("cy", cy.toString()); - shape.setAttribute("r", size.toString()); - shape.setAttribute("fill", (enabled) ? this.shapeFillColor : ((this.shapeFillIfDisabled) ? this.disabledColor : "none")); - shape.setAttribute("stroke", (enabled) ? "white" : this.disabledColor); - shape.setAttribute("stroke-width", "1"); - this.sectionRoot.appendChild(shape); - } - const text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = _text; - text.setAttribute("x", (this.columnLeft2 + this.textMarginX).toString()); - text.setAttribute("y", (this.section.endY + this.lineHeight * 0.5).toString()); - text.setAttribute("fill", (enabled) ? "white" : this.disabledColor); - text.setAttribute("font-size", _textSize.toString()); - text.setAttribute("font-family", this.textStyle); - text.setAttribute("alignment-baseline", "central"); - this.sectionRoot.appendChild(text); - const hl = document.createElementNS(Avionics.SVG.NS, "rect"); - hl.setAttribute("x", (this.columnLeft3 - 2).toString()); - hl.setAttribute("y", (this.section.endY + 2).toString()); - hl.setAttribute("width", (this.menuWidth - 2 - (this.columnLeft3 - 2)).toString()); - hl.setAttribute("height", ((this.section.endY + this.lineHeight - 2) - (this.section.endY + 2)).toString()); - hl.setAttribute("fill", this.interactionColor); - hl.setAttribute("visibility", "hidden"); - this.sectionRoot.appendChild(hl); - const range = document.createElementNS(Avionics.SVG.NS, "text"); - range.setAttribute("x", this.columnLeft3.toString()); - range.setAttribute("y", (this.section.endY + this.lineHeight * 0.5).toString()); - range.setAttribute("fill", (enabled) ? this.interactionColor : this.disabledColor); - range.setAttribute("font-size", _textSize.toString()); - range.setAttribute("font-family", this.textStyle); - range.setAttribute("alignment-baseline", "central"); - this.sectionRoot.appendChild(range); - const item = new PopupMenu_Item(PopupMenu_ItemType.RADIO_RANGE, this.section, this.section.endY, this.lineHeight); - item.dictKeys = _dictKeys; - item.radioElem = shape; - item.radioName = _text; - item.rangeElem = range; - item.rangeHLElem = hl; - item.rangeMin = _min; - item.rangeMax = _max; - item.rangeStep = _step; - item.rangeDecimals = Utils.countDecimals(_step); - this.section.items.push(item); - this.registerWithMouse(item); - this.section.endY += this.lineHeight; - } - addCheckbox(_text, _textSize, _dictKeys) { - const enabled = (_dictKeys != null) ? true : false; - const size = Math.min(this.lineHeight, this.columnLeft2) * 0.66; - const cx = this.columnLeft1 + (this.columnLeft2 - this.columnLeft1) * 0.5; - const cy = this.section.endY + this.lineHeight * 0.5; - let shape; - if (this.shape3D && enabled) { - const b = this.shape3DBorderSize; - const topLeftBorder = document.createElementNS(Avionics.SVG.NS, "path"); - topLeftBorder.setAttribute("d", "M" + (cx - size * 0.5) + " " + (cy - size * 0.5) + " l" + (size) + " 0 l" + (-b) + " " + (b) + " l" + (-(size - b * 2)) + " 0 l0 " + (size - b * 2) + " l" + (-b) + " " + (b) + " Z"); - topLeftBorder.setAttribute("fill", this.shape3DBorderLeft); - this.sectionRoot.appendChild(topLeftBorder); - const bottomRightBorder = document.createElementNS(Avionics.SVG.NS, "path"); - bottomRightBorder.setAttribute("d", "M" + (cx + size * 0.5) + " " + (cy + size * 0.5) + " l" + (-size) + " 0 l" + (b) + " " + (-b) + " l" + (size - b * 2) + " 0 l0 " + (-(size - b * 2)) + " l" + (b) + " " + (-b) + " Z"); - bottomRightBorder.setAttribute("fill", this.shape3DBorderRight); - this.sectionRoot.appendChild(bottomRightBorder); - shape = document.createElementNS(Avionics.SVG.NS, "rect"); - shape.setAttribute("x", (cx - size * 0.5 + b).toString()); - shape.setAttribute("y", (cy - size * 0.5 + b).toString()); - shape.setAttribute("width", (size - b * 2).toString()); - shape.setAttribute("height", (size - b * 2).toString()); - shape.setAttribute("fill", this.shapeFillColor); - this.sectionRoot.appendChild(shape); - } else { - shape = document.createElementNS(Avionics.SVG.NS, "rect"); - shape.setAttribute("x", (cx - size * 0.5).toString()); - shape.setAttribute("y", (cy - size * 0.5).toString()); - shape.setAttribute("width", size.toString()); - shape.setAttribute("height", size.toString()); - shape.setAttribute("fill", (enabled) ? this.shapeFillColor : ((this.shapeFillIfDisabled) ? this.disabledColor : "none")); - shape.setAttribute("stroke", (enabled) ? "white" : this.disabledColor); - shape.setAttribute("stroke-width", "1"); - this.sectionRoot.appendChild(shape); - } - const tick = document.createElementNS(Avionics.SVG.NS, "path"); - tick.setAttribute("d", "M" + (cx - size * 0.5) + " " + (cy) + " l" + (size * 0.4) + " " + (size * 0.5) + " l" + (size * 0.6) + " " + (-size)); - tick.setAttribute("fill", "none"); - tick.setAttribute("stroke", this.interactionColor); - tick.setAttribute("stroke-width", "4"); - tick.setAttribute("visibility", "hidden"); - this.sectionRoot.appendChild(tick); - const text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = _text; - text.setAttribute("x", (this.columnLeft2 + this.textMarginX).toString()); - text.setAttribute("y", (this.section.endY + this.lineHeight * 0.5).toString()); - text.setAttribute("fill", (enabled) ? "white" : this.disabledColor); - text.setAttribute("font-size", _textSize.toString()); - text.setAttribute("font-family", this.textStyle); - text.setAttribute("alignment-baseline", "central"); - this.sectionRoot.appendChild(text); - const item = new PopupMenu_Item(PopupMenu_ItemType.CHECKBOX, this.section, this.section.endY, this.lineHeight); - item.dictKeys = _dictKeys; - item.checkboxElem = shape; - item.checkboxTickElem = tick; - this.section.items.push(item); - this.registerWithMouse(item); - this.section.endY += this.lineHeight; - } - addSubMenu(_text, _textSize, _callback) { - const enabled = (_callback != null) ? true : false; - const size = Math.min(this.lineHeight, this.columnLeft2) * 0.66; - const cx = this.columnLeft1 + (this.columnLeft2 - this.columnLeft1) * 0.5; - const cy = this.section.endY + this.lineHeight * 0.5; - const arrow = document.createElementNS(Avionics.SVG.NS, "path"); - arrow.setAttribute("d", "M" + (cx - size * 0.5) + " " + (cy - size * 0.5) + " l0 " + (size) + " l" + (size * 0.75) + " " + (-size * 0.5) + " Z"); - arrow.setAttribute("fill", (enabled) ? this.interactionColor : ((this.shapeFillIfDisabled) ? this.disabledColor : "none")); - this.sectionRoot.appendChild(arrow); - const text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = _text; - text.setAttribute("x", (this.columnLeft2 + this.textMarginX).toString()); - text.setAttribute("y", (this.section.endY + this.lineHeight * 0.5).toString()); - text.setAttribute("fill", (enabled) ? "white" : this.disabledColor); - text.setAttribute("font-size", _textSize.toString()); - text.setAttribute("font-family", this.textStyle); - text.setAttribute("alignment-baseline", "central"); - this.sectionRoot.appendChild(text); - const item = new PopupMenu_Item(PopupMenu_ItemType.SUBMENU, this.section, this.section.endY, this.lineHeight); - item.subMenu = _callback; - this.section.items.push(item); - this.registerWithMouse(item); - this.section.endY += this.lineHeight; - } - addButton(_text, _textSize, _callback) { - const enabled = (_callback != null) ? true : false; - const cx = this.menuLeft + (this.menuWidth - this.menuLeft) * 0.5; - const cy = this.section.endY + this.lineHeight * 0.5; - const w = this.menuWidth * 0.66; - const h = this.lineHeight * 0.66; - let shape; - if (this.shape3D && enabled) { - const b = this.shape3DBorderSize; - const topLeftBorder = document.createElementNS(Avionics.SVG.NS, "path"); - topLeftBorder.setAttribute("d", "M" + (cx - w * 0.5) + " " + (cy - h * 0.5) + " l" + (w) + " 0 l" + (-b) + " " + (b) + " l" + (-(w - b * 2)) + " 0 l0 " + (h - b * 2) + " l" + (-b) + " " + (b) + " Z"); - topLeftBorder.setAttribute("fill", this.shape3DBorderLeft); - this.sectionRoot.appendChild(topLeftBorder); - const bottomRightBorder = document.createElementNS(Avionics.SVG.NS, "path"); - bottomRightBorder.setAttribute("d", "M" + (cx + w * 0.5) + " " + (cy + h * 0.5) + " l" + (-w) + " 0 l" + (b) + " " + (-b) + " l" + (w - b * 2) + " 0 l0 " + (-(h - b * 2)) + " l" + (b) + " " + (-b) + " Z"); - bottomRightBorder.setAttribute("fill", this.shape3DBorderRight); - this.sectionRoot.appendChild(bottomRightBorder); - shape = document.createElementNS(Avionics.SVG.NS, "rect"); - shape.setAttribute("x", (cx - w * 0.5 + b).toString()); - shape.setAttribute("y", (cy - h * 0.5 + b).toString()); - shape.setAttribute("width", (w - b * 2).toString()); - shape.setAttribute("height", (h - b * 2).toString()); - shape.setAttribute("fill", this.shapeFillColor); - this.sectionRoot.appendChild(shape); - } else { - shape = document.createElementNS(Avionics.SVG.NS, "rect"); - shape.setAttribute("x", (cx - w * 0.5).toString()); - shape.setAttribute("y", (cy - h * 0.5).toString()); - shape.setAttribute("width", w.toString()); - shape.setAttribute("height", h.toString()); - shape.setAttribute("fill", (enabled) ? this.shapeFillColor : ((this.shapeFillIfDisabled) ? this.disabledColor : "none")); - shape.setAttribute("stroke", (enabled) ? "white" : this.disabledColor); - shape.setAttribute("stroke-width", "1"); - this.sectionRoot.appendChild(shape); - } - const text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = _text; - text.setAttribute("x", cx.toString()); - text.setAttribute("y", cy.toString()); - text.setAttribute("fill", (enabled) ? "white" : this.disabledColor); - text.setAttribute("font-size", _textSize.toString()); - text.setAttribute("font-family", this.textStyle); - text.setAttribute("text-anchor", "middle"); - text.setAttribute("alignment-baseline", "central"); - this.sectionRoot.appendChild(text); - const item = new PopupMenu_Item(PopupMenu_ItemType.SUBMENU, this.section, this.section.endY, this.lineHeight); - item.subMenu = _callback; - this.section.items.push(item); - this.registerWithMouse(item); - this.section.endY += this.lineHeight; - } - updateHighlight() { - if (this.highlightElem) { - let itemId = 0; - let lastItem; - for (let i = 0; i < this.allSections.length; i++) { - const section = this.allSections[i]; - for (let j = 0; j < section.items.length; j++) { - const item = section.items[j]; - if (item.interactive) { - if (itemId == this.highlightId) { - this.setHighlightedItem(item); - return true; - } - lastItem = item; - itemId++; - } - } - } - if (lastItem) { - this.highlightId = itemId - 1; - this.setHighlightedItem(lastItem); - } - } - } - setHighlightedItem(_item) { - if (_item != this.highlightItem) { - this.highlightItem = _item; - this.highlightElem.setAttribute("y", _item.y.toString()); - this.speedInc = 1.0; - } - } - activateItem(_item, _val) { - if (!_item.enabled) { - return; - } - switch (_item.type) { - case PopupMenu_ItemType.RADIO: - case PopupMenu_ItemType.RADIO_LIST: - case PopupMenu_ItemType.RADIO_RANGE: - if (_val) { - _item.radioVal = true; - _item.radioElem.setAttribute("fill", this.interactionColor); - } else { - _item.radioVal = false; - _item.radioElem.setAttribute("fill", this.shapeFillColor); - } - break; - case PopupMenu_ItemType.CHECKBOX: - if (_val) { - _item.checkboxVal = true; - _item.checkboxTickElem.setAttribute("visibility", "visible"); - } else { - _item.checkboxVal = false; - _item.checkboxTickElem.setAttribute("visibility", "hidden"); - } - break; - } - } - updateSpeedInc() { - if (this.highlightItem) { - if (this.speedInc > 1) { - this.speedInc -= this.speedInc_DownFactor; - if (this.speedInc < 1) { - this.speedInc = 1; - } - } - } else { - this.speedInc = 1.0; - } - } - getSpeedAccel() { - const pow = 1.0 + ((this.speedInc - 1.0) * this.speedInc_PowFactor); - const accel = Math.pow(this.speedInc, pow); - return Math.floor(accel); - } - onChanged(_item) { - if (this.dictionary && _item.enabled) { - switch (_item.type) { - case PopupMenu_ItemType.RADIO: - case PopupMenu_ItemType.RADIO_LIST: - case PopupMenu_ItemType.RADIO_RANGE: - let found = false; - for (let i = 0; i < _item.section.items.length; i++) { - if (_item.section.items[i].radioVal) { - this.dictionary.set(_item.dictKeys[0], _item.radioName); - found = true; - break; - } - } - if (!found) { - this.dictionary.remove(_item.dictKeys[0]); - } - break; - case PopupMenu_ItemType.LIST: - this.dictionary.set(_item.dictKeys[0], _item.listValues[_item.listVal]); - break; - case PopupMenu_ItemType.RANGE: - this.dictionary.set(_item.dictKeys[0], _item.rangeVal.toString()); - break; - case PopupMenu_ItemType.CHECKBOX: - this.dictionary.set(_item.dictKeys[0], (_item.checkboxVal) ? "ON" : "OFF"); - break; - } - switch (_item.type) { - case PopupMenu_ItemType.RADIO_LIST: - this.dictionary.set(_item.dictKeys[1], _item.listValues[_item.listVal]); - break; - case PopupMenu_ItemType.RADIO_RANGE: - this.dictionary.set(_item.dictKeys[1], _item.rangeVal.toString()); - break; - } - } - } - registerWithMouse(_item) { - const mouseFrame = document.createElementNS(Avionics.SVG.NS, "rect"); - mouseFrame.setAttribute("x", this.menuLeft.toString()); - mouseFrame.setAttribute("y", this.section.endY.toString()); - mouseFrame.setAttribute("width", this.menuWidth.toString()); - mouseFrame.setAttribute("height", this.lineHeight.toString()); - mouseFrame.setAttribute("fill", "none"); - mouseFrame.setAttribute("pointer-events", "visible"); - this.sectionRoot.appendChild(mouseFrame); - mouseFrame.addEventListener("mouseover", this.onMouseOver.bind(this, _item)); - mouseFrame.addEventListener("mouseup", this.onMousePress.bind(this, _item)); - } - onMouseOver(_item) { - if (_item.enabled) { - let itemId = 0; - for (let i = 0; i < this.allSections.length; i++) { - const section = this.allSections[i]; - for (let j = 0; j < section.items.length; j++) { - const item = section.items[j]; - if (item.interactive) { - if (item == _item) { - this.highlightId = itemId; - return; - } - itemId++; - } - } - } - } - } - onMousePress(_item) { - if (_item.enabled) { - this.onActivate(); - } - } - } - Airliners.PopupMenu_Handler = PopupMenu_Handler; -})(Airliners || (Airliners = {})); diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/legacy/A32NX_NavSystem.js b/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/legacy/A32NX_NavSystem.js deleted file mode 100644 index b97f65405a6..00000000000 --- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/legacy/A32NX_NavSystem.js +++ /dev/null @@ -1,2927 +0,0 @@ -// Copyright (c) 2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -class NavSystem extends BaseInstrument { - constructor() { - super(...arguments); - this.soundSourceNode = "AS1000_MFD"; - this.eventAliases = []; - this.IndependentsElements = []; - this.pageGroups = []; - this.eventLinkedPageGroups = []; - this.currentEventLinkedPageGroup = null; - this.overridePage = null; - this.eventLinkedPopUpElements = []; - this.popUpElement = null; - this.popUpCloseCallback = null; - this.currentPageGroupIndex = 0; - this.currentInteractionState = 0; - this.cursorIndex = 0; - this.currentSelectableArray = []; - this.currentContextualMenu = null; - this.currentSearchFieldWaypoint = null; - this.contextualMenuDisplayBeginIndex = 0; - this.menuMaxElems = 6; - this.useUpdateBudget = true; - this.maxUpdateBudget = 6; - this.budgetedItemId = 0; - this.aspectRatioElement = null; - this.forcedAspectRatioSet = false; - this.forcedAspectRatio = 1; - this.forcedScreenRatio = 1; - this.initDuration = 0; - this.hasBeenOff = false; - this.needValidationAfterInit = false; - this.isStarted = false; - this.initAcknowledged = true; - this.reversionaryMode = false; - this.alwaysUpdateList = new Array(); - this.accumulatedDeltaTime = 0; - } - /** @type {FlightPlanManager} */ - get flightPlanManager() { - return this.currFlightPlanManager; - } - get instrumentAlias() { - return null; - } - connectedCallback() { - super.connectedCallback(); - this.contextualMenu = this.getChildById("ContextualMenu"); - this.contextualMenuTitle = this.getChildById("ContextualMenuTitle"); - this.contextualMenuElements = this.getChildById("ContextualMenuElements"); - this.menuSlider = this.getChildById("SliderMenu"); - this.menuSliderCursor = this.getChildById("SliderMenuCursor"); - this.currFlightPlanManager = null; - this.currFlightPlan = null; - this.currFlightPhaseManager = null; - } - get flightPhaseManager() { - return this.currFlightPhaseManager; - } - disconnectedCallback() { - super.disconnectedCallback(); - } - parseXMLConfig() { - super.parseXMLConfig(); - const soundSourceNodeElem = this.xmlConfig.getElementsByTagName("SoundSourceNode"); - if (soundSourceNodeElem.length > 0) { - this.soundSourceNode = soundSourceNodeElem[0].textContent; - } - if (this.instrumentXmlConfig) { - const skipValidationAfterInitElem = this.instrumentXmlConfig.getElementsByTagName("SkipValidationAfterInit"); - if (skipValidationAfterInitElem.length > 0 && this.needValidationAfterInit) { - this.needValidationAfterInit = skipValidationAfterInitElem[0].textContent != "True"; - } - const styleNode = this.instrumentXmlConfig.getElementsByTagName("Style"); - if (styleNode.length > 0) { - this.electricity.setAttribute("displaystyle", styleNode[0].textContent); - } - } - } - computeEvent(_event) { - if (this.isBootProcedureComplete()) { - for (let i = 0; i < this.eventLinkedPageGroups.length; i++) { - if (_event == this.eventLinkedPageGroups[i].popUpEvent) { - this.lastRelevantICAO = null; - this.lastRelevantICAOType = null; - if (this.overridePage) { - this.closeOverridePage(); - } - if (this.eventLinkedPageGroups[i] == this.currentEventLinkedPageGroup) { - this.exitEventLinkedPageGroup(); - } else { - const currentGroup = this.getCurrentPageGroup(); - if (currentGroup) { - currentGroup.onExit(); - } - this.currentEventLinkedPageGroup = this.eventLinkedPageGroups[i]; - this.currentEventLinkedPageGroup.pageGroup.onEnter(); - } - this.SwitchToInteractionState(0); - } - } - for (let i = 0; i < this.eventLinkedPopUpElements.length; i++) { - if (_event == this.eventLinkedPopUpElements[i].popUpEvent) { - if (this.popUpElement == this.eventLinkedPopUpElements[i]) { - this.popUpElement.onExit(); - this.popUpElement = null; - } else { - this.switchToPopUpPage(this.eventLinkedPopUpElements[i]); - } - } - } - for (let i = 0; i < this.IndependentsElements.length; i++) { - this.IndependentsElements[i].onEvent(_event); - } - if (this.popUpElement) { - this.popUpElement.onEvent(_event); - } - const currentPage = this.getCurrentPage(); - if (currentPage) { - currentPage.onEvent(_event); - } - switch (this.currentInteractionState) { - case 1: - if (this.currentSelectableArray[this.cursorIndex].SendEvent(_event)) { - break; - } - if (_event == "NavigationPush") { - this.SwitchToInteractionState(0); - } - if (_event == "NavigationLargeInc") { - this.cursorIndex = (this.cursorIndex + 1) % this.currentSelectableArray.length; - while (!this.currentSelectableArray[this.cursorIndex].onSelection(_event)) { - this.cursorIndex = (this.cursorIndex + 1) % this.currentSelectableArray.length; - } - } - if (_event == "NavigationLargeDec") { - this.cursorIndex = (this.cursorIndex - 1) < 0 ? (this.currentSelectableArray.length - 1) : (this.cursorIndex - 1); - while (!this.currentSelectableArray[this.cursorIndex].onSelection(_event)) { - this.cursorIndex = (this.cursorIndex - 1) < 0 ? (this.currentSelectableArray.length - 1) : (this.cursorIndex - 1); - } - } - if (_event == "MENU_Push") { - var defaultMenu; - if (this.popUpElement) { - defaultMenu = this.popUpElement.getDefaultMenu(); - } - if (!defaultMenu) { - var defaultMenu = this.getCurrentPage().defaultMenu; - } - if (defaultMenu != null) { - this.ShowContextualMenu(defaultMenu); - } - } - break; - case 2: - if (_event == "NavigationSmallInc") { - let count = 0; - do { - this.cursorIndex = (this.cursorIndex + 1) % this.currentContextualMenu.elements.length; - if (this.cursorIndex > (this.contextualMenuDisplayBeginIndex + 5)) { - this.contextualMenuDisplayBeginIndex++; - } - if (this.cursorIndex < (this.contextualMenuDisplayBeginIndex)) { - this.contextualMenuDisplayBeginIndex = 0; - } - count++; - } while (this.currentContextualMenu.elements[this.cursorIndex].isInactive() == true && count < this.currentContextualMenu.elements.length); - } - if (_event == "NavigationSmallDec") { - const count = 0; - do { - this.cursorIndex = (this.cursorIndex - 1) < 0 ? (this.currentContextualMenu.elements.length - 1) : (this.cursorIndex - 1); - if (this.cursorIndex < (this.contextualMenuDisplayBeginIndex)) { - this.contextualMenuDisplayBeginIndex--; - } - if (this.cursorIndex > (this.contextualMenuDisplayBeginIndex + 5)) { - this.contextualMenuDisplayBeginIndex = this.currentContextualMenu.elements.length - 5; - } - } while (this.currentContextualMenu.elements[this.cursorIndex].isInactive() == true && count < this.currentContextualMenu.elements.length); - } - if (_event == "MENU_Push") { - this.SwitchToInteractionState(0); - } - if (_event == "ENT_Push") { - this.currentContextualMenu.elements[this.cursorIndex].SendEvent(); - } - break; - case 3: - this.currentSearchFieldWaypoint.onInteractionEvent([_event]); - break; - case 0: - if (_event == "MENU_Push") { - var defaultMenu; - if (this.popUpElement) { - defaultMenu = this.popUpElement.getDefaultMenu(); - } - if (!defaultMenu) { - var defaultMenu = this.getCurrentPage().defaultMenu; - } - if (defaultMenu != null) { - this.ShowContextualMenu(defaultMenu); - } - } - if (_event == "NavigationSmallInc") { - this.lastRelevantICAO = null; - this.lastRelevantICAOType = null; - this.getCurrentPageGroup().nextPage(); - } - if (_event == "NavigationSmallDec") { - this.lastRelevantICAO = null; - this.lastRelevantICAOType = null; - this.getCurrentPageGroup().prevPage(); - } - if (_event == "NavigationLargeInc") { - this.lastRelevantICAO = null; - this.lastRelevantICAOType = null; - if (this.pageGroups.length > 1 && !this.currentEventLinkedPageGroup) { - this.pageGroups[this.currentPageGroupIndex].onExit(); - this.currentPageGroupIndex = (this.currentPageGroupIndex + 1) % this.pageGroups.length; - this.pageGroups[this.currentPageGroupIndex].onEnter(); - } - } - if (_event == "NavigationLargeDec") { - this.lastRelevantICAO = null; - this.lastRelevantICAOType = null; - if (this.pageGroups.length > 1 && !this.currentEventLinkedPageGroup) { - this.pageGroups[this.currentPageGroupIndex].onExit(); - this.currentPageGroupIndex = (this.currentPageGroupIndex + this.pageGroups.length - 1) % this.pageGroups.length; - this.pageGroups[this.currentPageGroupIndex].onEnter(); - } - } - if (_event == "NavigationPush") { - let defaultSelectableArray = this.getCurrentPage().element.getDefaultSelectables(); - if (this.popUpElement) { - defaultSelectableArray = this.popUpElement.element.getDefaultSelectables(); - } - if (defaultSelectableArray != null && defaultSelectableArray.length > 0) { - this.ActiveSelection(defaultSelectableArray); - } - } - break; - } - switch (_event) { - case "ActiveFPL_Modified": - this.currFlightPlan.FillWithCurrentFP(); - } - } - this.onEvent(_event); - } - exitEventLinkedPageGroup() { - this.currentEventLinkedPageGroup.pageGroup.onExit(); - this.currentEventLinkedPageGroup = null; - const currentGroup = this.getCurrentPageGroup(); - if (currentGroup) { - currentGroup.onEnter(); - } - } - DecomposeEventFromPrefix(_args) { - let search = this.instrumentIdentifier + "_"; - if (_args[0].startsWith(search)) { - return _args[0].slice(search.length); - } - search = this.instrumentAlias; - if (search != null && search != "") { - if (this.urlConfig.index) { - search += "_" + this.urlConfig.index; - } - search += "_"; - if (_args[0].startsWith(search)) { - return _args[0].slice(search.length); - } - } - search = this.templateID + "_"; - if (_args[0].startsWith(search)) { - const evt = _args[0].slice(search.length); - const separator = evt.search("_"); - if (separator >= 0) { - if (!isFinite(parseInt(evt.substring(0, separator)))) { - return evt; - } - } else if (!isFinite(parseInt(evt))) { - return evt; - } - } - search = "Generic_"; - if (_args[0].startsWith(search)) { - return _args[0].slice(search.length); - } - return null; - } - onInteractionEvent(_args) { - if (this.isElectricityAvailable() || SimVar.GetSimVarValue("L:A32NX_ELEC_DC_ESS_BUS_IS_POWERED", "Bool")) { - let event = this.DecomposeEventFromPrefix(_args); - if (event) { - if (event == "ElementSetAttribute" && _args.length >= 4) { - const element = this.getChildById(_args[1]); - if (element) { - Avionics.Utils.diffAndSetAttribute(element, _args[2], _args[3]); - } - } else { - this.computeEvent(event); - for (let i = 0; i < this.eventAliases.length; i++) { - if (this.eventAliases[i].source == event) { - this.computeEvent(this.eventAliases[i].output); - } - } - } - } else if (_args[0].startsWith("NavSystem_")) { - event = _args[0].slice("NavSystem_".length); - this.computeEvent(event); - for (let i = 0; i < this.eventAliases.length; i++) { - if (this.eventAliases[i].source == event) { - this.computeEvent(this.eventAliases[i].output); - } - } - } - } else { - console.log("Electricity Is NOT Available"); - } - } - reboot() { - super.reboot(); - this.startTime = Date.now(); - this.hasBeenOff = false; - this.isStarted = false; - this.initAcknowledged = true; - this.budgetedItemId = 0; - } - Update() { - super.Update(); - this.accumulatedDeltaTime += this.deltaTime; - - if (NavSystem._iterations < 10000) { - NavSystem._iterations += 1; - } - const t0 = performance.now(); - if (this.isElectricityAvailable()) { - if (!this.isStarted) { - this.onPowerOn(); - } - if (this.isBootProcedureComplete()) { - if (this.reversionaryMode) { - if (this.electricity) { - this.electricity.setAttribute("state", "Backup"); - SimVar.SetSimVarValue("L:" + this.instrumentIdentifier + "_ScreenLuminosity", "number", 1); - SimVar.SetSimVarValue("L:" + this.instrumentIdentifier + "_State", "number", 3); - } - } else { - if (this.electricity) { - this.electricity.setAttribute("state", "on"); - SimVar.SetSimVarValue("L:" + this.instrumentIdentifier + "_ScreenLuminosity", "number", 1); - SimVar.SetSimVarValue("L:" + this.instrumentIdentifier + "_State", "number", 2); - } - } - } else if (Date.now() - this.startTime > this.initDuration) { - if (this.electricity) { - this.electricity.setAttribute("state", "initWaitingValidation"); - SimVar.SetSimVarValue("L:" + this.instrumentIdentifier + "_ScreenLuminosity", "number", 0.2); - SimVar.SetSimVarValue("L:" + this.instrumentIdentifier + "_State", "number", 1); - } - } else { - if (this.electricity) { - this.electricity.setAttribute("state", "init"); - SimVar.SetSimVarValue("L:" + this.instrumentIdentifier + "_ScreenLuminosity", "number", 0.2); - SimVar.SetSimVarValue("L:" + this.instrumentIdentifier + "_State", "number", 1); - } - } - } else { - if (this.isStarted) { - this.onShutDown(); - } - if (this.electricity) { - this.electricity.setAttribute("state", "off"); - SimVar.SetSimVarValue("L:" + this.instrumentIdentifier + "_ScreenLuminosity", "number", 0); - SimVar.SetSimVarValue("L:" + this.instrumentIdentifier + "_State", "number", 0); - } - } - this.updateAspectRatio(); - if (this.popUpElement) { - this.popUpElement.onUpdate(this.accumulatedDeltaTime); - } - if (this.pagesContainer) { - this.pagesContainer.setAttribute("state", this.getCurrentPage().htmlElemId); - } - if (this.useUpdateBudget) { - this.updateGroupsWithBudget(); - } else { - this.updateGroups(); - } - switch (this.currentInteractionState) { - case 0: - for (var i = 0; i < this.currentSelectableArray.length; i++) { - this.currentSelectableArray[i].updateSelection(false); - } - break; - case 1: - for (var i = 0; i < this.currentSelectableArray.length; i++) { - if (i == this.cursorIndex) { - this.currentSelectableArray[i].updateSelection(this.blinkGetState(400, 200)); - } else { - this.currentSelectableArray[i].updateSelection(false); - } - } - break; - case 2: - this.currentContextualMenu.Update(this, this.menuMaxElems); - break; - } - try { - this.onUpdate(this.accumulatedDeltaTime); - } catch (e) {} - const t = performance.now() - t0; - NavSystem.maxTimeUpdateAllTime = Math.max(t, NavSystem.maxTimeUpdateAllTime); - NavSystem.maxTimeUpdate = Math.max(t, NavSystem.maxTimeUpdate); - const factor = 1 / NavSystem._iterations; - NavSystem.mediumTimeUpdate *= (1 - factor); - NavSystem.mediumTimeUpdate += factor * t; - this.accumulatedDeltaTime = 0; - } - updateGroups() { - for (let i = 0; i < this.IndependentsElements.length; i++) { - this.IndependentsElements[i].onUpdate(this.accumulatedDeltaTime); - } - if (!this.overridePage) { - const currentGroup = this.getCurrentPageGroup(); - if (currentGroup) { - currentGroup.onUpdate(this.accumulatedDeltaTime); - } - } else { - this.overridePage.onUpdate(this.accumulatedDeltaTime); - } - } - updateGroupsWithBudget() { - const target = this.budgetedItemId + this.maxUpdateBudget; - while (this.budgetedItemId < target) { - if (this.budgetedItemId < this.IndependentsElements.length) { - this.IndependentsElements[this.budgetedItemId].onUpdate(this.accumulatedDeltaTime); - this.budgetedItemId++; - continue; - } - if (!this.overridePage) { - const currentGroup = this.getCurrentPageGroup(); - if (currentGroup) { - const itemId = this.budgetedItemId - this.IndependentsElements.length; - if (currentGroup.onUpdateSpecificItem(this.accumulatedDeltaTime, itemId)) { - this.budgetedItemId++; - continue; - } - } - } else { - this.overridePage.onUpdate(this.accumulatedDeltaTime); - } - this.budgetedItemId = 0; - break; - } - } - onEvent(_event) { - } - onUpdate(_deltaTime) { - } - GetComActiveFreq() { - return this.frequencyFormat(SimVar.GetSimVarValue("COM ACTIVE FREQUENCY:1", "MHz"), 3); - } - GetComStandbyFreq() { - return this.frequencyFormat(SimVar.GetSimVarValue("COM STANDBY FREQUENCY:1", "MHz"), 3); - } - GetNavActiveFreq() { - return this.frequencyFormat(SimVar.GetSimVarValue("NAV ACTIVE FREQUENCY:1", "MHz"), 2); - } - GetNavStandbyFreq() { - return this.frequencyFormat(SimVar.GetSimVarValue("NAV STANDBY FREQUENCY:1", "MHz"), 2); - } - UpdateSlider(_slider, _cursor, _index, _nbElem, _maxElems) { - if (_nbElem > _maxElems) { - _slider.setAttribute("state", "Active"); - _cursor.setAttribute("style", "height:" + (_maxElems * 100 / _nbElem) + - "%;top:" + (_index * 100 / _nbElem) + "%"); - } else { - _slider.setAttribute("state", "Inactive"); - } - } - frequencyFormat(_frequency, _nbDigits) { - const IntPart = Math.floor(_frequency); - const Digits = Math.round((_frequency - IntPart) * Math.pow(10, _nbDigits)); - return fastToFixed(IntPart, 0) + '.' + ('000' + (Digits)).slice(-_nbDigits) + ''; - } - frequencyListFormat(_airport, _baseId, _maxElem = -1, _firstElemIndex = 0) { - if (_airport && _airport.frequencies) { - let htmlFreq = ""; - let endIndex = _airport.frequencies.length; - if (_maxElem != -1) { - endIndex = Math.min(_airport.frequencies.length, _firstElemIndex + _maxElem); - } - for (let i = 0; i < Math.min(_airport.frequencies.length, _maxElem); i++) { - htmlFreq += '
' + _airport.frequencies[i + _firstElemIndex].name.replace(" ", " ").slice(0, 15) + '
' + this.frequencyFormat(_airport.frequencies[i + _firstElemIndex].mhValue, 3) + '
'; - } - return htmlFreq; - } else { - return ""; - } - } - airportPrivateTypeStrFromEnum(_enum) { - switch (_enum) { - case 0: - return "Unknown"; - case 1: - return "Public"; - case 2: - return "Military"; - case 3: - return "Private"; - } - } - longitudeFormat(_longitude) { - let format = ""; - if (_longitude < 0) { - format += "W"; - _longitude = Math.abs(_longitude); - } else { - format += "E"; - } - const degrees = Math.floor(_longitude); - const minutes = ((_longitude - degrees) * 60); - format += fastToFixed(degrees, 0); - format += "°"; - format += fastToFixed(minutes, 2); - format += "'"; - return format; - } - latitudeFormat(_latitude) { - let format = ""; - if (_latitude < 0) { - format += "S"; - _latitude = Math.abs(_latitude); - } else { - format += "N"; - } - const degrees = Math.floor(_latitude); - const minutes = ((_latitude - degrees) * 60); - format += fastToFixed(degrees, 0); - format += "°"; - format += fastToFixed(minutes, 2); - format += "'"; - return format; - } - InteractionStateOut() { - switch (this.currentInteractionState) { - case 0: - break; - case 1: - for (let i = 0; i < this.currentSelectableArray.length; i++) { - this.currentSelectableArray[i].updateSelection(false); - } - break; - case 2: - this.contextualMenu.setAttribute("state", "Inactive"); - break; - } - } - InteractionStateIn() { - switch (this.currentInteractionState) { - case 0: - break; - case 1: - this.cursorIndex = 0; - break; - case 2: - this.contextualMenu.setAttribute("state", "Active"); - this.contextualMenuDisplayBeginIndex = 0; - this.cursorIndex = 0; - if (this.currentContextualMenu.elements[0].isInactive()) { - this.computeEvent("NavigationSmallInc"); - } - break; - } - } - SwitchToInteractionState(_newState) { - this.InteractionStateOut(); - this.currentInteractionState = _newState; - this.InteractionStateIn(); - } - ShowContextualMenu(_menu) { - this.currentContextualMenu = _menu; - this.SwitchToInteractionState(2); - this.currentContextualMenu.Update(this, this.menuMaxElems); - } - ActiveSelection(_selectables) { - this.SwitchToInteractionState(1); - this.currentSelectableArray = _selectables; - const begin = this.cursorIndex; - while (!this.currentSelectableArray[this.cursorIndex].isActive) { - this.cursorIndex = (this.cursorIndex + 1) % this.currentSelectableArray.length; - if (this.cursorIndex == begin) { - this.SwitchToInteractionState(0); - return; - } - } - } - setOverridePage(_page) { - if (this.overridePage) { - this.overridePage.onExit(); - } - if (this.currentContextualMenu) { - this.SwitchToInteractionState(0); - } - this.overridePage = _page; - this.overridePage.onEnter(); - } - closeOverridePage() { - if (this.overridePage) { - this.overridePage.onExit(); - } - if (this.currentContextualMenu) { - this.SwitchToInteractionState(0); - } - this.overridePage = null; - } - SwitchToPageName(_menu, _page) { - this.lastRelevantICAO = null; - this.lastRelevantICAOType = null; - if (this.overridePage) { - this.closeOverridePage(); - } - if (this.currentEventLinkedPageGroup) { - this.currentEventLinkedPageGroup.pageGroup.onExit(); - this.currentEventLinkedPageGroup = null; - } - this.pageGroups[this.currentPageGroupIndex].onExit(); - if (this.currentContextualMenu) { - this.SwitchToInteractionState(0); - } - for (let i = 0; i < this.pageGroups.length; i++) { - if (this.pageGroups[i].name == _menu) { - this.currentPageGroupIndex = i; - } - } - this.pageGroups[this.currentPageGroupIndex].goToPage(_page, true); - } - SwitchToMenuName(_name) { - this.lastRelevantICAO = null; - this.lastRelevantICAOType = null; - this.pageGroups[this.currentPageGroupIndex].onExit(); - if (this.currentContextualMenu) { - this.SwitchToInteractionState(0); - } - for (let i = 0; i < this.pageGroups.length; i++) { - if (this.pageGroups[i].name == _name) { - this.currentPageGroupIndex = i; - } - } - this.pageGroups[this.currentPageGroupIndex].onEnter(); - } - GetInteractionState() { - return this.currentInteractionState; - } - blinkGetState(_blinkPeriod, _duration) { - return Math.round((new Date().getTime()) / _duration) % (_blinkPeriod / _duration) == 0; - } - IsEditingSearchField() { - return this.GetInteractionState() == 3; - } - OnSearchFieldEndEditing() { - this.SwitchToInteractionState(0); - } - addEventLinkedPageGroup(_event, _pageGroup) { - this.eventLinkedPageGroups.push(new NavSystemEventLinkedPageGroup(_pageGroup, _event)); - } - addEventLinkedPopupWindow(_popUp) { - this.eventLinkedPopUpElements.push(_popUp); - _popUp.gps = this; - } - addIndependentElementContainer(_container) { - _container.setGPS(this); - this.IndependentsElements.push(_container); - } - getCurrentPageGroup() { - if (this.currentEventLinkedPageGroup) { - return this.currentEventLinkedPageGroup.pageGroup; - } else { - return this.pageGroups[this.currentPageGroupIndex]; - } - } - getCurrentPage() { - if (!this.overridePage) { - const currentGroup = this.getCurrentPageGroup(); - if (currentGroup) { - return currentGroup.getCurrentPage(); - } - return undefined; - } else { - return this.overridePage; - } - } - leaveEventPage() { - this.lastRelevantICAO = null; - this.lastRelevantICAOType = null; - this.currentEventLinkedPageGroup.pageGroup.onExit(); - this.currentEventLinkedPageGroup = null; - this.getCurrentPageGroup().onEnter(); - } - addEventAlias(_source, _output) { - this.eventAliases.push(new NavSystemEventAlias(_source, _output)); - } - closePopUpElement() { - let callback = null; - if (this.popUpElement) { - callback = this.popUpCloseCallback; - this.popUpElement.onExit(); - } - this.popUpElement = null; - this.popUpCloseCallback = null; - if (this.currentContextualMenu) { - this.SwitchToInteractionState(0); - } - if (callback) { - callback(); - } - ; - } - getElementOfType(c) { - for (let i = 0; i < this.IndependentsElements.length; i++) { - const elem = this.IndependentsElements[i].getElementOfType(c); - if (elem) { - return elem; - } - } - const curr = this.getCurrentPage().element.getElementOfType(c); - if (curr) { - return curr; - } else { - for (let i = 0; i < this.pageGroups.length; i++) { - for (let j = 0; j < this.pageGroups[i].pages.length; j++) { - const elem = this.pageGroups[i].pages[j].getElementOfType(c); - if (elem) { - return elem; - } - } - } - } - return null; - } - switchToPopUpPage(_pageContainer, _PopUpCloseCallback = null) { - if (this.popUpElement) { - this.popUpElement.onExit(); - if (this.popUpCloseCallback) { - this.popUpCloseCallback(); - } - ; - } - if (this.currentContextualMenu) { - this.SwitchToInteractionState(0); - } - this.popUpCloseCallback = _PopUpCloseCallback; - this.popUpElement = _pageContainer; - this.popUpElement.onEnter(); - } - preserveAspectRatio(_HtmlElementId) { - this.aspectRatioElement = _HtmlElementId; - this.forcedAspectRatioSet = false; - this.updateAspectRatio(); - } - updateAspectRatio() { - if (this.forcedAspectRatioSet) { - return; - } - if (this.aspectRatioElement == null) { - return; - } - const frame = this.getChildById(this.aspectRatioElement); - if (!frame) { - return; - } - const vpRect = this.getBoundingClientRect(); - const vpWidth = vpRect.width; - const vpHeight = vpRect.height; - if (vpWidth <= 0 || vpHeight <= 0) { - return; - } - const frameStyle = window.getComputedStyle(frame); - if (!frameStyle) { - return; - } - const refWidth = parseInt(frameStyle.getPropertyValue('--refWidth')); - const refHeight = parseInt(frameStyle.getPropertyValue('--refHeight')); - const curWidth = parseInt(frameStyle.width); - const curHeight = parseInt(frameStyle.height); - const curRatio = curHeight / curWidth; - let refRatio = curRatio; - if (refWidth > 0 && refHeight > 0) { - console.log("Forcing aspectratio to " + refWidth + "*" + refHeight); - refRatio = refHeight / refWidth; - let newLeft = parseInt(frameStyle.left); - let newWidth = curWidth; - let newHeight = curWidth * refRatio; - if (newHeight > vpHeight) { - newWidth = vpHeight / refRatio; - newHeight = vpHeight; - newLeft += (curWidth - newWidth) * 0.5; - } - newLeft = Math.round(newLeft); - newWidth = Math.round(newWidth); - newHeight = Math.round(newHeight); - frame.style.left = newLeft + "px"; - frame.style.width = newWidth + "px"; - frame.style.height = newHeight + "px"; - window.document.documentElement.style.setProperty("--bodyHeightScale", (newHeight / vpHeight).toString()); - } - this.forcedScreenRatio = 1.0 / curRatio; - this.forcedAspectRatio = 1.0 / refRatio; - this.forcedAspectRatioSet = true; - } - isAspectRatioForced() { - return this.forcedAspectRatioSet; - } - getForcedScreenRatio() { - return this.forcedScreenRatio; - } - getForcedAspectRatio() { - return this.forcedAspectRatio; - } - isComputingAspectRatio() { - if (this.aspectRatioElement != null && !this.forcedAspectRatioSet) { - return false; - } - return true; - } - onSoundEnd(_eventId) { - for (let i = 0; i < this.pageGroups.length; i++) { - for (let j = 0; j < this.pageGroups[i].pages.length; j++) { - this.pageGroups[i].pages[j].onSoundEnd(_eventId); - } - } - for (let i = 0; i < this.IndependentsElements.length; i++) { - this.IndependentsElements[i].onSoundEnd(_eventId); - } - for (let i = 0; i < this.eventLinkedPopUpElements.length; i++) { - this.eventLinkedPopUpElements[i].onSoundEnd(_eventId); - } - } - onShutDown() { - console.log("System Turned Off"); - this.hasBeenOff = true; - this.isStarted = false; - this.initAcknowledged = false; - for (let i = 0; i < this.pageGroups.length; i++) { - for (let j = 0; j < this.pageGroups[i].pages.length; j++) { - this.pageGroups[i].pages[j].onShutDown(); - } - } - for (let i = 0; i < this.IndependentsElements.length; i++) { - this.IndependentsElements[i].onShutDown(); - } - for (let i = 0; i < this.eventLinkedPopUpElements.length; i++) { - this.eventLinkedPopUpElements[i].onShutDown(); - } - this.alwaysUpdateList.splice(0, this.alwaysUpdateList.length); - } - onPowerOn() { - console.log("System Turned ON"); - this.startTime = Date.now(); - this.isStarted = true; - this.budgetedItemId = 0; - for (let i = 0; i < this.pageGroups.length; i++) { - for (let j = 0; j < this.pageGroups[i].pages.length; j++) { - this.pageGroups[i].pages[j].onPowerOn(); - } - } - for (let i = 0; i < this.IndependentsElements.length; i++) { - this.IndependentsElements[i].onPowerOn(); - } - for (let i = 0; i < this.eventLinkedPopUpElements.length; i++) { - this.eventLinkedPopUpElements[i].onPowerOn(); - } - } - isBootProcedureComplete() { - if (((Date.now() - this.startTime > this.initDuration) || !this.hasBeenOff) && (this.initAcknowledged || !this.needValidationAfterInit)) { - return true; - } - return false; - } - acknowledgeInit() { - this.initAcknowledged = true; - } - isInReversionaryMode() { - return this.reversionaryMode; - } - wasTurnedOff() { - return this.hasBeenOff; - } - hasWeatherRadar() { - if (this.instrumentXmlConfig) { - const elem = this.instrumentXmlConfig.getElementsByTagName("WeatherRadar"); - if (elem.length > 0 && (elem[0].textContent.toLowerCase() == "off" || elem[0].textContent.toLowerCase() == "none")) { - return false; - } - } - return true; - } - alwaysUpdate(_element, _val) { - for (let i = 0; i < this.alwaysUpdateList.length; i++) { - if (this.alwaysUpdateList[i] == _element) { - if (!_val) { - this.alwaysUpdateList.splice(i, 1); - } - return; - } - } - if (_val) { - this.alwaysUpdateList.push(_element); - } - } -} -NavSystem.maxTimeUpdateAllTime = 0; -NavSystem.maxTimeUpdate = 0; -NavSystem.mediumMaxTimeUpdate = 0; -NavSystem.mediumTimeUpdate = 0; -NavSystem.maxTimeMapUpdateAllTime = 0; -NavSystem.maxTimeMapUpdate = 0; -NavSystem.mediumMaxTimeMapUpdate = 0; -NavSystem.mediumTimeMapUpdate = 0; -NavSystem._iterations = 0; -class NavSystemPageGroup { - constructor(_name, _gps, _pages) { - this._updatingWithBudget = false; - this.name = _name; - this.gps = _gps; - this.pages = _pages; - this.pageIndex = 0; - for (let i = 0; i < _pages.length; i++) { - _pages[i].pageGroup = this; - _pages[i].gps = this.gps; - } - } - getCurrentPage() { - return this.pages[this.pageIndex]; - } - onEnter() { - this.pages[this.pageIndex].onEnter(); - } - onUpdate(_deltaTime) { - if (!this._updatingWithBudget) { - this.pages[this.pageIndex].onUpdate(_deltaTime); - } - } - onUpdateSpecificItem(_deltaTime, _itemId) { - if (_itemId == 0) { - this._updatingWithBudget = true; - { - this.onUpdate(_deltaTime); - } - this._updatingWithBudget = false; - } - return this.pages[this.pageIndex].onUpdateSpecificItem(_deltaTime, _itemId); - } - onExit() { - this.pages[this.pageIndex].onExit(); - } - nextPage() { - if (this.pages.length > 1) { - this.pages[this.pageIndex].onExit(); - this.pageIndex = (this.pageIndex + 1) % this.pages.length; - this.pages[this.pageIndex].onEnter(); - } - } - prevPage() { - if (this.pages.length > 1) { - this.pages[this.pageIndex].onExit(); - this.pageIndex = (this.pageIndex + this.pages.length - 1) % this.pages.length; - this.pages[this.pageIndex].onEnter(); - } - } - goToPage(_name, _skipExit = false) { - if (!_skipExit) { - this.pages[this.pageIndex].onExit(); - } - for (let i = 0; i < this.pages.length; i++) { - if (this.pages[i].name == _name) { - this.pageIndex = i; - } - } - this.onEnter(); - } -} -class NavSystemEventLinkedPageGroup { - constructor(_pageGroup, _event) { - this.pageGroup = _pageGroup; - this.popUpEvent = _event; - } -} -class NavSystemElementContainer { - constructor(_name, _htmlElemId, _element) { - this.name = ""; - this.htmlElemId = ""; - this.isInitialized = false; - this._updatingWithBudget = false; - this.name = _name; - this.htmlElemId = _htmlElemId; - this.element = _element; - if (_element) { - _element.container = this; - } - } - getDefaultMenu() { - return this.defaultMenu; - } - init() { - } - checkInit() { - if (this.element) { - if (this.element.isReady()) { - if (!this.element.isInitialized) { - this.element.container = this; - this.element.setGPS(this.gps); - this.element.init(this.gps.getChildById(this.htmlElemId)); - this.element.isInitialized = true; - } - } else { - return false; - } - } - if (!this.isInitialized) { - this.init(); - this.isInitialized = true; - } - return this.isInitialized; - } - onEnter() { - if (!this.checkInit()) { - return; - } - if (this.element) { - this.element.onEnter(); - } - } - onUpdate(_deltaTime) { - if (!this._updatingWithBudget) { - if (!this.checkInit()) { - return; - } - if (this.element) { - this.element.onUpdate(_deltaTime); - } - } - } - onUpdateSpecificItem(_deltaTime, _itemId) { - if (!this.checkInit()) { - return; - } - if (_itemId == 0) { - this._updatingWithBudget = true; - { - this.onUpdate(_deltaTime); - } - this._updatingWithBudget = false; - } - if (this.element) { - return this.element.onUpdateSpecificItem(_deltaTime, _itemId); - } - return false; - } - onExit() { - if (this.element) { - this.element.onExit(); - } - } - onEvent(_event) { - if (this.element) { - this.element.onEvent(_event); - } - } - onSoundEnd(_eventId) { - if (this.element) { - this.element.onSoundEnd(_eventId); - } - } - onShutDown() { - if (this.element) { - this.element.onShutDown(); - } - } - onPowerOn() { - if (this.element) { - this.element.onPowerOn(); - } - } - setGPS(_gps) { - this.gps = _gps; - if (this.element) { - this.element.setGPS(_gps); - } - } - getElementOfType(c) { - if (this.element) { - return this.element.getElementOfType(c); - } - return null; - } -} -class NavSystemEventLinkedPopUpWindow extends NavSystemElementContainer { - constructor(_name, _htmlElemId, _element, _popUpEvent) { - super(_name, _htmlElemId, _element); - this.popUpEvent = _popUpEvent; - } -} -class SoftKeyHandler { -} -class NavSystemPage extends NavSystemElementContainer { - constructor() { - super(...arguments); - this.softKeys = new SoftKeysMenu(); - } - getSoftKeyMenu() { - return this.softKeys; - } -} -// class Updatable { -// } -class NavSystemElement extends Updatable { - constructor() { - super(...arguments); - this.isInitialized = false; - this.defaultSelectables = []; - this._alwaysUpdate = false; - } - set alwaysUpdate(_val) { - this._alwaysUpdate = _val; - if (this.gps) { - this.gps.alwaysUpdate(this, _val); - } - } - isReady() { - return true; - } - onSoundEnd(_eventId) { - } - onShutDown() { - } - onPowerOn() { - } - onUpdateSpecificItem(_deltaTime, _itemId) { - if (_itemId == 0) { - this.onUpdate(_deltaTime); - } - return false; - } - getDefaultSelectables() { - return this.defaultSelectables; - } - setGPS(_gps) { - if (this.gps && !_gps && this._alwaysUpdate) { - this.gps.alwaysUpdate(this, false); - } - this.gps = _gps; - if (this.gps) { - this.gps.alwaysUpdate(this, this._alwaysUpdate); - } - } - getElementOfType(c) { - if (this instanceof c) { - return this; - } else { - return null; - } - } - redraw() { - } -} -class NavSystemIFrameElement extends NavSystemElement { - constructor(_frameName) { - super(); - this.iFrame = this.gps.getChildById(_frameName); - } - isReady() { - if (this.iFrame) { - this.canvas = this.iFrame.contentWindow; - if (this.canvas) { - const readyToSet = this.canvas["readyToSet"]; - if (readyToSet) { - return true; - } - } - } - return false; - } -} -class NavSystemElementGroup extends NavSystemElement { - constructor(_elements) { - super(); - this._updatingWithBudget = false; - this.elements = _elements; - } - isReady() { - for (let i = 0; i < this.elements.length; i++) { - if (!this.elements[i].isReady()) { - return false; - } - } - return true; - } - init(_root) { - this.defaultSelectables = []; - for (let i = 0; i < this.elements.length; i++) { - if (!this.elements[i].isInitialized) { - this.elements[i].container = this.container; - this.elements[i].setGPS(this.gps); - this.elements[i].init(_root); - this.elements[i].isInitialized = true; - this.defaultSelectables.concat(this.elements[i].getDefaultSelectables()); - } - } - } - onEnter() { - for (let i = 0; i < this.elements.length; i++) { - this.elements[i].onEnter(); - } - } - onUpdate(_deltaTime) { - if (!this._updatingWithBudget) { - for (let i = 0; i < this.elements.length; i++) { - this.elements[i].onUpdate(_deltaTime); - } - } - } - onUpdateSpecificItem(_deltaTime, _itemId) { - if (_itemId == 0) { - this._updatingWithBudget = true; - { - this.onUpdate(_deltaTime); - } - this._updatingWithBudget = false; - } - if (_itemId < this.elements.length) { - this.elements[_itemId].onUpdate(_deltaTime); - if (_itemId + 1 < this.elements.length) { - return true; - } - } - return false; - } - onExit() { - for (let i = 0; i < this.elements.length; i++) { - this.elements[i].onExit(); - } - } - onEvent(_event) { - for (let i = 0; i < this.elements.length; i++) { - this.elements[i].onEvent(_event); - } - } - onSoundEnd(_eventId) { - for (let i = 0; i < this.elements.length; i++) { - this.elements[i].onSoundEnd(_eventId); - } - } - onShutDown() { - for (let i = 0; i < this.elements.length; i++) { - this.elements[i].onShutDown(); - } - } - onPowerOn() { - for (let i = 0; i < this.elements.length; i++) { - this.elements[i].onPowerOn(); - } - } - getDefaultSelectables() { - this.defaultSelectables = []; - for (let i = 0; i < this.elements.length; i++) { - this.defaultSelectables.concat(this.elements[i].getDefaultSelectables()); - } - return this.defaultSelectables; - } - setGPS(_gps) { - this.gps = _gps; - for (let i = 0; i < this.elements.length; i++) { - this.elements[i].setGPS(_gps); - } - } - getElementOfType(c) { - for (let i = 0; i < this.elements.length; i++) { - const elem = this.elements[i].getElementOfType(c); - if (elem) { - return elem; - } - } - return null; - } - addElement(elem) { - this.elements.push(elem); - } -} -class NavSystemElementSelector extends NavSystemElement { - constructor() { - super(...arguments); - this.elements = []; - this.index = 0; - } - isReady() { - for (let i = 0; i < this.elements.length; i++) { - if (!this.elements[i].isReady()) { - return false; - } - } - return true; - } - init(_root) { - for (let i = 0; i < this.elements.length; i++) { - if (!this.elements[i].isInitialized) { - this.elements[i].container = this.container; - this.elements[i].setGPS(this.gps); - this.elements[i].init(_root); - this.elements[i].isInitialized = true; - } - } - } - onEnter() { - this.elements[this.index].onEnter(); - } - onUpdate(_deltaTime) { - this.elements[this.index].onUpdate(_deltaTime); - } - onExit() { - this.elements[this.index].onExit(); - } - onEvent(_event) { - this.elements[this.index].onEvent(_event); - } - onSoundEnd(_eventId) { - for (let i = 0; i < this.elements.length; i++) { - this.elements[i].onSoundEnd(_eventId); - } - } - onShutDown() { - for (let i = 0; i < this.elements.length; i++) { - this.elements[i].onShutDown(); - } - } - onPowerOn() { - for (let i = 0; i < this.elements.length; i++) { - this.elements[i].onPowerOn(); - } - } - selectElement(_name) { - for (let i = 0; i < this.elements.length; i++) { - if (this.elements[i].name == _name) { - this.index = i; - } - } - } - addElement(_elem) { - this.elements.push(_elem); - } - getDefaultSelectables() { - return this.elements[this.index].getDefaultSelectables(); - } - setGPS(_gps) { - this.gps = _gps; - for (let i = 0; i < this.elements.length; i++) { - this.elements[i].setGPS(_gps); - } - } - getElementOfType(c) { - for (let i = 0; i < this.elements.length; i++) { - const elem = this.elements[i].getElementOfType(c); - if (elem) { - return elem; - } - } - return null; - } - switchToIndex(_index) { - if (this.index < this.elements.length) { - this.elements[this.index].onExit(); - } - if (_index < this.elements.length) { - this.index = _index; - this.elements[this.index].onEnter(); - } - } -} -class SoftKeyElement { - constructor(_name = "", _callback = null, _stateCB = null) { - this.callback = _callback; - this.name = _name; - this.state = "None"; - this.stateCallback = _stateCB; - if (!_callback) { - this.state = "Greyed"; - } - } -} -class SoftKeysMenu { -} -class NavSystemEventAlias { - constructor(_from, _to) { - this.source = _from; - this.output = _to; - } -} -class CDIElement extends NavSystemElement { - init(_root) { - this.cdiCursor = this.gps.getChildById("CDICursor"); - this.toFrom = this.gps.getChildById("ToFrom"); - } - onEnter() { - } - onUpdate(_deltaTime) { - const CTD = SimVar.GetSimVarValue("GPS WP CROSS TRK", "nautical mile"); - this.cdiCursor.setAttribute("style", "left:" + ((CTD <= -1 ? -1 : CTD >= 1 ? 1 : CTD) * 50 + 50) + "%"); - } - onExit() { - } - onEvent(_event) { - } -} -var EMapDisplayMode; -(function (EMapDisplayMode) { - EMapDisplayMode[EMapDisplayMode["GPS"] = 0] = "GPS"; - EMapDisplayMode[EMapDisplayMode["RADAR"] = 1] = "RADAR"; -})(EMapDisplayMode || (EMapDisplayMode = {})); -var ERadarMode; -(function (ERadarMode) { - ERadarMode[ERadarMode["HORIZON"] = 0] = "HORIZON"; - ERadarMode[ERadarMode["VERTICAL"] = 1] = "VERTICAL"; -})(ERadarMode || (ERadarMode = {})); -class MapInstrumentElement extends NavSystemElement { - constructor() { - super(); - this.displayMode = EMapDisplayMode.GPS; - this.nexradOn = false; - this.radarMode = ERadarMode.HORIZON; - } - init(root) { - this.instrument = root.querySelector("map-instrument"); - if (this.instrument) { - TemplateElement.callNoBinding(this.instrument, () => { - this.onTemplateLoaded(); - }); - } - } - onTemplateLoaded() { - this.instrument.init(this.gps); - this.instrumentLoaded = true; - // This makes the black parts of the bing map blend perfectly with the backlight in weather and terrain modes - this.instrument.bingMap.m_imgElement.style.mixBlendMode = 'lighten'; - } - setGPS(_gps) { - super.setGPS(_gps); - if (this.instrument) { - this.instrument.init(this.gps); - } - } - onEnter() { - } - onUpdate(_deltaTime) { - if (this.instrumentLoaded) { - this.instrument.update(_deltaTime); - if (this.weatherTexts) { - const range = this.instrument.getWeatherRange(); - const ratio = 1.0 / this.weatherTexts.length; - for (let i = 0; i < this.weatherTexts.length; i++) { - this.weatherTexts[i].textContent = fastToFixed(range * ratio * (i + 1), 2) + "NM"; - } - } - } - } - onExit() { - } - onEvent(_event) { - if (this.instrument) { - this.instrument.onEvent(_event); - } - } - toggleDisplayMode() { - if (this.displayMode == EMapDisplayMode.GPS) { - this.gps.getCurrentPage().name = "WEATHER RADAR"; - this.displayMode = EMapDisplayMode.RADAR; - } else { - this.gps.getCurrentPage().name = "NAVIGATION MAP"; - this.displayMode = EMapDisplayMode.GPS; - } - this.updateWeather(); - } - setDisplayMode(_mode) { - this.displayMode = _mode; - if (_mode == EMapDisplayMode.GPS) { - this.gps.getCurrentPage().name = "NAVIGATION MAP"; - } else { - this.gps.getCurrentPage().name = "WEATHER RADAR"; - } - this.updateWeather(); - } - getDisplayMode() { - return this.displayMode; - } - toggleIsolines() { - if (this.instrument) { - if (this.instrument.getIsolines() == true) { - this.instrument.showIsolines(false); - } else { - this.instrument.showIsolines(true); - } - } - } - getIsolines() { - return this.instrument.getIsolines(); - } - toggleNexrad() { - this.nexradOn = !this.nexradOn; - this.updateWeather(); - } - getNexrad() { - return this.nexradOn; - } - setRadar(_mode) { - this.radarMode = _mode; - this.updateWeather(); - } - getRadarMode() { - return this.radarMode; - } - updateWeather() { - if (this.instrument) { - if (this.displayMode == EMapDisplayMode.GPS) { - if (this.nexradOn) { - this.setWeather(EWeatherRadar.TOPVIEW); - } else { - this.setWeather(EWeatherRadar.OFF); - } - } else { - if (this.radarMode == ERadarMode.HORIZON) { - this.setWeather(EWeatherRadar.HORIZONTAL); - } else { - this.setWeather(EWeatherRadar.VERTICAL); - } - } - } - } - setWeather(_mode) { - this.instrument.showWeather(_mode); - const svgRoot = this.instrument.weatherSVG; - if (svgRoot) { - Utils.RemoveAllChildren(svgRoot); - this.weatherTexts = null; - if (_mode == EWeatherRadar.HORIZONTAL || _mode == EWeatherRadar.VERTICAL) { - const circleRadius = 575; - const dashNbRect = 10; - const dashWidth = 8; - const dashHeight = 6; - if (_mode == EWeatherRadar.HORIZONTAL) { - this.instrument.setBingMapStyle("10.3%", "-13.3%", "127%", "157%"); - var coneAngle = 90; - svgRoot.setAttribute("viewBox", "0 0 400 400"); - var trsGroup = document.createElementNS(Avionics.SVG.NS, "g"); - trsGroup.setAttribute("transform", "translate(-125, 29) scale(1.63)"); - svgRoot.appendChild(trsGroup); - const viewBox = document.createElementNS(Avionics.SVG.NS, "svg"); - viewBox.setAttribute("viewBox", "-600 -600 1200 1200"); - trsGroup.appendChild(viewBox); - var circleGroup = document.createElementNS(Avionics.SVG.NS, "g"); - circleGroup.setAttribute("id", "Circles"); - viewBox.appendChild(circleGroup); - { - const rads = [0.25, 0.50, 0.75, 1.0]; - for (let r = 0; r < rads.length; r++) { - const rad = circleRadius * rads[r]; - let startDegrees = -coneAngle * 0.5; - const endDegrees = coneAngle * 0.5; - while (Math.floor(startDegrees) <= endDegrees) { - const line = document.createElementNS(Avionics.SVG.NS, "rect"); - const degree = (180 + startDegrees + 0.5); - line.setAttribute("x", "0"); - line.setAttribute("y", rad.toString()); - line.setAttribute("width", dashWidth.toString()); - line.setAttribute("height", dashHeight.toString()); - line.setAttribute("transform", "rotate(" + degree + " 0 0)"); - line.setAttribute("fill", "white"); - circleGroup.appendChild(line); - startDegrees += coneAngle / dashNbRect; - } - } - } - var lineGroup = document.createElementNS(Avionics.SVG.NS, "g"); - lineGroup.setAttribute("id", "Lines"); - viewBox.appendChild(lineGroup); - { - var coneStart = 180 - coneAngle * 0.5; - var coneStartLine = document.createElementNS(Avionics.SVG.NS, "line"); - coneStartLine.setAttribute("x1", "0"); - coneStartLine.setAttribute("y1", "0"); - coneStartLine.setAttribute("x2", "0"); - coneStartLine.setAttribute("y2", circleRadius.toString()); - coneStartLine.setAttribute("transform", "rotate(" + coneStart + " 0 0)"); - coneStartLine.setAttribute("stroke", "white"); - coneStartLine.setAttribute("stroke-width", "3"); - lineGroup.appendChild(coneStartLine); - var coneEnd = 180 + coneAngle * 0.5; - var coneEndLine = document.createElementNS(Avionics.SVG.NS, "line"); - coneEndLine.setAttribute("x1", "0"); - coneEndLine.setAttribute("y1", "0"); - coneEndLine.setAttribute("x2", "0"); - coneEndLine.setAttribute("y2", circleRadius.toString()); - coneEndLine.setAttribute("transform", "rotate(" + coneEnd + " 0 0)"); - coneEndLine.setAttribute("stroke", "white"); - coneEndLine.setAttribute("stroke-width", "3"); - lineGroup.appendChild(coneEndLine); - } - var textGroup = document.createElementNS(Avionics.SVG.NS, "g"); - textGroup.setAttribute("id", "Texts"); - viewBox.appendChild(textGroup); - { - this.weatherTexts = []; - var text = document.createElementNS(Avionics.SVG.NS, "text"); - text.setAttribute("x", "100"); - text.setAttribute("y", "-85"); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", "20"); - textGroup.appendChild(text); - this.weatherTexts.push(text); - var text = document.createElementNS(Avionics.SVG.NS, "text"); - text.setAttribute("x", "200"); - text.setAttribute("y", "-185"); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", "20"); - textGroup.appendChild(text); - this.weatherTexts.push(text); - var text = document.createElementNS(Avionics.SVG.NS, "text"); - text.setAttribute("x", "300"); - text.setAttribute("y", "-285"); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", "20"); - textGroup.appendChild(text); - this.weatherTexts.push(text); - var text = document.createElementNS(Avionics.SVG.NS, "text"); - text.setAttribute("x", "400"); - text.setAttribute("y", "-385"); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", "20"); - textGroup.appendChild(text); - this.weatherTexts.push(text); - } - } else if (_mode == EWeatherRadar.VERTICAL) { - this.instrument.setBingMapStyle("-75%", "-88%", "201%", "250%"); - var coneAngle = 51.43; - svgRoot.setAttribute("viewBox", "0 0 400 400"); - var trsGroup = document.createElementNS(Avionics.SVG.NS, "g"); - trsGroup.setAttribute("transform", "translate(402, -190) scale(1.95) rotate(90)"); - svgRoot.appendChild(trsGroup); - const viewBox = document.createElementNS(Avionics.SVG.NS, "svg"); - viewBox.setAttribute("viewBox", "-600 -600 1200 1200"); - trsGroup.appendChild(viewBox); - var circleGroup = document.createElementNS(Avionics.SVG.NS, "g"); - circleGroup.setAttribute("id", "Circles"); - viewBox.appendChild(circleGroup); - { - const rads = [0.25, 0.50, 0.75, 1.0]; - for (let r = 0; r < rads.length; r++) { - const rad = circleRadius * rads[r]; - let startDegrees = -coneAngle * 0.5; - const endDegrees = coneAngle * 0.5; - while (Math.floor(startDegrees) <= endDegrees) { - const line = document.createElementNS(Avionics.SVG.NS, "rect"); - const degree = (180 + startDegrees + 0.5); - line.setAttribute("x", "0"); - line.setAttribute("y", rad.toString()); - line.setAttribute("width", dashWidth.toString()); - line.setAttribute("height", dashHeight.toString()); - line.setAttribute("transform", "rotate(" + degree + " 0 0)"); - line.setAttribute("fill", "white"); - circleGroup.appendChild(line); - startDegrees += coneAngle / dashNbRect; - } - } - } - const limitGroup = document.createElementNS(Avionics.SVG.NS, "g"); - limitGroup.setAttribute("id", "Limits"); - viewBox.appendChild(limitGroup); - { - const endPosY = circleRadius + 50; - let posX = -130; - let posY = 50; - while (posY <= endPosY) { - const line = document.createElementNS(Avionics.SVG.NS, "rect"); - line.setAttribute("x", posX.toString()); - line.setAttribute("y", (-posY).toString()); - line.setAttribute("width", dashHeight.toString()); - line.setAttribute("height", dashWidth.toString()); - line.setAttribute("fill", "white"); - limitGroup.appendChild(line); - posY += dashWidth * 2; - } - posX = 130; - posY = 50; - while (posY <= endPosY) { - const line = document.createElementNS(Avionics.SVG.NS, "rect"); - line.setAttribute("x", posX.toString()); - line.setAttribute("y", (-posY).toString()); - line.setAttribute("width", dashHeight.toString()); - line.setAttribute("height", dashWidth.toString()); - line.setAttribute("fill", "white"); - limitGroup.appendChild(line); - posY += dashWidth * 2; - } - } - var lineGroup = document.createElementNS(Avionics.SVG.NS, "g"); - lineGroup.setAttribute("id", "Lines"); - viewBox.appendChild(lineGroup); - { - var coneStart = 180 - coneAngle * 0.5; - var coneStartLine = document.createElementNS(Avionics.SVG.NS, "line"); - coneStartLine.setAttribute("x1", "0"); - coneStartLine.setAttribute("y1", "0"); - coneStartLine.setAttribute("x2", "0"); - coneStartLine.setAttribute("y2", circleRadius.toString()); - coneStartLine.setAttribute("transform", "rotate(" + coneStart + " 0 0)"); - coneStartLine.setAttribute("stroke", "white"); - coneStartLine.setAttribute("stroke-width", "3"); - lineGroup.appendChild(coneStartLine); - var coneEnd = 180 + coneAngle * 0.5; - var coneEndLine = document.createElementNS(Avionics.SVG.NS, "line"); - coneEndLine.setAttribute("x1", "0"); - coneEndLine.setAttribute("y1", "0"); - coneEndLine.setAttribute("x2", "0"); - coneEndLine.setAttribute("y2", circleRadius.toString()); - coneEndLine.setAttribute("transform", "rotate(" + coneEnd + " 0 0)"); - coneEndLine.setAttribute("stroke", "white"); - coneEndLine.setAttribute("stroke-width", "3"); - lineGroup.appendChild(coneEndLine); - } - var textGroup = document.createElementNS(Avionics.SVG.NS, "g"); - textGroup.setAttribute("id", "Texts"); - viewBox.appendChild(textGroup); - { - var text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = "+60000FT"; - text.setAttribute("x", "50"); - text.setAttribute("y", "-150"); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", "20"); - text.setAttribute("transform", "rotate(-90)"); - textGroup.appendChild(text); - var text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = "-60000FT"; - text.setAttribute("x", "50"); - text.setAttribute("y", "160"); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", "20"); - text.setAttribute("transform", "rotate(-90)"); - textGroup.appendChild(text); - this.weatherTexts = []; - var text = document.createElementNS(Avionics.SVG.NS, "text"); - text.setAttribute("x", "85"); - text.setAttribute("y", "85"); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", "18"); - text.setAttribute("transform", "rotate(-90)"); - textGroup.appendChild(text); - this.weatherTexts.push(text); - var text = document.createElementNS(Avionics.SVG.NS, "text"); - text.setAttribute("x", "215"); - text.setAttribute("y", "160"); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", "18"); - text.setAttribute("transform", "rotate(-90)"); - textGroup.appendChild(text); - this.weatherTexts.push(text); - var text = document.createElementNS(Avionics.SVG.NS, "text"); - text.setAttribute("x", "345"); - text.setAttribute("y", "220"); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", "18"); - text.setAttribute("transform", "rotate(-90)"); - textGroup.appendChild(text); - this.weatherTexts.push(text); - var text = document.createElementNS(Avionics.SVG.NS, "text"); - text.setAttribute("x", "475"); - text.setAttribute("y", "280"); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", "18"); - text.setAttribute("transform", "rotate(-90)"); - textGroup.appendChild(text); - this.weatherTexts.push(text); - } - } - const legendGroup = document.createElementNS(Avionics.SVG.NS, "g"); - legendGroup.setAttribute("id", "legendGroup"); - svgRoot.appendChild(legendGroup); - { - const x = -5; - const y = 325; - const w = 70; - const h = 125; - const titleHeight = 20; - const scaleOffsetX = 5; - const scaleOffsetY = 5; - const scaleWidth = 13; - const scaleHeight = 24; - const left = x - w * 0.5; - const top = y - h * 0.5; - let rect = document.createElementNS(Avionics.SVG.NS, "rect"); - rect.setAttribute("x", left.toString()); - rect.setAttribute("y", top.toString()); - rect.setAttribute("width", w.toString()); - rect.setAttribute("height", h.toString()); - rect.setAttribute("stroke", "white"); - rect.setAttribute("stroke-width", "2"); - rect.setAttribute("stroke-opacity", "1"); - legendGroup.appendChild(rect); - rect = document.createElementNS(Avionics.SVG.NS, "rect"); - rect.setAttribute("x", left.toString()); - rect.setAttribute("y", top.toString()); - rect.setAttribute("width", w.toString()); - rect.setAttribute("height", titleHeight.toString()); - rect.setAttribute("stroke", "white"); - rect.setAttribute("stroke-width", "2"); - rect.setAttribute("stroke-opacity", "1"); - legendGroup.appendChild(rect); - var text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = "SCALE"; - text.setAttribute("x", x.toString()); - text.setAttribute("y", (top + titleHeight * 0.5).toString()); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", "11"); - text.setAttribute("text-anchor", "middle"); - legendGroup.appendChild(text); - let scaleIndex = 0; - rect = document.createElementNS(Avionics.SVG.NS, "rect"); - rect.setAttribute("x", (left + scaleOffsetX).toString()); - rect.setAttribute("y", (top + titleHeight + scaleOffsetY + scaleIndex * scaleHeight).toString()); - rect.setAttribute("width", scaleWidth.toString()); - rect.setAttribute("height", scaleHeight.toString()); - rect.setAttribute("fill", "red"); - rect.setAttribute("stroke", "white"); - rect.setAttribute("stroke-width", "2"); - rect.setAttribute("stroke-opacity", "1"); - legendGroup.appendChild(rect); - text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = "HEAVY"; - text.setAttribute("x", (left + scaleOffsetX + scaleWidth + 5).toString()); - text.setAttribute("y", (top + titleHeight + scaleOffsetY + scaleIndex * scaleHeight + scaleHeight * 0.5).toString()); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", "11"); - legendGroup.appendChild(text); - scaleIndex++; - rect = document.createElementNS(Avionics.SVG.NS, "rect"); - rect.setAttribute("x", (left + scaleOffsetX).toString()); - rect.setAttribute("y", (top + titleHeight + scaleOffsetY + scaleIndex * scaleHeight).toString()); - rect.setAttribute("width", scaleWidth.toString()); - rect.setAttribute("height", scaleHeight.toString()); - rect.setAttribute("fill", "yellow"); - rect.setAttribute("stroke", "white"); - rect.setAttribute("stroke-width", "2"); - rect.setAttribute("stroke-opacity", "1"); - legendGroup.appendChild(rect); - scaleIndex++; - rect = document.createElementNS(Avionics.SVG.NS, "rect"); - rect.setAttribute("x", (left + scaleOffsetX).toString()); - rect.setAttribute("y", (top + titleHeight + scaleOffsetY + scaleIndex * scaleHeight).toString()); - rect.setAttribute("width", scaleWidth.toString()); - rect.setAttribute("height", scaleHeight.toString()); - rect.setAttribute("fill", "green"); - rect.setAttribute("stroke", "white"); - rect.setAttribute("stroke-width", "2"); - rect.setAttribute("stroke-opacity", "1"); - legendGroup.appendChild(rect); - text = document.createElementNS(Avionics.SVG.NS, "text"); - text.textContent = "LIGHT"; - text.setAttribute("x", (left + scaleOffsetX + scaleWidth + 5).toString()); - text.setAttribute("y", (top + titleHeight + scaleOffsetY + scaleIndex * scaleHeight + scaleHeight * 0.5).toString()); - text.setAttribute("fill", "white"); - text.setAttribute("font-size", "11"); - legendGroup.appendChild(text); - scaleIndex++; - rect = document.createElementNS(Avionics.SVG.NS, "rect"); - rect.setAttribute("x", (left + scaleOffsetX).toString()); - rect.setAttribute("y", (top + titleHeight + scaleOffsetY + scaleIndex * scaleHeight).toString()); - rect.setAttribute("width", scaleWidth.toString()); - rect.setAttribute("height", scaleHeight.toString()); - rect.setAttribute("fill", "black"); - rect.setAttribute("stroke", "white"); - rect.setAttribute("stroke-width", "2"); - rect.setAttribute("stroke-opacity", "1"); - legendGroup.appendChild(rect); - } - } - } - } -} -class SoftKeyHtmlElement { - constructor(_elem) { - this.Element = _elem; - this.Value = ""; - } - fillFromElement(_elem) { - const val = _elem.name; - if (this.Value != val) { - this.Element.innerHTML = val; - this.Value = val; - } - if (_elem.stateCallback) { - _elem.state = _elem.stateCallback(); - } else if (!_elem.callback) { - _elem.state = "Greyed"; - } - if (_elem.state) { - Avionics.Utils.diffAndSetAttribute(this.Element, "state", _elem.state); - } - } -} -class SoftKeys extends NavSystemElement { - constructor(_softKeyHTMLClass = SoftKeyHtmlElement) { - super(); - this.softKeys = []; - this.softKeyHTMLClass = _softKeyHTMLClass; - } - init(root) { - for (let i = 1; i <= 12; i++) { - const name = "Key" + i.toString(); - const child = this.gps.getChildById(name); - if (child) { - const e = new this.softKeyHTMLClass(child); - this.softKeys.push(e); - } - } - this.isInitialized = true; - } - onEnter() { - } - onUpdate(_deltaTime) { - const currentPage = this.gps.getCurrentPage(); - if (currentPage) { - this.currentMenu = currentPage.getSoftKeyMenu(); - if (this.currentMenu && this.currentMenu.elements && this.currentMenu.elements.length > 0) { - for (let i = 0; i < this.currentMenu.elements.length; i++) { - this.softKeys[i].fillFromElement(this.currentMenu.elements[i]); - } - } - } - } - onExit() { - } - onEvent(_event) { - switch (_event) { - case "SOFTKEYS_1": - this.activeSoftKey(0); - break; - case "SOFTKEYS_2": - this.activeSoftKey(1); - break; - case "SOFTKEYS_3": - this.activeSoftKey(2); - break; - case "SOFTKEYS_4": - this.activeSoftKey(3); - break; - case "SOFTKEYS_5": - this.activeSoftKey(4); - break; - case "SOFTKEYS_6": - this.activeSoftKey(5); - break; - case "SOFTKEYS_7": - this.activeSoftKey(6); - break; - case "SOFTKEYS_8": - this.activeSoftKey(7); - break; - case "SOFTKEYS_9": - this.activeSoftKey(8); - break; - case "SOFTKEYS_10": - this.activeSoftKey(9); - break; - case "SOFTKEYS_11": - this.activeSoftKey(10); - break; - case "SOFTKEYS_12": - this.activeSoftKey(11); - break; - } - } - activeSoftKey(_number) { - if (this.currentMenu.elements[_number].callback) { - this.currentMenu.elements[_number].callback(); - } - } -} -var Annunciation_MessageType; -(function (Annunciation_MessageType) { - Annunciation_MessageType[Annunciation_MessageType["WARNING"] = 0] = "WARNING"; - Annunciation_MessageType[Annunciation_MessageType["CAUTION"] = 1] = "CAUTION"; - Annunciation_MessageType[Annunciation_MessageType["ADVISORY"] = 2] = "ADVISORY"; - Annunciation_MessageType[Annunciation_MessageType["SAFEOP"] = 3] = "SAFEOP"; -})(Annunciation_MessageType || (Annunciation_MessageType = {})); -; -class Annunciation_Message { - constructor() { - this.Visible = false; - this.Acknowledged = false; - } -} -; -class XMLCondition { - constructor() { - this.suffix = ""; - } -} -class Annunciation_Message_XML extends Annunciation_Message { - constructor() { - super(); - this.lastConditionIndex = -1; - this.conditions = []; - this.Handler = this.getHandlersValue.bind(this); - } - getHandlersValue() { - for (let i = 0; i < this.conditions.length; i++) { - if (this.conditions[i].logic.getValue() != 0) { - if (i != this.lastConditionIndex) { - this.lastConditionIndex = i; - this.Text = this.baseText + (this.conditions[i].suffix ? this.conditions[i].suffix : ""); - return false; - } - return true; - } - } - return false; - } -} -class Annunciation_Message_Timed extends Annunciation_Message { -} -; -class Annunciation_Message_Switch extends Annunciation_Message { - get Text() { - const index = this.Handler(); - if (index > 0) { - return this.Texts[index - 1]; - } else { - return ""; - } - } -} -class Condition { - constructor(_handler, _time = 0) { - this.beginTime = 0; - this.Handler = _handler; - this.Time = _time; - } -} -class Annunciator_Message_MultipleConditions extends Annunciation_Message { - constructor() { - super(); - this.Handler = this.getHandlersValue; - } - getHandlersValue() { - let result = false; - for (let i = 0; i < this.conditions.length; i++) { - if (this.conditions[i].Handler()) { - if (this.conditions[i].beginTime == 0) { - this.conditions[i].beginTime = Date.now() + 1000 * this.conditions[i].Time; - } else if (this.conditions[i].beginTime <= Date.now()) { - result = true; - } - } else { - this.conditions[i].beginTime = 0; - } - } - return result; - } -} -class Annunciations extends NavSystemElement { - constructor() { - super(...arguments); - this.allMessages = []; - this.alertLevel = 0; - this.alert = false; - this.needReload = true; - this.rootElementName = "Annunciations"; - } - init(root) { - this.engineType = Simplane.getEngineType(); - if (this.rootElementName != "") { - this.annunciations = this.gps.getChildById(this.rootElementName); - } - if (this.gps.xmlConfig) { - const annunciationsRoot = this.gps.xmlConfig.getElementsByTagName("Annunciations"); - if (annunciationsRoot.length > 0) { - const annunciations = annunciationsRoot[0].getElementsByTagName("Annunciation"); - for (let i = 0; i < annunciations.length; i++) { - this.addXmlMessage(annunciations[i]); - } - } - } - } - onEnter() { - } - onExit() { - } - addMessage(_type, _text, _handler) { - const msg = new Annunciation_Message(); - msg.Type = _type; - msg.Text = _text; - msg.Handler = _handler.bind(msg); - this.allMessages.push(msg); - } - addXmlMessage(_element) { - const msg = new Annunciation_Message_XML(); - switch (_element.getElementsByTagName("Type")[0].textContent) { - case "Warning": - msg.Type = Annunciation_MessageType.WARNING; - break; - case "Caution": - msg.Type = Annunciation_MessageType.CAUTION; - break; - case "Advisory": - msg.Type = Annunciation_MessageType.ADVISORY; - break; - case "SafeOp": - msg.Type = Annunciation_MessageType.SAFEOP; - break; - } - msg.baseText = _element.getElementsByTagName("Text")[0].textContent; - const conditions = _element.getElementsByTagName("Condition"); - for (let i = 0; i < conditions.length; i++) { - const condition = new XMLCondition(); - condition.logic = new CompositeLogicXMLElement(this.gps, conditions[i]); - condition.suffix = conditions[i].getAttribute("Suffix"); - msg.conditions.push(condition); - } - this.allMessages.push(msg); - } - addMessageTimed(_type, _text, _handler, _time) { - const msg = new Annunciation_Message_Timed(); - msg.Type = _type; - msg.Text = _text; - msg.Handler = _handler.bind(msg); - msg.timeNeeded = _time; - this.allMessages.push(msg); - } - addMessageSwitch(_type, _texts, _handler) { - const msg = new Annunciation_Message_Switch(); - msg.Type = _type; - msg.Texts = _texts; - msg.Handler = _handler.bind(msg); - this.allMessages.push(msg); - } - addMessageMultipleConditions(_type, _text, _conditions) { - const msg = new Annunciator_Message_MultipleConditions(); - msg.Type = _type; - msg.Text = _text; - msg.conditions = _conditions; - this.allMessages.push(msg); - } -} -class Cabin_Annunciations extends Annunciations { - constructor() { - super(...arguments); - this.displayWarning = []; - this.displayCaution = []; - this.displayAdvisory = []; - //this.warningToneNameZ = new Name_Z("CRC"); - this.cautionToneNameZ = new Name_Z("improved_tone_caution"); - this.warningTone = false; - this.firstAcknowledge = true; - this.offStart = false; - } - init(root) { - super.init(root); - this.alwaysUpdate = true; - this.isPlayingWarningTone = false; - for (let i = 0; i < this.allMessages.length; i++) { - const message = this.allMessages[i]; - let value = false; - if (message.Handler) { - value = message.Handler() != 0; - } - if (value != message.Visible) { - this.needReload = true; - message.Visible = value; - message.Acknowledged = !this.offStart; - if (value) { - switch (message.Type) { - case Annunciation_MessageType.WARNING: - this.displayWarning.push(message); - break; - case Annunciation_MessageType.CAUTION: - this.displayCaution.push(message); - break; - case Annunciation_MessageType.ADVISORY: - this.displayAdvisory.push(message); - break; - } - } - } - } - } - onEnter() { - } - onUpdate(_deltaTime) { - for (let i = 0; i < this.allMessages.length; i++) { - const message = this.allMessages[i]; - let value = false; - if (message.Handler) { - value = message.Handler() != 0; - } - if (value != message.Visible) { - this.needReload = true; - message.Visible = value; - message.Acknowledged = (this.gps.getTimeSinceStart() < 10000 && !this.offStart); - if (value) { - switch (message.Type) { - case Annunciation_MessageType.WARNING: - this.displayWarning.push(message); - break; - case Annunciation_MessageType.CAUTION: - this.displayCaution.push(message); - if (!message.Acknowledged && !this.isPlayingWarningTone && this.gps.isPrimary) { - const res = this.gps.playInstrumentSound("improved_tone_caution"); - if (res) { - this.isPlayingWarningTone = true; - } - } - break; - case Annunciation_MessageType.ADVISORY: - this.displayAdvisory.push(message); - break; - } - } else { - switch (message.Type) { - case Annunciation_MessageType.WARNING: - for (let i = 0; i < this.displayWarning.length; i++) { - if (this.displayWarning[i].Text == message.Text) { - this.displayWarning.splice(i, 1); - break; - } - } - break; - case Annunciation_MessageType.CAUTION: - for (let i = 0; i < this.displayCaution.length; i++) { - if (this.displayCaution[i].Text == message.Text) { - this.displayCaution.splice(i, 1); - break; - } - } - break; - case Annunciation_MessageType.ADVISORY: - for (let i = 0; i < this.displayAdvisory.length; i++) { - if (this.displayAdvisory[i].Text == message.Text) { - this.displayAdvisory.splice(i, 1); - break; - } - } - break; - } - } - } - } - if (this.annunciations) { - this.annunciations.setAttribute("state", this.gps.blinkGetState(800, 400) ? "Blink" : "None"); - } - if (this.needReload) { - let warningOn = 0; - let cautionOn = 0; - let messages = ""; - for (let i = this.displayWarning.length - 1; i >= 0; i--) { - messages += '
' + this.displayWarning[i].Text + "
"; - } - for (let i = this.displayCaution.length - 1; i >= 0; i--) { - messages += '
' + this.displayCaution[i].Text + "
"; - } - for (let i = this.displayAdvisory.length - 1; i >= 0; i--) { - messages += '
' + this.displayAdvisory[i].Text + "
"; - } - this.warningTone = warningOn > 0; - if (this.annunciations) { - this.annunciations.innerHTML = messages; - } - this.needReload = false; - } - /*if (this.warningTone && !this.isPlayingWarningTone && this.gps.isPrimary) { - this.isPlayingWarningTone = true; - }*/ - } - onEvent(_event) { - switch (_event) { - case "Master_Caution_Push": - for (let i = 0; i < this.allMessages.length; i++) { - if (this.allMessages[i].Type == Annunciation_MessageType.CAUTION && this.allMessages[i].Visible) { - this.allMessages[i].Acknowledged = true; - this.needReload = true; - } - } - break; - case "Master_Warning_Push": - for (let i = 0; i < this.allMessages.length; i++) { - if (this.allMessages[i].Type == Annunciation_MessageType.WARNING && this.allMessages[i].Visible) { - this.allMessages[i].Acknowledged = true; - this.needReload = true; - } - } - if (this.needReload && this.firstAcknowledge && this.gps.isPrimary) { - const res = this.gps.playInstrumentSound("aural_warning_ok"); - if (res) { - this.firstAcknowledge = false; - } - } - break; - } - } - onSoundEnd(_eventId) { - if (Name_Z.compare(_eventId, this.warningToneNameZ) || Name_Z.compare(_eventId, this.cautionToneNameZ)) { - this.isPlayingWarningTone = false; - } - } - onShutDown() { - for (let i = 0; i < this.allMessages.length; i++) { - this.allMessages[i].Acknowledged = false; - this.allMessages[i].Visible = false; - } - this.displayCaution = []; - this.displayWarning = []; - this.displayAdvisory = []; - this.firstAcknowledge = true; - this.needReload = true; - } - onPowerOn() { - this.offStart = true; - } - hasMessages() { - for (let i = 0; i < this.allMessages.length; i++) { - if (this.allMessages[i].Visible) { - return true; - } - } - return false; - } -} -class Engine_Annunciations extends Cabin_Annunciations { - init(root) { - super.init(root); - switch (this.engineType) { - case EngineType.ENGINE_TYPE_PISTON: - this.addMessage(Annunciation_MessageType.WARNING, "OIL PRESSURE", this.OilPressure); - this.addMessage(Annunciation_MessageType.WARNING, "LOW VOLTS", this.LowVoltage); - this.addMessage(Annunciation_MessageType.WARNING, "HIGH VOLTS", this.HighVoltage); - this.addMessage(Annunciation_MessageType.WARNING, "CO LVL HIGH", this.COLevelHigh); - this.addMessage(Annunciation_MessageType.CAUTION, "STBY BATT", this.StandByBattery); - this.addMessage(Annunciation_MessageType.CAUTION, "LOW VACUUM", this.LowVaccum); - this.addMessage(Annunciation_MessageType.CAUTION, "LOW FUEL R", this.LowFuelR); - this.addMessage(Annunciation_MessageType.CAUTION, "LOW FUEL L", this.LowFuelL); - break; - case EngineType.ENGINE_TYPE_TURBOPROP: - case EngineType.ENGINE_TYPE_JET: - this.addMessage(Annunciation_MessageType.WARNING, "FUEL OFF", this.fuelOff); - this.addMessage(Annunciation_MessageType.WARNING, "FUEL PRESS", this.fuelPress); - this.addMessage(Annunciation_MessageType.WARNING, "OIL PRESS", this.oilPressWarning); - this.addMessageMultipleConditions(Annunciation_MessageType.WARNING, "ITT", [ - new Condition(this.itt.bind(this, "1000")), - new Condition(this.itt.bind(this, "870"), 5), - new Condition(this.itt.bind(this, "840"), 20) - ]); - this.addMessage(Annunciation_MessageType.WARNING, "FLAPS ASYM", this.flapsAsym); - this.addMessage(Annunciation_MessageType.WARNING, "ELEC FEATH FAULT", this.elecFeathFault); - this.addMessage(Annunciation_MessageType.WARNING, "BLEED TEMP", this.bleedTemp); - this.addMessage(Annunciation_MessageType.WARNING, "CABIN ALTITUDE", this.cabinAltitude); - this.addMessage(Annunciation_MessageType.WARNING, "EDM", this.edm); - this.addMessage(Annunciation_MessageType.WARNING, "CABIN DIFF PRESS", this.cabinDiffPress); - this.addMessage(Annunciation_MessageType.WARNING, "DOOR", this.door); - this.addMessage(Annunciation_MessageType.WARNING, "USP ACTIVE", this.uspActive); - this.addMessage(Annunciation_MessageType.WARNING, "GEAR UNSAFE", this.gearUnsafe); - this.addMessage(Annunciation_MessageType.WARNING, "PARK BRAKE", this.parkBrake); - this.addMessage(Annunciation_MessageType.WARNING, "OXYGEN", this.oxygen); - this.addMessage(Annunciation_MessageType.CAUTION, "OIL PRESS", this.oilPressCaution); - this.addMessage(Annunciation_MessageType.CAUTION, "CHIP", this.chip); - this.addMessage(Annunciation_MessageType.CAUTION, "OIL TEMP", this.oilTemp); - this.addMessage(Annunciation_MessageType.CAUTION, "AUX BOOST PMP ON", this.auxBoostPmpOn); - this.addMessageSwitch(Annunciation_MessageType.CAUTION, ["FUEL LOW L", "FUEL LOW R", "FUEL LOW L-R"], this.fuelLowSelector); - this.addMessage(Annunciation_MessageType.CAUTION, "AUTO SEL", this.autoSel); - this.addMessageTimed(Annunciation_MessageType.CAUTION, "FUEL IMBALANCE", this.fuelImbalance, 30); - this.addMessageSwitch(Annunciation_MessageType.CAUTION, ["LOW LVL FAIL L", "LOW LVL FAIL R", "LOW LVL FAIL L-R"], this.lowLvlFailSelector); - this.addMessage(Annunciation_MessageType.CAUTION, "BAT OFF", this.batOff); - this.addMessage(Annunciation_MessageType.CAUTION, "BAT AMP", this.batAmp); - this.addMessage(Annunciation_MessageType.CAUTION, "MAIN GEN", this.mainGen); - this.addMessage(Annunciation_MessageType.CAUTION, "LOW VOLTAGE", this.lowVoltage); - this.addMessage(Annunciation_MessageType.CAUTION, "BLEED OFF", this.bleedOff); - this.addMessage(Annunciation_MessageType.CAUTION, "USE OXYGEN MASK", this.useOxygenMask); - this.addMessage(Annunciation_MessageType.CAUTION, "VACUUM LOW", this.vacuumLow); - this.addMessage(Annunciation_MessageType.CAUTION, "PROP DEICE FAIL", this.propDeiceFail); - this.addMessage(Annunciation_MessageType.CAUTION, "INERT SEP FAIL", this.inertSepFail); - this.addMessageSwitch(Annunciation_MessageType.CAUTION, ["PITOT NO HT L", "PITOT NO HT R", "PITOT NO HT L-R"], this.pitotNoHtSelector); - this.addMessageSwitch(Annunciation_MessageType.CAUTION, ["PITOT HT ON L", "PITOT HT ON R", "PITOT HT ON L-R"], this.pitotHtOnSelector); - this.addMessage(Annunciation_MessageType.CAUTION, "STALL NO HEAT", this.stallNoHeat); - this.addMessage(Annunciation_MessageType.CAUTION, "STALL HEAT ON", this.stallHeatOn); - this.addMessage(Annunciation_MessageType.CAUTION, "FRONT CARGO DOOR", this.frontCargoDoor); - this.addMessage(Annunciation_MessageType.CAUTION, "GPU DOOR", this.gpuDoor); - this.addMessage(Annunciation_MessageType.CAUTION, "IGNITION", this.ignition); - this.addMessage(Annunciation_MessageType.CAUTION, "STARTER", this.starter); - this.addMessage(Annunciation_MessageType.CAUTION, "MAX DIFF MODE", this.maxDiffMode); - this.addMessage(Annunciation_MessageType.CAUTION, "CPCS BACK UP MODE", this.cpcsBackUpMode); - break; - } - } - sayTrue() { - return true; - } - SafePropHeat() { - return false; - } - CautionPropHeat() { - return false; - } - StandByBattery() { - return false; - } - LowVaccum() { - return SimVar.GetSimVarValue("WARNING VACUUM", "Boolean"); - } - LowPower() { - return false; - } - LowFuelR() { - return SimVar.GetSimVarValue("FUEL RIGHT QUANTITY", "gallon") < 5; - } - LowFuelL() { - return SimVar.GetSimVarValue("FUEL LEFT QUANTITY", "gallon") < 5; - } - FuelTempFailed() { - return false; - } - ECUMinorFault() { - return false; - } - PitchTrim() { - return false; - } - StartEngage() { - return false; - } - OilPressure() { - return SimVar.GetSimVarValue("WARNING OIL PRESSURE", "Boolean"); - } - LowFuelPressure() { - const pressure = SimVar.GetSimVarValue("ENG FUEL PRESSURE", "psi"); - if (pressure <= 1) { - return true; - } - return false; - } - LowVoltage() { - const voltage = SimVar.GetSimVarValue("ELECTRICAL MAIN BUS VOLTAGE", "volts"); - if (voltage < 24) { - return true; - } - return false; - } - HighVoltage() { - const voltage = SimVar.GetSimVarValue("ELECTRICAL MAIN BUS VOLTAGE", "volts"); - if (voltage > 32) { - return true; - } - return false; - } - FuelTemperature() { - return false; - } - ECUMajorFault() { - return false; - } - COLevelHigh() { - return false; - } - fuelOff() { - return (SimVar.GetSimVarValue("FUEL TANK SELECTOR:1", "number") == 0); - } - fuelPress() { - return (SimVar.GetSimVarValue("GENERAL ENG FUEL PRESSURE:1", "psi") <= 10); - } - oilPressWarning() { - return (SimVar.GetSimVarValue("ENG OIL PRESSURE:1", "psi") <= 60); - } - itt(_limit = 840) { - const itt = SimVar.GetSimVarValue("TURB ENG ITT:1", "celsius"); - return (itt > _limit); - } - flapsAsym() { - return false; - } - elecFeathFault() { - return false; - } - bleedTemp() { - return false; - } - cabinAltitude() { - return SimVar.GetSimVarValue("PRESSURIZATION CABIN ALTITUDE", "feet") > 10000; - } - edm() { - return false; - } - cabinDiffPress() { - return SimVar.GetSimVarValue("PRESSURIZATION PRESSURE DIFFERENTIAL", "psi") > 6.2; - } - door() { - return SimVar.GetSimVarValue("EXIT OPEN:0", "percent") > 0; - } - uspActive() { - return false; - } - gearUnsafe() { - return false; - } - parkBrake() { - return SimVar.GetSimVarValue("L:A32NX_PARK_BRAKE_LEVER_POS", "Bool"); - } - oxygen() { - return false; - } - oilPressCaution() { - const press = SimVar.GetSimVarValue("ENG OIL PRESSURE:1", "psi"); - return (press <= 105 && press >= 60); - } - chip() { - return false; - } - oilTemp() { - const temp = SimVar.GetSimVarValue("GENERAL ENG OIL TEMPERATURE:1", "celsius"); - return (temp <= 0 || temp >= 104); - } - auxBoostPmpOn() { - return SimVar.GetSimVarValue("GENERAL ENG FUEL PUMP ON:1", "Bool"); - } - fuelLowSelector() { - const left = SimVar.GetSimVarValue("FUEL TANK LEFT MAIN QUANTITY", "gallon") < 9; - const right = SimVar.GetSimVarValue("FUEL TANK RIGHT MAIN QUANTITY", "gallon") < 9; - if (left && right) { - return 3; - } else if (left) { - return 1; - } else if (right) { - return 2; - } else { - return 0; - } - } - autoSel() { - return false; - } - fuelImbalance() { - const left = SimVar.GetSimVarValue("FUEL TANK LEFT MAIN QUANTITY", "gallon"); - const right = SimVar.GetSimVarValue("FUEL TANK RIGHT MAIN QUANTITY", "gallon"); - return Math.abs(left - right) > 15; - } - lowLvlFailSelector() { - return false; - } - batOff() { - return !SimVar.GetSimVarValue("ELECTRICAL MASTER BATTERY", "Bool"); - } - batAmp() { - return SimVar.GetSimVarValue("ELECTRICAL BATTERY BUS AMPS", "amperes") > 50; - } - mainGen() { - return !SimVar.GetSimVarValue("GENERAL ENG GENERATOR SWITCH:1", "Bool"); - } - lowVoltage() { - return SimVar.GetSimVarValue("ELECTRICAL MAIN BUS VOLTAGE", "volts") < 24.5; - } - bleedOff() { - return SimVar.GetSimVarValue("BLEED AIR SOURCE CONTROL", "Enum") == 1; - } - useOxygenMask() { - return SimVar.GetSimVarValue("PRESSURIZATION CABIN ALTITUDE", "feet") > 10000; - } - vacuumLow() { - return SimVar.GetSimVarValue("PARTIAL PANEL VACUUM", "Enum") == 1; - } - propDeiceFail() { - return false; - } - inertSepFail() { - return false; - } - pitotNoHtSelector() { - return 0; - } - pitotHtOnSelector() { - return 0; - } - stallNoHeat() { - return false; - } - stallHeatOn() { - return false; - } - frontCargoDoor() { - return false; - } - gpuDoor() { - return false; - } - ignition() { - return SimVar.GetSimVarValue("TURB ENG IS IGNITING:1", "Bool"); - } - starter() { - return SimVar.GetSimVarValue("GENERAL ENG STARTER ACTIVE:1", "Bool"); - } - maxDiffMode() { - return SimVar.GetSimVarValue("BLEED AIR SOURCE CONTROL", "Enum") == 3; - } - cpcsBackUpMode() { - return false; - } -} -class Warning_Data { - constructor(_shortText, _longText, _soundEvent, _Level, _callback, _once = false) { - this.hasPlayed = false; - this.shortText = _shortText; - this.longText = _longText; - this.soundEvent = _soundEvent; - this.soundEventId = new Name_Z(this.soundEvent); - this.level = _Level; - this.callback = _callback; - this.once = _once; - } -} -class Warning_Data_XML extends Warning_Data { - constructor(_gps, _shortText, _longText, _soundEvent, _Level, _logicElement, _once = false) { - super(_shortText, _longText, _soundEvent, _Level, null, _once); - this.xmlLogic = new CompositeLogicXMLElement(_gps, _logicElement); - this.callback = this.getXMLBoolean.bind(this); - } - getXMLBoolean() { - return this.xmlLogic.getValue() != 0; - } -} -class Warnings extends NavSystemElement { - constructor() { - super(...arguments); - this.warnings = []; - this.playingSounds = []; - this.pullUp_sinkRate_Points = [ - [1160, 0, 0], - [2320, 1070, 1460], - [4930, 2380, 2980], - [11600, 4285, 5360] - ]; - } - init(_root) { - let alertsFromXML = false; - if (this.gps.xmlConfig) { - const alertsGroup = this.gps.xmlConfig.getElementsByTagName("VoicesAlerts"); - if (alertsGroup.length > 0) { - alertsFromXML = true; - const alerts = alertsGroup[0].getElementsByTagName("Alert"); - for (let i = 0; i < alerts.length; i++) { - const typeParam = alerts[i].getElementsByTagName("Type"); - let type = 0; - if (typeParam.length > 0) { - switch (typeParam[0].textContent) { - case "Warning": - type = 3; - break; - case "Caution": - type = 2; - break; - case "Test": - type = 1; - break; - case "SoundOnly": - type = 0; - break; - } - } - let shortText = ""; - let longText = ""; - if (type != 0) { - const shortTextElem = alerts[i].getElementsByTagName("ShortText"); - if (shortTextElem.length > 0) { - shortText = shortTextElem[0].textContent; - } - const longTextElem = alerts[i].getElementsByTagName("LongText"); - if (longTextElem.length > 0) { - longText = longTextElem[0].textContent; - } - } - let soundEvent = ""; - const soundEventElem = alerts[i].getElementsByTagName("SoundEvent"); - if (soundEventElem.length > 0) { - soundEvent = soundEventElem[0].textContent; - } - const condition = alerts[i].getElementsByTagName("Condition")[0]; - let once = false; - const onceElement = alerts[i].getElementsByTagName("Once"); - if (onceElement.length > 0 && onceElement[0].textContent == "True") { - once = true; - } - this.warnings.push(new Warning_Data_XML(this.gps, shortText, longText, soundEvent, type, condition, once)); - } - } - } - if (!alertsFromXML) { - this.warnings.push(new Warning_Data("", "", "Garmin_Stall_f", 0, this.stallCallback.bind(this))); - this.warnings.push(new Warning_Data("PULL UP", "PULL UP", "Garmin_Pull_Up_f", 3, this.pullUpCallback.bind(this))); - this.warnings.push(new Warning_Data("TERRAIN", "SINK RATE", "Garmin_Sink_Rate_f", 2, this.sinkRateCallback.bind(this))); - this.warnings.push(new Warning_Data("", "", "Garmin_landing_gear_f", 0, this.landingGearCallback.bind(this))); - this.warnings.push(new Warning_Data("TAWS TEST", "", "", 1, this.tawsTestCallback.bind(this))); - this.warnings.push(new Warning_Data("", "", "Garmin_TAWS_System_Test_OK_f", 0, this.tawsTestFinishedCallback.bind(this), true)); - } - this.UID = parseInt(this.gps.getAttribute("Guid")) + 1; - SimVar.SetSimVarValue("L:AS1000_Warnings_Master_Set", "number", 0); - } - onUpdate(_deltaTime) { - const masterSet = SimVar.GetSimVarValue("L:AS1000_Warnings_Master_Set", "number"); - if (masterSet == 0) { - SimVar.SetSimVarValue("L:AS1000_Warnings_Master_Set", "number", this.UID); - } else if (masterSet == this.UID) { - let found = false; - let foundText = false; - let bestWarning = 0; - for (let i = 0; i < this.warnings.length; i++) { - const warning = this.warnings[i]; - if (!warning.once || !warning.hasPlayed) { - if (warning.callback()) { - if (warning.soundEvent != "") { - if ((this.playingSounds.length <= 0) || (i < this.playingSounds[this.playingSounds.length - 1])) { - const res = this.gps.playInstrumentSound(warning.soundEvent); - if (res) { - this.playingSounds.push(i); - warning.hasPlayed = true; - } - } - } - if (!foundText) { - bestWarning = i; - } - if (warning.shortText || warning.longText) { - foundText = true; - } - found = true; - } - } - } - if (found) { - SimVar.SetSimVarValue("L:AS1000_Warnings_WarningIndex", "number", bestWarning + 1); - } else { - SimVar.SetSimVarValue("L:AS1000_Warnings_WarningIndex", "number", 0); - } - } - } - onSoundEnd(_eventId) { - let i = 0; - while (i < this.playingSounds.length) { - const soundId = this.playingSounds[i]; - if (Name_Z.compare(this.warnings[soundId].soundEventId, _eventId)) { - this.playingSounds.splice(i, 1); - continue; - } - i++; - } - } - onShutDown() { - for (let i = 0; i < this.warnings.length; i++) { - this.warnings[i].hasPlayed = false; - } - this.playingSounds = []; - } - linearMultiPointsEvaluation(_points, _valueX, _valueY) { - let lastLowerIndex = -1; - for (let i = 0; i < _points.length; i++) { - if (_valueX > _points[i][0]) { - lastLowerIndex = i; - } else { - break; - } - } - if (lastLowerIndex == _points.length - 1) { - for (let i = 1; i < _points[lastLowerIndex].length; i++) { - if (_valueY < _points[lastLowerIndex][i]) { - return i; - } - } - return _points[lastLowerIndex].length; - } else if (lastLowerIndex == -1) { - for (let i = 1; i < _points[0].length; i++) { - if (_valueY < _points[0][i]) { - return i; - } - } - return _points[0].length; - } else { - const factorLower = (_valueX - _points[lastLowerIndex][0]) / _points[lastLowerIndex + 1][0]; - for (let i = 1; i < _points[lastLowerIndex].length; i++) { - const limit = _points[lastLowerIndex][i] * factorLower + _points[lastLowerIndex + 1][i] * (1 - factorLower); - if (_valueY < limit) { - return i; - } - } - return _points[lastLowerIndex].length; - } - } - pullUpCallback() { - const height = SimVar.GetSimVarValue("PLANE ALT ABOVE GROUND", "feet"); - const descentRate = -SimVar.GetSimVarValue("VERTICAL SPEED", "feet per minute"); - return this.linearMultiPointsEvaluation(this.pullUp_sinkRate_Points, descentRate, height) == 1; - } - sinkRateCallback() { - const height = SimVar.GetSimVarValue("PLANE ALT ABOVE GROUND", "feet"); - const descentRate = -SimVar.GetSimVarValue("VERTICAL SPEED", "feet per minute"); - return this.linearMultiPointsEvaluation(this.pullUp_sinkRate_Points, descentRate, height) == 2; - } - landingGearCallback() { - const gear = !SimVar.GetSimVarValue("IS GEAR RETRACTABLE", "Boolean") || SimVar.GetSimVarValue("L:A32NX_GEAR_HANDLE_POSITION", "Percent over 100") > 0.5; - const throttle = SimVar.GetSimVarValue("L:A32NX_AUTOTHRUST_TLA:1", "number"); - const flaps = SimVar.GetSimVarValue("L:A32NX_FLAPS_HANDLE_INDEX", "number"); - return !gear && (flaps > 1 || (throttle == 0)); - } - stallCallback() { - return SimVar.GetSimVarValue("STALL WARNING", "Boolean"); - } - tawsTestCallback() { - return this.gps.getTimeSinceStart() < 30000; - } - tawsTestFinishedCallback() { - return this.gps.getTimeSinceStart() >= 30000; - } -} -class Cabin_Warnings extends Warnings { - constructor() { - super(...arguments); - this.currentWarningLevel = 0; - this.currentWarningText = ""; - } - init(root) { - super.init(root); - this.alwaysUpdate = true; - } - onUpdate(_deltaTime) { - super.onUpdate(_deltaTime); - const warningIndex = SimVar.GetSimVarValue("L:AS1000_Warnings_WarningIndex", "number"); - let warningText; - let warningLevel; - if (warningIndex <= 0 || warningIndex >= this.warnings.length) { - warningText = ""; - warningLevel = 0; - } else { - warningText = this.warnings[warningIndex - 1].shortText; - warningLevel = this.warnings[warningIndex - 1].level; - } - if (this.currentWarningLevel != warningLevel || this.currentWarningText != warningText) { - this.currentWarningText = warningText; - this.currentWarningLevel = warningLevel; - if (this.warningBox && this.warningContent) { - this.warningContent.textContent = warningText; - switch (warningLevel) { - case 0: - this.warningBox.setAttribute("state", "Hidden"); - break; - case 1: - this.warningBox.setAttribute("state", "White"); - break; - case 2: - this.warningBox.setAttribute("state", "Yellow"); - break; - case 3: - this.warningBox.setAttribute("state", "Red"); - break; - } - } - } - } - getCurrentWarningLevel() { - return this.currentWarningLevel; - } - getCurrentWarningText() { - return this.currentWarningText; - } - onEnter() { } - onExit() { } - onEvent(_event) { } -} -class GlassCockpit_XMLEngine extends NavSystemElement { - constructor() { - super(...arguments); - this.xmlEngineDisplay = null; - this._t = 0; - } - init(_root) { - if (this.gps.xmlConfig) { - const engineRoot = this.gps.xmlConfig.getElementsByTagName("EngineDisplay"); - if (engineRoot.length > 0) { - this.xmlEngineDisplay = _root.querySelector("glasscockpit-xmlenginedisplay"); - this.xmlEngineDisplay.setConfiguration(this.gps, engineRoot[0]); - } - } - } - onEnter() { - } - onExit() { - } - onUpdate(_deltaTime) { - if (this.xmlEngineDisplay) { - this.xmlEngineDisplay.update(_deltaTime); - } - this._t++; - if (this._t > 30) { - this.gps.currFlightPlanManager.updateFlightPlan(); - this._t = 0; - } - } - onEvent(_event) { - this.xmlEngineDisplay.onEvent(_event); - } - onSoundEnd(_eventId) { - super.onSoundEnd(_eventId); - this.xmlEngineDisplay.onSoundEnd(_eventId); - } -} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/.eslintrc.js b/fbw-a380x/src/systems/instruments/src/FCU/.eslintrc.js new file mode 100644 index 00000000000..a4a79b204b3 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/.eslintrc.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = { + extends: '../../../../../../.eslintrc.js', + + // overrides airbnb, use sparingly + rules: { 'react/react-in-jsx-scope': 'off', 'react/no-unknown-property': 'off', 'react/style-prop-object': 'off' }, +}; diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Components/Altitude.tsx b/fbw-a380x/src/systems/instruments/src/FCU/Components/Altitude.tsx new file mode 100644 index 00000000000..a2b25c61d63 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Components/Altitude.tsx @@ -0,0 +1,45 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { ConsumerSubject, DisplayComponent, EventBus, FSComponent, MappedSubject, VNode } from '@microsoft/msfs-sdk'; +import { OverheadEvents } from '../../MsfsAvionicsCommon/providers/OverheadPublisher'; +import { MsfsAutopilotAssistanceEvents } from '@flybywiresim/fbw-sdk'; + +export interface AltitudeProps { + readonly bus: EventBus; +} + +export class Altitude extends DisplayComponent { + private sub = this.props.bus.getSubscriber(); + + private readonly isLightTestActive = ConsumerSubject.create(this.sub.on('ovhd_ann_lt_test_active'), false); + + private readonly selectedAltitude = ConsumerSubject.create(this.sub.on('msfs_autopilot_altitude_lock_var_3'), 100); + + private readonly altText = MappedSubject.create( + ([isLightTest, altitude]) => { + if (isLightTest) { + return '88888'; + } + const value = Math.floor(Math.max(altitude, 100)); + return value.toString().padStart(5, '0'); + }, + this.isLightTestActive, + this.selectedAltitude, + ); + + render(): VNode | null { + return ( +
+ + + ALT + + + {this.altText} + + +
+ ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Components/Baro.tsx b/fbw-a380x/src/systems/instruments/src/FCU/Components/Baro.tsx new file mode 100644 index 00000000000..4fe6fd83fcd --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Components/Baro.tsx @@ -0,0 +1,123 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { + ConsumerSubject, + DebounceTimer, + DisplayComponent, + EventBus, + FSComponent, + MappedSubject, + Subject, + VNode, +} from '@microsoft/msfs-sdk'; +import { OverheadEvents } from '../../MsfsAvionicsCommon/providers/OverheadPublisher'; +import { BaroEvents } from '../Managers/BaroManager'; + +export interface BaroProps { + readonly bus: EventBus; +} + +export class Baro extends DisplayComponent { + private static readonly PRESEL_TIME = 4_000; + + private readonly sub = this.props.bus.getSubscriber(); + + private readonly mode = ConsumerSubject.create(this.sub.on('baro_mode_1'), 'STD'); + + private readonly correction = ConsumerSubject.create(this.sub.on('baro_correction_1'), 1013); + + private readonly isLightTestActive = ConsumerSubject.create(this.sub.on('ovhd_ann_lt_test_active'), false); + + private readonly isQnhLabelVisible = MappedSubject.create( + ([mode, isLightTest]) => isLightTest || mode === 'QNH', + this.mode, + this.isLightTestActive, + ); + + private readonly isQfeLabelVisible = MappedSubject.create( + ([mode, isLightTest]) => isLightTest || mode === 'QFE', + this.mode, + this.isLightTestActive, + ); + + private readonly isPreSelVisible = Subject.create(false); + private readonly preSelVisibileTimer = new DebounceTimer(); + + private readonly baroText = MappedSubject.create( + ([mode, correction, isLightTest]) => { + if (isLightTest) { + return '88.88'; + } + switch (mode) { + case 'STD': + return 'Std'; + case 'QNH': + case 'QFE': + if (correction < 100) { + return correction.toFixed(2); + } + return correction.toFixed(0).padStart(4, '0'); + } + }, + this.mode, + this.correction, + this.isLightTestActive, + ); + + private readonly preSelBaroText = MappedSubject.create( + ([correction, isVisible]) => + isVisible ? (correction < 100 ? correction.toFixed(2) : correction.toFixed(0).padStart(4, '0')) : '', + this.correction, + this.isPreSelVisible, + ); + + onAfterRender(_node: VNode): void { + this.sub.on('baro_preselect_changed_1').handle(() => { + this.isPreSelVisible.set(true); + this.preSelVisibileTimer.schedule(() => this.isPreSelVisible.set(false), Baro.PRESEL_TIME); + }); + this.mode.sub((v) => v !== 'STD' && this.isPreSelVisible.set(false)); + } + + render(): VNode | null { + return ( +
+
+ + + QNH + + + QFE + + + {this.preSelBaroText} + + + {this.baroText} + + +
+
+ ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Components/FcuDisplay.tsx b/fbw-a380x/src/systems/instruments/src/FCU/Components/FcuDisplay.tsx new file mode 100644 index 00000000000..0cedd3f7c53 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Components/FcuDisplay.tsx @@ -0,0 +1,43 @@ +// Copyright (c) 2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { DisplayComponent, EventBus, FSComponent, Subscribable, VNode } from '@microsoft/msfs-sdk'; +import { Altitude } from './Altitude'; +import { Baro } from './Baro'; +import { Heading } from './Heading'; +import { Speed } from './Speed'; +import { VerticalSpeed } from './VerticalSpeed'; +import { NdData } from 'instruments/src/FCU/Components/NdData'; + +export interface FcuDisplayProps { + readonly bus: EventBus; + readonly isHidden: Subscribable; +} + +export class FcuDisplay extends DisplayComponent { + render(): VNode | null { + return ( + <> +
+
(v ? 'off' : 'on'))}> +
+ + + +
+ + +
+
+ + + {/* FIXME need a second baro for FO side */} + + + +
+
+ + ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Components/Heading.tsx b/fbw-a380x/src/systems/instruments/src/FCU/Components/Heading.tsx new file mode 100644 index 00000000000..3cde4bb987d --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Components/Heading.tsx @@ -0,0 +1,29 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { DisplayComponent, FSComponent, VNode } from '@microsoft/msfs-sdk'; + +export interface HeadingProps {} + +export class Heading extends DisplayComponent { + render(): VNode | null { + return ( +
+ + + HDG + + + TRK + + + --- + + + ° + + +
+ ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Components/NdData.tsx b/fbw-a380x/src/systems/instruments/src/FCU/Components/NdData.tsx new file mode 100644 index 00000000000..7537ca539f5 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Components/NdData.tsx @@ -0,0 +1,70 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { NavAidMode } from '@flybywiresim/fbw-sdk'; +import { ConsumerSubject, DisplayComponent, EventBus, FSComponent, VNode } from '@microsoft/msfs-sdk'; +import { FcuEvents } from 'instruments/src/FCU/Publishers/FcuPublisher'; + +export interface NdDataProps { + readonly bus: EventBus; + readonly index: 1 | 2; +} + +export class NdData extends DisplayComponent { + private static readonly NAVAID_1_IMAGES: Record = { + [NavAidMode.Off]: '/Images/fbw-a380x/fcu/POINTER1.png', + [NavAidMode.VOR]: '/Images/fbw-a380x/fcu/VOR1.png', + [NavAidMode.ADF]: '/Images/fbw-a380x/fcu/ADF1.png', + }; + private static readonly NAVAID_2_IMAGES: Record = { + [NavAidMode.Off]: '/Images/fbw-a380x/fcu/POINTER2.png', + [NavAidMode.VOR]: '/Images/fbw-a380x/fcu/VOR2.png', + [NavAidMode.ADF]: '/Images/fbw-a380x/fcu/ADF2.png', + }; + + private readonly sub = this.props.bus.getSubscriber(); + + private readonly navaidMode1 = ConsumerSubject.create( + this.sub.on(this.props.index === 2 ? 'fcu_right_navaid_mode_1' : 'fcu_left_navaid_mode_1'), + NavAidMode.Off, + ); + private readonly navaidMode2 = ConsumerSubject.create( + this.sub.on(this.props.index === 2 ? 'fcu_right_navaid_mode_2' : 'fcu_left_navaid_mode_2'), + NavAidMode.Off, + ); + + render(): VNode | null { + return ( +
+
+ + + + + +
+ +
+ NdData.NAVAID_1_IMAGES[v])} + /> + + + + NdData.NAVAID_2_IMAGES[v])} + /> +
+
+ ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Components/Speed.tsx b/fbw-a380x/src/systems/instruments/src/FCU/Components/Speed.tsx new file mode 100644 index 00000000000..dcc76a8afe5 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Components/Speed.tsx @@ -0,0 +1,29 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { DisplayComponent, FSComponent, VNode } from '@microsoft/msfs-sdk'; + +export interface SpeedProps {} + +export class Speed extends DisplayComponent { + render(): VNode | null { + return ( +
+ + + MACH + + + SPD + + + --- + + + KT + + +
+ ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Components/VerticalSpeed.tsx b/fbw-a380x/src/systems/instruments/src/FCU/Components/VerticalSpeed.tsx new file mode 100644 index 00000000000..4d852f7b75c --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Components/VerticalSpeed.tsx @@ -0,0 +1,26 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { DisplayComponent, FSComponent, VNode } from '@microsoft/msfs-sdk'; + +export interface VerticalSpeedProps {} + +export class VerticalSpeed extends DisplayComponent { + render(): VNode | null { + return ( +
+ + + V/S + + + FPA + + + ----- + + +
+ ); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/FcuBaseInstrument.ts b/fbw-a380x/src/systems/instruments/src/FCU/FcuBaseInstrument.ts new file mode 100644 index 00000000000..77b06c23d89 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/FcuBaseInstrument.ts @@ -0,0 +1,41 @@ +// Copyright (c) 2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { FsBaseInstrument } from '@microsoft/msfs-sdk'; +import { FcuFsInstrument } from './FcuFsInstrument'; + +/** + * The FCU Instrument. + */ +export class FcuBaseInstrument extends FsBaseInstrument { + /** @inheritdoc */ + public get isInteractive(): boolean { + return false; + } + + /** @inheritdoc */ + public constructInstrument(): FcuFsInstrument { + return new FcuFsInstrument(this); + } + + /** @inheritdoc */ + get templateID(): string { + return 'A380X_FCU'; + } + + /** @inheritdoc */ + public onPowerOn(): void { + super.onPowerOn(); + + this.fsInstrument.onPowerOn(); + } + + /** @inheritdoc */ + public onShutDown(): void { + super.onShutDown(); + + this.fsInstrument.onPowerOff(); + } +} + +registerInstrument('a380x-fcu', FcuBaseInstrument); diff --git a/fbw-a380x/src/systems/instruments/src/FCU/FcuFsInstrument.tsx b/fbw-a380x/src/systems/instruments/src/FCU/FcuFsInstrument.tsx new file mode 100644 index 00000000000..16061bfdd67 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/FcuFsInstrument.tsx @@ -0,0 +1,151 @@ +// Copyright (c) 2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { + Clock, + DebounceTimer, + EventBus, + FSComponent, + FsInstrument, + HEventPublisher, + InstrumentBackplane, + MappedSubject, + Subject, + SubscribableMapFunctions, +} from '@microsoft/msfs-sdk'; +import { FailuresConsumer, MsfsAutopilotAssitancePublisher, MsfsRadioNavigationPublisher } from '@flybywiresim/fbw-sdk'; +import { FcuDisplay } from './Components/FcuDisplay'; +import { AltitudeManager } from './Managers/AltitudeManager'; +import { AutopilotManager } from './Managers/AutopilotManager'; +import { BaroManager } from './Managers/BaroManager'; +import { HeadingManager } from './Managers/HeadingManager'; +import { SpeedManager } from './Managers/SpeedManager'; +import { VerticalSpeedManager } from './Managers/VerticalSpeedManager'; +import { FcuPublisher } from './Publishers/FcuPublisher'; +import { FGDataPublisher } from '../MsfsAvionicsCommon/providers/FGDataPublisher'; + +import './style.scss'; + +export class FcuFsInstrument implements FsInstrument { + private static readonly INIT_DURATION = 1000; + + private readonly bus = new EventBus(); + + private readonly isPowered = Subject.create(false); + + private readonly backplane = new InstrumentBackplane(); + private readonly clock = new Clock(this.bus); + private readonly hEventPublisher = new HEventPublisher(this.bus); + + private readonly failuresConsumer = new FailuresConsumer('A32NX'); + + //private readonly isFailedKey = A380Failure.Fcu; + private readonly isFailed = Subject.create(false); + + private readonly isOperating = Subject.create(false); + private readonly initTimer = new DebounceTimer(); + + /** + * Creates a new instance of FsInstrument. + * @param instrument This instrument's parent BaseInstrument. + * @param config The general avionics configuration object. + */ + constructor(public readonly instrument: BaseInstrument) { + // force enable animations + document.documentElement.classList.add('animationsEnabled'); + + this.renderComponents(); + + this.backplane.addInstrument('Clock', this.clock); + this.backplane.addPublisher('HEvent', this.hEventPublisher); + + this.backplane.addInstrument('AltitudeManager', new AltitudeManager(this.bus)); + this.backplane.addInstrument('AutopilotManager', new AutopilotManager(this.bus)); + this.backplane.addInstrument('BaroManager1', new BaroManager(this.bus, 1), true); + this.backplane.addInstrument('HeadingManager', new HeadingManager(this.bus)); + this.backplane.addInstrument('SpeedManager', new SpeedManager(this.bus)); + this.backplane.addInstrument('VerticalSpeedManager', new VerticalSpeedManager(this.bus)); + + this.backplane.addPublisher('FcuPublisher', new FcuPublisher(this.bus)); + this.backplane.addPublisher('FgBusPublisher', new FGDataPublisher(this.bus)); + this.backplane.addPublisher('MsfsAutopilotAssistancePublisher', new MsfsAutopilotAssitancePublisher(this.bus)); + this.backplane.addPublisher('MsfsRadioNavigationPublisher', new MsfsRadioNavigationPublisher(this.bus)); + + this.doInit(); + } + + /** @inheritdoc */ + private renderComponents(): void { + FSComponent.render( + , + document.getElementById('FCU_CONTENT'), + ); + + // Remove "instrument didn't load" text + document.getElementById('FCU_CONTENT')?.querySelector(':scope > h1')?.remove(); + } + + /** @inheritdoc */ + public Update(): void { + this.failuresConsumer.update(); + // this.isFailed.set(this.failuresConsumer.isActive(this.isFailedKey));; + + this.backplane.onUpdate(); + } + + /** @inheritdoc */ + public onInteractionEvent(args: string[]): void { + this.hEventPublisher.dispatchHEvent(args[0]); + } + + /** @inheritdoc */ + public onFlightStart(): void { + // noop and not useful + } + + /** @inheritdoc */ + public onGameStateChanged(_oldState: GameState, _newState: GameState): void { + // noop + } + + /** @inheritdoc */ + public onSoundEnd(_soundEventId: Name_Z): void { + // noop + } + + public onPowerOn(): void { + this.isPowered.set(true); + } + + public onPowerOff(): void { + this.isPowered.set(false); + } + + /** Init instrument. */ + private doInit(): void { + // this.failuresConsumer.register(this.isFailedKey); + + // FIXME: very dubious code from old FCU + this.isOperating.sub((v) => { + if (v) { + if (!SimVar.GetSimVarValue('AUTOPILOT FLIGHT DIRECTOR ACTIVE:1', 'bool')) { + SimVar.SetSimVarValue('K:TOGGLE_FLIGHT_DIRECTOR', 'number', 1); + } + if (!SimVar.GetSimVarValue('AUTOPILOT FLIGHT DIRECTOR ACTIVE:2', 'bool')) { + SimVar.SetSimVarValue('K:TOGGLE_FLIGHT_DIRECTOR', 'number', 2); + } + } + }); + + MappedSubject.create(this.isPowered, this.isFailed).sub(([isPowered, isFailed]) => { + if (isPowered && !isFailed) { + this.initTimer.schedule(() => this.isOperating.set(true), FcuFsInstrument.INIT_DURATION); + } else { + this.initTimer.clear(); + this.isOperating.set(false); + } + }, true); + + this.backplane.init(); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Managers/AltitudeManager.ts b/fbw-a380x/src/systems/instruments/src/FCU/Managers/AltitudeManager.ts new file mode 100644 index 00000000000..86941449619 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Managers/AltitudeManager.ts @@ -0,0 +1,76 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { + ConsumerSubject, + EventBus, + GameStateProvider, + HEvent, + Instrument, + MappedSubject, + SimVarValueType, + Subscribable, + Wait, +} from '@microsoft/msfs-sdk'; +import { FGVars, FgVerticalArmedFlags } from '../../MsfsAvionicsCommon/providers/FGDataPublisher'; +import { VerticalMode } from '@shared/autopilot'; + +export class AltitudeManager implements Instrument { + private static readonly MANAGED_ARMED_MASK = + FgVerticalArmedFlags.AltCst | FgVerticalArmedFlags.Clb | FgVerticalArmedFlags.Des | FgVerticalArmedFlags.Gs; + private static readonly MANAGED_MODES = [ + VerticalMode.ALT_CST, + VerticalMode.ALT_CST_CPT, + VerticalMode.CLB, + VerticalMode.DES, + VerticalMode.FINAL, + VerticalMode.GS_CPT, + VerticalMode.GS_TRACK, + VerticalMode.LAND, + VerticalMode.FLARE, + VerticalMode.ROLL_OUT, + ]; + + private readonly sub = this.bus.getSubscriber(); + + private readonly verticalMode = ConsumerSubject.create(this.sub.on('fg.fma.verticalMode'), 0); + private readonly verticalArmed = ConsumerSubject.create(this.sub.on('fg.fma.verticalArmedBitmask'), 0); + private readonly _isVerticalManaged = MappedSubject.create( + ([verticalMode, verticalArmed]) => + (verticalArmed & AltitudeManager.MANAGED_ARMED_MASK) > 0 || AltitudeManager.MANAGED_MODES.includes(verticalMode), + this.verticalMode, + this.verticalArmed, + ); + public readonly isVerticalManaged = this._isVerticalManaged as Subscribable; + + constructor(private readonly bus: EventBus) {} + + public init(): void { + Wait.awaitSubscribable(GameStateProvider.get(), (v) => v === GameState.ingame, true).then(() => { + let initValue = 100; + if (Simplane.getAltitudeAboveGround() > 1000) { + initValue = Math.min(49000, Math.max(100, Math.round(Simplane.getAltitude() / 100) * 100)); + } + Coherent.call('AP_ALT_VAR_SET_ENGLISH', 3, initValue, true).catch(console.error); + }); + + this.isVerticalManaged.sub((v) => SimVar.SetSimVarValue('L:A32NX_FCU_ALT_MANAGED', SimVarValueType.Bool, v), true); + + this.sub.on('hEvent').handle(this.onHEvent.bind(this)); + } + + public onUpdate(): void { + // noop + } + + private onHEvent(event: string): void { + // FIXME don't handle events when not powered up + if (event === 'A320_Neo_FCU_ALT_PUSH') { + SimVar.SetSimVarValue('K:A32NX.FCU_ALT_PUSH', 'number', 0); + SimVar.SetSimVarValue('K:ALTITUDE_SLOT_INDEX_SET', 'number', 2); + } else if (event === 'A320_Neo_FCU_ALT_PULL') { + SimVar.SetSimVarValue('K:A32NX.FCU_ALT_PULL', 'number', 0); + SimVar.SetSimVarValue('K:ALTITUDE_SLOT_INDEX_SET', 'number', 1); + } + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Managers/AutopilotManager.ts b/fbw-a380x/src/systems/instruments/src/FCU/Managers/AutopilotManager.ts new file mode 100644 index 00000000000..df8fce751ce --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Managers/AutopilotManager.ts @@ -0,0 +1,34 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, Instrument } from '@microsoft/msfs-sdk'; +import { TemporaryHax } from './TemporaryHax'; + +// FIXME port to MSFS avionics framework style +export class AutopilotManager extends TemporaryHax implements Instrument { + constructor(private readonly bus: EventBus) { + super(bus, document.getElementById('Autopilot')!); + this.init(); + this.onUpdate(); + } + + public init(): void {} + + protected override onEvent(_event: string): void { + if (_event === 'AP_1_PUSH') { + SimVar.SetSimVarValue('K:A32NX.FCU_AP_1_PUSH', 'number', 0); + } else if (_event === 'AP_2_PUSH') { + SimVar.SetSimVarValue('K:A32NX.FCU_AP_2_PUSH', 'number', 0); + } else if (_event === 'LOC_PUSH') { + SimVar.SetSimVarValue('K:A32NX.FCU_LOC_PUSH', 'number', 0); + } else if (_event === 'APPR_PUSH') { + SimVar.SetSimVarValue('K:A32NX.FCU_APPR_PUSH', 'number', 0); + } else if (_event === 'EXPED_PUSH') { + SimVar.SetSimVarValue('K:A32NX.FCU_EXPED_PUSH', 'number', 0); + } else if (_event === 'TRUEMAG_PUSH') { + SimVar.SetSimVarValue('L:A32NX_PUSH_TRUE_REF', 'bool', !SimVar.GetSimVarValue('L:A32NX_PUSH_TRUE_REF', 'bool')); + } + } + + public onUpdate(): void {} +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Managers/BaroManager.ts b/fbw-a380x/src/systems/instruments/src/FCU/Managers/BaroManager.ts new file mode 100644 index 00000000000..abf29d0f034 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Managers/BaroManager.ts @@ -0,0 +1,98 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, HEvent, Instrument, KeyEventManager, Subject, UnitType } from '@microsoft/msfs-sdk'; + +export type BaroMode = ReturnType; +export type BaroUnit = ReturnType; + +export type BaroIndex = 1; // | 2; + +interface BaroBaseEvents { + baro_mode: BaroMode; + baro_unit: BaroUnit; + baro_correction: number; + baro_preselect_changed: unknown; +} + +export type BaroEvents = { + [P in keyof BaroBaseEvents as `${P}_${BaroIndex}`]: BaroBaseEvents[P]; +}; + +export class BaroManager implements Instrument { + private readonly publisher = this.bus.getPublisher(); + private readonly sub = this.bus.getSubscriber(); + + private keyEventManager?: KeyEventManager; + + private readonly mode = Subject.create('STD'); + private readonly unit = Subject.create('millibar'); + private readonly correction = Subject.create(1013); + + private readonly modeEventKey: keyof BaroEvents = `baro_mode_${this.index}`; + private readonly unitEventKey: keyof BaroEvents = `baro_unit_${this.index}`; + private readonly correctionEventKey: keyof BaroEvents = `baro_correction_${this.index}`; + private readonly preselectChangedEventKey: keyof BaroEvents = `baro_preselect_changed_${this.index}`; + + constructor( + private readonly bus: EventBus, + private readonly index: BaroIndex, + ) { + KeyEventManager.getManager(this.bus).then((manager) => (this.keyEventManager = manager)); + } + + public init(): void { + this.mode.sub((v) => { + SimVar.SetSimVarValue('KOHLSMAN SETTING STD', 'Bool', v === 'STD'); + // FIXME get rid of this when we stop using MSFS altimeter + if (v !== 'STD') { + // put pre-select into altimeter + const correction = this.correction.get(); + const preSelectKohlsman = + 16 * (correction < 100 ? UnitType.HPA.convertFrom(correction, UnitType.IN_HG) : correction); + // FIXME danger, need to setup altimeter 2 in systems.cfg for fo side rather than isis + this.keyEventManager?.triggerKey('KOHLSMAN_SET', true, preSelectKohlsman, this.index); + } + this.publisher.pub(this.modeEventKey, v); + }, true); + + this.unit.sub((v) => this.publisher.pub(this.unitEventKey, v), true); + + this.correction.sub((v) => this.publisher.pub(this.correctionEventKey, v)); + + this.sub.on('hEvent').handle(this.onHEvent.bind(this)); + } + + public onUpdate(): void { + const units = Simplane.getPressureSelectedUnits(); + const mode = Simplane.getPressureSelectedMode(Aircraft.A320_NEO); + const correction = Simplane.getPressureValue(units); + + this.unit.set(units); + this.mode.set(mode); + if (mode !== 'STD') { + this.correction.set(correction); + } + } + + private onHEvent(event: string): void { + if (this.mode.get() !== 'STD') { + return; + } + + switch (event) { + case 'A380X_FCU_BARO_PRESEL_DEC': { + const correction = this.correction.get(); + this.correction.set(correction - (correction > 100 ? 1 : 0.01)); + this.publisher.pub(this.preselectChangedEventKey, null); + break; + } + case 'A380X_FCU_BARO_PRESEL_INC': { + const correction = this.correction.get(); + this.correction.set(correction + (correction > 100 ? 1 : 0.01)); + this.publisher.pub(this.preselectChangedEventKey, null); + break; + } + } + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Managers/HeadingManager.ts b/fbw-a380x/src/systems/instruments/src/FCU/Managers/HeadingManager.ts new file mode 100644 index 00000000000..3ef20270f58 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Managers/HeadingManager.ts @@ -0,0 +1,350 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, Instrument } from '@microsoft/msfs-sdk'; +import { TemporaryHax } from './TemporaryHax'; + +// FIXME port to MSFS avionics framework style +export class HeadingManager extends TemporaryHax implements Instrument { + private backToIdleTimeout = 45000; + private inSelection = false; + private _rotaryEncoderCurrentSpeed = 1; + private _rotaryEncoderMaximumSpeed = 5; + private _rotaryEncoderTimeout = 350; + private _rotaryEncoderIncrement = 0.1; + private _rotaryEncoderPreviousTimestamp = 0; + + private textHDG?: ReturnType; + private textTRK?: ReturnType; + private signDegrees?: ReturnType; + private currentValue?: number; + private selectedValue?: number; + private isSelectedValueActive?: boolean; + private isPreselectionModeActive?: boolean; + private wasHeadingSync?: boolean; + private _resetSelectionTimeout?: ReturnType; + private isActive?: boolean; + private isManagedArmed?: boolean; + private isManagedActive?: boolean; + private isTRKMode?: number | boolean; + private showSelectedHeading?: boolean; + private lightsTest?: boolean; + private trueRef?: boolean; + + constructor(private readonly bus: EventBus) { + super(bus, document.getElementById('Heading')!); + + this.init(); + this.onUpdate(); + } + + public init(): void { + this.textHDG = this.getTextElement('HDG'); + this.textTRK = this.getTextElement('TRK'); + this.signDegrees = this.getTextElement('DEGREES'); + this.currentValue = -1; + this.selectedValue = Simplane.getAltitudeAboveGround() > 1000 ? this.getCurrentHeading() : 0; + this.isSelectedValueActive = true; + this.isPreselectionModeActive = false; + this.wasHeadingSync = false; + this.refresh(true, false, false, false, true, 0, false, true); + } + + private onRotate(): void { + const lateralMode = SimVar.GetSimVarValue('L:A32NX_FMA_LATERAL_MODE', 'Number'); + const isTRKMode = SimVar.GetSimVarValue('L:A32NX_TRK_FPA_MODE_ACTIVE', 'Bool'); + const radioHeight = SimVar.GetSimVarValue('RADIO HEIGHT', 'feet'); + + if ( + !this.inSelection && + (this.isManagedModeActive(lateralMode) || this.isPreselectionAvailable(radioHeight, lateralMode)) + ) { + this.inSelection = true; + if (!this.isSelectedValueActive) { + if (isTRKMode) { + this.selectedValue = this.getCurrentTrack(); + } else { + this.selectedValue = this.getCurrentHeading(); + } + } + } + + this.isSelectedValueActive = true; + + if (this.inSelection && !this.isPreselectionAvailable(radioHeight, lateralMode)) { + this.isPreselectionModeActive = false; + clearTimeout(this._resetSelectionTimeout); + this._resetSelectionTimeout = setTimeout(() => { + this.selectedValue = -1; + this.isSelectedValueActive = false; + this.inSelection = false; + }, this.backToIdleTimeout); + } else { + this.isPreselectionModeActive = true; + } + } + + private getCurrentHeading(): number { + return ((Math.round(SimVar.GetSimVarValue('PLANE HEADING DEGREES MAGNETIC', 'degree')) % 360) + 360) % 360; + } + + private getCurrentTrack(): number { + return ((Math.round(SimVar.GetSimVarValue('GPS GROUND MAGNETIC TRACK', 'degree')) % 360) + 360) % 360; + } + + private onPush(): void { + clearTimeout(this._resetSelectionTimeout); + this.isPreselectionModeActive = false; + this.inSelection = false; + SimVar.SetSimVarValue('K:A32NX.FCU_TO_AP_HDG_PUSH', 'number', 0); + SimVar.SetSimVarValue('K:HEADING_SLOT_INDEX_SET', 'number', 2); + } + + private onPull(): void { + clearTimeout(this._resetSelectionTimeout); + const isTRKMode = SimVar.GetSimVarValue('L:A32NX_TRK_FPA_MODE_ACTIVE', 'Bool'); + if (!this.isSelectedValueActive) { + if (isTRKMode) { + this.selectedValue = this.getCurrentTrack(); + } else { + this.selectedValue = this.getCurrentHeading(); + } + } + this.inSelection = false; + this.isSelectedValueActive = true; + this.isPreselectionModeActive = false; + SimVar.SetSimVarValue('K:A32NX.FCU_TO_AP_HDG_PULL', 'number', 0); + SimVar.SetSimVarValue('K:HEADING_SLOT_INDEX_SET', 'number', 1); + } + + public onUpdate(): void { + const lateralMode = SimVar.GetSimVarValue('L:A32NX_FMA_LATERAL_MODE', 'Number'); + const lateralArmed = SimVar.GetSimVarValue('L:A32NX_FMA_LATERAL_ARMED', 'Number'); + const isTRKMode = SimVar.GetSimVarValue('L:A32NX_TRK_FPA_MODE_ACTIVE', 'Bool'); + const lightsTest = SimVar.GetSimVarValue('L:A32NX_OVHD_INTLT_ANN', 'number') == 0; + const isManagedActive = this.isManagedModeActive(lateralMode); + const isManagedArmed = this.isManagedModeArmed(lateralArmed); + const showSelectedValue = this.isSelectedValueActive || this.inSelection || this.isPreselectionModeActive; + + const isHeadingSync = SimVar.GetSimVarValue('L:A32NX_FCU_HEADING_SYNC', 'Number'); + if (!this.wasHeadingSync && isHeadingSync) { + if (isTRKMode) { + this.selectedValue = this.getCurrentTrack(); + } else { + this.selectedValue = this.getCurrentHeading(); + } + this.isSelectedValueActive = true; + this.onRotate(); + } + this.wasHeadingSync = isHeadingSync; + + this.refresh(true, isManagedArmed, isManagedActive, isTRKMode, showSelectedValue, this.selectedValue, lightsTest); + } + + private refresh( + _isActive: boolean, + _isManagedArmed: boolean, + _isManagedActive: boolean, + _isTRKMode: number | boolean, + _showSelectedHeading: boolean | undefined, + _value: number | undefined, + _lightsTest: boolean, + _force = false, + ): void { + if ( + _isActive != this.isActive || + _isManagedArmed != this.isManagedArmed || + _isManagedActive != this.isManagedActive || + _isTRKMode != this.isTRKMode || + _showSelectedHeading != this.showSelectedHeading || + _value != this.currentValue || + _lightsTest !== this.lightsTest || + _force + ) { + if (_isTRKMode != this.isTRKMode) { + this.onTRKModeChanged(_isTRKMode); + } + if ( + _isManagedArmed && + _isManagedArmed !== this.isManagedArmed && + SimVar.GetSimVarValue('RADIO HEIGHT', 'feet') < 30 + ) { + _value = -1; + _showSelectedHeading = false; + this.selectedValue = _value; + this.isSelectedValueActive = false; + this.isPreselectionModeActive = false; + SimVar.SetSimVarValue('K:HEADING_SLOT_INDEX_SET', 'number', 2); + } + if (_isManagedActive !== this.isManagedActive) { + if (_isManagedActive) { + _value = -1; + _showSelectedHeading = false; + this.selectedValue = _value; + this.isSelectedValueActive = false; + this.isPreselectionModeActive = false; + } else { + _showSelectedHeading = true; + if (!this.isSelectedValueActive) { + this.isSelectedValueActive = true; + if (_isTRKMode) { + _value = this.getCurrentTrack(); + this.selectedValue = _value; + } else { + _value = this.getCurrentHeading(); + this.selectedValue = _value; + } + } + } + } + + // ugly hack because the FG doesn't understand true heading + // FIXME teach the A380 FG about true/mag + // this.trueRef = SimVar.GetSimVarValue('L:A32NX_FMGC_TRUE_REF', 'boolean') > 0; + const valueNum = _value ?? 0; + const correctedHeading = this.trueRef ? (valueNum - SimVar.GetSimVarValue('MAGVAR', 'degree')) % 360 : valueNum; + + SimVar.SetSimVarValue('L:A320_FCU_SHOW_SELECTED_HEADING', 'number', _showSelectedHeading == true ? 1 : 0); + if (_value !== this.currentValue) { + SimVar.SetSimVarValue('L:A32NX_FCU_HEADING_SELECTED', 'Degrees', _value); + SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_HEADING_SELECTED', 'Degrees', correctedHeading); + Coherent.call('HEADING_BUG_SET', 1, Math.max(0, correctedHeading)).catch(console.error); + } else if (this.trueRef) { + SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_HEADING_SELECTED', 'Degrees', correctedHeading); + Coherent.call('HEADING_BUG_SET', 1, Math.max(0, correctedHeading)).catch(console.error); + } + + this.isActive = _isActive; + this.isManagedActive = _isManagedActive; + this.isManagedArmed = _isManagedArmed; + this.isTRKMode = _isTRKMode; + this.showSelectedHeading = _showSelectedHeading; + this.currentValue = _value; + this.setTextElementActive(this.textHDG, !this.isTRKMode); + this.setTextElementActive(this.textTRK, !!this.isTRKMode); + this.lightsTest = _lightsTest; + if (this.lightsTest) { + this.setTextElementActive(this.textHDG, true); + this.setTextElementActive(this.textTRK, true); + this.setElementVisibility(this.signDegrees, true); + this.textValueContent = '.8.8.8'; + return; + } + if ((this.isManagedArmed || this.isManagedActive) && !this.showSelectedHeading) { + this.textValueContent = '---'; + this.setElementVisibility(this.signDegrees, false); + } else { + const value = Math.round(Math.max(this.currentValue ?? 0, 0)) % 360; + this.textValueContent = value.toString().padStart(3, '0'); + this.setElementVisibility(this.signDegrees, true); + } + + SimVar.SetSimVarValue( + 'L:A32NX_FCU_HDG_MANAGED_DASHES', + 'boolean', + (this.isManagedArmed || this.isManagedActive) && !this.showSelectedHeading, + ); + } + } + + private isManagedModeActive(_mode: number): boolean { + return _mode !== 0 && _mode !== 10 && _mode !== 11 && _mode !== 40 && _mode !== 41; + } + + private isManagedModeArmed(_armed: number): boolean { + return _armed > 0; + } + + private isPreselectionAvailable(_radioHeight: number, _mode: number): boolean { + return _radioHeight < 30 || (_mode >= 30 && _mode <= 34) || _mode === 50; + } + + private onTRKModeChanged(_newValue: number | boolean): void { + if (_newValue) { + this.selectedValue = this.calculateTrackForHeading(this.selectedValue ?? 0); + } else { + this.selectedValue = this.calculateHeadingForTrack(this.selectedValue ?? 0); + } + } + + /** + * Calculates the corresponding track for a given heading, assuming it is flown in the current conditions (TAS + wind). + * @param {number} _heading The heading in degrees. + * @returns {number} The corresponding track in degrees. + */ + private calculateTrackForHeading(_heading: number): number { + const trueAirspeed = SimVar.GetSimVarValue('AIRSPEED TRUE', 'Knots'); + if (trueAirspeed < 50) { + return _heading; + } + + const heading = (_heading * Math.PI) / 180; + const windVelocity = SimVar.GetSimVarValue('AMBIENT WIND VELOCITY', 'Knots'); + const windDirection = (SimVar.GetSimVarValue('AMBIENT WIND DIRECTION', 'Degrees') * Math.PI) / 180; + // https://web.archive.org/web/20160302090326/http://williams.best.vwh.net/avform.htm#Wind + const wca = Math.atan2( + windVelocity * Math.sin(heading - windDirection), + trueAirspeed - windVelocity * Math.cos(heading - windDirection), + ); + const track = heading + (wca % (2 * Math.PI)); + return ((((track * 180) / Math.PI) % 360) + 360) % 360; + } + + /** + * Calculates the heading needed to fly a given track in the current conditions (TAS + wind). + * @param {number} _track The track in degrees. + * @returns {number} The corresponding heading in degrees. + */ + private calculateHeadingForTrack(_track: number): number { + const trueAirspeed = SimVar.GetSimVarValue('AIRSPEED TRUE', 'Knots'); + if (trueAirspeed < 50) { + return _track; + } + + const track = (_track * Math.PI) / 180; + const windVelocity = SimVar.GetSimVarValue('AMBIENT WIND VELOCITY', 'Knots'); + const windDirection = (SimVar.GetSimVarValue('AMBIENT WIND DIRECTION', 'Degrees') * Math.PI) / 180; + // https://web.archive.org/web/20160302090326/http://williams.best.vwh.net/avform.htm#Wind + const swc = (windVelocity / trueAirspeed) * Math.sin(windDirection - track); + const heading = track + (Math.asin(swc) % (2 * Math.PI)); + const _heading = ((((heading * 180) / Math.PI) % 360) + 360) % 360; + return Number.isNaN(_heading) ? _track : _heading; + } + + private getRotationSpeed(): number { + if ( + this._rotaryEncoderCurrentSpeed < 1 || + Date.now() - this._rotaryEncoderPreviousTimestamp > this._rotaryEncoderTimeout + ) { + this._rotaryEncoderCurrentSpeed = 1; + } else { + this._rotaryEncoderCurrentSpeed += this._rotaryEncoderIncrement; + } + this._rotaryEncoderPreviousTimestamp = Date.now(); + return Math.min(this._rotaryEncoderMaximumSpeed, Math.floor(this._rotaryEncoderCurrentSpeed)); + } + + protected onEvent(_event: string): void { + if (_event === 'HDG_INC_HEADING') { + this.selectedValue = ((Math.round((this.selectedValue ?? 0) + this.getRotationSpeed()) % 360) + 360) % 360; + this.onRotate(); + } else if (_event === 'HDG_DEC_HEADING') { + this.selectedValue = ((Math.round((this.selectedValue ?? 0) - this.getRotationSpeed()) % 360) + 360) % 360; + this.onRotate(); + } else if (_event === 'HDG_INC_TRACK') { + this.selectedValue = ((Math.round((this.selectedValue ?? 0) + this.getRotationSpeed()) % 360) + 360) % 360; + this.onRotate(); + } else if (_event === 'HDG_DEC_TRACK') { + this.selectedValue = ((Math.round((this.selectedValue ?? 0) - this.getRotationSpeed()) % 360) + 360) % 360; + this.onRotate(); + } else if (_event === 'HDG_PUSH') { + this.onPush(); + } else if (_event === 'HDG_PULL') { + this.onPull(); + } else if (_event === 'HDG_SET') { + this.selectedValue = Math.round(SimVar.GetSimVarValue('L:A320_Neo_FCU_HDG_SET_DATA', 'number') % 360); + this.isSelectedValueActive = true; + this.onRotate(); + } + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Managers/ModeManager.ts b/fbw-a380x/src/systems/instruments/src/FCU/Managers/ModeManager.ts new file mode 100644 index 00000000000..4074fadd8ce --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Managers/ModeManager.ts @@ -0,0 +1,56 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, Instrument } from '@microsoft/msfs-sdk'; +import { TemporaryHax } from './TemporaryHax'; + +// FIXME port to MSFS avionics framework style +export class ModeManager extends TemporaryHax implements Instrument { + private textHDG?: ReturnType; + private textVS?: ReturnType; + private textTRK?: ReturnType; + private textFPA?: ReturnType; + + private isTRKFPADisplayMode?: number | boolean; + private lightsTest?: number | boolean; + + constructor(private readonly bus: EventBus) { + super(bus, document.getElementById('Mode')!); + this.init(); + this.onUpdate(); + } + + public init(): void { + this.textHDG = this.getTextElement('HDG'); + this.textVS = this.getTextElement('VS'); + this.textTRK = this.getTextElement('TRK'); + this.textFPA = this.getTextElement('FPA'); + this.refresh(false, 0, true); + } + + public onUpdate(): void { + if (SimVar.GetSimVarValue('L:A32NX_FCU_MODE_REVERSION_TRK_FPA_ACTIVE', 'Bool')) { + SimVar.SetSimVarValue('L:A32NX_TRK_FPA_MODE_ACTIVE', 'Bool', 0); + } + const _isTRKFPADisplayMode = SimVar.GetSimVarValue('L:A32NX_TRK_FPA_MODE_ACTIVE', 'Bool'); + this.refresh(_isTRKFPADisplayMode, SimVar.GetSimVarValue('L:A32NX_OVHD_INTLT_ANN', 'number') == 0); + } + + private refresh(_isTRKFPADisplayMode: number | boolean, _lightsTest: number | boolean, _force = false): void { + if (_isTRKFPADisplayMode != this.isTRKFPADisplayMode || _lightsTest !== this.lightsTest || _force) { + this.isTRKFPADisplayMode = _isTRKFPADisplayMode; + this.lightsTest = _lightsTest; + if (this.lightsTest) { + this.setTextElementActive(this.textHDG, true); + this.setTextElementActive(this.textVS, true); + this.setTextElementActive(this.textTRK, true); + this.setTextElementActive(this.textFPA, true); + return; + } + this.setTextElementActive(this.textHDG, !this.isTRKFPADisplayMode); + this.setTextElementActive(this.textVS, !this.isTRKFPADisplayMode); + this.setTextElementActive(this.textTRK, !!this.isTRKFPADisplayMode); + this.setTextElementActive(this.textFPA, !!this.isTRKFPADisplayMode); + } + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Managers/SpeedManager.ts b/fbw-a380x/src/systems/instruments/src/FCU/Managers/SpeedManager.ts new file mode 100644 index 00000000000..f5f518a7ce7 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Managers/SpeedManager.ts @@ -0,0 +1,389 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, Instrument } from '@microsoft/msfs-sdk'; +import { TemporaryHax } from './TemporaryHax'; + +// FIXME port to MSFS avionics framework style +export class SpeedManager extends TemporaryHax implements Instrument { + private readonly backToIdleTimeout = 10000; + private readonly MIN_SPEED = 100; + private readonly MAX_SPEED = 399; + private readonly MIN_MACH = 0.1; + private readonly MAX_MACH = 0.99; + + private isActive = false; + private isManaged = false; + private showSelectedSpeed = true; + private currentValue = this.MIN_SPEED; + private selectedValue = this.MIN_SPEED; + private isMachActive: number | boolean = false; + private inSelection = false; + private isSelectedValueActive = false; + private isValidV2 = false; + private isVerticalModeSRS = false; + private isTargetManaged = false; + private _rotaryEncoderCurrentSpeed = 1; + private _rotaryEncoderMaximumSpeed = 10; + private _rotaryEncoderTimeout = 300; + private _rotaryEncoderIncrement = 0.15; + private _rotaryEncoderPreviousTimestamp = 0; + private targetSpeed?: number; + private textSPD?: ReturnType; + private textMACH?: ReturnType; + private textKNOTS?: ReturnType; + private lightsTest?: boolean; + private _resetSelectionTimeout?: ReturnType; + + constructor(private readonly bus: EventBus) { + super(bus, document.getElementById('Speed')!); + + this.init(); + this.onUpdate(); + } + + public init(): void { + this.isValidV2 = false; + this.isVerticalModeSRS = false; + this.selectedValue = this.MIN_SPEED; + this.currentValue = this.MIN_SPEED; + this.targetSpeed = this.MIN_SPEED; + this.isTargetManaged = false; + this.isMachActive = false; + this.textSPD = this.getTextElement('SPD'); + this.textMACH = this.getTextElement('MACH'); + this.textKNOTS = this.getTextElement('KNOTS'); + Coherent.call('AP_SPD_VAR_SET', 0, this.MIN_SPEED).catch(console.error); + SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_SPEED_SELECTED', 'number', this.MIN_SPEED); + SimVar.SetSimVarValue('K:AP_MANAGED_SPEED_IN_MACH_OFF', 'number', 0); + this.onPull(); + } + + public onUpdate(): void { + const isManaged = Simplane.getAutoPilotAirspeedManaged() && this.isTargetManaged; + const showSelectedSpeed = this.inSelection || !isManaged; + const isMachActive = SimVar.GetSimVarValue('AUTOPILOT MANAGED SPEED IN MACH', 'bool'); + const isExpedModeOn = SimVar.GetSimVarValue('L:A32NX_FMA_EXPEDITE_MODE', 'number') === 1; + const isManagedSpeedAvailable = this.isManagedSpeedAvailable(); + + // detect if managed speed should engage due to V2 entry or SRS mode + if (this.shouldEngageManagedSpeed()) { + this.onPush(); + } + // detect if EXPED mode was engaged + if (!isManaged && isExpedModeOn && isManagedSpeedAvailable) { + this.onPush(); + } + // when both AP and FD off -> revert to selected + if (isManaged && !isManagedSpeedAvailable) { + this.onPull(); + } + + // update speed + if (!isManaged && this.selectedValue > 0) { + // mach mode was switched + if (isMachActive != this.isMachActive) { + if (isMachActive || this.selectedValue > 1) { + // KIAS -> Mach + this.selectedValue = this.clampMach( + Math.round(SimVar.GetGameVarValue('FROM KIAS TO MACH', 'number', this.selectedValue) * 100) / 100, + ); + } else { + // Mach -> KIAS + this.selectedValue = this.clampSpeed( + Math.round(SimVar.GetGameVarValue('FROM MACH TO KIAS', 'number', this.selectedValue)), + ); + } + } + // get current target speed + let targetSpeed = + isMachActive || this.selectedValue < 1 + ? SimVar.GetGameVarValue('FROM MACH TO KIAS', 'number', this.selectedValue) + : this.selectedValue; + // clamp speed into valid range + targetSpeed = this.clampSpeed(targetSpeed); + // set target speed + if (targetSpeed !== this.targetSpeed) { + Coherent.call('AP_SPD_VAR_SET', 0, targetSpeed).catch(console.error); + this.targetSpeed = targetSpeed; + } + // detect mismatch + if (Simplane.getAutoPilotAirspeedHoldValue() !== this.targetSpeed) { + Coherent.call('AP_SPD_VAR_SET', 0, targetSpeed).catch(console.error); + } + } else { + this.targetSpeed = -1; + } + + this.refresh( + true, + isManaged, + showSelectedSpeed, + isMachActive, + this.selectedValue, + SimVar.GetSimVarValue('L:A32NX_OVHD_INTLT_ANN', 'number') == 0, + ); + } + + private shouldEngageManagedSpeed(): boolean { + const managedSpeedTarget = SimVar.GetSimVarValue('L:A32NX_SPEEDS_MANAGED_PFD', 'knots'); + const isValidV2 = SimVar.GetSimVarValue('L:AIRLINER_V2_SPEED', 'knots') >= 90; + const isVerticalModeSRS = SimVar.GetSimVarValue('L:A32NX_FMA_VERTICAL_MODE', 'enum') === 40; + + // V2 is entered into MCDU (was not set -> set) + // SRS mode engages (SRS no engaged -> engaged) + let shouldEngage = false; + if ((!this.isValidV2 && isValidV2) || (!this.isVerticalModeSRS && isVerticalModeSRS)) { + shouldEngage = true; + } + + // store state + if (!isValidV2 || managedSpeedTarget >= 90) { + // store V2 state only if managed speed target is valid (to debounce) + this.isValidV2 = isValidV2; + } + this.isVerticalModeSRS = isVerticalModeSRS; + + return shouldEngage; + } + + private isManagedSpeedAvailable(): boolean { + // managed speed is available when flight director or autopilot is engaged, or in approach phase (FMGC flight phase) + return ( + (Simplane.getAutoPilotFlightDirectorActive(1) || + Simplane.getAutoPilotFlightDirectorActive(2) || + SimVar.GetSimVarValue('L:A32NX_AUTOPILOT_ACTIVE', 'number') === 1 || + SimVar.GetSimVarValue('L:A32NX_FMGC_FLIGHT_PHASE', 'number') === 5) && + SimVar.GetSimVarValue('L:A32NX_SPEEDS_MANAGED_PFD', 'knots') >= 90 + ); + } + + private refresh( + _isActive: true, + _isManaged: boolean, + _showSelectedSpeed: boolean, + _machActive: number, + _value: number, + _lightsTest: boolean, + _force = false, + ): void { + if ( + _isActive != this.isActive || + _isManaged != this.isManaged || + _showSelectedSpeed != this.showSelectedSpeed || + _machActive != this.isMachActive || + _value != this.currentValue || + _lightsTest !== this.lightsTest || + _force + ) { + this.isActive = _isActive; + if (_isManaged !== this.isManaged && _isManaged) { + this.inSelection = false; + this.isSelectedValueActive = false; + this.selectedValue = -1; + console.warn('reset due to _isManaged == true'); + } + this.isManaged = _isManaged; + SimVar.SetSimVarValue('L:A32NX_FCU_SPD_MANAGED_DOT', 'boolean', this.isManaged); + if (_showSelectedSpeed !== this.showSelectedSpeed && !_showSelectedSpeed) { + this.inSelection = false; + this.isSelectedValueActive = false; + this.selectedValue = -1; + console.warn('reset due to _showSelectedSpeed == false'); + } + this.showSelectedSpeed = _showSelectedSpeed; + SimVar.SetSimVarValue('L:A32NX_FCU_SPD_MANAGED_DASHES', 'boolean', this.isManaged && !this.showSelectedSpeed); + if (this.currentValue != _value) { + SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_SPEED_SELECTED', 'number', _value); + } + this.currentValue = _machActive ? _value * 100 : _value; + this.isMachActive = _machActive; + this.setTextElementActive(this.textSPD, !_machActive); + this.setTextElementActive(this.textMACH, !!_machActive); + if (this.isMachActive) { + this.setTextElementActive(this.textKNOTS, false); + } else { + this.setTextElementActive(this.textKNOTS, !(this.isManaged && !this.showSelectedSpeed)); + } + this.lightsTest = _lightsTest; + if (this.lightsTest) { + this.textValueContent = '.8.8.8'; + this.setTextElementActive(this.textSPD, true); + this.setTextElementActive(this.textMACH, true); + this.setTextElementActive(this.textKNOTS, true); + return; + } + const value = _machActive ? Math.max(this.currentValue, 0) : Math.max(this.currentValue, 100); + let valueText: string; + if (!_isManaged && this.currentValue > 0) { + if (_machActive) { + valueText = `${value.toFixed(1)}`; + } else { + valueText = Math.round(value).toString().padStart(3, '0'); + } + this.textValueContent = valueText; + } else if (_isManaged || this.currentValue < 0) { + if (_showSelectedSpeed) { + if (_machActive) { + valueText = value.toFixed(1); + } else { + valueText = Math.round(value).toString().padStart(3, '0'); + } + this.textValueContent = valueText; + } else { + this.textValueContent = '---'; + } + } + } + } + + private clampSpeed(value: number): number { + return Utils.Clamp(value, this.MIN_SPEED, this.MAX_SPEED); + } + + private clampMach(value: number): number { + return Utils.Clamp(value, this.MIN_MACH, this.MAX_MACH); + } + + private getCurrentSpeed(): number { + return this.clampSpeed(Math.round(Simplane.getIndicatedSpeed())); + } + + private getCurrentMach(): number { + return this.clampMach(Math.round(Simplane.getMachSpeed() * 100) / 100); + } + + private onRotate(): void { + clearTimeout(this._resetSelectionTimeout); + if (!this.inSelection && this.isManaged) { + this.inSelection = true; + if (!this.isSelectedValueActive) { + if (this.isMachActive) { + this.selectedValue = this.getCurrentMach(); + } else { + this.selectedValue = this.getCurrentSpeed(); + } + } + } + this.isSelectedValueActive = true; + if (this.inSelection) { + this._resetSelectionTimeout = setTimeout(() => { + this.selectedValue = -1; + this.isSelectedValueActive = false; + this.inSelection = false; + }, this.backToIdleTimeout); + } + } + + private onPush(): void { + if (!this.isManagedSpeedAvailable()) { + return; + } + clearTimeout(this._resetSelectionTimeout); + SimVar.SetSimVarValue('K:SPEED_SLOT_INDEX_SET', 'number', 2); + this.inSelection = false; + this.isSelectedValueActive = false; + this.isTargetManaged = true; + } + + private onPull(): void { + clearTimeout(this._resetSelectionTimeout); + if (!this.isSelectedValueActive) { + if (this.isMachActive) { + this.selectedValue = this.getCurrentMach(); + } else { + this.selectedValue = this.getCurrentSpeed(); + } + } + SimVar.SetSimVarValue('K:SPEED_SLOT_INDEX_SET', 'number', 1); + this.inSelection = false; + this.isSelectedValueActive = false; + this.isTargetManaged = false; + } + + private onSwitchSpeedMach(): void { + clearTimeout(this._resetSelectionTimeout); + this.inSelection = false; + this.isSelectedValueActive = false; + if (this.isMachActive) { + SimVar.SetSimVarValue('K:AP_MANAGED_SPEED_IN_MACH_OFF', 'number', 0); + } else { + SimVar.SetSimVarValue('K:AP_MANAGED_SPEED_IN_MACH_ON', 'number', 0); + } + } + + private onPreSelSpeed(isMach: boolean): void { + clearTimeout(this._resetSelectionTimeout); + SimVar.SetSimVarValue('K:SPEED_SLOT_INDEX_SET', 'number', 1); + this.inSelection = false; + this.isSelectedValueActive = false; + this.isTargetManaged = false; + this.isMachActive = isMach; + if (isMach) { + this.selectedValue = SimVar.GetSimVarValue('L:A32NX_MachPreselVal', 'mach'); + SimVar.SetSimVarValue('K:AP_MANAGED_SPEED_IN_MACH_ON', 'number', 1); + } else { + this.selectedValue = SimVar.GetSimVarValue('L:A32NX_SpeedPreselVal', 'knots'); + SimVar.SetSimVarValue('K:AP_MANAGED_SPEED_IN_MACH_OFF', 'number', 1); + } + } + + private getRotationSpeed(): number { + if ( + this._rotaryEncoderCurrentSpeed < 1 || + Date.now() - this._rotaryEncoderPreviousTimestamp > this._rotaryEncoderTimeout + ) { + this._rotaryEncoderCurrentSpeed = 1; + } else { + this._rotaryEncoderCurrentSpeed += this._rotaryEncoderIncrement; + } + this._rotaryEncoderPreviousTimestamp = Date.now(); + return Math.min(this._rotaryEncoderMaximumSpeed, Math.floor(this._rotaryEncoderCurrentSpeed)); + } + + protected onEvent(_event: string): void { + if (_event === 'SPEED_INC') { + // use rotary encoder to speed dialing up / down + if (this.isMachActive) { + this.selectedValue = this.clampMach(this.selectedValue + 0.01); + } else { + this.selectedValue = this.clampSpeed(this.selectedValue + this.getRotationSpeed()); + } + this.onRotate(); + } else if (_event === 'SPEED_DEC') { + // use rotary encoder to speed dialing up / down + if (this.isMachActive) { + this.selectedValue = this.clampMach(this.selectedValue - 0.01); + } else { + this.selectedValue = this.clampSpeed(this.selectedValue - this.getRotationSpeed()); + } + this.onRotate(); + } else if (_event === 'SPEED_PUSH') { + this.onPush(); + } else if (_event === 'SPEED_PULL') { + this.onPull(); + } else if (_event === 'SPEED_SET') { + const value = SimVar.GetSimVarValue('L:A320_Neo_FCU_SPEED_SET_DATA', 'number'); + if (this.isMachActive) { + this.selectedValue = this.clampMach(value / 100.0); + } else { + this.selectedValue = this.clampSpeed(value); + } + this.isSelectedValueActive = true; + this.onRotate(); + } else if (_event === 'SPEED_TOGGLE_SPEED_MACH') { + this.onSwitchSpeedMach(); + } else if (_event === 'USE_PRE_SEL_SPEED') { + this.onPreSelSpeed(false); + } else if (_event === 'USE_PRE_SEL_MACH') { + this.onPreSelSpeed(true); + } else if (_event === 'SPEED_TCAS') { + this.onPull(); + if (this.isMachActive) { + this.selectedValue = this.getCurrentMach(); + } else { + this.selectedValue = this.getCurrentSpeed(); + } + } + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Managers/TemporaryHax.ts b/fbw-a380x/src/systems/instruments/src/FCU/Managers/TemporaryHax.ts new file mode 100644 index 00000000000..b6d78060ee8 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Managers/TemporaryHax.ts @@ -0,0 +1,72 @@ +import { EventBus, HEvent } from '@microsoft/msfs-sdk'; + +/** This is a small class to fill in the old NavSystem code until all of the windows are ported to MSFS avionics framework. */ +export abstract class TemporaryHax { + private readonly tempSub = this.tempBus.getSubscriber(); + + protected textValue: Element | null; + + constructor( + private readonly tempBus: EventBus, + protected divRef: HTMLElement, + ) { + this.textValue = this.getTextElement('Value'); + + this.tempSub.on('hEvent').handle((event) => { + if (event.startsWith('A320_Neo_FCU_')) { + this.onEvent(event.substring(13)); + } + }); + } + + protected onEvent(_event: string): void {} + + protected abstract init(): void; + + protected getDivElement(_name: string) { + if (this.divRef != null) { + return this.divRef.querySelector(`#${_name}`); + } + } + + protected set textValueContent(_textContent: string | null) { + if (this.textValue != null) { + this.textValue.textContent = _textContent; + this.textValue.innerHTML = this.textValue.innerHTML.replace('{sp}', ' '); + } + } + + protected getElement(_type: string, _name: string): HTMLElement | null { + if (this.divRef != null) { + const allText = this.divRef.getElementsByTagName(_type); + if (allText != null) { + for (let i = 0; i < allText.length; ++i) { + if (allText[i].id == _name) { + return allText[i] as HTMLElement; + } + } + } + } + return null; + } + + protected getTextElement(_name: string) { + return this.getElement('text', _name); + } + + protected setTextElementActive(_text?: Element | null, _active?: boolean, _baro?: boolean) { + if (_text !== undefined && _text !== null) { + _text.setAttribute('class', `Common ${_active ? 'Active' : 'Inactive'} ${_baro ? 'BaroValue' : ''}`); + } + } + + protected setElementVisibility(_element?: HTMLElement | null, _show?: boolean) { + if (_element !== undefined && _element !== null) { + _element.style.display = _show ? 'block' : 'none'; + } + } + + reboot() { + this.init(); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Managers/VerticalSpeedManager.ts b/fbw-a380x/src/systems/instruments/src/FCU/Managers/VerticalSpeedManager.ts new file mode 100644 index 00000000000..3a1545337f1 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Managers/VerticalSpeedManager.ts @@ -0,0 +1,337 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, Instrument } from '@microsoft/msfs-sdk'; +import { TemporaryHax } from './TemporaryHax'; +import { MathUtils } from '@flybywiresim/fbw-sdk'; + +enum A320_Neo_FCU_VSpeed_State { + Idle = 0, + Zeroing = 1, + Selecting = 2, + Flying = 3, +} + +// FIXME port to MSFS avionics framework style +export class VerticalSpeedManager extends TemporaryHax implements Instrument { + private forceUpdate = true; + private ABS_MINMAX_FPA = 9.9; + private ABS_MINMAX_VS = 6000; + private backToIdleTimeout = 45000; + private previousVerticalMode = 0; + + private _currentState?: number; + private textVS?: ReturnType; + private textFPA?: ReturnType; + private isActive?: boolean; + private isFPAMode?: boolean; + private selectedVs?: number; + private selectedFpa?: number; + private _resetSelectionTimeout?: ReturnType; + private currentValue?: number; + /** @deprecated not written anywhere!! */ + private currentVs: undefined; + private lightsTest?: number | boolean; + + constructor(private readonly bus: EventBus) { + super(bus, document.getElementById('VerticalSpeed')!); + + this.init(); + this.onUpdate(); + } + + private get currentState(): number | undefined { + return this._currentState; + } + + private set currentState(v) { + this._currentState = v; + SimVar.SetSimVarValue('L:A320_NE0_FCU_STATE', 'number', this.currentState); + } + + public init(): void { + this.textVS = this.getTextElement('VS'); + this.textFPA = this.getTextElement('FPA'); + this.isActive = false; + this.isFPAMode = false; + this._enterIdleState(); + this.selectedVs = 0; + this.selectedFpa = 0; + this.refresh(false, false, 0, 0, true); + } + + private onPush(): void { + const mode = SimVar.GetSimVarValue('L:A32NX_FMA_VERTICAL_MODE', 'Number'); + if (mode >= 32 && mode <= 34) { + return; + } + clearTimeout(this._resetSelectionTimeout); + this.forceUpdate = true; + + this.currentState = A320_Neo_FCU_VSpeed_State.Zeroing; + + this.selectedVs = 0; + this.selectedFpa = 0; + + SimVar.SetSimVarValue('K:A32NX.FCU_TO_AP_VS_PUSH', 'number', 0); + } + + private onRotate(): void { + if ( + this.currentState === A320_Neo_FCU_VSpeed_State.Idle || + this.currentState === A320_Neo_FCU_VSpeed_State.Selecting + ) { + clearTimeout(this._resetSelectionTimeout); + this.forceUpdate = true; + + if (this.currentState === A320_Neo_FCU_VSpeed_State.Idle) { + this.selectedVs = this.getCurrentVerticalSpeed(); + this.selectedFpa = this.getCurrentFlightPathAngle(); + } + + this.currentState = A320_Neo_FCU_VSpeed_State.Selecting; + + this._resetSelectionTimeout = setTimeout(() => { + this.selectedVs = 0; + this.selectedFpa = 0; + this.currentState = A320_Neo_FCU_VSpeed_State.Idle; + this.forceUpdate = true; + }, this.backToIdleTimeout); + } else if (this.currentState === A320_Neo_FCU_VSpeed_State.Zeroing) { + this.currentState = A320_Neo_FCU_VSpeed_State.Flying; + this.forceUpdate = true; + } + } + + private onPull(): void { + clearTimeout(this._resetSelectionTimeout); + this.forceUpdate = true; + + if (this.currentState === A320_Neo_FCU_VSpeed_State.Idle) { + if (this.isFPAMode) { + this.selectedFpa = this.getCurrentFlightPathAngle(); + } else { + this.selectedVs = this.getCurrentVerticalSpeed(); + } + } + + SimVar.SetSimVarValue('K:A32NX.FCU_TO_AP_VS_PULL', 'number', 0); + } + + private getCurrentFlightPathAngle(): number { + return this.calculateAngleForVerticalSpeed(Simplane.getVerticalSpeed()); + } + + private getCurrentVerticalSpeed(): number { + return Utils.Clamp(Math.round(Simplane.getVerticalSpeed() / 100) * 100, -this.ABS_MINMAX_VS, this.ABS_MINMAX_VS); + } + + private _enterIdleState(): void { + this.selectedVs = 0; + this.selectedFpa = 0; + this.currentState = A320_Neo_FCU_VSpeed_State.Idle; + this.forceUpdate = true; + } + + public onUpdate(): void { + const lightsTest = SimVar.GetSimVarValue('L:A32NX_OVHD_INTLT_ANN', 'number') == 0; + const isFPAMode = SimVar.GetSimVarValue('L:A32NX_TRK_FPA_MODE_ACTIVE', 'Bool'); + const verticalMode = SimVar.GetSimVarValue('L:A32NX_FMA_VERTICAL_MODE', 'Number'); + + if (this.previousVerticalMode != verticalMode && verticalMode !== 14 && verticalMode !== 15) { + clearTimeout(this._resetSelectionTimeout); + this._enterIdleState(); + } + + if ( + this.currentState !== A320_Neo_FCU_VSpeed_State.Flying && + this.currentState !== A320_Neo_FCU_VSpeed_State.Zeroing && + (verticalMode === 14 || verticalMode === 15) + ) { + clearTimeout(this._resetSelectionTimeout); + this.forceUpdate = true; + const isModeReversion = SimVar.GetSimVarValue('L:A32NX_FCU_MODE_REVERSION_ACTIVE', 'Number'); + const modeReversionTargetFpm = SimVar.GetSimVarValue('L:A32NX_FCU_MODE_REVERSION_TARGET_FPM', 'Number'); + if (isFPAMode) { + if (isModeReversion === 1) { + this.currentState = A320_Neo_FCU_VSpeed_State.Flying; + const modeReversionTargetFpa = this.calculateAngleForVerticalSpeed(modeReversionTargetFpm); + this.selectedFpa = Utils.Clamp( + Math.round(modeReversionTargetFpa * 10) / 10, + -this.ABS_MINMAX_FPA, + this.ABS_MINMAX_FPA, + ); + } else if (this.selectedFpa !== 0) { + this.currentState = A320_Neo_FCU_VSpeed_State.Flying; + } else { + this.currentState = A320_Neo_FCU_VSpeed_State.Zeroing; + } + } else if (isModeReversion === 1) { + this.currentState = A320_Neo_FCU_VSpeed_State.Flying; + this.selectedVs = Utils.Clamp( + Math.round(modeReversionTargetFpm / 100) * 100, + -this.ABS_MINMAX_VS, + this.ABS_MINMAX_VS, + ); + } else if (this.currentVs !== 0) { + this.currentState = A320_Neo_FCU_VSpeed_State.Flying; + } else { + this.currentState = A320_Neo_FCU_VSpeed_State.Zeroing; + } + } + + if (isFPAMode) { + this.refresh(true, true, this.selectedFpa, lightsTest, this.forceUpdate); + } else { + this.refresh(true, false, this.selectedVs, lightsTest, this.forceUpdate); + } + + this.forceUpdate = false; + this.previousVerticalMode = verticalMode; + } + + private refresh( + _isActive: boolean, + _isFPAMode: boolean, + _value: number | undefined, + _lightsTest: boolean | number, + _force = false, + ): void { + if ( + _isActive != this.isActive || + _isFPAMode != this.isFPAMode || + _value != this.currentValue || + _lightsTest !== this.lightsTest || + _force + ) { + if (this.isFPAMode != _isFPAMode) { + this.onFPAModeChanged(_isFPAMode); + } + if (this.currentValue !== _value) { + if (_isFPAMode) { + SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_FPA_SELECTED', 'Degree', this.selectedFpa); + SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_VS_SELECTED', 'feet per minute', 0); + } else { + SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_FPA_SELECTED', 'Degree', 0); + SimVar.SetSimVarValue('L:A32NX_AUTOPILOT_VS_SELECTED', 'feet per minute', this.selectedVs); + } + } + this.isActive = _isActive; + this.isFPAMode = _isFPAMode; + this.currentValue = _value; + this.lightsTest = _lightsTest; + if (this.lightsTest) { + this.setTextElementActive(this.textVS, true); + this.setTextElementActive(this.textFPA, true); + this.textValueContent = '+8.888'; + return; + } + this.setTextElementActive(this.textVS, !this.isFPAMode); + this.setTextElementActive(this.textFPA, this.isFPAMode); + + const currentValue = this.currentValue ?? 0; + if (this.isActive && this.currentState != A320_Neo_FCU_VSpeed_State.Idle) { + const sign = currentValue < 0 ? '-' : '+'; + if (this.isFPAMode) { + this.textValueContent = `${sign}${MathUtils.round(Math.abs(currentValue), 0.1).toFixed(1)}`; + } else if (this.currentState === A320_Neo_FCU_VSpeed_State.Zeroing) { + this.textValueContent = '~00oo'; + } else { + let value = Math.floor(currentValue); + value = Math.abs(value); + this.textValueContent = `${ + sign + + Math.floor(value * 0.01) + .toString() + .padStart(2, '0') + }oo`; + } + SimVar.SetSimVarValue('L:A32NX_FCU_VS_MANAGED', 'boolean', false); + } else { + this.textValueContent = '~----'; + SimVar.SetSimVarValue('L:A32NX_FCU_VS_MANAGED', 'boolean', true); + } + } + } + + protected onEvent(_event: string): void { + if (_event === 'VS_INC_VS') { + this.selectedVs = Utils.Clamp(Math.round((this.selectedVs ?? 0) + 100), -this.ABS_MINMAX_VS, this.ABS_MINMAX_VS); + this.onRotate(); + } else if (_event === 'VS_DEC_VS') { + this.selectedVs = Utils.Clamp(Math.round((this.selectedVs ?? 0) - 100), -this.ABS_MINMAX_VS, this.ABS_MINMAX_VS); + this.onRotate(); + } else if (_event === 'VS_INC_FPA') { + this.selectedFpa = Utils.Clamp( + Math.round(((this.selectedFpa ?? 0) + 0.1) * 10) / 10, + -this.ABS_MINMAX_FPA, + this.ABS_MINMAX_FPA, + ); + this.onRotate(); + } else if (_event === 'VS_DEC_FPA') { + this.selectedFpa = Utils.Clamp( + Math.round(((this.selectedFpa ?? 0) - 0.1) * 10) / 10, + -this.ABS_MINMAX_FPA, + this.ABS_MINMAX_FPA, + ); + this.onRotate(); + } else if (_event === 'VS_PUSH') { + this.onPush(); + } else if (_event === 'VS_PULL') { + this.onPull(); + } else if (_event === 'VS_SET') { + const value = SimVar.GetSimVarValue('L:A320_Neo_FCU_VS_SET_DATA', 'number'); + if (this.isFPAMode) { + if (Math.abs(value) < 100 || value == 0) { + this.selectedFpa = Utils.Clamp(Math.round(value) / 10, -this.ABS_MINMAX_FPA, this.ABS_MINMAX_FPA); + this.currentState = A320_Neo_FCU_VSpeed_State.Selecting; + this.onRotate(); + } + } else if (Math.abs(value) >= 100 || value == 0) { + this.selectedVs = Utils.Clamp(Math.round(value), -this.ABS_MINMAX_VS, this.ABS_MINMAX_VS); + this.currentState = A320_Neo_FCU_VSpeed_State.Selecting; + this.onRotate(); + } + } + } + + private onFPAModeChanged(_newValue: boolean): void { + if (_newValue) { + this.selectedFpa = this.calculateAngleForVerticalSpeed(this.selectedVs); + } else { + this.selectedVs = this.calculateVerticalSpeedForAngle(this.selectedFpa); + } + } + + /** + * Calculates the vertical speed needed to fly a flight path angle at the current ground speed. + * @param {number} _angle The flight path angle in degrees. + * @returns {number} The corresponding vertical speed in feet per minute. + */ + private calculateVerticalSpeedForAngle(_angle?: number): number { + if (_angle === undefined || _angle === 0) { + return 0; + } + const _groundSpeed = SimVar.GetSimVarValue('GPS GROUND SPEED', 'Meters per second'); + const groundSpeed = _groundSpeed * 3.28084 * 60; // Now in feet per minute. + const angle = (_angle * Math.PI) / 180; // Now in radians. + const verticalSpeed = Math.tan(angle) * groundSpeed; + return Utils.Clamp(Math.round(verticalSpeed / 100) * 100, -this.ABS_MINMAX_VS, this.ABS_MINMAX_VS); + } + + /** + * Calculates the flight path angle for a given vertical speed, assuming it is flown at the current ground speed. + * @param {number} verticalSpeed The flight path angle in feet per minute. + * @returns {number} The corresponding flight path angle in degrees. + */ + private calculateAngleForVerticalSpeed(verticalSpeed?: number): number { + if (verticalSpeed === undefined || Math.abs(verticalSpeed) < 10) { + return 0; + } + const _groundSpeed = SimVar.GetSimVarValue('GPS GROUND SPEED', 'Meters per second'); + const groundSpeed = _groundSpeed * 3.28084 * 60; // Now in feet per minute. + const angle = Math.atan(verticalSpeed / groundSpeed); + const _angle = (angle * 180) / Math.PI; + return Utils.Clamp(Math.round(_angle * 10) / 10, -this.ABS_MINMAX_FPA, this.ABS_MINMAX_FPA); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/Publishers/FcuPublisher.ts b/fbw-a380x/src/systems/instruments/src/FCU/Publishers/FcuPublisher.ts new file mode 100644 index 00000000000..06a6a82ab5c --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/Publishers/FcuPublisher.ts @@ -0,0 +1,43 @@ +// Copyright (c) 2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { NavAidMode } from '@flybywiresim/fbw-sdk'; +import { EventBus, PublishPacer, SimVarPublisher, SimVarPublisherEntry, SimVarValueType } from '@microsoft/msfs-sdk'; + +interface FcuBaseEvents { + /** Whether TRK/FPA mode is active. */ + fcu_trk_fpa_active: boolean; + fcu_left_navaid_mode: NavAidMode; + fcu_right_navaid_mode: NavAidMode; +} + +type IndexedTopics = 'fcu_left_navaid_mode' | 'fcu_right_navaid_mode'; + +type FcuIndexedEvents = { + [P in keyof Pick as `${P}_${1 | 2}`]: FcuBaseEvents[P]; +}; + +export interface FcuEvents extends FcuBaseEvents, FcuIndexedEvents {} + +export class FcuPublisher extends SimVarPublisher { + /** + * Create an FCU publisher + * @param bus The EventBus to publish to + * @param pacer An optional pacer to use to control the rate of publishing + */ + public constructor(bus: EventBus, pacer?: PublishPacer) { + const simvars = new Map>([ + ['fcu_trk_fpa_active', { name: `L:A32NX_TRK_FPA_MODE_ACTIVE`, type: SimVarValueType.Bool }], + [ + 'fcu_left_navaid_mode', + { name: `L:A32NX_EFIS_L_NAVAID_#index#_MODE`, type: SimVarValueType.Enum, indexed: true }, + ], + [ + 'fcu_right_navaid_mode', + { name: `L:A32NX_EFIS_R_NAVAID_#index#_MODE`, type: SimVarValueType.Enum, indexed: true }, + ], + ]); + + super(simvars, bus, pacer); + } +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/config.json b/fbw-a380x/src/systems/instruments/src/FCU/config.json new file mode 100644 index 00000000000..1a9eb323ca1 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/config.json @@ -0,0 +1,3 @@ +{ + "isInteractive": false +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/definitions.scss b/fbw-a380x/src/systems/instruments/src/FCU/definitions.scss new file mode 100644 index 00000000000..ee0387bf968 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/definitions.scss @@ -0,0 +1,20 @@ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +$display-colour-background: hsl(248, 94%, 53%); +$display-colour: hsl(0, 0%, 100%); +$display-colour-inactive: hsl(248, 94%, 48%); + +@font-face { + font-family: "Poppins-SemiBold"; + src: url("/Fonts/fbw-a380x/Poppins-SemiBold.ttf") format("truetype"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "Digital"; + src: url("/Fonts/fbw-a380x/A380X_FCU.ttf") format("truetype"); + font-weight: 900; + font-style: normal; +} diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/A380_FCU.css b/fbw-a380x/src/systems/instruments/src/FCU/style.scss similarity index 56% rename from fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/A380_FCU.css rename to fbw-a380x/src/systems/instruments/src/FCU/style.scss index e43d58aa62f..74d7e17372a 100644 --- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/html_ui/Pages/VCockpit/Instruments/LegacyA380X/FCU/A380_FCU.css +++ b/fbw-a380x/src/systems/instruments/src/FCU/style.scss @@ -1,5 +1,7 @@ -/* Copyright (c) 2023-2024 FlyByWire Simulations */ -/* SPDX-License-Identifier: GPL-3.0 */ +// Copyright (c) 2023-2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +@import "./definitions.scss"; :root { --bodyHeightScale: 1; @@ -36,45 +38,44 @@ display: none; } +:root text.Common { + width: 100%; + height: 100%; +} -@font-face { - font-family: "Poppins-SemiBold"; - src: url("/Fonts/fbw-a380x/Poppins-SemiBold.ttf") format("truetype"); - font-weight: normal; - font-style: normal; +.Invisible { + visibility: hidden; } -@font-face { - font-family: "Digital"; - src: url("/Fonts/fbw-a380x/A380X_FCU.ttf") format("truetype"); - font-weight: 900; - font-style: normal; +text.Label { + font-family: "Poppins-SemiBold"; + font-size: 230px; + fill: $display-colour-inactive; } -:root { - --main-display-colour-background: hsl(248, 94%, 53%); - --main-display-colour: hsl(0, 0%, 100%); - --main-display-colour-inactive: hsl(248, 94%, 48%); +.Baro text.Label { + font-size: 210px; } -:root text.Common { - width: 100%; - height: 100%; +text.Label.Visible { + fill: $display-colour; } +// deprecated :root text.Active { font-family: "Poppins-SemiBold"; font-size: 230px; - fill: var(--main-display-colour); + fill: $display-colour; } :root text.Active.BaroValue { font-size: 210px; } +// deprecated :root text.Inactive { font-family: "Poppins-SemiBold"; font-size: 230px; - fill: var(--main-display-colour-inactive); + fill: $display-colour-inactive; } :root text.Inactive.BaroValue { font-size: 210px; @@ -84,11 +85,11 @@ font-family: Digital; font-size:440px; text-anchor: start; - fill: var(--main-display-colour); + fill: $display-colour; letter-spacing: 0px; } -:root text.BaroValue { +:root .Baro text.Value { font-size: 400px; } @@ -99,39 +100,39 @@ } :root line { - stroke: var(--main-display-colour); + stroke: $display-colour; stroke-width: 5; } :root circle { - fill: var(--main-display-colour); + fill: $display-colour; } -a380-fcu-element { +#FCU_CONTENT { width: 100%; height: 100%; - background-color: var(--main-display-colour-background); + background-color: $display-colour-background; font-family: "Poppins-SemiBold"; position: relative; overflow: hidden; } -a380-fcu-element #Mainframe { +#FCU_CONTENT #Mainframe { width: 100%; height: 100%; display: block; position: relative; } -a380-fcu-element #Mainframe #LargeScreen { +#FCU_CONTENT #Mainframe #LargeScreen { width: 100%; height: 16%; display: block; position: absolute; - background-color: var(--main-display-colour-background); + background-color: $display-colour-background; } -a380-fcu-element #Mainframe #LargeScreen #Speed { +#FCU_CONTENT #Mainframe #LargeScreen #Speed { width: 30%; height: 100%; left: 5%; @@ -139,53 +140,79 @@ a380-fcu-element #Mainframe #LargeScreen #Speed { position: absolute; } -a380-fcu-element #Mainframe #LargeScreen #Heading { +#FCU_CONTENT #Mainframe #LargeScreen #Heading { width: 30%; height: 100%; left: 50%; position: absolute; } -a380-fcu-element #Mainframe #LargeScreen #Mode { +#FCU_CONTENT #Mainframe #LargeScreen #Mode { width: 20%; height: 100%; left: 80%; position: absolute; } -a380-fcu-element #Mainframe #LargeScreen #AltVS { +#FCU_CONTENT #Mainframe #LargeScreen #AltVS { width: 40%; height: 100%; left: 120%; position: absolute; } -a380-fcu-element #Mainframe #LargeScreen #AltVS #Altitude { +#FCU_CONTENT #Mainframe #LargeScreen #AltVS #Altitude { width: 100%; height: 100%; left: 0%; position: absolute; } -a380-fcu-element #Mainframe #LargeScreen #AltVS #VerticalSpeed { +#FCU_CONTENT #Mainframe #LargeScreen #AltVS #VerticalSpeed { width: 100%; height: 100%; left: 95%; position: absolute; } -a380-fcu-element #Mainframe #SmallScreen { +#FCU_CONTENT #Mainframe #SmallScreen { width: 27%; height: 17%; display: block; position: absolute; top: 16%; - background-color: var(--main-display-colour-background); + background-color: $display-colour-background; } -a380-fcu-element #Mainframe #SmallScreen #Selected, -a380-fcu-element #Mainframe #SmallScreen #Standard { +#FCU_CONTENT #Mainframe #SmallScreen #Selected, +#FCU_CONTENT #Mainframe #SmallScreen #Standard { width: 100%; height: 100%; top: 0; } + +.NdData { + position: absolute; +} + +.NdData.LeftSide { + top: 1750px; + left: 240px; +} + +.NdData.RightSide { + top: 2900px; + left: 300px; +} + +.NdData .TopRow { + position: absolute; + top: 0; + left: 0; +} + +.NdData .BottomRow { + position: absolute; + top: 550px; + left: 0; +} diff --git a/fbw-a380x/src/systems/instruments/src/FCU/tsconfig.json b/fbw-a380x/src/systems/instruments/src/FCU/tsconfig.json new file mode 100644 index 00000000000..097a56915da --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/FCU/tsconfig.json @@ -0,0 +1,17 @@ +{ + "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 */ + } +} diff --git a/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/providers/FGDataPublisher.ts b/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/providers/FGDataPublisher.ts index a19a963d4b2..9289daf67cf 100644 --- a/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/providers/FGDataPublisher.ts +++ b/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/providers/FGDataPublisher.ts @@ -1,9 +1,28 @@ import { EventBus, SimVarPublisher, SimVarValueType } from '@microsoft/msfs-sdk'; -import { LateralMode } from '@shared/autopilot'; +import { LateralMode, VerticalMode } from '@shared/autopilot'; + +export enum FgLateralArmedFlags { + None = 0, + Nav = 1 << 0, + Loc = 1 << 1, +} + +export enum FgVerticalArmedFlags { + None = 0, + Alt = 1 << 0, + AltCst = 1 << 1, + Clb = 1 << 2, + Des = 1 << 3, + Gs = 1 << 4, + Final = 1 << 5, + Tcas = 1 << 6, +} export interface FGVars { 'fg.fma.lateralMode': LateralMode; - 'fg.fma.lateralArmedBitmask': number; + 'fg.fma.lateralArmedBitmask': FgLateralArmedFlags; + 'fg.fma.verticalMode': VerticalMode; + 'fg.fma.verticalArmedBitmask': FgVerticalArmedFlags; } export class FGDataPublisher extends SimVarPublisher { @@ -12,6 +31,8 @@ export class FGDataPublisher extends SimVarPublisher { new Map([ ['fg.fma.lateralMode', { name: 'L:A32NX_FMA_LATERAL_MODE', type: SimVarValueType.Number }], ['fg.fma.lateralArmedBitmask', { name: 'L:A32NX_FMA_LATERAL_ARMED', type: SimVarValueType.Number }], + ['fg.fma.verticalMode', { name: 'L:A32NX_FMA_VERTICAL_MODE', type: SimVarValueType.Number }], + ['fg.fma.verticalArmedBitmask', { name: 'L:A32NX_FMA_VERTICAL_ARMED', type: SimVarValueType.Number }], ]), bus, ); diff --git a/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/providers/OverheadPublisher.ts b/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/providers/OverheadPublisher.ts new file mode 100644 index 00000000000..11d74fd0041 --- /dev/null +++ b/fbw-a380x/src/systems/instruments/src/MsfsAvionicsCommon/providers/OverheadPublisher.ts @@ -0,0 +1,30 @@ +// Copyright (c) 2024 FlyByWire Simulations +// SPDX-License-Identifier: GPL-3.0 + +import { EventBus, SimVarPublisher, SimVarPublisherEntry, SimVarValueType } from '@microsoft/msfs-sdk'; + +export enum AnnLightTestState { + Test = 0, + Bright = 1, + Dim = 2, +} + +export interface OverheadEvents { + ovhd_ann_lt_test_switch: AnnLightTestState; + ovhd_ann_lt_test_active: boolean; +} + +export class OverheadPublisher extends SimVarPublisher { + constructor(bus: EventBus) { + super( + new Map>([ + ['ovhd_ann_lt_test_switch', { name: 'L:A32NX_OVHD_INTLT_ANN', type: SimVarValueType.Enum }], + [ + 'ovhd_ann_lt_test_active', + { name: 'L:A32NX_OVHD_INTLT_ANN', type: SimVarValueType.Enum, map: (v) => v === AnnLightTestState.Test }, + ], + ]), + bus, + ); + } +} diff --git a/fbw-common/src/systems/shared/src/index.ts b/fbw-common/src/systems/shared/src/index.ts index 1e9799b2d8e..36b435b4daa 100644 --- a/fbw-common/src/systems/shared/src/index.ts +++ b/fbw-common/src/systems/shared/src/index.ts @@ -14,6 +14,7 @@ export * from './GenericDataListenerSync'; export * from './MathUtils'; export * from './PathVector'; export * from './PilotSeat'; +export * from './publishers'; export * from './RadioTypes'; export * from './RadioUtils'; export * from './RunwayUtils'; diff --git a/fbw-common/src/systems/shared/src/publishers/Msfs/MsfsAutopilotAssistancePublisher.ts b/fbw-common/src/systems/shared/src/publishers/Msfs/MsfsAutopilotAssistancePublisher.ts new file mode 100644 index 00000000000..5f29c7cf060 --- /dev/null +++ b/fbw-common/src/systems/shared/src/publishers/Msfs/MsfsAutopilotAssistancePublisher.ts @@ -0,0 +1,51 @@ +import { + EventBus, + IndexedEventType, + PublishPacer, + SimVarPublisher, + SimVarPublisherEntry, + SimVarValueType, +} from '@microsoft/msfs-sdk'; + +interface MsfsAutopilotAssistanceBaseEvents { + /** Get the slot index which the altitude hold mode will track when captured, in feet. */ + msfs_autopilot_altitude_lock_var: number; +} + +type IndexedTopics = 'msfs_autopilot_altitude_lock_var'; + +type MsfsAutopilotAssitanceIndexedEvents = { + [P in keyof Pick< + MsfsAutopilotAssistanceBaseEvents, + IndexedTopics + > as IndexedEventType

]: MsfsAutopilotAssistanceBaseEvents[P]; +}; + +/** + * Events for simvars listed on https://docs.flightsimulator.com/html/Programming_Tools/SimVars/Aircraft_SimVars/Aircraft_AutopilotAssistant_Variables.htm. + * Event names are the same as the simvar names, with msfs_ prefix, and index as suffix for indexed simvars. + */ +export interface MsfsAutopilotAssistanceEvents + extends MsfsAutopilotAssistanceBaseEvents, + MsfsAutopilotAssitanceIndexedEvents {} + +/** + * Publisher for simvars listed on https://docs.flightsimulator.com/html/Programming_Tools/SimVars/Aircraft_SimVars/Aircraft_AutopilotAssistant_Variables.htm. + */ +export class MsfsAutopilotAssitancePublisher extends SimVarPublisher { + /** + * Create a publisher. + * @param bus The EventBus to publish to + * @param pacer An optional pacer to use to control the rate of publishing + */ + public constructor(bus: EventBus, pacer?: PublishPacer) { + const simvars = new Map>([ + [ + 'msfs_autopilot_altitude_lock_var', + { name: `AUTOPILOT ALTITUDE LOCK VAR:#index#`, type: SimVarValueType.Feet, indexed: true }, + ], + ]); + + super(simvars, bus, pacer); + } +} diff --git a/fbw-common/src/systems/shared/src/publishers/Msfs/MsfsRadioNavigationPublisher.ts b/fbw-common/src/systems/shared/src/publishers/Msfs/MsfsRadioNavigationPublisher.ts new file mode 100644 index 00000000000..f1cfe74f0fa --- /dev/null +++ b/fbw-common/src/systems/shared/src/publishers/Msfs/MsfsRadioNavigationPublisher.ts @@ -0,0 +1,46 @@ +import { + EventBus, + IndexedEventType, + PublishPacer, + SimVarPublisher, + SimVarPublisherEntry, + SimVarValueType, +} from '@microsoft/msfs-sdk'; + +interface MsfsRadioNavigationBaseEvents { + /** Radar altitude in feet. */ + msfs_radio_height: number; +} + +type IndexedTopics = null; + +type MsfsAutopilotAssitanceIndexedEvents = { + [P in keyof Pick< + MsfsRadioNavigationBaseEvents, + IndexedTopics + > as IndexedEventType

]: MsfsRadioNavigationBaseEvents[P]; +}; + +/** + * Events for simvars listed on https://docs.flightsimulator.com/html/Programming_Tools/SimVars/Aircraft_SimVars/Aircraft_RadioNavigation_Variables.htm. + * Event names are the same as the simvar names, with msfs_ prefix, and index as suffix for indexed simvars. + */ +export interface MsfsRadioNavigationEvents extends MsfsRadioNavigationBaseEvents, MsfsAutopilotAssitanceIndexedEvents {} + +/** + * Publisher for simvars listed on https://docs.flightsimulator.com/html/Programming_Tools/SimVars/Aircraft_SimVars/Aircraft_RadioNavigation_Variables.htm. + */ +export class MsfsRadioNavigationPublisher extends SimVarPublisher { + /** + * Create a publisher. + * @param bus The EventBus to publish to + * @param pacer An optional pacer to use to control the rate of publishing + */ + public constructor(bus: EventBus, pacer?: PublishPacer) { + const simvars = new Map>([ + ['msfs_radio_height', { name: `RADIO HEIGHT`, type: SimVarValueType.Feet }], + ]); + + super(simvars, bus, pacer); + } +} diff --git a/fbw-common/src/systems/shared/src/publishers/Msfs/index.ts b/fbw-common/src/systems/shared/src/publishers/Msfs/index.ts new file mode 100644 index 00000000000..bc41a2e7615 --- /dev/null +++ b/fbw-common/src/systems/shared/src/publishers/Msfs/index.ts @@ -0,0 +1,2 @@ +export * from './MsfsAutopilotAssistancePublisher'; +export * from './MsfsRadioNavigationPublisher'; diff --git a/fbw-common/src/systems/shared/src/publishers/index.ts b/fbw-common/src/systems/shared/src/publishers/index.ts new file mode 100644 index 00000000000..f87ad7264ad --- /dev/null +++ b/fbw-common/src/systems/shared/src/publishers/index.ts @@ -0,0 +1 @@ +export * from './Msfs'; diff --git a/fbw-common/src/typings/fs-base-ui/html_ui/JS/SimPlane.d.ts b/fbw-common/src/typings/fs-base-ui/html_ui/JS/SimPlane.d.ts index 38adea318c7..debe34f456d 100644 --- a/fbw-common/src/typings/fs-base-ui/html_ui/JS/SimPlane.d.ts +++ b/fbw-common/src/typings/fs-base-ui/html_ui/JS/SimPlane.d.ts @@ -273,9 +273,9 @@ declare global { function getDesignSpeeds(): DesignSpeeds; function getTrueSpeed(): Knots; function getIndicatedSpeed(): Knots; - function getVerticalSpeed(): FeetPerMinute | null; + function getVerticalSpeed(): number; function getGroundSpeed(): Knots | null; - function getMachSpeed(): Mach | null; + function getMachSpeed(): number; /** * Gets the V1 speed up during and before takeoff, -1 after. @@ -321,7 +321,11 @@ declare global { function getStallSpeedPredicted(flapIndex: number): Knots | null; function getWindDirection(): Degrees | null; function getWindStrength(): Knots | null; - function getAutoPilotActive(apIndex: number): boolean | null; + /** + * Checks autopilot master status. + * @param apIndex Defaults to 0 if undefined. + */ + function getAutoPilotActive(apIndex?: number): boolean; function getAutoPilotAirspeedManaged(): boolean; function getAutoPilotAirspeedSelected(): boolean; function getAutoPilotAirspeedHoldActive(isManaged?: boolean): boolean | null; @@ -353,7 +357,7 @@ declare global { /** * @param units Default = feet. */ - function getAutoPilotDisplayedAltitudeLockValue(units?: string): number | null; + function getAutoPilotDisplayedAltitudeLockValue(units?: string): number; function getAutoPilotAltitudeLockUnits(): 'feet'; function getAutoPilotVerticalSpeedHoldActive(): boolean | null; function getAutoPilotVerticalSpeedHoldValue(): FeetPerMinute | null; @@ -421,7 +425,7 @@ declare global { function getInclinometer(): Position | null; function getAngleOfAttack(): Angl16 | null; function getOrientationAxis(): XYZ | null; - function getAltitude(): Feet | null; + function getAltitude(): number; function getGroundReference(): Feet | null; function getTurnRate(): RadiansPerSecond | null; function getHeadingMagnetic(): Heading | null; @@ -450,8 +454,8 @@ declare global { function getTotalFuel(): Kilograms | null; function getFuelUsed(engineIndex: number): Kilograms | null; function getCompassAngle(): Radians | null; - function getPressureValue(): InchesOfMercury | null; - function getPressureValue(units?: 'inches of mercury' | 'millibar'): InchesOfMercury | Millibar | null; + function getPressureValue(): number; + function getPressureValue(units?: 'inches of mercury' | 'millibar'): number; function getPressureSelectedUnits(): 'inches of mercury' | 'millibar'; function getPressureSelectedMode(aircraft: Aircraft): 'QFE' | 'QNH' | 'STD' | ''; function getHasGlassCockpit(): boolean | null;