14 Commits

Author SHA1 Message Date
Kilian 80cb726501 Customisable pax/bag weight
Better log
2025-06-21 21:41:17 +02:00
Kilian 2047c84d8d Fix SB persist 2025-06-19 16:05:54 +02:00
Kilian 390edd29b8 SB persist
Options page for GSX Sync
2025-06-18 23:57:46 +02:00
Kilian 4b60f8eec2 Rmv wasm build artifact 2025-06-15 02:01:03 +02:00
Kilian 9da2abdad7 Freighter 2025-06-15 01:57:28 +02:00
Kilian cbd7d4e0ae Check prog pax load/unload 2025-06-14 16:47:27 +02:00
Kilian c394cd4d7b GSX Synced boarding/deboarding 2025-06-14 03:34:22 +02:00
Kilian f7304c5c7b Setting of payload 2025-06-12 14:30:00 +02:00
Kilian 8975ea17af SB fetch and entry 2025-06-11 23:49:35 +02:00
Kilian 896a459bba Pax Station Entry 2025-06-11 17:30:36 +02:00
Kilian 890622453e Cleanup old configs 2025-06-11 00:44:15 +02:00
Kilian ee46d0bff1 ZFW Target Set 2025-06-11 00:27:27 +02:00
Kilian 9224300c85 WASM PAX data export 2025-06-10 01:12:58 +02:00
Kilian a34c532484 initial wasm commit 2025-06-09 07:04:32 +02:00
58 changed files with 5076 additions and 2858 deletions
+6 -1
View File
@@ -4,5 +4,10 @@ PackageSources/js-bundle/__generated__
PackageSources/js-bundle/.rollup.cache
PackageSources/js-bundle/node_modules
PackageSources/html_ui
PackageSources/wasm-module/.vs
PackageSources/wasm-module/MSFS
PackageSources/wasm-module/x64
*.blend1
*.blend1
PackageSources/SimObjects/Airplanes/**/panel/*.wasm
@@ -35,6 +35,14 @@
<AssetDir>PackageSources\load-manager-panel\</AssetDir>
<OutputDir>InGamePanels\</OutputDir>
</AssetGroup>
<AssetGroup Name="md-11-panel-config">
<Type>Copy</Type>
<Flags>
<FSXCompatibility>false</FSXCompatibility>
</Flags>
<AssetDir>PackageSources\SimObjects\Airplanes\</AssetDir>
<OutputDir>SimObjects\Airplanes\</OutputDir>
</AssetGroup>
</AssetGroups>
</AssetPackage>
@@ -0,0 +1,59 @@
// Panel Configuration File
// TFDi Design MD-11
[VCockpit01]
size_mm=2048,2048
visible=1
pixel_size=2048,2048
texture=$vc1
background_color=0,0,0
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=Core, 0,0,509,510
htmlgauge01=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU1,0,0,508,508
htmlgauge02=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU2,514,0,510,510
htmlgauge03=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU3,1027,0,510,510
htmlgauge04=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU4,1539,0,510,508
htmlgauge05=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU5,0,513,511,509
htmlgauge06=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU6,513,512,510,511
htmlgauge07=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=LMCDU, 0,1046,333,287
htmlgauge08=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=RMCDU,0,1386,334,288
htmlgauge09=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=CMCDU,0,1728,333,285
htmlgauge10=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=ISFD,1032,519,544,505
//KH new gauge
htmlgauge11=WasmInstrument/WasmInstrument.html?wasm_module=load-manager.wasm&wasm_gauge=Load_Manager,0,0,509,510
[VIEWS]
VIEW_FORWARD_DIR=2.000, 0.000, 0.000
[Color]
Day=255,255,255
Night=255,255,255
Luminous=201,64,64
[Vcockpit02]
Background_color=0,0,0
size_mm=1024,1024
visible=1
pixel_size=1024,1024
texture=$vc2
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=AFS,3,0,1015,185
htmlgauge01=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=FUEL,4,200,1016,196
//above should actually be 3,192,1018,199 - moved due to visual offset
[Vcockpit03]
Background_color=0,0,0
size_mm=1024,1024
visible=1
pixel_size=1024,1024
texture=$RADIOS_XPNDR
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=RADIOS_XPNDR,0,5,1019,295
[VCockpit04]
Background_color=0,0,0
size_mm=1536,1024
visible=1
pixel_size=1536,1024
texture=$EFB
htmlgauge00=aircraft_efb/TFDi_MD11_efb/efb.html,0,0,1536,1024
@@ -0,0 +1,59 @@
// Panel Configuration File
// TFDi Design MD-11
[VCockpit01]
size_mm=2048,2048
visible=1
pixel_size=2048,2048
texture=$vc1
background_color=0,0,0
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=Core, 0,0,509,510,PW
htmlgauge01=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU1,0,0,508,508
htmlgauge02=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU2,514,0,510,510
htmlgauge03=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU3,1027,0,510,510
htmlgauge04=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU4,1539,0,510,508
htmlgauge05=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU5,0,513,511,509
htmlgauge06=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU6,513,512,510,511
htmlgauge07=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=LMCDU, 0,1046,333,287
htmlgauge08=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=RMCDU,0,1386,334,288
htmlgauge09=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=CMCDU,0,1728,333,285
htmlgauge10=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=ISFD,1032,519,544,505
//KH new gauge
htmlgauge11=WasmInstrument/WasmInstrument.html?wasm_module=load-manager.wasm&wasm_gauge=Load_Manager,0,0,509,510
[VIEWS]
VIEW_FORWARD_DIR=2.000, 0.000, 0.000
[Color]
Day=255,255,255
Night=255,255,255
Luminous=201,64,64
[Vcockpit02]
Background_color=0,0,0
size_mm=1024,1024
visible=1
pixel_size=1024,1024
texture=$vc2
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=AFS,3,0,1015,185
htmlgauge01=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=FUEL,4,200,1016,196
//above should actually be 3,192,1018,199 - moved due to visual offset
[Vcockpit03]
Background_color=0,0,0
size_mm=1024,1024
visible=1
pixel_size=1024,1024
texture=$RADIOS_XPNDR
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=RADIOS_XPNDR,0,5,1019,295
[VCockpit04]
Background_color=0,0,0
size_mm=1536,1024
visible=1
pixel_size=1536,1024
texture=$EFB
htmlgauge00=aircraft_efb/TFDi_MD11_efb/efb.html,0,0,1536,1024
@@ -0,0 +1,59 @@
// Panel Configuration File
// TFDi Design MD-11
[VCockpit01]
size_mm=2048,2048
visible=1
pixel_size=2048,2048
texture=$vc1
background_color=0,0,0
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=Core, 0,0,509,510
htmlgauge01=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU1,0,0,508,508
htmlgauge02=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU2,514,0,510,510
htmlgauge03=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU3,1027,0,510,510
htmlgauge04=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU4,1539,0,510,508
htmlgauge05=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU5,0,513,511,509
htmlgauge06=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU6,513,512,510,511
htmlgauge07=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=LMCDU, 0,1046,333,287
htmlgauge08=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=RMCDU,0,1386,334,288
htmlgauge09=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=CMCDU,0,1728,333,285
htmlgauge10=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=ISFD,1032,519,544,505
//KH new gauge
htmlgauge11=WasmInstrument/WasmInstrument.html?wasm_module=load-manager.wasm&wasm_gauge=Load_Manager,0,0,509,510
[VIEWS]
VIEW_FORWARD_DIR=2.000, 0.000, 0.000
[Color]
Day=255,255,255
Night=255,255,255
Luminous=201,64,64
[Vcockpit02]
Background_color=0,0,0
size_mm=1024,1024
visible=1
pixel_size=1024,1024
texture=$vc2
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=AFS,3,0,1015,185
htmlgauge01=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=FUEL,4,200,1016,196
//above should actually be 3,192,1018,199 - moved due to visual offset
[Vcockpit03]
Background_color=0,0,0
size_mm=1024,1024
visible=1
pixel_size=1024,1024
texture=$RADIOS_XPNDR
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=RADIOS_XPNDR,0,5,1019,295
[VCockpit04]
Background_color=0,0,0
size_mm=1536,1024
visible=1
pixel_size=1536,1024
texture=$EFB
htmlgauge00=aircraft_efb/TFDi_MD11_efb/efb.html,0,0,1536,1024
@@ -0,0 +1,59 @@
// Panel Configuration File
// TFDi Design MD-11
[VCockpit01]
size_mm=2048,2048
visible=1
pixel_size=2048,2048
texture=$vc1
background_color=0,0,0
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=Core, 0,0,509,510,PW
htmlgauge01=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU1,0,0,508,508
htmlgauge02=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU2,514,0,510,510
htmlgauge03=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU3,1027,0,510,510
htmlgauge04=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU4,1539,0,510,508
htmlgauge05=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU5,0,513,511,509
htmlgauge06=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=DU6,513,512,510,511
htmlgauge07=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=LMCDU, 0,1046,333,287
htmlgauge08=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=RMCDU,0,1386,334,288
htmlgauge09=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=CMCDU,0,1728,333,285
htmlgauge10=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=ISFD,1032,519,544,505
//KH new gauge
htmlgauge11=WasmInstrument/WasmInstrument.html?wasm_module=load-manager.wasm&wasm_gauge=Load_Manager,0,0,509,510
[VIEWS]
VIEW_FORWARD_DIR=2.000, 0.000, 0.000
[Color]
Day=255,255,255
Night=255,255,255
Luminous=201,64,64
[Vcockpit02]
Background_color=0,0,0
size_mm=1024,1024
visible=1
pixel_size=1024,1024
texture=$vc2
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=AFS,3,0,1015,185
htmlgauge01=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=FUEL,4,200,1016,196
//above should actually be 3,192,1018,199 - moved due to visual offset
[Vcockpit03]
Background_color=0,0,0
size_mm=1024,1024
visible=1
pixel_size=1024,1024
texture=$RADIOS_XPNDR
htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_gauge=RADIOS_XPNDR,0,5,1019,295
[VCockpit04]
Background_color=0,0,0
size_mm=1536,1024
visible=1
pixel_size=1536,1024
texture=$EFB
htmlgauge00=aircraft_efb/TFDi_MD11_efb/efb.html,0,0,1536,1024
+5
View File
@@ -1 +1,6 @@
/// <reference types="@microsoft/msfs-types/js/common.d.ts" />
/// <reference types="@microsoft/msfs-types/pages/vcockpit/instruments/shared/baseinstrument.d.ts" />
/// <reference types="@microsoft/msfs-types/js/datastorage.d.ts" />
/// <reference types="@microsoft/msfs-types/js/buttons.d.ts" />
/// <reference types="@microsoft/msfs-types/js/services/toolbarpanels.d.ts" />
/// <reference types="@microsoft/msfs-types/js/simvar.d.ts" />
+7 -10
View File
@@ -1,6 +1,6 @@
{
"name": "tfdidesign-md11-load-manager",
"version": "0.1.14",
"version": "0.1.22",
"description": "",
"main": "index.js",
"type": "module",
@@ -13,7 +13,9 @@
"dev": "npx rollup -c -w",
"clean": "rimraf ../html_ui/InGamePanels/tfdidesign-md11-load-manager-panel/ && rimraf .rollup.cache",
"build": "npm version patch && npx rollup -c",
"release": "pnpm types && pnpm lint && pnpm run licenses && pnpm clean && npm version patch && cross-env NODE_ENV=production npx rollup -c"
"build-efb": "npm version patch && cross-env SPLIT=true npx rollup -c",
"release": "pnpm types && pnpm lint && pnpm run licenses && pnpm clean && npm version patch && cross-env NODE_ENV=production npx rollup -c",
"release-efb": "pnpm types && pnpm lint && pnpm run licenses && pnpm clean && npm version patch && cross-env NODE_ENV=production SPLIT=true npx rollup -c"
},
"engines": {
"node": ">=22"
@@ -58,14 +60,9 @@
"typescript": "5.8.3"
},
"dependencies": {
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.14.16",
"@mui/material": "^5.14.17",
"postcss-import": "^15.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"uuid": "^9.0.1"
"react": "^19.1.0",
"react-dom": "^19.1.0",
"uuid": "^11.1.0"
},
"packageManager": "pnpm@10.11.1+sha512.e519b9f7639869dc8d5c3c5dfef73b3f091094b0a006d7317353c72b124e80e1afd429732e28705ad6bfa1ee879c1fce46c128ccebd3192101f43dd67c667912"
}
+363 -928
View File
File diff suppressed because it is too large Load Diff
@@ -27,6 +27,15 @@ export default {
dir: panelDirBase,
format: 'es',
sourcemap: targetEnv !== 'production',
manualChunks: process.env.SPLIT
? (id) => {
if (id.includes('node_modules')) {
return 'vendor';
}
return null;
}
: undefined,
},
plugins: [
replace({
+31 -96
View File
@@ -1,126 +1,61 @@
import { FC, StrictMode, useCallback, useEffect, useRef, useState } from 'react';
import Freight from './components/freight/Freight';
import { FC, useCallback, useEffect, useState } from 'react';
import Freighter from './components/freighter/Freighter';
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_COMM_BUS_WASM_CALL,
COMM_BUS_LIVE_DATA_EVENT,
TFDI_SIMBRIEF_USERNAME_CALL,
TFDI_SIMBRIEF_USERNAME_EVENT,
} from './constants';
import { WASMDataF, 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]);
const [WASMData, setWASMData] = useState<WASMDataPax | WASMDataF>();
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: string) => {
setWASMData(JSON.parse(data));
}, []);
useEffect(() => {
console.log('Initializing CommBus');
commBus.on('receiveSimBriefUsername', usernameCallback);
commBus.on(TFDI_SIMBRIEF_USERNAME_EVENT, usernameCallback);
commBus.on(COMM_BUS_LIVE_DATA_EVENT, wasmCallback);
setTimeout(() => {
Coherent.call('COMM_BUS_WASM_CALLBACK', 'requestSimBriefUsername', 'null');
Coherent.call(COHERENT_COMM_BUS_WASM_CALL, TFDI_SIMBRIEF_USERNAME_CALL, 'null');
}, 1000);
return () => commBus.off('receiveSimBriefUsername', usernameCallback);
return () => {
commBus.off(TFDI_SIMBRIEF_USERNAME_EVENT, usernameCallback);
commBus.off(COMM_BUS_LIVE_DATA_EVENT, wasmCallback);
};
}, []);
return (
<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} />
) : (
<Pax
isER={isER}
unit={unit}
CGs={CGs}
fuelLive={fuel}
payloadLive={payloadLive as PayloadPax}
username={SBUsername}
GSXPaxNum={GSXPaxNum}
GSXCargoPercent={GSXCargoPercent}
GSXState={GSXState}
/>
)
<div className="flex w-full justify-center pt-2 bg-zinc-900">
<div className="flex w-3/4 flex-col items-center">
{isReady && WASMData ? (
WASMData.userData.isCargo ? (
<Freighter WASMData={WASMData as WASMDataF} username={SBUsername} />
) : (
<h1 className="text-sm font-medium">LOADING</h1>
)}
</div>
<Pax WASMData={WASMData as WASMDataPax} username={SBUsername} />
)
) : (
<h1 className="text-sm font-medium">LOADING</h1>
)}
</div>
</StrictMode>
</div>
);
};
@@ -1,44 +1,40 @@
| Department | Related to | Name | License period | Material not material | License type | Link | Remote version | Installed version | Defined version | Author |
| :--------- | :--------- | :------------------------------- | :------------- | :-------------------- | :----------- | :------------------------------------------------------------------------ | :------------- | :---------------- | :-------------- | :---------------------------------------------------------- |
| kessler | stuff | @emotion/react | perpetual | material | MIT | git+https://github.com/emotion-js/emotion.git#main | 11.14.0 | 11.14.0 | ^11.11.1 | Emotion Contributors |
| kessler | stuff | @emotion/styled | perpetual | material | MIT | git+https://github.com/emotion-js/emotion.git#main | 11.14.0 | 11.14.0 | ^11.11.0 | n/a |
| kessler | stuff | @mui/icons-material | perpetual | material | MIT | git+https://github.com/mui/material-ui.git | 5.17.1 | 5.17.1 | ^5.14.16 | MUI Team |
| kessler | stuff | @mui/material | perpetual | material | MIT | git+https://github.com/mui/material-ui.git | 5.17.1 | 5.17.1 | ^5.14.17 | MUI Team |
| kessler | stuff | postcss-import | perpetual | material | MIT | git+https://github.com/postcss/postcss-import.git | 15.1.0 | 15.1.0 | ^15.1.0 | Maxime Thirouin |
| kessler | stuff | react | perpetual | material | MIT | git+https://github.com/facebook/react.git | 18.3.1 | 18.3.1 | ^18.2.0 | n/a |
| kessler | stuff | react-dom | perpetual | material | MIT | git+https://github.com/facebook/react.git | 18.3.1 | 18.3.1 | ^18.2.0 | n/a |
| kessler | stuff | uuid | perpetual | material | MIT | git+https://github.com/uuidjs/uuid.git | 9.0.1 | 9.0.1 | ^9.0.1 | n/a |
| kessler | stuff | react | perpetual | material | MIT | git+https://github.com/facebook/react.git | 19.1.0 | 19.1.0 | ^19.1.0 | n/a |
| kessler | stuff | react-dom | perpetual | material | MIT | git+https://github.com/facebook/react.git | 19.1.0 | 19.1.0 | ^19.1.0 | n/a |
| kessler | stuff | uuid | perpetual | material | MIT | git+https://github.com/uuidjs/uuid.git | 11.1.0 | 11.1.0 | ^11.1.0 | n/a |
| kessler | stuff | @microsoft/msfs-types | perpetual | material | MIT | git+https://github.com/microsoft/msfs-avionics-mirror.git | 1.14.6 | 1.14.6 | ^1.14.6 | Asobo Studio / Working Title Simulations |
| kessler | stuff | @rollup/plugin-commonjs | perpetual | material | MIT | git+https://github.com/rollup/plugins.git | 25.0.8 | 25.0.8 | ^25.0.0 | Rich Harris <richard.a.harris@gmail.com> |
| kessler | stuff | @rollup/plugin-json | perpetual | material | MIT | git+https://github.com/rollup/plugins.git | 6.1.0 | 6.1.0 | ^6.0.0 | rollup |
| kessler | stuff | @rollup/plugin-node-resolve | perpetual | material | MIT | git+https://github.com/rollup/plugins.git | 15.3.1 | 15.3.1 | ^15.1.0 | Rich Harris <richard.a.harris@gmail.com> |
| kessler | stuff | @rollup/plugin-commonjs | perpetual | material | MIT | git+https://github.com/rollup/plugins.git | 28.0.5 | 28.0.3 | ^28.0.3 | Rich Harris <richard.a.harris@gmail.com> |
| kessler | stuff | @rollup/plugin-json | perpetual | material | MIT | git+https://github.com/rollup/plugins.git | 6.1.0 | 6.1.0 | ^6.1.0 | rollup |
| kessler | stuff | @rollup/plugin-node-resolve | perpetual | material | MIT | git+https://github.com/rollup/plugins.git | 16.0.1 | 16.0.1 | ^16.0.1 | Rich Harris <richard.a.harris@gmail.com> |
| kessler | stuff | @rollup/plugin-replace | perpetual | material | MIT | git+https://github.com/rollup/plugins.git | 6.0.2 | 6.0.2 | ^6.0.2 | Rich Harris <richard.a.harris@gmail.com> |
| kessler | stuff | @rollup/plugin-terser | perpetual | material | MIT | git+https://github.com/rollup/plugins.git | 0.4.4 | 0.4.4 | ^0.4.3 | Peter Placzek <peter.placzek1996@gmail.com> |
| kessler | stuff | @rollup/plugin-typescript | perpetual | material | MIT | git+https://github.com/rollup/plugins.git | 11.1.6 | 11.1.6 | ^11.1.1 | Oskar Segersvärd |
| kessler | stuff | @types/react | perpetual | material | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped.git | 18.3.23 | 18.3.23 | ^18.2.8 | n/a |
| kessler | stuff | @types/react-dom | perpetual | material | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped.git | 18.3.7 | 18.3.7 | ^18.2.4 | n/a |
| kessler | stuff | @types/uuid | perpetual | material | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped.git | 9.0.8 | 9.0.8 | ^9.0.7 | n/a |
| kessler | stuff | @typescript-eslint/eslint-plugin | perpetual | material | MIT | git+https://github.com/typescript-eslint/typescript-eslint.git | 6.21.0 | 6.21.0 | ^6.10.0 | n/a |
| kessler | stuff | @typescript-eslint/parser | perpetual | material | BSD-2-Clause | git+https://github.com/typescript-eslint/typescript-eslint.git | 6.21.0 | 6.21.0 | ^6.10.0 | n/a |
| kessler | stuff | autoprefixer | perpetual | material | MIT | git+https://github.com/postcss/autoprefixer.git | 10.4.21 | 10.4.21 | ^10.4.14 | Andrey Sitnik <andrey@sitnik.ru> |
| kessler | stuff | @rollup/plugin-terser | perpetual | material | MIT | git+https://github.com/rollup/plugins.git | 0.4.4 | 0.4.4 | ^0.4.4 | Peter Placzek <peter.placzek1996@gmail.com> |
| kessler | stuff | @rollup/plugin-typescript | perpetual | material | MIT | git+https://github.com/rollup/plugins.git | 12.1.2 | 12.1.2 | ^12.1.2 | Oskar Segersvärd |
| kessler | stuff | @types/react | perpetual | material | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped.git | 19.1.8 | 19.1.6 | ^19.1.6 | n/a |
| kessler | stuff | @types/react-dom | perpetual | material | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped.git | 19.1.6 | 19.1.6 | ^19.1.6 | n/a |
| kessler | stuff | @types/uuid | perpetual | material | MIT | https://github.com/DefinitelyTyped/DefinitelyTyped.git | 10.0.0 | 10.0.0 | ^10.0.0 | n/a |
| kessler | stuff | @typescript-eslint/eslint-plugin | perpetual | material | MIT | git+https://github.com/typescript-eslint/typescript-eslint.git | 6.21.0 | 6.21.0 | ^6.21.0 | n/a |
| kessler | stuff | @typescript-eslint/parser | perpetual | material | BSD-2-Clause | git+https://github.com/typescript-eslint/typescript-eslint.git | 6.21.0 | 6.21.0 | ^6.21.0 | n/a |
| kessler | stuff | autoprefixer | perpetual | material | MIT | git+https://github.com/postcss/autoprefixer.git | 10.4.21 | 10.4.21 | ^10.4.21 | Andrey Sitnik <andrey@sitnik.ru> |
| kessler | stuff | cross-env | perpetual | material | MIT | git+https://github.com/kentcdodds/cross-env.git | 7.0.3 | 7.0.3 | ^7.0.3 | Kent C. Dodds <me@kentcdodds.com> (https://kentcdodds.com) |
| kessler | stuff | eslint | perpetual | material | MIT | git+https://github.com/eslint/eslint.git | 8.57.1 | 8.57.1 | ^8.42.0 | Nicholas C. Zakas <nicholas+npm@nczconsulting.com> |
| kessler | stuff | eslint-plugin-import | perpetual | material | MIT | git+https://github.com/import-js/eslint-plugin-import.git | 2.31.0 | 2.31.0 | ^2.27.5 | Ben Mosher <me@benmosher.com> |
| kessler | stuff | eslint-plugin-react | perpetual | material | MIT | git+https://github.com/jsx-eslint/eslint-plugin-react.git | 7.37.5 | 7.37.5 | ^7.32.2 | Yannick Croissant <yannick.croissant+npm@gmail.com> |
| kessler | stuff | eslint-plugin-react-hooks | perpetual | material | MIT | git+https://github.com/facebook/react.git | 4.6.2 | 4.6.2 | ^4.6.0 | n/a |
| kessler | stuff | license-report | perpetual | material | MIT | git+https://github.com/kessler/license-report.git | 6.7.2 | 6.7.2 | ^6.5.0 | Yaniv Kessler |
| kessler | stuff | postcss | perpetual | material | MIT | git+https://github.com/postcss/postcss.git | 8.5.4 | 8.5.4 | ^8.4.24 | Andrey Sitnik <andrey@sitnik.ru> |
| kessler | stuff | prettier | perpetual | material | MIT | git+https://github.com/prettier/prettier.git | 3.5.3 | 3.5.3 | ^3.0.3 | James Long |
| kessler | stuff | prettier-plugin-organize-imports | perpetual | material | MIT | git+https://github.com/simonhaenisch/prettier-plugin-organize-imports.git | 3.2.4 | 3.2.4 | ^3.2.4 | Simon Haenisch (https://github.com/simonhaenisch) |
| kessler | stuff | rimraf | perpetual | material | ISC | git://github.com/isaacs/rimraf.git | 5.0.10 | 5.0.10 | ^5.0.1 | Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/) |
| kessler | stuff | rollup | perpetual | material | MIT | git+https://github.com/rollup/rollup.git | 4.41.1 | 4.41.1 | ^4.3.1 | Rich Harris |
| kessler | stuff | rollup-plugin-copy | perpetual | material | MIT | git+https://github.com/vladshcherbin/rollup-plugin-copy.git | 3.5.0 | 3.5.0 | ^3.4.0 | Vlad Shcherbin <vlad.shcherbin@gmail.com> |
| kessler | stuff | eslint | perpetual | material | MIT | git+https://github.com/eslint/eslint.git | 8.57.1 | 8.57.1 | ^8.57.1 | Nicholas C. Zakas <nicholas+npm@nczconsulting.com> |
| kessler | stuff | eslint-plugin-import | perpetual | material | MIT | git+https://github.com/import-js/eslint-plugin-import.git | 2.31.0 | 2.31.0 | ^2.31.0 | Ben Mosher <me@benmosher.com> |
| kessler | stuff | eslint-plugin-react | perpetual | material | MIT | git+https://github.com/jsx-eslint/eslint-plugin-react.git | 7.37.5 | 7.37.5 | ^7.37.5 | Yannick Croissant <yannick.croissant+npm@gmail.com> |
| kessler | stuff | eslint-plugin-react-hooks | perpetual | material | MIT | git+https://github.com/facebook/react.git | 4.6.2 | 4.6.2 | ^4.6.2 | n/a |
| kessler | stuff | license-report | perpetual | material | MIT | git+https://github.com/kessler/license-report.git | 6.8.0 | 6.7.2 | ^6.7.2 | Yaniv Kessler |
| kessler | stuff | postcss | perpetual | material | MIT | git+https://github.com/postcss/postcss.git | 8.5.5 | 8.5.4 | ^8.5.4 | Andrey Sitnik <andrey@sitnik.ru> |
| kessler | stuff | postcss-import | perpetual | material | MIT | git+https://github.com/postcss/postcss-import.git | 16.1.0 | 16.1.0 | ^16.1.0 | Maxime Thirouin |
| kessler | stuff | prettier | perpetual | material | MIT | git+https://github.com/prettier/prettier.git | 3.5.3 | 3.5.3 | ^3.5.3 | James Long |
| kessler | stuff | prettier-plugin-organize-imports | perpetual | material | MIT | git+https://github.com/simonhaenisch/prettier-plugin-organize-imports.git | 4.1.0 | 4.1.0 | ^4.1.0 | Simon Haenisch (https://github.com/simonhaenisch) |
| kessler | stuff | rimraf | perpetual | material | ISC | git://github.com/isaacs/rimraf.git | 6.0.1 | 6.0.1 | ^6.0.1 | Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/) |
| kessler | stuff | rollup | perpetual | material | MIT | git+https://github.com/rollup/rollup.git | 4.43.0 | 4.42.0 | ^4.42.0 | Rich Harris |
| kessler | stuff | rollup-plugin-copy | perpetual | material | MIT | git+https://github.com/vladshcherbin/rollup-plugin-copy.git | 3.5.0 | 3.5.0 | ^3.5.0 | Vlad Shcherbin <vlad.shcherbin@gmail.com> |
| kessler | stuff | rollup-plugin-postcss | perpetual | material | MIT | git+https://github.com/egoist/rollup-plugin-postcss.git | 4.0.2 | 4.0.2 | ^4.0.2 | EGOIST <0x142857@gmail.com> |
| kessler | stuff | rollup-plugin-react-svg | perpetual | material | MIT | git+https://github.com/boopathi/react-svg-loader.git | 3.0.3 | 3.0.3 | ^3.0.3 | boopathi |
| kessler | stuff | rollup-plugin-version-injector | perpetual | material | ISC | git+https://github.com/djhouseknecht/rollup-plugin-version-injector.git | 1.3.3 | 1.3.3 | ^1.3.3 | David Houseknecht <david.j.houseknecht@gmail.com> |
| kessler | stuff | sass | perpetual | material | MIT | git+https://github.com/sass/dart-sass.git | 1.89.1 | 1.89.1 | ^1.89.1 | Natalie Weizenbaum nweiz@google.com https://github.com/nex3 |
| kessler | stuff | sass | perpetual | material | MIT | git+https://github.com/sass/dart-sass.git | 1.89.2 | 1.89.1 | ^1.89.1 | Natalie Weizenbaum nweiz@google.com https://github.com/nex3 |
| kessler | stuff | svg-slim | perpetual | material | MIT | git+https://github.com/benboba/svg-slim.git | 2.0.5 | 2.0.5 | ^2.0.5 | Wang Feng <benboba@gmail.com> |
| kessler | stuff | tslib | perpetual | material | 0BSD | git+https://github.com/Microsoft/tslib.git | 2.8.1 | 2.8.1 | ^2.5.3 | Microsoft Corp. |
| kessler | stuff | typed-scss-modules | perpetual | material | MIT | git+https://github.com/skovy/typed-scss-modules.git | 7.1.4 | 7.1.4 | ^7.1.0 | Spencer Miskoviak <smiskoviak@gmail.com> |
| kessler | stuff | typescript | perpetual | material | Apache-2.0 | git+https://github.com/Microsoft/TypeScript.git | 5.2.2 | 5.2.2 | 5.2.2 | Microsoft Corp. |
| kessler | stuff | tslib | perpetual | material | 0BSD | git+https://github.com/Microsoft/tslib.git | 2.8.1 | 2.8.1 | ^2.8.1 | Microsoft Corp. |
| kessler | stuff | typed-scss-modules | perpetual | material | MIT | git+https://github.com/skovy/typed-scss-modules.git | 8.1.1 | 8.1.1 | ^8.1.1 | Spencer Miskoviak <smiskoviak@gmail.com> |
| kessler | stuff | typescript | perpetual | material | Apache-2.0 | git+https://github.com/microsoft/TypeScript.git | 5.8.3 | 5.8.3 | 5.8.3 | Microsoft Corp. |
@@ -1,14 +1,15 @@
import { FC } from 'react';
import { SharedConfig } from '../../configs/shared';
interface CGSelectProps {
minCG: number;
maxCG: number;
value: number;
disabled: boolean;
increase: () => void;
decrease: () => void;
}
const CGSelect: FC<CGSelectProps> = ({ value, disabled, increase, decrease }) => {
const CGSelect: FC<CGSelectProps> = ({ minCG, maxCG, value, disabled, increase, decrease }) => {
return (
<div className="relative">
<input
@@ -17,7 +18,7 @@ const CGSelect: FC<CGSelectProps> = ({ value, disabled, increase, decrease }) =>
value={value.toFixed(1)}
/>
<button
disabled={disabled || value <= SharedConfig.CGLimits.min}
disabled={disabled || value <= minCG}
className="absolute right-2 top-0 -mt-[.5px] border-t bg-zinc-700 text-white disabled:text-zinc-400"
onClick={increase}
>
@@ -45,7 +46,7 @@ const CGSelect: FC<CGSelectProps> = ({ value, disabled, increase, decrease }) =>
</svg>
</button>
<button
disabled={disabled || value >= SharedConfig.CGLimits.max}
disabled={disabled || value >= maxCG}
className="absolute bottom-0 right-2 -mt-[.5px] border-b bg-zinc-700 text-white disabled:text-zinc-400"
onClick={decrease}
>
@@ -0,0 +1,227 @@
import { FC, useEffect, useState } from 'react';
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
import { WASMDataF } from '../../types/WASMData';
import { LoadingState, SimBrief } from '../../types/general';
import { ImportFlightPlan } from '../../utils/TFDISBImport';
import { CoherentCallSBEntryF, inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
import CGSelect from '../CGSelect/CGSelect';
import ActionBar from '../actionbar/ActionBar';
interface SBEntryProps {
WASMData: WASMDataF;
loadingState: LoadingState;
username: string;
setLoadingState: (newState: LoadingState) => void;
}
const SBEntryF: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoadingState }) => {
const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget);
const [fuel, setFuel] = useState(Math.round(WASMData.livePayload.fuel));
const [fuelEnabled, setFuelEnabled] = useState(true);
const [SBInFlight, setSBInFlight] = useState(false);
const ZFW = () => {
if (loadingState !== 'loaded' && !GSXActive()) return Math.round(WASMData.targetPayload.total);
return Math.round(WASMData.livePayload.total);
};
const ZFWValid = () => {
return ZFW() <= WASMData.limits.maxZFW;
};
const GW = () => {
return fuel + ZFW();
};
const GWValid = () => {
return GW() <= WASMData.limits.maxTOW;
};
const GSXActive = () => {
return (
(WASMData.GSX.boardingState >= GSX_SERVICE_CALLED || WASMData.GSX.deboardingState >= GSX_SERVICE_CALLED) &&
WASMData.GSX.deboardingState !== GSX_SERVICE_FINISHED
);
};
const handleInput = (input: string, maxValue: number, setter: (value: number) => void) => {
if (!input) {
setter(0);
return;
}
const converted = parseInt(input);
if (converted) {
if (converted < 0) setter(0);
else if (converted > maxValue) setter(maxValue);
else setter(converted);
}
};
const handleSB = async () => {
setSBInFlight(true);
const SBResponse = await ImportFlightPlan(
username,
WASMData.limits.maxZFW,
WASMData.limits.maxTOW,
WASMData.limits.maxFuel,
WASMData.userData.isImperial
);
if (SBResponse.type === 'error') {
console.error('TODO: ERROR', SBResponse.message);
setSBInFlight(false);
return;
}
updateData(undefined, SBResponse.message);
setFuel(parseFloat(SBResponse.message.fuel) ?? 0);
setSBInFlight(false);
};
useEffect(
() =>
setFuel((prev) => {
if (prev > WASMData.limits.maxFuel) return WASMData.limits.maxFuel;
return prev;
}),
[WASMData.userData.isER]
);
useEffect(() => {
setFuelEnabled(inRangeOf(Math.round(WASMData.livePayload.fuel), fuel));
}, [WASMData.livePayload.fuel]);
const updateData = (_CGTarget?: number, SBPlan?: SimBrief) => {
CoherentCallSBEntryF(_CGTarget ?? CGTarget, SBPlan);
};
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-md bg-zinc-600 p-2 px-4">
<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, WASMData.limits.maxFuel, setFuel)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
<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',
WASMData.userData.isImperial ? fuel : fuel * 2.20462262185
);
SimVar.SetSimVarValue('L:MD11_EFB_READ_READY', 'bool', true);
setFuelEnabled(WASMData.livePayload.fuel === fuel);
}}
disabled={loadingState !== 'preview' || !fuelEnabled || GSXActive()}
>
Load Fuel
</button>
</div>
</div>
<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>Planned ZFW ({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 focus:border-blue-600 focus:ring-blue-600"
value={WASMData.sbPlanned.ZFW}
disabled
/>
</div>
<div className="relative flex w-full items-center justify-between bg-zinc-700 p-2 px-4">
<label>Planned GW ({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 focus:border-blue-600 focus:ring-blue-600"
value={WASMData.sbPlanned.GW}
disabled
/>
</div>
<div className="relative flex w-full items-center justify-between rounded-b-md bg-zinc-600 p-2 px-4">
<label>
Target ZFWCG ({WASMData.limits.minCG} - {WASMData.limits.maxCG})
</label>
<CGSelect
minCG={WASMData.limits.minCG}
maxCG={WASMData.limits.maxCG}
value={CGTarget}
disabled={loadingState !== 'preview' || GSXActive()}
increase={() =>
setCGTarget((prev) => {
const _new = prev + 0.1;
updateData(_new);
return _new;
})
}
decrease={() =>
setCGTarget((prev) => {
const _new = prev - 0.1;
updateData(_new);
return _new;
})
}
/>
</div>
</div>
<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' && !GSXActive() ? 'Expected' : 'Actual'} ZFW (
{WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
placeholder=""
className={`w-1/2 rounded-lg border ${ZFWValid() ? 'border-white' : 'border-red-500 text-red-500'} bg-zinc-700 px-3 py-2 text-right`}
disabled
value={ZFW()}
/>
</div>
<div className="relative flex w-full items-center justify-between rounded-b-md bg-zinc-700 p-2 px-4">
<label>
{loadingState !== 'loaded' && !GSXActive() ? 'Expected' : 'Actual'} GW (
{WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
placeholder=""
className={`w-1/2 rounded-lg border ${GWValid() ? 'border-white' : 'border-red-500 text-red-500'} bg-zinc-700 px-3 py-2 text-right`}
disabled
value={GW()}
/>
</div>
</div>
<ActionBar
loadingState={loadingState}
loadDisabled={!GWValid() || SBInFlight}
GSXSync={WASMData.options.GSXSync}
GSXActive={GSXActive()}
importSB={handleSB}
load={() => {
setLoadingState('loaded');
loadAircraft();
}}
unload={() => {
setLoadingState('preview');
unloadAircraft();
}}
/>
</>
);
};
export default SBEntryF;
@@ -1,111 +1,45 @@
import { FC, useEffect, useState } from 'react';
import { PaxConfig, PayloadPax } from '../../configs/pax';
import { Fuel, SharedConfig } from '../../configs/shared';
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
import { WASMDataPax } from '../../types/WASMData';
import { LoadingState, SimBrief } from '../../types/general';
import { ImportFlightPlan } from '../../utils/TFDISBImport';
import { CoherentCallSBEntryPax, inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
import CGSelect from '../CGSelect/CGSelect';
import ActionBar from '../actionbar/ActionBar';
interface StationEntryProps {
unit: 'kg' | 'lbs';
isER: boolean;
initialPayload: PayloadPax;
fuelLive: Fuel;
payloadLive: PayloadPax;
loadingState: 'preview' | 'accepted' | 'loaded';
interface SBEntryProps {
WASMData: WASMDataPax;
loadingState: LoadingState;
username: string;
setLoadingState: (newState: StationEntryProps['loadingState']) => void;
updateView: (payload: PayloadPax) => void;
loadAircraft: () => void;
setLoadingState: (newState: LoadingState) => void;
}
const SBEntryPax: FC<StationEntryProps> = ({
unit,
isER,
initialPayload,
fuelLive,
payloadLive,
loadingState,
username,
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 [SBPlan, setSBPlan] = useState<any>();
const SBEntryPax: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoadingState }) => {
const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget);
const [fuel, setFuel] = useState(Math.round(WASMData.livePayload.fuel));
const [fuelEnabled, setFuelEnabled] = useState(true);
const [SBInFlight, setSBInFlight] = useState(false);
const _ZFW = () => {
if (loadingState !== 'loaded') return ZFW;
const ZFW = () => {
if (loadingState !== 'loaded' && !GSXActive()) return Math.round(WASMData.targetPayload.total);
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();
return fuel + ZFW();
};
const GWValid = () => {
return GW() <= (isER ? SharedConfig.maxTOW.er[unit] : SharedConfig.maxTOW.norm[unit]);
return GW() <= WASMData.limits.maxTOW;
};
const GSXActive = () => {
return (
(WASMData.GSX.boardingState >= GSX_SERVICE_CALLED || WASMData.GSX.deboardingState >= GSX_SERVICE_CALLED) &&
WASMData.GSX.deboardingState !== GSX_SERVICE_FINISHED
);
};
const handleInput = (input: string, maxValue: number, setter: (value: number) => void) => {
@@ -125,117 +59,67 @@ const SBEntryPax: FC<StationEntryProps> = ({
const handleSB = async () => {
setSBInFlight(true);
const SBResponse = await ImportFlightPlan(username, PaxConfig, unit, isER);
const SBResponse = await ImportFlightPlan(
username,
WASMData.limits.maxZFW,
WASMData.limits.maxTOW,
WASMData.limits.maxFuel,
WASMData.userData.isImperial
);
if (SBResponse.type === 'error') {
console.error('TODO: ERROR', SBResponse.message);
setSBInFlight(false);
return;
}
const __ZFW = Math.round(
PaxConfig.weights.base[unit].total +
(isER ? SharedConfig.erExtraWeight[unit] * 2 : 0) +
payloadLive.empty +
SBResponse.message.pax * (PaxConfig.weights.pax[unit] + PaxConfig.weights.baggage[unit]) +
SBResponse.message.cargo
);
const _fuel = SBResponse.message.fuel;
updateData(undefined, SBResponse.message);
updateView(
PaxConfig.distribute(__ZFW, targetZFWCG, payloadLive.empty, fuelLive, unit, isER, SBResponse.message.pax)
);
setSBPlan(SBResponse.message);
setZFW(__ZFW);
setFuel(_fuel);
setFuel(parseFloat(SBResponse.message.fuel) ?? 0);
setSBInFlight(false);
};
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]
);
useEffect(() => {
setFuelEnabled((prev) => (!prev ? inRangeOf(Math.round(WASMData.livePayload.fuel), fuel) : prev));
}, [WASMData.livePayload.fuel]);
const updateData = (_CGTarget?: number, SBPlan?: SimBrief) => {
CoherentCallSBEntryPax(_CGTarget ?? CGTarget, SBPlan);
};
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>Planned ZFW ({unit})</label>
<input
type="text"
placeholder=""
className="w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right focus:border-blue-600 focus:ring-blue-600"
value={SBPlan?.plannedZFW ?? 0}
disabled
/>
</div>
<div className="relative flex w-full items-center justify-between bg-zinc-700 p-2 px-4">
<label>Planned ZFW ({unit})</label>
<input
type="text"
placeholder=""
className="w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right focus:border-blue-600 focus:ring-blue-600"
value={SBPlan?.plannedGW ?? 0}
disabled
/>
</div>
<div className="relative flex w-full items-center justify-between rounded-b-md bg-zinc-600 p-2 px-4">
<label>
Target ZFWCG ({SharedConfig.CGLimits.min} - {SharedConfig.CGLimits.max})
</label>
<CGSelect
value={targetZFWCG}
disabled={loadingState !== 'preview'}
increase={() =>
setTargetZFWCG((prev) => {
const _new = prev + 0.1;
updateView(PaxConfig.distribute(ZFW, _new, payloadLive.empty, fuelLive, unit, isER));
return _new;
})
}
decrease={() =>
setTargetZFWCG((prev) => {
const _new = prev - 0.1;
updateView(PaxConfig.distribute(ZFW, _new, payloadLive.empty, fuelLive, unit, isER));
return _new;
})
}
/>
</div>
</div>
<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
)
}
disabled
onChange={(e) => handleInput(e.target.value, WASMData.limits.maxFuel, setFuel)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
<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);
setFuelEnabled(WASMData.livePayload.fuel === fuel);
}}
disabled={loadingState !== 'preview' || SBInFlight}
disabled={loadingState !== 'preview' || !fuelEnabled || GSXActive()}
>
Load Fuel
</button>
@@ -244,20 +128,70 @@ const SBEntryPax: 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>Planned ZFW ({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 focus:border-blue-600 focus:ring-blue-600"
value={WASMData.sbPlanned.ZFW}
disabled
/>
</div>
<div className="relative flex w-full items-center justify-between bg-zinc-700 p-2 px-4">
<label>Planned GW ({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 focus:border-blue-600 focus:ring-blue-600"
value={WASMData.sbPlanned.GW}
disabled
/>
</div>
<div className="relative flex w-full items-center justify-between rounded-b-md bg-zinc-600 p-2 px-4">
<label>
{loadingState !== 'loaded' ? 'Expected' : 'Actual'} ZFW ({unit})
Target ZFWCG ({WASMData.limits.minCG} - {WASMData.limits.maxCG})
</label>
<CGSelect
minCG={WASMData.limits.minCG}
maxCG={WASMData.limits.maxCG}
value={CGTarget}
disabled={loadingState !== 'preview' || GSXActive()}
increase={() =>
setCGTarget((prev) => {
const _new = prev + 0.1;
updateData(_new);
return _new;
})
}
decrease={() =>
setCGTarget((prev) => {
const _new = prev - 0.1;
updateData(_new);
return _new;
})
}
/>
</div>
</div>
<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' && !GSXActive() ? 'Expected' : 'Actual'} ZFW (
{WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
placeholder=""
className={`w-1/2 rounded-lg border ${ZFWValid() ? 'border-white' : 'border-red-500 text-red-500'} bg-zinc-700 px-3 py-2 text-right`}
disabled
value={_ZFW()}
value={ZFW()}
/>
</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' && !GSXActive() ? 'Expected' : 'Actual'} GW (
{WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
@@ -271,10 +205,9 @@ const SBEntryPax: FC<StationEntryProps> = ({
<ActionBar
loadingState={loadingState}
acceptDisabled={!GWValid() || SBInFlight}
//TODO: Make GSX optional (accepted state for NON GSX)
accept={() => setLoadingState('loaded')}
reject={() => setLoadingState('preview')}
loadDisabled={!GWValid() || SBInFlight}
GSXSync={WASMData.options.GSXSync}
GSXActive={GSXActive()}
importSB={handleSB}
load={() => {
setLoadingState('loaded');
@@ -284,7 +217,7 @@ const SBEntryPax: FC<StationEntryProps> = ({
unload={() => {
setLoadingState('preview');
PaxConfig.unload(unit, isER);
unloadAircraft();
}}
/>
</>
@@ -1,36 +1,36 @@
import { FC } from 'react';
import { LoadingState } from '../../types/general';
interface ActionBarProps {
loadingState: 'preview' | 'accepted' | 'loaded';
acceptDisabled: boolean;
accept: () => void;
reject: () => void;
loadingState: LoadingState;
loadDisabled: boolean;
GSXSync: boolean;
GSXActive: boolean;
importSB?: () => void;
load: () => void;
unload: () => void;
}
const ActionBar: FC<ActionBarProps> = ({ loadingState, acceptDisabled, accept, reject, importSB, load, unload }) => {
const ActionBar: FC<ActionBarProps> = ({ loadingState, loadDisabled, GSXSync, GSXActive, importSB, load, unload }) => {
return (
<div className="relative flex w-full items-center justify-start gap-x-6">
{loadingState === 'preview' && (
{loadingState === 'preview' && !GSXSync && (
<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={accept}
disabled={acceptDisabled}
onClick={load}
disabled={loadDisabled}
>
Accept
Load
</button>
)}
{/*TODO: Make GSX optional (accepted state for NON GSX) */}
{loadingState === 'loaded' && (
{loadingState === 'loaded' && !GSXSync && (
<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={reject}
onClick={unload}
>
Reject
Unload
</button>
)}
@@ -41,31 +41,11 @@ const ActionBar: FC<ActionBarProps> = ({ loadingState, acceptDisabled, accept, r
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={importSB}
disabled={GSXActive}
>
Import from SimBrief
</button>
)}
{/*TODO: Make GSX optional */}
{/*
{loadingState === 'accepted' && (
<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={load}
>
Load
</button>
)}
{loadingState === 'loaded' && (
<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={unload}
>
Unload
</button>
)}
*/}
</div>
);
};
@@ -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;
@@ -0,0 +1,132 @@
import { FC, useState } from 'react';
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
import { LoadingState } from '../../types/general';
import { WASMDataF } from '../../types/WASMData';
import OptionsF from '../options/OptionsF';
import Profile from '../profile/Profile';
import SBEntryF from '../SBEntry/SBEntryF';
import StationEntryF from '../stationEntry/StationEntryF';
import Tabbar from '../tabbar/Tabbar';
import ZFWEntryF from '../zfwEntry/ZFWEntryF';
interface FreighterProps {
WASMData: WASMDataF;
username?: string;
}
const Freighter: FC<FreighterProps> = ({ WASMData, username }) => {
const [selectedTab, setSelectedTab] = useState(0);
const [loadingState, setLoadingState] = useState<LoadingState>('preview');
const upper1 = (overrideState: LoadingState = loadingState) => {
if (overrideState !== 'loaded') return Math.round(WASMData.targetPayload.upper1);
return Math.round(WASMData.livePayload.upper1);
};
const upper2 = (overrideState: LoadingState = loadingState) => {
if (overrideState !== 'loaded') return Math.round(WASMData.targetPayload.upper2);
return Math.round(WASMData.livePayload.upper2);
};
const upper3 = (overrideState: LoadingState = loadingState) => {
if (overrideState !== 'loaded') return Math.round(WASMData.targetPayload.upper3);
return Math.round(WASMData.livePayload.upper3);
};
const upper4 = (overrideState: LoadingState = loadingState) => {
if (overrideState !== 'loaded') return Math.round(WASMData.targetPayload.upper4);
return Math.round(WASMData.livePayload.upper4);
};
const lower1 = () => {
if (loadingState !== 'loaded' && !GSXActive()) return Math.round(WASMData.targetPayload.lowerForward);
return Math.round(WASMData.livePayload.lowerForward);
};
const lower2 = () => {
if (loadingState !== 'loaded' && !GSXActive()) return Math.round(WASMData.targetPayload.lowerRear);
return Math.round(WASMData.livePayload.lowerRear);
};
const OEW = () => {
if (loadingState !== 'loaded' && !GSXActive()) return Math.round(WASMData.targetPayload.empty);
return Math.round(WASMData.livePayload.empty);
};
const crew = () => {
if (loadingState !== 'loaded' && !GSXActive()) return Math.round(WASMData.targetPayload.crew);
return Math.round(WASMData.livePayload.crew);
};
const GSXActive = () => {
return (
(WASMData.GSX.boardingState >= GSX_SERVICE_CALLED || WASMData.GSX.deboardingState >= GSX_SERVICE_CALLED) &&
WASMData.GSX.deboardingState !== GSX_SERVICE_FINISHED
);
};
const CGs = (): [string, boolean, string, boolean] => {
if (loadingState !== 'loaded' && !GSXActive()) {
return [
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 [
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,
];
};
return (
<>
<Profile
type="F"
isER={WASMData.userData.isER}
upper1={`${upper1(GSXActive() ? 'loaded' : loadingState)}`}
upper2={`${upper2(GSXActive() ? 'loaded' : loadingState)}`}
upper3={`${upper3(GSXActive() ? 'loaded' : loadingState)}`}
upper4={`${upper4(GSXActive() ? 'loaded' : loadingState)}`}
lower1={`${lower1()}`}
lower2={`${lower2()}`}
OEW={`${OEW()}`}
crew={`${crew()}`}
unit={WASMData.userData.isImperial ? 'LBS' : 'KG'}
inPreview={loadingState !== 'loaded' && !GSXActive()}
CGs={CGs()}
/>
<Tabbar
tabs={
username ? ['Simbrief', 'ZFW', 'Passengers & Cargo', 'Options'] : ['ZFW', 'Passengers & Cargo', 'Options']
}
selectedTab={selectedTab}
setSelectedTab={setSelectedTab}
/>
{username && selectedTab === 0 && (
<SBEntryF
WASMData={WASMData}
loadingState={loadingState}
username={username}
setLoadingState={setLoadingState}
/>
)}
{((username && selectedTab === 1) || (!username && selectedTab === 0)) && (
<ZFWEntryF WASMData={WASMData} loadingState={loadingState} setLoadingState={setLoadingState} />
)}
{((username && selectedTab === 2) || (!username && selectedTab === 1)) && (
<StationEntryF WASMData={WASMData} loadingState={loadingState} setLoadingState={setLoadingState} />
)}
{((username && selectedTab === 3) || (!username && selectedTab === 2)) && (
<OptionsF WASMData={WASMData} loadingState={loadingState} />
)}
</>
);
};
export default Freighter;
@@ -0,0 +1,42 @@
import { FC } from 'react';
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
import { LoadingState } from '../../types/general';
import { WASMDataF } from '../../types/WASMData';
import { CoherentCallOptionsSet } from '../../utils/utils';
import ToggleComponent from '../toggleComponent/ToggleComponent';
interface OptionsFProps {
WASMData: WASMDataF;
loadingState: LoadingState;
}
const OptionsF: FC<OptionsFProps> = ({ WASMData, loadingState }) => {
const GSXActive = () => {
return (
(WASMData.GSX.boardingState >= GSX_SERVICE_CALLED || WASMData.GSX.deboardingState >= GSX_SERVICE_CALLED) &&
WASMData.GSX.deboardingState !== GSX_SERVICE_FINISHED
);
};
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-md bg-zinc-600 p-2 px-4">
<ToggleComponent
optionName="GSX Sync"
value={WASMData.options.GSXSync}
leftLabel={{ value: true }}
rightLabel={{ value: false }}
backgroundColor="bg-zinc-700"
setValue={(value) => {
CoherentCallOptionsSet(value);
}}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
</div>
</>
);
};
export default OptionsF;
@@ -0,0 +1,90 @@
import { FC, useEffect, useState } from 'react';
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
import { LoadingState } from '../../types/general';
import { WASMDataPax } from '../../types/WASMData';
import { CoherentCallOptionsSet } from '../../utils/utils';
import ToggleComponent from '../toggleComponent/ToggleComponent';
interface OptionsPaxProps {
WASMData: WASMDataPax;
loadingState: LoadingState;
}
const OptionsPax: FC<OptionsPaxProps> = ({ WASMData, loadingState }) => {
const [paxWeight, setPaxWeight] = useState(WASMData.options.paxWeight);
const [bagWeight, setBagWeight] = useState(WASMData.options.bagWeight);
const GSXActive = () => {
return (
(WASMData.GSX.boardingState >= GSX_SERVICE_CALLED || WASMData.GSX.deboardingState >= GSX_SERVICE_CALLED) &&
WASMData.GSX.deboardingState !== GSX_SERVICE_FINISHED
);
};
const updateData = () => {
CoherentCallOptionsSet(undefined, paxWeight, bagWeight);
};
const handleInput = (input: string, maxValue: number, setter: (value: number) => void) => {
if (!input) {
setter(0);
return;
}
const converted = parseInt(input);
if (converted) {
if (converted < 0) setter(0);
else if (converted > maxValue) setter(maxValue);
else setter(converted);
}
};
useEffect(() => updateData(), [paxWeight, bagWeight]);
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-md bg-zinc-600 p-2 px-4">
<ToggleComponent
optionName="GSX Sync"
value={WASMData.options.GSXSync}
leftLabel={{ value: true }}
rightLabel={{ value: false }}
backgroundColor="bg-zinc-700"
setValue={(value) => {
CoherentCallOptionsSet(value);
}}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
</div>
<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>Pax Weight ({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 focus:border-blue-600 focus:ring-blue-600"
value={paxWeight}
onChange={(e) => handleInput(e.target.value, Number.MAX_VALUE, setPaxWeight)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
<div className="relative flex w-full items-center justify-between bg-zinc-700 p-2 px-4">
<label>Bag Weight ({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 focus:border-blue-600 focus:ring-blue-600"
value={bagWeight}
onChange={(e) => handleInput(e.target.value, Number.MAX_VALUE, setBagWeight)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
</div>
</>
);
};
export default OptionsPax;
@@ -1,167 +1,109 @@
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 { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
import { LoadingState } from '../../types/general';
import { WASMDataPax } from '../../types/WASMData';
import OptionsPax from '../options/OptionsPax';
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';
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 [loadingState, setLoadingState] = useState<LoadingState>('preview');
const upper1 = (overrideState: 'preview' | 'accepted' | 'loaded' = loadingState) => {
if (overrideState !== 'loaded')
return PaxConfig.weightToPax(payload.business1Left + payload.business1Center + payload.business1Right, unit);
const upper1 = (overrideState: LoadingState = loadingState) => {
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);
const upper2 = (overrideState: LoadingState = loadingState) => {
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);
const upper3 = (overrideState: LoadingState = loadingState) => {
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);
const upper4 = (overrideState: LoadingState = loadingState) => {
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' && !GSXActive()) 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' && !GSXActive()) 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' && !GSXActive()) 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' && !GSXActive()) 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 GSXActive = () => {
return (
(WASMData.GSX.boardingState >= GSX_SERVICE_CALLED || WASMData.GSX.deboardingState >= GSX_SERVICE_CALLED) &&
WASMData.GSX.deboardingState !== GSX_SERVICE_FINISHED
);
};
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
);
const CGs = (): [string, boolean, string, boolean] => {
if (loadingState !== 'loaded' && !GSXActive()) {
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}
upper1={`${upper1()}`}
upper1max={loadingState === 'loaded' ? `${upper1('preview')}` : `${PaxConfig.stationMax.business1}`}
upper2={`${upper2()}`}
upper2max={loadingState === 'loaded' ? `${upper2('preview')}` : `${PaxConfig.stationMax.business2}`}
upper3={`${upper3()}`}
upper3max={loadingState === 'loaded' ? `${upper3('preview')}` : `${PaxConfig.stationMax.economy1}`}
upper4={`${upper4()}`}
upper4max={loadingState === 'loaded' ? `${upper4('preview')}` : `${PaxConfig.stationMax.economy2}`}
isER={WASMData.userData.isER}
upper1={`${upper1(GSXActive() ? 'loaded' : loadingState)}`}
upper1max={loadingState === 'loaded' || GSXActive() ? `${upper1('preview')}` : `${WASMData.limits.business1}`}
upper2={`${upper2(GSXActive() ? 'loaded' : loadingState)}`}
upper2max={loadingState === 'loaded' || GSXActive() ? `${upper2('preview')}` : `${WASMData.limits.business2}`}
upper3={`${upper3(GSXActive() ? 'loaded' : loadingState)}`}
upper3max={loadingState === 'loaded' || GSXActive() ? `${upper3('preview')}` : `${WASMData.limits.economy1}`}
upper4={`${upper4(GSXActive() ? 'loaded' : loadingState)}`}
upper4max={loadingState === 'loaded' || GSXActive() ? `${upper4('preview')}` : `${WASMData.limits.economy2}`}
lower1={`${lower1()}`}
lower2={`${lower2()}`}
OEW={`${_OEW()}`}
OEW={`${OEW()}`}
crew={`${crew()}`}
unit={unit.toUpperCase()}
inPreview={loadingState !== 'loaded'}
CGs={_CGs()}
unit={WASMData.userData.isImperial ? 'LBS' : 'KG'}
inPreview={loadingState !== 'loaded' && !GSXActive()}
CGs={CGs()}
/>
<Tabbar
tabs={
@@ -170,63 +112,22 @@ const Pax: FC<PaxProps> = ({
selectedTab={selectedTab}
setSelectedTab={setSelectedTab}
/>
{username && selectedTab === 0 && (
<SBEntryPax
unit={unit}
isER={isER}
initialPayload={payload}
fuelLive={fuelLive}
payloadLive={payloadLive}
WASMData={WASMData}
loadingState={loadingState}
username={username}
setLoadingState={setLoadingState}
updateView={(_payload) => {
setPayload(_payload);
}}
loadAircraft={() => {
PaxConfig.setBaseWeight(unit, isER);
PaxConfig.setWeights(payload, unit);
}}
/>
)}
{((username && selectedTab === 1) || (!username && selectedTab === 0)) && (
<ZFWEntryPax
unit={unit}
isER={isER}
initialPayload={payload}
fuelLive={fuelLive}
payloadLive={payloadLive}
loadingState={loadingState}
setLoadingState={setLoadingState}
updateView={(_payload) => {
setPayload(_payload);
}}
loadAircraft={() => {
PaxConfig.setBaseWeight(unit, isER);
PaxConfig.setWeights(payload, unit);
}}
/>
<ZFWEntryPax WASMData={WASMData} loadingState={loadingState} setLoadingState={setLoadingState} />
)}
{((username && selectedTab === 2) || (!username && selectedTab === 1)) && (
<StationEntryPax
unit={unit}
isER={isER}
initialPayload={payload}
fuelLive={fuelLive}
payloadLive={payloadLive}
loadingState={loadingState}
setLoadingState={setLoadingState}
updateView={(_payload) => {
setPayload(_payload);
}}
loadAircraft={() => {
PaxConfig.setBaseWeight(unit, isER);
PaxConfig.setWeights(payload, unit);
}}
/>
<StationEntryPax WASMData={WASMData} loadingState={loadingState} setLoadingState={setLoadingState} />
)}
{((username && selectedTab === 3) || (!username && selectedTab === 2)) && (
<OptionsPax WASMData={WASMData} loadingState={loadingState} />
)}
</>
);
@@ -0,0 +1,227 @@
import { FC, useEffect, useState } from 'react';
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
import { LoadingState } from '../../types/general';
import { WASMDataF } from '../../types/WASMData';
import { CoherentCallStationEntryF, inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
import ActionBar from '../actionbar/ActionBar';
interface StationEntryProps {
WASMData: WASMDataF;
loadingState: LoadingState;
setLoadingState: (newState: LoadingState) => void;
}
const StationEntryF: FC<StationEntryProps> = ({ WASMData, loadingState, setLoadingState }) => {
const [upper1, setUpper1] = useState(WASMData.targetPayload.upper1);
const [upper2, setUpper2] = useState(WASMData.targetPayload.upper2);
const [upper3, setUpper3] = useState(WASMData.targetPayload.upper3);
const [upper4, setUpper4] = useState(WASMData.targetPayload.upper4);
const [lowerForward, setLowerForward] = useState(WASMData.targetPayload.lowerForward);
const [lowerRear, setLowerRear] = useState(WASMData.targetPayload.lowerRear);
const [fuel, setFuel] = useState(Math.round(WASMData.livePayload.fuel));
const [fuelEnabled, setFuelEnabled] = useState(true);
const ZFW = () => {
if (loadingState !== 'loaded' && !GSXActive()) return Math.round(WASMData.targetPayload.total);
return Math.round(WASMData.livePayload.total);
};
const ZFWValid = () => {
return ZFW() <= WASMData.limits.maxZFW;
};
const GW = () => {
return fuel + ZFW();
};
const GWValid = () => {
return GW() <= WASMData.limits.maxTOW;
};
const GSXActive = () => {
return (
(WASMData.GSX.boardingState >= GSX_SERVICE_CALLED || WASMData.GSX.deboardingState >= GSX_SERVICE_CALLED) &&
WASMData.GSX.deboardingState !== GSX_SERVICE_FINISHED
);
};
const handleInput = (input: string, maxValue: number, setter: (value: number) => void) => {
if (!input) {
setter(0);
return;
}
const converted = parseInt(input);
if (converted) {
if (converted < 0) setter(0);
else if (converted > maxValue) setter(maxValue);
else setter(converted);
}
};
const updateData = () => {
CoherentCallStationEntryF(upper1, upper2, upper3, upper4, lowerForward, lowerRear);
};
useEffect(() => updateData(), [upper1, upper2, upper3, upper4, lowerForward, lowerRear]);
useEffect(
() =>
setFuel((prev) => {
if (prev > WASMData.limits.maxFuel) return WASMData.limits.maxFuel;
return prev;
}),
[WASMData.userData.isER]
);
useEffect(() => {
setFuelEnabled(inRangeOf(Math.round(WASMData.livePayload.fuel), fuel));
}, [WASMData.livePayload.fuel]);
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-md bg-zinc-600 p-2 px-4">
<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, WASMData.limits.maxFuel, setFuel)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
<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',
WASMData.userData.isImperial ? fuel : fuel * 2.20462262185
);
SimVar.SetSimVarValue('L:MD11_EFB_READ_READY', 'bool', true);
setFuelEnabled(WASMData.livePayload.fuel === fuel);
}}
disabled={loadingState !== 'preview' || !fuelEnabled || GSXActive()}
>
Load Fuel
</button>
</div>
</div>
<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>Upper 1</label>
<input
type="text"
placeholder=""
className="w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right focus:border-blue-600 focus:ring-blue-600"
value={upper1}
onChange={(e) => handleInput(e.target.value, WASMData.limits.upper1, setUpper1)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
<div className="relative flex w-full items-center justify-between bg-zinc-700 p-2 px-4">
<label>Upper 2</label>
<input
type="text"
placeholder=""
className="w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right focus:border-blue-600 focus:ring-blue-600"
value={upper2}
onChange={(e) => handleInput(e.target.value, WASMData.limits.upper2, setUpper2)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
<div className="relative flex w-full items-center justify-between bg-zinc-600 p-2 px-4">
<label>Upper 3</label>
<input
type="text"
placeholder=""
className="w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right focus:border-blue-600 focus:ring-blue-600"
value={upper3}
onChange={(e) => handleInput(e.target.value, WASMData.limits.upper3, setUpper3)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
<div className="relative flex w-full items-center justify-between bg-zinc-700 p-2 px-4">
<label>Upper 4</label>
<input
type="text"
placeholder=""
className="w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right focus:border-blue-600 focus:ring-blue-600"
value={upper4}
onChange={(e) => handleInput(e.target.value, WASMData.limits.upper4, setUpper4)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
<div className="relative flex w-full items-center justify-between bg-zinc-600 p-2 px-4">
<label>Forward Cargo ({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 focus:border-blue-600 focus:ring-blue-600"
value={lowerForward}
onChange={(e) => handleInput(e.target.value, WASMData.limits.lowerForward, setLowerForward)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
<div className="relative flex w-full items-center justify-between rounded-b-md bg-zinc-700 p-2 px-4">
<label>Aft Cargo ({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 focus:border-blue-600 focus:ring-blue-600"
value={lowerRear}
onChange={(e) => handleInput(e.target.value, WASMData.limits.lowerRear, setLowerRear)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
</div>
<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' && !GSXActive() ? 'Expected' : 'Actual'} ZFW (
{WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
placeholder=""
className={`w-1/2 rounded-lg border ${ZFWValid() ? 'border-white' : 'border-red-500 text-red-500'} bg-zinc-700 px-3 py-2 text-right`}
disabled
value={ZFW()}
/>
</div>
<div className="relative flex w-full items-center justify-between rounded-b-md bg-zinc-700 p-2 px-4">
<label>
{loadingState !== 'loaded' && !GSXActive() ? 'Expected' : 'Actual'} GW (
{WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
placeholder=""
className={`w-1/2 rounded-lg border ${GWValid() ? 'border-white' : 'border-red-500 text-red-500'} bg-zinc-700 px-3 py-2 text-right`}
disabled
value={GW()}
/>
</div>
</div>
<ActionBar
loadingState={loadingState}
loadDisabled={!ZFWValid() || !GWValid()}
GSXSync={WASMData.options.GSXSync}
GSXActive={GSXActive()}
load={() => {
setLoadingState('loaded');
loadAircraft();
}}
unload={() => {
setLoadingState('preview');
unloadAircraft();
}}
/>
</>
);
};
export default StationEntryF;
@@ -1,117 +1,46 @@
import { FC, useEffect, useState } from 'react';
import { PaxConfig, PayloadPax } from '../../configs/pax';
import { Fuel, SharedConfig } from '../../configs/shared';
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
import { LoadingState } from '../../types/general';
import { WASMDataPax } from '../../types/WASMData';
import { CoherentCallStationEntryPax, inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
import ActionBar from '../actionbar/ActionBar';
interface StationEntryProps {
unit: 'kg' | 'lbs';
isER: boolean;
initialPayload: PayloadPax;
fuelLive: Fuel;
payloadLive: PayloadPax;
loadingState: 'preview' | 'accepted' | 'loaded';
setLoadingState: (newState: StationEntryProps['loadingState']) => void;
updateView: (payload: PayloadPax) => void;
loadAircraft: () => void;
WASMData: WASMDataPax;
loadingState: LoadingState;
setLoadingState: (newState: LoadingState) => void;
}
const StationEntryPax: FC<StationEntryProps> = ({
unit,
isER,
initialPayload,
fuelLive,
payloadLive,
loadingState,
setLoadingState,
updateView,
loadAircraft,
}) => {
const [business1, setBusiness1] = useState(
PaxConfig.weightToPax(
initialPayload.business1Left + initialPayload.business1Center + initialPayload.business1Right,
unit
)
);
const [business2, setBusiness2] = useState(
PaxConfig.weightToPax(
initialPayload.business2Left + initialPayload.business2Center + initialPayload.business2Right,
unit
)
);
const [economy1, setEconomy1] = useState(
PaxConfig.weightToPax(
initialPayload.economy1Left + initialPayload.economy1Center + initialPayload.economy1Right,
unit
)
);
const [economy2, setEconomy2] = useState(
PaxConfig.weightToPax(
initialPayload.economy2Left + initialPayload.economy2Center + initialPayload.economy2Right,
unit
)
);
const [forwardCargo, setForwardCargo] = useState(initialPayload.forwardCargo);
const [rearCargo, setRearCargo] = useState(initialPayload.rearCargo);
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 StationEntryPax: FC<StationEntryProps> = ({ WASMData, loadingState, setLoadingState }) => {
const [business1, setBusiness1] = useState(WASMData.targetPayload.business1);
const [business2, setBusiness2] = useState(WASMData.targetPayload.business2);
const [economy1, setEconomy1] = useState(WASMData.targetPayload.economy1);
const [economy2, setEconomy2] = useState(WASMData.targetPayload.economy2);
const [forwardCargo, setForwardCargo] = useState(WASMData.targetPayload.forwardCargo);
const [rearCargo, setRearCargo] = useState(WASMData.targetPayload.rearCargo);
const [fuel, setFuel] = useState(Math.round(WASMData.livePayload.fuel));
const [fuelEnabled, setFuelEnabled] = useState(true);
const ZFW = () => {
if (loadingState !== 'loaded')
return Math.round(
(business1 + business2 + economy1 + economy2) * PaxConfig.weights.pax[unit] +
forwardCargo +
rearCargo +
PaxConfig.weights.base[unit].total +
(isER ? SharedConfig.erExtraWeight[unit] * 2 : 0) +
payloadLive.empty
);
if (loadingState !== 'loaded' && !GSXActive()) return Math.round(WASMData.targetPayload.total);
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 GSXActive = () => {
return (
(WASMData.GSX.boardingState >= GSX_SERVICE_CALLED || WASMData.GSX.deboardingState >= GSX_SERVICE_CALLED) &&
WASMData.GSX.deboardingState !== GSX_SERVICE_FINISHED
);
};
const handleInput = (input: string, maxValue: number, setter: (value: number) => void) => {
@@ -128,37 +57,55 @@ const StationEntryPax: FC<StationEntryProps> = ({
}
};
useEffect(() => _updateView(), [business1, business2, economy1, economy2, forwardCargo, rearCargo]);
const updateData = () => {
CoherentCallStationEntryPax(business1, business2, economy1, economy2, forwardCargo, rearCargo);
};
useEffect(() => updateData(), [business1, business2, economy1, economy2, forwardCargo, rearCargo]);
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 _updateView = () => {
const payload = PaxConfig.generateDistribution(
payloadLive.empty,
business1,
business2,
economy1,
economy2,
forwardCargo,
rearCargo,
unit,
isER
);
updateView(payload);
};
useEffect(() => {
setFuelEnabled(inRangeOf(Math.round(WASMData.livePayload.fuel), fuel));
}, [WASMData.livePayload.fuel]);
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-md bg-zinc-600 p-2 px-4">
<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, WASMData.limits.maxFuel, setFuel)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
<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',
WASMData.userData.isImperial ? fuel : fuel * 2.20462262185
);
SimVar.SetSimVarValue('L:MD11_EFB_READ_READY', 'bool', true);
setFuelEnabled(WASMData.livePayload.fuel === fuel);
}}
disabled={loadingState !== 'preview' || !fuelEnabled || GSXActive()}
>
Load Fuel
</button>
</div>
</div>
<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>Business</label>
@@ -167,8 +114,8 @@ const StationEntryPax: FC<StationEntryProps> = ({
placeholder=""
className="w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right focus:border-blue-600 focus:ring-blue-600"
value={business1}
onChange={(e) => handleInput(e.target.value, PaxConfig.stationMax.business1, setBusiness1)}
disabled={loadingState !== 'preview'}
onChange={(e) => handleInput(e.target.value, WASMData.limits.business1, setBusiness1)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
<div className="relative flex w-full items-center justify-between bg-zinc-700 p-2 px-4">
@@ -178,8 +125,8 @@ const StationEntryPax: FC<StationEntryProps> = ({
placeholder=""
className="w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right focus:border-blue-600 focus:ring-blue-600"
value={business2}
onChange={(e) => handleInput(e.target.value, PaxConfig.stationMax.business2, setBusiness2)}
disabled={loadingState !== 'preview'}
onChange={(e) => handleInput(e.target.value, WASMData.limits.business2, setBusiness2)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
<div className="relative flex w-full items-center justify-between bg-zinc-600 p-2 px-4">
@@ -189,8 +136,8 @@ const StationEntryPax: FC<StationEntryProps> = ({
placeholder=""
className="w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right focus:border-blue-600 focus:ring-blue-600"
value={economy1}
onChange={(e) => handleInput(e.target.value, PaxConfig.stationMax.economy1, setEconomy1)}
disabled={loadingState !== 'preview'}
onChange={(e) => handleInput(e.target.value, WASMData.limits.economy1, setEconomy1)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
<div className="relative flex w-full items-center justify-between bg-zinc-700 p-2 px-4">
@@ -200,69 +147,39 @@ const StationEntryPax: FC<StationEntryProps> = ({
placeholder=""
className="w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right focus:border-blue-600 focus:ring-blue-600"
value={economy2}
onChange={(e) => handleInput(e.target.value, PaxConfig.stationMax.economy2, setEconomy2)}
disabled={loadingState !== 'preview'}
onChange={(e) => handleInput(e.target.value, WASMData.limits.economy2, setEconomy2)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
<div className="relative flex w-full items-center justify-between bg-zinc-600 p-2 px-4">
<label>Forward Cargo ({unit})</label>
<label>Forward Cargo ({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 focus:border-blue-600 focus:ring-blue-600"
value={forwardCargo}
onChange={(e) => handleInput(e.target.value, SharedConfig.stationMax.forward[unit], setForwardCargo)}
disabled={loadingState !== 'preview'}
onChange={(e) => handleInput(e.target.value, WASMData.limits.forwardCargo, setForwardCargo)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
<div className="relative flex w-full items-center justify-between rounded-b-md bg-zinc-700 p-2 px-4">
<label>Aft Cargo ({unit})</label>
<label>Aft Cargo ({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 focus:border-blue-600 focus:ring-blue-600"
value={rearCargo}
onChange={(e) => handleInput(e.target.value, SharedConfig.stationMax.rear[unit], setRearCargo)}
disabled={loadingState !== 'preview'}
onChange={(e) => handleInput(e.target.value, WASMData.limits.rearCargo, setRearCargo)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
</div>
<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>
<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
)
}
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_READ_READY', 'bool', true);
}}
disabled={loadingState !== 'preview'}
>
Load Fuel
</button>
</div>
</div>
<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' && !GSXActive() ? 'Expected' : 'Actual'} ZFW (
{WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
@@ -274,7 +191,8 @@ const StationEntryPax: 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' && !GSXActive() ? 'Expected' : 'Actual'} GW (
{WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
@@ -288,9 +206,9 @@ const StationEntryPax: FC<StationEntryProps> = ({
<ActionBar
loadingState={loadingState}
acceptDisabled={!ZFWValid() || !GWValid()}
accept={() => setLoadingState('accepted')}
reject={() => setLoadingState('preview')}
loadDisabled={!ZFWValid() || !GWValid()}
GSXSync={WASMData.options.GSXSync}
GSXActive={GSXActive()}
load={() => {
setLoadingState('loaded');
@@ -299,7 +217,7 @@ const StationEntryPax: FC<StationEntryProps> = ({
unload={() => {
setLoadingState('preview');
PaxConfig.unload(unit, isER);
unloadAircraft();
}}
/>
</>
@@ -0,0 +1,51 @@
interface ToggleComponentProps<T> {
optionName: string;
value: T;
leftLabel: {
value: T;
label?: string;
};
rightLabel: {
value: T;
label?: string;
};
backgroundColor: string;
disabled?: boolean;
setValue: (value: T) => void;
}
const ToggleComponent = <T,>({
optionName,
value,
leftLabel,
rightLabel,
backgroundColor,
disabled,
setValue,
}: ToggleComponentProps<T>) => {
return (
<div className="flex w-full items-center justify-between text-xs">
<span>{optionName}</span>
<div className="inline-flex w-1/2 rounded-md shadow-sm">
<button
type="button"
className={`${value === leftLabel.value ? 'bg-green-700' : backgroundColor} w-1/2 rounded-l-lg border border-white px-4 py-2 text-white disabled:pointer-events-none disabled:opacity-50`}
onClick={() => setValue(leftLabel.value)}
disabled={disabled}
>
{leftLabel.label || 'Enabled'}
</button>
<button
type="button"
className={`${value === rightLabel.value ? 'bg-green-700' : backgroundColor} w-1/2 rounded-r-md border border-white px-4 py-2 text-white disabled:pointer-events-none disabled:opacity-50`}
onClick={() => setValue(rightLabel.value)}
disabled={disabled}
>
{rightLabel.label || 'Disabled'}
</button>
</div>
</div>
);
};
export default ToggleComponent;
@@ -0,0 +1,221 @@
import { FC, useEffect, useState } from 'react';
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
import { WASMDataF } from '../../types/WASMData';
import { LoadingState } from '../../types/general';
import { CoherentCallZFWEntry, inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
import CGSelect from '../CGSelect/CGSelect';
import ActionBar from '../actionbar/ActionBar';
interface ZFWEntryProps {
WASMData: WASMDataF;
loadingState: LoadingState;
setLoadingState: (newState: LoadingState) => void;
}
const ZFWEntryF: FC<ZFWEntryProps> = ({ WASMData, loadingState, setLoadingState }) => {
const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget);
const [fuel, setFuel] = useState(Math.round(WASMData.livePayload.fuel));
const [ZFWTarget, setZFWTarget] = useState(Math.round(WASMData.targetPayload.total));
const [fuelEnabled, setFuelEnabled] = useState(true);
const ZFW = () => {
if (loadingState !== 'loaded' && !GSXActive()) return ZFWTarget;
return Math.round(WASMData.livePayload.total);
};
const ZFWValid = () => {
return ZFW() <= WASMData.limits.maxZFW;
};
const GW = () => {
return fuel + ZFW();
};
const GWValid = () => {
return GW() <= WASMData.limits.maxTOW;
};
const GSXActive = () => {
return (
(WASMData.GSX.boardingState >= GSX_SERVICE_CALLED || WASMData.GSX.deboardingState >= GSX_SERVICE_CALLED) &&
WASMData.GSX.deboardingState !== GSX_SERVICE_FINISHED
);
};
const handleInput = (input: string, maxValue: number, setter: (value: number) => void) => {
if (!input) {
setter(0);
return;
}
const converted = parseInt(input);
if (converted) {
if (converted < 0) setter(0);
else if (converted > maxValue) setter(maxValue);
else setter(converted);
}
};
const handleInputZFW = (input: string) => {
if (!input) return;
const converted = parseInt(input);
if (converted) {
if (converted < 0) setZFWTarget(Math.round(WASMData.targetPayload.empty + WASMData.targetPayload.crew));
else if (converted > WASMData.limits.maxZFW) setZFWTarget(WASMData.limits.maxZFW);
else setZFWTarget(converted);
}
};
const handleBlur = (input: string) => {
const minZFW = Math.round(WASMData.targetPayload.empty + WASMData.targetPayload.crew);
if (!input) {
setZFWTarget(minZFW);
return;
}
const converted = parseInt(input);
if (converted) {
if (converted < minZFW) setZFWTarget(minZFW);
else if (converted > WASMData.limits.maxZFW) setZFWTarget(WASMData.limits.maxZFW);
else setZFWTarget(converted);
}
updateData(converted);
};
useEffect(
() =>
setFuel((prev) => {
if (prev > WASMData.limits.maxFuel) return WASMData.limits.maxFuel;
return prev;
}),
[WASMData.userData.isER]
);
useEffect(() => {
setFuelEnabled(inRangeOf(Math.round(WASMData.livePayload.fuel), fuel));
}, [WASMData.livePayload.fuel]);
const updateData = (_ZFWTarget?: number, _CGTarget?: number) => {
CoherentCallZFWEntry(_ZFWTarget ?? ZFWTarget, _CGTarget ?? CGTarget);
};
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-md bg-zinc-600 p-2 px-4">
<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, WASMData.limits.maxFuel, setFuel)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
<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',
WASMData.userData.isImperial ? fuel : fuel * 2.20462262185
);
SimVar.SetSimVarValue('L:MD11_EFB_READ_READY', 'bool', true);
setFuelEnabled(WASMData.livePayload.fuel === fuel);
}}
disabled={loadingState !== 'preview' || !fuelEnabled || GSXActive()}
>
Load Fuel
</button>
</div>
</div>
<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 ({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 focus:border-blue-600 focus:ring-blue-600"
value={ZFWTarget}
onChange={(e) => handleInputZFW(e.target.value)}
onBlur={(e) => handleBlur(e.target.value)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</div>
<div className="relative flex w-full items-center justify-between rounded-b-md bg-zinc-700 p-2 px-4">
<label>
Target ZFWCG ({WASMData.limits.minCG} - {WASMData.limits.maxCG})
</label>
<CGSelect
minCG={WASMData.limits.minCG}
maxCG={WASMData.limits.maxCG}
value={CGTarget}
disabled={loadingState !== 'preview' || GSXActive()}
increase={() =>
setCGTarget((prev) => {
const _new = prev + 0.1;
updateData(undefined, _new);
return _new;
})
}
decrease={() =>
setCGTarget((prev) => {
const _new = prev - 0.1;
updateData(undefined, _new);
return _new;
})
}
/>
</div>
</div>
<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' && !GSXActive() ? 'Expected' : 'Actual'} ZFW (
{WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
placeholder=""
className={`w-1/2 rounded-lg border ${ZFWValid() ? 'border-white' : 'border-red-500 text-red-500'} bg-zinc-700 px-3 py-2 text-right`}
disabled
value={ZFW()}
/>
</div>
<div className="relative flex w-full items-center justify-between rounded-b-md bg-zinc-700 p-2 px-4">
<label>
{loadingState !== 'loaded' && !GSXActive() ? 'Expected' : 'Actual'} GW (
{WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
placeholder=""
className={`w-1/2 rounded-lg border ${GWValid() ? 'border-white' : 'border-red-500 text-red-500'} bg-zinc-700 px-3 py-2 text-right`}
disabled
value={GW()}
/>
</div>
</div>
<ActionBar
loadingState={loadingState}
loadDisabled={!GWValid()}
GSXSync={WASMData.options.GSXSync}
GSXActive={GSXActive()}
load={() => {
setLoadingState('loaded');
loadAircraft();
}}
unload={() => {
setLoadingState('preview');
unloadAircraft();
}}
/>
</>
);
};
export default ZFWEntryF;
@@ -1,106 +1,43 @@
import { FC, useEffect, useState } from 'react';
import { PaxConfig, PayloadPax } from '../../configs/pax';
import { Fuel, SharedConfig } from '../../configs/shared';
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
import { WASMDataPax } from '../../types/WASMData';
import { LoadingState } from '../../types/general';
import { CoherentCallZFWEntry, inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
import CGSelect from '../CGSelect/CGSelect';
import ActionBar from '../actionbar/ActionBar';
interface StationEntryProps {
unit: 'kg' | 'lbs';
isER: boolean;
initialPayload: PayloadPax;
fuelLive: Fuel;
payloadLive: PayloadPax;
loadingState: 'preview' | 'accepted' | 'loaded';
setLoadingState: (newState: StationEntryProps['loadingState']) => void;
updateView: (payload: PayloadPax) => void;
loadAircraft: () => void;
interface ZFWEntryProps {
WASMData: WASMDataPax;
loadingState: LoadingState;
setLoadingState: (newState: LoadingState) => 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<ZFWEntryProps> = ({ WASMData, loadingState, setLoadingState }) => {
const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget);
const [fuel, setFuel] = useState(Math.round(WASMData.livePayload.fuel));
const [ZFWTarget, setZFWTarget] = useState(Math.round(WASMData.targetPayload.total));
const [fuelEnabled, setFuelEnabled] = useState(true);
const _ZFW = () => {
if (loadingState !== 'loaded') return ZFW;
const ZFW = () => {
if (loadingState !== 'loaded' && !GSXActive()) return ZFWTarget;
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();
return fuel + ZFW();
};
const GWValid = () => {
return GW() <= (isER ? SharedConfig.maxTOW.er[unit] : SharedConfig.maxTOW.norm[unit]);
return GW() <= WASMData.limits.maxTOW;
};
const GSXActive = () => {
return (
(WASMData.GSX.boardingState >= GSX_SERVICE_CALLED || WASMData.GSX.deboardingState >= GSX_SERVICE_CALLED) &&
WASMData.GSX.deboardingState !== GSX_SERVICE_FINISHED
);
};
const handleInput = (input: string, maxValue: number, setter: (value: number) => void) => {
@@ -121,113 +58,71 @@ 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]);
else setZFW(converted);
if (converted < 0) setZFWTarget(Math.round(WASMData.targetPayload.empty + WASMData.targetPayload.crew));
else if (converted > WASMData.limits.maxZFW) setZFWTarget(WASMData.limits.maxZFW);
else setZFWTarget(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);
setZFWTarget(minZFW);
return;
}
const converted = parseInt(input);
if (converted) {
if (converted < minZFW) setZFW(minZFW);
else if (converted > PaxConfig.maxZWF[unit]) setZFW(PaxConfig.maxZWF[unit]);
else setZFW(converted);
if (converted < minZFW) setZFWTarget(minZFW);
else if (converted > WASMData.limits.maxZFW) setZFWTarget(WASMData.limits.maxZFW);
else setZFWTarget(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]
);
useEffect(() => {
setFuelEnabled(inRangeOf(Math.round(WASMData.livePayload.fuel), fuel));
}, [WASMData.livePayload.fuel]);
const updateData = (_ZFWTarget?: number, _CGTarget?: number) => {
CoherentCallZFWEntry(_ZFWTarget ?? ZFWTarget, _CGTarget ?? CGTarget);
};
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>
<input
type="text"
placeholder=""
className="w-1/2 rounded-lg border border-white bg-zinc-700 px-3 py-2 text-right focus:border-blue-600 focus:ring-blue-600"
value={ZFW}
onChange={(e) => handleInputZFW(e.target.value)}
onBlur={(e) => handleBlur(e.target.value)}
disabled={loadingState !== 'preview'}
/>
</div>
<div className="relative flex w-full items-center justify-between rounded-b-md bg-zinc-700 p-2 px-4">
<label>
Target ZFWCG ({SharedConfig.CGLimits.min} - {SharedConfig.CGLimits.max})
</label>
<CGSelect
value={targetZFWCG}
disabled={loadingState !== 'preview'}
increase={() =>
setTargetZFWCG((prev) => {
const _new = prev + 0.1;
updateView(PaxConfig.distribute(ZFW, _new, payloadLive.empty, fuelLive, unit, isER));
return _new;
})
}
decrease={() =>
setTargetZFWCG((prev) => {
const _new = prev - 0.1;
updateView(PaxConfig.distribute(ZFW, _new, payloadLive.empty, fuelLive, unit, isER));
return _new;
})
}
/>
</div>
</div>
<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
)
}
disabled={loadingState !== 'preview'}
onChange={(e) => handleInput(e.target.value, WASMData.limits.maxFuel, setFuel)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
<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);
setFuelEnabled(WASMData.livePayload.fuel === fuel);
}}
disabled={loadingState !== 'preview'}
disabled={loadingState !== 'preview' || !fuelEnabled || GSXActive()}
>
Load Fuel
</button>
@@ -236,20 +131,62 @@ 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>Target ZFW ({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 focus:border-blue-600 focus:ring-blue-600"
value={ZFWTarget}
onChange={(e) => handleInputZFW(e.target.value)}
onBlur={(e) => handleBlur(e.target.value)}
disabled={loadingState !== 'preview' || GSXActive()}
/>
</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'} ZFW ({unit})
Target ZFWCG ({WASMData.limits.minCG} - {WASMData.limits.maxCG})
</label>
<CGSelect
minCG={WASMData.limits.minCG}
maxCG={WASMData.limits.maxCG}
value={CGTarget}
disabled={loadingState !== 'preview' || GSXActive()}
increase={() =>
setCGTarget((prev) => {
const _new = prev + 0.1;
updateData(undefined, _new);
return _new;
})
}
decrease={() =>
setCGTarget((prev) => {
const _new = prev - 0.1;
updateData(undefined, _new);
return _new;
})
}
/>
</div>
</div>
<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' && !GSXActive() ? 'Expected' : 'Actual'} ZFW (
{WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
placeholder=""
className={`w-1/2 rounded-lg border ${ZFWValid() ? 'border-white' : 'border-red-500 text-red-500'} bg-zinc-700 px-3 py-2 text-right`}
disabled
value={_ZFW()}
value={ZFW()}
/>
</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' && !GSXActive() ? 'Expected' : 'Actual'} GW (
{WASMData.userData.isImperial ? 'lbs' : 'kg'})
</label>
<input
type="text"
@@ -263,9 +200,9 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
<ActionBar
loadingState={loadingState}
acceptDisabled={!GWValid()}
accept={() => setLoadingState('accepted')}
reject={() => setLoadingState('preview')}
loadDisabled={!GWValid()}
GSXSync={WASMData.options.GSXSync}
GSXActive={GSXActive()}
load={() => {
setLoadingState('loaded');
@@ -274,7 +211,7 @@ const ZFWEntryPax: FC<StationEntryProps> = ({
unload={() => {
setLoadingState('preview');
PaxConfig.unload(unit, isER);
unloadAircraft();
}}
/>
</>
@@ -1,153 +0,0 @@
import { ArmsFuel, Fuel, toPercentMAC } from './shared';
// TODO: Extract from CFG at runtime.
const ArmsFreight = {
empty: -159.6,
pilot: 984,
firstOfficer: 984,
engineer: 960,
upper1Left: 660,
upper1Right: 660,
upper2Left: 240,
upper2Right: 240,
upper3Left: -240,
upper3Right: -240,
upper4Left: -600,
upper4Right: -600,
lowerForward: 360,
lowerRear: -360,
leftAuxF: 60,
rightAuxF: 60,
};
export interface PayloadFreight {
empty: number;
pilot: number;
firstOfficer: number;
engineer: number;
upper1Left: number;
upper1Right: number;
upper2Left: number;
upper2Right: number;
upper3Left: number;
upper3Right: number;
upper4Left: number;
upper4Right: number;
lowerForward: number;
lowerRear: number;
leftAuxF: number;
rightAuxF: number;
}
//PMC pallet
export const maxUpperStationWeight = {
lbs: (26 / 8) * 15000,
kg: (26 / 8) * 6804,
};
export const baseWeightFreight = {
pilot: {
lbs: 190,
kg: 86,
},
firstOfficer: {
lbs: 190,
kg: 86,
},
};
export const maxZFWFreight = {
lbs: 451300,
kg: 204706,
};
export const calculateCGsFreight = (payload: PayloadFreight, fuel: Fuel): [number, number] => {
let totalMoment =
payload.empty * ArmsFreight.empty +
payload.pilot * ArmsFreight.pilot +
payload.firstOfficer * ArmsFreight.firstOfficer +
payload.engineer * ArmsFreight.engineer +
payload.upper1Left * ArmsFreight.upper1Left +
payload.upper1Right * ArmsFreight.upper1Right +
payload.upper2Left * ArmsFreight.upper2Left +
payload.upper2Right * ArmsFreight.upper2Right +
payload.upper3Left * ArmsFreight.upper3Left +
payload.upper3Right * ArmsFreight.upper3Right +
payload.upper4Left * ArmsFreight.upper4Left +
payload.upper4Right * ArmsFreight.upper4Right +
payload.lowerForward * ArmsFreight.lowerForward +
payload.lowerRear * ArmsFreight.lowerRear +
payload.leftAuxF * ArmsFreight.leftAuxF +
payload.rightAuxF * ArmsFreight.rightAuxF;
let totalWeight =
payload.empty +
payload.pilot +
payload.firstOfficer +
payload.engineer +
payload.upper1Left +
payload.upper1Right +
payload.upper2Left +
payload.upper2Right +
payload.upper3Left +
payload.upper3Right +
payload.upper4Left +
payload.upper4Right +
payload.lowerForward +
payload.lowerRear +
payload.leftAuxF +
payload.rightAuxF;
const ZFWCG = toPercentMAC(totalMoment / totalWeight);
totalMoment +=
fuel.main1 * ArmsFuel.main1 +
fuel.main3 * ArmsFuel.main3 +
fuel.main2 * ArmsFuel.main2 +
fuel.upperAux * ArmsFuel.upperAux +
fuel.lowerAux * ArmsFuel.lowerAux +
fuel.main1Tip * ArmsFuel.main1Tip +
fuel.main3Tip * ArmsFuel.main3Tip +
fuel.tail * ArmsFuel.tail +
fuel.forwardAux1 * ArmsFuel.forwardAux1 +
fuel.forwardAux2 * ArmsFuel.forwardAux2;
totalWeight +=
fuel.main1 +
fuel.main3 +
fuel.main2 +
fuel.upperAux +
fuel.lowerAux +
fuel.main1Tip +
fuel.main3Tip +
fuel.tail +
fuel.forwardAux1 +
fuel.forwardAux2;
const TOCG = toPercentMAC(totalMoment / totalWeight);
return [ZFWCG, TOCG];
};
export const getWeightsFreight = (unit: 'kg' | 'lbs') => {
const payload: PayloadFreight = {
empty: SimVar.GetSimVarValue('EMPTY WEIGHT', unit),
pilot: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:1', unit),
firstOfficer: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:2', unit),
engineer: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:3', unit),
upper1Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:4', unit),
upper1Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:5', unit),
upper2Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:6', unit),
upper2Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:7', unit),
upper3Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:8', unit),
upper3Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:9', unit),
upper4Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:10', unit),
upper4Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:11', unit),
lowerForward: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:12', unit),
lowerRear: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:13', unit),
leftAuxF: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:14', unit),
rightAuxF: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:15', unit),
};
return payload;
};
-598
View File
@@ -1,598 +0,0 @@
import { ArmsFuel, Fuel, SharedConfig, toPercentMAC } from './shared';
export interface PayloadPax {
empty: number;
pilot: number;
firstOfficer: number;
engineer: number;
cabinCrewFront: number;
business1Left: number;
business1Center: number;
business1Right: number;
business2Left: number;
business2Center: number;
business2Right: number;
economy1Left: number;
economy1Center: number;
economy1Right: number;
economy2Left: number;
economy2Center: number;
economy2Right: number;
cabinCrewRear: number;
forwardCargo: number;
rearCargo: number;
leftAuxPax: number;
rightAuxPax: number;
paxCount: {
business1: number;
business2: number;
economy1: number;
economy2: number;
total: number;
};
}
export const PaxConfig = {
// TODO: Extract from CFG at runtime.
arms: {
empty: -159.6,
pilot: 984,
firstOfficer: 984,
engineer: 960,
cabinCrewFront: 792,
business1Left: 540,
business1Center: 540,
business1Right: 540,
business2Left: 300,
business2Center: 300,
business2Right: 300,
economy1Left: -240,
economy1Center: -240,
economy1Right: -240,
economy2Left: -600,
economy2Center: -600,
economy2Right: -600,
cabinCrewRear: -660,
forwardCargo: 360,
rearCargo: -360,
leftAuxPax: 60,
rightAuxPax: 60,
},
// TODO: Make user adjustable
weights: {
pax: {
lbs: 190,
kg: 86,
},
baggage: {
lbs: 50,
kg: 23,
},
base: {
lbs: {
pilot: 190,
firstOfficer: 190,
engineer: 190,
cabinCrewFront: 380,
cabinCrewRear: 950,
total: 190 + 190 + 190 + 380 + 950,
},
kg: {
pilot: 86,
firstOfficer: 86,
engineer: 86,
cabinCrewFront: 172,
cabinCrewRear: 430,
total: 86 + 86 + 86 + 172 + 430,
},
},
},
stationMax: {
business1: 30,
business2: 45,
economy1: 86,
economy2: 137,
total: 30 + 45 + 86 + 137,
},
maxZWF: {
lbs: 400000,
kg: 181437,
},
weightToPax: (weight: number, unit: 'kg' | 'lbs') => {
return Math.round(weight / PaxConfig.weights.pax[unit]);
},
calculateCGs: (payload: PayloadPax, fuel: Fuel): [number, number] => {
let totalMoment =
payload.empty * PaxConfig.arms.empty +
payload.pilot * PaxConfig.arms.pilot +
payload.firstOfficer * PaxConfig.arms.firstOfficer +
payload.engineer * PaxConfig.arms.engineer +
payload.cabinCrewFront * PaxConfig.arms.cabinCrewFront +
payload.business1Left * PaxConfig.arms.business1Left +
payload.business1Center * PaxConfig.arms.business1Center +
payload.business1Right * PaxConfig.arms.business1Right +
payload.business2Left * PaxConfig.arms.business2Left +
payload.business2Center * PaxConfig.arms.business2Center +
payload.business2Right * PaxConfig.arms.business2Right +
payload.economy1Left * PaxConfig.arms.economy1Left +
payload.economy1Center * PaxConfig.arms.economy1Center +
payload.economy1Right * PaxConfig.arms.economy1Right +
payload.economy2Left * PaxConfig.arms.economy2Left +
payload.economy2Center * PaxConfig.arms.economy2Center +
payload.economy2Right * PaxConfig.arms.economy2Right +
payload.cabinCrewRear * PaxConfig.arms.cabinCrewRear +
payload.forwardCargo * PaxConfig.arms.forwardCargo +
payload.rearCargo * PaxConfig.arms.rearCargo +
payload.leftAuxPax * PaxConfig.arms.leftAuxPax +
payload.rightAuxPax * PaxConfig.arms.rightAuxPax;
let totalWeight =
payload.empty +
payload.pilot +
payload.firstOfficer +
payload.engineer +
payload.cabinCrewFront +
payload.business1Left +
payload.business1Center +
payload.business1Right +
payload.business2Left +
payload.business2Center +
payload.business2Right +
payload.economy1Left +
payload.economy1Center +
payload.economy1Right +
payload.economy2Left +
payload.economy2Center +
payload.economy2Right +
payload.cabinCrewRear +
payload.forwardCargo +
payload.rearCargo +
payload.leftAuxPax +
payload.rightAuxPax;
const ZFWCG = toPercentMAC(totalMoment / totalWeight);
totalMoment +=
fuel.main1 * ArmsFuel.main1 +
fuel.main3 * ArmsFuel.main3 +
fuel.main2 * ArmsFuel.main2 +
fuel.upperAux * ArmsFuel.upperAux +
fuel.lowerAux * ArmsFuel.lowerAux +
fuel.main1Tip * ArmsFuel.main1Tip +
fuel.main3Tip * ArmsFuel.main3Tip +
fuel.tail * ArmsFuel.tail +
fuel.forwardAux1 * ArmsFuel.forwardAux1 +
fuel.forwardAux2 * ArmsFuel.forwardAux2;
totalWeight +=
fuel.main1 +
fuel.main3 +
fuel.main2 +
fuel.upperAux +
fuel.lowerAux +
fuel.main1Tip +
fuel.main3Tip +
fuel.tail +
fuel.forwardAux1 +
fuel.forwardAux2;
const toCG = toPercentMAC(totalMoment / totalWeight);
return [ZFWCG, toCG];
},
generateDistribution: (
OEW: number,
business1: number,
business2: number,
economy1: number,
economy2: number,
forwardCargo: number,
rearCargo: number,
unit: 'kg' | 'lbs',
isER: boolean
): PayloadPax => {
const distribution: PayloadPax = {
empty: OEW,
...PaxConfig.weights.base[unit],
business1Left: (business1 / 3) * PaxConfig.weights.pax[unit],
business1Center: (business1 / 3) * PaxConfig.weights.pax[unit],
business1Right: (business1 / 3) * PaxConfig.weights.pax[unit],
business2Left: (business2 / 3) * PaxConfig.weights.pax[unit],
business2Center: (business2 / 3) * PaxConfig.weights.pax[unit],
business2Right: (business2 / 3) * PaxConfig.weights.pax[unit],
economy1Left: (economy1 / 3) * PaxConfig.weights.pax[unit],
economy1Center: (economy1 / 3) * PaxConfig.weights.pax[unit],
economy1Right: (economy1 / 3) * PaxConfig.weights.pax[unit],
economy2Left: (economy2 / 3) * PaxConfig.weights.pax[unit],
economy2Center: (economy2 / 3) * PaxConfig.weights.pax[unit],
economy2Right: (economy2 / 3) * PaxConfig.weights.pax[unit],
forwardCargo: forwardCargo,
rearCargo: rearCargo,
leftAuxPax: isER ? SharedConfig.erExtraWeight[unit] : 0,
rightAuxPax: isER ? SharedConfig.erExtraWeight[unit] : 0,
paxCount: {
business1,
business2,
economy1,
economy2,
total: business1 + business2 + economy1 + economy2,
},
};
return distribution;
},
distribute: (
ZFWTarget: number,
CGTarget: number,
OEW: number,
fuelLive: Fuel,
unit: 'kg' | 'lbs',
isER: boolean,
numPax?: number
) => {
const payload =
ZFWTarget -
OEW -
PaxConfig.weights.base[unit].pilot -
PaxConfig.weights.base[unit].firstOfficer -
PaxConfig.weights.base[unit].engineer -
PaxConfig.weights.base[unit].cabinCrewFront -
PaxConfig.weights.base[unit].cabinCrewRear -
(isER ? SharedConfig.erExtraWeight[unit] * 2 : 0);
let _numPax =
numPax ??
Math.max(
0,
Math.min(
PaxConfig.stationMax.total,
Math.floor(payload / (PaxConfig.weights.pax[unit] + PaxConfig.weights.baggage[unit]))
)
);
let cargo = Math.round(payload - _numPax * PaxConfig.weights.pax[unit] - _numPax * PaxConfig.weights.baggage[unit]);
let business1 = 0;
let business2 = 0;
let economy1 = 0;
let economy2 = 0;
let forwardCargo = 0;
let rearCargo = 0;
let __numPax = 0;
let count = PaxConfig.stationMax.economy2;
// initial distribution
while (_numPax > 0 && count > 0) {
if (_numPax >= 4) {
if (business1 < PaxConfig.stationMax.business1) {
business1++;
__numPax++;
}
if (business2 < PaxConfig.stationMax.business2) {
business2++;
__numPax++;
}
if (economy1 < PaxConfig.stationMax.economy1) {
economy1++;
__numPax++;
}
if (economy2 < PaxConfig.stationMax.economy2) {
economy2++;
__numPax++;
}
} else if (_numPax === 3) {
if (business2 < PaxConfig.stationMax.business2) {
business2++;
__numPax++;
}
if (economy1 < PaxConfig.stationMax.economy1) {
economy1++;
__numPax++;
}
if (economy2 < PaxConfig.stationMax.economy2) {
economy2++;
__numPax++;
}
} else if (_numPax === 2) {
if (economy1 < PaxConfig.stationMax.economy1) {
economy1++;
__numPax++;
}
if (economy2 < PaxConfig.stationMax.economy2) {
economy2++;
__numPax++;
}
} else if (_numPax === 1) {
if (economy2 < PaxConfig.stationMax.economy2) {
economy2++;
__numPax++;
}
}
_numPax -= __numPax;
if (__numPax % 2 === 0) {
forwardCargo += (__numPax / 2) * PaxConfig.weights.baggage[unit];
rearCargo += (__numPax / 2) * PaxConfig.weights.baggage[unit];
} else {
__numPax--;
forwardCargo += (__numPax / 2 + 1) * PaxConfig.weights.baggage[unit];
rearCargo += (__numPax / 2) * PaxConfig.weights.baggage[unit];
}
__numPax = 0;
count--;
}
count = SharedConfig.stationMax.forward[unit];
while (cargo > 0 && count > 0) {
if (forwardCargo < SharedConfig.stationMax.forward[unit]) {
forwardCargo++;
cargo--;
}
if (rearCargo < SharedConfig.stationMax.rear[unit]) {
rearCargo++;
cargo--;
}
count--;
}
// Refinement pax
count = PaxConfig.stationMax.total;
while (count > 0) {
const distribution = PaxConfig.generateDistribution(
OEW,
business1,
business2,
economy1,
economy2,
forwardCargo,
rearCargo,
unit,
isER
);
const _CGs = PaxConfig.calculateCGs(distribution, fuelLive);
// in front of target
if (_CGs[0] < CGTarget - SharedConfig.CGLimits.tolerance) {
if (business1 > 0) {
business1--;
} else if (business2 > 0) {
business2--;
} else if (economy1 > 0) {
economy1--;
} else {
break;
}
if (economy2 < PaxConfig.stationMax.economy2) {
economy2++;
} else if (economy1 < PaxConfig.stationMax.economy1) {
economy1++;
} else if (business2 < PaxConfig.stationMax.business2) {
business2++;
} else {
business1++;
}
}
// behind target
else if (_CGs[0] > CGTarget + SharedConfig.CGLimits.tolerance) {
if (economy2 > 0) {
economy2--;
} else if (economy1 > 0) {
economy1--;
} else if (business2 > 0) {
business2--;
} else {
break;
}
if (business1 < PaxConfig.stationMax.business1) {
business1++;
} else if (business2 < PaxConfig.stationMax.business2) {
business2++;
} else if (economy1 < PaxConfig.stationMax.economy1) {
economy1++;
} else {
economy2++;
}
} else {
break;
}
count--;
}
// Refinement cargo
count = SharedConfig.stationMax.forward[unit] + SharedConfig.stationMax.rear[unit];
while (count > 0) {
const distribution = PaxConfig.generateDistribution(
OEW,
business1,
business2,
economy1,
economy2,
forwardCargo,
rearCargo,
unit,
isER
);
const _CGs = PaxConfig.calculateCGs(distribution, fuelLive);
// in front of target
if (_CGs[0] < CGTarget - SharedConfig.CGLimits.tolerance) {
if (forwardCargo > 0 && rearCargo < SharedConfig.stationMax.rear[unit]) {
if (
forwardCargo > PaxConfig.weights.baggage[unit] &&
rearCargo < SharedConfig.stationMax.forward[unit] - PaxConfig.weights.baggage[unit]
) {
forwardCargo -= PaxConfig.weights.baggage[unit];
rearCargo += PaxConfig.weights.baggage[unit];
} else {
forwardCargo--;
rearCargo++;
}
} else {
break;
}
}
// behind target
else if (_CGs[0] > CGTarget + SharedConfig.CGLimits.tolerance) {
if (rearCargo > 0 && forwardCargo < SharedConfig.stationMax.forward[unit]) {
if (
rearCargo > PaxConfig.weights.baggage[unit] &&
forwardCargo < SharedConfig.stationMax.rear[unit] - PaxConfig.weights.baggage[unit]
) {
rearCargo -= PaxConfig.weights.baggage[unit];
forwardCargo += PaxConfig.weights.baggage[unit];
} else {
rearCargo--;
forwardCargo++;
}
} else {
break;
}
} else {
break;
}
count--;
}
const distribution = PaxConfig.generateDistribution(
OEW,
business1,
business2,
economy1,
economy2,
forwardCargo,
rearCargo,
unit,
isER
);
return distribution;
},
// SIM access
getWeights: (unit: 'kg' | 'lbs') => {
const payload: PayloadPax = {
empty: SimVar.GetSimVarValue('EMPTY WEIGHT', unit),
pilot: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:1', unit),
firstOfficer: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:2', unit),
engineer: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:3', unit),
cabinCrewFront: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:4', unit),
business1Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:5', unit),
business1Center: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:6', unit),
business1Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:7', unit),
business2Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:8', unit),
business2Center: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:9', unit),
business2Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:10', unit),
economy1Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:11', unit),
economy1Center: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:12', unit),
economy1Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:13', unit),
economy2Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:14', unit),
economy2Center: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:15', unit),
economy2Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:16', unit),
cabinCrewRear: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:17', unit),
forwardCargo: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:18', unit),
rearCargo: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:19', unit),
leftAuxPax: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:20', unit),
rightAuxPax: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:21', unit),
paxCount: {
business1: 0,
business2: 0,
economy1: 0,
economy2: 0,
total: 0,
},
};
return payload;
},
setBaseWeight: (unit: 'kg' | 'lbs', isER: boolean) => {
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:1', unit, PaxConfig.weights.base[unit].pilot); // Pilot
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:2', unit, PaxConfig.weights.base[unit].firstOfficer); // First officer
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:3', unit, PaxConfig.weights.base[unit].engineer); // Engineer
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:4', unit, PaxConfig.weights.base[unit].cabinCrewFront); // Cabin Crew front
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:17', unit, PaxConfig.weights.base[unit].cabinCrewRear); // Cabin Crew rear
if (isER) {
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:20', unit, SharedConfig.erExtraWeight[unit]); // Forward Aux 1
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:21', unit, SharedConfig.erExtraWeight[unit]); // Forward Aux 2
}
},
setWeights: (payload: PayloadPax, unit: 'kg' | 'lbs') => {
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:4', unit, payload.cabinCrewFront); // Cabin Crew front
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:5', unit, payload.business1Left); // Business 1L
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:6', unit, payload.business1Center); // Business 1C
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:7', unit, payload.business1Right); // Business 1R
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:8', unit, payload.business2Left); // Business 2L
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:9', unit, payload.business2Center); // Business 2C
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:10', unit, payload.business2Right); // Business 2R
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:11', unit, payload.economy1Left); // Economy 1L
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:12', unit, payload.economy1Center); // Economy 1C
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:13', unit, payload.economy1Right); // Economy 1R
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:14', unit, payload.economy2Left); // Economy 2L
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:15', unit, payload.economy2Center); // Economy 2C
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:16', unit, payload.economy2Right); // Economy 2R
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:17', unit, payload.cabinCrewRear); //Cabin Crew rear
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:18', unit, payload.forwardCargo); // Forward Cargo
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:19', unit, payload.rearCargo); // Rear Cargo
},
setWeightsProgressive: (payload: PayloadPax, paxNum: number, cargoPercent: number, unit: 'kg' | 'lbs') => {
const business1Scalar = Math.min(1, paxNum / payload.paxCount.business1);
paxNum -= payload.paxCount.business1 * business1Scalar;
const business2Scalar = Math.min(1, paxNum / payload.paxCount.business2);
paxNum -= payload.paxCount.business2 * business2Scalar;
const economy1Scalar = Math.min(1, paxNum / payload.paxCount.economy1);
paxNum -= payload.paxCount.economy1 * economy1Scalar;
const economy2Scalar = Math.min(1, paxNum / payload.paxCount.economy2);
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:4', unit, payload.cabinCrewFront); // Cabin Crew front
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:5', unit, payload.business1Left * business1Scalar); // Business 1L
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:6', unit, payload.business1Center * business1Scalar); // Business 1C
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:7', unit, payload.business1Right * business1Scalar); // Business 1R
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:8', unit, payload.business2Left * business2Scalar); // Business 2L
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:9', unit, payload.business2Center * business2Scalar); // Business 2C
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:10', unit, payload.business2Right * business2Scalar); // Business 2R
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:11', unit, payload.economy1Left * economy1Scalar); // Economy 1L
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:12', unit, payload.economy1Center * economy1Scalar); // Economy 1C
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:13', unit, payload.economy1Right * economy1Scalar); // Economy 1R
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:14', unit, payload.economy2Left * economy2Scalar); // Economy 2L
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:15', unit, payload.economy2Center * economy2Scalar); // Economy 2C
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:16', unit, payload.economy2Right * economy2Scalar); // Economy 2R
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:17', unit, payload.cabinCrewRear); //Cabin Crew rear
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:18', unit, payload.forwardCargo * (cargoPercent / 100)); // Forward Cargo
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:19', unit, payload.rearCargo * (cargoPercent / 100)); // Rear Cargo
},
unload: (unit: 'kg' | 'lbs', isER: boolean) => {
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:1', unit, 0); // Pilot
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:2', unit, 0); // First officer
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:3', unit, 0); // Engineer
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:4', unit, 0); // Cabin Crew front
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:17', unit, 0); // Cabin Crew rear
if (!isER) {
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:20', unit, 0); // Forward Aux 1
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:21', unit, 0); // Forward Aux 2
}
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:4', unit, 0); // Cabin Crew front
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:5', unit, 0); // Business 1L
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:6', unit, 0); // Business 1C
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:7', unit, 0); // Business 1R
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:8', unit, 0); // Business 2L
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:9', unit, 0); // Business 2C
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:10', unit, 0); // Business 2R
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:11', unit, 0); // Economy 1L
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:12', unit, 0); // Economy 1C
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:13', unit, 0); // Economy 1R
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:14', unit, 0); // Economy 2L
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:15', unit, 0); // Economy 2C
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:16', unit, 0); // Economy 2R
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:17', unit, 0); //Cabin Crew rear
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:18', unit, 0); // Forward Cargo
SimVar.SetSimVarValue('PAYLOAD STATION WEIGHT:19', unit, 0); // Rear Cargo
},
};
@@ -1,166 +0,0 @@
export interface Fuel {
main1: number;
main3: number;
main2: number;
upperAux: number;
lowerAux: number;
main1Tip: number;
main3Tip: number;
tail: number;
forwardAux1: number;
forwardAux2: number;
}
// TODO: Extract from CFG at runtime.
export const ArmsFuel = {
main1: -240,
main3: -240,
main2: 120,
upperAux: 0,
lowerAux: 0,
main1Tip: -468,
main3Tip: -468,
tail: -840,
forwardAux1: 60,
forwardAux2: 60,
};
// TODO: Extract following four from CFG at runtime
const rootChord = 34.68;
const wingSpan = 170.5;
const wingArea = 3648;
const quarterMAC = -165; //aero_center
const tipChord = (2 * wingArea) / wingSpan - rootChord;
const taperRatio = tipChord / rootChord;
const MAC = (2 / 3) * rootChord * ((1 + taperRatio + taperRatio ** 2) / (1 + taperRatio)) * 12;
const LEMAC = quarterMAC + (1 / 4) * MAC;
export const toPercentMAC = (positionInInches: number) => {
return ((positionInInches - LEMAC) / MAC) * -100;
};
export const SharedConfig = {
stationMax: {
//PMC pallet due to 104in door
forward: {
lbs: 6 * 15000,
kg: 6 * 6804,
},
//LD3s due to 70in door
rear: {
lbs: 14 * 3500,
kg: 14 * 1588,
},
},
maxTOW: {
er: {
lbs: 630500,
kg: 285990,
},
norm: {
lbs: 625500,
kg: 283722,
},
},
maxFuel: {
er: {
lbs: 282619,
kg: 128193,
},
norm: {
lbs: 256207,
kg: 116213,
},
},
erExtraWeight: {
lbs: 200,
kg: 91,
},
CGLimits: {
min: 12,
default: 20.5,
max: 34,
tolerance: 0.05,
},
};
export const initialPayload = {
empty: 0,
pilot: 0,
firstOfficer: 0,
engineer: 0,
cabinCrewFront: 0,
business1Left: 0,
business1Center: 0,
business1Right: 0,
business2Left: 0,
business2Center: 0,
business2Right: 0,
economy1Left: 0,
economy1Center: 0,
economy1Right: 0,
economy2Left: 0,
economy2Center: 0,
economy2Right: 0,
cabinCrewRear: 0,
forwardCargo: 0,
rearCargo: 0,
leftAuxPax: 0,
rightAuxPax: 0,
upper1Left: 0,
upper1Right: 0,
upper2Left: 0,
upper2Right: 0,
upper3Left: 0,
upper3Right: 0,
upper4Left: 0,
upper4Right: 0,
lowerForward: 0,
lowerRear: 0,
leftAuxF: 0,
rightAuxF: 0,
paxCount: {
business1: 0,
business2: 0,
economy1: 0,
economy2: 0,
total: 0,
},
};
export const initialFuel = {
main1: 0,
main3: 0,
main2: 0,
upperAux: 0,
lowerAux: 0,
main1Tip: 0,
main3Tip: 0,
tail: 0,
forwardAux1: 0,
forwardAux2: 0,
};
// SIM access
export const getFuel = (unit: 'kg' | 'lbs') => {
const conversion = SimVar.GetSimVarValue('FUEL WEIGHT PER GALLON', unit);
const fuel = {
main1: SimVar.GetSimVarValue('FUEL TANK LEFT MAIN QUANTITY', 'gal') * conversion,
main3: SimVar.GetSimVarValue('FUEL TANK RIGHT MAIN QUANTITY', 'gal') * conversion,
main2: SimVar.GetSimVarValue('FUEL TANK CENTER QUANTITY', 'gal') * conversion,
upperAux: SimVar.GetSimVarValue('FUEL TANK CENTER2 QUANTITY', 'gal') * conversion,
lowerAux: SimVar.GetSimVarValue('FUEL TANK CENTER3 QUANTITY', 'gal') * conversion,
main1Tip: SimVar.GetSimVarValue('FUEL TANK LEFT TIP QUANTITY', 'gal') * conversion,
main3Tip: SimVar.GetSimVarValue('FUEL TANK RIGHT TIP QUANTITY', 'gal') * conversion,
tail: SimVar.GetSimVarValue('FUEL TANK EXTERNAL1 QUANTITY', 'gal') * conversion,
forwardAux1: SimVar.GetSimVarValue('FUEL TANK LEFT AUX QUANTITY', 'gal') * conversion,
forwardAux2: SimVar.GetSimVarValue('FUEL TANK RIGHT AUX QUANTITY', 'gal') * conversion,
};
return fuel;
};
+20 -6
View File
@@ -1,6 +1,20 @@
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_COMM_BUS_WASM_CALL = 'COMM_BUS_WASM_CALLBACK';
export const TFDI_SIMBRIEF_USERNAME_CALL = 'requestSimBriefUsername';
export const TFDI_SIMBRIEF_USERNAME_EVENT = 'receiveSimBriefUsername';
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';
export const CG_ADJUST = 0.05;
export const GSX_SERVICE_CALLED = 4;
export const GSX_SERVICE_ACTIVE = 5;
export const GSX_SERVICE_FINISHED = 6;
export const MODE_SB_SET = 0;
export const MODE_ZFW_SET = 1;
export const MODE_STATION_SET = 2;
export const MODE_LOAD_SET = 3;
export const MODE_UNLOAD_SET = 4;
export const MODE_OPTIONS_SET = 5;
+1 -3
View File
@@ -32,9 +32,7 @@
class="ingameUiFrame panelInvisible"
content-fit="true"
>
<virtual-scroll direction="y">
<div id="react" style="display: flex"></div>
</virtual-scroll>
<div id="react" style="display: flex"></div>
</ingame-ui>
</tfdidesign-md11-load-manager-panel>
<script src="index.js" type="text/javascript"></script>
+1
View File
@@ -16,6 +16,7 @@ class TFDiDesignMD11LoadManagerPanel extends TemplateElement {
const container = document.getElementById('react');
if (container) {
console.log('Starting React');
const root = createRoot(container);
root.render(createElement(App, { commBus: this.commBus }));
}
@@ -0,0 +1,102 @@
interface WASMData {
GSX: GSX;
userData: UserData;
options: Options;
sbPlanned: {
ZFW: number;
GW: number;
};
}
export interface WASMDataPax extends WASMData {
livePayload: LivePayloadPax;
targetPayload: TargetPayloadPax;
limits: LimitsPax;
}
export interface WASMDataF extends WASMData {
livePayload: LivePayloadF;
targetPayload: TargetPayloadF;
limits: LimitsF;
}
interface TargetPayload {
CGTarget: number;
ZFWCG: number;
TOCG: number;
total: number;
}
interface TargetPayloadPax extends TargetPayload {
empty: number;
crew: number;
business1: number;
business2: number;
economy1: number;
economy2: number;
forwardCargo: number;
rearCargo: number;
}
interface LivePayloadPax extends TargetPayloadPax {
fuel: number;
}
interface TargetPayloadF extends TargetPayload {
empty: number;
crew: number;
upper1: number;
upper2: number;
upper3: number;
upper4: number;
lowerForward: number;
lowerRear: number;
}
interface LivePayloadF extends TargetPayloadF {
fuel: number;
}
interface GSX {
boardingState: number;
deboardingState: 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;
}
interface Options {
GSXSync: boolean;
paxWeight: number;
bagWeight: number;
}
@@ -0,0 +1,9 @@
export type LoadingState = 'preview' | 'loaded';
export interface SimBrief {
plannedZFW: number;
plannedGW: number;
pax: number;
cargo: number;
fuel: number;
}
-10
View File
@@ -1,10 +0,0 @@
import '@mui/material';
declare module '@mui/material/styles' {
interface Theme {
screenHeight: number;
}
interface ThemeOptions {
screenHeight: number;
}
}
-11
View File
@@ -1,11 +0,0 @@
import { CSSProperties, DOMAttributes, ReactNode } from 'react';
type CustomElement<T> = Partial<T & DOMAttributes<T> & { children: ReactNode }>;
declare global {
namespace JSX {
interface IntrinsicElements {
['virtual-scroll']: CustomElement<{ class?: string; direction: 'x' | 'y'; style?: CSSProperties }>;
}
}
}
@@ -1,6 +1,3 @@
import { PaxConfig } from '../configs/pax';
import { SharedConfig } from '../configs/shared';
const getSimBriefFlightPlan = async (simBriefUsername: string) => {
const flightPlanURL = `https://www.simbrief.com/api/xml.fetcher.php?username=${simBriefUsername}&json=1`;
let response: Response;
@@ -8,6 +5,7 @@ const getSimBriefFlightPlan = async (simBriefUsername: string) => {
try {
response = await fetch(flightPlanURL);
success = true;
//eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
response = e.response;
}
@@ -19,9 +17,10 @@ const getSimBriefFlightPlan = async (simBriefUsername: string) => {
export const ImportFlightPlan = async (
username: string,
config: typeof PaxConfig,
unit: 'kg' | 'lbs',
isER: boolean
maxZFW: number,
maxTOW: number,
maxFuel: number,
isImperial: boolean
) => {
const flightPlan = await getSimBriefFlightPlan(username);
if (!flightPlan.success) {
@@ -41,23 +40,17 @@ export const ImportFlightPlan = async (
}
let convFactor = 1;
if (data.params.units === 'kgs' && unit === 'lbs') convFactor = 2.20462262185;
if (data.params.units === 'lbs' && unit === 'kg') convFactor = 1 / 2.20462262185;
if (data.params.units === 'kgs' && isImperial) convFactor = 2.20462262185;
if (data.params.units === 'lbs' && !isImperial) convFactor = 1 / 2.20462262185;
return {
type: 'data',
message: {
plannedZFW: Math.min(config.maxZWF[unit], Math.round(data.weights.est_zfw * convFactor)),
plannedGW: Math.min(
isER ? SharedConfig.maxTOW.er[unit] : SharedConfig.maxTOW.norm[unit],
Math.round(data.weights.est_ramp * convFactor)
),
plannedZFW: Math.min(maxZFW, Math.round(data.weights.est_zfw * convFactor)),
plannedGW: Math.min(maxTOW, Math.round(data.weights.est_ramp * convFactor)),
pax: data.weights.pax_count_actual,
cargo: Math.round(data.weights.freight_added * convFactor),
fuel: Math.min(
isER ? SharedConfig.maxFuel.er[unit] : SharedConfig.maxFuel.norm[unit],
Math.round(data.fuel.plan_ramp * convFactor)
),
fuel: Math.min(maxFuel, Math.round(data.fuel.plan_ramp * convFactor)),
},
};
};
+134
View File
@@ -0,0 +1,134 @@
import {
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
MODE_LOAD_SET,
MODE_OPTIONS_SET,
MODE_SB_SET,
MODE_STATION_SET,
MODE_UNLOAD_SET,
MODE_ZFW_SET,
} from '../constants';
import { SimBrief } from '../types/general';
export const loadAircraft = () => {
Coherent.call(
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
JSON.stringify({
mode: MODE_LOAD_SET,
})
);
};
export const unloadAircraft = () => {
Coherent.call(
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
JSON.stringify({
mode: MODE_UNLOAD_SET,
})
);
};
export const inRangeOf = (value: number, target: number, tolerance: number = 10) => {
return Math.abs(value - target) < tolerance;
};
export const CoherentCallZFWEntry = (ZFWTarget: number, CGTarget: number) => {
Coherent.call(
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
JSON.stringify({
mode: MODE_ZFW_SET,
ZFWTarget: ZFWTarget,
CGTarget: CGTarget,
})
);
};
export const CoherentCallStationEntryPax = (
business1: number,
business2: number,
economy1: number,
economy2: number,
forwardCargo: number,
rearCargo: number
) => {
Coherent.call(
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
JSON.stringify({
mode: MODE_STATION_SET,
business1,
business2,
economy1,
economy2,
forwardCargo,
rearCargo,
})
);
};
export const CoherentCallStationEntryF = (
upper1: number,
upper2: number,
upper3: number,
upper4: number,
lowerForward: number,
lowerRear: number
) => {
Coherent.call(
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
JSON.stringify({
mode: MODE_STATION_SET,
upper1,
upper2,
upper3,
upper4,
lowerForward,
lowerRear,
})
);
};
export const CoherentCallSBEntryPax = (CGTarget: number, SBPlan?: SimBrief) => {
const payload = {
mode: MODE_SB_SET,
cargo: SBPlan?.cargo,
numPax: SBPlan ? parseInt(SBPlan.pax as unknown as string) : undefined,
CGTarget: CGTarget,
plannedZFW: SBPlan?.plannedZFW,
plannedGW: SBPlan?.plannedGW,
};
const string = JSON.stringify(payload);
Coherent.call(COHERENT_COMM_BUS_WASM_CALL, COMM_BUS_UPDATE_TARGET_EVENT, string);
};
export const CoherentCallSBEntryF = (CGTarget: number, SBPlan?: SimBrief) => {
Coherent.call(
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
JSON.stringify({
mode: MODE_SB_SET,
cargo: SBPlan?.cargo,
CGTarget: CGTarget,
plannedZFW: SBPlan?.plannedZFW,
plannedGW: SBPlan?.plannedGW,
})
);
};
export const CoherentCallOptionsSet = (GSXSync?: boolean, paxWeight?: number, bagWeight?: number) => {
Coherent.call(
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
JSON.stringify({
mode: MODE_OPTIONS_SET,
GSXSync,
paxWeight,
bagWeight,
})
);
};
+306
View File
@@ -0,0 +1,306 @@
#include "freighter.h"
// ZFW Entry
void distribute(fPayloadData_t* const targetPayload, const FuelData_t* const fuel, const double ZFWTarget, const UserData_t* const userData) {
// Find payload, num pax and extra cargo
double payload = ZFWTarget - targetPayload->empty - targetPayload->pilot - targetPayload->firstOfficer - targetPayload->engineer -
targetPayload->leftAux - targetPayload->rightAux;
unsigned int cargo = round(payload);
distribute(targetPayload, fuel, cargo, userData);
}
// SimBrief Entry
void distribute(fPayloadData_t* const targetPayload, const FuelData_t* const fuel, unsigned int cargo, const UserData_t* const userData) {
// Clear
targetPayload->stations.upper1 = targetPayload->stations.upper2 = targetPayload->stations.upper3 = targetPayload->stations.upper4 =
targetPayload->stations.total = 0;
targetPayload->lowerForward = targetPayload->lowerRear = 0;
unsigned short _cargo = 0;
unsigned int count = MAX_UPPER_CARGO(userData->isImperial) * 4 + MAX_FRONT_CARGO(userData->isImperial) + MAX_REAR_CARGO(userData->isImperial, userData->isER);
// Initial distributiob
while (cargo > 0 && count > 0) {
if (cargo >= 6) {
if (targetPayload->stations.upper1 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper1++;
_cargo++;
}
if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper2++;
_cargo++;
}
if (targetPayload->stations.upper3 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper3++;
_cargo++;
}
if (targetPayload->stations.upper4 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper4++;
_cargo++;
}
if (targetPayload->lowerForward < MAX_FRONT_CARGO(userData->isImperial)) {
targetPayload->lowerForward++;
_cargo++;
}
if (targetPayload->lowerRear < MAX_REAR_CARGO(userData->isImperial, userData->isER)) {
targetPayload->lowerRear++;
_cargo++;
}
} else if (cargo == 5) {
if (targetPayload->stations.upper1 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper1++;
_cargo++;
}
if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper2++;
_cargo++;
}
if (targetPayload->stations.upper3 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper3++;
_cargo++;
}
if (targetPayload->stations.upper4 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper4++;
_cargo++;
}
if (targetPayload->lowerForward < MAX_FRONT_CARGO(userData->isImperial)) {
targetPayload->lowerForward++;
_cargo++;
}
}
else if (cargo == 4) {
if (targetPayload->stations.upper1 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper1++;
_cargo++;
}
if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper2++;
_cargo++;
}
if (targetPayload->stations.upper3 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper3++;
_cargo++;
}
if (targetPayload->stations.upper4 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper4++;
_cargo++;
}
}
else if (cargo == 3) {
if (targetPayload->stations.upper1 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper1++;
_cargo++;
}
if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper2++;
_cargo++;
}
if (targetPayload->stations.upper3 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper3++;
_cargo++;
}
}
else if (cargo == 2) {
if (targetPayload->stations.upper1 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper1++;
_cargo++;
}
if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper2++;
_cargo++;
}
}
else if (cargo == 1) {
if (targetPayload->stations.upper1 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper1++;
_cargo++;
}
}
cargo -= _cargo;
targetPayload->stations.total += _cargo;
_cargo = 0;
count--;
}
// Refinement
count = MAX_UPPER_CARGO(userData->isImperial) * 4 + MAX_FRONT_CARGO(userData->isImperial) + MAX_REAR_CARGO(userData->isImperial, userData->isER);
while (count > 0) {
generatePayload(targetPayload, userData->isImperial);
calculateCGs(targetPayload, fuel, &targetPayload->ZFWCG, &targetPayload->TOCG, userData->isImperial);
// in front of target
if (targetPayload->ZFWCG < targetPayload->CGTarget - CG_TOLERANCE) {
if (targetPayload->stations.upper1 > 0) {
targetPayload->stations.upper1--;
}
else if (targetPayload->stations.upper2 > 0) {
targetPayload->stations.upper2--;
}
else if (targetPayload->stations.upper3 > 0) {
targetPayload->stations.upper3--;
}
else {
break;
}
if (targetPayload->stations.upper4 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper4++;
}
else if (targetPayload->stations.upper3 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper3++;
}
else if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper2++;
}
else {
targetPayload->stations.upper1++;
}
}
// behind target
else if (targetPayload->ZFWCG > targetPayload->CGTarget + CG_TOLERANCE) {
if (targetPayload->stations.upper4 > 0) {
targetPayload->stations.upper4--;
}
else if (targetPayload->stations.upper3 > 0) {
targetPayload->stations.upper3--;
}
else if (targetPayload->stations.upper2 > 0) {
targetPayload->stations.upper2--;
}
else {
break;
}
if (targetPayload->stations.upper1 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper1++;
}
else if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper2++;
}
else if (targetPayload->stations.upper3 < MAX_UPPER_CARGO(userData->isImperial)) {
targetPayload->stations.upper3++;
}
else {
targetPayload->stations.upper4++;
}
}
else {
break;
}
count--;
}
// Refinement cargo
count = MAX_FRONT_CARGO(userData->isImperial) + MAX_REAR_CARGO(userData->isImperial, userData->isER);
while (count > 0) {
generatePayload(targetPayload, userData->isImperial);
calculateCGs(targetPayload, fuel, &targetPayload->ZFWCG, &targetPayload->TOCG, userData->isImperial);
// in front of target
if (targetPayload->ZFWCG < targetPayload->CGTarget - CG_TOLERANCE) {
if (targetPayload->lowerForward > 0 && targetPayload->lowerRear < MAX_REAR_CARGO(userData->isImperial, userData->isER)) {
targetPayload->lowerForward--;
targetPayload->lowerRear++;
}
else {
break;
}
}
// behind target
else if (targetPayload->ZFWCG > targetPayload->CGTarget + CG_TOLERANCE) {
if (targetPayload->lowerRear > 0 && targetPayload->lowerForward < MAX_FRONT_CARGO(userData->isImperial)) {
targetPayload->lowerRear--;
targetPayload->lowerForward++;
}
else {
break;
}
}
else {
break;
}
count--;
}
}
// Updates pax stations with their respective weights
// Used internally and used for Station Entry (pax only, cargo is set directly)
void generatePayload(fPayloadData_t* const targetPayload, const bool isImperial) {
targetPayload->upper1Left = targetPayload->upper1Right = (targetPayload->stations.upper1 / 2.0);
targetPayload->upper2Left = targetPayload->upper2Right = (targetPayload->stations.upper2 / 2.0);
targetPayload->upper3Left = targetPayload->upper3Right = (targetPayload->stations.upper3 / 2.0);
targetPayload->upper4Left = targetPayload->upper4Right = (targetPayload->stations.upper4 / 2.0);
targetPayload->total = targetPayload->empty + targetPayload->pilot + targetPayload->firstOfficer + targetPayload->engineer + targetPayload->upper1Left +
targetPayload->upper1Right + targetPayload->upper2Left + targetPayload->upper2Right + targetPayload->upper3Left +
targetPayload->upper3Right + targetPayload->upper4Left + targetPayload->upper4Right + targetPayload->lowerForward +
targetPayload->lowerRear + targetPayload->leftAux + targetPayload->rightAux;
}
// Normalise to Pounds
// MANDATORY BEFORE SETTING WEIGHTS
// USE ON COPY OF GLOBAL STATE ONLY
void normalisePayload(fPayloadData_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->upper1Left = TO_POUNDS(isImperial, targetPayload->upper1Left);
targetPayload->upper1Right = TO_POUNDS(isImperial, targetPayload->upper1Right);
targetPayload->upper2Left = TO_POUNDS(isImperial, targetPayload->upper2Left);
targetPayload->upper2Right = TO_POUNDS(isImperial, targetPayload->upper2Right);
targetPayload->upper3Left = TO_POUNDS(isImperial, targetPayload->upper3Left);
targetPayload->upper3Right = TO_POUNDS(isImperial, targetPayload->upper3Right);
targetPayload->upper4Left = TO_POUNDS(isImperial, targetPayload->upper4Left);
targetPayload->upper4Right = TO_POUNDS(isImperial, targetPayload->upper4Right);
targetPayload->lowerForward = TO_POUNDS(isImperial, targetPayload->lowerForward);
targetPayload->lowerRear = TO_POUNDS(isImperial, targetPayload->lowerRear);
targetPayload->leftAux = TO_POUNDS(isImperial, targetPayload->leftAux);
targetPayload->rightAux = TO_POUNDS(isImperial, targetPayload->rightAux);
}
void calculateCGs(const fPayloadData_t* const targetPayload, const FuelData_t* const fuel, double* const ZFWCG, double* const TOCG, const bool isImperial) {
fPayloadData_t localPayload = {};
memcpy(&localPayload, targetPayload, sizeof(localPayload));
normalisePayload(&localPayload, isImperial);
double totalMoment = localPayload.empty * ARM_EMPTY + localPayload.pilot * ARM_PILOT + localPayload.firstOfficer * ARM_FIRST_OFFICER +
localPayload.engineer * ARM_ENGINEER + localPayload.upper1Left * ARM_F_UPPER1_LEFT + localPayload.upper1Right * ARM_F_UPPER1_RIGHT +
localPayload.upper2Left * ARM_F_UPPER2_LEFT + localPayload.upper2Right * ARM_F_UPPER2_RIGHT + localPayload.upper3Left * ARM_F_UPPER3_LEFT +
localPayload.upper3Right * ARM_F_UPPER3_RIGHT + localPayload.upper4Left * ARM_F_UPPER4_LEFT + localPayload.upper4Right * ARM_F_UPPER4_RIGHT +
localPayload.lowerForward * ARM_FORWARD_CARGO + localPayload.lowerRear * ARM_REAR_CARGO + localPayload.leftAux * ARM_LEFT_AUX +
localPayload.rightAux * ARM_RIGHT_AUX;
double totalWeight = localPayload.empty + localPayload.pilot + localPayload.firstOfficer + localPayload.engineer + localPayload.upper1Left +
localPayload.upper1Right + localPayload.upper2Left + localPayload.upper2Right + localPayload.upper3Left + localPayload.upper3Right +
localPayload.upper4Left + localPayload.upper4Right + localPayload.lowerForward + localPayload.lowerRear + localPayload.leftAux +
localPayload.rightAux;
*ZFWCG = TO_PERCENT_MAC(totalMoment / totalWeight);
totalMoment += fuel->main1 * ARM_MAIN1 + fuel->main3 * ARM_MAIN3 + fuel->main2 * ARM_MAIN2 + fuel->upperAux * ARM_UPPER_AUX +
fuel->lowerAux * ARM_LOWER_AUX + fuel->main1Tip * ARM_MAIN1_TIP + fuel->main3Tip * ARM_MAIN3_TIP +
fuel->tail * ARM_TAIL + fuel->forwardAux1 * ARM_FORWARD_AUX1 + fuel->forwardAux2 * ARM_FORWARD_AUX2;
totalWeight += fuel->total;
*TOCG = TO_PERCENT_MAC(totalMoment / totalWeight);
}
void load(const fPayloadData_t* const targetPayload, const HANDLE simConnect, const bool isImperial) {
fPayloadData_t localPayload = {};
memcpy(&localPayload, targetPayload, sizeof(localPayload));
normalisePayload(&localPayload, isImperial);
SimConnect_SetDataOnSimObject(simConnect, DATA_DEFINITION_PAYLOAD_F, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(fPayloadDataSet_t), &localPayload);
}
void unloadF(const HANDLE simConnect, const bool isER) {
fPayloadData_t localPayload = {};
localPayload.leftAux = localPayload.rightAux = isER ? AUX_WEIGHT(true) : 0;
localPayload.pilot = localPayload.firstOfficer = localPayload.engineer = PILOT_WEIGHT(true);
SimConnect_SetDataOnSimObject(simConnect, DATA_DEFINITION_PAYLOAD_F, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(fPayloadDataSet_t), &localPayload);
}
+111
View File
@@ -0,0 +1,111 @@
#pragma once
#ifndef __INTELLISENSE__
# define MODULE_EXPORT __attribute__( ( visibility( "default" ) ) )
# define MODULE_WASM_MODNAME(mod) __attribute__((import_module(mod)))
#else
# define MODULE_EXPORT
# define MODULE_WASM_MODNAME(mod)
# define __attribute__(x)
# define __restrict__
#endif
/********************************* Headers *********************************/
// MSFS headers
#include <MSFS/MSFS_WindowsTypes.h>
#include <SimConnect.h>
// C headers
#include <math.h>
// C++ headers
#include <algorithm>
// Own headers
#include "shared.h"
/******************************** Constants ********************************/
//PMC pallet due to 104in door
#define MAX_FRONT_CARGO(IS_IMPERIAL) ((IS_IMPERIAL) ? (6.0 * 15000.0) : (6.0 * 6804.0))
// Max ZFW
#define MAX_F_ZFW(IS_IMPERIAL) ((IS_IMPERIAL) ? (451300) : (204706))
// Arms
#define ARM_F_UPPER1_LEFT 660
#define ARM_F_UPPER1_RIGHT 660
#define ARM_F_UPPER2_LEFT 240
#define ARM_F_UPPER2_RIGHT 240
#define ARM_F_UPPER3_LEFT -240
#define ARM_F_UPPER3_RIGHT -240
#define ARM_F_UPPER4_LEFT -600
#define ARM_F_UPPER4_RIGHT -600
/***************************** Data structures *****************************/
typedef struct {
// SimConnect mapped
double pilot;
double firstOfficer;
double engineer;
double upper1Left;
double upper1Right;
double upper2Left;
double upper2Right;
double upper3Left;
double upper3Right;
double upper4Left;
double upper4Right;
double lowerForward;
double lowerRear;
double leftAux;
double rightAux;
// Additional properties
double empty;
double total;
double CGTarget;
double ZFWCG;
double TOCG;
struct stations {
unsigned int upper1;
unsigned int upper2;
unsigned int upper3;
unsigned int upper4;
unsigned int total;
} stations;
struct sbPlanned {
double ZFW;
double GW;
unsigned int cargo;
} sbPlanned;
} fPayloadData_t;
typedef struct {
// SimConnect mapped
double pilot;
double firstOfficer;
double engineer;
double upper1Left;
double upper1Right;
double upper2Left;
double upper2Right;
double upper3Left;
double upper3Right;
double upper4Left;
double upper4Right;
double lowerForward;
double lowerRear;
double leftAux;
double rightAux;
} fPayloadDataSet_t;
/******************************** Functions ********************************/
// ZFW Entry
void distribute(fPayloadData_t* const targetPayload, const FuelData_t* const fuel, const double ZFWTarget, const UserData_t* const userData);
// SimBrief Entry
void distribute(fPayloadData_t* const targetPayload, const FuelData_t* const fuel, unsigned int cargo, const UserData_t* const userData);
// Updates pax stations with their respective weights
// Used internally and used for Station Entry (pax only, cargo is ste directly)
// STATION WEIGHTS ARE NOT NORMALISED TO POUNDS
void generatePayload(fPayloadData_t* const targetPayload, const bool isImperial);
// Normalise to Pounds
// For Station Entry: CALL AFTER `generatePayload`
void normalisePayload(fPayloadData_t* const targetPayload, const bool isImperial);
void calculateCGs(const fPayloadData_t* const targetPayload, const FuelData_t* const fuel, double* const ZFWCG, double* const TOCG, const bool isImperial);
void load(const fPayloadData_t* const targetPayload, const HANDLE simConnect, const bool isImperial);
void unloadF(const HANDLE simConnect, const bool isER);
@@ -0,0 +1,25 @@
// load-manager-cli.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include "../wasm-module/pax.h"
int main()
{
bool isImperial = false;
bool isER = true;
paxPayloadData_t targetPaxPayloadData = paxPayloadData_t();
targetPaxPayloadData.empty = FROM_POUNDS(isImperial, 283975);
targetPaxPayloadData.pilot = PAX_WEIGHT(isImperial);
targetPaxPayloadData.firstOfficer = PAX_WEIGHT(isImperial);
targetPaxPayloadData.engineer = PAX_WEIGHT(isImperial);
targetPaxPayloadData.cabinCrewFront = FRONT_CREW_WEIGHT(isImperial);
targetPaxPayloadData.cabinCrewRear = REAR_CREW_WEIGHT(isImperial);
targetPaxPayloadData.leftAux = isER ? AUX_WEIGHT(isImperial) : 0;
targetPaxPayloadData.rightAux = isER ? AUX_WEIGHT(isImperial) : 0;
FuelData_t liveFuelData = FuelData_t();
distribute(&targetPaxPayloadData, &liveFuelData, 162000.0, 20.5, isImperial);
}
@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{992caf4b-b3f6-4a86-a1c1-663686082c5e}</ProjectGuid>
<RootNamespace>loadmanagercli</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IntDir>$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IntDir>$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="pax.cpp" />
<ClCompile Include="load-manager-cli.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pax.h" />
<ClInclude Include="types.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="load-manager-cli.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pax.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pax.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="types.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
#pragma once
#ifndef __INTELLISENSE__
# define MODULE_EXPORT __attribute__( ( visibility( "default" ) ) )
# define MODULE_WASM_MODNAME(mod) __attribute__((import_module(mod)))
#else
# define MODULE_EXPORT
# define MODULE_WASM_MODNAME(mod)
# define __attribute__(x)
# define __restrict__
#endif
/********************************* Headers *********************************/
// MSFS headers
#include <MSFS/MSFS.h>
#include <MSFS/MSFS_CommBus.h>
#include <MSFS/Legacy/gauges.h>
#include <rapidjson/document.h>
#include <rapidjson/filereadstream.h>
#include "rapidjson/filewritestream.h"
#include <rapidjson/writer.h>
#include <SimConnect.h>
// C headers
#include <string.h>
// C++ headers
#include <ctime>
// Own headers
#include "shared.h"
#include "pax.h"
#include "freighter.h"
/******************************** Constants ********************************/
// Module identification
#define MODULE_NAME "[KHOFMANN TFDi MD-11 Load Manager] "
#define VERSION_STRING " 1.0 "
// COMM BUS
#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"
/******************************** Functions ********************************/
void commBusUpdateTargetCallback(const char* args, unsigned int size, void* ctx);
int receiveData(const char* buf);
void sendData();
void CALLBACK MyDispatchProc(SIMCONNECT_RECV* pData, DWORD cbData, void* pContext);
void log(FILE* file, const char* format, void* optionalElement = NULL);
@@ -0,0 +1,37 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.36105.23
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "load-manager", "load-manager.vcxproj", "{A5468B35-BBBD-4C55-97ED-81BFE343B0E4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "load-manager-cli", "load-manager-cli.vcxproj", "{992CAF4B-B3F6-4A86-A1C1-663686082C5E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|MSFS = Debug|MSFS
Debug|x64 = Debug|x64
Release|MSFS = Release|MSFS
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Debug|MSFS.ActiveCfg = Debug|MSFS
{A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Debug|MSFS.Build.0 = Debug|MSFS
{A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Debug|x64.ActiveCfg = Debug|x64
{A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Release|MSFS.ActiveCfg = Release|MSFS
{A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Release|MSFS.Build.0 = Release|MSFS
{A5468B35-BBBD-4C55-97ED-81BFE343B0E4}.Release|x64.ActiveCfg = Release|MSFS
{992CAF4B-B3F6-4A86-A1C1-663686082C5E}.Debug|MSFS.ActiveCfg = Debug|x64
{992CAF4B-B3F6-4A86-A1C1-663686082C5E}.Debug|x64.ActiveCfg = Debug|x64
{992CAF4B-B3F6-4A86-A1C1-663686082C5E}.Debug|x64.Build.0 = Debug|x64
{992CAF4B-B3F6-4A86-A1C1-663686082C5E}.Release|MSFS.ActiveCfg = Release|x64
{992CAF4B-B3F6-4A86-A1C1-663686082C5E}.Release|x64.ActiveCfg = Release|x64
{992CAF4B-B3F6-4A86-A1C1-663686082C5E}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {835CA9EE-0C67-4B32-B910-3F3A40943047}
EndGlobalSection
EndGlobal
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>
@@ -0,0 +1,299 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|MSFS">
<Configuration>Debug</Configuration>
<Platform>MSFS</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|MSFS">
<Configuration>Release</Configuration>
<Platform>MSFS</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{A5468B35-BBBD-4C55-97ED-81BFE343B0E4}</ProjectGuid>
<RootNamespace>Module</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>load-manager</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|MSFS'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>MSFS</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>MSFS</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|MSFS'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>MSFS</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>MSFS</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|MSFS'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|MSFS'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|MSFS'">
<TargetExt>.wasm</TargetExt>
<GenerateManifest />
<LinkIncremental />
<IncludePath>$(MSFS_IncludePath)</IncludePath>
<TargetName>$(ProjectName)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<TargetExt>.wasm</TargetExt>
<GenerateManifest />
<LinkIncremental />
<IncludePath>$(MSFS_IncludePath)</IncludePath>
<TargetName>$(ProjectName)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|MSFS'">
<TargetExt>.wasm</TargetExt>
<IncludePath>$(MSFS_IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<TargetExt>.wasm</TargetExt>
<IncludePath>$(MSFS_IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|MSFS'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>
</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<BufferSecurityCheck>false</BufferSecurityCheck>
<AdditionalIncludeDirectories>
</AdditionalIncludeDirectories>
<PreprocessorDefinitions>__wasi__;_STRING_H_CPLUSPLUS_98_CONFORMANCE_;_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_;_LIBCPP_HAS_NO_THREADS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ShowIncludes>false</ShowIncludes>
<ExceptionHandling>false</ExceptionHandling>
<RuntimeTypeInfo>
</RuntimeTypeInfo>
<SupportJustMyCode>
</SupportJustMyCode>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<BasicRuntimeChecks>
</BasicRuntimeChecks>
<LanguageStandard>stdcpp14</LanguageStandard>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<AdditionalDependencies>
</AdditionalDependencies>
<IgnoreAllDefaultLibraries>
</IgnoreAllDefaultLibraries>
<NoEntryPoint>true</NoEntryPoint>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<RandomizedBaseAddress>
</RandomizedBaseAddress>
<DataExecutionPrevention>
</DataExecutionPrevention>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>
</ProgramDatabaseFile>
<ImportLibrary>
</ImportLibrary>
<ProfileGuidedDatabase>
</ProfileGuidedDatabase>
<AdditionalLibraryDirectories>
</AdditionalLibraryDirectories>
</Link>
<PostBuildEvent>
<Command>cd $(SolutionDir)..\..\
Powershell.exe -File .\copy-debug.ps1</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>
</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<BufferSecurityCheck>false</BufferSecurityCheck>
<AdditionalIncludeDirectories>
</AdditionalIncludeDirectories>
<PreprocessorDefinitions>__wasi__;_STRING_H_CPLUSPLUS_98_CONFORMANCE_;_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_;_LIBCPP_HAS_NO_THREADS;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ShowIncludes>false</ShowIncludes>
<ExceptionHandling>false</ExceptionHandling>
<RuntimeTypeInfo>
</RuntimeTypeInfo>
<SupportJustMyCode>
</SupportJustMyCode>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<BasicRuntimeChecks>
</BasicRuntimeChecks>
<LanguageStandard>stdcpp14</LanguageStandard>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<AdditionalDependencies>
</AdditionalDependencies>
<IgnoreAllDefaultLibraries>
</IgnoreAllDefaultLibraries>
<NoEntryPoint>true</NoEntryPoint>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<RandomizedBaseAddress>
</RandomizedBaseAddress>
<DataExecutionPrevention>
</DataExecutionPrevention>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ProgramDatabaseFile>
</ProgramDatabaseFile>
<ImportLibrary>
</ImportLibrary>
<ProfileGuidedDatabase>
</ProfileGuidedDatabase>
<AdditionalLibraryDirectories>
</AdditionalLibraryDirectories>
</Link>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|MSFS'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>
</AdditionalIncludeDirectories>
<PreprocessorDefinitions>__wasi__;__wasm32__;_STRING_H_CPLUSPLUS_98_CONFORMANCE_;_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_;_LIBCPP_NO_EXCEPTIONS;_LIBCPP_HAS_NO_THREADS;_LIBCPP_STD_VER=14;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BufferSecurityCheck>false</BufferSecurityCheck>
<ExceptionHandling>false</ExceptionHandling>
<DebugInformationFormat>false</DebugInformationFormat>
<SupportJustMyCode>
</SupportJustMyCode>
<LanguageStandard>stdcpp14</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreAllDefaultLibraries>
</IgnoreAllDefaultLibraries>
<NoEntryPoint>true</NoEntryPoint>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<RandomizedBaseAddress>
</RandomizedBaseAddress>
<DataExecutionPrevention>
</DataExecutionPrevention>
<GenerateDebugInformation>false</GenerateDebugInformation>
<ProgramDatabaseFile>
</ProgramDatabaseFile>
<ImportLibrary>
</ImportLibrary>
<ProfileGuidedDatabase>
</ProfileGuidedDatabase>
<AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
</Link>
<PostBuildEvent>
<Command>cd $(SolutionDir)..\..\
Powershell.exe -File .\copy-release.ps1</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>
</AdditionalIncludeDirectories>
<PreprocessorDefinitions>__wasi__;__wasm32__;_STRING_H_CPLUSPLUS_98_CONFORMANCE_;_WCHAR_H_CPLUSPLUS_98_CONFORMANCE_;_LIBCPP_NO_EXCEPTIONS;_LIBCPP_HAS_NO_THREADS;_LIBCPP_STD_VER=14;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<BufferSecurityCheck>false</BufferSecurityCheck>
<ExceptionHandling>false</ExceptionHandling>
<DebugInformationFormat>false</DebugInformationFormat>
<SupportJustMyCode>
</SupportJustMyCode>
<LanguageStandard>stdcpp14</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreAllDefaultLibraries>
</IgnoreAllDefaultLibraries>
<NoEntryPoint>true</NoEntryPoint>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<RandomizedBaseAddress>
</RandomizedBaseAddress>
<DataExecutionPrevention>
</DataExecutionPrevention>
<GenerateDebugInformation>false</GenerateDebugInformation>
<ProgramDatabaseFile>
</ProgramDatabaseFile>
<ImportLibrary>
</ImportLibrary>
<ProfileGuidedDatabase>
</ProfileGuidedDatabase>
<AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
</Link>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="freighter.cpp" />
<ClCompile Include="load-manager.cpp" />
<ClCompile Include="pax.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="freighter.h" />
<ClInclude Include="load-manager.h" />
<ClInclude Include="pax.h" />
<ClInclude Include="shared.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="load-manager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pax.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="freighter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pax.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="load-manager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="shared.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="freighter.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
</ItemGroup>
</Project>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>
+323
View File
@@ -0,0 +1,323 @@
#include "pax.h"
// ZFW Entry, fill pax first (pax+bag), rest is cargo
void distribute(paxPayloadData_t* const targetPayload, const FuelData_t* const fuel, const double ZFWTarget, const UserData_t* const userData,
const UserOptions_t* const userOptions) {
// Find payload, num pax and extra cargo
double payload = ZFWTarget - targetPayload->empty - targetPayload->pilot - targetPayload->firstOfficer - targetPayload->engineer -
targetPayload->cabinCrewFront - targetPayload->cabinCrewRear - targetPayload->leftAux - targetPayload->rightAux;
unsigned short numPax = max(0.0, min((double)MAX_PAX, floor(payload / (PAX_WEIGHT(userData->isImperial, userOptions) +
BAG_WEIGHT(userData->isImperial, userOptions)))));
unsigned int cargo = round(payload - numPax * PAX_WEIGHT(userData->isImperial, userOptions) - numPax * BAG_WEIGHT(userData->isImperial, userOptions));
distribute(targetPayload, fuel, numPax, cargo, userData, userOptions);
}
// SimBrief Entry, SB pax count and extra cargo
void distribute(paxPayloadData_t* const targetPayload, const FuelData_t* const fuel, unsigned short numPax, unsigned int cargo, const UserData_t* const userData,
const UserOptions_t* const userOptions) {
// 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 int count = MAX_PAX;
// Initial distribution pax + bags
while (numPax > 0 && count > 0) {
if (numPax >= 4) {
if (targetPayload->paxCount.business1 < MAX_BUSINESS_1) {
targetPayload->paxCount.business1++;
_numPax++;
}
if (targetPayload->paxCount.business2 < MAX_BUSINESS_2) {
targetPayload->paxCount.business2++;
_numPax++;
}
if (targetPayload->paxCount.economy1 < MAX_ECONOMY_1) {
targetPayload->paxCount.economy1++;
_numPax++;
}
if (targetPayload->paxCount.economy2 < MAX_ECONOMY_2) {
targetPayload->paxCount.economy2++;
_numPax++;
}
}
else if (numPax == 3) {
if (targetPayload->paxCount.business2 < MAX_BUSINESS_2) {
targetPayload->paxCount.business2++;
_numPax++;
}
if (targetPayload->paxCount.economy1 < MAX_ECONOMY_1) {
targetPayload->paxCount.economy1++;
_numPax++;
}
if (targetPayload->paxCount.economy2 < MAX_ECONOMY_2) {
targetPayload->paxCount.economy2++;
_numPax++;
}
}
else if (numPax == 2) {
if (targetPayload->paxCount.economy1 < MAX_ECONOMY_1) {
targetPayload->paxCount.economy1++;
_numPax++;
}
if (targetPayload->paxCount.economy2 < MAX_ECONOMY_2) {
targetPayload->paxCount.economy2++;
_numPax++;
}
}
else if (numPax == 1) {
if (targetPayload->paxCount.economy2 < MAX_ECONOMY_2) {
targetPayload->paxCount.economy2++;
_numPax++;
}
}
numPax -= _numPax;
targetPayload->paxCount.total += _numPax;
if (_numPax % 2 == 0) {
targetPayload->forwardCargo += (_numPax / 2) * BAG_WEIGHT(userData->isImperial, userOptions);
targetPayload->rearCargo += (_numPax / 2) * BAG_WEIGHT(userData->isImperial, userOptions);
}
else {
_numPax--;
targetPayload->forwardCargo += (_numPax / 2 + 1) * BAG_WEIGHT(userData->isImperial, userOptions);
targetPayload->rearCargo += (_numPax / 2) * BAG_WEIGHT(userData->isImperial, userOptions);
}
_numPax = 0;
count--;
}
count = MAX_FRONT_CARGO(userData->isImperial);
// Initial distibution of remaining cargo
while (cargo > 0 && count > 0) {
if (targetPayload->forwardCargo < MAX_FRONT_CARGO(userData->isImperial)) {
targetPayload->forwardCargo++;
cargo--;
}
if (targetPayload->rearCargo < MAX_REAR_CARGO(userData->isImperial, userData->isER) && cargo > 0) {
targetPayload->rearCargo++;
cargo--;
}
count--;
}
// Refinement pax
count = MAX_PAX;
while (count > 0) {
generatePayload(targetPayload, userData->isImperial, userOptions);
calculateCGs(targetPayload, fuel, &targetPayload->ZFWCG, &targetPayload->TOCG, userData->isImperial);
// in front of target
if (targetPayload->ZFWCG < targetPayload->CGTarget - CG_TOLERANCE) {
if (targetPayload->paxCount.business1 > 0) {
targetPayload->paxCount.business1--;
}
else if (targetPayload->paxCount.business2 > 0) {
targetPayload->paxCount.business2--;
}
else if (targetPayload->paxCount.economy1 > 0) {
targetPayload->paxCount.economy1--;
}
else {
break;
}
if (targetPayload->paxCount.economy2 < MAX_ECONOMY_2) {
targetPayload->paxCount.economy2++;
}
else if (targetPayload->paxCount.economy1 < MAX_ECONOMY_1) {
targetPayload->paxCount.economy1++;
}
else if (targetPayload->paxCount.business2 < MAX_BUSINESS_2) {
targetPayload->paxCount.business2++;
}
else {
targetPayload->paxCount.business1++;
}
}
// behind target
else if (targetPayload->ZFWCG > targetPayload->CGTarget + CG_TOLERANCE) {
if (targetPayload->paxCount.economy2 > 0) {
targetPayload->paxCount.economy2--;
}
else if (targetPayload->paxCount.economy1 > 0) {
targetPayload->paxCount.economy1--;
}
else if (targetPayload->paxCount.business2 > 0) {
targetPayload->paxCount.business2--;
}
else {
break;
}
if (targetPayload->paxCount.business1 < MAX_BUSINESS_1) {
targetPayload->paxCount.business1++;
}
else if (targetPayload->paxCount.business2 < MAX_BUSINESS_2) {
targetPayload->paxCount.business2++;
}
else if (targetPayload->paxCount.economy1 < MAX_ECONOMY_1) {
targetPayload->paxCount.economy1++;
}
else {
targetPayload->paxCount.economy2++;
}
}
else {
break;
}
count--;
}
// Refinement cargo
count = MAX_FRONT_CARGO(userData->isImperial) + MAX_REAR_CARGO(userData->isImperial, userData->isER);
while (count > 0) {
generatePayload(targetPayload, userData->isImperial, userOptions);
calculateCGs(targetPayload, fuel, &targetPayload->ZFWCG, &targetPayload->TOCG, userData->isImperial);
// in front of target
if (targetPayload->ZFWCG < targetPayload->CGTarget - CG_TOLERANCE) {
if (targetPayload->forwardCargo > 0 && targetPayload->rearCargo < MAX_REAR_CARGO(userData->isImperial, userData->isER)) {
if (targetPayload->forwardCargo > BAG_WEIGHT(userData->isImperial, userOptions) &&
targetPayload->rearCargo < MAX_FRONT_CARGO(userData->isImperial) - BAG_WEIGHT(userData->isImperial, userOptions)) {
targetPayload->forwardCargo -= BAG_WEIGHT(userData->isImperial, userOptions);
targetPayload->rearCargo += BAG_WEIGHT(userData->isImperial, userOptions);
}
else {
targetPayload->forwardCargo--;
targetPayload->rearCargo++;
}
}
else {
break;
}
}
// behind target
else if (targetPayload->ZFWCG > targetPayload->CGTarget + CG_TOLERANCE) {
if (targetPayload->rearCargo > 0 && targetPayload->forwardCargo < MAX_FRONT_CARGO(userData->isImperial)) {
if (targetPayload->rearCargo > BAG_WEIGHT(userData->isImperial, userOptions) &&
targetPayload->forwardCargo < MAX_REAR_CARGO(userData->isImperial, userData->isER) - BAG_WEIGHT(userData->isImperial, userOptions)) {
targetPayload->rearCargo -= BAG_WEIGHT(userData->isImperial, userOptions);
targetPayload->forwardCargo += BAG_WEIGHT(userData->isImperial, userOptions);
}
else {
targetPayload->rearCargo--;
targetPayload->forwardCargo++;
}
}
else {
break;
}
}
else {
break;
}
count--;
}
}
// Updates pax stations with their respective weights
// Used internally and used for Station Entry (pax only, cargo is set directly)
void generatePayload(paxPayloadData_t* const targetPayload, const bool isImperial, const UserOptions_t* const userOptions) {
targetPayload->business1Left = targetPayload->business1Center = targetPayload->business1Right = (targetPayload->paxCount.business1 / 3.0) * PAX_WEIGHT(isImperial, userOptions);
targetPayload->business2Left = targetPayload->business2Center = targetPayload->business2Right = (targetPayload->paxCount.business2 / 3.0) * PAX_WEIGHT(isImperial, userOptions);
targetPayload->economy1Left = targetPayload->economy1Center = targetPayload->economy1Right = (targetPayload->paxCount.economy1 / 3.0) * PAX_WEIGHT(isImperial, userOptions);
targetPayload->economy2Left = targetPayload->economy2Center = targetPayload->economy2Right = (targetPayload->paxCount.economy2 / 3.0) * PAX_WEIGHT(isImperial, userOptions);
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
// MANDATORY BEFORE SETTING WEIGHTS
// USE ON COPY OF GLOBAL STATE ONLY
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->business1Center = TO_POUNDS(isImperial, targetPayload->business1Center);
targetPayload->business1Right = TO_POUNDS(isImperial, targetPayload->business1Right);
targetPayload->business2Left = TO_POUNDS(isImperial, targetPayload->business2Left);
targetPayload->business2Center = TO_POUNDS(isImperial, targetPayload->business2Center);
targetPayload->business2Right = TO_POUNDS(isImperial, targetPayload->business2Right);
targetPayload->economy1Left = TO_POUNDS(isImperial, targetPayload->economy1Left);
targetPayload->economy1Center = TO_POUNDS(isImperial, targetPayload->economy1Center);
targetPayload->economy1Right = TO_POUNDS(isImperial, targetPayload->economy1Right);
targetPayload->economy2Left = TO_POUNDS(isImperial, targetPayload->economy2Left);
targetPayload->economy2Center = TO_POUNDS(isImperial, targetPayload->economy2Center);
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);
targetPayload->leftAux = TO_POUNDS(isImperial, targetPayload->leftAux);
targetPayload->rightAux = TO_POUNDS(isImperial, targetPayload->rightAux);
}
void calculateCGs(const paxPayloadData_t* const targetPayload, const FuelData_t* const fuel, double* const ZFWCG, double* const TOCG, const bool isImperial) {
paxPayloadData_t localPayload = {};
memcpy(&localPayload, targetPayload, sizeof(localPayload));
normalisePayload(&localPayload, isImperial);
double totalMoment = localPayload.empty * ARM_EMPTY + localPayload.pilot * ARM_PILOT + localPayload.firstOfficer * ARM_FIRST_OFFICER +
localPayload.engineer * ARM_ENGINEER + localPayload.cabinCrewFront * ARM_PAX_CABIN_CREW_FRONT +
localPayload.business1Left * ARM_PAX_BUSINESS1_LEFT + localPayload.business1Center * ARM_PAX_BUSINESS1_CENTER +
localPayload.business1Right * ARM_PAX_BUSINESS1_RIGHT + localPayload.business2Left * ARM_PAX_BUSINESS2_LEFT +
localPayload.business2Center * ARM_PAX_BUSINESS2_CENTER + localPayload.business2Right * ARM_PAX_BUSINESS2_RIGHT +
localPayload.economy1Left * ARM_PAX_ECONOMY1_LEFT + localPayload.economy1Center * ARM_PAX_ECONOMY1_CENTER +
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);
totalMoment += fuel->main1 * ARM_MAIN1 + fuel->main3 * ARM_MAIN3 + fuel->main2 * ARM_MAIN2 + fuel->upperAux * ARM_UPPER_AUX +
fuel->lowerAux * ARM_LOWER_AUX + fuel->main1Tip * ARM_MAIN1_TIP + fuel->main3Tip * ARM_MAIN3_TIP +
fuel->tail * ARM_TAIL + fuel->forwardAux1 * ARM_FORWARD_AUX1 + fuel->forwardAux2 * ARM_FORWARD_AUX2;
totalWeight += fuel->total;
*TOCG = TO_PERCENT_MAC(totalMoment / totalWeight);
}
void load(const paxPayloadData_t* const targetPayload, const HANDLE simConnect, const bool isImperial) {
paxPayloadData_t localPayload = {};
memcpy(&localPayload, targetPayload, sizeof(localPayload));
normalisePayload(&localPayload, isImperial);
SimConnect_SetDataOnSimObject(simConnect, DATA_DEFINITION_PAYLOAD_PAX, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(paxPayloadDataSet_t), &localPayload);
}
void unload(const HANDLE simConnect, const bool isER) {
paxPayloadDataSet_t localPayload = {};
localPayload.cabinCrewFront = FRONT_CREW_WEIGHT(true);
localPayload.cabinCrewRear = REAR_CREW_WEIGHT(true);
localPayload.leftAux = localPayload.rightAux = isER ? AUX_WEIGHT(true) : 0;
localPayload.pilot = localPayload.firstOfficer = localPayload.engineer = PILOT_WEIGHT(true);
SimConnect_SetDataOnSimObject(simConnect, DATA_DEFINITION_PAYLOAD_PAX, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(paxPayloadDataSet_t), &localPayload);
}
const double PAX_WEIGHT(const bool isImperial, const UserOptions_t* const options) {
return (isImperial) ? (options->paxWeightLBS) : (options->paxWeightKG);
}
const double BAG_WEIGHT(const bool isImperial, const UserOptions_t* const options) {
return (isImperial) ? (options->bagWeightLBS) : (options->bagWeightKG);
}
+147
View File
@@ -0,0 +1,147 @@
#pragma once
#ifndef __INTELLISENSE__
# define MODULE_EXPORT __attribute__( ( visibility( "default" ) ) )
# define MODULE_WASM_MODNAME(mod) __attribute__((import_module(mod)))
#else
# define MODULE_EXPORT
# define MODULE_WASM_MODNAME(mod)
# define __attribute__(x)
# define __restrict__
#endif
/********************************* Headers *********************************/
// MSFS headers
#include <MSFS/MSFS_WindowsTypes.h>
#include <SimConnect.h>
// C headers
#include <math.h>
// C++ headers
#include <algorithm>
// Own headers
#include "shared.h"
/******************************** Constants ********************************/
// 2x 190lbs default
#define FRONT_CREW_WEIGHT(IS_IMPERIAL) ((IS_IMPERIAL) ? (380.0) : (172.0))
// 5x 190lbs default
#define REAR_CREW_WEIGHT(IS_IMPERIAL) ((IS_IMPERIAL) ? (950.0) : (430.0))
// All actual Business seats
#define MAX_BUSINESS_1 30
// First 5 rows of economy
#define MAX_BUSINESS_2 45
// Next 14 rows
#define MAX_ECONOMY_1 86
// Remaining rows
#define MAX_ECONOMY_2 137
// Total
#define MAX_PAX 298
// Max ZFW
#define MAX_PAX_ZFW(IS_IMPERIAL) ((IS_IMPERIAL) ? (400000) : (181437))
// Arms
#define ARM_PAX_CABIN_CREW_FRONT 792.0
#define ARM_PAX_BUSINESS1_LEFT 540.0
#define ARM_PAX_BUSINESS1_CENTER 540.0
#define ARM_PAX_BUSINESS1_RIGHT 540.0
#define ARM_PAX_BUSINESS2_LEFT 300.0
#define ARM_PAX_BUSINESS2_CENTER 300.0
#define ARM_PAX_BUSINESS2_RIGHT 300.0
#define ARM_PAX_ECONOMY1_LEFT -240.0
#define ARM_PAX_ECONOMY1_CENTER -240.0
#define ARM_PAX_ECONOMY1_RIGHT -240.0
#define ARM_PAX_ECONOMY2_LEFT -600.0
#define ARM_PAX_ECONOMY2_CENTER -600.0
#define ARM_PAX_ECONOMY2_RIGHT -600.0
#define ARM_PAX_CABIN_CREW_REAR -660.0
/***************************** Data structures *****************************/
typedef struct {
// SimConnect mapped
double pilot;
double firstOfficer;
double engineer;
double cabinCrewFront;
double business1Left;
double business1Center;
double business1Right;
double business2Left;
double business2Center;
double business2Right;
double economy1Left;
double economy1Center;
double economy1Right;
double economy2Left;
double economy2Center;
double economy2Right;
double cabinCrewRear;
double forwardCargo;
double rearCargo;
double leftAux;
double rightAux;
// Additional properties
double empty;
double total;
double CGTarget;
double ZFWCG;
double TOCG;
struct paxCount {
unsigned char business1;
unsigned char business2;
unsigned char economy1;
unsigned char economy2;
unsigned short total;
} paxCount;
struct sbPlanned {
double ZFW;
double GW;
unsigned short pax;
unsigned int cargo;
} sbPlanned;
} paxPayloadData_t;
typedef struct {
double pilot;
double firstOfficer;
double engineer;
double cabinCrewFront;
double business1Left;
double business1Center;
double business1Right;
double business2Left;
double business2Center;
double business2Right;
double economy1Left;
double economy1Center;
double economy1Right;
double economy2Left;
double economy2Center;
double economy2Right;
double cabinCrewRear;
double forwardCargo;
double rearCargo;
double leftAux;
double rightAux;
} paxPayloadDataSet_t;
/******************************** Functions ********************************/
//ZFW Entry, fill pax first (pax+bag), rest is cargo
void distribute(paxPayloadData_t* const targetPayload, const FuelData_t* const fuel, const double ZFWTarget, const UserData_t* const userData,
const UserOptions_t* const userOptions);
//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 UserData_t* const userData,
const UserOptions_t* const userOptions);
// Updates pax stations with their respective weights
// Used internally and used for Station Entry (pax only, cargo is ste directly)
// STATION WEIGHTS ARE NOT NORMALISED TO POUNDS
void generatePayload(paxPayloadData_t* const targetPayload, const bool isImperial, const UserOptions_t* const userOptions);
// Normalise to Pounds
// For Station Entry: CALL AFTER `generatePayload`
void normalisePayload(paxPayloadData_t* const targetPayload, const bool isImperial);
void calculateCGs(const paxPayloadData_t* const targetPayload, const FuelData_t* const fuel, double* const ZFWCG, double* const TOCG, const bool isImperial);
void load(const paxPayloadData_t* const targetPayload, const HANDLE simConnect, const bool isImperial);
void unload(const HANDLE simConnect, const bool isER);
// Based on ICAO/LH findings
const double PAX_WEIGHT(const bool isImperial, const UserOptions_t* const options);
// Based on ICAO/LH findings
const double BAG_WEIGHT(const bool isImperial, const UserOptions_t* const options);
+131
View File
@@ -0,0 +1,131 @@
#pragma once
#ifndef __INTELLISENSE__
# define MODULE_EXPORT __attribute__( ( visibility( "default" ) ) )
# define MODULE_WASM_MODNAME(mod) __attribute__((import_module(mod)))
#else
# define MODULE_EXPORT
# define MODULE_WASM_MODNAME(mod)
# define __attribute__(x)
# define __restrict__
#endif
/******************************** Constants ********************************/
// 190lbs default
#define PILOT_WEIGHT(IS_IMPERIAL) ((IS_IMPERIAL) ? (190.0) : (86.0))
// 200lbs per tank (one LD3)
#define AUX_WEIGHT(IS_IMPERIAL) ((IS_IMPERIAL) ? (200.0) : (91.0))
//PMC pallet due to 104in door
#define MAX_FRONT_CARGO(IS_IMPERIAL) ((IS_IMPERIAL) ? (6.0 * 15000.0) : (6.0 * 6804.0))
#define MAX_UPPER_CARGO(IS_IMPERIAL) ((IS_IMPERIAL) ? (6.5 * 15000.0) : (6.5 * 6804.0))
//LD3s due to 70in door, ER option takes up two slots
#define MAX_REAR_CARGO(IS_IMPERIAL, IS_ER) ((IS_IMPERIAL) ? ((IS_ER ? 12.0 : 14.0) * 3500.0) : ((IS_ER ? 12.0 : 14.0) * 1588.0))
// 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 Shared
#define ARM_EMPTY -159.6
#define ARM_PILOT 984.0
#define ARM_FIRST_OFFICER 984.0
#define ARM_ENGINEER 960.0
#define ARM_FORWARD_CARGO 360.0
#define ARM_REAR_CARGO -360.0
#define ARM_LEFT_AUX 60.0
#define ARM_RIGHT_AUX 60.0
// Arms Fuel
#define ARM_MAIN1 -240.0
#define ARM_MAIN3 -240.0
#define ARM_MAIN2 120.0
#define ARM_UPPER_AUX 0.0
#define ARM_LOWER_AUX 0.0
#define ARM_MAIN1_TIP -468.0
#define ARM_MAIN3_TIP -468.0
#define ARM_TAIL -840.0
#define ARM_FORWARD_AUX1 60.0
#define ARM_FORWARD_AUX2 60.0
// MAC
#define ROOT_CHORD 34.68
#define WING_SPAN 170.5
#define WING_AREA 3648.0
#define QUARTER_MAC -165.0 //aero_center
#define TIP_CHORD ((2.0 * WING_AREA) / WING_SPAN - ROOT_CHORD)
#define TAPER_RATIO (TIP_CHORD / ROOT_CHORD)
#define MAC ((2.0 / 3.0) * ROOT_CHORD * ((1.0 + TAPER_RATIO + (TAPER_RATIO * TAPER_RATIO)) / (1.0 + TAPER_RATIO)) * 12.0)
#define LEMAC (QUARTER_MAC + (1.0 / 4.0) * MAC)
// CG Limits
#define MIN_CG 12.0
#define MAX_CG 34.0
#define CG_TOLERANCE 0.05
// GSX States
#define GSX_SERVICE_ACTIVE 5
/********************************* Macros **********************************/
// Conversions
#define TO_POUNDS(IS_IMPERIAL, VALUE) ((IS_IMPERIAL) ? (VALUE) : (VALUE) * 2.20462262185)
#define FROM_POUNDS(IS_IMPERIAL, VALUE) ((IS_IMPERIAL) ? (VALUE) : (VALUE) * (1.0 / 2.20462262185))
// MAC
#define TO_PERCENT_MAC(POS) ((((POS) - LEMAC) / MAC) * -100.0)
/********************************** ENUMS **********************************/
enum DATA_DEFINITIONS {
DATA_DEFINITION_EMPTY_WEIGHT,
DATA_DEFINITION_PAYLOAD_PAX,
DATA_DEFINITION_PAYLOAD_F,
DATA_DEFINITION_FUEL,
DATA_DEFINITION_GSX,
DATA_DEFINITION_USER_DATA,
};
enum DATA_REQUESTS {
DATA_REQUEST_EMPTY_WEIGHT,
DATA_REQUEST_PAYLOAD_PAX,
DATA_REQUEST_PAYLOAD_F,
DATA_REQUEST_FUEL,
DATA_REQUEST_GSX,
DATA_REQUEST_USER_DATA,
};
/***************************** Data structures *****************************/
typedef struct {
double isCargo;
double isER;
double isImperial;
} UserData_t;
typedef struct {
double boardingState; // See manual, 5 => active
double deboardingState; // See manual, 5 => active
double passengersBoarded; // Num pax
double passengersDeboarded; // Num pax
double cargoBoarded; // In percent
double cargoDeboarded; // In percent
} GSXData_t;
typedef struct {
// SimConnect mapped
double poundsPerGallon; // DO NOT USE
double main1;
double main3;
double main2;
double upperAux;
double lowerAux;
double main1Tip;
double main3Tip;
double tail;
double forwardAux1;
double forwardAux2;
// Additional properties
double total;
} FuelData_t;
typedef struct {
bool GSXSync;
double paxWeightKG;
double bagWeightKG;
double paxWeightLBS;
double bagWeightLBS;
} UserOptions_t;
+16 -1
View File
@@ -1,6 +1,21 @@
# How to build
Build WASM module from VS
`pnpm build` from `js-bundle`
Build in sim
# Sources
- https://www.boeing.com/content/dam/boeing/boeingdotcom/company/about_bca/startup/pdf/freighters/MD11BCF.pdf
- LAMM
- 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
TODO:
- EFB integration (BRANCH)
- Check if including bundle is getting loaded
- Add to bundle global scoped react "import"
- Automate this?
- Add to EFB.js and EFB.html
- Automate this?
+4
View File
@@ -0,0 +1,4 @@
Copy-Item .\PackageSources\wasm-module\MSFS\Debug\load-manager.wasm .\PackageSources\SimObjects\Airplanes\TFDi_Design_MD-11_GE\panel
Copy-Item .\PackageSources\wasm-module\MSFS\Debug\load-manager.wasm .\PackageSources\SimObjects\Airplanes\TFDi_Design_MD-11_PW\panel
Copy-Item .\PackageSources\wasm-module\MSFS\Debug\load-manager.wasm .\PackageSources\SimObjects\Airplanes\TFDi_Design_MD-11F_GE\panel
Copy-Item .\PackageSources\wasm-module\MSFS\Debug\load-manager.wasm .\PackageSources\SimObjects\Airplanes\TFDi_Design_MD-11F_PW\panel
+4
View File
@@ -0,0 +1,4 @@
Copy-Item .\PackageSources\wasm-module\MSFS\Release\load-manager.wasm .\PackageSources\SimObjects\Airplanes\TFDi_Design_MD-11_GE\panel
Copy-Item .\PackageSources\wasm-module\MSFS\Release\load-manager.wasm .\PackageSources\SimObjects\Airplanes\TFDi_Design_MD-11_PW\panel
Copy-Item .\PackageSources\wasm-module\MSFS\Release\load-manager.wasm .\PackageSources\SimObjects\Airplanes\TFDi_Design_MD-11F_GE\panel
Copy-Item .\PackageSources\wasm-module\MSFS\Release\load-manager.wasm .\PackageSources\SimObjects\Airplanes\TFDi_Design_MD-11F_PW\panel
@@ -1,8 +1,10 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}
"folders": [
{
"path": "."
}
],
"settings": {
"cSpell.words": ["deboarding", "khofmann", "tfdi", "TFDI", "TOCG", "ZFWCG"]
}
}