Initial GSX Sync for fuel

This commit is contained in:
Kilian Hofmann 2026-02-13 02:50:43 +01:00
parent 122a93461d
commit 69680ced03
20 changed files with 153 additions and 62 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "tfdidesign-md11-load-manager", "name": "tfdidesign-md11-load-manager",
"version": "0.2.8", "version": "0.2.10",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",

View File

@ -11,10 +11,11 @@ interface SBEntryProps {
WASMData: WASMDataF; WASMData: WASMDataF;
loadingState: LoadingState; loadingState: LoadingState;
gsxActive: boolean; gsxActive: boolean;
gsxFuelActive: boolean;
setLoadingState: (newState: LoadingState) => void; setLoadingState: (newState: LoadingState) => void;
} }
const SBEntryF: FC<SBEntryProps> = ({ WASMData, loadingState, gsxActive, setLoadingState }) => { const SBEntryF: FC<SBEntryProps> = ({ WASMData, loadingState, gsxActive, gsxFuelActive, setLoadingState }) => {
const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget); const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget);
const [fuel, setFuel] = useState(Math.round(WASMData.targetPayload.fuel)); const [fuel, setFuel] = useState(Math.round(WASMData.targetPayload.fuel));
const [SBInFlight, setSBInFlight] = useState(false); const [SBInFlight, setSBInFlight] = useState(false);
@ -102,7 +103,7 @@ const SBEntryF: FC<SBEntryProps> = ({ WASMData, loadingState, gsxActive, setLoad
className={`w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right`} className={`w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right`}
value={fuel} value={fuel}
onChange={(value) => handleInput(value, WASMData.limits.maxFuel, setFuel)} onChange={(value) => handleInput(value, WASMData.limits.maxFuel, setFuel)}
disabled={loadingState !== 'preview' || gsxActive} disabled={loadingState !== 'preview' || gsxFuelActive}
/> />
</div> </div>
</div> </div>

View File

@ -11,13 +11,13 @@ interface SBEntryProps {
WASMData: WASMDataPax; WASMData: WASMDataPax;
loadingState: LoadingState; loadingState: LoadingState;
gsxActive: boolean; gsxActive: boolean;
gsxFuelActive: boolean;
setLoadingState: (newState: LoadingState) => void; setLoadingState: (newState: LoadingState) => void;
} }
const SBEntryPax: FC<SBEntryProps> = ({ WASMData, loadingState, gsxActive, setLoadingState }) => { const SBEntryPax: FC<SBEntryProps> = ({ WASMData, loadingState, gsxActive, gsxFuelActive, setLoadingState }) => {
const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget); const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget);
const [fuel, setFuel] = useState(Math.round(WASMData.targetPayload.fuel)); const [fuel, setFuel] = useState(Math.round(WASMData.targetPayload.fuel));
const [fuelEnabled, setFuelEnabled] = useState(true);
const [SBInFlight, setSBInFlight] = useState(false); const [SBInFlight, setSBInFlight] = useState(false);
// FROM EFB // FROM EFB
@ -106,7 +106,7 @@ const SBEntryPax: FC<SBEntryProps> = ({ WASMData, loadingState, gsxActive, setLo
className={`w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right`} className={`w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right`}
value={fuel} value={fuel}
onChange={(value) => handleInput(value, WASMData.limits.maxFuel, setFuel)} onChange={(value) => handleInput(value, WASMData.limits.maxFuel, setFuel)}
disabled={loadingState !== 'preview' || gsxActive} disabled={loadingState !== 'preview' || gsxFuelActive}
/> />
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { LoadingState } from '../../types/general'; import { LoadingState } from '../../types/general';
import { GSXLoadingState, WASMDataF } from '../../types/WASMData'; import { GSXFuelingState, GSXLoadingState, WASMDataF } from '../../types/WASMData';
import OptionsF from '../options/OptionsF'; import OptionsF from '../options/OptionsF';
import Profile from '../profile/Profile'; import Profile from '../profile/Profile';
import SBEntryF from '../SBEntry/SBEntryF'; import SBEntryF from '../SBEntry/SBEntryF';
@ -65,6 +65,9 @@ const Freighter: FC<FreighterProps> = ({ WASMData, username }) => {
WASMData.GSX.loadingState !== GSXLoadingState.DEBOARDED WASMData.GSX.loadingState !== GSXLoadingState.DEBOARDED
); );
}; };
const GSXFuelActive = () => {
return WASMData.GSX.couatlStarted && WASMData.GSX.fuelingState !== GSXFuelingState.IDLE;
};
const CGs = (): [string, boolean, string, boolean] => { const CGs = (): [string, boolean, string, boolean] => {
if (loadingState !== 'loaded' && !GSXActive()) { if (loadingState !== 'loaded' && !GSXActive()) {
@ -118,9 +121,7 @@ const Freighter: FC<FreighterProps> = ({ WASMData, username }) => {
} }
/> />
<Tabbar <Tabbar
tabs={ tabs={username ? ['Simbrief', 'ZFW', 'Cargo', 'Options'] : ['ZFW', 'Cargo', 'Options']}
username ? ['Simbrief', 'ZFW', 'Cargo', 'Options'] : ['ZFW', 'Cargo', 'Options']
}
selectedTab={selectedTab} selectedTab={selectedTab}
setSelectedTab={setSelectedTab} setSelectedTab={setSelectedTab}
/> />
@ -130,6 +131,7 @@ const Freighter: FC<FreighterProps> = ({ WASMData, username }) => {
loadingState={loadingState} loadingState={loadingState}
setLoadingState={setLoadingState} setLoadingState={setLoadingState}
gsxActive={GSXActive()} gsxActive={GSXActive()}
gsxFuelActive={GSXFuelActive()}
/> />
)} )}
{((username && selectedTab === 1) || (!username && selectedTab === 0)) && ( {((username && selectedTab === 1) || (!username && selectedTab === 0)) && (
@ -138,6 +140,7 @@ const Freighter: FC<FreighterProps> = ({ WASMData, username }) => {
loadingState={loadingState} loadingState={loadingState}
setLoadingState={setLoadingState} setLoadingState={setLoadingState}
gsxActive={GSXActive()} gsxActive={GSXActive()}
gsxFuelActive={GSXFuelActive()}
/> />
)} )}
{((username && selectedTab === 2) || (!username && selectedTab === 1)) && ( {((username && selectedTab === 2) || (!username && selectedTab === 1)) && (
@ -146,6 +149,7 @@ const Freighter: FC<FreighterProps> = ({ WASMData, username }) => {
loadingState={loadingState} loadingState={loadingState}
setLoadingState={setLoadingState} setLoadingState={setLoadingState}
gsxActive={GSXActive()} gsxActive={GSXActive()}
gsxFuelActive={GSXFuelActive()}
/> />
)} )}
{((username && selectedTab === 3) || (!username && selectedTab === 2)) && ( {((username && selectedTab === 3) || (!username && selectedTab === 2)) && (

View File

@ -1,6 +1,6 @@
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { LoadingState } from '../../types/general'; import { LoadingState } from '../../types/general';
import { GSXLoadingState, WASMDataPax } from '../../types/WASMData'; import { GSXFuelingState, GSXLoadingState, WASMDataPax } from '../../types/WASMData';
import OptionsPax from '../options/OptionsPax'; import OptionsPax from '../options/OptionsPax';
import Profile from '../profile/Profile'; import Profile from '../profile/Profile';
import SBEntryPax from '../SBEntry/SBEntryPax'; import SBEntryPax from '../SBEntry/SBEntryPax';
@ -65,6 +65,9 @@ const Pax: FC<PaxProps> = ({ WASMData, username }) => {
WASMData.GSX.loadingState !== GSXLoadingState.DEBOARDED WASMData.GSX.loadingState !== GSXLoadingState.DEBOARDED
); );
}; };
const GSXFuelActive = () => {
return WASMData.GSX.couatlStarted && WASMData.GSX.fuelingState !== GSXFuelingState.IDLE;
};
const CGs = (): [string, boolean, string, boolean] => { const CGs = (): [string, boolean, string, boolean] => {
if (loadingState !== 'loaded' && !GSXActive()) { if (loadingState !== 'loaded' && !GSXActive()) {
@ -134,6 +137,7 @@ const Pax: FC<PaxProps> = ({ WASMData, username }) => {
loadingState={loadingState} loadingState={loadingState}
setLoadingState={setLoadingState} setLoadingState={setLoadingState}
gsxActive={GSXActive()} gsxActive={GSXActive()}
gsxFuelActive={GSXFuelActive()}
/> />
)} )}
{((username && selectedTab === 1) || (!username && selectedTab === 0)) && ( {((username && selectedTab === 1) || (!username && selectedTab === 0)) && (
@ -142,6 +146,7 @@ const Pax: FC<PaxProps> = ({ WASMData, username }) => {
loadingState={loadingState} loadingState={loadingState}
setLoadingState={setLoadingState} setLoadingState={setLoadingState}
gsxActive={GSXActive()} gsxActive={GSXActive()}
gsxFuelActive={GSXFuelActive()}
/> />
)} )}
{((username && selectedTab === 2) || (!username && selectedTab === 1)) && ( {((username && selectedTab === 2) || (!username && selectedTab === 1)) && (
@ -150,6 +155,7 @@ const Pax: FC<PaxProps> = ({ WASMData, username }) => {
loadingState={loadingState} loadingState={loadingState}
setLoadingState={setLoadingState} setLoadingState={setLoadingState}
gsxActive={GSXActive()} gsxActive={GSXActive()}
gsxFuelActive={GSXFuelActive()}
/> />
)} )}
{((username && selectedTab === 3) || (!username && selectedTab === 2)) && ( {((username && selectedTab === 3) || (!username && selectedTab === 2)) && (

View File

@ -9,10 +9,11 @@ interface StationEntryProps {
WASMData: WASMDataF; WASMData: WASMDataF;
loadingState: LoadingState; loadingState: LoadingState;
gsxActive: boolean; gsxActive: boolean;
gsxFuelActive: boolean;
setLoadingState: (newState: LoadingState) => void; setLoadingState: (newState: LoadingState) => void;
} }
const StationEntryF: FC<StationEntryProps> = ({ WASMData, loadingState, gsxActive, setLoadingState }) => { const StationEntryF: FC<StationEntryProps> = ({ WASMData, loadingState, gsxActive, gsxFuelActive, setLoadingState }) => {
const [upper1, setUpper1] = useState(WASMData.targetPayload.upper1); const [upper1, setUpper1] = useState(WASMData.targetPayload.upper1);
const [upper2, setUpper2] = useState(WASMData.targetPayload.upper2); const [upper2, setUpper2] = useState(WASMData.targetPayload.upper2);
const [upper3, setUpper3] = useState(WASMData.targetPayload.upper3); const [upper3, setUpper3] = useState(WASMData.targetPayload.upper3);
@ -76,7 +77,7 @@ const StationEntryF: FC<StationEntryProps> = ({ WASMData, loadingState, gsxActiv
className={`w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right`} className={`w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right`}
value={fuel} value={fuel}
onChange={(value) => handleInput(value, WASMData.limits.maxFuel, setFuel)} onChange={(value) => handleInput(value, WASMData.limits.maxFuel, setFuel)}
disabled={loadingState !== 'preview' || gsxActive} disabled={loadingState !== 'preview' || gsxFuelActive}
/> />
</div> </div>
</div> </div>

View File

@ -9,10 +9,17 @@ interface StationEntryProps {
WASMData: WASMDataPax; WASMData: WASMDataPax;
loadingState: LoadingState; loadingState: LoadingState;
gsxActive: boolean; gsxActive: boolean;
gsxFuelActive: boolean;
setLoadingState: (newState: LoadingState) => void; setLoadingState: (newState: LoadingState) => void;
} }
const StationEntryPax: FC<StationEntryProps> = ({ WASMData, loadingState, gsxActive, setLoadingState }) => { const StationEntryPax: FC<StationEntryProps> = ({
WASMData,
loadingState,
gsxActive,
gsxFuelActive,
setLoadingState,
}) => {
const [business1, setBusiness1] = useState(WASMData.targetPayload.business1); const [business1, setBusiness1] = useState(WASMData.targetPayload.business1);
const [business2, setBusiness2] = useState(WASMData.targetPayload.business2); const [business2, setBusiness2] = useState(WASMData.targetPayload.business2);
const [economy1, setEconomy1] = useState(WASMData.targetPayload.economy1); const [economy1, setEconomy1] = useState(WASMData.targetPayload.economy1);
@ -76,7 +83,7 @@ const StationEntryPax: FC<StationEntryProps> = ({ WASMData, loadingState, gsxAct
className={`w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right`} className={`w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right`}
value={fuel} value={fuel}
onChange={(value) => handleInput(value, WASMData.limits.maxFuel, setFuel)} onChange={(value) => handleInput(value, WASMData.limits.maxFuel, setFuel)}
disabled={loadingState !== 'preview' || gsxActive} disabled={loadingState !== 'preview' || gsxFuelActive}
/> />
</div> </div>
</div> </div>

View File

@ -10,10 +10,11 @@ interface ZFWEntryProps {
WASMData: WASMDataF; WASMData: WASMDataF;
loadingState: LoadingState; loadingState: LoadingState;
gsxActive: boolean; gsxActive: boolean;
gsxFuelActive: boolean;
setLoadingState: (newState: LoadingState) => void; setLoadingState: (newState: LoadingState) => void;
} }
const ZFWEntryF: FC<ZFWEntryProps> = ({ WASMData, loadingState, gsxActive, setLoadingState }) => { const ZFWEntryF: FC<ZFWEntryProps> = ({ WASMData, loadingState, gsxActive, gsxFuelActive, setLoadingState }) => {
const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget); const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget);
const [fuel, setFuel] = useState(Math.round(WASMData.targetPayload.fuel)); const [fuel, setFuel] = useState(Math.round(WASMData.targetPayload.fuel));
const [ZFWTarget, setZFWTarget] = useState(Math.round(WASMData.targetPayload.total)); const [ZFWTarget, setZFWTarget] = useState(Math.round(WASMData.targetPayload.total));
@ -102,7 +103,7 @@ const ZFWEntryF: FC<ZFWEntryProps> = ({ WASMData, loadingState, gsxActive, setLo
className={`w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right`} className={`w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right`}
value={fuel} value={fuel}
onChange={(value) => handleInput(value, WASMData.limits.maxFuel, setFuel)} onChange={(value) => handleInput(value, WASMData.limits.maxFuel, setFuel)}
disabled={loadingState !== 'preview' || gsxActive} disabled={loadingState !== 'preview' || gsxFuelActive}
/> />
</div> </div>
</div> </div>

View File

@ -10,10 +10,11 @@ interface ZFWEntryProps {
WASMData: WASMDataPax; WASMData: WASMDataPax;
loadingState: LoadingState; loadingState: LoadingState;
gsxActive: boolean; gsxActive: boolean;
gsxFuelActive: boolean;
setLoadingState: (newState: LoadingState) => void; setLoadingState: (newState: LoadingState) => void;
} }
const ZFWEntryPax: FC<ZFWEntryProps> = ({ WASMData, loadingState, gsxActive, setLoadingState }) => { const ZFWEntryPax: FC<ZFWEntryProps> = ({ WASMData, loadingState, gsxActive, gsxFuelActive, setLoadingState }) => {
const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget); const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget);
const [fuel, setFuel] = useState(Math.round(WASMData.targetPayload.fuel)); const [fuel, setFuel] = useState(Math.round(WASMData.targetPayload.fuel));
const [ZFWTarget, setZFWTarget] = useState(Math.round(WASMData.targetPayload.total)); const [ZFWTarget, setZFWTarget] = useState(Math.round(WASMData.targetPayload.total));
@ -102,7 +103,7 @@ const ZFWEntryPax: FC<ZFWEntryProps> = ({ WASMData, loadingState, gsxActive, set
className={`w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right`} className={`w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right`}
value={fuel} value={fuel}
onChange={(value) => handleInput(value, WASMData.limits.maxFuel, setFuel)} onChange={(value) => handleInput(value, WASMData.limits.maxFuel, setFuel)}
disabled={loadingState !== 'preview' || gsxActive} disabled={loadingState !== 'preview' || gsxFuelActive}
/> />
</div> </div>
</div> </div>

View File

@ -59,6 +59,7 @@ type LivePayloadF = TargetPayloadF;
interface GSX { interface GSX {
couatlStarted: boolean; couatlStarted: boolean;
loadingState: GSXLoadingState; loadingState: GSXLoadingState;
fuelingState: GSXFuelingState;
} }
interface Limits { interface Limits {
@ -107,3 +108,8 @@ export enum GSXLoadingState {
DEBOARDING = 3, DEBOARDING = 3,
DEBOARDED = 4, DEBOARDED = 4,
} }
export enum GSXFuelingState {
IDLE = 0,
FUELING = 1,
}

View File

@ -275,11 +275,13 @@ void calculateCGs(fPayloadData_t *const targetPayload, const FuelData_t *const f
targetPayload->TOCG = TO_PERCENT_MAC(totalMoment / totalWeight); targetPayload->TOCG = TO_PERCENT_MAC(totalMoment / totalWeight);
} }
void load(const fPayloadData_t *const targetPayload, const HANDLE simConnect, const bool isImperial) { void load(const fPayloadData_t *const targetPayload, const FuelData_t *const fuel, const HANDLE simConnect,
const bool isImperial) {
fPayloadData_t localPayload = {}; fPayloadData_t localPayload = {};
memcpy(&localPayload, targetPayload, sizeof(localPayload)); memcpy(&localPayload, targetPayload, sizeof(localPayload));
normalisePayload(&localPayload, isImperial); normalisePayload(&localPayload, isImperial);
calculateCGs(&localPayload, fuel, isImperial);
localPayload._ZFWCG = localPayload.ZFWCG / 100.0; localPayload._ZFWCG = localPayload.ZFWCG / 100.0;
SimConnect_SetDataOnSimObject(simConnect, DATA_DEFINITION_PAYLOAD_F, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(fPayloadDataSet_t), SimConnect_SetDataOnSimObject(simConnect, DATA_DEFINITION_PAYLOAD_F, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(fPayloadDataSet_t),

View File

@ -113,5 +113,6 @@ void generatePayload(fPayloadData_t *const targetPayload, const bool isImperial)
// For Station Entry: CALL AFTER `generatePayload` // For Station Entry: CALL AFTER `generatePayload`
void normalisePayload(fPayloadData_t *const targetPayload, const bool isImperial); void normalisePayload(fPayloadData_t *const targetPayload, const bool isImperial);
void calculateCGs(fPayloadData_t *const targetPayload, const FuelData_t *const fuel, const bool isImperial); void calculateCGs(fPayloadData_t *const targetPayload, const FuelData_t *const fuel, const bool isImperial);
void load(const fPayloadData_t *const targetPayload, const HANDLE simConnect, const bool isImperial); void load(const fPayloadData_t *const targetPayload, const FuelData_t *const fuel, const HANDLE simConnect,
const bool isImperial);
void unloadF(const HANDLE simConnect, const bool isER); void unloadF(const HANDLE simConnect, const bool isER);

View File

@ -1,8 +1,7 @@
#include "fuel.h" #include "fuel.h"
// ZFW Entry void distribute(FuelData_t *const targetFuel, const double fuelTarget, const bool isImperial, const bool isER) {
void distribute(FuelData_t *const targetFuel, const double fuelTarget, const UserData_t *const userData) { double fuelLbs = TO_POUNDS(isImperial, fuelTarget);
double fuelLbs = TO_POUNDS(userData->isImperial, fuelTarget);
// Equal mains // Equal mains
if (fuelLbs <= MAX_TANK_1_3_LBS * 3) { if (fuelLbs <= MAX_TANK_1_3_LBS * 3) {
@ -98,7 +97,7 @@ void distribute(FuelData_t *const targetFuel, const double fuelTarget, const Use
targetFuel->forwardAux1 = 0; targetFuel->forwardAux1 = 0;
targetFuel->forwardAux2 = 0; targetFuel->forwardAux2 = 0;
} else if (userData->isER) { } else if (isER) {
targetFuel->main1 = MAX_TANK_1_3_MAIN_LBS; targetFuel->main1 = MAX_TANK_1_3_MAIN_LBS;
targetFuel->main3 = MAX_TANK_1_3_MAIN_LBS; targetFuel->main3 = MAX_TANK_1_3_MAIN_LBS;
targetFuel->main1Tip = MAX_TANK_1_3_TIP_LBS; targetFuel->main1Tip = MAX_TANK_1_3_TIP_LBS;
@ -118,6 +117,10 @@ void distribute(FuelData_t *const targetFuel, const double fuelTarget, const Use
targetFuel->forwardAux2; targetFuel->forwardAux2;
} }
void distribute(FuelData_t *const targetFuel, const double fuelTarget, const UserData_t *const userData) {
distribute(targetFuel, fuelTarget, userData->isImperial, userData->isER);
}
void fuel(const FuelData_t *const targetFuel, const HANDLE simConnect) { void fuel(const FuelData_t *const targetFuel, const HANDLE simConnect) {
FuelDataSet_t localFuel = {}; FuelDataSet_t localFuel = {};

View File

@ -22,7 +22,12 @@
#include "shared.h" #include "shared.h"
/******************************** Constants ********************************/ /******************************** Constants ********************************/
// Pounds to gallons conversion
#define LBS_PER_GAL 6.699999809 #define LBS_PER_GAL 6.699999809
// Fueling rate
#define FUELING_RATE ((LBS_PER_GAL * 1000) / 60)
// Max tank capacity gallons
#define MAX_TANK_1_3_MAIN_GAL 5167.907 #define MAX_TANK_1_3_MAIN_GAL 5167.907
#define MAX_TANK_1_3_TIP_GAL 874.5 #define MAX_TANK_1_3_TIP_GAL 874.5
#define MAX_TANK_1_3_GAL (5167.907 + 874.5) #define MAX_TANK_1_3_GAL (5167.907 + 874.5)
@ -31,6 +36,7 @@
#define MAX_LWR_AUX_GAL 1641.82 #define MAX_LWR_AUX_GAL 1641.82
#define MAX_TAIL_GAL 1957.779 #define MAX_TAIL_GAL 1957.779
#define MAX_FWD_AUX_GAL 1970.8 #define MAX_FWD_AUX_GAL 1970.8
// Max tank capacity pounds
#define MAX_TANK_1_3_MAIN_LBS (LBS_PER_GAL * MAX_TANK_1_3_MAIN_GAL) #define MAX_TANK_1_3_MAIN_LBS (LBS_PER_GAL * MAX_TANK_1_3_MAIN_GAL)
#define MAX_TANK_1_3_TIP_LBS (LBS_PER_GAL * MAX_TANK_1_3_TIP_GAL) #define MAX_TANK_1_3_TIP_LBS (LBS_PER_GAL * MAX_TANK_1_3_TIP_GAL)
#define MAX_TANK_1_3_LBS (LBS_PER_GAL * MAX_TANK_1_3_GAL) #define MAX_TANK_1_3_LBS (LBS_PER_GAL * MAX_TANK_1_3_GAL)
@ -39,7 +45,6 @@
#define MAX_LWR_AUX_LBS (LBS_PER_GAL * MAX_LWR_AUX_GAL) #define MAX_LWR_AUX_LBS (LBS_PER_GAL * MAX_LWR_AUX_GAL)
#define MAX_TAIL_LBS (LBS_PER_GAL * MAX_TAIL_GAL) #define MAX_TAIL_LBS (LBS_PER_GAL * MAX_TAIL_GAL)
#define MAX_FWD_AUX_LBS (LBS_PER_GAL * MAX_FWD_AUX_GAL) #define MAX_FWD_AUX_LBS (LBS_PER_GAL * MAX_FWD_AUX_GAL)
// Max Fuel // Max Fuel
#define MAX_FUEL(IS_IMPERIAL) ((IS_IMPERIAL) ? (256207) : (116213)) #define MAX_FUEL(IS_IMPERIAL) ((IS_IMPERIAL) ? (256207) : (116213))
#define MAX_FUEL_ER(IS_IMPERIAL) ((IS_IMPERIAL) ? (282619) : (128193)) #define MAX_FUEL_ER(IS_IMPERIAL) ((IS_IMPERIAL) ? (282619) : (128193))
@ -84,5 +89,6 @@ typedef struct {
} FuelDataSet_t; } FuelDataSet_t;
/******************************** Functions ********************************/ /******************************** Functions ********************************/
void distribute(FuelData_t *const targetFuel, const double fuelTarget, const bool isImperial, const bool isER);
void distribute(FuelData_t *const targetFuel, const double fuelTarget, const UserData_t *const userData); void distribute(FuelData_t *const targetFuel, const double fuelTarget, const UserData_t *const userData);
void fuel(const FuelData_t *const targetFuel, const HANDLE simConnect); void fuel(const FuelData_t *const targetFuel, const HANDLE simConnect);

View File

@ -423,6 +423,12 @@ extern "C" MSFS_CALLBACK void module_init(void) {
log(stderr, "Could not add L:FSDT_GSX_DEBOARDING_STATE to data definition, terminating.\n"); log(stderr, "Could not add L:FSDT_GSX_DEBOARDING_STATE to data definition, terminating.\n");
return; return;
} }
hr = SimConnect_AddToDataDefinition(simConnect, DATA_DEFINITION_GSX, "L:FSDT_GSX_REFUELING_STATE", "number",
SIMCONNECT_DATATYPE_FLOAT64);
if (hr != S_OK) {
log(stderr, "Could not add L:FSDT_GSX_REFULEING_STATE to data definition, terminating.\n");
return;
}
hr = SimConnect_AddToDataDefinition(simConnect, DATA_DEFINITION_GSX, "L:FSDT_GSX_NUMPASSENGERS_BOARDING_TOTAL", "number", hr = SimConnect_AddToDataDefinition(simConnect, DATA_DEFINITION_GSX, "L:FSDT_GSX_NUMPASSENGERS_BOARDING_TOTAL", "number",
SIMCONNECT_DATATYPE_FLOAT64); SIMCONNECT_DATATYPE_FLOAT64);
if (hr != S_OK) { if (hr != S_OK) {
@ -604,10 +610,42 @@ extern "C" MSFS_CALLBACK void module_deinit(void) {
// Main loop // Main loop
extern "C" MSFS_CALLBACK bool Load_Manager_gauge_callback(FsContext ctx, int service_id, void *pData) { extern "C" MSFS_CALLBACK bool Load_Manager_gauge_callback(FsContext ctx, int service_id, void *pData) {
static double dTime = 0;
switch (service_id) { switch (service_id) {
case PANEL_SERVICE_PRE_UPDATE: { case PANEL_SERVICE_PRE_DRAW: {
sGaugeDrawData *d = (sGaugeDrawData *)pData;
lookup_var(&tick18); lookup_var(&tick18);
if (fmod(tick18.var_value.n, 3) == 0) sendData(); if (fmod(tick18.var_value.n, 3) == 0) sendData();
if (GSXData->fuelingState == FUELING_STATE_FUELING && abs(liveFuelData->total - targetFuelData->total) < 0.1) {
GSXData->fuelingState = FUELING_STATE_FUELD;
fuel(targetFuelData, simConnect);
}
dTime += d->dt;
if (GSXData->fuelingState == FUELING_STATE_FUELING && dTime >= 0.5) {
FuelData_t toSet = {};
if (targetFuelData->total > liveFuelData->total) {
distribute(&toSet,
min(targetFuelData->total,
liveFuelData->total + min(max(targetFuelData->total - liveFuelData->total, 1), FUELING_RATE) * dTime),
true, UserData->isER);
} else {
distribute(&toSet,
max(targetFuelData->total,
liveFuelData->total + max(min(targetFuelData->total - liveFuelData->total, -1), -FUELING_RATE) * dTime),
true, UserData->isER);
}
dTime = 0;
fuel(&toSet, simConnect);
}
} }
default: default:
break; break;
@ -723,10 +761,10 @@ int receiveData(const char *buf) {
case CALL_MODE_LOAD_SET: { case CALL_MODE_LOAD_SET: {
if (UserData->isCargo) { if (UserData->isCargo) {
fuel(targetFuelData, simConnect); fuel(targetFuelData, simConnect);
load(targetFPayloadData, simConnect, UserData->isImperial); load(targetFPayloadData, targetFuelData, simConnect, UserData->isImperial);
} else { } else {
fuel(targetFuelData, simConnect); fuel(targetFuelData, simConnect);
load(targetPaxPayloadData, simConnect, UserData->isImperial); load(targetPaxPayloadData, targetFuelData, simConnect, UserData->isImperial);
} }
AircraftLoaded = true; AircraftLoaded = true;
@ -781,6 +819,7 @@ int receiveData(const char *buf) {
} }
case CALL_MODE_GSX_RESET: { case CALL_MODE_GSX_RESET: {
GSXData->loadingState = LOADING_STATE_IDLE; GSXData->loadingState = LOADING_STATE_IDLE;
GSXData->fuelingState = FUELING_STATE_IDLE;
UserOptions->GSXSync = false; UserOptions->GSXSync = false;
} }
default: default:
@ -943,6 +982,7 @@ void sendData() {
// GSX // GSX
GSX.AddMember<bool>("couatlStarted", GSXData->couatlStarted, allocator); GSX.AddMember<bool>("couatlStarted", GSXData->couatlStarted, allocator);
GSX.AddMember("loadingState", GSXData->loadingState, allocator); GSX.AddMember("loadingState", GSXData->loadingState, allocator);
GSX.AddMember("fuelingState", GSXData->fuelingState, allocator);
// User Data // User Data
userData.AddMember<bool>("isCargo", UserData->isCargo, allocator); userData.AddMember<bool>("isCargo", UserData->isCargo, allocator);
@ -1068,7 +1108,7 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContex
abs(liveFPayloadData->total - TO_POUNDS(UserData->isImperial, targetFPayloadData->total)) > 100) { abs(liveFPayloadData->total - TO_POUNDS(UserData->isImperial, targetFPayloadData->total)) > 100) {
log(stdout, "F AC loaded, Payload mismatch, resetting target\n"); log(stdout, "F AC loaded, Payload mismatch, resetting target\n");
load(targetFPayloadData, simConnect, UserData->isImperial); load(targetFPayloadData, liveFuelData, simConnect, UserData->isImperial);
} }
break; break;
@ -1094,7 +1134,7 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContex
abs(livePaxPayloadData->total - TO_POUNDS(UserData->isImperial, targetPaxPayloadData->total)) > 100) { abs(livePaxPayloadData->total - TO_POUNDS(UserData->isImperial, targetPaxPayloadData->total)) > 100) {
log(stdout, "PAX AC loaded, Payload mismatch, resetting target\n"); log(stdout, "PAX AC loaded, Payload mismatch, resetting target\n");
load(targetPaxPayloadData, simConnect, UserData->isImperial); load(targetPaxPayloadData, liveFuelData, simConnect, UserData->isImperial);
} }
break; break;
@ -1122,6 +1162,7 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContex
GSXData->couatlStarted = data->couatlStarted; GSXData->couatlStarted = data->couatlStarted;
GSXData->boardingState = data->boardingState; GSXData->boardingState = data->boardingState;
GSXData->deboardingState = data->deboardingState; GSXData->deboardingState = data->deboardingState;
GSXData->refuelingState = data->refuelingState;
GSXData->passengersBoarded = data->passengersBoarded; GSXData->passengersBoarded = data->passengersBoarded;
GSXData->passengersDeboarded = data->passengersDeboarded; GSXData->passengersDeboarded = data->passengersDeboarded;
GSXData->cargoBoarded = data->cargoBoarded; GSXData->cargoBoarded = data->cargoBoarded;
@ -1147,7 +1188,7 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContex
localPayload.lowerRear = targetFPayloadData->lowerRear * (cargoBoarded / 100); localPayload.lowerRear = targetFPayloadData->lowerRear * (cargoBoarded / 100);
generatePayload(&localPayload, UserData->isImperial); generatePayload(&localPayload, UserData->isImperial);
load(&localPayload, simConnect, UserData->isImperial); load(&localPayload, liveFuelData, simConnect, UserData->isImperial);
} else { } else {
double passengersBoarded = GSXData->passengersBoarded; double passengersBoarded = GSXData->passengersBoarded;
paxPayloadData_t localPayload = {}; paxPayloadData_t localPayload = {};
@ -1165,7 +1206,7 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContex
localPayload.rearCargo = targetPaxPayloadData->rearCargo * (cargoBoarded / 100); localPayload.rearCargo = targetPaxPayloadData->rearCargo * (cargoBoarded / 100);
generatePayload(&localPayload, UserData->isImperial, UserOptions); generatePayload(&localPayload, UserData->isImperial, UserOptions);
load(&localPayload, simConnect, UserData->isImperial); load(&localPayload, liveFuelData, simConnect, UserData->isImperial);
} }
break; break;
@ -1180,13 +1221,13 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContex
memcpy(&localPayload, targetFPayloadData, sizeof(localPayload)); memcpy(&localPayload, targetFPayloadData, sizeof(localPayload));
generatePayload(&localPayload, UserData->isImperial); generatePayload(&localPayload, UserData->isImperial);
load(&localPayload, simConnect, UserData->isImperial); load(&localPayload, liveFuelData, simConnect, UserData->isImperial);
} else { } else {
paxPayloadData_t localPayload = {}; paxPayloadData_t localPayload = {};
memcpy(&localPayload, targetPaxPayloadData, sizeof(localPayload)); memcpy(&localPayload, targetPaxPayloadData, sizeof(localPayload));
generatePayload(&localPayload, UserData->isImperial, UserOptions); generatePayload(&localPayload, UserData->isImperial, UserOptions);
load(&localPayload, simConnect, UserData->isImperial); load(&localPayload, liveFuelData, simConnect, UserData->isImperial);
} }
break; break;
@ -1216,7 +1257,7 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContex
localPayload.lowerRear -= targetFPayloadData->lowerRear * (cargoDeboarded / 100); localPayload.lowerRear -= targetFPayloadData->lowerRear * (cargoDeboarded / 100);
generatePayload(&localPayload, UserData->isImperial); generatePayload(&localPayload, UserData->isImperial);
load(&localPayload, simConnect, UserData->isImperial); load(&localPayload, liveFuelData, simConnect, UserData->isImperial);
} else { } else {
double passengersDeboarded = GSXData->passengersDeboarded; double passengersDeboarded = GSXData->passengersDeboarded;
paxPayloadData_t localPayload = {}; paxPayloadData_t localPayload = {};
@ -1234,7 +1275,7 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContex
localPayload.rearCargo -= targetPaxPayloadData->rearCargo * (cargoDeboarded / 100); localPayload.rearCargo -= targetPaxPayloadData->rearCargo * (cargoDeboarded / 100);
generatePayload(&localPayload, UserData->isImperial, UserOptions); generatePayload(&localPayload, UserData->isImperial, UserOptions);
load(&localPayload, simConnect, UserData->isImperial); load(&localPayload, liveFuelData, simConnect, UserData->isImperial);
} }
break; break;
@ -1257,7 +1298,7 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContex
localPayload.lowerRear = 0; localPayload.lowerRear = 0;
generatePayload(&localPayload, UserData->isImperial); generatePayload(&localPayload, UserData->isImperial);
load(&localPayload, simConnect, UserData->isImperial); load(&localPayload, liveFuelData, simConnect, UserData->isImperial);
} else { } else {
paxPayloadData_t localPayload = {}; paxPayloadData_t localPayload = {};
memcpy(&localPayload, targetPaxPayloadData, sizeof(localPayload)); memcpy(&localPayload, targetPaxPayloadData, sizeof(localPayload));
@ -1270,7 +1311,7 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContex
localPayload.rearCargo = 0; localPayload.rearCargo = 0;
generatePayload(&localPayload, UserData->isImperial, UserOptions); generatePayload(&localPayload, UserData->isImperial, UserOptions);
load(&localPayload, simConnect, UserData->isImperial); load(&localPayload, liveFuelData, simConnect, UserData->isImperial);
} }
break; break;
@ -1278,6 +1319,26 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV *pData, DWORD cbData, void *pContex
default: default:
break; break;
} }
switch ((char)GSXData->refuelingState) {
case GSX_SERVICE_IDLE: {
GSXData->fuelingState = FUELING_STATE_IDLE;
break;
}
case GSX_SERVICE_ACTIVE: {
GSXData->fuelingState = FUELING_STATE_FUELING;
break;
}
case GSX_SERVICE_FINISHED: {
GSXData->fuelingState = FUELING_STATE_FUELD;
fuel(targetFuelData, simConnect);
break;
}
default:
break;
}
} }
break; break;

View File

@ -34,7 +34,7 @@
/******************************** Constants ********************************/ /******************************** Constants ********************************/
// Module identification // Module identification
#define MODULE_NAME "[KHOFMANN TFDi MD-11 Load Manager] " #define MODULE_NAME "[KHOFMANN TFDi MD-11 Load Manager] "
#define VERSION_STRING "2.9-beta" #define VERSION_STRING "2.12-beta"
// COMM BUS // COMM BUS
#define COMM_BUS_LIVE_DATA_EVENT "khofmann_tfdi_md-11_load_manager_live_data" #define COMM_BUS_LIVE_DATA_EVENT "khofmann_tfdi_md-11_load_manager_live_data"
#define COMM_BUS_UPDATE_TARGET_EVENT "khofmann_tfdi_md-11_load_manager_update_target" #define COMM_BUS_UPDATE_TARGET_EVENT "khofmann_tfdi_md-11_load_manager_update_target"

View File

@ -291,11 +291,13 @@ void calculateCGs(paxPayloadData_t *const targetPayload, const FuelData_t *const
targetPayload->TOCG = TO_PERCENT_MAC(totalMoment / totalWeight); targetPayload->TOCG = TO_PERCENT_MAC(totalMoment / totalWeight);
} }
void load(const paxPayloadData_t *const targetPayload, const HANDLE simConnect, const bool isImperial) { void load(const paxPayloadData_t *const targetPayload, const FuelData_t *const fuel, const HANDLE simConnect,
const bool isImperial) {
paxPayloadData_t localPayload = {}; paxPayloadData_t localPayload = {};
memcpy(&localPayload, targetPayload, sizeof(localPayload)); memcpy(&localPayload, targetPayload, sizeof(localPayload));
normalisePayload(&localPayload, isImperial); normalisePayload(&localPayload, isImperial);
calculateCGs(&localPayload, fuel, isImperial);
localPayload._ZFWCG = localPayload.ZFWCG / 100.0; localPayload._ZFWCG = localPayload.ZFWCG / 100.0;
SimConnect_SetDataOnSimObject(simConnect, DATA_DEFINITION_PAYLOAD_PAX, SIMCONNECT_OBJECT_ID_USER, 0, 0, SimConnect_SetDataOnSimObject(simConnect, DATA_DEFINITION_PAYLOAD_PAX, SIMCONNECT_OBJECT_ID_USER, 0, 0,

View File

@ -146,7 +146,8 @@ void generatePayload(paxPayloadData_t *const targetPayload, const bool isImperia
// For Station Entry: CALL AFTER `generatePayload` // For Station Entry: CALL AFTER `generatePayload`
void normalisePayload(paxPayloadData_t *const targetPayload, const bool isImperial); void normalisePayload(paxPayloadData_t *const targetPayload, const bool isImperial);
void calculateCGs(paxPayloadData_t *const targetPayload, const FuelData_t *const fuel, const bool isImperial); void calculateCGs(paxPayloadData_t *const targetPayload, const FuelData_t *const fuel, const bool isImperial);
void load(const paxPayloadData_t *const targetPayload, const HANDLE simConnect, const bool isImperial); void load(const paxPayloadData_t *const targetPayload, const FuelData_t *const fuel, const HANDLE simConnect,
const bool isImperial);
void unload(const HANDLE simConnect, const bool isER); void unload(const HANDLE simConnect, const bool isER);
// Based on ICAO/LH findings // Based on ICAO/LH findings
const double PAX_WEIGHT(const bool isImperial, const UserOptions_t *const options); const double PAX_WEIGHT(const bool isImperial, const UserOptions_t *const options);

View File

@ -45,6 +45,7 @@
#define MAX_CG 34.0 #define MAX_CG 34.0
#define CG_TOLERANCE 0.05 #define CG_TOLERANCE 0.05
// GSX States // GSX States
#define GSX_SERVICE_IDLE 1
#define GSX_SERVICE_ACTIVE 5 #define GSX_SERVICE_ACTIVE 5
#define GSX_SERVICE_FINISHED 6 #define GSX_SERVICE_FINISHED 6
@ -84,6 +85,8 @@ enum LOADING_STATES {
LOADING_STATE_DEBOARDED, LOADING_STATE_DEBOARDED,
}; };
enum FUELING_STATES { FUELING_STATE_IDLE, FUELING_STATE_FUELING, FUELING_STATE_FUELD };
enum CALL_MODES { enum CALL_MODES {
CALL_MODE_SB_SET, CALL_MODE_SB_SET,
CALL_MODE_ZFW_SET, CALL_MODE_ZFW_SET,
@ -106,6 +109,7 @@ typedef struct {
double couatlStarted; // boolean double couatlStarted; // boolean
double boardingState; // See manual, 5 => active double boardingState; // See manual, 5 => active
double deboardingState; // See manual, 5 => active double deboardingState; // See manual, 5 => active
double refuelingState; // See manual, 5 => active
double passengersBoarded; // Num pax double passengersBoarded; // Num pax
double passengersDeboarded; // Num pax double passengersDeboarded; // Num pax
double cargoBoarded; // In percent double cargoBoarded; // In percent
@ -113,6 +117,7 @@ typedef struct {
// Additional properties // Additional properties
enum LOADING_STATES loadingState; enum LOADING_STATES loadingState;
enum FUELING_STATES fuelingState;
} GSXData_t; } GSXData_t;
typedef struct { typedef struct {

23
TODO.md
View File

@ -1,20 +1,3 @@
Fuel (once implemented) TEST progressive fueling
ZFWCG for WBS (once implemented) `MD11_EXTCTL_WBS_ZFWCG` TEST progressive loading/unloading ZFWCG
https://discord.com/channels/589851727373795331/1165358675037270066/1469039399961497753 Implement minimum fuel in tank (i.e. unusable)
Fuel Schedule
Mains 1/2/3
Upper Aux
Lower Aux
Tail
LEft/Right Forward Aux
Fuel Limits from EFB
GSX Sync
When Fueling is called, fuel progressively using FuelDataSet_t and FUEL_SET enums
Find fueling rate of plane
In JS
Remove Set Fuel
Fuel is set with total load button