Freighter

This commit is contained in:
Kilian Hofmann 2025-06-15 01:57:28 +02:00
parent cbd7d4e0ae
commit 9da2abdad7
32 changed files with 1372 additions and 242 deletions

4
.gitignore vendored
View File

@ -8,4 +8,6 @@ PackageSources/wasm-module/.vs
PackageSources/wasm-module/MSFS
PackageSources/wasm-module/x64
*.blend1
*.blend1
PackageSources/SimObjects/Airplanes/**/panel/*.wasm

View File

@ -35,17 +35,6 @@
<AssetDir>PackageSources\load-manager-panel\</AssetDir>
<OutputDir>InGamePanels\</OutputDir>
</AssetGroup>
<AssetGroup Name="md-11-load-manager-wasm-pw-pax">
<Type>Copy</Type>
<Flags>
<FSXCompatibility>false</FSXCompatibility>
</Flags>
<AssetDir>PackageSources\wasm-module\MSFS\Debug\</AssetDir>
<OutputDir>SimObjects\Airplanes\TFDi_Design_MD-11_PW\panel\</OutputDir>
<Config>
<Include>*.wasm</Include>
</Config>
</AssetGroup>
<AssetGroup Name="md-11-panel-config">
<Type>Copy</Type>
<Flags>

View File

@ -18,6 +18,8 @@ htmlgauge07=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_ga
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

View File

@ -18,6 +18,8 @@ htmlgauge07=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_ga
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

View File

@ -18,6 +18,8 @@ htmlgauge07=WasmInstrument/WasmInstrument.html?wasm_module=md11host.wasm&wasm_ga
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

View File

@ -1,6 +1,6 @@
{
"name": "tfdidesign-md11-load-manager",
"version": "0.1.17",
"version": "0.1.21",
"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"

View File

@ -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({

View File

@ -1,4 +1,5 @@
import { FC, useCallback, useEffect, useState } from 'react';
import Freighter from './components/freighter/Freighter';
import Pax from './components/pax/Pax';
import {
COHERENT_COMM_BUS_WASM_CALL,
@ -6,7 +7,7 @@ import {
TFDI_SIMBRIEF_USERNAME_CALL,
TFDI_SIMBRIEF_USERNAME_EVENT,
} from './constants';
import { WASMDataPax } from './types/WASMData';
import { WASMDataF, WASMDataPax } from './types/WASMData';
interface IAppProps {
commBus: ViewListener.ViewListener;
@ -14,7 +15,7 @@ interface IAppProps {
const App: FC<IAppProps> = ({ commBus }) => {
const [SBUsername, setSBUsername] = useState<string>();
const [WASMData, setWASMData] = useState<WASMDataPax>();
const [WASMData, setWASMData] = useState<WASMDataPax | WASMDataF>();
const [isReady, setIsReady] = useState(false);
// CommBus
@ -46,9 +47,9 @@ const App: FC<IAppProps> = ({ commBus }) => {
<div className="flex w-3/4 flex-col items-center">
{isReady && WASMData ? (
WASMData.userData.isCargo ? (
<>Not yet Implemented</>
<Freighter WASMData={WASMData as WASMDataF} username={SBUsername} />
) : (
<Pax WASMData={WASMData} username={SBUsername} />
<Pax WASMData={WASMData as WASMDataPax} username={SBUsername} />
)
) : (
<h1 className="text-sm font-medium">LOADING</h1>

View File

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

View File

@ -0,0 +1,247 @@
import { FC, useEffect, useRef, useState } from 'react';
import {
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
GSX_SERVICE_CALLED,
GSX_SERVICE_FINISHED,
} from '../../constants';
import { WASMDataF } from '../../types/WASMData';
import { LoadingState } from '../../types/general';
import { ImportFlightPlan } from '../../utils/TFDISBImport';
import { 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);
//eslint-disable-next-line @typescript-eslint/no-explicit-any
const [SBPlan, setSBPlan] = useState<any>();
const [SBInFlight, setSBInFlight] = useState(false);
const cargo = useRef(0);
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;
}
cargo.current = parseFloat(SBResponse.message.cargo) ?? 0;
updateData();
setSBPlan(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) => {
Coherent.call(
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
JSON.stringify({
mode: 0,
cargo: cargo.current ?? 0,
CGTarget: _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>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={SBPlan?.plannedZFW ?? 0}
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={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 ({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;

View File

@ -23,6 +23,7 @@ const SBEntryPax: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoa
const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget);
const [fuel, setFuel] = useState(Math.round(WASMData.livePayload.fuel));
const [fuelEnabled, setFuelEnabled] = useState(true);
//eslint-disable-next-line @typescript-eslint/no-explicit-any
const [SBPlan, setSBPlan] = useState<any>();
const [SBInFlight, setSBInFlight] = useState(false);
@ -139,7 +140,7 @@ const SBEntryPax: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoa
WASMData.userData.isImperial ? fuel : fuel * 2.20462262185
);
SimVar.SetSimVarValue('L:MD11_EFB_READ_READY', 'bool', true);
setFuelEnabled(false);
setFuelEnabled(WASMData.livePayload.fuel === fuel);
}}
disabled={loadingState !== 'preview' || !fuelEnabled || GSXActive()}
>
@ -160,7 +161,7 @@ const SBEntryPax: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoa
/>
</div>
<div className="relative flex w-full items-center justify-between bg-zinc-700 p-2 px-4">
<label>Planned ZFW ({WASMData.userData.isImperial ? 'lbs' : 'kg'})</label>
<label>Planned GW ({WASMData.userData.isImperial ? 'lbs' : 'kg'})</label>
<input
type="text"
placeholder=""

View File

@ -0,0 +1,128 @@
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 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="PAX"
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} />
)}
</>
);
};
export default Freighter;

View File

@ -0,0 +1,244 @@
import { FC, useEffect, useState } from 'react';
import {
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
GSX_SERVICE_CALLED,
GSX_SERVICE_FINISHED,
} from '../../constants';
import { LoadingState } from '../../types/general';
import { WASMDataF } from '../../types/WASMData';
import { 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);
}
};
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]);
const updateData = () => {
Coherent.call(
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
JSON.stringify({
mode: 2,
business1: upper1,
business2: upper2,
economy1: upper3,
economy2: upper4,
forwardCargo: lowerForward,
rearCargo: lowerRear,
})
);
};
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;

View File

@ -114,7 +114,7 @@ const StationEntryPax: FC<StationEntryProps> = ({ WASMData, loadingState, setLoa
WASMData.userData.isImperial ? fuel : fuel * 2.20462262185
);
SimVar.SetSimVarValue('L:MD11_EFB_READ_READY', 'bool', true);
setFuelEnabled(false);
setFuelEnabled(WASMData.livePayload.fuel === fuel);
}}
disabled={loadingState !== 'preview' || !fuelEnabled || GSXActive()}
>

View File

@ -0,0 +1,234 @@
import { FC, useEffect, useState } from 'react';
import {
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
GSX_SERVICE_CALLED,
GSX_SERVICE_FINISHED,
} from '../../constants';
import { WASMDataF } from '../../types/WASMData';
import { LoadingState } from '../../types/general';
import { 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) => {
Coherent.call(
COHERENT_COMM_BUS_WASM_CALL,
COMM_BUS_UPDATE_TARGET_EVENT,
JSON.stringify({
mode: 1,
ZFWTarget: _ZFWTarget ?? ZFWTarget,
CGTarget: _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;

View File

@ -133,7 +133,7 @@ const ZFWEntryPax: FC<ZFWEntryProps> = ({ WASMData, loadingState, setLoadingStat
WASMData.userData.isImperial ? fuel : fuel * 2.20462262185
);
SimVar.SetSimVarValue('L:MD11_EFB_READ_READY', 'bool', true);
setFuelEnabled(false);
setFuelEnabled(WASMData.livePayload.fuel === fuel);
}}
disabled={loadingState !== 'preview' || !fuelEnabled || GSXActive()}
>

View File

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

View File

@ -7,6 +7,15 @@ export interface WASMDataPax {
options: Options;
}
export interface WASMDataF {
livePayload: LivePayloadF;
targetPayload: TargetPayloadF;
GSX: GSX;
userData: UserData;
limits: LimitsF;
options: Options;
}
interface TargetPayload {
CGTarget: number;
ZFWCG: number;

View File

@ -5,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;
}

View File

@ -21,5 +21,5 @@ export const unloadAircraft = () => {
};
export const inRangeOf = (value: number, target: number, tolerance: number = 10) => {
return Math.abs(value - target) < 10;
return Math.abs(value - target) < tolerance;
};

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 bool isImperial, const bool isER) {
// 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, isImperial, isER);
}
// SimBrief Entry
void distribute(fPayloadData_t* const targetPayload, const FuelData_t* const fuel, unsigned int cargo, const bool isImperial, const bool isER) {
// 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(isImperial) * 4 + MAX_FRONT_CARGO(isImperial) + MAX_REAR_CARGO(isImperial, isER);
// Initial distributiob
while (cargo > 0 && count > 0) {
if (cargo >= 6) {
if (targetPayload->stations.upper1 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper1++;
_cargo++;
}
if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper2++;
_cargo++;
}
if (targetPayload->stations.upper3 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper3++;
_cargo++;
}
if (targetPayload->stations.upper4 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper4++;
_cargo++;
}
if (targetPayload->lowerForward < MAX_FRONT_CARGO(isImperial)) {
targetPayload->lowerForward++;
_cargo++;
}
if (targetPayload->lowerRear < MAX_REAR_CARGO(isImperial, isER)) {
targetPayload->lowerRear++;
_cargo++;
}
} else if (cargo == 5) {
if (targetPayload->stations.upper1 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper1++;
_cargo++;
}
if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper2++;
_cargo++;
}
if (targetPayload->stations.upper3 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper3++;
_cargo++;
}
if (targetPayload->stations.upper4 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper4++;
_cargo++;
}
if (targetPayload->lowerForward < MAX_FRONT_CARGO(isImperial)) {
targetPayload->lowerForward++;
_cargo++;
}
}
else if (cargo == 4) {
if (targetPayload->stations.upper1 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper1++;
_cargo++;
}
if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper2++;
_cargo++;
}
if (targetPayload->stations.upper3 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper3++;
_cargo++;
}
if (targetPayload->stations.upper4 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper4++;
_cargo++;
}
}
else if (cargo == 3) {
if (targetPayload->stations.upper1 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper1++;
_cargo++;
}
if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper2++;
_cargo++;
}
if (targetPayload->stations.upper3 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper3++;
_cargo++;
}
}
else if (cargo == 2) {
if (targetPayload->stations.upper1 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper1++;
_cargo++;
}
if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper2++;
_cargo++;
}
}
else if (cargo == 1) {
if (targetPayload->stations.upper1 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper1++;
_cargo++;
}
}
cargo -= _cargo;
targetPayload->stations.total += _cargo;
_cargo = 0;
count--;
}
// Refinement
count = MAX_UPPER_CARGO(isImperial) * 4 + MAX_FRONT_CARGO(isImperial) + MAX_REAR_CARGO(isImperial, isER);
while (count > 0) {
generatePayload(targetPayload, isImperial);
calculateCGs(targetPayload, fuel, &targetPayload->ZFWCG, &targetPayload->TOCG, 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(isImperial)) {
targetPayload->stations.upper4++;
}
else if (targetPayload->stations.upper3 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper3++;
}
else if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(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(isImperial)) {
targetPayload->stations.upper1++;
}
else if (targetPayload->stations.upper2 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper2++;
}
else if (targetPayload->stations.upper3 < MAX_UPPER_CARGO(isImperial)) {
targetPayload->stations.upper3++;
}
else {
targetPayload->stations.upper4++;
}
}
else {
break;
}
count--;
}
// Refinement cargo
count = MAX_FRONT_CARGO(isImperial) + MAX_REAR_CARGO(isImperial, isER);
while (count > 0) {
generatePayload(targetPayload, isImperial);
calculateCGs(targetPayload, fuel, &targetPayload->ZFWCG, &targetPayload->TOCG, isImperial);
// in front of target
if (targetPayload->ZFWCG < targetPayload->CGTarget - CG_TOLERANCE) {
if (targetPayload->lowerForward > 0 && targetPayload->lowerRear < MAX_REAR_CARGO(isImperial, 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(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);
}

View File

@ -0,0 +1,36 @@
#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
#include <MSFS/MSFS_WindowsTypes.h>
#include <math.h>
#include <algorithm>
#include <SimConnect.h>
#include "types.h"
// ZFW Entry
void distribute(fPayloadData_t* const targetPayload, const FuelData_t* const fuel, const double ZFWTarget, const bool isImperial, const bool isER);
// SimBrief Entry
void distribute(fPayloadData_t* const targetPayload, const FuelData_t* const fuel, unsigned int cargo, const bool isImperial, const bool isER);
// 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);

View File

@ -342,27 +342,27 @@ extern "C" MSFS_CALLBACK void module_init(void) {
log(stdout, MODULE_NAME"Data definitions created\n");
// SimConnect Requests
hr = SimConnect_RequestDataOnSimObject(simConnect, DATA_REQUEST_EMPTY_WEIGHT, DATA_DEFINITION_EMPTY_WEIGHT, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_SECOND);
hr = SimConnect_RequestDataOnSimObject(simConnect, DATA_REQUEST_EMPTY_WEIGHT, DATA_DEFINITION_EMPTY_WEIGHT, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_VISUAL_FRAME);
if (hr != S_OK) {
log(stderr, MODULE_NAME"Could not request empty weight, terminating.\n");
return;
}
hr = SimConnect_RequestDataOnSimObject(simConnect, DATA_REQUEST_PAYLOAD_PAX, DATA_DEFINITION_PAYLOAD_PAX, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_SECOND);
hr = SimConnect_RequestDataOnSimObject(simConnect, DATA_REQUEST_PAYLOAD_PAX, DATA_DEFINITION_PAYLOAD_PAX, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_VISUAL_FRAME);
if (hr != S_OK) {
log(stderr, MODULE_NAME"Could not request payload pax, terminating.\n");
return;
}
hr = SimConnect_RequestDataOnSimObject(simConnect, DATA_REQUEST_PAYLOAD_F, DATA_DEFINITION_PAYLOAD_F, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_SECOND);
hr = SimConnect_RequestDataOnSimObject(simConnect, DATA_REQUEST_PAYLOAD_F, DATA_DEFINITION_PAYLOAD_F, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_VISUAL_FRAME);
if (hr != S_OK) {
log(stderr, MODULE_NAME"Could not request payload f, terminating.\n");
return;
}
hr = SimConnect_RequestDataOnSimObject(simConnect, DATA_REQUEST_FUEL, DATA_DEFINITION_FUEL, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_SECOND);
hr = SimConnect_RequestDataOnSimObject(simConnect, DATA_REQUEST_FUEL, DATA_DEFINITION_FUEL, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_VISUAL_FRAME);
if (hr != S_OK) {
log(stderr, MODULE_NAME"Could not request fuel, terminating.\n");
return;
}
hr = SimConnect_RequestDataOnSimObject(simConnect, DATA_REQUEST_GSX, DATA_DEFINITION_GSX, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_SECOND);
hr = SimConnect_RequestDataOnSimObject(simConnect, DATA_REQUEST_GSX, DATA_DEFINITION_GSX, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_VISUAL_FRAME);
if (hr != S_OK) {
log(stderr, MODULE_NAME"Could not request GSX, terminating.\n");
return;
@ -507,8 +507,9 @@ int receiveData(const char* buf) {
double CGTarget = document["CGTarget"].GetDouble();
if (UserData->isCargo) {
targetPaxPayloadData->CGTarget = CGTarget;
//TODO: F load
targetFPayloadData->CGTarget = CGTarget;
distribute(targetFPayloadData, liveFuelData, cargo, UserData->isImperial, UserData->isER);
}
else {
if (!document.HasMember("numPax")) return -1;
@ -526,8 +527,8 @@ int receiveData(const char* buf) {
double CGTarget = document["CGTarget"].GetDouble();
if (UserData->isCargo) {
targetPaxPayloadData->CGTarget = CGTarget;
//TODO: F load
targetFPayloadData->CGTarget = CGTarget;
distribute(targetFPayloadData, liveFuelData, ZFWTarget, UserData->isImperial, UserData->isER);
}
else {
targetPaxPayloadData->CGTarget = CGTarget;
@ -538,7 +539,17 @@ int receiveData(const char* buf) {
// Station Entry
case 2: {
if (UserData->isCargo) {
//TODO: F load
if (!document.HasMember("upper1") || !document.HasMember("upper2") ||
!document.HasMember("upper3") || !document.HasMember("upper4") ||
!document.HasMember("lowerForward") || !document.HasMember("lowerRear")) return -1;
targetFPayloadData->stations.upper1 = document["upper1"].GetInt();
targetFPayloadData->stations.upper2 = document["upper2"].GetInt();
targetFPayloadData->stations.upper3 = document["upper3"].GetInt();
targetFPayloadData->stations.upper4 = document["upper4"].GetInt();
targetFPayloadData->lowerForward = document["lowerForward"].GetDouble();
targetFPayloadData->lowerRear = document["lowerRear"].GetDouble();
generatePayload(targetFPayloadData, UserData->isImperial);
}
else {
if (!document.HasMember("business1") || !document.HasMember("business2") ||
@ -558,7 +569,7 @@ int receiveData(const char* buf) {
// Trigger load
case 3: {
if (UserData->isCargo) {
//TODO: F load
load(targetFPayloadData, simConnect, UserData->isImperial);
}
else {
load(targetPaxPayloadData, simConnect, UserData->isImperial);
@ -569,7 +580,7 @@ int receiveData(const char* buf) {
// Trigger unload
case 4: {
if (UserData->isCargo) {
//TODO: F load
unloadF(simConnect, UserData->isER);
}
else {
unload(simConnect, UserData->isER);
@ -635,8 +646,7 @@ void sendData () {
livePayload.AddMember("lowerRear", FROM_POUNDS(UserData->isImperial, liveFPayloadData->lowerRear), allocator);
livePayload.AddMember("total", FROM_POUNDS(UserData->isImperial, liveFPayloadData->total), allocator);
// CGs
//TODO: Enable for F
//calculateCGs(liveFPayloadData, liveFuelData, &liveFPayloadData->ZFWCG, &liveFPayloadData->TOCG, true);
calculateCGs(liveFPayloadData, liveFuelData, &liveFPayloadData->ZFWCG, &liveFPayloadData->TOCG, true);
livePayload.AddMember("ZFWCG", liveFPayloadData->ZFWCG, allocator);
livePayload.AddMember("TOCG", liveFPayloadData->TOCG, allocator);
}
@ -695,8 +705,7 @@ void sendData () {
targetPayload.AddMember("total", targetFPayloadData->total, allocator);
targetPayload.AddMember("CGTarget", targetFPayloadData->CGTarget, allocator);
// CGs
//TODO: Enable for F
//calculateCGs(targetFPayloadData, liveFuelData, &targetFPayloadData->ZFWCG, &targetFPayloadData->TOCG, UserData->isImperial);
calculateCGs(targetFPayloadData, liveFuelData, &targetFPayloadData->ZFWCG, &targetFPayloadData->TOCG, UserData->isImperial);
targetPayload.AddMember("ZFWCG", targetFPayloadData->ZFWCG, allocator);
targetPayload.AddMember("TOCG", targetFPayloadData->TOCG, allocator);
}
@ -737,16 +746,14 @@ void sendData () {
limits.AddMember("maxFuel", UserData->isER ? MAX_FUEL_ER(UserData->isImperial) : MAX_FUEL(UserData->isImperial), allocator);
limits.AddMember("maxTOW", UserData->isER ? MAX_TOW_ER(UserData->isImperial) : MAX_TOW(UserData->isImperial), allocator);
// Cargo Only
// TODO: Actual F limits
if (UserData->isCargo) {
limits.AddMember("upper1", -1, allocator);
limits.AddMember("upper2", -1, allocator);
limits.AddMember("upper3", -1, allocator);
limits.AddMember("upper4", -1, allocator);
limits.AddMember("upper1", MAX_UPPER_CARGO(UserData->isImperial), allocator);
limits.AddMember("upper2", MAX_UPPER_CARGO(UserData->isImperial), allocator);
limits.AddMember("upper3", MAX_UPPER_CARGO(UserData->isImperial), allocator);
limits.AddMember("upper4", MAX_UPPER_CARGO(UserData->isImperial), allocator);
limits.AddMember("lowerForward", MAX_FRONT_CARGO(UserData->isImperial), allocator);
limits.AddMember("lowerRear", MAX_REAR_CARGO(UserData->isImperial, UserData->isER), allocator);
// TODO: Actual F limit
//limits.AddMember("MaxZFW", MAX_F_ZFW, allocator);
limits.AddMember("maxZFW", MAX_F_ZFW(UserData->isImperial), allocator);
limits.AddMember("minZFW", targetFPayloadData->empty + targetFPayloadData->leftAux + targetFPayloadData->rightAux, allocator);
}
// Pax only
@ -858,7 +865,18 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV* pData, DWORD cbData, void* pContex
if (GSXData->boardingState == GSX_SERVICE_ACTIVE) {
double cargoBoarded = GSXData->cargoBoarded;
if (UserData->isCargo) {
//TODO: Progressive F load
fPayloadData_t localPayload = {};
memcpy(&localPayload, targetFPayloadData, sizeof(localPayload));
localPayload.stations.upper1 = targetFPayloadData->stations.upper1 * (cargoBoarded / 100);
localPayload.stations.upper2 = targetFPayloadData->stations.upper2 * (cargoBoarded / 100);
localPayload.stations.upper3 = targetFPayloadData->stations.upper3 * (cargoBoarded / 100);
localPayload.stations.upper4 = targetFPayloadData->stations.upper4 * (cargoBoarded / 100);
localPayload.lowerForward = targetFPayloadData->lowerForward * (cargoBoarded / 100);
localPayload.lowerRear = targetFPayloadData->lowerRear * (cargoBoarded / 100);
generatePayload(&localPayload, UserData->isImperial);
load(&localPayload, simConnect, UserData->isImperial);
}
else {
double passengersBoarded = GSXData->passengersBoarded;
@ -883,7 +901,18 @@ void CALLBACK MyDispatchProc(SIMCONNECT_RECV* pData, DWORD cbData, void* pContex
if (GSXData->deboardingState == GSX_SERVICE_ACTIVE) {
double cargoDeboarded = GSXData->cargoDeboarded;
if (UserData->isCargo) {
//TODO: Progressive F unload
fPayloadData_t localPayload = {};
memcpy(&localPayload, targetFPayloadData, sizeof(localPayload));
localPayload.stations.upper1 -= targetFPayloadData->stations.upper1 * (cargoDeboarded / 100);
localPayload.stations.upper2 -= targetFPayloadData->stations.upper2 * (cargoDeboarded / 100);
localPayload.stations.upper3 -= targetFPayloadData->stations.upper3 * (cargoDeboarded / 100);
localPayload.stations.upper4 -= targetFPayloadData->stations.upper4 * (cargoDeboarded / 100);
localPayload.lowerForward -= targetFPayloadData->lowerForward * (cargoDeboarded / 100);
localPayload.lowerRear -= targetFPayloadData->lowerRear * (cargoDeboarded / 100);
generatePayload(&localPayload, UserData->isImperial);
load(&localPayload, simConnect, UserData->isImperial);
}
else {
double passengersDeboarded = GSXData->passengersDeboarded;

View File

@ -23,6 +23,7 @@
#include "types.h"
#include "pax.h"
#include "freighter.h"
#define MODULE_NAME "[KHOFMANN TFDi MD-11 Load Manager] "

View File

@ -283,10 +283,12 @@
</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="types.h" />

View File

@ -7,6 +7,9 @@
<ClCompile Include="pax.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="freighter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pax.h">
@ -18,6 +21,9 @@
<ClInclude Include="types.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="freighter.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Header Files">

View File

@ -1,6 +1,6 @@
#include "pax.h"
//ZFW Entry, fill pax first (pax+bag), rest is cargo
// 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 bool isImperial, const bool isER) {
// Find payload, num pax and extra cargo
double payload = ZFWTarget - targetPayload->empty - targetPayload->pilot - targetPayload->firstOfficer - targetPayload->engineer -
@ -11,7 +11,7 @@ void distribute(paxPayloadData_t* const targetPayload, const FuelData_t* const f
distribute(targetPayload, fuel, numPax, cargo, isImperial, isER);
}
//SimBrief Entry, SB pax count and extra cargo
// 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 bool isImperial, const bool isER) {
// Clear
targetPayload->paxCount.business1 = targetPayload->paxCount.business2 = targetPayload->paxCount.economy1 = targetPayload->paxCount.economy2 =
@ -292,7 +292,6 @@ void calculateCGs(const paxPayloadData_t* const targetPayload, const FuelData_t*
*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));
@ -309,5 +308,5 @@ void unload(const HANDLE simConnect, const bool isER) {
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(localPayload), &localPayload);
SimConnect_SetDataOnSimObject(simConnect, DATA_DEFINITION_PAYLOAD_PAX, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(paxPayloadDataSet_t), &localPayload);
}

View File

@ -17,8 +17,9 @@
//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
#define MAX_REAR_CARGO(IS_IMPERIAL, IS_ER) ((IS_IMPERIAL) ? ((IS_ER ? 14.0 : 2.0) * 3500.0) : ((IS_ER ? 14.0 : 2.0) * 1588.0))
#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))
// All actual Business seats
#define MAX_BUSINESS_1 30
@ -33,6 +34,7 @@
// Max ZFW
#define MAX_PAX_ZFW(IS_IMPERIAL) ((IS_IMPERIAL) ? (400000) : (181437))
#define MAX_F_ZFW(IS_IMPERIAL) ((IS_IMPERIAL) ? (451300) : (204706))
// Max TOW
#define MAX_TOW(IS_IMPERIAL) ((IS_IMPERIAL) ? (625500) : (283722))
@ -63,6 +65,15 @@
#define ARM_PAX_ECONOMY2_CENTER -600.0
#define ARM_PAX_ECONOMY2_RIGHT -600.0
#define ARM_PAX_CABIN_CREW_REAR -660.0
// Cargo only
#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
// Shared part 2
#define ARM_FORWARD_CARGO 360.0
#define ARM_REAR_CARGO -360.0
@ -225,6 +236,13 @@ typedef struct {
double CGTarget;
double ZFWCG;
double TOCG;
struct stations {
unsigned int upper1;
unsigned int upper2;
unsigned int upper3;
unsigned int upper4;
unsigned int total;
} stations;
} fPayloadData_t;
typedef struct {
// SimConnect mapped

View File

@ -1,3 +1,7 @@
# How to build
After building WASM, run `copy-debug` or `copy-release` to copy WASM module.
# Sources
- https://www.boeing.com/content/dam/boeing/boeingdotcom/company/about_bca/startup/pdf/freighters/MD11BCF.pdf
@ -7,10 +11,15 @@
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?
- JS
- Persist SB data across page changes
- Duplicate Input pages for F
- Options (GSX sync, pax/bag weights)
- Types for SB plan (only what I need)
- WASM
- Custom pax/bag weights
- F loading stuff

4
copy-debug.ps1 Normal file
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
copy-release.ps1 Normal file
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