Skip to content

Commit

Permalink
fix(fms): nav db info
Browse files Browse the repository at this point in the history
  • Loading branch information
tracernz committed May 26, 2024
1 parent a52df47 commit 098e600
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,72 +2,31 @@
//
// SPDX-License-Identifier: GPL-3.0

const months = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
const monthLength = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
const DB_MONTHS = Object.freeze({
'01': "JAN",
'02': "FEB",
'03': "MAR",
'04': "APR",
'05': "MAY",
'06': "JUN",
'07': "JUL",
'08': "AUG",
'09': "SEP",
'10': "OCT",
'11': "NOV",
'12': "DEC"
});

// Honeywell H4+ feature only
const confirmDataBaseSwitch = false;

function findNewMonthIndex(index) {
if (index === 0) {
return 11;
} else {
return index - 1;
}
}

function lessThan10(num) {
if (num < 10) {
return `0${num}`;
} else {
return num;
}
}

function calculateActiveDate(date) {
if (date.length === 13) {
const startMonth = date.slice(0, 3);
const startDay = date.slice(3, 5);

const endMonth = date.slice(5, 8);
const endDay = date.slice(8, 10);
function calculateActiveDate(dbIdent) {
const effDay = dbIdent.effectiveFrom.substring(8);
const effMonth = dbIdent.effectiveFrom.substring(5, 7);
const expDay = dbIdent.effectiveTo.substring(8);
const expMonth = dbIdent.effectiveTo.substring(5, 7);

return `${startDay}${startMonth}-${endDay}${endMonth}`;
} else {
return date;
}
}

function calculateSecDate(date) {
if (date.length === 13) {
const primStartMonth = date.slice(0, 3);
const primStartDay = date.slice(3, 5);

const primStartMonthIndex = months.findIndex((item) => item === primStartMonth);

if (primStartMonthIndex === -1) {
return "ERR";
}

let newEndMonth = primStartMonth;
let newEndDay = primStartDay - 1;

let newStartDay = newEndDay - 27;
let newStartMonth = primStartMonth;

if (newEndDay === 0) {
newEndMonth = months[findNewMonthIndex(primStartMonthIndex)];
newEndDay = monthLength[findNewMonthIndex(primStartMonthIndex)];
}

if (newStartDay <= 0) {
newStartMonth = months[findNewMonthIndex(primStartMonthIndex)];
newStartDay = monthLength[findNewMonthIndex(primStartMonthIndex)] + newStartDay;
}

return `${lessThan10(newStartDay)}${newStartMonth}-${lessThan10(newEndDay)}${newEndMonth}`;
} else {
return "ERR";
}
return `${effDay}${DB_MONTHS[effMonth]}-${expDay}${DB_MONTHS[expMonth]}`;
}

async function switchDataBase(mcdu) {
Expand All @@ -86,7 +45,6 @@ class CDUIdentPage {
mcdu.page.Current = mcdu.page.IdentPage;
mcdu.activeSystem = "FMGC";

const date = mcdu.getNavDataDateRange();
const stored = mcdu.dataManager.numberOfStoredElements();

let storedTitleCell = "";
Expand Down Expand Up @@ -137,20 +95,24 @@ class CDUIdentPage {
};
}

const dbCycle = mcdu.getNavDatabaseIdent();
const navCycleDates = dbCycle === null ? '' : calculateActiveDate(dbCycle);
const navSerial = dbCycle === null ? '' : `${dbCycle.provider.substring(0, 2).toUpperCase()}${dbCycle.airacCycle}0001`;

// H4+ only confirm prompt
if (confirmDataBaseSwitch) {
secondaryDBTopLine =
confirmType === ConfirmType.SwitchDataBase
? "{amber}{small} " + calculateActiveDate(date) + "{end}"
: "\xa0SECOND NAV DATA BASE";
? `{amber}{small}\xa0${navCycleDates}{end}`
: "\xa0SECOND\xa0NAV\xa0DATA\xa0BASE";
secondaryDBSubLine =
confirmType === ConfirmType.SwitchDataBase
? "{amber}{CANCEL SWAP CONFIRM*{end}"
: "{small}{" + calculateActiveDate(date) + "{end}[color]cyan";
? "{amber}{CANCEL\xa0\xa0\xa0\xa0SWAP\xa0CONFIRM*{end}"
: `{small}{cyan}{${navCycleDates}{end}{end}`;
} else {
secondaryDBTopLine = "\xa0SECOND NAV DATA BASE";
secondaryDBTopLine = "\xa0SECOND\xa0NAV\xa0DATA\xa0BASE";
secondaryDBSubLine =
"{small}{" + calculateActiveDate(date) + "{end}[color]cyan";
`{small}{cyan}{${navCycleDates}{end}{end}`;
}

mcdu.leftInputDelay[2] = () => {
Expand Down Expand Up @@ -189,8 +151,8 @@ class CDUIdentPage {
["LEAP-1A26[color]green"],
["\xa0ACTIVE NAV DATA BASE"],
[
"\xa0" + calculateActiveDate(date) + "[color]cyan",
"AIRAC[color]green",
`{cyan}\xa0${navCycleDates}{end}`,
`{green}${navSerial}{end}`,
],
[secondaryDBTopLine],
[secondaryDBSubLine],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ class FMCMainDisplay extends BaseAirliners {
this.arincTransitionLevel,
this.arincEisWord2,
];

this.navDbIdent = null;
}

Init() {
Expand Down Expand Up @@ -311,6 +313,8 @@ class FMCMainDisplay extends BaseAirliners {
}, 15000);

SimVar.SetSimVarValue('L:A32NX_FM_LS_COURSE', 'number', -1);

this.navigationDatabaseService.activeDatabase.getDatabaseIdent().then((dbIdent) => this.navDbIdent = dbIdent);
}

initVariables(resetTakeoffData = true) {
Expand Down Expand Up @@ -4460,10 +4464,6 @@ class FMCMainDisplay extends BaseAirliners {
return h * 60 + m;
}

//TODO: can this be util?
getNavDataDateRange() {
return SimVar.GetGameVarValue("FLIGHT NAVDATA DATE RANGE", "string");
}
/**
* Generic function which returns true if engine(index) is ON (N2 > 20)
* @returns {boolean}
Expand Down Expand Up @@ -5082,6 +5082,14 @@ class FMCMainDisplay extends BaseAirliners {

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

/**
* Gets the navigation database ident (including cycle info).
* @returns {import('msfs-navdata').DatabaseIdent | null}.
*/
getNavDatabaseIdent() {
return this.navDbIdent;
}
}

FMCMainDisplay.clrValue = "\xa0\xa0\xa0\xa0\xa0CLR";
Expand Down
7 changes: 6 additions & 1 deletion fbw-a32nx/src/systems/fmgc/src/NavigationDatabase.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2021-2022 FlyByWire Simulations
// Copyright (c) 2021-2024 FlyByWire Simulations
// Copyright (c) 2021-2022 Synaptic Simulations
//
// SPDX-License-Identifier: GPL-3.0
Expand All @@ -7,6 +7,7 @@ import {
Airport,
Airway,
Database,
DatabaseIdent,
Fix,
IlsNavaid,
MsfsBackend,
Expand Down Expand Up @@ -81,4 +82,8 @@ export class NavigationDatabase {
// This does not work in the MSFS backend
return this.backendDatabase.getAirways([ident]);
}

public getDatabaseIdent(): Promise<DatabaseIdent> {
return this.backendDatabase.getDatabaseIdent();
}
}
70 changes: 49 additions & 21 deletions fbw-common/src/systems/navdata/client/backends/Msfs/Msfs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright (c) 2021, 2022 FlyByWire Simulations
// Copyright (c) 2021, 2022, 2024 FlyByWire Simulations
// Copyright (c) Microsoft Corporation
// SPDX-License-Identifier: GPL-3.0

/* eslint-disable no-await-in-loop */
Expand Down Expand Up @@ -63,6 +64,13 @@ declare class CoherentNearestSearchSession implements NearestSearchSession<strin
}

export class MsfsBackend implements DataInterface {
/** Duration of an AIRAC cycle (28 days) in milliseconds. */
public static readonly CYCLE_DURATION = 86400_000 * 28;

private static readonly MSFS_DATE_RANGE_REGEX = /([A-Z]{3})(\d\d?)([A-Z]{3})(\d\d?)\/(\d\d)/;

private static dateCache = new Date();

private cache: FacilityCache;

private mapping: MsfsMapping;
Expand Down Expand Up @@ -112,30 +120,50 @@ export class MsfsBackend implements DataInterface {

/** @inheritdoc */
public async getDatabaseIdent(): Promise<DatabaseIdent> {
const out = {
provider: 'Msfs',
airacCycle: '',
effectiveFrom: '',
effectiveTo: '',
};

// "APR21MAY18/22"
const range = SimVar.GetGameVarValue('FLIGHT NAVDATA DATE RANGE', 'string');
const months = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'];
const fromMonth = months.indexOf(range.substring(0, 3)) + 1;
const fromDay = parseInt(range.substring(3, 5));
const toMonth = months.indexOf(range.substring(5, 8)) + 1;
const toDay = parseInt(range.substring(8, 10));
const fromYear = parseInt(range.substring(11, 13));

let toYear = fromYear;
if (fromMonth === 12 && toMonth < 12) {
toYear++;
const facilitiesDateRange = SimVar.GetGameVarValue('FLIGHT NAVDATA DATE RANGE', 'string');

const match = facilitiesDateRange.match(MsfsBackend.MSFS_DATE_RANGE_REGEX);
if (match === null) {
console.warn('AiracUtils: Failed to parse facilitiesDateRange', facilitiesDateRange);
return out;
}

// the to date is supposed to overlap the next cycle
const toDate = new Date(Date.UTC(toYear, toMonth - 1, toDay));
toDate.setUTCDate(toDay + 1);
const [, effMonth, effDay, expMonth, expDay, expYear] = match;

return {
provider: 'Msfs', // TODO navi or navblue
airacCycle: '', // TODO
effectiveFrom: iso8601CalendarDate(fromYear, fromMonth, fromDay),
effectiveTo: iso8601CalendarDate(toDate.getUTCFullYear(), toDate.getUTCMonth() + 1, toDate.getUTCDate()),
};
const effDate = new Date(`${effMonth}-${effDay}-${expYear} UTC`);
const expDate = new Date(`${expMonth}-${expDay}-${expYear} UTC`);

// We need to work around a bug where the sim gives the year of the expiration date rather than the effective date.
if (effDate.getTime() > expDate.getTime()) {
effDate.setUTCFullYear(effDate.getUTCFullYear() - 1);
}

const effectiveTimestamp = effDate.getTime();
MsfsBackend.dateCache.setTime(effectiveTimestamp);
const expirationTimestamp = effectiveTimestamp + MsfsBackend.CYCLE_DURATION;
const realExpDate = new Date(expirationTimestamp);
const january1 = Date.UTC(MsfsBackend.dateCache.getUTCFullYear(), 0, 1);
const january1Delta = effectiveTimestamp - january1;
const cycle = Math.trunc(january1Delta / MsfsBackend.CYCLE_DURATION) + 1;

out.airacCycle = `${(MsfsBackend.dateCache.getUTCFullYear() % 100).toString().padStart(2, '0')}${cycle.toString().padStart(2, '0')}`;
// For reasons of brain death JS date month is 0-based while day is 1-based, so we add 1 to the month.
out.effectiveFrom = iso8601CalendarDate(effDate.getUTCFullYear(), effDate.getUTCMonth() + 1, effDate.getUTCDate());
out.effectiveTo = iso8601CalendarDate(
realExpDate.getUTCFullYear(),
realExpDate.getUTCMonth() + 1,
realExpDate.getUTCDate(),
);

return out;
}

/** @inheritdoc */
Expand Down

0 comments on commit 098e600

Please sign in to comment.