ZFW Target Set

This commit is contained in:
Kilian Hofmann 2025-06-11 00:27:27 +02:00
parent 9224300c85
commit ee46d0bff1
13 changed files with 382 additions and 499 deletions

View File

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

View File

@ -1,104 +1,40 @@
import { FC, StrictMode, useCallback, useEffect, useRef, useState } from 'react'; import { FC, StrictMode, useCallback, useEffect, useState } from 'react';
import Freight from './components/freight/Freight';
import Pax from './components/pax/Pax'; import Pax from './components/pax/Pax';
import { PayloadFreight, calculateCGsFreight, getWeightsFreight } from './configs/freighter'; import { COHERENT_COMBUS_WASM_CALL, COMM_BUS_LIVE_DATA_EVENT, TFDI_SIMBRIEF_USERNAME_EVENT } from './constants';
import { PaxConfig, PayloadPax } from './configs/pax'; import { WASMDataPax } from './types/WASMData';
import { Fuel, getFuel, initialFuel, initialPayload } from './configs/shared';
interface IAppProps { interface IAppProps {
commBus: ViewListener.ViewListener; commBus: ViewListener.ViewListener;
} }
const App: FC<IAppProps> = ({ commBus }) => { 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>(); const [SBUsername, setSBUsername] = useState<string>();
//FIXME: TS Type
// From sim const [WASMData, setWASMData] = useState<WASMDataPax>();
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]);
const [isReady, setIsReady] = useState(false); const [isReady, setIsReady] = useState(false);
const requestRef = useRef<number | undefined>(undefined);
// Main Loop for Live Payload // 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 // CommBus
const usernameCallback = useCallback((username: string) => { const usernameCallback = useCallback((username: string) => {
setSBUsername(username); setSBUsername(username);
setIsReady(true); setIsReady(true);
}, []); }, []);
const wasmCallback = useCallback((data: any) => { const wasmCallback = useCallback((data: string) => {
console.log('WASM DATA', JSON.parse(data)); setWASMData(JSON.parse(data));
}, []); }, []);
useEffect(() => { useEffect(() => {
console.log('Initializing CommBus'); console.log('Initializing CommBus');
commBus.on('receiveSimBriefUsername', usernameCallback); commBus.on(COMM_BUS_LIVE_DATA_EVENT, usernameCallback);
commBus.on('khofmann_tfdi_md-11_load_manager_live_data', wasmCallback); commBus.on(COMM_BUS_LIVE_DATA_EVENT, wasmCallback);
setTimeout(() => { setTimeout(() => {
Coherent.call('COMM_BUS_WASM_CALLBACK', 'requestSimBriefUsername', 'null'); Coherent.call(COHERENT_COMBUS_WASM_CALL, TFDI_SIMBRIEF_USERNAME_EVENT, 'null');
}, 1000); }, 1000);
return () => { return () => {
commBus.off('receiveSimBriefUsername', usernameCallback); 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> <StrictMode>
<div className="flex w-full justify-center pt-2 bg-zinc-900"> <div className="flex w-full justify-center pt-2 bg-zinc-900">
<div className="flex w-3/4 flex-col items-center"> <div className="flex w-3/4 flex-col items-center">
{isReady ? ( {isReady && WASMData ? (
isCargo ? ( WASMData.userData.isCargo ? (
<Freight isER={isER} unit={unit} OEW={payloadLive.empty} CGs={CGs} /> <>Not yet Implemented</>
) : ( ) : (
<Pax <Pax WASMData={WASMData} username={SBUsername} />
isER={isER}
unit={unit}
CGs={CGs}
fuelLive={fuel}
payloadLive={payloadLive as PayloadPax}
username={SBUsername}
GSXPaxNum={GSXPaxNum}
GSXCargoPercent={GSXCargoPercent}
GSXState={GSXState}
/>
) )
) : ( ) : (
<h1 className="text-sm font-medium">LOADING</h1> <h1 className="text-sm font-medium">LOADING</h1>

View File

@ -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;

View File

@ -1,167 +1,97 @@
import { FC, useEffect, useState } from 'react'; import { FC, useState } from 'react';
import { PaxConfig, PayloadPax } from '../../configs/pax'; import { WASMDataPax } from '../../types/WASMData';
import { Fuel, initialPayload, SharedConfig } from '../../configs/shared';
import Profile from '../profile/Profile'; import Profile from '../profile/Profile';
import SBEntryPax from '../SBEntry/SBEntryPax';
import StationEntryPax from '../stationEntry/StationEntryPax';
import Tabbar from '../tabbar/Tabbar'; import Tabbar from '../tabbar/Tabbar';
import ZFWEntryPax from '../zfwEntry/ZFWEntryPax'; import ZFWEntryPax from '../zfwEntry/ZFWEntryPax';
interface PaxProps { interface PaxProps {
isER: boolean; WASMData: WASMDataPax;
unit: 'kg' | 'lbs';
CGs: [number, number];
payloadLive: PayloadPax;
fuelLive: Fuel;
username?: string; username?: string;
GSXPaxNum: number;
GSXCargoPercent: number;
GSXState: 'boarding' | 'deboarding' | 'idle';
} }
const Pax: FC<PaxProps> = ({ const Pax: FC<PaxProps> = ({ WASMData, username }) => {
isER,
unit,
CGs,
fuelLive,
payloadLive,
username,
GSXPaxNum,
GSXCargoPercent,
GSXState,
}) => {
const [selectedTab, setSelectedTab] = useState(0); const [selectedTab, setSelectedTab] = useState(0);
const [payload, setPayload] = useState<PayloadPax>(initialPayload);
const [loadingState, setLoadingState] = useState<'preview' | 'accepted' | 'loaded'>('preview'); const [loadingState, setLoadingState] = useState<'preview' | 'accepted' | 'loaded'>('preview');
const upper1 = (overrideState: 'preview' | 'accepted' | 'loaded' = loadingState) => { const upper1 = (overrideState: 'preview' | 'accepted' | 'loaded' = loadingState) => {
if (overrideState !== 'loaded') if (overrideState !== 'loaded') return WASMData.targetPayload.business1;
return PaxConfig.weightToPax(payload.business1Left + payload.business1Center + payload.business1Right, unit);
return PaxConfig.weightToPax( return WASMData.livePayload.business1;
payloadLive.business1Left + payloadLive.business1Center + payloadLive.business1Right,
unit
);
}; };
const upper2 = (overrideState: 'preview' | 'accepted' | 'loaded' = loadingState) => { const upper2 = (overrideState: 'preview' | 'accepted' | 'loaded' = loadingState) => {
if (overrideState !== 'loaded') if (overrideState !== 'loaded') return WASMData.targetPayload.business2;
return PaxConfig.weightToPax(payload.business2Left + payload.business2Center + payload.business2Right, unit);
return PaxConfig.weightToPax( return WASMData.livePayload.business2;
payloadLive.business2Left + payloadLive.business2Center + payloadLive.business2Right,
unit
);
}; };
const upper3 = (overrideState: 'preview' | 'accepted' | 'loaded' = loadingState) => { const upper3 = (overrideState: 'preview' | 'accepted' | 'loaded' = loadingState) => {
if (overrideState !== 'loaded') if (overrideState !== 'loaded') return WASMData.targetPayload.economy1;
return PaxConfig.weightToPax(payload.economy1Left + payload.economy1Center + payload.economy1Right, unit);
return PaxConfig.weightToPax( return WASMData.livePayload.economy1;
payloadLive.economy1Left + payloadLive.economy1Center + payloadLive.economy1Right,
unit
);
}; };
const upper4 = (overrideState: 'preview' | 'accepted' | 'loaded' = loadingState) => { const upper4 = (overrideState: 'preview' | 'accepted' | 'loaded' = loadingState) => {
if (overrideState !== 'loaded') if (overrideState !== 'loaded') return WASMData.targetPayload.economy2;
return PaxConfig.weightToPax(payload.economy2Left + payload.economy2Center + payload.economy2Right, unit);
return PaxConfig.weightToPax( return WASMData.livePayload.economy2;
payloadLive.economy2Left + payloadLive.economy2Center + payloadLive.economy2Right,
unit
);
}; };
const lower1 = () => { 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 = () => { 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 = () => { const OEW = () => {
if (loadingState !== 'loaded') if (loadingState !== 'loaded') return Math.round(WASMData.targetPayload.empty);
return Math.round(payloadLive.empty + (isER ? SharedConfig.erExtraWeight[unit] * 2 : 1));
return Math.round(payloadLive.empty + payloadLive.leftAuxPax + payloadLive.rightAuxPax); return Math.round(WASMData.livePayload.empty);
}; };
const crew = () => { const crew = () => {
if (loadingState !== 'loaded') return PaxConfig.weights.base[unit].total; if (loadingState !== 'loaded') return Math.round(WASMData.targetPayload.crew);
return Math.round( return Math.round(WASMData.livePayload.crew);
payloadLive.cabinCrewFront +
payloadLive.cabinCrewRear +
payloadLive.pilot +
payloadLive.firstOfficer +
payloadLive.engineer
);
}; };
const _CGs = (): [string, boolean, string, boolean] => { const CGs = (): [string, boolean, string, boolean] => {
if (loadingState !== 'loaded') { 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 [ return [
__CGs[0].toFixed(1), WASMData.targetPayload.ZFWCG.toFixed(1),
__CGs[0] < SharedConfig.CGLimits.min || __CGs[0] > SharedConfig.CGLimits.max, WASMData.targetPayload.ZFWCG < WASMData.limits.minCG || WASMData.targetPayload.ZFWCG > WASMData.limits.maxCG,
__CGs[1].toFixed(1), WASMData.targetPayload.TOCG.toFixed(1),
__CGs[1] < SharedConfig.CGLimits.min || __CGs[1] > SharedConfig.CGLimits.max, WASMData.targetPayload.TOCG < WASMData.limits.minCG || WASMData.targetPayload.TOCG > WASMData.limits.maxCG,
]; ];
} }
return [ return [
CGs[0].toFixed(1), WASMData.livePayload.ZFWCG.toFixed(1),
CGs[0] < SharedConfig.CGLimits.min || CGs[0] > SharedConfig.CGLimits.max, WASMData.livePayload.ZFWCG < WASMData.limits.minCG || WASMData.livePayload.ZFWCG > WASMData.limits.maxCG,
CGs[1].toFixed(1), WASMData.livePayload.TOCG.toFixed(1),
CGs[1] < SharedConfig.CGLimits.min || CGs[1] > SharedConfig.CGLimits.max, 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 ( return (
<> <>
<Profile <Profile
type="PAX" type="PAX"
isER={isER} isER={WASMData.userData.isER}
upper1={`${upper1()}`} upper1={`${upper1()}`}
upper1max={loadingState === 'loaded' ? `${upper1('preview')}` : `${PaxConfig.stationMax.business1}`} upper1max={loadingState === 'loaded' ? `${upper1('preview')}` : `${WASMData.limits.business1}`}
upper2={`${upper2()}`} upper2={`${upper2()}`}
upper2max={loadingState === 'loaded' ? `${upper2('preview')}` : `${PaxConfig.stationMax.business2}`} upper2max={loadingState === 'loaded' ? `${upper2('preview')}` : `${WASMData.limits.business2}`}
upper3={`${upper3()}`} upper3={`${upper3()}`}
upper3max={loadingState === 'loaded' ? `${upper3('preview')}` : `${PaxConfig.stationMax.economy1}`} upper3max={loadingState === 'loaded' ? `${upper3('preview')}` : `${WASMData.limits.economy1}`}
upper4={`${upper4()}`} upper4={`${upper4()}`}
upper4max={loadingState === 'loaded' ? `${upper4('preview')}` : `${PaxConfig.stationMax.economy2}`} upper4max={loadingState === 'loaded' ? `${upper4('preview')}` : `${WASMData.limits.economy2}`}
lower1={`${lower1()}`} lower1={`${lower1()}`}
lower2={`${lower2()}`} lower2={`${lower2()}`}
OEW={`${_OEW()}`} OEW={`${OEW()}`}
crew={`${crew()}`} crew={`${crew()}`}
unit={unit.toUpperCase()} unit={WASMData.userData.isImperial ? 'LBS' : 'KG'}
inPreview={loadingState !== 'loaded'} inPreview={loadingState !== 'loaded'}
CGs={_CGs()} CGs={CGs()}
/> />
<Tabbar <Tabbar
tabs={ tabs={
@ -171,6 +101,7 @@ const Pax: FC<PaxProps> = ({
setSelectedTab={setSelectedTab} setSelectedTab={setSelectedTab}
/> />
{/*
{username && selectedTab === 0 && ( {username && selectedTab === 0 && (
<SBEntryPax <SBEntryPax
unit={unit} unit={unit}
@ -190,26 +121,18 @@ const Pax: FC<PaxProps> = ({
}} }}
/> />
)} )}
*/}
{((username && selectedTab === 1) || (!username && selectedTab === 0)) && ( {((username && selectedTab === 1) || (!username && selectedTab === 0)) && (
<ZFWEntryPax <ZFWEntryPax
unit={unit} WASMData={WASMData}
isER={isER}
initialPayload={payload}
fuelLive={fuelLive}
payloadLive={payloadLive}
loadingState={loadingState} loadingState={loadingState}
setLoadingState={setLoadingState} setLoadingState={setLoadingState}
updateView={(_payload) => {
setPayload(_payload);
}}
loadAircraft={() => { loadAircraft={() => {
PaxConfig.setBaseWeight(unit, isER); console.log('SET WEIGHT');
PaxConfig.setWeights(payload, unit);
}} }}
/> />
)} )}
{/*
{((username && selectedTab === 2) || (!username && selectedTab === 1)) && ( {((username && selectedTab === 2) || (!username && selectedTab === 1)) && (
<StationEntryPax <StationEntryPax
unit={unit} unit={unit}
@ -228,6 +151,7 @@ const Pax: FC<PaxProps> = ({
}} }}
/> />
)} )}
*/}
</> </>
); );
}; };

View File

@ -1,106 +1,36 @@
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useState } from 'react';
import { PaxConfig, PayloadPax } from '../../configs/pax'; import { emptyAircraft, SharedConfig } from '../../configs/shared';
import { Fuel, 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 CGSelect from '../CGSelect/CGSelect';
import ActionBar from '../actionbar/ActionBar'; import ActionBar from '../actionbar/ActionBar';
interface StationEntryProps { interface StationEntryProps {
unit: 'kg' | 'lbs'; WASMData: WASMDataPax;
isER: boolean;
initialPayload: PayloadPax;
fuelLive: Fuel;
payloadLive: PayloadPax;
loadingState: 'preview' | 'accepted' | 'loaded'; loadingState: 'preview' | 'accepted' | 'loaded';
setLoadingState: (newState: StationEntryProps['loadingState']) => void; setLoadingState: (newState: StationEntryProps['loadingState']) => void;
updateView: (payload: PayloadPax) => void;
loadAircraft: () => void; loadAircraft: () => void;
} }
const ZFWEntryPax: FC<StationEntryProps> = ({ const ZFWEntryPax: FC<StationEntryProps> = ({ WASMData, loadingState, setLoadingState, loadAircraft }) => {
unit, const [targetZFWCG, setTargetZFWCG] = useState(WASMData.targetPayload.ZFWCG);
isER, const [fuel, setFuel] = useState(Math.round(WASMData.livePayload.fuel));
initialPayload, const [ZFW, setZFW] = useState(Math.round(Math.max(WASMData.limits.minZFW, WASMData.targetPayload.total)));
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 _ZFW = () => { const _ZFW = () => {
if (loadingState !== 'loaded') return ZFW; if (loadingState !== 'loaded') return ZFW;
return Math.round( return Math.round(WASMData.livePayload.total);
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
);
}; };
const ZFWValid = () => { const ZFWValid = () => {
return _ZFW() <= PaxConfig.maxZWF[unit]; return _ZFW() <= WASMData.limits.maxZFW;
}; };
const GW = () => { const GW = () => {
return fuel + _ZFW(); return fuel + _ZFW();
}; };
const GWValid = () => { 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) => { const handleInput = (input: string, maxValue: number, setter: (value: number) => void) => {
@ -121,20 +51,13 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
const converted = parseInt(input); const converted = parseInt(input);
if (converted) { if (converted) {
if (converted < 0) if (converted < 0) setZFW(Math.round(WASMData.targetPayload.empty + WASMData.targetPayload.crew));
setZFW( else if (converted > WASMData.limits.maxZFW) setZFW(WASMData.limits.maxZFW);
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]);
else setZFW(converted); else setZFW(converted);
} }
}; };
const handleBlur = (input: string) => { const handleBlur = (input: string) => {
const minZFW = Math.round( const minZFW = Math.round(WASMData.targetPayload.empty + WASMData.targetPayload.crew);
PaxConfig.weights.base[unit].total + (isER ? SharedConfig.erExtraWeight[unit] * 2 : 0) + payloadLive.empty
);
if (!input) { if (!input) {
setZFW(minZFW); setZFW(minZFW);
@ -144,30 +67,39 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
const converted = parseInt(input); const converted = parseInt(input);
if (converted) { if (converted) {
if (converted < minZFW) setZFW(minZFW); 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); else setZFW(converted);
} }
updateView(PaxConfig.distribute(converted, targetZFWCG, payloadLive.empty, fuelLive, unit, isER)); updateData(converted);
}; };
useEffect( useEffect(
() => () =>
setFuel((prev) => setFuel((prev) => {
prev > (isER ? SharedConfig.maxFuel.er[unit] : SharedConfig.maxFuel.norm[unit]) if (prev > WASMData.limits.maxFuel) return WASMData.limits.maxFuel;
? isER return prev;
? SharedConfig.maxFuel.er[unit] }),
: SharedConfig.maxFuel.norm[unit] [WASMData.userData.isER]
: prev
),
[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 ( return (
<> <>
<div className="block flex w-full flex-col opacity-100 transition-opacity duration-150 ease-linear mb-4"> <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"> <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 <input
type="text" type="text"
placeholder="" placeholder=""
@ -188,14 +120,14 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
increase={() => increase={() =>
setTargetZFWCG((prev) => { setTargetZFWCG((prev) => {
const _new = prev + 0.1; const _new = prev + 0.1;
updateView(PaxConfig.distribute(ZFW, _new, payloadLive.empty, fuelLive, unit, isER)); updateData(undefined, _new);
return _new; return _new;
}) })
} }
decrease={() => decrease={() =>
setTargetZFWCG((prev) => { setTargetZFWCG((prev) => {
const _new = prev - 0.1; const _new = prev - 0.1;
updateView(PaxConfig.distribute(ZFW, _new, payloadLive.empty, fuelLive, unit, isER)); updateData(undefined, _new);
return _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="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"> <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 <input
type="text" type="text"
placeholder="" placeholder=""
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={(e) => onChange={(e) => handleInput(e.target.value, WASMData.limits.maxFuel, setFuel)}
handleInput(
e.target.value,
isER ? SharedConfig.maxFuel.er[unit] : SharedConfig.maxFuel.norm[unit],
setFuel
)
}
disabled={loadingState !== 'preview'} disabled={loadingState !== 'preview'}
/> />
<button <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" 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" data-ripple-light="true"
onClick={() => { 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); SimVar.SetSimVarValue('L:MD11_EFB_READ_READY', 'bool', true);
}} }}
disabled={loadingState !== 'preview'} disabled={loadingState !== 'preview' || fuel === Math.round(WASMData.livePayload.fuel)}
> >
Load Fuel Load Fuel
</button> </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="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"> <div className="relative flex w-full items-center justify-between rounded-t-md bg-zinc-600 p-2 px-4">
<label> <label>
{loadingState !== 'loaded' ? 'Expected' : 'Actual'} ZFW ({unit}) {loadingState !== 'loaded' ? 'Expected' : 'Actual'} ZFW ({WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label> </label>
<input <input
type="text" type="text"
@ -249,7 +179,7 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
</div> </div>
<div className="relative flex w-full items-center justify-between rounded-b-md bg-zinc-700 p-2 px-4"> <div className="relative flex w-full items-center justify-between rounded-b-md bg-zinc-700 p-2 px-4">
<label> <label>
{loadingState !== 'loaded' ? 'Expected' : 'Actual'} GW ({unit}) {loadingState !== 'loaded' ? 'Expected' : 'Actual'} GW ({WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label> </label>
<input <input
type="text" type="text"
@ -274,7 +204,7 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
unload={() => { unload={() => {
setLoadingState('preview'); setLoadingState('preview');
PaxConfig.unload(unit, isER); emptyAircraft();
}} }}
/> />
</> </>

View File

@ -1,3 +1,5 @@
import { COHERENT_COMBUS_WASM_CALL, COMM_BUS_LIVE_DATA_EVENT } from '../constants';
export interface Fuel { export interface Fuel {
main1: number; main1: number;
main3: number; main3: number;
@ -164,3 +166,13 @@ export const getFuel = (unit: 'kg' | 'lbs') => {
return fuel; return fuel;
}; };
export const emptyAircraft = () => {
Coherent.call(
COHERENT_COMBUS_WASM_CALL,
COMM_BUS_LIVE_DATA_EVENT,
JSON.stringify({
mode: 3,
})
);
};

View File

@ -1,6 +1,4 @@
export const COMMANDS = 'KHOFMANN_PDF_READER_COMMANDS'; export const COHERENT_COMBUS_WASM_CALL = 'COMM_BUS_WASM_CALLBACK';
export const DATA = 'KHOFMANN_PDF_READER_DATA'; export const TFDI_SIMBRIEF_USERNAME_EVENT = 'requestSimBriefUsername';
export const LIST = 'LIST'; export const COMM_BUS_LIVE_DATA_EVENT = 'khofmann_tfdi_md-11_load_manager_live_data';
export const LOAD = 'LOAD'; export const COMM_BUS_UPDATE_TARGET_EVENT = 'khofmann_tfdi_md-11_load_manager_update_target';
export const SAVE = 'SAVE';
export const MAX_LIST = 10;

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;
}

View File

@ -15,7 +15,7 @@ FuelData_t* liveFuelData;
bool commBusCallbackRegistered; bool commBusCallbackRegistered;
HANDLE simConnect; HANDLE simConnect;
FILE* logFile; FILE* logFile;
char sendTimer = 0; MODULE_VAR tick18 = { TICK18 };
// Init // Init
extern "C" MSFS_CALLBACK void module_init(void) { extern "C" MSFS_CALLBACK void module_init(void) {
@ -35,6 +35,8 @@ extern "C" MSFS_CALLBACK void module_init(void) {
targetFPayloadData = new fPayloadData_t(); targetFPayloadData = new fPayloadData_t();
liveFuelData = new FuelData_t(); liveFuelData = new FuelData_t();
targetFPayloadData->ZFWCG = targetPaxPayloadData->ZFWCG = 21;
#pragma region SimConnect #pragma region SimConnect
// SimConnect open // SimConnect open
@ -414,15 +416,15 @@ 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) {
/*
switch (service_id) { switch (service_id) {
case PANEL_SERVICE_PRE_UPDATE: { case PANEL_SERVICE_PRE_UPDATE: {
lookup_var(&tick18);
if (fmod(tick18.var_value.n, 3) == 0) sendData();
} }
default: default:
break; break;
} }
*/
return true; return true;
} }
@ -431,6 +433,8 @@ void commBusUpdateTargetCallback(const char* args, unsigned int size, void* ctx)
printf("Target payload update request: %d", receiveData(args)); printf("Target payload update request: %d", receiveData(args));
} }
#pragma region JSON data handling
// JSON receive // JSON receive
int receiveData(const char* buf) { int receiveData(const char* buf) {
if (liveFPayloadData == nullptr || livePaxPayloadData == nullptr || targetFPayloadData == nullptr || targetPaxPayloadData == nullptr || if (liveFPayloadData == nullptr || livePaxPayloadData == nullptr || targetFPayloadData == nullptr || targetPaxPayloadData == nullptr ||
@ -440,20 +444,6 @@ int receiveData(const char* buf) {
document.Parse(buf); document.Parse(buf);
if (document.HasParseError()) return document.GetParseError(); if (document.HasParseError()) return document.GetParseError();
// Shared part 1
targetFPayloadData->empty = targetPaxPayloadData->empty = FROM_POUNDS(UserData->isImperial, liveFPayloadData->empty);
targetFPayloadData->pilot = targetPaxPayloadData->pilot = PILOT_WEIGHT(UserData->isImperial);
targetFPayloadData->firstOfficer = targetPaxPayloadData->firstOfficer = PILOT_WEIGHT(UserData->isImperial);
targetFPayloadData->engineer = targetPaxPayloadData->engineer = PILOT_WEIGHT(UserData->isImperial);
// Shared part 2
targetFPayloadData->leftAux = targetPaxPayloadData->leftAux = UserData->isER ? AUX_WEIGHT(UserData->isImperial) : 0;
targetFPayloadData->rightAux = targetPaxPayloadData->rightAux = UserData->isER ? AUX_WEIGHT(UserData->isImperial) : 0;
// Pax only fixed weights
if (!UserData->isCargo) {
targetPaxPayloadData->cabinCrewFront = FRONT_CREW_WEIGHT(UserData->isImperial);
targetPaxPayloadData->cabinCrewRear = REAR_CREW_WEIGHT(UserData->isImperial);
}
if (document.HasMember("mode")) { if (document.HasMember("mode")) {
int mode = document["mode"].GetUint(); int mode = document["mode"].GetUint();
switch(mode) { switch(mode) {
@ -512,6 +502,7 @@ int receiveData(const char* buf) {
default: default:
break; break;
} }
sendData();
} }
else return -1; else return -1;
@ -533,6 +524,10 @@ void sendData () {
targetPayload.SetObject(); targetPayload.SetObject();
rapidjson::Value GSX; rapidjson::Value GSX;
GSX.SetObject(); GSX.SetObject();
rapidjson::Value userData;
userData.SetObject();
rapidjson::Value limits;
limits.SetObject();
rapidjson::StringBuffer strbuf; rapidjson::StringBuffer strbuf;
rapidjson::Writer<rapidjson::StringBuffer> writer(strbuf); rapidjson::Writer<rapidjson::StringBuffer> writer(strbuf);
@ -558,6 +553,12 @@ void sendData () {
livePayload.AddMember("upper4", FROM_POUNDS(UserData->isImperial, liveFPayloadData->upper4Left + liveFPayloadData->upper4Right), allocator); livePayload.AddMember("upper4", FROM_POUNDS(UserData->isImperial, liveFPayloadData->upper4Left + liveFPayloadData->upper4Right), allocator);
livePayload.AddMember("lowerForward", FROM_POUNDS(UserData->isImperial, liveFPayloadData->lowerForward), allocator); livePayload.AddMember("lowerForward", FROM_POUNDS(UserData->isImperial, liveFPayloadData->lowerForward), allocator);
livePayload.AddMember("lowerRear", FROM_POUNDS(UserData->isImperial, liveFPayloadData->lowerRear), allocator); livePayload.AddMember("lowerRear", FROM_POUNDS(UserData->isImperial, liveFPayloadData->lowerRear), allocator);
livePayload.AddMember("total", FROM_POUNDS(UserData->isImperial, liveFPayloadData->total), allocator);
// CGs
//TODO: Enable for F
//calculateCGs(liveFPayloadData, liveFuelData, &liveFPayloadData->ZFWCG, &liveFPayloadData->TOCG, true);
targetPayload.AddMember("ZFWCG", liveFPayloadData->ZFWCG, allocator);
targetPayload.AddMember("TOCG", liveFPayloadData->TOCG, allocator);
} }
// Pax only (converted to passengers) // Pax only (converted to passengers)
else { else {
@ -566,7 +567,7 @@ void sendData () {
FROM_POUNDS(UserData->isImperial, livePaxPayloadData->engineer) + FROM_POUNDS(UserData->isImperial, livePaxPayloadData->cabinCrewFront) + FROM_POUNDS(UserData->isImperial, livePaxPayloadData->engineer) + FROM_POUNDS(UserData->isImperial, livePaxPayloadData->cabinCrewFront) +
FROM_POUNDS(UserData->isImperial, livePaxPayloadData->cabinCrewRear), FROM_POUNDS(UserData->isImperial, livePaxPayloadData->cabinCrewRear),
allocator); allocator);
livePayload.AddMember("businnes1", livePayload.AddMember("business1",
(short)(FROM_POUNDS(UserData->isImperial, livePaxPayloadData->business1Left + livePaxPayloadData->business1Center + (short)(FROM_POUNDS(UserData->isImperial, livePaxPayloadData->business1Left + livePaxPayloadData->business1Center +
livePaxPayloadData->business1Right) / PAX_WEIGHT(UserData->isImperial)), livePaxPayloadData->business1Right) / PAX_WEIGHT(UserData->isImperial)),
allocator); allocator);
@ -584,11 +585,12 @@ void sendData () {
allocator); allocator);
livePayload.AddMember("forwardCargo", FROM_POUNDS(UserData->isImperial, livePaxPayloadData->forwardCargo), allocator); livePayload.AddMember("forwardCargo", FROM_POUNDS(UserData->isImperial, livePaxPayloadData->forwardCargo), allocator);
livePayload.AddMember("rearCargo", FROM_POUNDS(UserData->isImperial, livePaxPayloadData->rearCargo), allocator); livePayload.AddMember("rearCargo", FROM_POUNDS(UserData->isImperial, livePaxPayloadData->rearCargo), allocator);
} livePayload.AddMember("total", FROM_POUNDS(UserData->isImperial, livePaxPayloadData->total), allocator);
// CGs // CGs
calculateCGs(livePaxPayloadData, liveFuelData, &livePaxPayloadData->ZFWCG, &livePaxPayloadData->TOCG, true); calculateCGs(livePaxPayloadData, liveFuelData, &livePaxPayloadData->ZFWCG, &livePaxPayloadData->TOCG, true);
livePayload.AddMember("ZFWCG", UserData->isCargo ? liveFPayloadData->ZFWCG : livePaxPayloadData->ZFWCG, allocator); livePayload.AddMember("ZFWCG",livePaxPayloadData->ZFWCG, allocator);
livePayload.AddMember("TOCG", UserData->isCargo ? liveFPayloadData->ZFWCG : livePaxPayloadData->TOCG, allocator); livePayload.AddMember("TOCG", livePaxPayloadData->TOCG, allocator);
}
// Fuel // Fuel
livePayload.AddMember("fuel", FROM_POUNDS(UserData->isImperial, liveFuelData->total), allocator); livePayload.AddMember("fuel", FROM_POUNDS(UserData->isImperial, liveFuelData->total), allocator);
#pragma endregion #pragma endregion
@ -610,6 +612,12 @@ void sendData () {
targetPayload.AddMember("upper4", targetFPayloadData->upper4Left + targetFPayloadData->upper4Right, allocator); targetPayload.AddMember("upper4", targetFPayloadData->upper4Left + targetFPayloadData->upper4Right, allocator);
targetPayload.AddMember("lowerForward", targetFPayloadData->lowerForward, allocator); targetPayload.AddMember("lowerForward", targetFPayloadData->lowerForward, allocator);
targetPayload.AddMember("lowerRear", targetFPayloadData->lowerRear, allocator); targetPayload.AddMember("lowerRear", targetFPayloadData->lowerRear, allocator);
targetPayload.AddMember("total", targetFPayloadData->total, allocator);
// CGs
//TODO: Enable for F
//calculateCGs(targetFPayloadData, liveFuelData, &targetFPayloadData->ZFWCG, &targetFPayloadData->TOCG, UserData->isImperial);
targetPayload.AddMember("ZFWCG", targetFPayloadData->ZFWCG, allocator);
targetPayload.AddMember("TOCG", targetFPayloadData->TOCG, allocator);
} }
// Pax only (converted to passengers) // Pax only (converted to passengers)
else { else {
@ -617,16 +625,18 @@ void sendData () {
targetPaxPayloadData->pilot + targetPaxPayloadData->firstOfficer + targetPaxPayloadData->engineer + targetPaxPayloadData->pilot + targetPaxPayloadData->firstOfficer + targetPaxPayloadData->engineer +
targetPaxPayloadData->cabinCrewFront + targetPaxPayloadData->cabinCrewRear, targetPaxPayloadData->cabinCrewFront + targetPaxPayloadData->cabinCrewRear,
allocator); allocator);
targetPayload.AddMember("businnes1", targetPaxPayloadData->paxCount.business1, allocator); targetPayload.AddMember("business1", targetPaxPayloadData->paxCount.business1, allocator);
targetPayload.AddMember("business2", targetPaxPayloadData->paxCount.business2, allocator); targetPayload.AddMember("business2", targetPaxPayloadData->paxCount.business2, allocator);
targetPayload.AddMember("economy1", targetPaxPayloadData->paxCount.economy1, allocator); targetPayload.AddMember("economy1", targetPaxPayloadData->paxCount.economy1, allocator);
targetPayload.AddMember("economy2", targetPaxPayloadData->paxCount.economy2, allocator); targetPayload.AddMember("economy2", targetPaxPayloadData->paxCount.economy2, allocator);
targetPayload.AddMember("forwardCargo", targetPaxPayloadData->forwardCargo, allocator); targetPayload.AddMember("forwardCargo", targetPaxPayloadData->forwardCargo, allocator);
targetPayload.AddMember("rearCargo", targetPaxPayloadData->rearCargo, allocator); targetPayload.AddMember("rearCargo", targetPaxPayloadData->rearCargo, allocator);
} targetPayload.AddMember("total", targetPaxPayloadData->total, allocator);
// CGs // CGs
targetPayload.AddMember("ZFWCG", UserData->isCargo ? targetFPayloadData->ZFWCG : targetPaxPayloadData->ZFWCG, allocator); calculateCGs(targetPaxPayloadData, liveFuelData, &targetPaxPayloadData->ZFWCG, &targetPaxPayloadData->TOCG, UserData->isImperial);
targetPayload.AddMember("TOCG", UserData->isCargo ? targetFPayloadData->TOCG : targetPaxPayloadData->TOCG, allocator); targetPayload.AddMember("ZFWCG", targetPaxPayloadData->ZFWCG, allocator);
targetPayload.AddMember("TOCG", targetPaxPayloadData->TOCG, allocator);
}
#pragma endregion #pragma endregion
@ -639,10 +649,47 @@ void sendData () {
GSX.AddMember("cargoBoarded", GSXData->cargoBoarded, allocator); GSX.AddMember("cargoBoarded", GSXData->cargoBoarded, allocator);
GSX.AddMember("cargoDeboarded", GSXData->cargoDeboarded, allocator); GSX.AddMember("cargoDeboarded", GSXData->cargoDeboarded, allocator);
// User Data
userData.AddMember<bool>("isCargo", UserData->isCargo, allocator);
userData.AddMember<bool>("isER", UserData->isER, allocator);
userData.AddMember<bool>("isImperial", UserData->isImperial, allocator);
// Limits
limits.AddMember("minCG", MIN_CG, allocator);
limits.AddMember("maxCG", MAX_CG, allocator);
limits.AddMember("maxFuel", UserData->isER ? MAX_FUEL_ER(UserData->isImperial) : MAX_FUEL(UserData->isImperial), allocator);
limits.AddMember("maxTOW", UserData->isER ? MAX_TOW_ER(UserData->isImperial) : MAX_TOW(UserData->isImperial), allocator);
// Cargo Only
// TODO: Actual F limits
if (UserData->isCargo) {
limits.AddMember("upper1", -1, allocator);
limits.AddMember("upper2", -1, allocator);
limits.AddMember("upper3", -1, allocator);
limits.AddMember("upper4", -1, allocator);
limits.AddMember("lowerForward", MAX_FRONT_CARGO(UserData->isImperial), allocator);
limits.AddMember("lowerRear", MAX_REAR_CARGO(UserData->isImperial), allocator);
// TODO: Actual F limit
//limits.AddMember("MaxZFW", MAX_F_ZFW, allocator);
limits.AddMember("minZFW", targetFPayloadData->empty + targetFPayloadData->leftAux + targetFPayloadData->rightAux, allocator);
}
// Pax only
else {
limits.AddMember("business1", MAX_BUSINESS_1, allocator);
limits.AddMember("business2", MAX_BUSINESS_2, allocator);
limits.AddMember("economy1", MAX_ECONOMY_1, allocator);
limits.AddMember("economy2", MAX_ECONOMY_2, allocator);
limits.AddMember("forwardCargo", MAX_FRONT_CARGO(UserData->isImperial), allocator);
limits.AddMember("rearCargo", MAX_REAR_CARGO(UserData->isImperial), allocator);
limits.AddMember("maxZFW", MAX_PAX_ZFW(UserData->isImperial), allocator);
limits.AddMember("minZFW", targetPaxPayloadData->empty + targetPaxPayloadData->leftAux + targetPaxPayloadData->rightAux, allocator);
}
// Construct document // Construct document
document.AddMember("livePayload", livePayload.Move(), allocator); document.AddMember("livePayload", livePayload.Move(), allocator);
document.AddMember("targetPayload", targetPayload.Move(), allocator); document.AddMember("targetPayload", targetPayload.Move(), allocator);
document.AddMember("GSX", GSX.Move(), allocator); document.AddMember("GSX", GSX.Move(), allocator);
document.AddMember("userData", userData.Move(), allocator);
document.AddMember("limits", limits.Move(), allocator);
// Write to CommBus // Write to CommBus
document.Accept(writer); document.Accept(writer);
@ -650,6 +697,8 @@ void sendData () {
fsCommBusCall(COMM_BUS_LIVE_DATA_EVENT, strbuf.GetString(), strbuf.GetSize(), FsCommBusBroadcast_JS); fsCommBusCall(COMM_BUS_LIVE_DATA_EVENT, strbuf.GetString(), strbuf.GetSize(), FsCommBusBroadcast_JS);
} }
#pragma endregion
// Logfile // Logfile
void log(FILE* file, const char* format, void* optionalElement) void log(FILE* file, const char* format, void* optionalElement)
{ {
@ -670,16 +719,18 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV* pData, DWORD cbData, void* pContex
case DATA_REQUEST_EMPTY_WEIGHT: { case DATA_REQUEST_EMPTY_WEIGHT: {
liveFPayloadData->empty = livePaxPayloadData->empty = *((double*)&pObjData->dwData); liveFPayloadData->empty = livePaxPayloadData->empty = *((double*)&pObjData->dwData);
sendTimer++;
break; break;
} }
case DATA_REQUEST_PAYLOAD_F: { case DATA_REQUEST_PAYLOAD_F: {
fPayloadData_t* data = (fPayloadData_t*)&pObjData->dwData; fPayloadData_t* data = (fPayloadData_t*)&pObjData->dwData;
data->empty = liveFPayloadData->empty; data->empty = liveFPayloadData->empty;
memcpy(liveFPayloadData, data, sizeof(fPayloadData_t)); memcpy(liveFPayloadData, data, sizeof(fPayloadData_t));
liveFPayloadData->total = liveFPayloadData->empty + liveFPayloadData->pilot + liveFPayloadData->firstOfficer +
sendTimer++; liveFPayloadData->engineer + liveFPayloadData->upper1Left + liveFPayloadData->upper1Right +
liveFPayloadData->upper2Left + liveFPayloadData->upper2Right + liveFPayloadData->upper3Left +
liveFPayloadData->upper3Right + liveFPayloadData->upper4Left + liveFPayloadData->upper4Right +
liveFPayloadData->lowerForward + liveFPayloadData->lowerRear + liveFPayloadData->leftAux +
liveFPayloadData->rightAux;
break; break;
} }
@ -687,8 +738,14 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV* pData, DWORD cbData, void* pContex
paxPayloadData_t* data = (paxPayloadData_t*)&pObjData->dwData; paxPayloadData_t* data = (paxPayloadData_t*)&pObjData->dwData;
data->empty = livePaxPayloadData->empty; data->empty = livePaxPayloadData->empty;
memcpy(livePaxPayloadData, data, sizeof(paxPayloadData_t)); memcpy(livePaxPayloadData, data, sizeof(paxPayloadData_t));
livePaxPayloadData->total = livePaxPayloadData->empty + livePaxPayloadData->pilot + livePaxPayloadData->firstOfficer +
sendTimer++; livePaxPayloadData->engineer + livePaxPayloadData->cabinCrewFront + livePaxPayloadData->business1Left +
livePaxPayloadData->business1Center + livePaxPayloadData->business1Right + livePaxPayloadData->business2Left +
livePaxPayloadData->business2Center + livePaxPayloadData->business2Right + livePaxPayloadData->economy1Left +
livePaxPayloadData->economy1Center + livePaxPayloadData->economy1Right + livePaxPayloadData->economy2Left +
livePaxPayloadData->economy2Center + livePaxPayloadData->economy2Right + livePaxPayloadData->cabinCrewRear +
livePaxPayloadData->forwardCargo + livePaxPayloadData->rearCargo + livePaxPayloadData->leftAux +
livePaxPayloadData->rightAux;
break; break;
} }
@ -708,16 +765,12 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV* pData, DWORD cbData, void* pContex
liveFuelData->lowerAux + liveFuelData->main1Tip + liveFuelData->main3Tip + liveFuelData->tail + liveFuelData->lowerAux + liveFuelData->main1Tip + liveFuelData->main3Tip + liveFuelData->tail +
liveFuelData->forwardAux1 + liveFuelData->forwardAux2; liveFuelData->forwardAux1 + liveFuelData->forwardAux2;
sendTimer++;
break; break;
} }
case DATA_REQUEST_GSX: { case DATA_REQUEST_GSX: {
GSXData_t* data = (GSXData_t*)&pObjData->dwData; GSXData_t* data = (GSXData_t*)&pObjData->dwData;
memcpy(GSXData, data, sizeof(GSXData_t)); memcpy(GSXData, data, sizeof(GSXData_t));
sendTimer++;
break; break;
} }
case DATA_REQUEST_USER_DATA: { case DATA_REQUEST_USER_DATA: {
@ -725,7 +778,19 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV* pData, DWORD cbData, void* pContex
data->isImperial = ((long)data->isImperial) & 1; data->isImperial = ((long)data->isImperial) & 1;
memcpy(UserData, data, sizeof(UserData_t)); memcpy(UserData, data, sizeof(UserData_t));
sendTimer++; // Update static weights
// Shared part 1
targetFPayloadData->empty = targetPaxPayloadData->empty = FROM_POUNDS(UserData->isImperial, liveFPayloadData->empty);
targetFPayloadData->pilot = targetPaxPayloadData->pilot = targetFPayloadData->firstOfficer = targetPaxPayloadData->firstOfficer =
targetFPayloadData->engineer = targetPaxPayloadData->engineer = PILOT_WEIGHT(UserData->isImperial);
// Shared part 2
targetFPayloadData->leftAux = targetPaxPayloadData->leftAux = targetFPayloadData->rightAux = targetPaxPayloadData->rightAux =
UserData->isER ? AUX_WEIGHT(UserData->isImperial) : 0;
// Pax only fixed weights
if (!UserData->isCargo) {
targetPaxPayloadData->cabinCrewFront = FRONT_CREW_WEIGHT(UserData->isImperial);
targetPaxPayloadData->cabinCrewRear = REAR_CREW_WEIGHT(UserData->isImperial);
}
break; break;
} }
@ -734,12 +799,6 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV* pData, DWORD cbData, void* pContex
} }
} }
// Send once every request "frame"
if (sendTimer == 6) {
sendData();
sendTimer = 0;
}
break; break;
} }
case SIMCONNECT_RECV_ID_EXCEPTION: case SIMCONNECT_RECV_ID_EXCEPTION:

View File

@ -13,6 +13,11 @@ void distribute(paxPayloadData_t* const targetPayload, const FuelData_t* const f
//SimBrief Entry, SB pax count and total cargo //SimBrief Entry, SB pax count and total cargo
void distribute(paxPayloadData_t* const targetPayload, const FuelData_t* const fuel, unsigned short numPax, unsigned int cargo, const double CGTarget, const bool isImperial) { void distribute(paxPayloadData_t* const targetPayload, const FuelData_t* const fuel, unsigned short numPax, unsigned int cargo, const double CGTarget, const bool isImperial) {
// Clear
targetPayload->paxCount.business1 = targetPayload->paxCount.business2 = targetPayload->paxCount.economy1 = targetPayload->paxCount.economy2 =
targetPayload->paxCount.total = 0;
targetPayload->forwardCargo = targetPayload->rearCargo = 0;
unsigned short _numPax = 0; unsigned short _numPax = 0;
unsigned int count = MAX_PAX; unsigned int count = MAX_PAX;
// Initial distribution pax + bags // Initial distribution pax + bags
@ -219,12 +224,22 @@ void generatePayload(paxPayloadData_t* const targetPayload, const bool isImperia
targetPayload->business2Left = targetPayload->business2Center = targetPayload->business2Right = (targetPayload->paxCount.business2 / 3.0) * PAX_WEIGHT(isImperial); targetPayload->business2Left = targetPayload->business2Center = targetPayload->business2Right = (targetPayload->paxCount.business2 / 3.0) * PAX_WEIGHT(isImperial);
targetPayload->economy1Left = targetPayload->economy1Center = targetPayload->economy1Right = (targetPayload->paxCount.economy1 / 3.0) * PAX_WEIGHT(isImperial); targetPayload->economy1Left = targetPayload->economy1Center = targetPayload->economy1Right = (targetPayload->paxCount.economy1 / 3.0) * PAX_WEIGHT(isImperial);
targetPayload->economy2Left = targetPayload->economy2Center = targetPayload->economy2Right = (targetPayload->paxCount.economy2 / 3.0) * PAX_WEIGHT(isImperial); targetPayload->economy2Left = targetPayload->economy2Center = targetPayload->economy2Right = (targetPayload->paxCount.economy2 / 3.0) * PAX_WEIGHT(isImperial);
targetPayload->total = targetPayload->empty + targetPayload->pilot + targetPayload->firstOfficer + targetPayload->engineer + targetPayload->cabinCrewFront +
targetPayload->business1Left + targetPayload->business1Center + targetPayload->business1Right + targetPayload->business2Left +
targetPayload->business2Center + targetPayload->business2Right + targetPayload->economy1Left + targetPayload->economy1Center +
targetPayload->economy1Right + targetPayload->economy2Left + targetPayload->economy2Center + targetPayload->economy2Right +
targetPayload->cabinCrewRear + targetPayload->forwardCargo + targetPayload->rearCargo + targetPayload->leftAux + targetPayload->rightAux;
} }
// Normalise to Pounds // Normalise to Pounds
// MANDATORY BEFORE SETTING WEIGHTS // MANDATORY BEFORE SETTING WEIGHTS
// ENSURE ONLY EVER CALLED ONCE PER SET CYCLE // ENSURE ONLY EVER CALLED ONCE PER SET CYCLE
void normalisePayload(paxPayloadData_t* const targetPayload, const bool isImperial) { void normalisePayload(paxPayloadData_t* const targetPayload, const bool isImperial) {
targetPayload->empty = TO_POUNDS(isImperial, targetPayload->empty);
targetPayload->pilot = TO_POUNDS(isImperial, targetPayload->pilot);
targetPayload->firstOfficer = TO_POUNDS(isImperial, targetPayload->firstOfficer);
targetPayload->engineer = TO_POUNDS(isImperial, targetPayload->engineer);
targetPayload->cabinCrewFront = TO_POUNDS(isImperial, targetPayload->cabinCrewFront);
targetPayload->business1Left = TO_POUNDS(isImperial, targetPayload->business1Left); targetPayload->business1Left = TO_POUNDS(isImperial, targetPayload->business1Left);
targetPayload->business1Center = TO_POUNDS(isImperial, targetPayload->business1Center); targetPayload->business1Center = TO_POUNDS(isImperial, targetPayload->business1Center);
targetPayload->business1Right = TO_POUNDS(isImperial, targetPayload->business1Right); targetPayload->business1Right = TO_POUNDS(isImperial, targetPayload->business1Right);
@ -237,27 +252,32 @@ void normalisePayload(paxPayloadData_t* const targetPayload, const bool isImperi
targetPayload->economy2Left = TO_POUNDS(isImperial, targetPayload->economy2Left); targetPayload->economy2Left = TO_POUNDS(isImperial, targetPayload->economy2Left);
targetPayload->economy2Center = TO_POUNDS(isImperial, targetPayload->economy2Center); targetPayload->economy2Center = TO_POUNDS(isImperial, targetPayload->economy2Center);
targetPayload->economy2Right = TO_POUNDS(isImperial, targetPayload->economy2Right); targetPayload->economy2Right = TO_POUNDS(isImperial, targetPayload->economy2Right);
targetPayload->cabinCrewRear = TO_POUNDS(isImperial, targetPayload->cabinCrewRear);
targetPayload->forwardCargo = TO_POUNDS(isImperial, targetPayload->forwardCargo);
targetPayload->rearCargo = TO_POUNDS(isImperial, targetPayload->rearCargo);
} }
void calculateCGs(const paxPayloadData_t* const targetPayload, const FuelData_t* fuel, double* const ZFWCG, double* const TOCG, const bool isImperial) { void calculateCGs(const paxPayloadData_t* const targetPayload, const FuelData_t* const fuel, double* const ZFWCG, double* const TOCG, const bool isImperial) {
double totalMoment = targetPayload->empty * ARM_EMPTY + targetPayload->pilot * ARM_PILOT + targetPayload->firstOfficer * ARM_FIRST_OFFICER + paxPayloadData_t localPayload = {};
targetPayload->engineer * ARM_ENGINEER + targetPayload->cabinCrewFront * ARM_PAX_CABIN_CREW_FRONT + memcpy(&localPayload, targetPayload, sizeof(localPayload));
targetPayload->business1Left * ARM_PAX_BUSINESS1_LEFT + targetPayload->business1Center * ARM_PAX_BUSINESS1_CENTER + normalisePayload(&localPayload, isImperial);
targetPayload->business1Right * ARM_PAX_BUSINESS1_RIGHT + targetPayload->business2Left * ARM_PAX_BUSINESS2_LEFT +
targetPayload->business2Center * ARM_PAX_BUSINESS2_CENTER + targetPayload->business2Right * ARM_PAX_BUSINESS2_RIGHT +
targetPayload->economy1Left * ARM_PAX_ECONOMY1_LEFT + targetPayload->economy1Center * ARM_PAX_ECONOMY1_CENTER +
targetPayload->economy1Right * ARM_PAX_ECONOMY1_RIGHT + targetPayload->economy2Left * ARM_PAX_ECONOMY2_LEFT +
targetPayload->economy2Center * ARM_PAX_ECONOMY2_CENTER + targetPayload->economy2Right * ARM_PAX_ECONOMY2_RIGHT +
targetPayload->cabinCrewRear * ARM_PAX_CABIN_CREW_REAR + targetPayload->forwardCargo * ARM_FORWARD_CARGO +
targetPayload->rearCargo * ARM_REAR_CARGO + targetPayload->leftAux * ARM_LEFT_AUX + targetPayload->rightAux * ARM_RIGHT_AUX;
double totalWeight = targetPayload->empty + targetPayload->pilot + targetPayload->firstOfficer + targetPayload->engineer + double totalMoment = localPayload.empty * ARM_EMPTY + localPayload.pilot * ARM_PILOT + localPayload.firstOfficer * ARM_FIRST_OFFICER +
targetPayload->cabinCrewFront + targetPayload->business1Left + targetPayload->business1Center + localPayload.engineer * ARM_ENGINEER + localPayload.cabinCrewFront * ARM_PAX_CABIN_CREW_FRONT +
targetPayload->business1Right + targetPayload->business2Left + targetPayload->business2Center + localPayload.business1Left * ARM_PAX_BUSINESS1_LEFT + localPayload.business1Center * ARM_PAX_BUSINESS1_CENTER +
targetPayload->business2Right + targetPayload->economy1Left + targetPayload->economy1Center + localPayload.business1Right * ARM_PAX_BUSINESS1_RIGHT + localPayload.business2Left * ARM_PAX_BUSINESS2_LEFT +
targetPayload->economy1Right + targetPayload->economy2Left + targetPayload->economy2Center + localPayload.business2Center * ARM_PAX_BUSINESS2_CENTER + localPayload.business2Right * ARM_PAX_BUSINESS2_RIGHT +
targetPayload->economy2Right + targetPayload->cabinCrewRear + targetPayload->forwardCargo + localPayload.economy1Left * ARM_PAX_ECONOMY1_LEFT + localPayload.economy1Center * ARM_PAX_ECONOMY1_CENTER +
targetPayload->rearCargo + targetPayload->leftAux + targetPayload->rightAux; localPayload.economy1Right * ARM_PAX_ECONOMY1_RIGHT + localPayload.economy2Left * ARM_PAX_ECONOMY2_LEFT +
localPayload.economy2Center * ARM_PAX_ECONOMY2_CENTER + localPayload.economy2Right * ARM_PAX_ECONOMY2_RIGHT +
localPayload.cabinCrewRear * ARM_PAX_CABIN_CREW_REAR + localPayload.forwardCargo * ARM_FORWARD_CARGO +
localPayload.rearCargo * ARM_REAR_CARGO + localPayload.leftAux * ARM_LEFT_AUX + localPayload.rightAux * ARM_RIGHT_AUX;
double totalWeight = localPayload.empty + localPayload.pilot + localPayload.firstOfficer + localPayload.engineer + localPayload.cabinCrewFront +
localPayload.business1Left + localPayload.business1Center + localPayload.business1Right + localPayload.business2Left +
localPayload.business2Center + localPayload.business2Right + localPayload.economy1Left + localPayload.economy1Center +
localPayload.economy1Right + localPayload.economy2Left + localPayload.economy2Center + localPayload.economy2Right +
localPayload.cabinCrewRear + localPayload.forwardCargo + localPayload.rearCargo + localPayload.leftAux + localPayload.rightAux;
*ZFWCG = TO_PERCENT_MAC(totalMoment / totalWeight); *ZFWCG = TO_PERCENT_MAC(totalMoment / totalWeight);

View File

@ -27,4 +27,4 @@ void generatePayload(paxPayloadData_t* const targetPayload, const bool isImperia
// Normalise to Pounds // Normalise to Pounds
// 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(const paxPayloadData_t* const targetPayload, const FuelData_t* fuel, double* const ZFWCG, double* const TOCG, const bool isImperial); void calculateCGs(const paxPayloadData_t* const targetPayload, const FuelData_t* const fuel, double* const ZFWCG, double* const TOCG, const bool isImperial);

View File

@ -31,6 +31,17 @@
// Total // Total
#define MAX_PAX 298 #define MAX_PAX 298
// Max ZFW
#define MAX_PAX_ZFW(IS_IMPERIAL) ((IS_IMPERIAL) ? (400000) : (181437))
// Max TOW
#define MAX_TOW(IS_IMPERIAL) ((IS_IMPERIAL) ? (625500) : (283722))
#define MAX_TOW_ER(IS_IMPERIAL) ((IS_IMPERIAL) ? (630500) : (285990))
// Max Fuel
#define MAX_FUEL(IS_IMPERIAL) ((IS_IMPERIAL) ? (256207) : (116213))
#define MAX_FUEL_ER(IS_IMPERIAL) ((IS_IMPERIAL) ? (282619) : (128193))
// Arms // Arms
// Shared part 1 // Shared part 1
#define ARM_EMPTY -159.6 #define ARM_EMPTY -159.6
@ -132,10 +143,9 @@ typedef struct {
// Additional properties // Additional properties
double empty; double empty;
double total;
double ZFWCG; double ZFWCG;
double TOCG; double TOCG;
struct paxCount { struct paxCount {
unsigned char business1; unsigned char business1;
unsigned char business2; unsigned char business2;
@ -165,7 +175,7 @@ typedef struct {
// Additional properties // Additional properties
double empty; double empty;
double total;
double ZFWCG; double ZFWCG;
double TOCG; double TOCG;
} fPayloadData_t; } fPayloadData_t;

View File

@ -5,6 +5,17 @@
- https://www.satco-inc.com/product-pallet/?part_number=31086-595 - https://www.satco-inc.com/product-pallet/?part_number=31086-595
- https://www.satco-inc.com/product-container/?part_number=34124-901 - https://www.satco-inc.com/product-container/?part_number=34124-901
Coherent.call("COMM_BUS_WASM_CALLBACK", "khofmann_tfdi_md-11_load_manager_update_target", '{"value" : 42}');
Coherent.call("COMM_BUS_WASM_CALLBACK", "khofmann_tfdi_md-11_load_manager_update_target", '{"mode" : 1, "ZFWTarget": 162000, "CGTarget": 20.5}'); Coherent.call("COMM_BUS_WASM_CALLBACK", "khofmann_tfdi_md-11_load_manager_update_target", '{"mode" : 1, "ZFWTarget": 162000, "CGTarget": 20.5}');
TODO:
- JS
- Connect to WASM as Data Source (sans SB username)
- Setting target
- SB
- Stations
- GSX State?
- Duplicate Input pages for F
- WASM
- Setting of payload
- GSX synced setting