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: GSX Integration #7695

Merged
merged 23 commits into from Feb 11, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/CHANGELOG.md
Expand Up @@ -26,9 +26,11 @@
1. [FLIGHTMODEL] Update gear drag - @donstim (donbikes#4084)
1. [FLIGHT MODEL/EFB] Modified empty weight cg and loading station/fuel tank locations - @donstim (donbikes#4084)
1. [HYD] Fix gear sequence starting when failing prox sensor - @Crocket63 (crocket)
1. [MISC] Added aircraft version check and uer notification - @frankkopp (Frank Kopp)
1. [MISC] Added aircraft version check and uer notification - @frankkopp (Frank Kopp)
1. [EFB] Added QuickControls to flyPad StatusBar - @Benjozork (Benjamin Dupont) @frankkopp (Frank Kopp)
1. [SOUND] Fix announcements playing twice and adding check for power to PA - @frankkopp (Frank Kopp)
1. [EFB] GSX Integration - Lucky38i (Lucky38#3550)

## 0.9.0

Expand Down
@@ -0,0 +1,63 @@
[aircraft]
icaotype = A20N
parkingbrakestest = (L:A32NX_PARK_BRAKE_LEVER_POS,number)
nosegear = 8.26
refueling = 0
battery = 1
pushbackraise = 1
pushbackdummyevent = 0
pushbackcheckengines = 0
iscargo = 0
trafficcones = 1
Copy link
Member

Choose a reason for hiding this comment

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

I guess we should disable GSX cones as we have our own. What do you think?

preferredexit = 0
wingrootpos = 2.36 -1.89 0.85
wingtippos = 15.70 -7.41 1.84
fuelpos = 9.63 -3.39 0.88
waterpos = 1.28 -18.07 0.52
lavatorypos = 0.59 -18.07 0.12
gpupos = 3.29 11.06 -1.56
bypasspinpos = 0.08 8.45 -1.42
engine1pos = -5.67 0.50 -0.81
engine2pos = 5.67 0.50 -0.80
engine3pos = 0.00 0.00 0.00
engine4pos = 0.00 0.00 0.00

[exit1]
pos = -1.88 8.51 0.86 4.00
code = 1
name = Passenger Door
embeddedStair = 0

[exit2]
remove = 1

[exit3]
remove = 1

[exit4]
remove = 1

[service1]
pos = 2.01 8.50 0.99 -5.00
code = 4
name = Service Door
embeddedStair = 0

[service2]
pos = 1.80 -16.15 1.03 12.00
code = 4
name = Service Door AFT
embeddedStair = 0

[cargo1]
pos = 0.85 5.50 -0.40 -0.39
code = 6
name = Cargo Door
embeddedStair = 0
uldcode = BELT

[cargo2]
remove = 1

[cargomain]
remove = 1
242 changes: 192 additions & 50 deletions flybywire-aircraft-a320-neo/html_ui/Pages/A32NX_Core/A32NX_Boarding.js
Expand Up @@ -25,6 +25,20 @@ class A32NX_Boarding {
const payloadConstruct = new A32NX_PayloadConstructor();
this.paxStations = payloadConstruct.paxStations;
this.cargoStations = payloadConstruct.cargoStations;

// GSX Helpers
this.passengersLeftToFillOrEmpty = 0;
this.prevBoardedOrDeboarded = 0;
this.prevCargoDeboardedPercentage = 0;

this.gsxStates = {
AVAILABLE: 1,
NOT_AVAILABLE: 2,
BYPASSED: 3,
REQUESTED: 4,
PERFORMING: 5,
COMPLETED: 6,
};
}

async init() {
Expand Down Expand Up @@ -72,6 +86,7 @@ class A32NX_Boarding {
return SimVar.SetSimVarValue(`PAYLOAD STATION WEIGHT:${paxStation.stationIndex}`, getUserUnit(), paxStation.pax * PAX_WEIGHT);
}));
}

loadCargoPayload() {
return Promise.all(Object.values(this.cargoStations).map(loadStation => {
return SimVar.SetSimVarValue(`PAYLOAD STATION WEIGHT:${loadStation.stationIndex}`, getUserUnit(), loadStation.load);
Expand All @@ -86,25 +101,7 @@ class A32NX_Boarding {
}
}

async update(_deltaTime) {
this.time += _deltaTime;

const boardingStartedByUser = SimVar.GetSimVarValue("L:A32NX_BOARDING_STARTED_BY_USR", "Bool");
const boardingRate = NXDataStore.get("CONFIG_BOARDING_RATE", 'REAL');

if (!boardingStartedByUser) {
return;
}

if ((!airplaneCanBoard() && boardingRate == 'REAL') || (!airplaneCanBoard() && boardingRate == 'FAST')) {
return;
}

const currentPax = Object.values(this.paxStations).map((station) => SimVar.GetSimVarValue(`L:${station.simVar}`, "Number")).reduce((acc, cur) => acc + cur);
const paxTarget = Object.values(this.paxStations).map((station) => SimVar.GetSimVarValue(`L:${station.simVar}_DESIRED`, "Number")).reduce((acc, cur) => acc + cur);
const currentLoad = Object.values(this.cargoStations).map((station) => SimVar.GetSimVarValue(`L:${station.simVar}`, "Number")).reduce((acc, cur) => acc + cur);
const loadTarget = Object.values(this.cargoStations).map((station) => SimVar.GetSimVarValue(`L:${station.simVar}_DESIRED`, "Number")).reduce((acc, cur) => acc + cur);

areStationsFilled() {
let isAllPaxStationFilled = true;
for (const _station of Object.values(this.paxStations)) {
const stationCurrentPax = SimVar.GetSimVarValue(`L:${_station.simVar}`, "Number");
Expand All @@ -126,39 +123,117 @@ class A32NX_Boarding {
break;
}
}
return [isAllPaxStationFilled, isAllCargoStationFilled];
}

// Sound Controllers
if ((currentPax < paxTarget) && boardingStartedByUser == true) {
await SimVar.SetSimVarValue("L:A32NX_SOUND_PAX_BOARDING", "Bool", true);
this.isBoarding = true;
} else {
await SimVar.SetSimVarValue("L:A32NX_SOUND_PAX_BOARDING", "Bool", false);
generateMsDelay(boardingRate) {
switch (boardingRate) {
case 'FAST':
return 1000;
case 'REAL':
return 5000;
default:
return 5000;
}
}

await SimVar.SetSimVarValue("L:A32NX_SOUND_PAX_DEBOARDING", "Bool", currentPax > paxTarget && boardingStartedByUser == true);
manageGsxBoarding(boardState) {
switch (boardState) {
//GSX doesn't emit 100% boarding, this case is to ensure cargo is 100% filled to target
case this.gsxStates.COMPLETED:
for (const loadStation of Object.values(this.cargoStations)) {
const stationCurrentLoadTarget = SimVar.GetSimVarValue(`L:${loadStation.simVar}_DESIRED`, "Number");
this.fillCargoStation(loadStation, stationCurrentLoadTarget);
}
break;
case this.gsxStates.PERFORMING:
const gsxBoardingTotal = SimVar.GetSimVarValue("L:FSDT_GSX_NUMPASSENGERS_BOARDING_TOTAL", "Number");
this.passengersLeftToFillOrEmpty = gsxBoardingTotal - this.prevBoardedOrDeboarded;

for (const paxStation of Object.values(this.paxStations).reverse()) {
const stationCurrentPax = SimVar.GetSimVarValue(`L:${paxStation.simVar}`, "Number");
const stationCurrentPaxTarget = SimVar.GetSimVarValue(`L:${paxStation.simVar}_DESIRED`, "Number");
if (this.passengersLeftToFillOrEmpty <= 0) {
break;
}

const loadAmount = Math.min(this.passengersLeftToFillOrEmpty, paxStation.seats);
if (stationCurrentPax < stationCurrentPaxTarget) {
this.fillPaxStation(paxStation, stationCurrentPax + loadAmount);
this.passengersLeftToFillOrEmpty -= loadAmount;
}
}
this.prevBoardedOrDeboarded = gsxBoardingTotal;

if ((currentPax == paxTarget) && this.isBoarding == true) {
await SimVar.SetSimVarValue("L:A32NX_SOUND_BOARDING_COMPLETE", "Bool", true);
this.isBoarding = false;
return;
const gsxCargoPercentage = SimVar.GetSimVarValue("L:FSDT_GSX_BOARDING_CARGO_PERCENT", "Number");
for (const loadStation of Object.values(this.cargoStations)) {
const stationCurrentLoadTarget = SimVar.GetSimVarValue(`L:${loadStation.simVar}_DESIRED`, "Number");

const loadAmount = stationCurrentLoadTarget * (gsxCargoPercentage / 100);
this.fillCargoStation(loadStation, loadAmount);
}
break;
default:
break;
}
await SimVar.SetSimVarValue("L:A32NX_SOUND_BOARDING_COMPLETE", "Bool", false);
}

await SimVar.SetSimVarValue("L:A32NX_SOUND_PAX_AMBIENCE", "Bool", currentPax > 0);
manageGsxDeBoarding(boardState) {
switch (boardState) {

if (currentPax === paxTarget && currentLoad === loadTarget && isAllPaxStationFilled && isAllCargoStationFilled) {
// Finish boarding
this.boardingState = "finished";
await SimVar.SetSimVarValue("L:A32NX_BOARDING_STARTED_BY_USR", "Bool", false);
// this is a backup state incase the EFB page isn't open to set desired PAX/Cargo to 0
case this.gsxStates.REQUESTED:
Object.values(this.paxStations)
.reverse()
.forEach((paxStation) => SimVar.SetSimVarValue(`L:${paxStation.simVar}_DESIRED`, "Number", parseInt(0)));

} else if ((currentPax < paxTarget) || (currentLoad < loadTarget)) {
this.boardingState = "boarding";
} else if ((currentPax === paxTarget) && (currentLoad === loadTarget)) {
this.boardingState = "finished";
Object.values(this.cargoStations)
.forEach((loadStation) => SimVar.SetSimVarValue(`L:${loadStation.simVar}_DESIRED`, "Number", parseInt(0)));
break;

// GSX doesn't emit 100% deboard percentage, this is set to ensure cargo completetly empties
case this.gsxStates.COMPLETED:
for (const loadStation of Object.values(this.cargoStations)) {
this.fillCargoStation(loadStation, 0);
}
break;

case this.gsxStates.PERFORMING:
const gsxDeBoardingTotal = SimVar.GetSimVarValue("L:FSDT_GSX_NUMPASSENGERS_DEBOARDING_TOTAL", "Number");
this.passengersLeftToFillOrEmpty = gsxDeBoardingTotal - this.prevBoardedOrDeboarded;

for (const paxStation of Object.values(this.paxStations).reverse()) {
const stationCurrentPax = SimVar.GetSimVarValue(`L:${paxStation.simVar}`, "Number");
const stationCurrentPaxTarget = SimVar.GetSimVarValue(`L:${paxStation.simVar}_DESIRED`, "Number");
if (this.passengersLeftToFillOrEmpty <= 0) {
break;
}

if (stationCurrentPax > stationCurrentPaxTarget) {
this.fillPaxStation(paxStation, stationCurrentPax - Math.min(this.passengersLeftToFillOrEmpty, paxStation.seats));
this.passengersLeftToFillOrEmpty -= Math.min(this.passengersLeftToFillOrEmpty, paxStation.seats);
}
}
this.prevBoardedOrDeboarded = gsxDeBoardingTotal;

const gsxCargoDeBoardPercentage = SimVar.GetSimVarValue("L:FSDT_GSX_DEBOARDING_CARGO_PERCENT", "Number");
for (const loadStation of Object.values(this.cargoStations)) {
if (this.prevCargoDeboardedPercentage == gsxCargoDeBoardPercentage) {
break;
}
const stationCurrentLoad = SimVar.GetSimVarValue(`L:${loadStation.simVar}`, "Number");

const loadAmount = stationCurrentLoad * ((100 - gsxCargoDeBoardPercentage) / 100);
this.fillCargoStation(loadStation, loadAmount);
}
this.prevCargoDeboardedPercentage = gsxCargoDeBoardPercentage;
default:
break;
}
}

async manageBoarding(boardingRate) {
if (boardingRate == 'INSTANT') {
// Instant
for (const paxStation of Object.values(this.paxStations)) {
const stationCurrentPaxTarget = SimVar.GetSimVarValue(`L:${paxStation.simVar}_DESIRED`, "Number");
await this.fillPaxStation(paxStation, stationCurrentPaxTarget);
Expand All @@ -172,15 +247,7 @@ class A32NX_Boarding {
return;
}

let msDelay = 5000;

if (boardingRate == 'FAST') {
msDelay = 1000;
}

if (boardingRate == 'REAL') {
msDelay = 5000;
}
const msDelay = this.generateMsDelay(boardingRate);

if (this.time > msDelay) {
this.time = 0;
Expand Down Expand Up @@ -221,4 +288,79 @@ class A32NX_Boarding {
this.loadCargoPayload();
}
}

async manageSoundControllers(currentPax, paxTarget, boardingStartedByUser) {
if ((currentPax < paxTarget) && boardingStartedByUser == true) {
await SimVar.SetSimVarValue("L:A32NX_SOUND_PAX_BOARDING", "Bool", true);
this.isBoarding = true;
} else {
await SimVar.SetSimVarValue("L:A32NX_SOUND_PAX_BOARDING", "Bool", false);
}

await SimVar.SetSimVarValue("L:A32NX_SOUND_PAX_DEBOARDING", "Bool", currentPax > paxTarget && boardingStartedByUser == true);

if ((currentPax == paxTarget) && this.isBoarding == true) {
await SimVar.SetSimVarValue("L:A32NX_SOUND_BOARDING_COMPLETE", "Bool", true);
this.isBoarding = false;
return;
}
await SimVar.SetSimVarValue("L:A32NX_SOUND_BOARDING_COMPLETE", "Bool", false);

await SimVar.SetSimVarValue("L:A32NX_SOUND_PAX_AMBIENCE", "Bool", currentPax > 0);
}

async manageBoardingState(currentPax, paxTarget) {
const currentLoad = Object.values(this.cargoStations).map((station) => SimVar.GetSimVarValue(`L:${station.simVar}`, "Number")).reduce((acc, cur) => acc + cur);
const loadTarget = Object.values(this.cargoStations).map((station) => SimVar.GetSimVarValue(`L:${station.simVar}_DESIRED`, "Number")).reduce((acc, cur) => acc + cur);
const [isAllPaxStationFilled, isAllCargoStationFilled] = this.areStationsFilled();

if (currentPax === paxTarget && currentLoad === loadTarget && isAllPaxStationFilled && isAllCargoStationFilled) {
// Finish boarding
this.boardingState = "finished";
await SimVar.SetSimVarValue("L:A32NX_BOARDING_STARTED_BY_USR", "Bool", false);

} else if ((currentPax < paxTarget) || (currentLoad < loadTarget)) {
this.boardingState = "boarding";
} else if ((currentPax === paxTarget) && (currentLoad === loadTarget)) {
this.boardingState = "finished";
}
}

async update(_deltaTime) {
this.time += _deltaTime;

const gsxPayloadSyncEnabled = NXDataStore.get("GSX_PAYLOAD_SYNC", 0);

if (gsxPayloadSyncEnabled === '1') {
const gsxBoardState = Math.round(SimVar.GetSimVarValue("L:FSDT_GSX_BOARDING_STATE", "Number"));
const gsxDeBoardState = Math.round(SimVar.GetSimVarValue("L:FSDT_GSX_DEBOARDING_STATE", "Number"));

this.manageGsxDeBoarding(gsxDeBoardState);
this.manageGsxBoarding(gsxBoardState);

this.loadPaxPayload();
this.loadCargoPayload();

} else {
const boardingStartedByUser = SimVar.GetSimVarValue("L:A32NX_BOARDING_STARTED_BY_USR", "Bool");
const boardingRate = NXDataStore.get("CONFIG_BOARDING_RATE", 'REAL');

if (!boardingStartedByUser) {
return;
}

if ((!airplaneCanBoard() && boardingRate == 'REAL') || (!airplaneCanBoard() && boardingRate == 'FAST')) {
return;
}

const currentPax = Object.values(this.paxStations).map((station) => SimVar.GetSimVarValue(`L:${station.simVar}`, "Number")).reduce((acc, cur) => acc + cur);
const paxTarget = Object.values(this.paxStations).map((station) => SimVar.GetSimVarValue(`L:${station.simVar}_DESIRED`, "Number")).reduce((acc, cur) => acc + cur);

await this.manageSoundControllers(currentPax, paxTarget, boardingStartedByUser);

await this.manageBoardingState(currentPax, paxTarget);

this.manageBoarding(boardingRate);
}
}
}