SB persist
Options page for GSX Sync
This commit is contained in:
parent
4b60f8eec2
commit
390edd29b8
@ -1,14 +1,9 @@
|
|||||||
import { FC, useEffect, useRef, useState } from 'react';
|
import { FC, useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
|
||||||
COHERENT_COMM_BUS_WASM_CALL,
|
|
||||||
COMM_BUS_UPDATE_TARGET_EVENT,
|
|
||||||
GSX_SERVICE_CALLED,
|
|
||||||
GSX_SERVICE_FINISHED,
|
|
||||||
} from '../../constants';
|
|
||||||
import { WASMDataF } from '../../types/WASMData';
|
import { WASMDataF } from '../../types/WASMData';
|
||||||
import { LoadingState } from '../../types/general';
|
import { LoadingState, SimBrief } from '../../types/general';
|
||||||
import { ImportFlightPlan } from '../../utils/TFDISBImport';
|
import { ImportFlightPlan } from '../../utils/TFDISBImport';
|
||||||
import { inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
|
import { CoherentCallSBEntryF, inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
|
||||||
import CGSelect from '../CGSelect/CGSelect';
|
import CGSelect from '../CGSelect/CGSelect';
|
||||||
import ActionBar from '../actionbar/ActionBar';
|
import ActionBar from '../actionbar/ActionBar';
|
||||||
|
|
||||||
@ -23,8 +18,7 @@ const SBEntryF: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoadi
|
|||||||
const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget);
|
const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget);
|
||||||
const [fuel, setFuel] = useState(Math.round(WASMData.livePayload.fuel));
|
const [fuel, setFuel] = useState(Math.round(WASMData.livePayload.fuel));
|
||||||
const [fuelEnabled, setFuelEnabled] = useState(true);
|
const [fuelEnabled, setFuelEnabled] = useState(true);
|
||||||
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
const [SBPlan, setSBPlan] = useState<SimBrief>();
|
||||||
const [SBPlan, setSBPlan] = useState<any>();
|
|
||||||
const [SBInFlight, setSBInFlight] = useState(false);
|
const [SBInFlight, setSBInFlight] = useState(false);
|
||||||
|
|
||||||
const cargo = useRef(0);
|
const cargo = useRef(0);
|
||||||
@ -83,7 +77,7 @@ const SBEntryF: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoadi
|
|||||||
|
|
||||||
cargo.current = parseFloat(SBResponse.message.cargo) ?? 0;
|
cargo.current = parseFloat(SBResponse.message.cargo) ?? 0;
|
||||||
|
|
||||||
updateData();
|
updateData(undefined, SBResponse.message);
|
||||||
|
|
||||||
setSBPlan(SBResponse.message);
|
setSBPlan(SBResponse.message);
|
||||||
setFuel(parseFloat(SBResponse.message.fuel) ?? 0);
|
setFuel(parseFloat(SBResponse.message.fuel) ?? 0);
|
||||||
@ -102,16 +96,8 @@ const SBEntryF: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoadi
|
|||||||
setFuelEnabled(inRangeOf(Math.round(WASMData.livePayload.fuel), fuel));
|
setFuelEnabled(inRangeOf(Math.round(WASMData.livePayload.fuel), fuel));
|
||||||
}, [WASMData.livePayload.fuel]);
|
}, [WASMData.livePayload.fuel]);
|
||||||
|
|
||||||
const updateData = (_CGTarget?: number) => {
|
const updateData = (_CGTarget?: number, _SBPlan?: SimBrief) => {
|
||||||
Coherent.call(
|
CoherentCallSBEntryF(cargo.current ?? 0, _CGTarget ?? CGTarget, _SBPlan ?? SBPlan);
|
||||||
COHERENT_COMM_BUS_WASM_CALL,
|
|
||||||
COMM_BUS_UPDATE_TARGET_EVENT,
|
|
||||||
JSON.stringify({
|
|
||||||
mode: 0,
|
|
||||||
cargo: cargo.current ?? 0,
|
|
||||||
CGTarget: _CGTarget ?? CGTarget,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -153,7 +139,7 @@ const SBEntryF: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoadi
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder=""
|
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"
|
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}
|
value={SBPlan?.plannedZFW ?? WASMData.sbPlanned.ZFW}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -163,7 +149,7 @@ const SBEntryF: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoadi
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder=""
|
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"
|
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}
|
value={SBPlan?.plannedGW ?? WASMData.sbPlanned.GW}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,14 +1,9 @@
|
|||||||
import { FC, useEffect, useRef, useState } from 'react';
|
import { FC, useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
|
||||||
COHERENT_COMM_BUS_WASM_CALL,
|
|
||||||
COMM_BUS_UPDATE_TARGET_EVENT,
|
|
||||||
GSX_SERVICE_CALLED,
|
|
||||||
GSX_SERVICE_FINISHED,
|
|
||||||
} from '../../constants';
|
|
||||||
import { WASMDataPax } from '../../types/WASMData';
|
import { WASMDataPax } from '../../types/WASMData';
|
||||||
import { LoadingState } from '../../types/general';
|
import { LoadingState, SimBrief } from '../../types/general';
|
||||||
import { ImportFlightPlan } from '../../utils/TFDISBImport';
|
import { ImportFlightPlan } from '../../utils/TFDISBImport';
|
||||||
import { inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
|
import { CoherentCallSBEntryPax, inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
|
||||||
import CGSelect from '../CGSelect/CGSelect';
|
import CGSelect from '../CGSelect/CGSelect';
|
||||||
import ActionBar from '../actionbar/ActionBar';
|
import ActionBar from '../actionbar/ActionBar';
|
||||||
|
|
||||||
@ -23,8 +18,7 @@ const SBEntryPax: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoa
|
|||||||
const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget);
|
const [CGTarget, setCGTarget] = useState(WASMData.targetPayload.CGTarget);
|
||||||
const [fuel, setFuel] = useState(Math.round(WASMData.livePayload.fuel));
|
const [fuel, setFuel] = useState(Math.round(WASMData.livePayload.fuel));
|
||||||
const [fuelEnabled, setFuelEnabled] = useState(true);
|
const [fuelEnabled, setFuelEnabled] = useState(true);
|
||||||
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
const [SBPlan, setSBPlan] = useState<SimBrief>();
|
||||||
const [SBPlan, setSBPlan] = useState<any>();
|
|
||||||
const [SBInFlight, setSBInFlight] = useState(false);
|
const [SBInFlight, setSBInFlight] = useState(false);
|
||||||
|
|
||||||
const numPax = useRef(0);
|
const numPax = useRef(0);
|
||||||
@ -85,7 +79,7 @@ const SBEntryPax: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoa
|
|||||||
cargo.current = parseFloat(SBResponse.message.cargo) ?? 0;
|
cargo.current = parseFloat(SBResponse.message.cargo) ?? 0;
|
||||||
numPax.current = parseInt(SBResponse.message.pax) ?? 0;
|
numPax.current = parseInt(SBResponse.message.pax) ?? 0;
|
||||||
|
|
||||||
updateData();
|
updateData(SBResponse.message);
|
||||||
|
|
||||||
setSBPlan(SBResponse.message);
|
setSBPlan(SBResponse.message);
|
||||||
setFuel(parseFloat(SBResponse.message.fuel) ?? 0);
|
setFuel(parseFloat(SBResponse.message.fuel) ?? 0);
|
||||||
@ -104,17 +98,8 @@ const SBEntryPax: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoa
|
|||||||
setFuelEnabled(inRangeOf(Math.round(WASMData.livePayload.fuel), fuel));
|
setFuelEnabled(inRangeOf(Math.round(WASMData.livePayload.fuel), fuel));
|
||||||
}, [WASMData.livePayload.fuel]);
|
}, [WASMData.livePayload.fuel]);
|
||||||
|
|
||||||
const updateData = (_CGTarget?: number) => {
|
const updateData = (_CGTarget?: number, _SBPlan?: SimBrief) => {
|
||||||
Coherent.call(
|
CoherentCallSBEntryPax(cargo.current ?? 0, numPax.current ?? 0, _CGTarget ?? CGTarget, _SBPlan ?? SBPlan);
|
||||||
COHERENT_COMM_BUS_WASM_CALL,
|
|
||||||
COMM_BUS_UPDATE_TARGET_EVENT,
|
|
||||||
JSON.stringify({
|
|
||||||
mode: 0,
|
|
||||||
cargo: cargo.current ?? 0,
|
|
||||||
numPax: numPax.current ?? 0,
|
|
||||||
CGTarget: _CGTarget ?? CGTarget,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -156,7 +141,7 @@ const SBEntryPax: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoa
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder=""
|
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"
|
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}
|
value={SBPlan?.plannedZFW ?? WASMData.sbPlanned.ZFW}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -166,7 +151,7 @@ const SBEntryPax: FC<SBEntryProps> = ({ WASMData, loadingState, username, setLoa
|
|||||||
type="text"
|
type="text"
|
||||||
placeholder=""
|
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"
|
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}
|
value={SBPlan?.plannedGW ?? WASMData.sbPlanned.GW}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
import { FC } 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 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 OptionsPax;
|
||||||
@ -2,6 +2,7 @@ import { FC, useState } from 'react';
|
|||||||
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
|
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
|
||||||
import { LoadingState } from '../../types/general';
|
import { LoadingState } from '../../types/general';
|
||||||
import { WASMDataPax } from '../../types/WASMData';
|
import { WASMDataPax } from '../../types/WASMData';
|
||||||
|
import OptionsPax from '../options/OptionsPax';
|
||||||
import Profile from '../profile/Profile';
|
import Profile from '../profile/Profile';
|
||||||
import SBEntryPax from '../SBEntry/SBEntryPax';
|
import SBEntryPax from '../SBEntry/SBEntryPax';
|
||||||
import StationEntryPax from '../stationEntry/StationEntryPax';
|
import StationEntryPax from '../stationEntry/StationEntryPax';
|
||||||
@ -125,6 +126,9 @@ const Pax: FC<PaxProps> = ({ WASMData, username }) => {
|
|||||||
{((username && selectedTab === 2) || (!username && selectedTab === 1)) && (
|
{((username && selectedTab === 2) || (!username && selectedTab === 1)) && (
|
||||||
<StationEntryPax WASMData={WASMData} loadingState={loadingState} setLoadingState={setLoadingState} />
|
<StationEntryPax WASMData={WASMData} loadingState={loadingState} setLoadingState={setLoadingState} />
|
||||||
)}
|
)}
|
||||||
|
{((username && selectedTab === 3) || (!username && selectedTab === 2)) && (
|
||||||
|
<OptionsPax WASMData={WASMData} loadingState={loadingState} />
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import {
|
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
|
||||||
COHERENT_COMM_BUS_WASM_CALL,
|
|
||||||
COMM_BUS_UPDATE_TARGET_EVENT,
|
|
||||||
GSX_SERVICE_CALLED,
|
|
||||||
GSX_SERVICE_FINISHED,
|
|
||||||
} from '../../constants';
|
|
||||||
import { LoadingState } from '../../types/general';
|
import { LoadingState } from '../../types/general';
|
||||||
import { WASMDataF } from '../../types/WASMData';
|
import { WASMDataF } from '../../types/WASMData';
|
||||||
import { inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
|
import { CoherentCallStationEntryF, inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
|
||||||
import ActionBar from '../actionbar/ActionBar';
|
import ActionBar from '../actionbar/ActionBar';
|
||||||
|
|
||||||
interface StationEntryProps {
|
interface StationEntryProps {
|
||||||
@ -76,19 +71,7 @@ const StationEntryF: FC<StationEntryProps> = ({ WASMData, loadingState, setLoadi
|
|||||||
}, [WASMData.livePayload.fuel]);
|
}, [WASMData.livePayload.fuel]);
|
||||||
|
|
||||||
const updateData = () => {
|
const updateData = () => {
|
||||||
Coherent.call(
|
CoherentCallStationEntryF(upper1, upper2, upper3, upper4, lowerForward, lowerRear);
|
||||||
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 (
|
return (
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import {
|
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
|
||||||
COHERENT_COMM_BUS_WASM_CALL,
|
|
||||||
COMM_BUS_UPDATE_TARGET_EVENT,
|
|
||||||
GSX_SERVICE_CALLED,
|
|
||||||
GSX_SERVICE_FINISHED,
|
|
||||||
} from '../../constants';
|
|
||||||
import { LoadingState } from '../../types/general';
|
import { LoadingState } from '../../types/general';
|
||||||
import { WASMDataPax } from '../../types/WASMData';
|
import { WASMDataPax } from '../../types/WASMData';
|
||||||
import { inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
|
import { CoherentCallStationEntryPax, inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
|
||||||
import ActionBar from '../actionbar/ActionBar';
|
import ActionBar from '../actionbar/ActionBar';
|
||||||
|
|
||||||
interface StationEntryProps {
|
interface StationEntryProps {
|
||||||
@ -76,19 +71,7 @@ const StationEntryPax: FC<StationEntryProps> = ({ WASMData, loadingState, setLoa
|
|||||||
}, [WASMData.livePayload.fuel]);
|
}, [WASMData.livePayload.fuel]);
|
||||||
|
|
||||||
const updateData = () => {
|
const updateData = () => {
|
||||||
Coherent.call(
|
CoherentCallStationEntryPax(business1, business2, economy1, economy2, forwardCargo, rearCargo);
|
||||||
COHERENT_COMM_BUS_WASM_CALL,
|
|
||||||
COMM_BUS_UPDATE_TARGET_EVENT,
|
|
||||||
JSON.stringify({
|
|
||||||
mode: 2,
|
|
||||||
business1,
|
|
||||||
business2,
|
|
||||||
economy1,
|
|
||||||
economy2,
|
|
||||||
forwardCargo,
|
|
||||||
rearCargo,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -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;
|
||||||
@ -1,13 +1,8 @@
|
|||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import {
|
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
|
||||||
COHERENT_COMM_BUS_WASM_CALL,
|
|
||||||
COMM_BUS_UPDATE_TARGET_EVENT,
|
|
||||||
GSX_SERVICE_CALLED,
|
|
||||||
GSX_SERVICE_FINISHED,
|
|
||||||
} from '../../constants';
|
|
||||||
import { WASMDataF } from '../../types/WASMData';
|
import { WASMDataF } from '../../types/WASMData';
|
||||||
import { LoadingState } from '../../types/general';
|
import { LoadingState } from '../../types/general';
|
||||||
import { inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
|
import { CoherentCallZFWEntry, inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
|
||||||
import CGSelect from '../CGSelect/CGSelect';
|
import CGSelect from '../CGSelect/CGSelect';
|
||||||
import ActionBar from '../actionbar/ActionBar';
|
import ActionBar from '../actionbar/ActionBar';
|
||||||
|
|
||||||
@ -99,15 +94,7 @@ const ZFWEntryF: FC<ZFWEntryProps> = ({ WASMData, loadingState, setLoadingState
|
|||||||
}, [WASMData.livePayload.fuel]);
|
}, [WASMData.livePayload.fuel]);
|
||||||
|
|
||||||
const updateData = (_ZFWTarget?: number, _CGTarget?: number) => {
|
const updateData = (_ZFWTarget?: number, _CGTarget?: number) => {
|
||||||
Coherent.call(
|
CoherentCallZFWEntry(_ZFWTarget ?? ZFWTarget, _CGTarget ?? CGTarget);
|
||||||
COHERENT_COMM_BUS_WASM_CALL,
|
|
||||||
COMM_BUS_UPDATE_TARGET_EVENT,
|
|
||||||
JSON.stringify({
|
|
||||||
mode: 1,
|
|
||||||
ZFWTarget: _ZFWTarget ?? ZFWTarget,
|
|
||||||
CGTarget: _CGTarget ?? CGTarget,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,13 +1,8 @@
|
|||||||
import { FC, useEffect, useState } from 'react';
|
import { FC, useEffect, useState } from 'react';
|
||||||
import {
|
import { GSX_SERVICE_CALLED, GSX_SERVICE_FINISHED } from '../../constants';
|
||||||
COHERENT_COMM_BUS_WASM_CALL,
|
|
||||||
COMM_BUS_UPDATE_TARGET_EVENT,
|
|
||||||
GSX_SERVICE_CALLED,
|
|
||||||
GSX_SERVICE_FINISHED,
|
|
||||||
} from '../../constants';
|
|
||||||
import { WASMDataPax } from '../../types/WASMData';
|
import { WASMDataPax } from '../../types/WASMData';
|
||||||
import { LoadingState } from '../../types/general';
|
import { LoadingState } from '../../types/general';
|
||||||
import { inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
|
import { CoherentCallZFWEntry, inRangeOf, loadAircraft, unloadAircraft } from '../../utils/utils';
|
||||||
import CGSelect from '../CGSelect/CGSelect';
|
import CGSelect from '../CGSelect/CGSelect';
|
||||||
import ActionBar from '../actionbar/ActionBar';
|
import ActionBar from '../actionbar/ActionBar';
|
||||||
|
|
||||||
@ -99,15 +94,7 @@ const ZFWEntryPax: FC<ZFWEntryProps> = ({ WASMData, loadingState, setLoadingStat
|
|||||||
}, [WASMData.livePayload.fuel]);
|
}, [WASMData.livePayload.fuel]);
|
||||||
|
|
||||||
const updateData = (_ZFWTarget?: number, _CGTarget?: number) => {
|
const updateData = (_ZFWTarget?: number, _CGTarget?: number) => {
|
||||||
Coherent.call(
|
CoherentCallZFWEntry(_ZFWTarget ?? ZFWTarget, _CGTarget ?? CGTarget);
|
||||||
COHERENT_COMM_BUS_WASM_CALL,
|
|
||||||
COMM_BUS_UPDATE_TARGET_EVENT,
|
|
||||||
JSON.stringify({
|
|
||||||
mode: 1,
|
|
||||||
ZFWTarget: _ZFWTarget ?? ZFWTarget,
|
|
||||||
CGTarget: _CGTarget ?? CGTarget,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -11,3 +11,10 @@ export const CG_ADJUST = 0.05;
|
|||||||
export const GSX_SERVICE_CALLED = 4;
|
export const GSX_SERVICE_CALLED = 4;
|
||||||
export const GSX_SERVICE_ACTIVE = 5;
|
export const GSX_SERVICE_ACTIVE = 5;
|
||||||
export const GSX_SERVICE_FINISHED = 6;
|
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,19 +1,23 @@
|
|||||||
export interface WASMDataPax {
|
interface WASMData {
|
||||||
livePayload: LivePayloadPax;
|
|
||||||
targetPayload: TargetPayloadPax;
|
|
||||||
GSX: GSX;
|
GSX: GSX;
|
||||||
userData: UserData;
|
userData: UserData;
|
||||||
limits: LimitsPax;
|
|
||||||
options: Options;
|
options: Options;
|
||||||
|
sbPlanned: {
|
||||||
|
ZFW: number;
|
||||||
|
GW: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WASMDataF {
|
export interface WASMDataPax extends WASMData {
|
||||||
|
livePayload: LivePayloadPax;
|
||||||
|
targetPayload: TargetPayloadPax;
|
||||||
|
limits: LimitsPax;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WASMDataF extends WASMData {
|
||||||
livePayload: LivePayloadF;
|
livePayload: LivePayloadF;
|
||||||
targetPayload: TargetPayloadF;
|
targetPayload: TargetPayloadF;
|
||||||
GSX: GSX;
|
|
||||||
userData: UserData;
|
|
||||||
limits: LimitsF;
|
limits: LimitsF;
|
||||||
options: Options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TargetPayload {
|
interface TargetPayload {
|
||||||
|
|||||||
@ -1 +1,9 @@
|
|||||||
export type LoadingState = 'preview' | 'loaded';
|
export type LoadingState = 'preview' | 'loaded';
|
||||||
|
|
||||||
|
export interface SimBrief {
|
||||||
|
plannedZFW: number;
|
||||||
|
plannedGW: number;
|
||||||
|
pax: number;
|
||||||
|
cargo: number;
|
||||||
|
fuel: number;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,11 +1,21 @@
|
|||||||
import { COHERENT_COMM_BUS_WASM_CALL, COMM_BUS_UPDATE_TARGET_EVENT } from '../constants';
|
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 = () => {
|
export const loadAircraft = () => {
|
||||||
Coherent.call(
|
Coherent.call(
|
||||||
COHERENT_COMM_BUS_WASM_CALL,
|
COHERENT_COMM_BUS_WASM_CALL,
|
||||||
COMM_BUS_UPDATE_TARGET_EVENT,
|
COMM_BUS_UPDATE_TARGET_EVENT,
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
mode: 3,
|
mode: MODE_LOAD_SET,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -15,7 +25,7 @@ export const unloadAircraft = () => {
|
|||||||
COHERENT_COMM_BUS_WASM_CALL,
|
COHERENT_COMM_BUS_WASM_CALL,
|
||||||
COMM_BUS_UPDATE_TARGET_EVENT,
|
COMM_BUS_UPDATE_TARGET_EVENT,
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
mode: 4,
|
mode: MODE_UNLOAD_SET,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -23,3 +33,103 @@ export const unloadAircraft = () => {
|
|||||||
export const inRangeOf = (value: number, target: number, tolerance: number = 10) => {
|
export const inRangeOf = (value: number, target: number, tolerance: number = 10) => {
|
||||||
return Math.abs(value - target) < tolerance;
|
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 = (cargo: number, numPax: number, CGTarget: number, SBPlan?: SimBrief) => {
|
||||||
|
Coherent.call(
|
||||||
|
COHERENT_COMM_BUS_WASM_CALL,
|
||||||
|
COMM_BUS_UPDATE_TARGET_EVENT,
|
||||||
|
JSON.stringify({
|
||||||
|
mode: MODE_SB_SET,
|
||||||
|
cargo: cargo,
|
||||||
|
numPax: numPax,
|
||||||
|
CGTarget: CGTarget,
|
||||||
|
plannedZFW: SBPlan?.plannedZFW ?? 0,
|
||||||
|
plannedGW: SBPlan?.plannedGW ?? 0,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CoherentCallSBEntryF = (cargo: number, CGTarget: number, SBPlan?: SimBrief) => {
|
||||||
|
Coherent.call(
|
||||||
|
COHERENT_COMM_BUS_WASM_CALL,
|
||||||
|
COMM_BUS_UPDATE_TARGET_EVENT,
|
||||||
|
JSON.stringify({
|
||||||
|
mode: MODE_SB_SET,
|
||||||
|
cargo: cargo,
|
||||||
|
CGTarget: CGTarget,
|
||||||
|
plannedZFW: SBPlan?.plannedZFW ?? 0,
|
||||||
|
plannedGW: SBPlan?.plannedGW ?? 0,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@ -502,12 +502,14 @@ int receiveData(const char* buf) {
|
|||||||
switch(mode) {
|
switch(mode) {
|
||||||
// SB Entry
|
// SB Entry
|
||||||
case 0: {
|
case 0: {
|
||||||
if (!document.HasMember("cargo") || !document.HasMember("CGTarget")) return -1;
|
if (!document.HasMember("cargo") || !document.HasMember("CGTarget") || !document.HasMember("plannedZFW") || !document.HasMember("plannedGW")) return -1;
|
||||||
unsigned int cargo = document["cargo"].GetInt();
|
unsigned int cargo = document["cargo"].GetInt();
|
||||||
double CGTarget = document["CGTarget"].GetDouble();
|
double CGTarget = document["CGTarget"].GetDouble();
|
||||||
|
|
||||||
if (UserData->isCargo) {
|
if (UserData->isCargo) {
|
||||||
targetFPayloadData->CGTarget = CGTarget;
|
targetFPayloadData->CGTarget = CGTarget;
|
||||||
|
targetFPayloadData->sbPlanned.ZFW = document["plannedZFW"].GetDouble();
|
||||||
|
targetFPayloadData->sbPlanned.GW = document["plannedGW"].GetDouble();
|
||||||
|
|
||||||
distribute(targetFPayloadData, liveFuelData, cargo, UserData->isImperial, UserData->isER);
|
distribute(targetFPayloadData, liveFuelData, cargo, UserData->isImperial, UserData->isER);
|
||||||
}
|
}
|
||||||
@ -516,6 +518,9 @@ int receiveData(const char* buf) {
|
|||||||
unsigned short numPax = document["numPax"].GetInt();
|
unsigned short numPax = document["numPax"].GetInt();
|
||||||
|
|
||||||
targetPaxPayloadData->CGTarget = CGTarget;
|
targetPaxPayloadData->CGTarget = CGTarget;
|
||||||
|
targetPaxPayloadData->sbPlanned.ZFW = document["plannedZFW"].GetDouble();
|
||||||
|
targetPaxPayloadData->sbPlanned.GW = document["plannedGW"].GetDouble();
|
||||||
|
|
||||||
distribute(targetPaxPayloadData, liveFuelData, numPax, cargo, UserData->isImperial, UserData->isER);
|
distribute(targetPaxPayloadData, liveFuelData, numPax, cargo, UserData->isImperial, UserData->isER);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -588,6 +593,32 @@ int receiveData(const char* buf) {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Option set
|
||||||
|
case 5: {
|
||||||
|
if (document.HasMember("GSXSync")) {
|
||||||
|
UserOptions->GSXSync = document["GSXSync"].GetBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
rapidjson::Document optionsDoc;
|
||||||
|
rapidjson::Document::AllocatorType& allocator = optionsDoc.GetAllocator();
|
||||||
|
optionsDoc.SetObject();
|
||||||
|
optionsDoc.AddMember("GSXSync", UserOptions->GSXSync, allocator);
|
||||||
|
optionsDoc.AddMember("paxWeightKG", UserOptions->paxWeightKG, allocator);
|
||||||
|
optionsDoc.AddMember("bagWeightKG", UserOptions->bagWeightKG, allocator);
|
||||||
|
optionsDoc.AddMember("paxWeightLBS", UserOptions->paxWeightLBS, allocator);
|
||||||
|
optionsDoc.AddMember("bagWeightLBS", UserOptions->bagWeightLBS, allocator);
|
||||||
|
FILE* optionsFile = fopen("\\work\\options.json", "wb");
|
||||||
|
if (optionsFile != NULL) {
|
||||||
|
char writeBuffer[256];
|
||||||
|
rapidjson::FileWriteStream os(optionsFile, writeBuffer, sizeof(writeBuffer));
|
||||||
|
rapidjson::Writer<rapidjson::FileWriteStream> writer(os);
|
||||||
|
optionsDoc.Accept(writer);
|
||||||
|
fclose(optionsFile);
|
||||||
|
|
||||||
|
log(stdout, MODULE_NAME"Options written.\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -619,6 +650,8 @@ void sendData () {
|
|||||||
limits.SetObject();
|
limits.SetObject();
|
||||||
rapidjson::Value options;
|
rapidjson::Value options;
|
||||||
options.SetObject();
|
options.SetObject();
|
||||||
|
rapidjson::Value sbPlanned;
|
||||||
|
sbPlanned.SetObject();
|
||||||
|
|
||||||
rapidjson::StringBuffer strbuf;
|
rapidjson::StringBuffer strbuf;
|
||||||
rapidjson::Writer<rapidjson::StringBuffer> writer(strbuf);
|
rapidjson::Writer<rapidjson::StringBuffer> writer(strbuf);
|
||||||
@ -773,6 +806,10 @@ void sendData () {
|
|||||||
options.AddMember("paxWeight", UserData->isImperial ? UserOptions->paxWeightLBS : UserOptions->paxWeightKG, allocator);
|
options.AddMember("paxWeight", UserData->isImperial ? UserOptions->paxWeightLBS : UserOptions->paxWeightKG, allocator);
|
||||||
options.AddMember("bagWeight", UserData->isImperial ? UserOptions->bagWeightLBS : UserOptions->bagWeightKG, allocator);
|
options.AddMember("bagWeight", UserData->isImperial ? UserOptions->bagWeightLBS : UserOptions->bagWeightKG, allocator);
|
||||||
|
|
||||||
|
// SB Planned
|
||||||
|
sbPlanned.AddMember("ZFW", UserData->isCargo ? targetFPayloadData->sbPlanned.ZFW: targetPaxPayloadData->sbPlanned.ZFW, allocator);
|
||||||
|
sbPlanned.AddMember("GW", UserData->isCargo ? targetFPayloadData->sbPlanned.GW : targetPaxPayloadData->sbPlanned.GW, allocator);
|
||||||
|
|
||||||
// Construct document
|
// Construct document
|
||||||
document.AddMember("livePayload", livePayload.Move(), allocator);
|
document.AddMember("livePayload", livePayload.Move(), allocator);
|
||||||
document.AddMember("targetPayload", targetPayload.Move(), allocator);
|
document.AddMember("targetPayload", targetPayload.Move(), allocator);
|
||||||
@ -780,6 +817,7 @@ void sendData () {
|
|||||||
document.AddMember("userData", userData.Move(), allocator);
|
document.AddMember("userData", userData.Move(), allocator);
|
||||||
document.AddMember("limits", limits.Move(), allocator);
|
document.AddMember("limits", limits.Move(), allocator);
|
||||||
document.AddMember("options", options.Move(), allocator);
|
document.AddMember("options", options.Move(), allocator);
|
||||||
|
document.AddMember("sbPlanned", sbPlanned.Move(), allocator);
|
||||||
|
|
||||||
// Write to CommBus
|
// Write to CommBus
|
||||||
document.Accept(writer);
|
document.Accept(writer);
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
//PMC pallet due to 104in door
|
//PMC pallet due to 104in door
|
||||||
#define MAX_FRONT_CARGO(IS_IMPERIAL) ((IS_IMPERIAL) ? (6.0 * 15000.0) : (6.0 * 6804.0))
|
#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))
|
#define MAX_UPPER_CARGO(IS_IMPERIAL) ((IS_IMPERIAL) ? (6.5 * 15000.0) : (6.5 * 6804.0))
|
||||||
//LD3s due to 70in door
|
//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))
|
#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
|
// All actual Business seats
|
||||||
@ -187,6 +187,10 @@ typedef struct {
|
|||||||
unsigned char economy2;
|
unsigned char economy2;
|
||||||
unsigned short total;
|
unsigned short total;
|
||||||
} paxCount;
|
} paxCount;
|
||||||
|
struct sbPlanned {
|
||||||
|
double ZFW;
|
||||||
|
double GW;
|
||||||
|
} sbPlanned;
|
||||||
} paxPayloadData_t;
|
} paxPayloadData_t;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
double pilot;
|
double pilot;
|
||||||
@ -243,6 +247,10 @@ typedef struct {
|
|||||||
unsigned int upper4;
|
unsigned int upper4;
|
||||||
unsigned int total;
|
unsigned int total;
|
||||||
} stations;
|
} stations;
|
||||||
|
struct sbPlanned {
|
||||||
|
double ZFW;
|
||||||
|
double GW;
|
||||||
|
} sbPlanned;
|
||||||
} fPayloadData_t;
|
} fPayloadData_t;
|
||||||
typedef struct {
|
typedef struct {
|
||||||
// SimConnect mapped
|
// SimConnect mapped
|
||||||
|
|||||||
@ -18,8 +18,6 @@ TODO:
|
|||||||
- Add to EFB.js and EFB.html
|
- Add to EFB.js and EFB.html
|
||||||
- Automate this?
|
- Automate this?
|
||||||
- JS
|
- JS
|
||||||
- Persist SB data across page changes
|
- Options (pax/bag weights)
|
||||||
- Options (GSX sync, pax/bag weights)
|
|
||||||
- Types for SB plan (only what I need)
|
|
||||||
- WASM
|
- WASM
|
||||||
- Custom pax/bag weights
|
- Custom pax/bag weights
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user