Skip to content

Commit

Permalink
feat: warning for "stuck" to conf written by external software/hardwa…
Browse files Browse the repository at this point in the history
…re (#7970)

* fix: add typings for notification

* fix: bug found with typings

* feat: ecp to conf pushbutton check

* fix
  • Loading branch information
tracernz authored Apr 24, 2023
1 parent b46f3ff commit ba30ef1
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 33 deletions.
19 changes: 18 additions & 1 deletion fbw-a32nx/src/systems/extras-host/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// SPDX-License-Identifier: GPL-3.0

import { EventBus, HEventPublisher } from '@microsoft/msfs-sdk';
import { NotificationManager } from '@shared/notification';
import { ExtrasSimVarPublisher } from 'extras-host/modules/common/ExtrasSimVarPublisher';
import { PushbuttonCheck } from 'extras-host/modules/pushbutton_check/PushbuttonCheck';
import { KeyInterceptor } from './modules/key_interceptor/KeyInterceptor';
import { VersionCheck } from './modules/version_check/VersionCheck';
import './style.scss';
Expand All @@ -26,8 +29,14 @@ import './style.scss';
class ExtrasHost extends BaseInstrument {
private readonly bus: EventBus;

private readonly notificationManager: NotificationManager;

private readonly hEventPublisher: HEventPublisher;

private readonly simVarPublisher: ExtrasSimVarPublisher;

private readonly pushbuttonCheck: PushbuttonCheck;

private readonly versionCheck: VersionCheck;

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

this.bus = new EventBus();
this.hEventPublisher = new HEventPublisher(this.bus);
this.simVarPublisher = new ExtrasSimVarPublisher(this.bus);

this.notificationManager = new NotificationManager();

this.pushbuttonCheck = new PushbuttonCheck(this.bus, this.notificationManager);
this.versionCheck = new VersionCheck(this.bus);
this.keyInterceptor = new KeyInterceptor(this.bus);
this.keyInterceptor = new KeyInterceptor(this.bus, this.notificationManager);

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

this.pushbuttonCheck.connectedCallback();
this.versionCheck.connectedCallback();
this.keyInterceptor.connectedCallback();
}
Expand All @@ -80,8 +94,11 @@ class ExtrasHost extends BaseInstrument {
this.hEventPublisher.startPublish();
this.versionCheck.startPublish();
this.keyInterceptor.startPublish();
this.simVarPublisher.startPublish();
}
this.gameState = gs;
} else {
this.simVarPublisher.onUpdate();
}

this.versionCheck.update();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2023 FlyByWire Simulations
// SPDX-License-Identifier: GPL-3.0

import { EventBus, PublishPacer, SimVarDefinition, SimVarPublisher, SimVarValueType } from '@microsoft/msfs-sdk';

/* eslint-disable camelcase */

export interface ExtrasSimVarEvents {
/** ECP TO CONF pushbutton state */
ecp_to_config_pushbutton: boolean,
/** FWC flight phase from 1 - 10 */
fwc_flight_phase: number,
}

export class ExtrasSimVarPublisher extends SimVarPublisher<ExtrasSimVarEvents> {
private static readonly simVars = new Map<keyof ExtrasSimVarEvents, SimVarDefinition>([
['ecp_to_config_pushbutton', { name: 'L:A32NX_BTN_TOCONFIG', type: SimVarValueType.Bool }],
['fwc_flight_phase', { name: 'L:A32NX_FWC_FLIGHT_PHASE', type: SimVarValueType.Number }],
]);

constructor(bus: EventBus, pacer?: PublishPacer<ExtrasSimVarEvents>) {
super(ExtrasSimVarPublisher.simVars, bus, pacer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0

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

Expand All @@ -16,17 +16,14 @@ export class KeyInterceptor {

private keyInterceptManager: KeyEventManager;

private notification: NotificationManager;

private dialogVisible = false;

constructor(private readonly bus: EventBus) {
constructor(private readonly bus: EventBus, private readonly notification: NotificationManager) {
this.eventBus = bus;
KeyEventManager.getManager(this.eventBus).then((manager) => {
this.keyInterceptManager = manager;
this.registerIntercepts();
});
this.notification = new NotificationManager();
console.log('KeyInterceptor: Created');
}

Expand Down Expand Up @@ -75,8 +72,8 @@ export class KeyInterceptor {
'Ctrl+E Not supported',
`<div style="font-size: 120%; text-align: left;">
Engine Auto Start is not supported by the A32NX.<br/>
<br/>
Do you want to you use the flyPad's Aircraft Presets to set the aircraft to
<br/>
Do you want to you use the flyPad's Aircraft Presets to set the aircraft to
<strong>"${AircraftPresetsList.getPresetName(presetID)}"</strong>?
</div>`,
'small',
Expand Down Expand Up @@ -114,8 +111,8 @@ export class KeyInterceptor {
this.notification.showNotification({
title: 'Aircraft Presets',
message: `Loading Preset is already in progress "${(AircraftPresetsList.getPresetName(loadingInProgress))}"`,
type: 'MESSAGE',
duration: 1500,
type: NotificationType.Message,
timeout: 1500,
});
return true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) 2023 FlyByWire Simulations
// SPDX-License-Identifier: GPL-3.0

import { ConsumerSubject, DebounceTimer, EventBus, MappedSubject } from '@microsoft/msfs-sdk';
import { NotificationManager, NotificationTheme } from '@shared/notification';
import { ExtrasSimVarEvents } from 'extras-host/modules/common/ExtrasSimVarPublisher';

/**
* Monitors cockpit pushbuttons that may be written externally to ensure they are not "stuck" because
* the external writer failed to set them back to 0.
*/
export class PushbuttonCheck {
/** Maximum time in ms that TO CONF can be pressed before it is considered "stuck" */
private static readonly TO_CONFIG_MAX_PRESS_TIME = 30000;

private readonly sub = this.bus.getSubscriber<ExtrasSimVarEvents>();

private readonly fwcFlightPhase = ConsumerSubject.create(null, 1);

private readonly toConfButton = ConsumerSubject.create(null, false);

private readonly toConfTimer = new DebounceTimer();

private readonly toConfButtonInCruise = MappedSubject.create(([toConf, phase]) => toConf && phase === 6, this.toConfButton, this.fwcFlightPhase)

private toConfMessageShown = false;

constructor(private readonly bus: EventBus, private readonly notification: NotificationManager) {}

public connectedCallback(): void {
this.toConfButtonInCruise.sub(this.onToConfigPushbutton.bind(this));

this.fwcFlightPhase.setConsumer(this.sub.on('fwc_flight_phase'));
this.toConfButton.setConsumer(this.sub.on('ecp_to_config_pushbutton'));
}

private onToConfigPushbutton(pressed: boolean): void {
if (pressed && !this.toConfTimer.isPending() && !this.toConfMessageShown) {
this.toConfTimer.schedule(() => {
this.toConfMessageShown = true;
this.notification.showNotification({
title: 'ECP Pushbutton Held',
// eslint-disable-next-line max-len
message: 'The TO CONF pushbutton has been held for a long time!\n\nIf you have external hardware or software controlling this variable (L:A32NX_BTN_TOCONFIG), please check that it is setup to write the variable to 0 when the button is released.',
theme: NotificationTheme.Tips,
});
}, PushbuttonCheck.TO_CONFIG_MAX_PRESS_TIME);
} else if (!pressed) {
this.toConfTimer.clear();
}
}
}
64 changes: 41 additions & 23 deletions fbw-a32nx/src/systems/shared/src/notification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,43 @@ import { EventBus, KeyEvents, KeyEventManager } from '@microsoft/msfs-sdk';

let nxNotificationsListener: ViewListener.ViewListener;

export enum NotificationType {
Message = 'MESSAGE',
Subtitles = 'SUBTITLES',
}

export enum NotificationTheme {
Tips = 'TIPS',
Gameplay = 'GAMEPLAY',
System = 'SYSTEM',
}

export enum NotificationImage {
Notification = 'IMAGE_NOTIFICATION',
Score = 'IMAGE_SCORE',
}

/** Parameters that may be provided for construction of a notification */
export interface NotificationParameters {
/** Type of the notification */
type: NotificationType,
/** Theme of the notification */
theme: NotificationTheme,
/** Image icon to display */
image: NotificationImage,
/** Title of the notification */
title: string,
/** Message to display (can be multiline) */
message: string,
/** Time in ms before notification message will disappear */
timeout: number,
}

/**
* NotificationData container for notifications to package notification metadata
*/
export type NotificationData = {
export interface NotificationData extends Omit<NotificationParameters, 'message'> {
id: string;
title: string;
type: string;
theme: string;
image: string;
description: string;
timeout: number;
time: number;
Expand Down Expand Up @@ -66,7 +94,7 @@ export class NotificationManager {
});
}

showNotification(params: any = {}): void {
showNotification(params: Partial<NotificationParameters> = {}): void {
const notif: Notification = new Notification();
notif.showNotification(params);
this.notifications.push(notif);
Expand All @@ -87,9 +115,9 @@ class Notification {
this.params = {
id: `${title}_${this.time}`,
title,
type: 'MESSAGE',
theme: 'GAMEPLAY',
image: 'IMAGE_NOTIFICATION',
type: NotificationType.Message,
theme: NotificationTheme.Gameplay,
image: NotificationImage.Notification,
description: 'Default Message',
timeout: 10000,
time: this.time,
Expand All @@ -98,14 +126,9 @@ class Notification {

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

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

if (!nxNotificationsListener) {
Expand Down

0 comments on commit ba30ef1

Please sign in to comment.