ZFW Target Set

This commit is contained in:
2025-06-11 00:27:27 +02:00
parent 9224300c85
commit ee46d0bff1
13 changed files with 382 additions and 499 deletions
+15 -89
View File
@@ -1,104 +1,40 @@
import { FC, StrictMode, useCallback, useEffect, useRef, useState } from 'react';
import Freight from './components/freight/Freight';
import { FC, StrictMode, useCallback, useEffect, useState } from 'react';
import Pax from './components/pax/Pax';
import { PayloadFreight, calculateCGsFreight, getWeightsFreight } from './configs/freighter';
import { PaxConfig, PayloadPax } from './configs/pax';
import { Fuel, getFuel, initialFuel, initialPayload } from './configs/shared';
import { COHERENT_COMBUS_WASM_CALL, COMM_BUS_LIVE_DATA_EVENT, TFDI_SIMBRIEF_USERNAME_EVENT } from './constants';
import { WASMDataPax } from './types/WASMData';
interface IAppProps {
commBus: ViewListener.ViewListener;
}
const App: FC<IAppProps> = ({ commBus }) => {
// Inferred
const [unit, setUnit] = useState<'lbs' | 'kg'>('lbs');
const [isCargo, setIsCargo] = useState(false);
const [isER, setIsER] = useState(false);
const [SBUsername, setSBUsername] = useState<string>();
// From sim
const [payloadLive, setPayloadLive] = useState<PayloadPax | PayloadFreight>(initialPayload);
const [fuel, setFuel] = useState<Fuel>(initialFuel);
const [GSXPaxNum, setGSXPaxNum] = useState(0);
const [GSXCargoPercent, setGSXCargoPercent] = useState(0);
const [GSXState, setGSXState] = useState<'boarding' | 'deboarding' | 'idle'>('idle');
// Calculated
const [CGs, setCGs] = useState<[number, number]>([0, 0]);
//FIXME: TS Type
const [WASMData, setWASMData] = useState<WASMDataPax>();
const [isReady, setIsReady] = useState(false);
const requestRef = useRef<number | undefined>(undefined);
// Main Loop for Live Payload
const mainLoop = () => {
try {
if (SimVar.IsReady()) {
setIsER(SimVar.GetSimVarValue('L:MD11_OPT_ER', 'bool'));
setIsCargo(SimVar.GetSimVarValue('L:MD11_EFB_IS_CARGO', 'bool'));
setUnit((SimVar.GetSimVarValue('L:MD11_EFB_OPTIONS_GENERAL', 'number') & 1) << 0 ? 'lbs' : 'kg');
// GSX
const boardingState = SimVar.GetSimVarValue('L:FSDT_GSX_BOARDING_STATE', 'number');
const deboardingState = SimVar.GetSimVarValue('L:FSDT_GSX_DEBOARDING_STATE', 'number');
setGSXState(boardingState === 5 ? 'boarding' : deboardingState === 5 ? 'deboarding' : 'idle');
setGSXPaxNum(
boardingState === 5
? SimVar.GetSimVarValue('L:FSDT_GSX_NUMPASSENGERS_BOARDING_TOTAL', 'number')
: deboardingState === 5
? SimVar.GetSimVarValue('L:FSDT_GSX_NUMPASSENGERS_DEBOARDING_TOTAL', 'number')
: 0
);
setGSXCargoPercent(
boardingState === 5
? SimVar.GetSimVarValue('L:FSDT_GSX_BOARDING_CARGO_PERCENT', 'number')
: deboardingState === 5
? 100 - SimVar.GetSimVarValue('L:FSDT_GSX_DEBOARDING_CARGO_PERCENT', 'number')
: 0
);
const payload = isCargo ? getWeightsFreight(unit) : PaxConfig.getWeights(unit);
const _fuel = getFuel(unit);
setCGs(
isCargo
? calculateCGsFreight(payload as PayloadFreight, _fuel)
: PaxConfig.calculateCGs(payload as PayloadPax, _fuel)
);
setPayloadLive(payload);
setFuel(_fuel);
}
} catch {}
requestRef.current = requestAnimationFrame(mainLoop);
};
useEffect(() => {
requestRef.current = requestAnimationFrame(mainLoop);
if (requestRef.current !== undefined) return () => cancelAnimationFrame(requestRef.current as number);
}, [unit, isCargo]);
// CommBus
const usernameCallback = useCallback((username: string) => {
setSBUsername(username);
setIsReady(true);
}, []);
const wasmCallback = useCallback((data: any) => {
console.log('WASM DATA', JSON.parse(data));
const wasmCallback = useCallback((data: string) => {
setWASMData(JSON.parse(data));
}, []);
useEffect(() => {
console.log('Initializing CommBus');
commBus.on('receiveSimBriefUsername', usernameCallback);
commBus.on('khofmann_tfdi_md-11_load_manager_live_data', wasmCallback);
commBus.on(COMM_BUS_LIVE_DATA_EVENT, usernameCallback);
commBus.on(COMM_BUS_LIVE_DATA_EVENT, wasmCallback);
setTimeout(() => {
Coherent.call('COMM_BUS_WASM_CALLBACK', 'requestSimBriefUsername', 'null');
Coherent.call(COHERENT_COMBUS_WASM_CALL, TFDI_SIMBRIEF_USERNAME_EVENT, 'null');
}, 1000);
return () => {
commBus.off('receiveSimBriefUsername', usernameCallback);
commBus.off('khofmann_tfdi_md-11_load_manager_live_data', wasmCallback);
commBus.off(COMM_BUS_LIVE_DATA_EVENT, wasmCallback);
};
}, []);
@@ -106,21 +42,11 @@ const App: FC<IAppProps> = ({ commBus }) => {
<StrictMode>
<div className="flex w-full justify-center pt-2 bg-zinc-900">
<div className="flex w-3/4 flex-col items-center">
{isReady ? (
isCargo ? (
<Freight isER={isER} unit={unit} OEW={payloadLive.empty} CGs={CGs} />
{isReady && WASMData ? (
WASMData.userData.isCargo ? (
<>Not yet Implemented</>
) : (
<Pax
isER={isER}
unit={unit}
CGs={CGs}
fuelLive={fuel}
payloadLive={payloadLive as PayloadPax}
username={SBUsername}
GSXPaxNum={GSXPaxNum}
GSXCargoPercent={GSXCargoPercent}
GSXState={GSXState}
/>
<Pax WASMData={WASMData} username={SBUsername} />
)
) : (
<h1 className="text-sm font-medium">LOADING</h1>
@@ -1,92 +0,0 @@
import { FC, useState } from 'react';
import { PayloadFreight } from '../../configs/freighter';
import { initialPayload, SharedConfig } from '../../configs/shared';
import Profile from '../profile/Profile';
import Tabbar from '../tabbar/Tabbar';
interface FreightProps {
isER: boolean;
unit: 'kg' | 'lbs';
OEW: number;
CGs: [number, number];
}
const Freight: FC<FreightProps> = ({ isER, unit, OEW, CGs }) => {
const [selectedTab, setSelectedTab] = useState(0);
const [payload, setPayload] = useState<PayloadFreight>(initialPayload);
const [inPreview, setInPreview] = useState(true);
const upper1 = () => {
return Math.round(payload.upper1Left + payload.upper1Right);
};
const upper2 = () => {
return Math.round(payload.upper2Left + payload.upper2Right);
};
const upper3 = () => {
return Math.round(payload.upper3Left + payload.upper3Right);
};
const upper4 = () => {
return Math.round(payload.upper4Left + payload.upper4Right);
};
const lower1 = () => {
return Math.round(payload.lowerForward);
};
const lower2 = () => {
return Math.round(payload.lowerRear);
};
const _OEW = () => {
return Math.round(OEW + (isER ? SharedConfig.erExtraWeight[unit] * 2 : 1));
};
const crew = () => {
return Math.round(payload.pilot + payload.firstOfficer + payload.engineer);
};
const cgs = (): [string, string] => {
return [CGs[0].toFixed(1), CGs[1].toFixed(1)];
};
return (
<>
<Profile
type="F"
isER={isER}
upper1={`${upper1()}`}
upper2={`${upper2()}`}
upper3={`${upper3()}`}
upper4={`${upper4()}`}
lower1={`${lower1()}`}
lower2={`${lower2()}`}
OEW={`${_OEW()}`}
crew={`${crew()}`}
unit={unit.toUpperCase()}
inPreview={inPreview}
CGs={cgs()}
/>
<Tabbar tabs={['Simbrief', 'ZFW', 'Cargo']} selectedTab={selectedTab} setSelectedTab={setSelectedTab} />
<div className="relative flex w-full items-center justify-start gap-x-6">
<button
className="middle none center rounded-lg bg-green-600 px-6 py-3 font-sans text-xs font-bold uppercase text-white shadow-md shadow-green-500/20 transition-all hover:shadow-lg hover:shadow-green-500/40 focus:opacity-[0.85] focus:shadow-none active:opacity-[0.85] active:shadow-none disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"
data-ripple-light="true"
onClick={() => {
console.log('TODO: SET PAYLOAD IN SIM');
setInPreview(false);
}}
>
Load
</button>
<button
className="middle none center rounded-lg bg-red-600 px-6 py-3 font-sans text-xs font-bold uppercase text-white shadow-md shadow-red-500/20 transition-all hover:shadow-lg hover:shadow-red-500/40 focus:opacity-[0.85] focus:shadow-none active:opacity-[0.85] active:shadow-none disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"
data-ripple-light="true"
onClick={() => {
console.log('TODO: CLEAR PAYLOAD IN SIM');
setInPreview(true);
}}
>
Unload
</button>
</div>
</>
);
};
export default Freight;
@@ -1,167 +1,97 @@
import { FC, useEffect, useState } from 'react';
import { PaxConfig, PayloadPax } from '../../configs/pax';
import { Fuel, initialPayload, SharedConfig } from '../../configs/shared';
import { FC, useState } from 'react';
import { WASMDataPax } from '../../types/WASMData';
import Profile from '../profile/Profile';
import SBEntryPax from '../SBEntry/SBEntryPax';
import StationEntryPax from '../stationEntry/StationEntryPax';
import Tabbar from '../tabbar/Tabbar';
import ZFWEntryPax from '../zfwEntry/ZFWEntryPax';
interface PaxProps {
isER: boolean;
unit: 'kg' | 'lbs';
CGs: [number, number];
payloadLive: PayloadPax;
fuelLive: Fuel;
WASMData: WASMDataPax;
username?: string;
GSXPaxNum: number;
GSXCargoPercent: number;
GSXState: 'boarding' | 'deboarding' | 'idle';
}
const Pax: FC<PaxProps> = ({
isER,
unit,
CGs,
fuelLive,
payloadLive,
username,
GSXPaxNum,
GSXCargoPercent,
GSXState,
}) => {
const Pax: FC<PaxProps> = ({ WASMData, username }) => {
const [selectedTab, setSelectedTab] = useState(0);
const [payload, setPayload] = useState<PayloadPax>(initialPayload);
const [loadingState, setLoadingState] = useState<'preview' | 'accepted' | 'loaded'>('preview');
const upper1 = (overrideState: 'preview' | 'accepted' | 'loaded' = loadingState) => {
if (overrideState !== 'loaded')
return PaxConfig.weightToPax(payload.business1Left + payload.business1Center + payload.business1Right, unit);
if (overrideState !== 'loaded') return WASMData.targetPayload.business1;
return PaxConfig.weightToPax(
payloadLive.business1Left + payloadLive.business1Center + payloadLive.business1Right,
unit
);
return WASMData.livePayload.business1;
};
const upper2 = (overrideState: 'preview' | 'accepted' | 'loaded' = loadingState) => {
if (overrideState !== 'loaded')
return PaxConfig.weightToPax(payload.business2Left + payload.business2Center + payload.business2Right, unit);
if (overrideState !== 'loaded') return WASMData.targetPayload.business2;
return PaxConfig.weightToPax(
payloadLive.business2Left + payloadLive.business2Center + payloadLive.business2Right,
unit
);
return WASMData.livePayload.business2;
};
const upper3 = (overrideState: 'preview' | 'accepted' | 'loaded' = loadingState) => {
if (overrideState !== 'loaded')
return PaxConfig.weightToPax(payload.economy1Left + payload.economy1Center + payload.economy1Right, unit);
if (overrideState !== 'loaded') return WASMData.targetPayload.economy1;
return PaxConfig.weightToPax(
payloadLive.economy1Left + payloadLive.economy1Center + payloadLive.economy1Right,
unit
);
return WASMData.livePayload.economy1;
};
const upper4 = (overrideState: 'preview' | 'accepted' | 'loaded' = loadingState) => {
if (overrideState !== 'loaded')
return PaxConfig.weightToPax(payload.economy2Left + payload.economy2Center + payload.economy2Right, unit);
if (overrideState !== 'loaded') return WASMData.targetPayload.economy2;
return PaxConfig.weightToPax(
payloadLive.economy2Left + payloadLive.economy2Center + payloadLive.economy2Right,
unit
);
return WASMData.livePayload.economy2;
};
const lower1 = () => {
if (loadingState !== 'loaded') return Math.round(payload.forwardCargo);
if (loadingState !== 'loaded') return Math.round(WASMData.targetPayload.forwardCargo);
return Math.round(payloadLive.forwardCargo);
return Math.round(WASMData.livePayload.forwardCargo);
};
const lower2 = () => {
if (loadingState !== 'loaded') return Math.round(payload.rearCargo);
if (loadingState !== 'loaded') return Math.round(WASMData.targetPayload.rearCargo);
return Math.round(payloadLive.rearCargo);
return Math.round(WASMData.livePayload.rearCargo);
};
const _OEW = () => {
if (loadingState !== 'loaded')
return Math.round(payloadLive.empty + (isER ? SharedConfig.erExtraWeight[unit] * 2 : 1));
const OEW = () => {
if (loadingState !== 'loaded') return Math.round(WASMData.targetPayload.empty);
return Math.round(payloadLive.empty + payloadLive.leftAuxPax + payloadLive.rightAuxPax);
return Math.round(WASMData.livePayload.empty);
};
const crew = () => {
if (loadingState !== 'loaded') return PaxConfig.weights.base[unit].total;
if (loadingState !== 'loaded') return Math.round(WASMData.targetPayload.crew);
return Math.round(
payloadLive.cabinCrewFront +
payloadLive.cabinCrewRear +
payloadLive.pilot +
payloadLive.firstOfficer +
payloadLive.engineer
);
return Math.round(WASMData.livePayload.crew);
};
const _CGs = (): [string, boolean, string, boolean] => {
const CGs = (): [string, boolean, string, boolean] => {
if (loadingState !== 'loaded') {
const __CGs = PaxConfig.calculateCGs(
{
...payload,
empty: payloadLive.empty,
cabinCrewFront: PaxConfig.weights.base[unit].cabinCrewFront,
cabinCrewRear: PaxConfig.weights.base[unit].cabinCrewRear,
pilot: PaxConfig.weights.base[unit].pilot,
firstOfficer: PaxConfig.weights.base[unit].firstOfficer,
engineer: PaxConfig.weights.base[unit].engineer,
leftAuxPax: isER ? SharedConfig.erExtraWeight[unit] : 0,
rightAuxPax: isER ? SharedConfig.erExtraWeight[unit] : 0,
},
fuelLive
);
return [
__CGs[0].toFixed(1),
__CGs[0] < SharedConfig.CGLimits.min || __CGs[0] > SharedConfig.CGLimits.max,
__CGs[1].toFixed(1),
__CGs[1] < SharedConfig.CGLimits.min || __CGs[1] > SharedConfig.CGLimits.max,
WASMData.targetPayload.ZFWCG.toFixed(1),
WASMData.targetPayload.ZFWCG < WASMData.limits.minCG || WASMData.targetPayload.ZFWCG > WASMData.limits.maxCG,
WASMData.targetPayload.TOCG.toFixed(1),
WASMData.targetPayload.TOCG < WASMData.limits.minCG || WASMData.targetPayload.TOCG > WASMData.limits.maxCG,
];
}
return [
CGs[0].toFixed(1),
CGs[0] < SharedConfig.CGLimits.min || CGs[0] > SharedConfig.CGLimits.max,
CGs[1].toFixed(1),
CGs[1] < SharedConfig.CGLimits.min || CGs[1] > SharedConfig.CGLimits.max,
WASMData.livePayload.ZFWCG.toFixed(1),
WASMData.livePayload.ZFWCG < WASMData.limits.minCG || WASMData.livePayload.ZFWCG > WASMData.limits.maxCG,
WASMData.livePayload.TOCG.toFixed(1),
WASMData.livePayload.TOCG < WASMData.limits.minCG || WASMData.livePayload.TOCG > WASMData.limits.maxCG,
];
};
//TODO: Make GSX optional
useEffect(() => {
if (GSXState === 'idle') return;
PaxConfig.setWeightsProgressive(
payload,
GSXState === 'boarding' ? GSXPaxNum : payload.paxCount.total - GSXPaxNum,
GSXCargoPercent,
unit
);
}, [GSXPaxNum, GSXCargoPercent, GSXState]);
return (
<>
<Profile
type="PAX"
isER={isER}
isER={WASMData.userData.isER}
upper1={`${upper1()}`}
upper1max={loadingState === 'loaded' ? `${upper1('preview')}` : `${PaxConfig.stationMax.business1}`}
upper1max={loadingState === 'loaded' ? `${upper1('preview')}` : `${WASMData.limits.business1}`}
upper2={`${upper2()}`}
upper2max={loadingState === 'loaded' ? `${upper2('preview')}` : `${PaxConfig.stationMax.business2}`}
upper2max={loadingState === 'loaded' ? `${upper2('preview')}` : `${WASMData.limits.business2}`}
upper3={`${upper3()}`}
upper3max={loadingState === 'loaded' ? `${upper3('preview')}` : `${PaxConfig.stationMax.economy1}`}
upper3max={loadingState === 'loaded' ? `${upper3('preview')}` : `${WASMData.limits.economy1}`}
upper4={`${upper4()}`}
upper4max={loadingState === 'loaded' ? `${upper4('preview')}` : `${PaxConfig.stationMax.economy2}`}
upper4max={loadingState === 'loaded' ? `${upper4('preview')}` : `${WASMData.limits.economy2}`}
lower1={`${lower1()}`}
lower2={`${lower2()}`}
OEW={`${_OEW()}`}
OEW={`${OEW()}`}
crew={`${crew()}`}
unit={unit.toUpperCase()}
unit={WASMData.userData.isImperial ? 'LBS' : 'KG'}
inPreview={loadingState !== 'loaded'}
CGs={_CGs()}
CGs={CGs()}
/>
<Tabbar
tabs={
@@ -171,6 +101,7 @@ const Pax: FC<PaxProps> = ({
setSelectedTab={setSelectedTab}
/>
{/*
{username && selectedTab === 0 && (
<SBEntryPax
unit={unit}
@@ -190,26 +121,18 @@ const Pax: FC<PaxProps> = ({
}}
/>
)}
*/}
{((username && selectedTab === 1) || (!username && selectedTab === 0)) && (
<ZFWEntryPax
unit={unit}
isER={isER}
initialPayload={payload}
fuelLive={fuelLive}
payloadLive={payloadLive}
WASMData={WASMData}
loadingState={loadingState}
setLoadingState={setLoadingState}
updateView={(_payload) => {
setPayload(_payload);
}}
loadAircraft={() => {
PaxConfig.setBaseWeight(unit, isER);
PaxConfig.setWeights(payload, unit);
console.log('SET WEIGHT');
}}
/>
)}
{/*
{((username && selectedTab === 2) || (!username && selectedTab === 1)) && (
<StationEntryPax
unit={unit}
@@ -228,6 +151,7 @@ const Pax: FC<PaxProps> = ({
}}
/>
)}
*/}
</>
);
};
@@ -1,106 +1,36 @@
import { FC, useEffect, useState } from 'react';
import { PaxConfig, PayloadPax } from '../../configs/pax';
import { Fuel, SharedConfig } from '../../configs/shared';
import { emptyAircraft, SharedConfig } from '../../configs/shared';
import { COHERENT_COMBUS_WASM_CALL, COMM_BUS_UPDATE_TARGET_EVENT } from '../../constants';
import { WASMDataPax } from '../../types/WASMData';
import CGSelect from '../CGSelect/CGSelect';
import ActionBar from '../actionbar/ActionBar';
interface StationEntryProps {
unit: 'kg' | 'lbs';
isER: boolean;
initialPayload: PayloadPax;
fuelLive: Fuel;
payloadLive: PayloadPax;
WASMData: WASMDataPax;
loadingState: 'preview' | 'accepted' | 'loaded';
setLoadingState: (newState: StationEntryProps['loadingState']) => void;
updateView: (payload: PayloadPax) => void;
loadAircraft: () => void;
}
const ZFWEntryPax: FC<StationEntryProps> = ({
unit,
isER,
initialPayload,
fuelLive,
payloadLive,
loadingState,
setLoadingState,
updateView,
loadAircraft,
}) => {
const [targetZFWCG, setTargetZFWCG] = useState(SharedConfig.CGLimits.default);
const [fuel, setFuel] = useState(
Math.round(
fuelLive.main1 +
fuelLive.main1Tip +
fuelLive.main2 +
fuelLive.main3 +
fuelLive.main3Tip +
fuelLive.upperAux +
fuelLive.lowerAux +
fuelLive.tail +
fuelLive.forwardAux1 +
fuelLive.forwardAux2
)
);
const [ZFW, setZFW] = useState(
Math.round(
PaxConfig.weights.base[unit].total +
(isER ? SharedConfig.erExtraWeight[unit] * 2 : 0) +
payloadLive.empty +
initialPayload.business1Left +
initialPayload.business1Center +
initialPayload.business1Right +
initialPayload.business2Left +
initialPayload.business2Center +
initialPayload.business2Right +
initialPayload.economy1Left +
initialPayload.economy1Center +
initialPayload.economy1Right +
initialPayload.economy2Left +
initialPayload.economy2Center +
initialPayload.economy2Right +
initialPayload.forwardCargo +
initialPayload.rearCargo
)
);
const ZFWEntryPax: FC<StationEntryProps> = ({ WASMData, loadingState, setLoadingState, loadAircraft }) => {
const [targetZFWCG, setTargetZFWCG] = useState(WASMData.targetPayload.ZFWCG);
const [fuel, setFuel] = useState(Math.round(WASMData.livePayload.fuel));
const [ZFW, setZFW] = useState(Math.round(Math.max(WASMData.limits.minZFW, WASMData.targetPayload.total)));
const _ZFW = () => {
if (loadingState !== 'loaded') return ZFW;
return Math.round(
payloadLive.empty +
payloadLive.pilot +
payloadLive.firstOfficer +
payloadLive.engineer +
payloadLive.cabinCrewFront +
payloadLive.business1Left +
payloadLive.business1Center +
payloadLive.business1Right +
payloadLive.business2Left +
payloadLive.business2Center +
payloadLive.business2Right +
payloadLive.economy1Left +
payloadLive.economy1Center +
payloadLive.economy1Right +
payloadLive.economy2Left +
payloadLive.economy2Center +
payloadLive.economy2Right +
payloadLive.cabinCrewRear +
payloadLive.forwardCargo +
payloadLive.rearCargo +
payloadLive.leftAuxPax +
payloadLive.rightAuxPax
);
return Math.round(WASMData.livePayload.total);
};
const ZFWValid = () => {
return _ZFW() <= PaxConfig.maxZWF[unit];
return _ZFW() <= WASMData.limits.maxZFW;
};
const GW = () => {
return fuel + _ZFW();
};
const GWValid = () => {
return GW() <= (isER ? SharedConfig.maxTOW.er[unit] : SharedConfig.maxTOW.norm[unit]);
return GW() <= WASMData.limits.maxTOW;
};
const handleInput = (input: string, maxValue: number, setter: (value: number) => void) => {
@@ -121,20 +51,13 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
const converted = parseInt(input);
if (converted) {
if (converted < 0)
setZFW(
Math.round(
PaxConfig.weights.base[unit].total + (isER ? SharedConfig.erExtraWeight[unit] * 2 : 0) + payloadLive.empty
)
);
else if (converted > PaxConfig.maxZWF[unit]) setZFW(PaxConfig.maxZWF[unit]);
if (converted < 0) setZFW(Math.round(WASMData.targetPayload.empty + WASMData.targetPayload.crew));
else if (converted > WASMData.limits.maxZFW) setZFW(WASMData.limits.maxZFW);
else setZFW(converted);
}
};
const handleBlur = (input: string) => {
const minZFW = Math.round(
PaxConfig.weights.base[unit].total + (isER ? SharedConfig.erExtraWeight[unit] * 2 : 0) + payloadLive.empty
);
const minZFW = Math.round(WASMData.targetPayload.empty + WASMData.targetPayload.crew);
if (!input) {
setZFW(minZFW);
@@ -144,30 +67,39 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
const converted = parseInt(input);
if (converted) {
if (converted < minZFW) setZFW(minZFW);
else if (converted > PaxConfig.maxZWF[unit]) setZFW(PaxConfig.maxZWF[unit]);
else if (converted > WASMData.limits.maxZFW) setZFW(WASMData.limits.maxZFW);
else setZFW(converted);
}
updateView(PaxConfig.distribute(converted, targetZFWCG, payloadLive.empty, fuelLive, unit, isER));
updateData(converted);
};
useEffect(
() =>
setFuel((prev) =>
prev > (isER ? SharedConfig.maxFuel.er[unit] : SharedConfig.maxFuel.norm[unit])
? isER
? SharedConfig.maxFuel.er[unit]
: SharedConfig.maxFuel.norm[unit]
: prev
),
[isER]
setFuel((prev) => {
if (prev > WASMData.limits.maxFuel) return WASMData.limits.maxFuel;
return prev;
}),
[WASMData.userData.isER]
);
const updateData = (ZFWTarget?: number, CGTarget?: number) => {
Coherent.call(
COHERENT_COMBUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
JSON.stringify({
mode: 1,
ZFWTarget: ZFWTarget ?? ZFW,
CGTarget: CGTarget ?? targetZFWCG,
})
);
};
return (
<>
<div className="block flex w-full flex-col opacity-100 transition-opacity duration-150 ease-linear mb-4">
<div className="relative flex w-full items-center justify-between rounded-t-md bg-zinc-600 p-2 px-4">
<label>Target ZFW ({unit})</label>
<label>Target ZFW ({WASMData.userData.isImperial ? 'lbs' : 'kg'})</label>
<input
type="text"
placeholder=""
@@ -188,14 +120,14 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
increase={() =>
setTargetZFWCG((prev) => {
const _new = prev + 0.1;
updateView(PaxConfig.distribute(ZFW, _new, payloadLive.empty, fuelLive, unit, isER));
updateData(undefined, _new);
return _new;
})
}
decrease={() =>
setTargetZFWCG((prev) => {
const _new = prev - 0.1;
updateView(PaxConfig.distribute(ZFW, _new, payloadLive.empty, fuelLive, unit, isER));
updateData(undefined, _new);
return _new;
})
}
@@ -205,29 +137,27 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
<div className="block flex w-full flex-col opacity-100 transition-opacity duration-150 ease-linear mb-4">
<div className="relative flex w-full items-center justify-between rounded-md bg-zinc-600 p-2 px-4">
<label>Fuel ({unit})</label>
<label>Fuel ({WASMData.userData.isImperial ? 'lbs' : 'kg'})</label>
<input
type="text"
placeholder=""
className={`w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right`}
value={fuel}
onChange={(e) =>
handleInput(
e.target.value,
isER ? SharedConfig.maxFuel.er[unit] : SharedConfig.maxFuel.norm[unit],
setFuel
)
}
onChange={(e) => handleInput(e.target.value, WASMData.limits.maxFuel, setFuel)}
disabled={loadingState !== 'preview'}
/>
<button
className="middle none center rounded-lg bg-green-600 px-6 py-3 font-sans text-xs font-bold uppercase text-white shadow-md shadow-green-500/20 transition-all hover:shadow-lg hover:shadow-green-500/40 focus:opacity-[0.85] focus:shadow-none active:opacity-[0.85] active:shadow-none disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none"
data-ripple-light="true"
onClick={() => {
SimVar.SetSimVarValue('L:MD11_EFB_PAYLOAD_FUEL', 'lbs', unit === 'kg' ? fuel * 2.20462262185 : fuel);
SimVar.SetSimVarValue(
'L:MD11_EFB_PAYLOAD_FUEL',
'lbs',
WASMData.userData.isImperial ? fuel : fuel * 2.20462262185
);
SimVar.SetSimVarValue('L:MD11_EFB_READ_READY', 'bool', true);
}}
disabled={loadingState !== 'preview'}
disabled={loadingState !== 'preview' || fuel === Math.round(WASMData.livePayload.fuel)}
>
Load Fuel
</button>
@@ -237,7 +167,7 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
<div className="block flex w-full flex-col opacity-100 transition-opacity duration-150 ease-linear mb-4">
<div className="relative flex w-full items-center justify-between rounded-t-md bg-zinc-600 p-2 px-4">
<label>
{loadingState !== 'loaded' ? 'Expected' : 'Actual'} ZFW ({unit})
{loadingState !== 'loaded' ? 'Expected' : 'Actual'} ZFW ({WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
@@ -249,7 +179,7 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
</div>
<div className="relative flex w-full items-center justify-between rounded-b-md bg-zinc-700 p-2 px-4">
<label>
{loadingState !== 'loaded' ? 'Expected' : 'Actual'} GW ({unit})
{loadingState !== 'loaded' ? 'Expected' : 'Actual'} GW ({WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
@@ -274,7 +204,7 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
unload={() => {
setLoadingState('preview');
PaxConfig.unload(unit, isER);
emptyAircraft();
}}
/>
</>
@@ -1,3 +1,5 @@
import { COHERENT_COMBUS_WASM_CALL, COMM_BUS_LIVE_DATA_EVENT } from '../constants';
export interface Fuel {
main1: number;
main3: number;
@@ -164,3 +166,13 @@ export const getFuel = (unit: 'kg' | 'lbs') => {
return fuel;
};
export const emptyAircraft = () => {
Coherent.call(
COHERENT_COMBUS_WASM_CALL,
COMM_BUS_LIVE_DATA_EVENT,
JSON.stringify({
mode: 3,
})
);
};
+4 -6
View File
@@ -1,6 +1,4 @@
export const COMMANDS = 'KHOFMANN_PDF_READER_COMMANDS';
export const DATA = 'KHOFMANN_PDF_READER_DATA';
export const LIST = 'LIST';
export const LOAD = 'LOAD';
export const SAVE = 'SAVE';
export const MAX_LIST = 10;
export const COHERENT_COMBUS_WASM_CALL = 'COMM_BUS_WASM_CALLBACK';
export const TFDI_SIMBRIEF_USERNAME_EVENT = 'requestSimBriefUsername';
export const COMM_BUS_LIVE_DATA_EVENT = 'khofmann_tfdi_md-11_load_manager_live_data';
export const COMM_BUS_UPDATE_TARGET_EVENT = 'khofmann_tfdi_md-11_load_manager_update_target';
+85
View File
@@ -0,0 +1,85 @@
export interface WASMDataPax {
livePayload: LivePayloadPax;
targetPayload: TargetPayloadPax;
GSX: GSX;
userData: UserData;
limits: LimitsPax;
}
interface TargetPayloadPax {
empty: number;
crew: number;
business1: number;
business2: number;
economy1: number;
economy2: number;
forwardCargo: number;
rearCargo: number;
ZFWCG: number;
TOCG: number;
total: number;
}
interface LivePayloadPax extends TargetPayloadPax {
fuel: number;
}
interface TargetPayloadF {
empty: number;
crew: number;
upper1: number;
upper2: number;
upper3: number;
upper4: number;
lowerForward: number;
lowerRear: number;
ZFWCG: number;
TOCG: number;
total: number;
}
interface LivePayloadF extends TargetPayloadF {
fuel: number;
}
interface GSX {
boardingState: number;
deboardingState: number;
passengersBoarded: number;
passengersDeboarded: number;
cargoBoarded: number;
cargoDeboarded: number;
}
interface Limits {
minCG: number;
maxCG: number;
maxFuel: number;
maxTOW: number;
maxZFW: number;
minZFW: number;
}
interface LimitsPax extends Limits {
business1: number;
business2: number;
economy1: number;
economy2: number;
forwardCargo: number;
rearCargo: number;
}
interface LimitsF extends Limits {
upper1: number;
upper2: number;
upper3: number;
upper4: number;
lowerForward: number;
lowerRear: number;
}
interface UserData {
isCargo: boolean;
isER: boolean;
isImperial: boolean;
}