Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: fms-v2 msfs sync #8508

Merged
merged 31 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5098641
add intial sync with all todos and hacks
Saschl Jan 29, 2024
cbd7dfa
serialize dest and origin airports
Saschl Jan 29, 2024
433f690
Merge remote-tracking branch 'fbw/fms-v2' into fms-v2-asobosync
Saschl Feb 2, 2024
5093d0f
fix: guard origin and destination airport in serialization
Saschl Feb 2, 2024
38d144a
Merge remote-tracking branch 'origin/fms-v2' into fms-v2-asobosync
Saschl Feb 14, 2024
9315f9e
Merge remote-tracking branch 'origin/fms-v2' into fms-v2-asobosync
Saschl Feb 14, 2024
4b1c173
Merge remote-tracking branch 'fbw/master' into fms-v2-asobosync
Saschl Feb 18, 2024
09780c1
Merge remote-tracking branch 'fbw/fms-v2' into fms-v2-asobosync
Saschl Feb 18, 2024
0d12377
fix: waypoint insertion, add extra module for sync
Saschl Feb 18, 2024
cce8045
remove unncecessary change
Saschl Feb 18, 2024
06fe333
use more granular sync events
Saschl Feb 19, 2024
63fb80b
some cleanup
Saschl Feb 29, 2024
c9e8789
Merge remote-tracking branch 'fbw/fms-v2' into fms-v2-asobosync
Saschl Feb 29, 2024
ee9406a
feat: sync flight plan from game
Saschl Feb 29, 2024
4b43eb0
fix some errors
Saschl Feb 29, 2024
a1fadd4
more log
Saschl Feb 29, 2024
be012d7
fix: export FlightPlanRpcServer
Saschl Feb 29, 2024
59c2ba9
fix: mapping
Saschl Feb 29, 2024
3ef891e
fix: delay init until ingame
Saschl Feb 29, 2024
b9f2f1f
use native fms2 sync
Saschl Mar 1, 2024
0d87e5a
fix: missing airport and runway in sync
Saschl Mar 1, 2024
a18cb55
Revert "fix: missing airport and runway in sync"
Saschl Mar 1, 2024
c8afe73
Revert "use native fms2 sync"
Saschl Mar 1, 2024
563d2d2
fix: sync confusion
Saschl Mar 1, 2024
ff81121
small improvement
Saschl Mar 11, 2024
65f0a77
Merge remote-tracking branch 'fbw/fms-v2' into fms-v2-asobosync
Saschl Mar 11, 2024
f820369
enable basic load from sim
Saschl Mar 11, 2024
2919b66
fix: rpcclient creation
Saschl Mar 11, 2024
e7ca96f
only use rpc client for initial load
Saschl Mar 11, 2024
2504252
fix: datastore subscription
Saschl Mar 11, 2024
016f462
fix: enroute waypoint insertion
Saschl Mar 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions fbw-a32nx/src/systems/extras-host/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ class ExtrasHost extends BaseInstrument {

private readonly flightPlanAsoboSync: FlightPlanAsoboSync;

private readonly syncThrottler: UpdateThrottler;

/**
* "mainmenu" = 0
* "loading" = 1
Expand All @@ -72,8 +70,6 @@ class ExtrasHost extends BaseInstrument {
this.flightPlanTest = new FlightPlanTest(this.bus);
this.flightPlanAsoboSync = new FlightPlanAsoboSync(this.bus);

this.syncThrottler = new UpdateThrottler(5000);

console.log('A32NX_EXTRASHOST: Created');
}

Expand Down Expand Up @@ -112,9 +108,6 @@ class ExtrasHost extends BaseInstrument {
this.gameState = gs;
} else {
this.simVarPublisher.onUpdate();
if (this.syncThrottler.canUpdate(this.deltaTime) > 0) {
this.flightPlanAsoboSync.update();
}
}

this.versionCheck.update();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
/* eslint-disable no-await-in-loop */

import { NXDataStore } from '@flybywiresim/fbw-sdk';
import { SerializedFlightPlanLeg } from '@fmgc/flightplanning/new/legs/FlightPlanLeg';
import { SerializedFlightPlan } from '@fmgc/flightplanning/new/plans/BaseFlightPlan';
import { FlightPlanSyncEvents, FlightPlanSyncResponsePacket } from '@fmgc/flightplanning/new/sync/FlightPlanSyncEvents';
import { Discontinuity, SerializedFlightPlanLeg } from '@fmgc/flightplanning/new/legs/FlightPlanLeg';
import { FlightPlanSyncEvents, PerformanceDataFlightPlanSyncEvents } from '@fmgc/flightplanning/new/sync/FlightPlanSyncEvents';
import { A320FlightPlanPerformanceData, FlightPlanIndex } from '@fmgc/index';
import { EventBus, FacilityType, FacilityLoader, FacilityRepository } from '@microsoft/msfs-sdk';

export class FlightPlanAsoboSync {
Expand All @@ -17,44 +17,125 @@ export class FlightPlanAsoboSync {

private facilityLoaderCustom: FacilityLoader;

private cruiseFlightLevel = undefined;

private originAirport = undefined;

private destinationAirport = undefined;

private procedureDetails: ProcedureDetails = undefined;

private enrouteLegs: (SerializedFlightPlanLeg | Discontinuity)[] = undefined;

constructor(private readonly bus: EventBus) {}

connectedCallback(): void {
const sub = this.bus.getSubscriber<FlightPlanSyncEvents>();
const sub = this.bus.getSubscriber<FlightPlanSyncEvents & PerformanceDataFlightPlanSyncEvents<A320FlightPlanPerformanceData>>();

this.facilityLoaderCustom = new FacilityLoader(FacilityRepository.getRepository(this.bus));

RegisterViewListener('JS_LISTENER_FLIGHTPLAN', () => {
this.isReady = true;
});
sub.on('flightPlanManager.syncResponse').handle(async (event) => {
await this.syncFlightPlanToGame(event);
});
}

public update(): void {
// initial sync
if (NXDataStore.get('FP_SYNC', 'LOAD') === 'SAVE') {
const pub = this.bus.getPublisher<FlightPlanSyncEvents>();
pub.pub('flightPlanManager.syncRequest', undefined, true);
}

sub.on('flightPlanManager.syncResponse').handle(async (event) => {
console.log('SYNC RESPONSE', event);
const plan = event.plans[FlightPlanIndex.Active];
this.enrouteLegs = plan.segments.enrouteSegment.allLegs;
this.originAirport = plan.originAirport;
this.destinationAirport = plan.destinationAirport;

// TODO not really needed anymore
this.procedureDetails = {
originRunway: plan.originRunway,
departureIdent: plan.segments.departureSegment?.procedureIdent,
departureTransitionIdent: plan.segments.departureRunwayTransitionSegment?.procedureIdent,

arrivalIdent: plan.segments.arrivalSegment?.procedureIdent,
arrivalTransitionIdent: plan.segments.arrivalEnrouteTransitionSegment?.procedureIdent,
arrivalRunwayTransitionIdent: plan.segments?.arrivalRunwayTransitionSegment.procedureIdent,
destinationRunway: plan.destinationRunway,
approachIdent: plan.segments.approachSegment?.procedureIdent,
approachTransitionIdent: plan.segments.approachViaSegment?.procedureIdent,
};

await this.syncFlightPlanToGame();
});

sub.on('flightPlan.setPerformanceData.cruiseFlightLevel').handle(async (event) => {
if (event.planIndex === FlightPlanIndex.Active || event.planIndex === FlightPlanIndex.Uplink) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably don't want to listen to any events regarding the uplink plan, that's meant for temporary plan construction while uplinking from say, simbrief.

this.cruiseFlightLevel = event.value;
console.log('SET CRUISE FLIGHT LEVEL', this.cruiseFlightLevel);
await Coherent.call('SET_CRUISE_ALTITUDE', this.cruiseFlightLevel * 100);
}
});
sub.on('flightPlan.setSegmentLegs').handle(async (event) => {
console.log('SEGMENT LEGS', event);
if ((event.planIndex === FlightPlanIndex.Active) && event.segmentIndex === 4) {
this.enrouteLegs = event.legs;
await this.syncFlightPlanToGame();
}
});

/* sub.on('flightPlanManager.swap').handle(async (event) => {
if (event.targetPlanIndex === FlightPlanIndex.Active) {
pub.
} */
/*

sub.on('flightPlan.setProcedureDetails').handle(async (event) => {
if ((event.planIndex === FlightPlanIndex.Temporary)) {
this.procedureDetails = event.details;
console.log('PROCEDURE DETAILS', this.procedureDetails);
await this.syncFlightPlanToGame();
}
}); */

/* sub.on('flightPlan.setOriginAirport').handle(async (event) => {
this.originAirport = event.originAirport;
console.log('ORIGIN', event);
await this.syncFlightPlanToGame();
});

sub.on('flightPlan.setDestinationAirport').handle(async (event) => {
if (event.planIndex === FlightPlanIndex.Active) {
this.destinationAirport = event.originAirport;
console.log('DESTINATION', this.destinationAirport);
await this.syncFlightPlanToGame();
}
}); */

sub.on('flightPlanManager.copy').handle(async (event) => {
if (NXDataStore.get('FP_SYNC', 'LOAD') === 'SAVE' && event.targetPlanIndex === FlightPlanIndex.Active) {
const pub = this.bus.getPublisher<FlightPlanSyncEvents>();
pub.pub('flightPlanManager.syncRequest', undefined, true);
}
});
sub.on('flightPlanManager.create').handle(async (event) => {
if (NXDataStore.get('FP_SYNC', 'LOAD') === 'SAVE' && event.planIndex === FlightPlanIndex.Active) {
const pub = this.bus.getPublisher<FlightPlanSyncEvents>();
pub.pub('flightPlanManager.syncRequest', undefined, true);
}
});
}

private async syncFlightPlanToGame(event: FlightPlanSyncResponsePacket): Promise<void> {
private async syncFlightPlanToGame(): Promise<void> {
try {
if (this.isReady && this.lastFlightPlanVersion !== event.plans[0]?.flightPlanVersion) {
const plan = event.plans[0] as SerializedFlightPlan;

if (this.isReady) {
await Coherent.call('SET_CURRENT_FLIGHTPLAN_INDEX', 0, true);
await Coherent.call('CLEAR_CURRENT_FLIGHT_PLAN');
console.log('A32NX_EXTRASHOST: Received flightPlanManager.syncResponse');
console.log(event);
this.lastFlightPlanVersion = plan.flightPlanVersion;

if (plan.originAirport && plan.destinationAirport) {
await Coherent.call('SET_ORIGIN', `A ${plan.originAirport} `, false);
await Coherent.call('SET_DESTINATION', `A ${plan.destinationAirport} `, false);
if (this.originAirport && this.destinationAirport) {
await Coherent.call('SET_ORIGIN', `A ${this.originAirport} `, false);
await Coherent.call('SET_DESTINATION', `A ${this.destinationAirport} `, false);

const allEnrouteLegs = event.plans[0].segments.enrouteSegment.allLegs;
const allEnrouteLegs = this.enrouteLegs;
console.log('ALL ENROUTE LEGS', allEnrouteLegs);

let globalIndex = 1;
Expand All @@ -77,21 +158,25 @@ export class FlightPlanAsoboSync {
}
}

const originFacility = await this.facilityLoaderCustom.getFacility(FacilityType.Airport, `A ${plan.originAirport.substring(0, 4)} `);
const originFacility = await this.facilityLoaderCustom.getFacility(FacilityType.Airport, `A ${this.originAirport.substring(0, 4)} `);
let originRw = 0;
let departureRw = 0;
for (const runway of originFacility.runways) {
for (const designation of runway.designation.split('-')) {
console.log('ORIGIN RUNWAY', plan.originRunway);
console.log('ORIGIN RUNWAY', this.procedureDetails.originRunway);
console.log(designation);
if (designation === (plan.originRunway.substring(2, 4).startsWith('0') ? plan.originRunway.substring(3, 4) : plan.originRunway.substring(2, 4))) {
if (designation
=== (this.procedureDetails.originRunway.substring(2, 4).startsWith('0')
? this.procedureDetails.originRunway.substring(3, 4)
: this.procedureDetails.originRunway.substring(2, 4))) {
console.log(`Runway parent ${originRw} is matching with actual index ${departureRw}. Is ${JSON.stringify(runway)}`);
await Coherent.call('SET_ORIGIN_RUNWAY_INDEX', originRw);
await Coherent.call('SET_DEPARTURE_RUNWAY_INDEX', departureRw);
const departureIndex = originFacility.departures.findIndex((departure) => departure.name === plan.segments?.departureSegment?.procedureIdent);
const departureIndex = originFacility.departures
.findIndex((departure) => departure.name === this.procedureDetails.departureIdent);
const departureTransitionIndex = originFacility.departures
.findIndex((departure) => departure.enRouteTransitions
.map((t) => t.name === plan.segments?.departureRunwayTransitionSegment?.procedureIdent));
.map((t) => t.name === this.procedureDetails.departureTransitionIdent));

console.log('DEPARTURE INDEX', departureIndex);
console.log('DEPARTURE TransitionIndex', departureIndex);
Expand All @@ -113,28 +198,43 @@ export class FlightPlanAsoboSync {

let destinationRunwayIndex = 0;

console.log('DESTINATION SEGMENT', plan.segments);
// console.log('DESTINATION SEGMENT', plan.segments);

if (plan.destinationAirport) {
const destinationRunwayIdent = plan.destinationRunway;
if (this.destinationAirport) {
const destinationRunwayIdent = this.procedureDetails.destinationRunway;
console.log('IDENT', destinationRunwayIdent);

const arg = await this.facilityLoaderCustom.getFacility(FacilityType.Airport, `A ${plan.destinationAirport.substring(0, 4)} `);
const arg = await this.facilityLoaderCustom.getFacility(FacilityType.Airport, `A ${this.destinationAirport.substring(0, 4)} `);
console.log('ARRIVAL', arg);
for (const runway of arg.runways) {
for (const designation of runway.designation.split('-')) {
console.log(destinationRunwayIdent);
console.log(designation);
if (designation === (
destinationRunwayIdent.substring(2, 4).startsWith('0') ? destinationRunwayIdent.substring(3, 4) : destinationRunwayIdent.substring(2, 4))) {
if (designation
=== (destinationRunwayIdent.substring(2, 4).startsWith('0')
? destinationRunwayIdent.substring(3, 4)
: destinationRunwayIdent.substring(2, 4))) {
console.log(`Runway is matching with actual index ${destinationRunwayIndex}. Is ${JSON.stringify(runway)}`);
const arrivalIndex = arg.arrivals.findIndex((arrival) => arrival.name === plan.segments?.arrivalSegment?.procedureIdent);
const arrivalIndex = arg.arrivals
.findIndex((arrival) => arrival.name === this.procedureDetails.arrivalIdent);
const arritvalTransitionIndex = arg.arrivals
.findIndex((arrival) => arrival.enRouteTransitions.map((t) => t.name === plan.segments?.arrivalRunwayTransitionSegment?.procedureIdent));
.findIndex((arrival) => arrival.enRouteTransitions.map((t) => t.name === this.procedureDetails.arrivalTransitionIdent));

console.log('APPR IDENT', this.procedureDetails.approachIdent);
console.log('available appr', arg.approaches);
let approachName = this.procedureDetails.approachIdent;

if (approachName.startsWith('D')) {
approachName = `VOR ${destinationRunwayIdent.substring(2, 4).startsWith('0')
? destinationRunwayIdent.substring(3, 4)
: destinationRunwayIdent.substring(2, 4)} ${approachName
.charAt(approachName.length - 1)}`;
console.log('NEW APPR NAME', approachName);
}
const apoprachIndex = arg.approaches
.findIndex((approach) => approach.name === plan.segments.approachSegment.procedureIdent);
.findIndex((approach) => approach.name === approachName);
const approachEnrouteTransitionIndex = arg.approaches
.findIndex((approach) => approach.transitions.map((t) => t.name === plan.segments.arrivalRunwayTransitionSegment.procedureIdent));
.findIndex((approach) => approach.transitions.map((t) => t.name === this.procedureDetails.approachTransitionIdent));

console.log('DESTINATION RUNWAY INDEX', destinationRunwayIndex);
console.log('ARRIVAL INDEX', arrivalIndex);
Expand All @@ -156,7 +256,8 @@ export class FlightPlanAsoboSync {
}
}
}
await Coherent.call('SET_CRUISE_ALTITUDE', plan.performanceData.cruiseFlightLevel * 100);

await Coherent.call('SET_CRUISE_ALTITUDE', this.cruiseFlightLevel * 100);
}
}
await Coherent.call('RECOMPUTE_ACTIVE_WAYPOINT_INDEX', 0);
Expand All @@ -165,3 +266,49 @@ export class FlightPlanAsoboSync {
}
}
}
export class ProcedureDetails {
/** The origin runway object, consisting of the index of the origin runway
* in the origin runway information and the direction. */
public originRunway: string | undefined = undefined;

/** The ICAO for the facility associated with the departure procedure. */
// public departureFacilityIcao: string | undefined = undefined;

/** The index of the departure in the origin airport information. */
public departureIdent: string | undefined = undefined;

/** The index of the departure transition in the origin airport departure information. */
public departureTransitionIdent: string | undefined = undefined;

/** The index of the selected runway in the original airport departure information. */
// public departureRunwayIdent: string | undefined = undefined;

/** The ICAO for the facility associated with the arrival procedure. */
// public arrivalFacilityIcao: string | undefined = undefined;

/** The index of the arrival in the destination airport information. */
public arrivalIdent: string | undefined = undefined;

/** The index of the arrival transition in the destination airport arrival information. */
public arrivalTransitionIdent: string | undefined = undefined;

/** The index of the selected runway transition at the destination airport arrival information. */
public arrivalRunwayTransitionIdent : string | undefined = undefined;

/** The arrival runway object, consisting of the index of the destination runway
* in the destination runway information and the direction. */
// public arrivalRunway: OneWayRunway | undefined = undefined;

/** The ICAO for the facility associated with the approach procedure. */
// public approachFacilityIcao: string | undefined = undefined;

/** The index of the apporach in the destination airport information. */
public approachIdent: string | undefined = undefined;

/** The index of the approach transition in the destination airport approach information. */
public approachTransitionIdent: string | undefined = undefined;

/** The destination runway object, consisting of the index of the destination runway
* in the destination runway information and the direction. */
public destinationRunway: string | undefined = undefined;
}