Skip to content

Commit ba30ef1

Browse files
authored
feat: warning for "stuck" to conf written by external software/hardware (#7970)
* fix: add typings for notification * fix: bug found with typings * feat: ecp to conf pushbutton check * fix
1 parent b46f3ff commit ba30ef1

File tree

5 files changed

+141
-33
lines changed

5 files changed

+141
-33
lines changed

fbw-a32nx/src/systems/extras-host/index.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// SPDX-License-Identifier: GPL-3.0
33

44
import { EventBus, HEventPublisher } from '@microsoft/msfs-sdk';
5+
import { NotificationManager } from '@shared/notification';
6+
import { ExtrasSimVarPublisher } from 'extras-host/modules/common/ExtrasSimVarPublisher';
7+
import { PushbuttonCheck } from 'extras-host/modules/pushbutton_check/PushbuttonCheck';
58
import { KeyInterceptor } from './modules/key_interceptor/KeyInterceptor';
69
import { VersionCheck } from './modules/version_check/VersionCheck';
710
import './style.scss';
@@ -26,8 +29,14 @@ import './style.scss';
2629
class ExtrasHost extends BaseInstrument {
2730
private readonly bus: EventBus;
2831

32+
private readonly notificationManager: NotificationManager;
33+
2934
private readonly hEventPublisher: HEventPublisher;
3035

36+
private readonly simVarPublisher: ExtrasSimVarPublisher;
37+
38+
private readonly pushbuttonCheck: PushbuttonCheck;
39+
3140
private readonly versionCheck: VersionCheck;
3241

3342
private readonly keyInterceptor: KeyInterceptor;
@@ -45,9 +54,13 @@ class ExtrasHost extends BaseInstrument {
4554

4655
this.bus = new EventBus();
4756
this.hEventPublisher = new HEventPublisher(this.bus);
57+
this.simVarPublisher = new ExtrasSimVarPublisher(this.bus);
58+
59+
this.notificationManager = new NotificationManager();
4860

61+
this.pushbuttonCheck = new PushbuttonCheck(this.bus, this.notificationManager);
4962
this.versionCheck = new VersionCheck(this.bus);
50-
this.keyInterceptor = new KeyInterceptor(this.bus);
63+
this.keyInterceptor = new KeyInterceptor(this.bus, this.notificationManager);
5164

5265
console.log('A32NX_EXTRASHOST: Created');
5366
}
@@ -67,6 +80,7 @@ class ExtrasHost extends BaseInstrument {
6780
public connectedCallback(): void {
6881
super.connectedCallback();
6982

83+
this.pushbuttonCheck.connectedCallback();
7084
this.versionCheck.connectedCallback();
7185
this.keyInterceptor.connectedCallback();
7286
}
@@ -80,8 +94,11 @@ class ExtrasHost extends BaseInstrument {
8094
this.hEventPublisher.startPublish();
8195
this.versionCheck.startPublish();
8296
this.keyInterceptor.startPublish();
97+
this.simVarPublisher.startPublish();
8398
}
8499
this.gameState = gs;
100+
} else {
101+
this.simVarPublisher.onUpdate();
85102
}
86103

87104
this.versionCheck.update();
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) 2023 FlyByWire Simulations
2+
// SPDX-License-Identifier: GPL-3.0
3+
4+
import { EventBus, PublishPacer, SimVarDefinition, SimVarPublisher, SimVarValueType } from '@microsoft/msfs-sdk';
5+
6+
/* eslint-disable camelcase */
7+
8+
export interface ExtrasSimVarEvents {
9+
/** ECP TO CONF pushbutton state */
10+
ecp_to_config_pushbutton: boolean,
11+
/** FWC flight phase from 1 - 10 */
12+
fwc_flight_phase: number,
13+
}
14+
15+
export class ExtrasSimVarPublisher extends SimVarPublisher<ExtrasSimVarEvents> {
16+
private static readonly simVars = new Map<keyof ExtrasSimVarEvents, SimVarDefinition>([
17+
['ecp_to_config_pushbutton', { name: 'L:A32NX_BTN_TOCONFIG', type: SimVarValueType.Bool }],
18+
['fwc_flight_phase', { name: 'L:A32NX_FWC_FLIGHT_PHASE', type: SimVarValueType.Number }],
19+
]);
20+
21+
constructor(bus: EventBus, pacer?: PublishPacer<ExtrasSimVarEvents>) {
22+
super(ExtrasSimVarPublisher.simVars, bus, pacer);
23+
}
24+
}

fbw-a32nx/src/systems/extras-host/modules/key_interceptor/KeyInterceptor.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: GPL-3.0
33

44
import { EventBus, KeyEvents, KeyEventManager } from '@microsoft/msfs-sdk';
5-
import { NotificationManager } from '@shared/notification';
5+
import { NotificationManager, NotificationType } from '@shared/notification';
66
import { PopUpDialog } from '@shared/popup';
77
import { AircraftPresetsList } from '../common/AircraftPresetsList';
88

@@ -16,17 +16,14 @@ export class KeyInterceptor {
1616

1717
private keyInterceptManager: KeyEventManager;
1818

19-
private notification: NotificationManager;
20-
2119
private dialogVisible = false;
2220

23-
constructor(private readonly bus: EventBus) {
21+
constructor(private readonly bus: EventBus, private readonly notification: NotificationManager) {
2422
this.eventBus = bus;
2523
KeyEventManager.getManager(this.eventBus).then((manager) => {
2624
this.keyInterceptManager = manager;
2725
this.registerIntercepts();
2826
});
29-
this.notification = new NotificationManager();
3027
console.log('KeyInterceptor: Created');
3128
}
3229

@@ -75,8 +72,8 @@ export class KeyInterceptor {
7572
'Ctrl+E Not supported',
7673
`<div style="font-size: 120%; text-align: left;">
7774
Engine Auto Start is not supported by the A32NX.<br/>
78-
<br/>
79-
Do you want to you use the flyPad's Aircraft Presets to set the aircraft to
75+
<br/>
76+
Do you want to you use the flyPad's Aircraft Presets to set the aircraft to
8077
<strong>"${AircraftPresetsList.getPresetName(presetID)}"</strong>?
8178
</div>`,
8279
'small',
@@ -114,8 +111,8 @@ export class KeyInterceptor {
114111
this.notification.showNotification({
115112
title: 'Aircraft Presets',
116113
message: `Loading Preset is already in progress "${(AircraftPresetsList.getPresetName(loadingInProgress))}"`,
117-
type: 'MESSAGE',
118-
duration: 1500,
114+
type: NotificationType.Message,
115+
timeout: 1500,
119116
});
120117
return true;
121118
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Copyright (c) 2023 FlyByWire Simulations
2+
// SPDX-License-Identifier: GPL-3.0
3+
4+
import { ConsumerSubject, DebounceTimer, EventBus, MappedSubject } from '@microsoft/msfs-sdk';
5+
import { NotificationManager, NotificationTheme } from '@shared/notification';
6+
import { ExtrasSimVarEvents } from 'extras-host/modules/common/ExtrasSimVarPublisher';
7+
8+
/**
9+
* Monitors cockpit pushbuttons that may be written externally to ensure they are not "stuck" because
10+
* the external writer failed to set them back to 0.
11+
*/
12+
export class PushbuttonCheck {
13+
/** Maximum time in ms that TO CONF can be pressed before it is considered "stuck" */
14+
private static readonly TO_CONFIG_MAX_PRESS_TIME = 30000;
15+
16+
private readonly sub = this.bus.getSubscriber<ExtrasSimVarEvents>();
17+
18+
private readonly fwcFlightPhase = ConsumerSubject.create(null, 1);
19+
20+
private readonly toConfButton = ConsumerSubject.create(null, false);
21+
22+
private readonly toConfTimer = new DebounceTimer();
23+
24+
private readonly toConfButtonInCruise = MappedSubject.create(([toConf, phase]) => toConf && phase === 6, this.toConfButton, this.fwcFlightPhase)
25+
26+
private toConfMessageShown = false;
27+
28+
constructor(private readonly bus: EventBus, private readonly notification: NotificationManager) {}
29+
30+
public connectedCallback(): void {
31+
this.toConfButtonInCruise.sub(this.onToConfigPushbutton.bind(this));
32+
33+
this.fwcFlightPhase.setConsumer(this.sub.on('fwc_flight_phase'));
34+
this.toConfButton.setConsumer(this.sub.on('ecp_to_config_pushbutton'));
35+
}
36+
37+
private onToConfigPushbutton(pressed: boolean): void {
38+
if (pressed && !this.toConfTimer.isPending() && !this.toConfMessageShown) {
39+
this.toConfTimer.schedule(() => {
40+
this.toConfMessageShown = true;
41+
this.notification.showNotification({
42+
title: 'ECP Pushbutton Held',
43+
// eslint-disable-next-line max-len
44+
message: 'The TO CONF pushbutton has been held for a long time!\n\nIf you have external hardware or software controlling this variable (L:A32NX_BTN_TOCONFIG), please check that it is setup to write the variable to 0 when the button is released.',
45+
theme: NotificationTheme.Tips,
46+
});
47+
}, PushbuttonCheck.TO_CONFIG_MAX_PRESS_TIME);
48+
} else if (!pressed) {
49+
this.toConfTimer.clear();
50+
}
51+
}
52+
}

fbw-a32nx/src/systems/shared/src/notification.ts

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,43 @@ import { EventBus, KeyEvents, KeyEventManager } from '@microsoft/msfs-sdk';
22

33
let nxNotificationsListener: ViewListener.ViewListener;
44

5+
export enum NotificationType {
6+
Message = 'MESSAGE',
7+
Subtitles = 'SUBTITLES',
8+
}
9+
10+
export enum NotificationTheme {
11+
Tips = 'TIPS',
12+
Gameplay = 'GAMEPLAY',
13+
System = 'SYSTEM',
14+
}
15+
16+
export enum NotificationImage {
17+
Notification = 'IMAGE_NOTIFICATION',
18+
Score = 'IMAGE_SCORE',
19+
}
20+
21+
/** Parameters that may be provided for construction of a notification */
22+
export interface NotificationParameters {
23+
/** Type of the notification */
24+
type: NotificationType,
25+
/** Theme of the notification */
26+
theme: NotificationTheme,
27+
/** Image icon to display */
28+
image: NotificationImage,
29+
/** Title of the notification */
30+
title: string,
31+
/** Message to display (can be multiline) */
32+
message: string,
33+
/** Time in ms before notification message will disappear */
34+
timeout: number,
35+
}
36+
537
/**
638
* NotificationData container for notifications to package notification metadata
739
*/
8-
export type NotificationData = {
40+
export interface NotificationData extends Omit<NotificationParameters, 'message'> {
941
id: string;
10-
title: string;
11-
type: string;
12-
theme: string;
13-
image: string;
1442
description: string;
1543
timeout: number;
1644
time: number;
@@ -66,7 +94,7 @@ export class NotificationManager {
6694
});
6795
}
6896

69-
showNotification(params: any = {}): void {
97+
showNotification(params: Partial<NotificationParameters> = {}): void {
7098
const notif: Notification = new Notification();
7199
notif.showNotification(params);
72100
this.notifications.push(notif);
@@ -87,9 +115,9 @@ class Notification {
87115
this.params = {
88116
id: `${title}_${this.time}`,
89117
title,
90-
type: 'MESSAGE',
91-
theme: 'GAMEPLAY',
92-
image: 'IMAGE_NOTIFICATION',
118+
type: NotificationType.Message,
119+
theme: NotificationTheme.Gameplay,
120+
image: NotificationImage.Notification,
93121
description: 'Default Message',
94122
timeout: 10000,
95123
time: this.time,
@@ -98,14 +126,9 @@ class Notification {
98126

99127
/**
100128
* Modify the display data for this Notification
101-
* @param {string} params.title Title for notification - will show as the message header
102-
* @param {string} params.type Type of Notification - Valid types are MESSAGE|SUBTITLES
103-
* @param {string} params.theme Theme of Notification. Valid types are TIPS|GAMEPLAY|SYSTEM
104-
* @param {string} params.image Notification image. Valid types are IMAGE_NOTIFICATION|IMAGE_SCORE
105-
* @param {string} params.message Notification message
106-
* @param {number} params.timeout Time in ms before notification message will disappear
129+
* @param params Parameters for the notification
107130
*/
108-
private setData(params: any = {}): void {
131+
private setData(params: Partial<NotificationParameters> = {}): void {
109132
if (params.title) {
110133
this.params.title = params.title;
111134
this.params.id = `${params.title}_${new Date().getTime()}`;
@@ -129,14 +152,9 @@ class Notification {
129152

130153
/**
131154
* Show notification with given or already initiated parametrs.
132-
* @param {string} params.title Title for notification - will show as the message header
133-
* @param {string} params.type Type of Notification - Valid types are MESSAGE|SUBTITLES
134-
* @param {string} params.theme Theme of Notification. Valid types are TIPS|GAMEPLAY|SYSTEM
135-
* @param {string} params.image Notification image. Valid types are IMAGE_NOTIFICATION|IMAGE_SCORE
136-
* @param {string} params.message Notification message
137-
* @param {number} params.timeout Time in ms before notification message will disappear
155+
* @param params Parameters for the notification
138156
*/
139-
showNotification(params: any = {}): void {
157+
showNotification(params: Partial<NotificationParameters> = {}): void {
140158
this.setData(params);
141159

142160
if (!nxNotificationsListener) {

0 commit comments

Comments
 (0)