Skip to content

Commit

Permalink
FMS/simbrief sync
Browse files Browse the repository at this point in the history
  • Loading branch information
flogross89 committed Jan 22, 2025
1 parent 6a3284e commit 560d669
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 42 deletions.
40 changes: 27 additions & 13 deletions fbw-a380x/src/systems/instruments/src/OIT/OIT.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
DisplayComponent,
EventBus,
FSComponent,
MappedSubject,
SimVarValueType,
Subject,
VNode,
Expand Down Expand Up @@ -56,12 +57,16 @@ export class OIT extends DisplayComponent<OitProps> {
return this.props.laptop.data;
}

private readonly operationMode = Subject.create<OisOperationMode>('flt-ops');

public readonly hEventConsumer = this.props.bus.getSubscriber<InternalKbdKeyEvent>().on('kbdKeyEvent');

public readonly interactionMode = Subject.create<InteractionMode>(InteractionMode.Touchscreen);

private readonly topRef = FSComponent.createRef<HTMLDivElement>();

private readonly displayUnitRef = FSComponent.createRef<OitDisplayUnit>();

private readonly activePageRef = FSComponent.createRef<HTMLDivElement>();

private activePage: VNode = (<OitNotFound bus={this.props.bus} oit={this} />);
Expand All @@ -71,20 +76,28 @@ export class OIT extends DisplayComponent<OitProps> {

this.uiService.activeUri.sub((uri) => {
this.activeUriChanged(uri);

// Activate EFB overlay if on charts or flt-folder page
SimVar.SetSimVarValue(
`L:A32NX_OIS_${getDisplayIndex()}_SHOW_CHARTS`,
SimVarValueType.Bool,
uri.uri === 'flt-ops/charts', // uri.uri === 'flt-ops/charts'
);
SimVar.SetSimVarValue(
`L:A32NX_OIS_${getDisplayIndex()}_SHOW_OFP`,
SimVarValueType.Bool,
uri.uri === 'flt-ops/flt-folder',
);
});

MappedSubject.create(
([uri, displayFailed, displayPowered, operationMode]) => {
// Activate EFB overlay if on charts or flt-folder page
SimVar.SetSimVarValue(
`L:A32NX_OIS_${getDisplayIndex()}_SHOW_CHARTS`,
SimVarValueType.Bool,
uri.uri === 'flt-ops/charts' && operationMode === 'flt-ops' && !displayFailed && displayPowered,
);
SimVar.SetSimVarValue(
`L:A32NX_OIS_${getDisplayIndex()}_SHOW_OFP`,
SimVarValueType.Bool,
uri.uri === 'flt-ops/flt-folder' && operationMode === 'flt-ops' && !displayFailed && displayPowered,
);
},
this.uiService.activeUri,
this.displayUnitRef.instance.failed,
this.displayUnitRef.instance.powered,
this.operationMode,
);

this.uiService.navigateTo('flt-ops/sts'); // should be /sts
}

Expand Down Expand Up @@ -116,10 +129,11 @@ export class OIT extends DisplayComponent<OitProps> {
render(): VNode | null {
return (
<OitDisplayUnit
ref={this.displayUnitRef}
bus={this.props.bus}
displayUnitId={this.props.captOrFo === 'CAPT' ? OitDisplayUnitID.CaptOit : OitDisplayUnitID.FoOit}
failuresConsumer={this.props.failuresConsumer}
nssOrFltOps={Subject.create<OisOperationMode>('flt-ops')}
nssOrFltOps={this.operationMode}
>
<div ref={this.topRef} class="oit-main">
<OitHeader uiService={this.uiService} oit={this} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) 2025 FlyByWire Simulations
// SPDX-License-Identifier: GPL-3.0

export interface OisInternalData {
/** SYNCHRO AVIONICS is pressed (transmitted to EFB overlay) */
synchroAvncs: boolean;
}
8 changes: 7 additions & 1 deletion fbw-a380x/src/systems/instruments/src/OIT/OisLaptop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { ConsumerSubject, EventBus, Instrument, SimVarValueType, Subject, Subscribable } from '@microsoft/msfs-sdk';
import { FailuresConsumer, FmsData } from '@flybywiresim/fbw-sdk';
import { A380Failure } from '@failures';
import { OisInternalData } from 'instruments/src/OIT/OisInternalPublisher';

type LaptopIndex = 1 | 2;

Expand All @@ -17,7 +18,7 @@ export class OisLaptop implements Instrument {
private readonly _isHealthy = Subject.create(false);
public readonly isHealthy = this._isHealthy as Subscribable<boolean>;

private readonly sub = this.bus.getSubscriber<FmsData>();
private readonly sub = this.bus.getSubscriber<FmsData & OisInternalData>();

private readonly fltNumberBus = ConsumerSubject.create(this.sub.on('fmsFlightNumber'), null);
private readonly fromAirportBus = ConsumerSubject.create(this.sub.on('fmsOrigin'), null);
Expand All @@ -27,6 +28,9 @@ export class OisLaptop implements Instrument {
this.data.fltNumber.set(this.fltNumberBus.get());
this.data.fromAirport.set(this.fromAirportBus.get());
this.data.toAirport.set(this.toAirportBus.get());

// Workaround since synced messages in the same VCockpit don't work
SimVar.SetSimVarValue('L:A32NX_OIS_SYNCHRO_AVIONICS', SimVarValueType.Number, Math.random());
}

constructor(
Expand All @@ -38,6 +42,8 @@ export class OisLaptop implements Instrument {
/** @inheritdoc */
init(): void {
this.failuresConsumer.register(this.failureKey);

this.sub.on('synchroAvncs').handle(() => this.synchroAvionics());
}

/** @inheritdoc */
Expand Down
15 changes: 11 additions & 4 deletions fbw-a380x/src/systems/instruments/src/OIT/OitDisplayUnit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import {
ClockEvents,
ComponentProps,
ConsumerSubject,
DisplayComponent,
EventBus,
Expand Down Expand Up @@ -63,7 +64,7 @@ enum DisplayUnitState {
Failed,
}

export class OitDisplayUnit extends DisplayComponent<DisplayUnitProps> {
export class OitDisplayUnit extends DisplayComponent<DisplayUnitProps & ComponentProps> {
private readonly sub = this.props.bus.getSubscriber<OitSimvars & ClockEvents>();

private readonly state = Subject.create<DisplayUnitState>(
Expand All @@ -81,7 +82,7 @@ export class OitDisplayUnit extends DisplayComponent<DisplayUnitProps> {

private oitRef = FSComponent.createRef<HTMLDivElement>();

private readonly powered = Subject.create(false);
public readonly powered = Subject.create(false);

private readonly brightness = ConsumerSubject.create(
this.sub.on(this.props.displayUnitId === OitDisplayUnitID.CaptOit ? 'potentiometerCaptain' : 'potentiometerFo'),
Expand Down Expand Up @@ -111,13 +112,19 @@ export class OitDisplayUnit extends DisplayComponent<DisplayUnitProps> {
true,
).map(SubscribableMapFunctions.not());

private readonly failed = MappedSubject.create(
private readonly fltOpsFailed = MappedSubject.create(
SubscribableMapFunctions.or(),
this.fltOpsAnsuFailed,
this.fltOpsLaptopFailed,
);

public readonly failed = MappedSubject.create(
([opMode, state, nssFail, fltOpsFail]) =>
state === DisplayUnitState.On && ((opMode === 'nss' && nssFail) || (opMode === 'flt-ops' && fltOpsFail)),
this.props.nssOrFltOps,
this.state,
this.allNssAnsuFailed,
this.fltOpsLaptopFailed,
this.fltOpsFailed,
);

public onAfterRender(node: VNode): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { DropdownMenu } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/Dropd
import { InputField } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/InputField';
import { AirportFormat, LongAlphanumericFormat } from 'instruments/src/MFD/pages/common/DataEntryFormats';
import { Button } from 'instruments/src/MsfsAvionicsCommon/UiWidgets/Button';
import { OisInternalData } from 'instruments/src/OIT/OisInternalPublisher';

interface OitFltOpsStatusPageProps extends AbstractOitPageProps {}

Expand Down Expand Up @@ -49,7 +50,7 @@ export class OitFltOpsStatus extends DisplayComponent<OitFltOpsStatusPageProps>
<div style="width: 40px;" />
<Button
label="SYNCHRO<br />AVIONICS"
onClick={() => this.props.oit.laptop.synchroAvionics()}
onClick={() => this.props.bus.getPublisher<OisInternalData>().pub('synchroAvncs', true, true, false)}
containerStyle="width: 175px;"
/>
<div style="flex-grow: 1" />
Expand Down
138 changes: 115 additions & 23 deletions fbw-a380x/src/systems/instruments/src/OITlegacy/OitLegacy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,37 @@ import {
ErrorFallback,
EventBusContextProvider,
FailuresOrchestratorProvider,
fetchSimbriefDataAction,
ModalProvider,
Navigation,
PowerContext,
PowerStates,
setAirframeInfo,
setCabinInfo,
setFlypadInfo,
SimbriefData,
store,
TroubleshootingContextProvider,
useAppDispatch,
useEventBus,
useNavigraphAuthInfo,
} from '@flybywiresim/flypad';

import './Efb.scss';
import { EventBus } from '@microsoft/msfs-sdk';
import { EventBus, Subscription } from '@microsoft/msfs-sdk';
import { Provider } from 'react-redux';
import { ErrorBoundary } from 'react-error-boundary';
import { MemoryRouter as Router } from 'react-router';
import { NavigraphAuthProvider, UniversalConfigProvider, useSimVar } from '@flybywiresim/fbw-sdk';
import {
FmsData,
NavigraphAuthProvider,
UniversalConfigProvider,
usePersistentProperty,
useSimVar,
} from '@flybywiresim/fbw-sdk';
import { ToastContainer } from 'react-toastify';
import { OisInternalData } from '../OIT/OisInternalPublisher';
import { simbriefDataFromFms } from 'instruments/src/OITlegacy/utils';

export const getDisplayIndex = () => {
const url = Array.from(document.querySelectorAll('vcockpit-panel > *'))
Expand All @@ -41,12 +55,6 @@ export interface OitEfbWrapperProps {
export const OitEfbWrapper: React.FC<OitEfbWrapperProps> = ({ eventBus }) => {
const [powerState, setPowerState] = useState<PowerStates>(PowerStates.LOADED);

const [showCharts] = useSimVar(`L:A32NX_OIS_${getDisplayIndex()}_SHOW_CHARTS`, 'Bool', 100);
const [showOfp] = useSimVar(`L:A32NX_OIS_${getDisplayIndex()}_SHOW_OFP`, 'Bool', 100);

const showEfbOverlay = showCharts === 1 || showOfp === 1;
document.getElementsByTagName('a380x-oitlegacy')[0].classList.toggle('nopointer', !showEfbOverlay);

const [err, setErr] = useState(false);

useEffect(() => {
Expand Down Expand Up @@ -135,21 +143,8 @@ export const OitEfbWrapper: React.FC<OitEfbWrapperProps> = ({ eventBus }) => {
<EventBusContextProvider eventBus={eventBus}>
<NavigraphAuthProvider>
<PowerContext.Provider value={{ powerState, setPowerState }}>
{showEfbOverlay && (
<div
className="bg-theme-body"
style={{
backgroundColor: '#000',
}}
>
<div className="flex flex-row">
<div className="h-screen w-screen p-2.5 pt-0">
{showCharts === 1 && <Navigation />}
{showOfp === 1 && <Dispatch />}
</div>
</div>
</div>
)}
<ToastContainer position="top-center" draggableDirection="y" limit={2} />
<OitEfbPageWrapper eventBus={eventBus} />
</PowerContext.Provider>
</NavigraphAuthProvider>
</EventBusContextProvider>
Expand All @@ -162,3 +157,100 @@ export const OitEfbWrapper: React.FC<OitEfbWrapperProps> = ({ eventBus }) => {
</AircraftContext.Provider>
);
};

export const OitEfbPageWrapper: React.FC<OitEfbWrapperProps> = () => {
const dispatch = useAppDispatch();

const [showCharts] = useSimVar(`L:A32NX_OIS_${getDisplayIndex()}_SHOW_CHARTS`, 'Bool', 100);
const [showOfp] = useSimVar(`L:A32NX_OIS_${getDisplayIndex()}_SHOW_OFP`, 'Bool', 100);
const [synchroAvionics] = useSimVar('L:A32NX_OIS_SYNCHRO_AVIONICS', 'number', 100);

const [simBriefData, setSimBriefData] = useState<SimbriefData>();
const [fromAirport, setFromAirport] = useState<string>('');
const [toAirport, setToAirport] = useState<string>('');
const [altnAirport, setAltnAirport] = useState<string>('');

const showEfbOverlay = showCharts === 1 || showOfp === 1;
document.getElementsByTagName('a380x-oitlegacy')[0].classList.toggle('nopointer', !showEfbOverlay);

const navigraphAuthInfo = useNavigraphAuthInfo();
const [overrideSimBriefUserID] = usePersistentProperty('CONFIG_OVERRIDE_SIMBRIEF_USERID');

const bus = useEventBus();

const updateSimBriefInfo = async () => {
try {
const action = await fetchSimbriefDataAction(
(navigraphAuthInfo.loggedIn && navigraphAuthInfo.username) || '',
overrideSimBriefUserID ?? '',
);
setSimBriefData(action.payload);
const newAction = simbriefDataFromFms(simBriefData, fromAirport, toAirport, altnAirport);
dispatch(newAction);
} catch (e) {
console.error(e.message);
}
};

useEffect(() => {
const sub = bus.getSubscriber<FmsData & OisInternalData>();
const subs: Subscription[] = [];
subs.push(
sub.on('synchroAvncs').handle(() => {
updateSimBriefInfo(); // never triggers, because synced messages in the same VCockpit don't work
}),
);
subs.push(
sub
.on('fmsOrigin')
.whenChanged()
.handle((icao) => setFromAirport(icao)),
);
subs.push(
sub
.on('fmsDestination')
.whenChanged()
.handle((icao) => setToAirport(icao)),
);
subs.push(
sub
.on('fmsAlternate')
.whenChanged()
.handle((icao) => setAltnAirport(icao)),
);

return () => {
subs.forEach((s) => s.destroy());
};
}, []);

// Enable this for FMS auto-sync
/* useEffect(() => {
dispatch(simbriefDataFromFms(simBriefData, fromAirport, toAirport, altnAirport));
}, [fromAirport, toAirport, altnAirport]);
*/

useEffect(() => {
updateSimBriefInfo();
}, [navigraphAuthInfo.loggedIn, synchroAvionics]);

return (
<>
{showEfbOverlay && (
<div
className="bg-theme-body"
style={{
backgroundColor: '#000',
}}
>
<div className="flex flex-row">
<div className="h-screen w-screen p-2.5 pt-0">
{showCharts === 1 && <Navigation />}
{showOfp === 1 && <Dispatch />}
</div>
</div>
</div>
)}
</>
);
};
15 changes: 15 additions & 0 deletions fbw-a380x/src/systems/instruments/src/OITlegacy/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { initialState, setSimbriefData, SimbriefData } from '@flybywiresim/flypad';
import { PayloadAction } from '@reduxjs/toolkit';

export function simbriefDataFromFms(
simBriefData: SimbriefData,
origin?: string,
destination?: string,
altn?: string,
): PayloadAction<SimbriefData> {
const state = Object.assign({}, simBriefData ?? initialState.data);
state.departingAirport = origin ?? state.departingAirport;
state.arrivingAirport = destination ?? state.arrivingAirport;
state.altIcao = altn ?? state.altIcao;
return setSimbriefData(state);
}
1 change: 1 addition & 0 deletions fbw-common/src/systems/instruments/src/EFB/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-3.0

export * from './AircraftContext';
export * from './Apis/Navigraph/Components/Authentication';
export * from './Apis/Simbrief';
export * from './Efb';
export * from './Enum/Airframe';
Expand Down

0 comments on commit 560d669

Please sign in to comment.