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): implement dir to with radial in/out #9761

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,30 @@
// TODO this whole thing is thales layout...

class CDUDirectToPage {
static ShowPage(mcdu, directWaypoint, wptsListIndex = 0) {
static ShowPage(mcdu, directToObject, wptsListIndex = 0, isRadialInPilotEntered = false) {
mcdu.clearDisplay();
mcdu.page.Current = mcdu.page.DirectToPage;
mcdu.returnPageCallback = () => {
CDUDirectToPage.ShowPage(mcdu, directWaypoint, wptsListIndex);
CDUDirectToPage.ShowPage(mcdu, directToObject, wptsListIndex, isRadialInPilotEntered);
};

mcdu.activeSystem = 'FMGC';

let directWaypointCell = "";
if (directWaypoint) {
directWaypointCell = directWaypoint.ident;
} else if (mcdu.flightPlanService.hasTemporary) {
mcdu.eraseTemporaryFlightPlan(() => {
CDUDirectToPage.ShowPage(mcdu);
});
return;
}

const waypointsCell = ["", "", "", "", ""];
let iMax = 5;
let eraseLabel = "";
let eraseLine = "";
let insertLabel = "";
let insertLine = "";
if (mcdu.flightPlanService.hasTemporary) {
// Invalid state, should not be able to call up DIR when a temporary exists
if (!directToObject) {
mcdu.eraseTemporaryFlightPlan(() => {
CDUDirectToPage.ShowPage(mcdu);
});
return;
}

iMax--;
eraseLabel = "\xa0DIR TO[color]amber";
eraseLine = "{ERASE[color]amber";
Expand Down Expand Up @@ -59,8 +57,12 @@ class CDUDirectToPage {
Fmgc.WaypointEntryUtils.getOrCreateWaypoint(mcdu, value, false).then((w) => {
if (w) {
mcdu.eraseTemporaryFlightPlan(() => {
mcdu.directToWaypoint(w).then(() => {
CDUDirectToPage.ShowPage(mcdu, w, wptsListIndex);
directToObject = {
nonFlightPlanFix: w
};

mcdu.directTo(directToObject).then(() => {
CDUDirectToPage.ShowPage(mcdu, directToObject, wptsListIndex);
}).catch(err => {
mcdu.setScratchpadMessage(NXFictionalMessages.internalError);
console.error(err);
Expand All @@ -79,17 +81,147 @@ class CDUDirectToPage {
});
};

// DIRECT TO
mcdu.onRightInput[1] = (s, scratchpadCallback) => {
if (!directToObject) {
mcdu.setScratchpadMessage(NXSystemMessages.notAllowed);
scratchpadCallback();
return;
}

mcdu.eraseTemporaryFlightPlan(() => {
// TODO delete is really bad
delete directToObject.withAbeam;
delete directToObject.courseIn;
delete directToObject.courseOut;

mcdu.directTo(directToObject).then(() => {
CDUDirectToPage.ShowPage(mcdu, directToObject, wptsListIndex);
}).catch(err => {
mcdu.setScratchpadMessage(NXFictionalMessages.internalError);
console.error(err);
});
});
};

// ABEAM
mcdu.onRightInput[2] = () => {
mcdu.setScratchpadMessage(NXFictionalMessages.notYetImplemented);
};
mcdu.onRightInput[3] = () => {
mcdu.setScratchpadMessage(NXFictionalMessages.notYetImplemented);

const plan = mcdu.flightPlanService.active;
const defaultRadialIn = CDUDirectToPage.computeDefaultRadialIn(plan, directToObject);

// RADIAL IN
mcdu.onRightInput[3] = (s, scratchpadCallback) => {
if (!directToObject) {
mcdu.setScratchpadMessage(NXSystemMessages.notAllowed);
scratchpadCallback();
return;
}

let course = undefined;
let isPilotEntered = false;
if (s === FMCMainDisplay.clrValue) {
if (directToObject.courseIn !== undefined && defaultRadialIn !== undefined) {
mcdu.eraseTemporaryFlightPlan(() => {
directToObject.courseIn = defaultRadialIn;

mcdu.directTo(directToObject).then(() => {
CDUDirectToPage.ShowPage(mcdu, directToObject, wptsListIndex);
}).catch(err => {
mcdu.setScratchpadMessage(NXFictionalMessages.internalError);
console.error(err);
});
});
return;
} else {
mcdu.setScratchpadMessage(NXSystemMessages.notAllowed);
scratchpadCallback();
return;
}
} else if (s === "" && defaultRadialIn !== undefined) {
course = defaultRadialIn;
} else if (/^\d{1,3}/.test(s)) {
course = parseInt(s);
if (course > 360) {
mcdu.setScratchpadMessage(NXSystemMessages.entryOutOfRange);
scratchpadCallback();
return;
}

isPilotEntered = true;
} else {
// TODO this should allow a true course
mcdu.setScratchpadMessage(NXSystemMessages.formatError);
scratchpadCallback();
return;
}

mcdu.eraseTemporaryFlightPlan(() => {
// TODO delete is really bad
delete directToObject.withAbeam;
directToObject.courseIn = course % 360;
delete directToObject.courseOut;

mcdu.directTo(directToObject).then(() => {
CDUDirectToPage.ShowPage(mcdu, directToObject, wptsListIndex, isPilotEntered);
}).catch(err => {
mcdu.setScratchpadMessage(NXFictionalMessages.internalError);
console.error(err);
});
});
};
mcdu.onRightInput[4] = () => {
mcdu.setScratchpadMessage(NXFictionalMessages.notYetImplemented);

// RADIAL OUT
mcdu.onRightInput[4] = (s, scratchpadCallback) => {
if (!directToObject) {
mcdu.setScratchpadMessage(NXSystemMessages.notAllowed);
scratchpadCallback();
return;
}

// TODO this should allow a true course
if (!/^\d{1,3}/.test(s)) {
mcdu.setScratchpadMessage(NXSystemMessages.formatError);
scratchpadCallback();
return;
}

const course = parseInt(s);
if (course > 360) {
mcdu.setScratchpadMessage(NXSystemMessages.entryOutOfRange);
scratchpadCallback();
return;
}

mcdu.eraseTemporaryFlightPlan(() => {
delete directToObject.withAbeam;
delete directToObject.courseIn;
directToObject.courseOut = course % 360;

mcdu.directTo(directToObject).then(() => {
CDUDirectToPage.ShowPage(mcdu, directToObject, wptsListIndex);
}).catch(err => {
mcdu.setScratchpadMessage(NXFictionalMessages.internalError);
console.error(err);
});
});
};

const plan = mcdu.flightPlanService.active;
let directWaypointCell = "";
if (directToObject) {
if (directToObject.flightPlanLegIndex !== undefined) {
// Don't just fetch the leg at the index, since the plan might've sequenced after this page was called up
const directToLeg = plan.maybeElementAt(directToObject.flightPlanLegIndex);

if (directToLeg && directToLeg.isDiscontinuity === false) {
directWaypointCell = directToLeg.ident;
}
} else if (directToObject.nonFlightPlanFix !== undefined) {
directWaypointCell = directToObject.nonFlightPlanFix.ident;
}
}

let i = 0;
let cellIter = 0;
Expand All @@ -116,8 +248,12 @@ class CDUDirectToPage {
if (waypointsCell[cellIter]) {
mcdu.onLeftInput[cellIter + 1] = () => {
mcdu.eraseTemporaryFlightPlan(() => {
mcdu.directToLeg(legIndex).then(() => {
CDUDirectToPage.ShowPage(mcdu, leg.terminationWaypoint(), wptsListIndex);
directToObject = {
flightPlanLegIndex: legIndex
};

mcdu.directTo(directToObject).then(() => {
CDUDirectToPage.ShowPage(mcdu, directToObject, wptsListIndex);
}).catch(err => {
mcdu.setScratchpadMessage(NXFictionalMessages.internalError);
console.error(err);
Expand All @@ -139,32 +275,95 @@ class CDUDirectToPage {
if (wptsListIndex < totalWaypointsCount - 5) {
mcdu.onUp = () => {
wptsListIndex++;
CDUDirectToPage.ShowPage(mcdu, directWaypoint, wptsListIndex);
CDUDirectToPage.ShowPage(mcdu, directToObject, wptsListIndex);
};
up = true;
}
if (wptsListIndex > 0) {
mcdu.onDown = () => {
wptsListIndex--;
CDUDirectToPage.ShowPage(mcdu, directWaypoint, wptsListIndex);
CDUDirectToPage.ShowPage(mcdu, directToObject, wptsListIndex);
};
down = true;
}

const isWithAbeamSelected = directToObject && directToObject.withAbeam;
const canSelectWithAbeam = directToObject && !isWithAbeamSelected;

const isRadialInSelected = directToObject && directToObject.courseIn !== undefined;
const canSelectRadialIn = directToObject && !isRadialInSelected;

let radialInText = "[ ]°";
if (isRadialInSelected) {
radialInText = isRadialInPilotEntered
? `${directToObject.courseIn.toFixed(0).padStart(3, '0')}°`
: `{small}${directToObject.courseIn.toFixed(0).padStart(3, '0')}°{end}`;
} else if (defaultRadialIn !== undefined) {
radialInText = `{small}${defaultRadialIn.toFixed(0).padStart(3, '0')}°{end}`;
}

const isRadialOutSelected = directToObject && directToObject.courseOut !== undefined;
const canSelectRadialOut = directToObject && !isRadialOutSelected;
const radialOut = isRadialOutSelected
? `${directToObject.courseOut.toFixed(0).padStart(3, '0')}°`
: "[ ]°";

const isDirectToSelected = directToObject && !isWithAbeamSelected && !isRadialInSelected && !isRadialOutSelected;
const canSelectDirectTo = directToObject && !isDirectToSelected;

mcdu.setArrows(up, down, false ,false);
mcdu.setTemplate([
["DIR TO"],
["\xa0WAYPOINT", "DIST\xa0", "UTC"],
["*[" + (directWaypointCell ? directWaypointCell : "\xa0\xa0\xa0\xa0\xa0") + "][color]cyan", "---", "----"],
["\xa0F-PLN WPTS"],
[waypointsCell[0], "DIRECT TO[color]cyan"],
[waypointsCell[0], `DIRECT TO ${canSelectDirectTo ? "}" : " "}[color]${isDirectToSelected ? "yellow" : "cyan"}`],
["", "WITH\xa0"],
[waypointsCell[1], "ABEAM PTS[color]cyan"],
[waypointsCell[1], `ABEAM PTS ${canSelectWithAbeam ? "}" : " "}[color]${isWithAbeamSelected ? "yellow" : "cyan"}[color]inop`],
["", "RADIAL IN\xa0"],
[waypointsCell[2], "[ ]°[color]cyan"],
[waypointsCell[2], `${radialInText} ${canSelectRadialIn ? "}" : " "}[color]${isRadialInSelected ? "yellow" : "cyan"}`],
["", "RADIAL OUT\xa0"],
[waypointsCell[3], "[ ]°[color]cyan"],
[waypointsCell[3], `${radialOut} ${canSelectRadialOut ? "}" : " "}[color]${isRadialOutSelected ? "yellow" : "cyan"}`],
[eraseLabel, insertLabel],
[eraseLine ? eraseLine : waypointsCell[4], insertLine]
]);
}

/**
*
* @param {import("../../../../../../../../../systems/fmgc/src/flightplanning/plans/FlightPlan").FlightPlan} plan
* @param {import("../../../../../../../../../systems/fmgc/src/flightplanning/types/DirectTo").DirectTo} directToObject
* @returns {number | undefined}
*/
static computeDefaultRadialIn(plan, directToObject) {
if (!directToObject || directToObject.flightPlanLegIndex === undefined) {
return undefined;
}

const directToLeg = plan.maybeElementAt(directToObject.flightPlanLegIndex);
if (!directToLeg || directToLeg.isDiscontinuity === true || directToLeg.terminationWaypoint() === null) {
return undefined;
}

const maybeLegBefore = plan.maybeElementAt(directToObject.flightPlanLegIndex - 1);
const maybeLegAfter = plan.maybeElementAt(directToObject.flightPlanLegIndex + 1);

if (directToObject.flightPlanLegIndex > plan.activeLegIndex && maybeLegBefore && maybeLegBefore.isDiscontinuity === false && maybeLegBefore.terminationWaypoint() !== null) {
const trueRadialIn = Avionics.Utils.computeGreatCircleHeading(directToLeg.terminationWaypoint().location, maybeLegBefore.terminationWaypoint().location);

return A32NX_Util.trueToMagnetic(
trueRadialIn,
A32NX_Util.getRadialMagVar(directToLeg.terminationWaypoint())
);
} else if (maybeLegAfter && maybeLegAfter.isDiscontinuity === false && maybeLegAfter.terminationWaypoint() !== null) {
const trueRadialIn = 180 + Avionics.Utils.computeGreatCircleHeading(directToLeg.terminationWaypoint().location, maybeLegAfter.terminationWaypoint().location);

return A32NX_Util.trueToMagnetic(
trueRadialIn,
A32NX_Util.getRadialMagVar(directToLeg.terminationWaypoint())
);
}

return undefined;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,15 @@ class FMCMainDisplay extends BaseAirliners {
this.dataManager = new Fmgc.DataManager(this);

this.efisInterfaces = { L: new Fmgc.EfisInterface('L', this.currFlightPlanService), R: new Fmgc.EfisInterface('R', this.currFlightPlanService) };
this.guidanceController = new Fmgc.GuidanceController(this.bus, this, this.currFlightPlanService, this.efisInterfaces, Fmgc.a320EfisRangeSettings, Fmgc.A320AircraftConfig);
this.navigation = new Fmgc.Navigation(this.bus, this.currFlightPlanService);
this.guidanceController = new Fmgc.GuidanceController(this.bus,
this,
this.currFlightPlanService,
this.efisInterfaces,
this.navigation,
Fmgc.a320EfisRangeSettings,
Fmgc.A320AircraftConfig
);
this.efisSymbols = new Fmgc.EfisSymbols(
this.bus,
this.guidanceController,
Expand Down Expand Up @@ -5061,32 +5068,10 @@ class FMCMainDisplay extends BaseAirliners {
}

/**
* Modifies the active flight plan to go direct to a specific waypoint, not necessarily in the flight plan
* @param {import('msfs-navdata').Waypoint} waypoint
*/
async directToWaypoint(waypoint) {
// FIXME fm pos
const adirLat = ADIRS.getLatitude();
const adirLong = ADIRS.getLongitude();
const trueTrack = ADIRS.getTrueTrack();

if (!adirLat.isNormalOperation() || !adirLong.isNormalOperation() || !trueTrack.isNormalOperation()) {
return;
}

const ppos = {
lat: adirLat.value,
long: adirLong.value,
};

await this.flightPlanService.directToWaypoint(ppos, trueTrack.value, waypoint);
}

/**
* Modifies the active flight plan to go direct to a specific leg
* @param {number} legIndex index of leg to go direct to
* Modifies the active flight plan to perform a DIR to operation
* @param {import('../../../../../../../../../systems/fmgc/src/flightplanning/types/DirectTo').DirectTo} directTo
*/
async directToLeg(legIndex) {
async directTo(directTo) {
// FIXME fm pos
const adirLat = ADIRS.getLatitude();
const adirLong = ADIRS.getLongitude();
Expand All @@ -5101,7 +5086,7 @@ class FMCMainDisplay extends BaseAirliners {
long: adirLong.value,
};

await this.flightPlanService.directToLeg(ppos, trueTrack.value, legIndex);
await this.flightPlanService.directTo(ppos, trueTrack.value, directTo);
}

/**
Expand Down
Loading
Loading