startResize(colKey, e)}
+ style={{ position: "absolute", top: 0, right: 0, width: 8, height: "100%", cursor: "col-resize" }}
+ title="Drag to resize"
+ />
+ );
+
+ const mkBox = (border, tickColor, fill = "transparent") => {
+ const box = (withTick) => (
+
+ ✓
+
+ );
+ return { icon: box(false), checkedIcon: box(true) };
+ };
+
+ const normalBox = mkBox("rgba(255,255,255,0.45)", "rgba(255,255,255,0.9)");
+ const movedBox = mkBox("#c62828", "rgba(255,255,255,0.9)");
+ const greenBox = mkBox("#4caf50", "#111", "#4caf50");
+
+ const coordFmtOptions = [
+ { value: "DEC", label: "Dec" },
+ { value: "DMS", label: "DMS" },
+ { value: "DMS_S", label: "DMS.S" },
+ { value: "DMM", label: "DMM" },
+ { value: "MGRS", label: "MGRS" },
+ ];
+
+ const elevUnitLabel = unitsMode === "Metric" ? "m" : "ft";
+ const speedUnitLabel = unitsMode === "Metric" ? "km/h" : "kt";
+
+ const fcCompact = (min) => ({ minWidth: min, width: "auto" });
+ const selectCompactSx = { "& .MuiSelect-select": { py: 1.0 } };
+
+ return (
+
+
+
+
+ Coalition
+ setIntelCoalitionFilter(e.target.value)}
+ sx={selectCompactSx}
+ >
+ All
+ Red
+ Blue
+ Neutral
+
+
+
+
+ Type
+ setIntelUnitType(e.target.value)}
+ sx={selectCompactSx}
+ >
+ All
+ Ground
+ Air
+ Naval
+
+
+
+
+ {SHOW_WITHIN_FILTER && (
+ setIntelWithinEnabled(e.target.checked)}
+ icon={greenBox.icon}
+ checkedIcon={greenBox.checkedIcon}
+ />
+ )}
+
+ Within
+
+ {
+ e.preventDefault();
+ const dir = e.deltaY > 0 ? -1 : 1;
+ setIntelRadius((prev) => {
+ const n = Number(prev) || 0;
+ return Math.min(MAX_WITHIN_RADIUS, Math.max(1, Math.round(n + dir)));
+ });
+ }}
+ sx={{ display: "flex", alignItems: "center" }}
+ title="Mouse wheel changes radius"
+ >
+ {
+ const val = Math.round(Number(e.target.value) || 0);
+ setIntelRadius(Math.min(MAX_WITHIN_RADIUS, Math.max(1, val)));
+ }}
+ sx={{ width: 85 }}
+ />
+
+
+
+
+ Units
+ setUnitsMode(e.target.value)} sx={selectCompactSx}>
+ nmi
+ km
+
+
+
+ of
+
+
+ Origin
+ setIntelOrigin(e.target.value)} sx={selectCompactSx}>
+ Camera position
+ Ownship
+
+
+
+
+ Show/Hide
+ setColsAnchorEl(e.currentTarget)}
+ endAdornment={
+
+
+
+ }
+ sx={{
+ cursor: "pointer",
+ "& input": { cursor: "pointer", py: 1.0 },
+ }}
+ />
+
+
+ setColsAnchorEl(null)}>
+
+
+ {[
+ ["coalition", "Coalition"],
+ ["type", "Type"],
+ ["category", "Category"],
+ ["subcategory", "SubCategory"],
+ ["capability", "Capability"],
+ ["name", "Unit / Group Name"],
+ ["position", "Position"],
+ ["elev", `Elev (${elevUnitLabel})`],
+ ["brgDeg", "Bearing"],
+ ["rng", "Range"],
+ ["speed", `Speed (${speedUnitLabel})`],
+ ].map(([key, label]) => (
+ toggleCol(key)}
+ size="small"
+ icon={greenBox.icon}
+ checkedIcon={greenBox.checkedIcon}
+ />
+ }
+ label={label}
+ />
+ ))}
+
+
+
+
+
+
+
+ {isIntelLoading ? "Request" : "Request"}
+
+
+
+ Import {selectedCount}
+
+
+
+
+
+
+
+ {isColVisible("coalition") && (
+
+ handleSort("coalition")}
+ sx={{ width: "100%", justifyContent: "space-between" }}
+ >
+ Coalition
+
+
+
+ )}
+
+ {isColVisible("type") && (
+
+ handleSort("type")}
+ sx={{ width: "100%", justifyContent: "space-between" }}
+ >
+ Type
+
+
+
+ )}
+
+ {isColVisible("category") && (
+
+
+ handleSort("category")}
+ title="Sort by Category"
+ >
+
+ Category
+
+
+ {
+ e.stopPropagation();
+ setCategoryAnchorEl(e.currentTarget);
+ }}
+ onMouseDown={(e) => e.stopPropagation()}
+ sx={{
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "center",
+ width: 18,
+ height: 18,
+ cursor: "pointer",
+ flexShrink: 0,
+ }}
+ title="Filter categories"
+ >
+ {normalBox.checkedIcon}
+
+
+
+ handleSort("category")}
+ sx={{
+ display: "inline-flex",
+ alignItems: "center",
+ justifyContent: "flex-end",
+ flexShrink: 0,
+ cursor: "pointer",
+ ml: 0.5,
+ mr: -0.15,
+ }}
+ >
+
+
+
+
+
+
+ setCategoryAnchorEl(null)}
+ >
+
+
+
+ }
+ label="All"
+ />
+
+ {availableCategories.map((cat) => (
+ toggleCategory(cat)}
+ size="small"
+ icon={greenBox.icon}
+ checkedIcon={greenBox.checkedIcon}
+ />
+ }
+ label={cat}
+ />
+ ))}
+
+
+
+
+
+
+ )}
+
+ {isColVisible("subcategory") && (
+
+ handleSort("subcategory")}
+ sx={{ width: "100%", justifyContent: "space-between" }}
+ >
+ SubCategory
+
+
+
+ )}
+
+ {isColVisible("capability") && (
+
+ handleSort("capability")}
+ sx={{ width: "100%", justifyContent: "space-between" }}
+ >
+ Capability
+
+
+
+ )}
+
+ {isColVisible("name") && (
+
+ handleSort("name")}
+ sx={{ width: "100%", justifyContent: "space-between" }}
+ >
+ Unit / Group Name
+
+
+
+ )}
+
+ {isColVisible("position") && (
+
+
+ handleSort("position")}
+ sx={{ flex: 1, minWidth: 0 }}
+ >
+ Position
+
+
+ e.stopPropagation()}
+ onMouseDown={(e) => e.stopPropagation()}
+ title="Coordinate format"
+ >
+ setCoordFmt(e.target.value)}
+ variant="outlined"
+ sx={{
+ height: 26,
+ fontSize: "0.70rem",
+ "& .MuiSelect-select": { py: 0.3, pr: 2.5 },
+ }}
+ >
+ {coordFmtOptions.map((o) => (
+
+ {o.label}
+
+ ))}
+
+
+
+
+
+
+ )}
+
+ {isColVisible("elev") && (
+
+ handleSort("elev")}
+ sx={{ width: "100%", justifyContent: "space-between" }}
+ >
+ Elev ({elevUnitLabel})
+
+
+
+ )}
+
+ {isColVisible("brgDeg") && (
+
+ handleSort("brgDeg")}
+ sx={{ width: "100%", justifyContent: "space-between" }}
+ >
+ Bearing
+
+
+
+ )}
+
+ {isColVisible("rng") && (
+
+ handleSort("rng")}
+ sx={{ width: "100%", justifyContent: "space-between" }}
+ >
+ Range
+
+
+
+ )}
+
+ {isColVisible("speed") && (
+
+ handleSort("speed")}
+ >
+ Speed ({speedUnitLabel})
+
+
+
+ )}
+
+ handleSort("selected")}
+ title="Click to sort by selected"
+ >
+ e.stopPropagation()}
+ onMouseDown={(e) => e.stopPropagation()}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {sortedRows.map((row) => {
+ const moved = !!row.moved;
+ const box = moved ? movedBox : normalBox;
+
+ return (
+ toggleRow(row.id)}
+ sx={{ cursor: "pointer" }}
+ >
+ {isColVisible("coalition") && {row.coalition} }
+ {isColVisible("type") && {row.type} }
+ {isColVisible("category") && {row.category} }
+ {isColVisible("subcategory") && {row.subcategory} }
+ {isColVisible("capability") && {row.capability} }
+ {isColVisible("name") && {row.name} }
+ {isColVisible("position") && {row.position} }
+ {isColVisible("elev") && {Math.round(row.displayElev)} }
+
+ {isColVisible("brgDeg") && (
+
+ {Number.isFinite(row.brgDeg) ? row.brgDeg.toFixed(1) : "0.0"}°
+
+ )}
+
+ {isColVisible("rng") && (
+
+ {Number.isFinite(row.displayRng)
+ ? unitsMode === "Metric"
+ ? row.displayRng.toFixed(2)
+ : row.displayRng.toFixed(1)
+ : unitsMode === "Metric"
+ ? "0.00"
+ : "0.0"}
+
+ )}
+
+ {isColVisible("speed") && (
+
+ {Number.isFinite(row.speed) ? row.speed.toFixed(1) : "0.0"}
+
+ )}
+
+
+ toggleRow(row.id)}
+ onClick={(e) => e.stopPropagation()}
+ size="small"
+ icon={box.icon}
+ checkedIcon={box.checkedIcon}
+ sx={{ p: 0.25 }}
+ title={moved ? "Unit has moved since last snapshot" : "Import"}
+ />
+
+
+ );
+ })}
+
+ {sortedRows.length === 0 && (
+
+
+
+ No units match the current filters (or no snapshot received yet).
+
+
+
+ )}
+
+
+
+
+
+ Press REQUEST to capture units. REQUEST again to calculate moving units.
+ PREVIOUS SNAPSHOT MAY BE SHOWN. Check Snapshot age -->
+
+
+
+ Snapshot age: {snapshotAgeText}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/settings/AlwaysOnTopSetting.js b/src/components/settings/AlwaysOnTopSetting.js
index a5b6222..416154d 100644
--- a/src/components/settings/AlwaysOnTopSetting.js
+++ b/src/components/settings/AlwaysOnTopSetting.js
@@ -1,26 +1,26 @@
-import { Checkbox, FormControlLabel, FormGroup, Stack } from "@mui/material";
-import { useSelector } from "react-redux";
-
-const AlwaysOnTopSetting = ({ settingChangeHandler }) => {
- const userPreferences = useSelector((state) => state.ui.userPreferences);
-
- return (
-
-
-
- settingChangeHandler("alwaysOnTop", e.target.checked)
- }
- />
- }
- label={"Always on top"}
- />
-
-
- );
-};
-
-export default AlwaysOnTopSetting;
+import { Checkbox, FormControlLabel, FormGroup, Stack } from "@mui/material";
+import { useSelector } from "react-redux";
+
+const AlwaysOnTopSetting = ({ settingChangeHandler }) => {
+ const userPreferences = useSelector((state) => state.ui.userPreferences);
+
+ return (
+
+
+
+ settingChangeHandler("alwaysOnTop", e.target.checked)
+ }
+ />
+ }
+ label={"Always on top"}
+ />
+
+
+ );
+};
+
+export default AlwaysOnTopSetting;
diff --git a/src/components/settings/ButtonDelaySetting.js b/src/components/settings/ButtonDelaySetting.js
index 22bbbf5..05cfa40 100644
--- a/src/components/settings/ButtonDelaySetting.js
+++ b/src/components/settings/ButtonDelaySetting.js
@@ -1,36 +1,36 @@
-import { Box, Slider, Stack, Typography } from "@mui/material";
-import { useSelector } from "react-redux";
-
-const ButtonDelaySetting = ({ settingChangeHandler }) => {
- const userPreferences = useSelector((state) => state.ui.userPreferences);
-
- return (
-
-
- Button press delay
-
-
- Try increasing this if buttons are pressing incorrectly, or too quickly.
-
-
- `+${value}ms`}
- valueLabelDisplay="auto"
- aria-labelledby="input-slider"
- onChangeCommitted={(e, v) => settingChangeHandler("buttonDelay", v)}
- />
-
-
- );
-};
-
-export default ButtonDelaySetting;
+import { Box, Slider, Stack, Typography } from "@mui/material";
+import { useSelector } from "react-redux";
+
+const ButtonDelaySetting = ({ settingChangeHandler }) => {
+ const userPreferences = useSelector((state) => state.ui.userPreferences);
+
+ return (
+
+
+ Button press delay
+
+
+ Try increasing this if buttons are pressing incorrectly, or too quickly.
+
+
+ `+${value}ms`}
+ valueLabelDisplay="auto"
+ aria-labelledby="input-slider"
+ onChangeCommitted={(e, v) => settingChangeHandler("buttonDelay", v)}
+ />
+
+
+ );
+};
+
+export default ButtonDelaySetting;
diff --git a/src/components/settings/CrosshairSetting.js b/src/components/settings/CrosshairSetting.js
index d25c058..eb75475 100644
--- a/src/components/settings/CrosshairSetting.js
+++ b/src/components/settings/CrosshairSetting.js
@@ -1,72 +1,72 @@
-import {
- Box,
- Checkbox,
- FormControlLabel,
- FormGroup,
- Stack,
- Typography,
-} from "@mui/material";
-import { MuiColorInput } from "mui-color-input";
-import _ from "lodash";
-import { useEffect, useState } from "react";
-import { useSelector } from "react-redux";
-import "./SettingsDialog.css";
-const { ipcRenderer } = window.require("electron");
-
-const CrosshairSetting = ({ settingChangeHandler }) => {
- const userPreferences = useSelector((state) => state.ui.userPreferences);
- const oldCrosshair = userPreferences["oldCrosshair"];
- const userPreferenceColor = userPreferences["crosshairColor"];
-
- const [crosshairColor, setCrosshairColor] = useState("#7CFC00FF");
- useEffect(() => {
- const color = userPreferenceColor;
- if (color) setCrosshairColor(color);
- }, [userPreferenceColor]);
-
- return (
-
-
- Crosshair options
-
-
-
-
-
- settingChangeHandler("oldCrosshair", e.target.checked)
- }
- />
- }
- label={"Old style crosshair"}
- />
-
- The old crosshair has customizable colors, but does not show in VR
- or work correctly with multiple monitors.
-
-
- {oldCrosshair && (
-
- Color
-
- {
- settingChangeHandler("crosshairColor", color);
- ipcRenderer.send("color", color);
- }, 150)}
- format="hex"
- isAlphaHidden
- />
-
-
- )}
-
-
- );
-};
-
-export default CrosshairSetting;
+import {
+ Box,
+ Checkbox,
+ FormControlLabel,
+ FormGroup,
+ Stack,
+ Typography,
+} from "@mui/material";
+import { MuiColorInput } from "mui-color-input";
+import _ from "lodash";
+import { useEffect, useState } from "react";
+import { useSelector } from "react-redux";
+import "./SettingsDialog.css";
+const { ipcRenderer } = window.require("electron");
+
+const CrosshairSetting = ({ settingChangeHandler }) => {
+ const userPreferences = useSelector((state) => state.ui.userPreferences);
+ const oldCrosshair = userPreferences["oldCrosshair"];
+ const userPreferenceColor = userPreferences["crosshairColor"];
+
+ const [crosshairColor, setCrosshairColor] = useState("#7CFC00FF");
+ useEffect(() => {
+ const color = userPreferenceColor;
+ if (color) setCrosshairColor(color);
+ }, [userPreferenceColor]);
+
+ return (
+
+
+ Crosshair options
+
+
+
+
+
+ settingChangeHandler("oldCrosshair", e.target.checked)
+ }
+ />
+ }
+ label={"Old style crosshair"}
+ />
+
+ The old crosshair has customizable colors, but does not show in VR
+ or work correctly with multiple monitors.
+
+
+ {oldCrosshair && (
+
+ Color
+
+ {
+ settingChangeHandler("crosshairColor", color);
+ ipcRenderer.send("color", color);
+ }, 150)}
+ format="hex"
+ isAlphaHidden
+ />
+
+
+ )}
+
+
+ );
+};
+
+export default CrosshairSetting;
diff --git a/src/components/settings/KeybindSetting/KeybindItem.js b/src/components/settings/KeybindSetting/KeybindItem.js
index 24912bf..211360c 100644
--- a/src/components/settings/KeybindSetting/KeybindItem.js
+++ b/src/components/settings/KeybindSetting/KeybindItem.js
@@ -1,60 +1,93 @@
-import { useSelector } from "react-redux";
-import { Grid, TextField, Typography } from "@mui/material";
-const { ipcRenderer } = window.require("electron");
-
-const KeybindItem = ({ name, preferenceKey, changeKeybindHandler }) => {
- const userPreferences = useSelector((state) => state.ui.userPreferences);
- const keybind = userPreferences[preferenceKey] || "None";
-
- const handleInputFocus = () => {
- ipcRenderer.send("focus");
- };
- const handleInputDefocus = (event) => {
- ipcRenderer.send("defocus");
- event.target.blur();
- };
-
- const handleKeyPress = (event) => {
- event.preventDefault();
- const key = event.key;
- let code = event.code;
- if (key === "Shift" || key === "Control" || key === "Alt" || event.repeat)
- return;
- if (key === "Delete") {
- changeKeybindHandler(preferenceKey, "None");
- } else {
- const ctrl = event.ctrlKey ? "CTRL+" : "";
- const shift = event.shiftKey ? "SHIFT+" : "";
- const alt = event.altKey ? "ALT+" : "";
- code = code.replace("Key", "").replace("Digit", "");
- const newKeybind = ctrl + shift + alt + code;
- if (Object.values(userPreferences).includes(newKeybind)) return;
- changeKeybindHandler(preferenceKey, newKeybind, false);
- }
- };
-
- return (
- <>
-
-
- {name}
-
-
-
-
-
- >
- );
-};
-
-export default KeybindItem;
+import { useSelector } from "react-redux";
+import { Grid, TextField, Typography } from "@mui/material";
+const { ipcRenderer } = window.require("electron");
+
+const getByPath = (obj, path) => {
+ if (!obj || !path) return undefined;
+
+ // NEW: first try direct key match (supports "optionDialogKeybinds.op1" stored literally)
+ if (Object.prototype.hasOwnProperty.call(obj, path)) return obj[path];
+
+ // Then try nested lookup (supports optionDialogKeybinds: { op1: ... })
+ if (!path.includes(".")) return obj[path];
+ return path.split(".").reduce((acc, part) => (acc ? acc[part] : undefined), obj);
+};
+
+
+const KeybindItem = ({ name, preferenceKey, changeKeybindHandler }) => {
+ // CHANGED: grab the whole ui slice so we can find where the live value really is
+ const ui = useSelector((state) => state.ui);
+
+ // CHANGED: search common locations for preferences
+ const candidates = [
+ ui?.preferences,
+ ui?.userPreferences,
+ ui?.settingsPreferences,
+ ui?.settings,
+ ui, // last resort
+ ];
+
+ let keybind;
+ for (const c of candidates) {
+ const v = getByPath(c, preferenceKey);
+ if (v !== undefined) {
+ keybind = v;
+ break;
+ }
+ }
+ if (!keybind) keybind = "None";
+
+ const handleInputFocus = () => {
+ ipcRenderer.send("focus");
+ };
+ const handleInputDefocus = (event) => {
+ ipcRenderer.send("defocus");
+ event.target.blur();
+ };
+
+ const handleKeyPress = (event) => {
+ event.preventDefault();
+ const key = event.key;
+ let code = event.code;
+ if (key === "Shift" || key === "Control" || key === "Alt" || event.repeat)
+ return;
+
+ if (key === "Delete") {
+ changeKeybindHandler(preferenceKey, "None");
+ } else {
+ const ctrl = event.ctrlKey ? "CTRL+" : "";
+ const shift = event.shiftKey ? "SHIFT+" : "";
+ const alt = event.altKey ? "ALT+" : "";
+ code = code.replace("Key", "").replace("Digit", "");
+ const newKeybind = ctrl + shift + alt + code;
+
+ // leave duplicate handling alone for now (we’ll improve later if needed)
+ changeKeybindHandler(preferenceKey, newKeybind, false);
+ }
+ };
+
+ return (
+ <>
+
+
+ {name}
+
+
+
+
+
+ >
+ );
+};
+
+export default KeybindItem;
diff --git a/src/components/settings/KeybindSetting/KeybindSetting.js b/src/components/settings/KeybindSetting/KeybindSetting.js
index 0786a7d..10b438a 100644
--- a/src/components/settings/KeybindSetting/KeybindSetting.js
+++ b/src/components/settings/KeybindSetting/KeybindSetting.js
@@ -1,45 +1,77 @@
-import { Stack, Typography } from "@mui/material";
-import KeybindItem from "./KeybindItem";
-
-const KeybindSetting = ({ settingChangeHandler }) => {
- return (
-
- Keybindings
-
- Click in the below boxes, then press a key combination. Press Delete to
- remove a keybinding.
-
-
- Restart of the app is needed to apply the new bindings.
-
-
-
-
-
-
-
-
- );
-};
-
-export default KeybindSetting;
+import { Stack, Typography } from "@mui/material";
+import KeybindItem from "./KeybindItem";
+
+const KeybindSetting = ({ settingChangeHandler, preferences }) => {
+ const optionKeybinds = preferences?.optionDialogKeybinds || {};
+
+ return (
+
+ Keybindings
+
+ Click in the below boxes, then press a key combination. Press Delete to
+ remove a keybinding.
+
+
+ Restart of the app is needed to apply the new bindings.
+
+
+ {/* Existing keybinds */}
+
+
+
+
+
+
+ {/* --- NEW Option Dialog Keybinds --- */}
+
+ Option Dialog Keybinds (op1–op4)
+
+
+
+
+
+
+ );
+};
+
+export default KeybindSetting;
diff --git a/src/components/settings/SettingsDialog.css b/src/components/settings/SettingsDialog.css
index e604af4..6566beb 100644
--- a/src/components/settings/SettingsDialog.css
+++ b/src/components/settings/SettingsDialog.css
@@ -1,4 +1,4 @@
-.MuiColorInput-Popover {
- width: 400px;
- transform: scale(0.7);
-}
+.MuiColorInput-Popover {
+ width: 400px;
+ transform: scale(0.7);
+}
diff --git a/src/components/settings/SettingsDialog.js b/src/components/settings/SettingsDialog.js
index f07ae56..7612edc 100644
--- a/src/components/settings/SettingsDialog.js
+++ b/src/components/settings/SettingsDialog.js
@@ -1,67 +1,69 @@
-import {
- Dialog,
- DialogContent,
- DialogTitle,
- Divider,
- IconButton,
- List,
- ListItem,
-} from "@mui/material";
-import CloseIcon from "@mui/icons-material/Close";
-import { uiActions } from "../../store/ui";
-import { saveSettingsPreferences } from "../../utils/savePreferences";
-import { useDispatch } from "react-redux";
-import AlwaysOnTopSetting from "./AlwaysOnTopSetting";
-import ButtonDelaySetting from "./ButtonDelaySetting";
-import CrosshairSetting from "./CrosshairSetting";
-import KeybindSetting from "./KeybindSetting/KeybindSetting";
-
-const SettingsDialog = ({ open, closeHandler }) => {
- const dispatch = useDispatch();
- const settingChangeHandler = (
- settingName,
- newValue,
- applyImmediately = true,
- ) => {
- const setting = {
- key: settingName,
- value: newValue,
- apply: applyImmediately,
- };
- dispatch(uiActions.setSettingsPreference(setting));
- saveSettingsPreferences(setting);
- };
-
- return (
-
- Settings
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-export default SettingsDialog;
+import {
+ Dialog,
+ DialogContent,
+ DialogTitle,
+ Divider,
+ IconButton,
+ List,
+ ListItem,
+} from "@mui/material";
+import CloseIcon from "@mui/icons-material/Close";
+import { saveSettingsPreferences } from "../../utils/savePreferences";
+import { useDispatch, useSelector } from "react-redux";
+import { uiActions } from "../../store/ui";
+import AlwaysOnTopSetting from "./AlwaysOnTopSetting";
+import ButtonDelaySetting from "./ButtonDelaySetting";
+import CrosshairSetting from "./CrosshairSetting";
+import KeybindSetting from "./KeybindSetting/KeybindSetting";
+
+const SettingsDialog = ({ open, closeHandler }) => {
+ const dispatch = useDispatch();
+ const preferences = useSelector((state) => state.ui.preferences);
+
+ const settingChangeHandler = (settingName, newValue, applyImmediately = true) => {
+ const setting = {
+ key: settingName,
+ value: newValue,
+ apply: applyImmediately,
+ };
+ dispatch(uiActions.setSettingsPreference(setting));
+ saveSettingsPreferences(setting);
+ };
+
+ return (
+
+ Settings
+
+
+
+
+
+
+
+
+
+
+ {/* Updated KeybindSetting now includes op1–op4 */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default SettingsDialog;
diff --git a/src/components/waypoints/WaypointFieldsLatLong.js b/src/components/waypoints/WaypointFieldsLatLong.js
index 6873078..bb1a2eb 100644
--- a/src/components/waypoints/WaypointFieldsLatLong.js
+++ b/src/components/waypoints/WaypointFieldsLatLong.js
@@ -1,59 +1,59 @@
-import { Box, Grid, Input, Typography } from "@mui/material";
-
-const WaypointFieldsLatLong = ({
- id,
- latHem,
- longHem,
- lat,
- long,
- elev,
- onElevation,
- handleInputFocus,
- handleInputDefocus,
- handleInputFinished,
-}) => {
- return (
-
-
-
-
- LAT
-
-
-
-
-
-
-
-
-
-
- LONG
-
-
-
-
-
-
-
-
-
-
- ELEV
-
-
-
- onElevation(e, id)}
- onMouseEnter={handleInputFocus}
- onMouseLeave={handleInputDefocus}
- onKeyDown={handleInputFinished}
- >
-
-
-
- );
-};
-
-export default WaypointFieldsLatLong;
+import { Box, Grid, Input, Typography } from "@mui/material";
+
+const WaypointFieldsLatLong = ({
+ id,
+ latHem,
+ longHem,
+ lat,
+ long,
+ elev,
+ onElevation,
+ handleInputFocus,
+ handleInputDefocus,
+ handleInputFinished,
+}) => {
+ return (
+
+
+
+
+ LAT
+
+
+
+
+
+
+
+
+
+
+ LONG
+
+
+
+
+
+
+
+
+
+
+ ELEV
+
+
+
+ onElevation(e, id)}
+ onMouseEnter={handleInputFocus}
+ onMouseLeave={handleInputDefocus}
+ onKeyDown={handleInputFinished}
+ >
+
+
+
+ );
+};
+
+export default WaypointFieldsLatLong;
diff --git a/src/components/waypoints/WaypointFieldsMGRS.js b/src/components/waypoints/WaypointFieldsMGRS.js
index f08f6c1..4ef4b2a 100644
--- a/src/components/waypoints/WaypointFieldsMGRS.js
+++ b/src/components/waypoints/WaypointFieldsMGRS.js
@@ -1,45 +1,45 @@
-import { Box, Grid, Input, Typography } from "@mui/material";
-
-const WaypointFieldsMGRS = ({
- id,
- mgrs,
- elev,
- onElevation,
- handleInputFocus,
- handleInputDefocus,
- handleInputFinished,
-}) => {
- return (
-
-
-
-
- MGRS
-
-
-
-
-
-
-
-
-
-
- ELEV
-
-
-
- onElevation(e, id)}
- onMouseEnter={handleInputFocus}
- onMouseLeave={handleInputDefocus}
- onKeyDown={handleInputFinished}
- >
-
-
-
- );
-};
-
-export default WaypointFieldsMGRS;
+import { Box, Grid, Input, Typography } from "@mui/material";
+
+const WaypointFieldsMGRS = ({
+ id,
+ mgrs,
+ elev,
+ onElevation,
+ handleInputFocus,
+ handleInputDefocus,
+ handleInputFinished,
+}) => {
+ return (
+
+
+
+
+ MGRS
+
+
+
+
+
+
+
+
+
+
+ ELEV
+
+
+
+ onElevation(e, id)}
+ onMouseEnter={handleInputFocus}
+ onMouseLeave={handleInputDefocus}
+ onKeyDown={handleInputFinished}
+ >
+
+
+
+ );
+};
+
+export default WaypointFieldsMGRS;
diff --git a/src/components/waypoints/WaypointItem.css b/src/components/waypoints/WaypointItem.css
index 457dd98..75c481b 100644
--- a/src/components/waypoints/WaypointItem.css
+++ b/src/components/waypoints/WaypointItem.css
@@ -1,3 +1,3 @@
-.dragHandle:hover {
- cursor: ns-resize;
-}
+.dragHandle:hover {
+ cursor: ns-resize;
+}
diff --git a/src/components/waypoints/WaypointItem.js b/src/components/waypoints/WaypointItem.js
index a0e1434..a61f4d7 100644
--- a/src/components/waypoints/WaypointItem.js
+++ b/src/components/waypoints/WaypointItem.js
@@ -1,145 +1,145 @@
-import {
- Button,
- IconButton,
- ListItem,
- ListItemText,
- Stack,
- Grid,
- TextField,
- Box,
- Tooltip,
- Collapse,
- Grow,
-} from "@mui/material";
-import {
- DragHandle,
- ArrowDropDown,
- ArrowDropUp,
- Delete,
-} from "@mui/icons-material";
-import { useSortable } from "@dnd-kit/sortable";
-import { CSS } from "@dnd-kit/utilities";
-
-import "./WaypointItem.css";
-import WaypointFieldsMGRS from "./WaypointFieldsMGRS";
-import WaypointFieldsLatLong from "./WaypointFieldsLatLong";
-
-const { ipcRenderer } = window.require("electron");
-
-const WaypointItem = ({
- wp,
- pending,
- onSave,
- onDelete,
- onRename,
- expanded,
- onExpand,
- onElevation,
-}) => {
- const { attributes, listeners, setNodeRef, transform, transition } =
- useSortable({ id: wp?.id });
- const style = {
- transform: CSS.Transform.toString(transform),
- transition,
- };
-
- const handleInputFocus = () => {
- ipcRenderer.send("focus");
- };
- const handleInputDefocus = (event) => {
- ipcRenderer.send("defocus");
- event.target.blur();
- };
- const handleInputFinished = (event) => {
- if (event.key === "Enter") {
- onExpand(wp?.id, !expanded);
- handleInputDefocus();
- }
- };
-
- return (
-
-
-
-
-
-
- {expanded ? (
- onRename(e, wp?.id)}
- onMouseEnter={handleInputFocus}
- onMouseLeave={handleInputDefocus}
- onKeyDown={handleInputFinished}
- onFocus={(e) => e.target.select()}
- />
- ) : (
- {wp?.name || "New Waypoint"}
- )}
-
- {wp?.MGRS ? (
-
- ) : (
-
- )}
-
-
-
-
- {pending ? (
-
-
-
- Save
-
-
-
- ) : (
-
- onDelete(e, wp?.id)}>
-
-
- onExpand(wp?.id, !expanded)}>
- {expanded ? : }
-
-
-
-
-
- )}
-
-
-
-
- );
-};
-
-export default WaypointItem;
+import {
+ Button,
+ IconButton,
+ ListItem,
+ ListItemText,
+ Stack,
+ Grid,
+ TextField,
+ Box,
+ Tooltip,
+ Collapse,
+ Grow,
+} from "@mui/material";
+import {
+ DragHandle,
+ ArrowDropDown,
+ ArrowDropUp,
+ Delete,
+} from "@mui/icons-material";
+import { useSortable } from "@dnd-kit/sortable";
+import { CSS } from "@dnd-kit/utilities";
+
+import "./WaypointItem.css";
+import WaypointFieldsMGRS from "./WaypointFieldsMGRS";
+import WaypointFieldsLatLong from "./WaypointFieldsLatLong";
+
+const { ipcRenderer } = window.require("electron");
+
+const WaypointItem = ({
+ wp,
+ pending,
+ onSave,
+ onDelete,
+ onRename,
+ expanded,
+ onExpand,
+ onElevation,
+}) => {
+ const { attributes, listeners, setNodeRef, transform, transition } =
+ useSortable({ id: wp?.id });
+ const style = {
+ transform: CSS.Transform.toString(transform),
+ transition,
+ };
+
+ const handleInputFocus = () => {
+ ipcRenderer.send("focus");
+ };
+ const handleInputDefocus = (event) => {
+ ipcRenderer.send("defocus");
+ event.target.blur();
+ };
+ const handleInputFinished = (event) => {
+ if (event.key === "Enter") {
+ onExpand(wp?.id, !expanded);
+ handleInputDefocus();
+ }
+ };
+
+ return (
+
+
+
+
+
+
+ {expanded ? (
+ onRename(e, wp?.id)}
+ onMouseEnter={handleInputFocus}
+ onMouseLeave={handleInputDefocus}
+ onKeyDown={handleInputFinished}
+ onFocus={(e) => e.target.select()}
+ />
+ ) : (
+ {wp?.name || "New Waypoint"}
+ )}
+
+ {wp?.MGRS ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {pending ? (
+
+
+
+ Save
+
+
+
+ ) : (
+
+ onDelete(e, wp?.id)}>
+
+
+ onExpand(wp?.id, !expanded)}>
+ {expanded ? : }
+
+
+
+
+
+ )}
+
+
+
+
+ );
+};
+
+export default WaypointItem;
diff --git a/src/components/waypoints/WaypointList.js b/src/components/waypoints/WaypointList.js
index 56fd066..f05e57e 100644
--- a/src/components/waypoints/WaypointList.js
+++ b/src/components/waypoints/WaypointList.js
@@ -1,157 +1,157 @@
-import { Box, Button, Card, List, Typography } from "@mui/material";
-import WaypointItem from "./WaypointItem";
-import { useDispatch, useSelector } from "react-redux";
-import { waypointsActions } from "../../store/waypoints";
-import Convertors from "../../utils/Convertors";
-import { closestCenter, DndContext } from "@dnd-kit/core";
-import {
- restrictToVerticalAxis,
- restrictToParentElement,
- restrictToFirstScrollableAncestor,
-} from "@dnd-kit/modifiers";
-import {
- SortableContext,
- verticalListSortingStrategy,
-} from "@dnd-kit/sortable";
-import ConvertModuleWaypoints from "../../utils/ConvertModuleWaypoints";
-import { useEffect, useRef, useState } from "react";
-
-const WaypointList = () => {
- const isPending = useSelector((state) => state.ui.pendingWaypoint);
- const { lat, long, elev, module } = useSelector((state) => state.dcsPoint);
- const dcsWaypoints = useSelector((state) => state.waypoints.dcsWaypoints);
- const dispatch = useDispatch();
- const [expandedWaypointId, setExpandedWaypointId] = useState(-1);
-
- const moduleCoordinates = ConvertModuleWaypoints(dcsWaypoints, module);
- const hasWaypoints = dcsWaypoints.length > 0;
- const ref = useRef(null);
- useEffect(() => {
- if (dcsWaypoints.length) {
- ref.current?.scrollIntoView({
- behavior: "smooth",
- block: "end",
- });
- }
- }, [dcsWaypoints.length]);
-
- const saveWaypointHandler = () => {
- dispatch(
- waypointsActions.addDcsWaypoint({
- lat,
- long,
- elev,
- }),
- );
- };
- const deleteHandler = (event, id) => {
- dispatch(waypointsActions.delete(id));
- };
-
- const deleteAllHandler = () => dispatch(waypointsActions.deleteAll());
-
- const renameHandler = (event, id) => {
- const name = event.target.value;
- if (name.length > 0) dispatch(waypointsActions.changeName({ id, name }));
- };
-
- const elevationHandler = (event, id) => {
- const enteredValue = event.target.value;
- if (!isNaN(enteredValue)) {
- const elev = Convertors.fToM(event.target.value);
- dispatch(waypointsActions.changeElevation({ id, elev }));
- }
- };
-
- const expandHandler = (id, isExpanded) => {
- isExpanded ? setExpandedWaypointId(id) : setExpandedWaypointId(-1);
- };
-
- const checkIfExpanded = (id) => expandedWaypointId === id;
-
- const handleDragEnd = (event) => {
- const { active, over } = event;
- if (active.id !== over.id) {
- dispatch(
- waypointsActions.changeOrder({ active: active.id, over: over.id }),
- );
- }
- };
-
- return (
-
-
-
-
- {hasWaypoints || isPending ? (
- <>
- {moduleCoordinates.map((wp) => (
-
- ))}
-
- {isPending && (
-
- )}
- >
- ) : (
-
-
- No waypoints selected...
-
-
- )}
-
-
- {hasWaypoints && (
-
-
- Clear All
-
-
- )}
-
-
-
- );
-};
-
-export default WaypointList;
+import { Box, Button, Card, List, Typography } from "@mui/material";
+import WaypointItem from "./WaypointItem";
+import { useDispatch, useSelector } from "react-redux";
+import { waypointsActions } from "../../store/waypoints";
+import Convertors from "../../utils/Convertors";
+import { closestCenter, DndContext } from "@dnd-kit/core";
+import {
+ restrictToVerticalAxis,
+ restrictToParentElement,
+ restrictToFirstScrollableAncestor,
+} from "@dnd-kit/modifiers";
+import {
+ SortableContext,
+ verticalListSortingStrategy,
+} from "@dnd-kit/sortable";
+import ConvertModuleWaypoints from "../../utils/ConvertModuleWaypoints";
+import { useEffect, useRef, useState } from "react";
+
+const WaypointList = () => {
+ const isPending = useSelector((state) => state.ui.pendingWaypoint);
+ const { lat, long, elev, module } = useSelector((state) => state.dcsPoint);
+ const dcsWaypoints = useSelector((state) => state.waypoints.dcsWaypoints);
+ const dispatch = useDispatch();
+ const [expandedWaypointId, setExpandedWaypointId] = useState(-1);
+
+ const moduleCoordinates = ConvertModuleWaypoints(dcsWaypoints, module);
+ const hasWaypoints = dcsWaypoints.length > 0;
+ const ref = useRef(null);
+ useEffect(() => {
+ if (dcsWaypoints.length) {
+ ref.current?.scrollIntoView({
+ behavior: "smooth",
+ block: "end",
+ });
+ }
+ }, [dcsWaypoints.length]);
+
+ const saveWaypointHandler = () => {
+ dispatch(
+ waypointsActions.addDcsWaypoint({
+ lat,
+ long,
+ elev,
+ }),
+ );
+ };
+ const deleteHandler = (event, id) => {
+ dispatch(waypointsActions.delete(id));
+ };
+
+ const deleteAllHandler = () => dispatch(waypointsActions.deleteAll());
+
+ const renameHandler = (event, id) => {
+ const name = event.target.value;
+ if (name.length > 0) dispatch(waypointsActions.changeName({ id, name }));
+ };
+
+ const elevationHandler = (event, id) => {
+ const enteredValue = event.target.value;
+ if (!isNaN(enteredValue)) {
+ const elev = Convertors.fToM(event.target.value);
+ dispatch(waypointsActions.changeElevation({ id, elev }));
+ }
+ };
+
+ const expandHandler = (id, isExpanded) => {
+ isExpanded ? setExpandedWaypointId(id) : setExpandedWaypointId(-1);
+ };
+
+ const checkIfExpanded = (id) => expandedWaypointId === id;
+
+ const handleDragEnd = (event) => {
+ const { active, over } = event;
+ if (active.id !== over.id) {
+ dispatch(
+ waypointsActions.changeOrder({ active: active.id, over: over.id }),
+ );
+ }
+ };
+
+ return (
+
+
+
+
+ {hasWaypoints || isPending ? (
+ <>
+ {moduleCoordinates.map((wp) => (
+
+ ))}
+
+ {isPending && (
+
+ )}
+ >
+ ) : (
+
+
+ No waypoints selected...
+
+
+ )}
+
+
+ {hasWaypoints && (
+
+
+ Clear All
+
+
+ )}
+
+
+
+ );
+};
+
+export default WaypointList;
diff --git a/src/data/buildUnitLookup.js b/src/data/buildUnitLookup.js
new file mode 100644
index 0000000..ebf084a
--- /dev/null
+++ b/src/data/buildUnitLookup.js
@@ -0,0 +1,49 @@
+/**
+ * Build a fast lookup map from an editable UNIT_CATALOG.
+ * Supports:
+ * - "F-16C_50" (exact string)
+ * - { prefix: "Mirage-F1" }
+ * - { contains: "Mirage-F1" }
+ * - { regex: "^Mirage-F1" } (JS regex string)
+ *
+ * Requires `allTypes` list so patterns can expand.
+ */
+export function buildUnitLookup(unitCatalog, allTypes = []) {
+ const lookup = {};
+ const all = new Set(allTypes);
+
+ const expandSpec = (spec) => {
+ if (typeof spec === "string") return [spec];
+
+ if (spec && typeof spec === "object") {
+ if (spec.prefix) {
+ return [...all].filter((t) => t.startsWith(spec.prefix));
+ }
+ if (spec.contains) {
+ return [...all].filter((t) => t.includes(spec.contains));
+ }
+ if (spec.regex) {
+ const r = new RegExp(spec.regex);
+ return [...all].filter((t) => r.test(t));
+ }
+ }
+ return [];
+ };
+
+ for (const cat of unitCatalog || []) {
+ const category = cat?.category ?? "Unknown";
+ for (const sub of cat?.subcategories || []) {
+ const subcategory = sub?.name ?? "Unknown";
+
+ for (const spec of sub?.units || []) {
+ const types = expandSpec(spec);
+ for (const type of types) {
+ if (!type) continue;
+ lookup[type] = { category, subcategory };
+ }
+ }
+ }
+ }
+
+ return lookup;
+}
\ No newline at end of file
diff --git a/src/data/unitCatalog-Editme.js b/src/data/unitCatalog-Editme.js
new file mode 100644
index 0000000..ac00fa9
--- /dev/null
+++ b/src/data/unitCatalog-Editme.js
@@ -0,0 +1,1045 @@
+/* Editable catalog: Category -> Subcategory -> unit type strings */
+
+export const UNIT_CATALOG = [
+ ////////////////////////////////////////// ARMOURED PERSONNEL CARRIER //////////////////////////////////////////
+ {
+ "category": "Armour",
+ "subcategories": [
+ {
+ "name": "APC",
+ "units": [
+ "AAV7",
+ "BTR-60",
+ "BTR-70",
+ "BTR-80",
+ "LARC-V",
+ "M-113",
+ "M2A1_halftrack",
+ "M4_Tractor",
+ "MTLB",
+ "Sd_Kfz_251",
+ "TPZ"
+ ]
+ },
+ ////////////////////////////////////////// INFANTRY FIGHTING VEHICLE //////////////////////////////////////////
+ {
+ "name": "IFV",
+ "units": [
+ "BMD-1",
+ "BMP-1",
+ "BMP-2",
+ "BMP-3",
+ "BTR-82A",
+ "CHAP_BMPT",
+ "LAV-25",
+ "M-2 Bradley",
+ "M1126 Stryker ICV",
+ "Marder",
+ "MCV-80",
+ "ZBD04A"
+ ]
+ },
+ ////////////////////////////////////////// MAIN BATTLE TANK //////////////////////////////////////////
+ {
+ "name": "MBT",
+ "units": [
+ "Challenger2",
+ "CHAP_T64BV",
+ "CHAP_T84OplotM",
+ "CHAP_T90M",
+ "Chieftain_mk3",
+ "Leclerc",
+ "Leopard-2",
+ "leopard-2A4",
+ "leopard-2A4_trs",
+ "Leopard-2A5",
+ "Leopard1A3",
+ "M-1 Abrams",
+ "M-60",
+ "M1A2C_SEP_V3",
+ "Merkava_Mk4",
+ "T-55",
+ "T-72B",
+ "T-72B3",
+ "T-80B",
+ "T-80UD",
+ "T-90",
+ "T62M",
+ "TYPE-59",
+ "ZTZ96B"
+ ]
+ },
+ ////////////////////////////////////////// SCOUT RECON //////////////////////////////////////////
+ {
+ "name": "Scout Recon",
+ "units": [
+ "BRDM-2",
+ "CHAP_FV101",
+ "CHAP_FV107",
+ "CHAP_M1130",
+ "CHAP_MATV",
+ "Cobra",
+ "Daimler_AC",
+ "M1043 HMMWV Armament",
+ "M8_Greyhound",
+ "MaxxPro_MRAP",
+ "Sd_Kfz_234_2_Puma"
+ ]
+ },
+ ////////////////////////////////////////// ATGM SELF PROPELLED //////////////////////////////////////////
+ {
+ "name": "SP ATGM",
+ "units": [
+ "BRDM-2_malyutka",
+ "BTR_D",
+ "M1045 HMMWV TOW",
+ "M1134 Stryker ATGM",
+ "VAB_Mephisto"
+ ]
+ },
+ ////////////////////////////////////////// SELF PROPELLED GUN //////////////////////////////////////////
+ {
+ "name": "SP Gun",
+ "units": [
+ "Elefant_SdKfz_184",
+ "Jagdpanther_G1",
+ "JagdPz_IV",
+ "M10_GMC",
+ "M1128 Stryker MGS",
+ "Stug_III",
+ "Stug_IV",
+ "SturmPzIV"
+ ]
+ },
+ ////////////////////////////////////////// TANK //////////////////////////////////////////
+ {
+ "name": "Tank",
+ "units": [
+ "Centaur_IV",
+ "Churchill_VII",
+ "Cromwell_IV",
+ "M4_Sherman",
+ "M4A4_Sherman_FF",
+ "PT_76",
+ "Pz_IV_H",
+ "Pz_V_Panther_G",
+ "T-34-85",
+ "Tetrarch",
+ "Tiger_I",
+ "Tiger_II_H",
+ "Type_89_I_Go",
+ "Type_98_Ke_Ni"
+ ]
+ }
+ ]
+ },
+ ////////////////////////////////////////// ARTILLERY //////////////////////////////////////////
+ {
+ "category": "Artillery",
+ "subcategories": [
+ {
+ "name": "Command",
+ "units": [
+ "Grad_FDDM",
+ "MLRS FDDM"
+ ]
+ },
+ ////////////////////////////////////////// HOWITZER //////////////////////////////////////////
+ {
+ "name": "Howitzer",
+ "units": [
+ "L118_Unit",
+ "LeFH_18-40-105",
+ "M2A1-105",
+ "Pak40"
+ ]
+ },
+ ////////////////////////////////////////// MLRS //////////////////////////////////////////
+ {
+ "name": "MLRS",
+ "units": [
+ "CHAP_M142_ATACMS_M39A1",
+ "CHAP_M142_ATACMS_M48",
+ "CHAP_M142_GMLRS_M30",
+ "CHAP_M142_GMLRS_M31",
+ "CHAP_TOS1A",
+ "Grad-URAL",
+ "HL_B8M1",
+ "MLRS",
+ "Smerch",
+ "Smerch_HE",
+ "tt_B8M1",
+ "Uragan_BM-27"
+ ]
+ },
+ ////////////////////////////////////////// MORTAR //////////////////////////////////////////
+ {
+ "name": "Mortar",
+ "units": [
+ "2B11 mortar"
+ ]
+ },
+ ////////////////////////////////////////// ARTILLERY - SELF PROPELLED //////////////////////////////////////////
+ {
+ "name": "Self Propelled",
+ "units": [
+ "M-109",
+ "M12_GMC",
+ "PLZ05",
+ "SAU 2-C9",
+ "SAU Akatsia",
+ "SAU Gvozdika",
+ "SAU Msta",
+ "SpGH_Dana",
+ "T155_Firtina",
+ "Wespe124"
+ ]
+ }
+ ]
+ },
+ ////////////////////////////////////////// FORTIFICATION //////////////////////////////////////////
+ {
+ "category": "Fortification",
+ "subcategories": [
+ {
+ "name": "Buildings",
+ "units": [
+ "fire_control",
+ "house1arm",
+ "house2arm",
+ "houseA_arm",
+ "outpost",
+ "outpost_road",
+ "outpost_road_l",
+ "outpost_road_r",
+ "SK_C_28_naval_gun",
+ "TACAN_beacon"
+ ]
+ },
+ {
+ "name": "Pillbox",
+ "units": [
+ "Bunker",
+ "Sandbox"
+ ]
+ }
+ ]
+ },
+ ////////////////////////////////////////// INFANTRY //////////////////////////////////////////
+ {
+ "category": "Infantry",
+ "subcategories": [
+ {
+ "name": "Rifleman",
+ "units": [
+ "Infantry AK",
+ "Infantry AK Ins",
+ "Infantry AK ver2",
+ "Infantry AK ver3",
+ "JTAC",
+ "Paratrooper AKS-74",
+ "Soldier AK",
+ "Soldier M249",
+ "Soldier M4",
+ "Soldier M4 GRG",
+ "soldier_mauser98",
+ "soldier_wwii_us",
+ "soldier_wwii_br_01"
+ ]
+ },
+ ////////////////////////////////////////// OBSERVER //////////////////////////////////////////
+ {
+ "name": "Observer",
+ "units": [
+ "JTAC"
+ ]
+ },
+ ////////////////////////////////////////// RIFLEMAN //////////////////////////////////////////
+ {
+ "name": "RPG",
+ "units": [
+ "Soldier RPG",
+ "Paratrooper RPG-16"
+ ]
+ },
+ ////////////////////////////////////////// MANPAD SUPPORT //////////////////////////////////////////
+ {
+ "name": "Support",
+ "units": [
+ "SA-18 Igla comm",
+ "SA-18 Igla-S comm",
+ "Stinger comm",
+ "Stinger comm dsr"
+ ]
+ }
+ ]
+ },
+ ////////////////////////////////////////// MISSILES SSM //////////////////////////////////////////
+ {
+ "category": "Missiles",
+ "subcategories": [
+ {
+ "name": "SSM",
+ "units": [
+ "CHAP_9K720_Cluster",
+ "CHAP_9K720_HE",
+ "hy_launcher",
+ "Scud_B",
+ "v1_launcher",
+ "Silkworm_SR"
+ ]
+ }
+ ]
+ },
+ ////////////////////////////////////////// UNARMED //////////////////////////////////////////
+ {
+ "category": "Unarmed",
+ "subcategories": [
+ {
+ "name": "Ammunition",
+ "units": [
+ "PL5EII Loadout",
+ "PL8 Loadout",
+ "SD10 Loadout"
+ ]
+ },
+ ////////////////////////////////////////// BEACONS //////////////////////////////////////////
+ {
+ "name": "Beacon",
+ "units": [
+ "prmg_gp_beacon",
+ "prmg_loc_beacon",
+ "rsbn_beacon"
+ ]
+ },
+ ////////////////////////////////////////// BUS //////////////////////////////////////////
+ {
+ "name": "Bus",
+ "units": [
+ "IKARUS Bus",
+ "LAZ Bus",
+ "LiAZ Bus",
+ "Trolley bus"
+ ]
+ },
+ ////////////////////////////////////////// CAR //////////////////////////////////////////
+ {
+ "name": "Car",
+ "units": [
+ "Horch_901_typ_40_kfz_21",
+ "Hummer",
+ "Kubelwagen_82",
+ "Land_Rover_109_S3",
+ "Sd_Kfz_2",
+ "UAZ-469",
+ "VAZ Car",
+ "Willys_MB"
+ ]
+ },
+ ////////////////////////////////////////// COMMAND //////////////////////////////////////////
+ {
+ "name": "Command",
+ "units": [
+ "Predator GCS",
+ "Predator TrojanSpirit",
+ "SKP-11",
+ "Ural-375 PBU",
+ "ZIL-131 KUNG"
+ ]
+ },
+ ////////////////////////////////////////// FIRE FIGHTER //////////////////////////////////////////
+ {
+ "name": "Firefighter",
+ "units": [
+ "AA8",
+ "HEMTT TFFT",
+ "tacr2a",
+ "Ural ATsP-6"
+ ]
+ },
+ ////////////////////////////////////////// GPS SPOOFER //////////////////////////////////////////
+ {
+ "name": "GPS Spoofer",
+ "units": [
+ "GPS_Spoofer_Blue",
+ "GPS_Spoofer_Red"
+ ]
+ },
+ ////////////////////////////////////////// GPU //////////////////////////////////////////
+ {
+ "name": "GPU",
+ "units": [
+ "Ural-4320 APA-5D",
+ "ZiL-131 APA-80"
+ ]
+ },
+ ////////////////////////////////////////// TANKER //////////////////////////////////////////
+ {
+ "name": "Tanker",
+ "units": [
+ "ATMZ-5",
+ "ATZ-10",
+ "ATZ-5",
+ "M978 HEMTT Tanker",
+ "r11_volvo_drivable",
+ "ural_atz5_civil"
+ ]
+ },
+ ////////////////////////////////////////// TRACTOR //////////////////////////////////////////
+ {
+ "name": "Tractor",
+ "units": [
+ "ATZ-60_Maz",
+ "S_75_ZIL",
+ "TZ-22_KrAZ",
+ "zil-131_civil"
+ ]
+ },
+ ////////////////////////////////////////// TRANSPORT //////////////////////////////////////////
+ {
+ "name": "Transport",
+ "units": [
+ "Blitz_36-6700A",
+ "CHAP_M1083",
+ "Land_Rover_101_FC",
+ "M30_CC",
+ "Sd_Kfz_7",
+ "Type_98_So_Da"
+ ]
+ },
+ ////////////////////////////////////////// TRUCK //////////////////////////////////////////
+ {
+ "name": "Truck",
+ "units": [
+ "Bedford_MWD",
+ "CCKW_353",
+ "GAZ-3307",
+ "GAZ-3308",
+ "GAZ-66",
+ "gaz-66_civil",
+ "KAMAZ Truck",
+ "kamaz_tent_civil",
+ "KrAZ6322",
+ "M 818",
+ "MAZ-6303",
+ "Type_94_Truck",
+ "Ural-375",
+ "Ural-4320-31",
+ "Ural-4320T",
+ "ural_4230_civil_b",
+ "ural_4230_civil_t",
+ "ZIL-135",
+ "ZIL-4331"
+ ]
+ },
+ ////////////////////////////////////////// TUG //////////////////////////////////////////
+ {
+ "name": "Tug",
+ "units": [
+ "B600_drivable",
+ "GD-20",
+ "MJ-1_drivable",
+ "TugHarlan_drivable"
+ ]
+ },
+ ////////////////////////////////////////// VAN //////////////////////////////////////////
+ {
+ "name": "Van",
+ "units": [
+ "P20_drivable",
+ "Tigr_233036"
+ ]
+ }
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE FLAK //////////////////////////////////////////
+ {
+ "category": "AAA",
+ "subcategories": [
+ {
+ "name": "FLAK",
+ "units": [
+ "flak18",
+ "flak36",
+ "flak37",
+ "flak41",
+ "KS-19",
+ "QF_37_AA",
+ "Type_3_80mm_AA",
+ "Type_88_75mm_AA"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE CANNON //////////////////////////////////////////
+ {
+ "name": "Cannon",
+ "units": [
+ "Allies_Director",
+ "bofors40",
+ "flak30",
+ "flak38",
+ "Flakscheinwerfer_37",
+ "HL_DSHK",
+ "HL_KORD",
+ "KDO_Mod40",
+ "M1_37mm",
+ "M45_Quadmount",
+ "Maschinensatz_33",
+ "S-60_Type59_Artillery",
+ "SON_9",
+ "Type_96_25mm_AA",
+ "ZU-23 Closed Insurgent",
+ "ZU-23 Emplacement",
+ "ZU-23 Emplacement Closed",
+ "ZU-23 Insurgent",
+ "HL_ZU-23",
+ "tt_ZU-23",
+ "tt_DSHK",
+ "tt_KORD",
+ "Type_94_25mm_AA_Truck",
+ "Ural-375 ZU-23",
+ "Ural-375 ZU-23 Insurgent",
+ "ZSU_57_2"
+ ]
+ }
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE SAM //////////////////////////////////////////
+ {
+ "category": "SAM",
+ "subcategories": [
+ {
+ "name": "SA-2",
+ "units": [
+ "S_75M_Volhov",
+ "RD_75",
+ "SNR_75V"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE SA-3 //////////////////
+ {
+ "name": "SA-3",
+ "units": [
+ "5p73 s-125 ln",
+ "snr s-125 tr",
+ "p-19 s-125 sr"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE SA-5 //////////////////
+ {
+ "name": "SA-5",
+ "units": [
+ "S-200_Launcher",
+ "RPC_5N62V"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE SA-6 //////////////////
+ {
+ "name": "SA-6",
+ "units": [
+ "Kub 2P25 ln",
+ "Kub 1S91 str"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE SA-10 //////////////////
+ {
+ "name": "SA-10",
+ "units": [
+ "S-300PS 5P85C ln",
+ "S-300PS 5P85D ln",
+ "S-300PS 54K6 cp",
+ "S-300PS 64H6E sr",
+ "S-300PS 40B6MD sr",
+ "S-300PS 40B6MD sr_19J6",
+ "S-300PS 40B6M tr",
+ "S-300PS 5H63C 30H6_tr",
+ "P14_SR",
+ "RLS_19J6",
+ "generator_5i57"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE SA-11 //////////////////
+ {
+ "name": "SA-11",
+ "units": [
+ "SA-11 Buk CC 9S470M1",
+ "SA-11 Buk SR 9S18M1",
+ "SA-11 Buk LN 9A310M1"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE HAWK //////////////////
+ {
+ "name": "HAWK",
+ "units": [
+ "Hawk pcp",
+ "Hawk ln",
+ "Hawk sr",
+ "Hawk tr",
+ "Hawk cwar"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE PATRIOT //////////////////
+ {
+ "name": "PATRIOT",
+ "units": [
+ "Patriot ln",
+ "Patriot cp",
+ "Patriot ECS",
+ "Patriot AMG",
+ "Patriot EPP",
+ "Patriot str"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE IRIS-T //////////////////
+ {
+ "name": "IRIS-T",
+ "units": [
+ "CHAP_IRISTSLM_CP",
+ "CHAP_IRISTSLM_LN",
+ "CHAP_IRISTSLM_STR"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE EWR //////////////////
+ {
+ "name": "EWR",
+ "units": [
+ "FPS-117",
+ "FPS-117 Dome",
+ "FPS-117 ECS",
+ "FuMG-401",
+ "FuSe-65",
+ "1L13 EWR",
+ "55G6 EWR"
+ ]
+ }
+ ]
+ },
+ ////////////////////////////////////////// SHORAD AVENGER //////////////////
+ {
+ "category": "SAM - Shorad",
+ "subcategories": [
+ {
+ "name": "Avenger",
+ "units": [
+ "M1097 Avenger"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE CHAPPARAL //////////////////
+ {
+ "name": "Chapparal",
+ "units": [
+ "M48 Chaparral"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE LINEBACKER //////////////////
+ {
+ "name": "Linebacker",
+ "units": [
+ "M6 Linebacker"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE RAPIER //////////////////
+ {
+ "name": "Rapier",
+ "units": [
+ "rapier_fsa_launcher",
+ "rapier_fsa_blindfire_radar",
+ "rapier_fsa_optical_tracker_unit"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE ROLAND //////////////////
+ {
+ "name": "Roland",
+ "units": [
+ "Roland ADS",
+ "Roland Radar"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE SA-8 //////////////////
+ {
+ "name": "SA-8",
+ "units": [
+ "Osa 9A33 ln"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE SA-9 //////////////////
+ {
+ "name": "SA-9",
+ "units": [
+ "Strela-1 9P31"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE SA-13 //////////////////
+ {
+ "name": "SA-13",
+ "units": [
+ "Strela-10M3"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE SA-15 //////////////////
+ {
+ "name": "SA-15",
+ "units": [
+ "Tor 9A331",
+ "CHAP_TorM2"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE SA-19 //////////////////
+ {
+ "name": "SA-19",
+ "units": [
+ "2S6 Tunguska",
+ "CHAP_PantsirS1",
+ "Dog Ear radar"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE MANPAD //////////////////
+ {
+ "name": "MANPAD",
+ "units": [
+ "Igla manpad INS",
+ "SA-18 Igla manpad",
+ "SA-18 Igla-S manpad",
+ "Soldier stinger"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE NASAMS //////////////////
+ {
+ "name": "NASAMS",
+ "units": [
+ "NASAMS_Command_Post",
+ "NASAMS_Radar_MPQ64F1",
+ "NASAMS_LN_B",
+ "NASAMS_LN_C"
+ ]
+ },
+ ////////////////////////////////////////// AIR DEFENCE HQ-7 //////////////////
+ {
+ "name": "HQ-7",
+ "units": [
+ "HQ-7_LN_P",
+ "HQ-7_LN_SP",
+ "HQ-7_STR_SP"
+ ]
+ }
+ ]
+ },
+ ////////////////////////////////////////// SHORAD GUN //////////////////////////////////////////
+ {
+ "category": "SHORAD - Gun",
+ "subcategories": [
+ {
+ "name": "Guided Cannon",
+ "units": [
+ "Gepard",
+ "HEMTT_C-RAM_Phalanx",
+ "Vulcan",
+ "ZSU-23-4 Shilka"
+ ]
+ }
+ ]
+ },
+ ////////////////////////////////////////// NAVAL //////////////////////////////////////////
+ {
+ "category": "Naval",
+ "subcategories": [
+ {
+ "name": "Amphibious",
+ "units": [
+ "BDK-775",
+ "LST_Mk2",
+ "USS_Samuel_Chase"
+ ]
+ },
+ {
+ "name": "BattleCruiser",
+ "units": [
+ "PIOTR"
+ ]
+ },
+ {
+ "name": "Carrier",
+ "units": [
+ "ara_vdm",
+ "atconveyor",
+ "CV_1143_5",
+ "CVN_71",
+ "CVN_72",
+ "CVN_73",
+ "CVN_75",
+ "Essex",
+ "Forrestal",
+ "hms_invincible",
+ "KUZNECOW",
+ "LHA_Tarawa",
+ "Stennis"
+ ]
+ },
+ {
+ "name": "Civilian",
+ "units": [
+ "Dry-cargo ship-1",
+ "Dry-cargo ship-2",
+ "ELNYA",
+ "HandyWind",
+ "Seawise_Giant",
+ "Ship_Tilde_Supply",
+ "ZWEZDNY"
+ ]
+ },
+ {
+ "name": "Corvette/Patrol",
+ "units": [
+ "ALBATROS",
+ "CastleClass_01",
+ "CHAP_Project22160",
+ "CHAP_Project22160_TorM2KM",
+ "MOLNIYA"
+ ]
+ },
+ {
+ "name": "Cruiser",
+ "units": [
+ "MOSCOW",
+ "TICONDEROG"
+ ]
+ },
+ {
+ "name": "Destroyer",
+ "units": [
+ "Type_052B",
+ "Type_052C",
+ "USS_Arleigh_Burke_IIa"
+ ]
+ },
+ {
+ "name": "Fast Attack",
+ "units": [
+ "La_Combattante_II",
+ "Schnellboot_type_S130",
+ "speedboat",
+ "Type_021_1"
+ ]
+ },
+ {
+ "name": "Frigate",
+ "units": [
+ "leander-gun-achilles",
+ "leander-gun-andromeda",
+ "leander-gun-ariadne",
+ "leander-gun-condell",
+ "leander-gun-lynch",
+ "NEUSTRASH",
+ "PERRY",
+ "REZKY",
+ "Type_054A"
+ ]
+ },
+ {
+ "name": "Submarine",
+ "units": [
+ "IMPROVED_KILO",
+ "KILO",
+ "santafe",
+ "Type_093",
+ "Uboat_VIIC"
+ ]
+ },
+ {
+ "name": "Support",
+ "units": [
+ "HarborTug",
+ "Higgins_boat"
+ ]
+ }
+ ]
+ },
+ ////////////////////////////////////////// AERIAL //////////////////////////////////////////
+ {
+ "category": "Aerial",
+ "subcategories": [
+ {
+ "name": "Multirole",
+ "units": [
+ "A-10A",
+ "A-10C",
+ "A-10C_2",
+ "A-20G",
+ "A6E",
+ "AJS37",
+ "AV8BNA",
+ "F-117A",
+ "F-14A-135-GR",
+ "F-14A-135-GR-Early",
+ "F-14B",
+ "F-15C",
+ "F-15E",
+ "F-15ESE",
+ "F-16A",
+ "F-16A MLU",
+ "F-16C bl.50",
+ "F-16C bl.52d",
+ "F-16C_50",
+ "F-4E-45MC",
+ "F-5E",
+ "F-5E-3",
+ "F-5E-3_FC",
+ "F-86F Sabre",
+ "F-86F_FC",
+ "FA-18C_hornet",
+ "FCHS",
+ "G-4",
+ "H-6J",
+ "I-16",
+ "J-11A",
+ "JAS39Gripen",
+ "JF-17",
+ "JH-7A",
+ "Ju-88A4",
+ "L-39C",
+ "L-39ZA",
+ "L-39ZA_FC",
+ "M-2000C",
+ "MiG-15bis",
+ "MiG-15bis_FC",
+ "MiG-19P",
+ "MiG-21Bis",
+ "MiG-23MLD",
+ "MiG-25PD",
+ "MiG-25RBT",
+ "MiG-27K",
+ "MiG-29A",
+ "MiG-29G",
+ "MiG-29S",
+ "MiG-31",
+ { prefix: "Mirage-F1" },
+ "MosquitoFBMkVI",
+ "OV-10A",
+ "QF-4E",
+ "S-3B",
+ "Su-17M4",
+ "Su-24M",
+ "Su-24MR",
+ "Su-25",
+ "Su-25T",
+ "Su-25TM",
+ "Su-27",
+ "Su-30",
+ "Su-33",
+ "Su-34",
+ "Tornado GR4",
+ "Tornado IDS"
+ ]
+ },
+ {
+ "name": "WWII Fighter",
+ "units": [
+ "Bf-109K-4",
+ "Fw-190A8",
+ "Fw-190D9",
+ "P-47D-30",
+ "P-47D-40",
+ "P-51D",
+ "P-51D-30-NA",
+ "SpitfireLFMkIX",
+ "SpitfireLFMkIXCW",
+ ]
+ },
+ {
+ "name": "AWACS",
+ "units": [
+ "A-50",
+ "E-2C",
+ "E-3A",
+ "KJ-2000"
+ ]
+ },
+ {
+ "name": "Tanker",
+ "units": [
+ "KC-135",
+ "KC130",
+ "KC130J",
+ "KC135MPRS",
+ "S-3B Tanker"
+ ]
+ },
+ {
+ "name": "Transport",
+ "units": [
+ "An-26B",
+ "An-30M",
+ "C-130",
+ "C-130J-30",
+ "C-17A",
+ "C-47",
+ "Hercules",
+ "IL-76MD",
+ "Yak-40",
+ "Fokker_100"
+ ]
+ },
+ {
+ "name": "UAV",
+ "units": [
+ "MQ-9 Reaper",
+ "RQ-1A Predator",
+ "WingLoong-I"
+ ]
+ },
+ {
+ "name": "Trainer",
+ "units": [
+ "C-101CC",
+ "C-101EB",
+ "T-45",
+ "TF-51D",
+ "Yak-52",
+ "Christen Eagle II"
+ ]
+ },
+ {
+ "name": "Bomber",
+ "units": [
+ "B-1B",
+ "B-52H",
+ "Tu-142",
+ "Tu-160",
+ "Tu-22M3",
+ "Tu-95MS"
+ ]
+ },
+ {
+ "name": "Helicopter",
+ "units": [
+ "AH-1W",
+ "AH-64A",
+ "AH-64D",
+ "AH-64D_BLK_II",
+ "CH-47D",
+ "CH-47Fbl1",
+ "CH-53E",
+ "Ka-27",
+ "Ka-50",
+ "Ka-50_3",
+ "Mi-24P",
+ "Mi-24V",
+ "Mi-26",
+ "Mi-28N",
+ "Mi-8MT",
+ "OH-58D",
+ "OH58D",
+ "OH-6A",
+ "SA342L",
+ "SA342M",
+ "SA342Minigun",
+ "SA342Mistral",
+ "SH-60B",
+ "UH-1H",
+ "UH-60A",
+ "UH-60L",
+ "UH-60L_DAP"
+ ]
+ }
+ ]
+ }
+];
\ No newline at end of file
diff --git a/src/data/unitLookup.js b/src/data/unitLookup.js
new file mode 100644
index 0000000..89b6d65
--- /dev/null
+++ b/src/data/unitLookup.js
@@ -0,0 +1,1444 @@
+/* Auto-generated unit type -> {category, subcategory} lookup. */
+
+export const UNIT_LOOKUP = {
+ "A-10C_2": {
+ "category": "Aerial",
+ "subcategory": "1"
+ },
+ "BDK-775": {
+ "category": "Naval",
+ "subcategory": "Amphibious"
+ },
+ "USS_Samuel_Chase": {
+ "category": "Naval",
+ "subcategory": "Amphibious"
+ },
+ "LST_Mk2": {
+ "category": "Naval",
+ "subcategory": "Amphibious"
+ },
+ "PIOTR": {
+ "category": "Naval",
+ "subcategory": "BattleCruiser"
+ },
+ "ara_vdm": {
+ "category": "Naval",
+ "subcategory": "Carrier"
+ },
+ "KUZNECOW": {
+ "category": "Naval",
+ "subcategory": "Carrier"
+ },
+ "CV_1143_5": {
+ "category": "Naval",
+ "subcategory": "Carrier"
+ },
+ "Forrestal": {
+ "category": "Naval",
+ "subcategory": "Carrier"
+ },
+ "CVN_71": {
+ "category": "Naval",
+ "subcategory": "Carrier"
+ },
+ "CVN_72": {
+ "category": "Naval",
+ "subcategory": "Carrier"
+ },
+ "CVN_73": {
+ "category": "Naval",
+ "subcategory": "Carrier"
+ },
+ "Stennis": {
+ "category": "Naval",
+ "subcategory": "Carrier"
+ },
+ "CVN_75": {
+ "category": "Naval",
+ "subcategory": "Carrier"
+ },
+ "Essex": {
+ "category": "Naval",
+ "subcategory": "Carrier"
+ },
+ "hms_invincible": {
+ "category": "Naval",
+ "subcategory": "Carrier"
+ },
+ "LHA_Tarawa": {
+ "category": "Naval",
+ "subcategory": "Carrier"
+ },
+ "atconveyor": {
+ "category": "Naval",
+ "subcategory": "Carrier"
+ },
+ "ZWEZDNY": {
+ "category": "Naval",
+ "subcategory": "Civilian"
+ },
+ "HandyWind": {
+ "category": "Naval",
+ "subcategory": "Civilian"
+ },
+ "Dry-cargo ship-1": {
+ "category": "Naval",
+ "subcategory": "Civilian"
+ },
+ "Dry-cargo ship-2": {
+ "category": "Naval",
+ "subcategory": "Civilian"
+ },
+ "Ship_Tilde_Supply": {
+ "category": "Naval",
+ "subcategory": "Civilian"
+ },
+ "ELNYA": {
+ "category": "Naval",
+ "subcategory": "Civilian"
+ },
+ "Seawise_Giant": {
+ "category": "Naval",
+ "subcategory": "Civilian"
+ },
+ "CastleClass_01": {
+ "category": "Naval",
+ "subcategory": "Corvette/Patrol"
+ },
+ "ALBATROS": {
+ "category": "Naval",
+ "subcategory": "Corvette/Patrol"
+ },
+ "MOLNIYA": {
+ "category": "Naval",
+ "subcategory": "Corvette/Patrol"
+ },
+ "CHAP_Project22160": {
+ "category": "Naval",
+ "subcategory": "Corvette/Patrol"
+ },
+ "CHAP_Project22160_TorM2KM": {
+ "category": "Naval",
+ "subcategory": "Corvette/Patrol"
+ },
+ "TICONDEROG": {
+ "category": "Naval",
+ "subcategory": "Cruiser"
+ },
+ "MOSCOW": {
+ "category": "Naval",
+ "subcategory": "Cruiser"
+ },
+ "USS_Arleigh_Burke_IIa": {
+ "category": "Naval",
+ "subcategory": "Destroyer"
+ },
+ "Type_052B": {
+ "category": "Naval",
+ "subcategory": "Destroyer"
+ },
+ "Type_052C": {
+ "category": "Naval",
+ "subcategory": "Destroyer"
+ },
+ "speedboat": {
+ "category": "Naval",
+ "subcategory": "Fast Attack"
+ },
+ "Schnellboot_type_S130": {
+ "category": "Naval",
+ "subcategory": "Fast Attack"
+ },
+ "La_Combattante_II": {
+ "category": "Naval",
+ "subcategory": "Fast Attack"
+ },
+ "Type_021_1": {
+ "category": "Naval",
+ "subcategory": "Fast Attack"
+ },
+ "leander-gun-condell": {
+ "category": "Naval",
+ "subcategory": "Frigate"
+ },
+ "leander-gun-lynch": {
+ "category": "Naval",
+ "subcategory": "Frigate"
+ },
+ "PERRY": {
+ "category": "Naval",
+ "subcategory": "Frigate"
+ },
+ "REZKY": {
+ "category": "Naval",
+ "subcategory": "Frigate"
+ },
+ "NEUSTRASH": {
+ "category": "Naval",
+ "subcategory": "Frigate"
+ },
+ "leander-gun-achilles": {
+ "category": "Naval",
+ "subcategory": "Frigate"
+ },
+ "leander-gun-andromeda": {
+ "category": "Naval",
+ "subcategory": "Frigate"
+ },
+ "leander-gun-ariadne": {
+ "category": "Naval",
+ "subcategory": "Frigate"
+ },
+ "Type_054A": {
+ "category": "Naval",
+ "subcategory": "Frigate"
+ },
+ "santafe": {
+ "category": "Naval",
+ "subcategory": "Submarine"
+ },
+ "IMPROVED_KILO": {
+ "category": "Naval",
+ "subcategory": "Submarine"
+ },
+ "KILO": {
+ "category": "Naval",
+ "subcategory": "Submarine"
+ },
+ "Type_093": {
+ "category": "Naval",
+ "subcategory": "Submarine"
+ },
+ "Uboat_VIIC": {
+ "category": "Naval",
+ "subcategory": "Submarine"
+ },
+ "Higgins_boat": {
+ "category": "Naval",
+ "subcategory": "Support"
+ },
+ "HarborTug": {
+ "category": "Naval",
+ "subcategory": "Support"
+ },
+ "Type_96_25mm_AA": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "Type_88_75mm_AA": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "flak18": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "flak36": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "flak37": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "flak41": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "Type_3_80mm_AA": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "bofors40": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "SON_9": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "flak30": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "flak38": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "KDO_Mod40": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "KS-19": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "M1_37mm": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "M45_Quadmount": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "QF_37_AA": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "S-60_Type59_Artillery": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "ZU-23 Emplacement Closed": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "ZU-23 Emplacement": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "ZU-23 Closed Insurgent": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "ZU-23 Insurgent": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "Allies_Director": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "Maschinensatz_33": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "Flakscheinwerfer_37": {
+ "category": "Air Defense",
+ "subcategory": "AAA"
+ },
+ "Hawk pcp": {
+ "category": "Air Defense",
+ "subcategory": "Command"
+ },
+ "CHAP_IRISTSLM_CP": {
+ "category": "Air Defense",
+ "subcategory": "Command-1"
+ },
+ "NASAMS_Command_Post": {
+ "category": "Air Defense",
+ "subcategory": "Command-1"
+ },
+ "Patriot cp": {
+ "category": "Air Defense",
+ "subcategory": "Command-1"
+ },
+ "Patriot AMG": {
+ "category": "Air Defense",
+ "subcategory": "Command-1"
+ },
+ "Patriot ECS": {
+ "category": "Air Defense",
+ "subcategory": "Command-1"
+ },
+ "S-300PS 54K6 cp": {
+ "category": "Air Defense",
+ "subcategory": "Command-1"
+ },
+ "SA-11 Buk CC 9S470M1": {
+ "category": "Air Defense",
+ "subcategory": "Command-1"
+ },
+ "1L13 EWR": {
+ "category": "Air Defense",
+ "subcategory": "EW Radar"
+ },
+ "55G6 EWR": {
+ "category": "Air Defense",
+ "subcategory": "EW Radar-1"
+ },
+ "FPS-117 ECS": {
+ "category": "Air Defense",
+ "subcategory": "EW Radar-1"
+ },
+ "FPS-117": {
+ "category": "Air Defense",
+ "subcategory": "EW Radar-1"
+ },
+ "FPS-117 Dome": {
+ "category": "Air Defense",
+ "subcategory": "EW Radar-1"
+ },
+ "FuMG-401": {
+ "category": "Air Defense",
+ "subcategory": "EW Radar-1"
+ },
+ "FuSe-65": {
+ "category": "Air Defense",
+ "subcategory": "EW Radar-1"
+ },
+ "generator_5i57": {
+ "category": "Air Defense",
+ "subcategory": "Generator"
+ },
+ "Patriot EPP": {
+ "category": "Air Defense",
+ "subcategory": "Generator"
+ },
+ "Hawk ln": {
+ "category": "Air Defense",
+ "subcategory": "Launcher"
+ },
+ "CHAP_IRISTSLM_LN": {
+ "category": "Air Defense",
+ "subcategory": "Launcher"
+ },
+ "NASAMS_LN_B": {
+ "category": "Air Defense",
+ "subcategory": "Launcher"
+ },
+ "NASAMS_LN_C": {
+ "category": "Air Defense",
+ "subcategory": "Launcher"
+ },
+ "Patriot ln": {
+ "category": "Air Defense",
+ "subcategory": "Launcher"
+ },
+ "S-300PS 5P85C ln": {
+ "category": "Air Defense",
+ "subcategory": "Launcher"
+ },
+ "S-300PS 5P85D ln": {
+ "category": "Air Defense",
+ "subcategory": "Launcher"
+ },
+ "S_75M_Volhov": {
+ "category": "Air Defense",
+ "subcategory": "Launcher"
+ },
+ "5p73 s-125 ln": {
+ "category": "Air Defense",
+ "subcategory": "Launcher"
+ },
+ "S-200_Launcher": {
+ "category": "Air Defense",
+ "subcategory": "Launcher"
+ },
+ "Kub 2P25 ln": {
+ "category": "Air Defense",
+ "subcategory": "Launcher"
+ },
+ "SA-18 Igla manpad": {
+ "category": "Air Defense",
+ "subcategory": "MANPAD"
+ },
+ "SA-18 Igla comm": {
+ "category": "Air Defense",
+ "subcategory": "MANPAD"
+ },
+ "Igla manpad INS": {
+ "category": "Air Defense",
+ "subcategory": "MANPAD"
+ },
+ "SA-18 Igla-S manpad": {
+ "category": "Air Defense",
+ "subcategory": "MANPAD"
+ },
+ "SA-18 Igla-S comm": {
+ "category": "Air Defense",
+ "subcategory": "MANPAD"
+ },
+ "Soldier stinger": {
+ "category": "Air Defense",
+ "subcategory": "MANPAD"
+ },
+ "Stinger comm": {
+ "category": "Air Defense",
+ "subcategory": "MANPAD"
+ },
+ "Stinger comm dsr": {
+ "category": "Air Defense",
+ "subcategory": "MANPAD"
+ },
+ "rapier_fsa_optical_tracker_unit": {
+ "category": "Air Defense",
+ "subcategory": "Optical Tracker"
+ },
+ "M1097 Avenger": {
+ "category": "Air Defense",
+ "subcategory": "SAM SHORAD"
+ },
+ "M48 Chaparral": {
+ "category": "Air Defense",
+ "subcategory": "SAM SHORAD"
+ },
+ "M6 Linebacker": {
+ "category": "Air Defense",
+ "subcategory": "SAM SHORAD"
+ },
+ "rapier_fsa_launcher": {
+ "category": "Air Defense",
+ "subcategory": "SAM SHORAD"
+ },
+ "Roland ADS": {
+ "category": "Air Defense",
+ "subcategory": "SAM SHORAD"
+ },
+ "Strela-10M3": {
+ "category": "Air Defense",
+ "subcategory": "SAM SHORAD"
+ },
+ "Tor 9A331": {
+ "category": "Air Defense",
+ "subcategory": "SAM SHORAD"
+ },
+ "CHAP_TorM2": {
+ "category": "Air Defense",
+ "subcategory": "SAM SHORAD"
+ },
+ "2S6 Tunguska": {
+ "category": "Air Defense",
+ "subcategory": "SAM SHORAD"
+ },
+ "Osa 9A33 ln": {
+ "category": "Air Defense",
+ "subcategory": "SAM SHORAD"
+ },
+ "Strela-1 9P31": {
+ "category": "Air Defense",
+ "subcategory": "SAM SHORAD"
+ },
+ "Hawk cwar": {
+ "category": "Air Defense",
+ "subcategory": "Search/Track Radar"
+ },
+ "CHAP_IRISTSLM_STR": {
+ "category": "Air Defense",
+ "subcategory": "Search/Track Radar"
+ },
+ "Patriot str": {
+ "category": "Air Defense",
+ "subcategory": "Search/Track Radar"
+ },
+ "Roland Radar": {
+ "category": "Air Defense",
+ "subcategory": "Search/Track Radar"
+ },
+ "RPC_5N62V": {
+ "category": "Air Defense",
+ "subcategory": "Search/Track Radar"
+ },
+ "Kub 1S91 str": {
+ "category": "Air Defense",
+ "subcategory": "Search/Track Radar"
+ },
+ "HQ-7_STR_SP": {
+ "category": "Air Defense",
+ "subcategory": "Search Radar"
+ },
+ "Dog Ear radar": {
+ "category": "Air Defense",
+ "subcategory": "Search Radar"
+ },
+ "Hawk sr": {
+ "category": "Air Defense",
+ "subcategory": "Search Radar"
+ },
+ "NASAMS_Radar_MPQ64F1": {
+ "category": "Air Defense",
+ "subcategory": "Search Radar"
+ },
+ "S-300PS 64H6E sr": {
+ "category": "Air Defense",
+ "subcategory": "Search Radar"
+ },
+ "S-300PS 40B6MD sr": {
+ "category": "Air Defense",
+ "subcategory": "Search Radar"
+ },
+ "S-300PS 40B6MD sr_19J6": {
+ "category": "Air Defense",
+ "subcategory": "Search Radar"
+ },
+ "SA-11 Buk SR 9S18M1": {
+ "category": "Air Defense",
+ "subcategory": "Search Radar"
+ },
+ "p-19 s-125 sr": {
+ "category": "Air Defense",
+ "subcategory": "Search Radar"
+ },
+ "P14_SR": {
+ "category": "Air Defense",
+ "subcategory": "Search Radar"
+ },
+ "RLS_19J6": {
+ "category": "Air Defense",
+ "subcategory": "Search Radar"
+ },
+ "Type_94_25mm_AA_Truck": {
+ "category": "Air Defense",
+ "subcategory": "SProp AAA"
+ },
+ "Ural-375 ZU-23": {
+ "category": "Air Defense",
+ "subcategory": "SProp AAA"
+ },
+ "Ural-375 ZU-23 Insurgent": {
+ "category": "Air Defense",
+ "subcategory": "SProp AAA"
+ },
+ "HEMTT_C-RAM_Phalanx": {
+ "category": "Air Defense",
+ "subcategory": "SProp AAA"
+ },
+ "CHAP_PantsirS1": {
+ "category": "Air Defense",
+ "subcategory": "SProp AAA"
+ },
+ "Gepard": {
+ "category": "Air Defense",
+ "subcategory": "SProp AAA"
+ },
+ "HL_ZU-23": {
+ "category": "Air Defense",
+ "subcategory": "SProp AAA"
+ },
+ "tt_ZU-23": {
+ "category": "Air Defense",
+ "subcategory": "SProp AAA"
+ },
+ "Vulcan": {
+ "category": "Air Defense",
+ "subcategory": "SProp AAA"
+ },
+ "ZSU-23-4 Shilka": {
+ "category": "Air Defense",
+ "subcategory": "SProp AAA"
+ },
+ "ZSU_57_2": {
+ "category": "Air Defense",
+ "subcategory": "SProp AAA"
+ },
+ "Hawk tr": {
+ "category": "Air Defense",
+ "subcategory": "Track Radar"
+ },
+ "rapier_fsa_blindfire_radar": {
+ "category": "Air Defense",
+ "subcategory": "Track Radar"
+ },
+ "S-300PS 40B6M tr": {
+ "category": "Air Defense",
+ "subcategory": "Track Radar"
+ },
+ "S-300PS 5H63C 30H6_tr": {
+ "category": "Air Defense",
+ "subcategory": "Track Radar"
+ },
+ "SNR_75V": {
+ "category": "Air Defense",
+ "subcategory": "Track Radar"
+ },
+ "RD_75": {
+ "category": "Air Defense",
+ "subcategory": "Track Radar"
+ },
+ "snr s-125 tr": {
+ "category": "Air Defense",
+ "subcategory": "Track Radar"
+ },
+ "AAV7": {
+ "category": "Armour",
+ "subcategory": "APC"
+ },
+ "BTR-60": {
+ "category": "Armour",
+ "subcategory": "APC"
+ },
+ "BTR-70": {
+ "category": "Armour",
+ "subcategory": "APC"
+ },
+ "BTR-80": {
+ "category": "Armour",
+ "subcategory": "APC"
+ },
+ "M-113": {
+ "category": "Armour",
+ "subcategory": "APC"
+ },
+ "M2A1_halftrack": {
+ "category": "Armour",
+ "subcategory": "APC"
+ },
+ "MTLB": {
+ "category": "Armour",
+ "subcategory": "APC"
+ },
+ "Sd_Kfz_251": {
+ "category": "Armour",
+ "subcategory": "APC"
+ },
+ "TPZ": {
+ "category": "Armour",
+ "subcategory": "APC"
+ },
+ "M4_Tractor": {
+ "category": "Armour",
+ "subcategory": "APC"
+ },
+ "LARC-V": {
+ "category": "Armour",
+ "subcategory": "APC"
+ },
+ "ZBD04A": {
+ "category": "Armour",
+ "subcategory": "APC"
+ },
+ "BMD-1": {
+ "category": "Armour",
+ "subcategory": "IFV"
+ },
+ "BMP-1": {
+ "category": "Armour",
+ "subcategory": "IFV"
+ },
+ "BMP-2": {
+ "category": "Armour",
+ "subcategory": "IFV"
+ },
+ "BMP-3": {
+ "category": "Armour",
+ "subcategory": "IFV"
+ },
+ "CHAP_BMPT": {
+ "category": "Armour",
+ "subcategory": "IFV"
+ },
+ "BTR-82A": {
+ "category": "Armour",
+ "subcategory": "IFV"
+ },
+ "LAV-25": {
+ "category": "Armour",
+ "subcategory": "IFV"
+ },
+ "M1126 Stryker ICV": {
+ "category": "Armour",
+ "subcategory": "IFV"
+ },
+ "M-2 Bradley": {
+ "category": "Armour",
+ "subcategory": "IFV"
+ },
+ "Marder": {
+ "category": "Armour",
+ "subcategory": "IFV"
+ },
+ "MCV-80": {
+ "category": "Armour",
+ "subcategory": "IFV"
+ },
+ "Challenger2": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "Chieftain_mk3": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "Leclerc": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "Leopard1A3": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "leopard-2A4": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "leopard-2A4_trs": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "Leopard-2A5": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "Leopard-2": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "M-1 Abrams": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "M1A2C_SEP_V3": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "M-60": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "Merkava_Mk4": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "T-55": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "T62M": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "CHAP_T64BV": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "T-72B": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "T-72B3": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "T-80B": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "T-80UD": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "CHAP_T84OplotM": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "T-90": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "CHAP_T90M": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "TYPE-59": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "ZTZ96B": {
+ "category": "Armour",
+ "subcategory": "MBT"
+ },
+ "CHAP_MATV": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "MaxxPro_MRAP": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "Daimler_AC": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "CHAP_M1130": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "CHAP_FV101": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "BRDM-2": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "Cobra": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "CHAP_FV107": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "HL_DSHK": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "HL_KORD": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "M1043 HMMWV Armament": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "tt_DSHK": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "tt_KORD": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "M8_Greyhound": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "Sd_Kfz_234_2_Puma": {
+ "category": "Armour",
+ "subcategory": "Scout Recon"
+ },
+ "BTR_D": {
+ "category": "Armour",
+ "subcategory": "SP ATGM"
+ },
+ "BRDM-2_malyutka": {
+ "category": "Armour",
+ "subcategory": "SP ATGM"
+ },
+ "M1045 HMMWV TOW": {
+ "category": "Armour",
+ "subcategory": "SP ATGM"
+ },
+ "M1134 Stryker ATGM": {
+ "category": "Armour",
+ "subcategory": "SP ATGM"
+ },
+ "VAB_Mephisto": {
+ "category": "Armour",
+ "subcategory": "SP ATGM"
+ },
+ "SturmPzIV": {
+ "category": "Armour",
+ "subcategory": "SP Gun"
+ },
+ "Elefant_SdKfz_184": {
+ "category": "Armour",
+ "subcategory": "SP Gun"
+ },
+ "Jagdpanther_G1": {
+ "category": "Armour",
+ "subcategory": "SP Gun"
+ },
+ "JagdPz_IV": {
+ "category": "Armour",
+ "subcategory": "SP Gun"
+ },
+ "M10_GMC": {
+ "category": "Armour",
+ "subcategory": "SP Gun"
+ },
+ "M1128 Stryker MGS": {
+ "category": "Armour",
+ "subcategory": "SP Gun"
+ },
+ "Stug_III": {
+ "category": "Armour",
+ "subcategory": "SP Gun"
+ },
+ "Stug_IV": {
+ "category": "Armour",
+ "subcategory": "SP Gun"
+ },
+ "PT_76": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "Centaur_IV": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "Churchill_VII": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "Cromwell_IV": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "M4_Sherman": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "M4A4_Sherman_FF": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "Pz_V_Panther_G": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "Pz_IV_H": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "T-34-85": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "Tetrarch": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "Tiger_I": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "Tiger_II_H": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "Type_89_I_Go": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "Type_98_Ke_Ni": {
+ "category": "Armour",
+ "subcategory": "Tank"
+ },
+ "Grad_FDDM": {
+ "category": "Artillery",
+ "subcategory": "Command/Control"
+ },
+ "MLRS FDDM": {
+ "category": "Artillery",
+ "subcategory": "Command/Control"
+ },
+ "LeFH_18-40-105": {
+ "category": "Artillery",
+ "subcategory": "Howitzer"
+ },
+ "M2A1-105": {
+ "category": "Artillery",
+ "subcategory": "Howitzer"
+ },
+ "Pak40": {
+ "category": "Artillery",
+ "subcategory": "Howitzer"
+ },
+ "L118_Unit": {
+ "category": "Artillery",
+ "subcategory": "Howitzer"
+ },
+ "2B11 mortar": {
+ "category": "Artillery",
+ "subcategory": "Mortar"
+ },
+ "Smerch": {
+ "category": "Artillery",
+ "subcategory": "MLRS"
+ },
+ "Smerch_HE": {
+ "category": "Artillery",
+ "subcategory": "MLRS"
+ },
+ "Uragan_BM-27": {
+ "category": "Artillery",
+ "subcategory": "MLRS"
+ },
+ "Grad-URAL": {
+ "category": "Artillery",
+ "subcategory": "MLRS"
+ },
+ "HL_B8M1": {
+ "category": "Artillery",
+ "subcategory": "MLRS"
+ },
+ "tt_B8M1": {
+ "category": "Artillery",
+ "subcategory": "MLRS"
+ },
+ "CHAP_M142_ATACMS_M39A1": {
+ "category": "Artillery",
+ "subcategory": "MLRS"
+ },
+ "CHAP_M142_ATACMS_M48": {
+ "category": "Artillery",
+ "subcategory": "MLRS"
+ },
+ "CHAP_M142_GMLRS_M30": {
+ "category": "Artillery",
+ "subcategory": "MLRS"
+ },
+ "CHAP_M142_GMLRS_M31": {
+ "category": "Artillery",
+ "subcategory": "MLRS"
+ },
+ "MLRS": {
+ "category": "Artillery",
+ "subcategory": "MLRS"
+ },
+ "CHAP_TOS1A": {
+ "category": "Artillery",
+ "subcategory": "MLRS"
+ },
+ "PLZ05": {
+ "category": "Artillery",
+ "subcategory": "SP Artillery"
+ },
+ "SAU Gvozdika": {
+ "category": "Artillery",
+ "subcategory": "SP Artillery"
+ },
+ "SAU Msta": {
+ "category": "Artillery",
+ "subcategory": "SP Artillery"
+ },
+ "SAU Akatsia": {
+ "category": "Artillery",
+ "subcategory": "SP Artillery"
+ },
+ "SpGH_Dana": {
+ "category": "Artillery",
+ "subcategory": "SP Artillery"
+ },
+ "M-109": {
+ "category": "Artillery",
+ "subcategory": "SP Artillery"
+ },
+ "M12_GMC": {
+ "category": "Artillery",
+ "subcategory": "SP Artillery"
+ },
+ "Wespe124": {
+ "category": "Artillery",
+ "subcategory": "SP Artillery"
+ },
+ "T155_Firtina": {
+ "category": "Artillery",
+ "subcategory": "SP Artillery"
+ },
+ "SAU 2-C9": {
+ "category": "Artillery",
+ "subcategory": "SP Artillery"
+ },
+ "house1arm": {
+ "category": "Fortification",
+ "subcategory": "Buildings"
+ },
+ "TACAN_beacon": {
+ "category": "Fortification",
+ "subcategory": "Buildings"
+ },
+ "houseA_arm": {
+ "category": "Fortification",
+ "subcategory": "Buildings"
+ },
+ "fire_control": {
+ "category": "Fortification",
+ "subcategory": "Buildings"
+ },
+ "SK_C_28_naval_gun": {
+ "category": "Fortification",
+ "subcategory": "Buildings"
+ },
+ "outpost": {
+ "category": "Fortification",
+ "subcategory": "Buildings"
+ },
+ "outpost_road": {
+ "category": "Fortification",
+ "subcategory": "Buildings"
+ },
+ "outpost_road_r": {
+ "category": "Fortification",
+ "subcategory": "Buildings"
+ },
+ "outpost_road_l": {
+ "category": "Fortification",
+ "subcategory": "Buildings"
+ },
+ "house2arm": {
+ "category": "Fortification",
+ "subcategory": "Buildings"
+ },
+ "Sandbox": {
+ "category": "Fortification",
+ "subcategory": "Pillbox"
+ },
+ "Bunker": {
+ "category": "Fortification",
+ "subcategory": "Pillbox"
+ },
+ "Soldier AK": {
+ "category": "Infantry",
+ "subcategory": "ALL"
+ },
+ "Infantry AK": {
+ "category": "Infantry",
+ "subcategory": "ALL"
+ },
+ "Infantry AK ver2": {
+ "category": "Infantry",
+ "subcategory": "ALL"
+ },
+ "Infantry AK ver3": {
+ "category": "Infantry",
+ "subcategory": "ALL"
+ },
+ "soldier_wwii_us": {
+ "category": "Infantry",
+ "subcategory": "ALL"
+ },
+ "Soldier M249": {
+ "category": "Infantry",
+ "subcategory": "ALL"
+ },
+ "Soldier M4": {
+ "category": "Infantry",
+ "subcategory": "ALL"
+ },
+ "Soldier M4 GRG": {
+ "category": "Infantry",
+ "subcategory": "ALL"
+ },
+ "soldier_mauser98": {
+ "category": "Infantry",
+ "subcategory": "ALL"
+ },
+ "Soldier RPG": {
+ "category": "Infantry",
+ "subcategory": "ALL"
+ },
+ "Silkworm_SR": {
+ "category": "Infantry",
+ "subcategory": "ALL-1"
+ },
+ "hy_launcher": {
+ "category": "Missiles",
+ "subcategory": "SSM"
+ },
+ "CHAP_9K720_Cluster": {
+ "category": "Missiles",
+ "subcategory": "SSM"
+ },
+ "CHAP_9K720_HE": {
+ "category": "Missiles",
+ "subcategory": "SSM"
+ },
+ "Scud_B": {
+ "category": "Missiles",
+ "subcategory": "SSM"
+ },
+ "v1_launcher": {
+ "category": "Missiles",
+ "subcategory": "SSM"
+ },
+ "IKARUS Bus": {
+ "category": "Unarmed",
+ "subcategory": "Civilian"
+ },
+ "LAZ Bus": {
+ "category": "Unarmed",
+ "subcategory": "Civilian"
+ },
+ "LiAZ Bus": {
+ "category": "Unarmed",
+ "subcategory": "Civilian"
+ },
+ "VAZ Car": {
+ "category": "Unarmed",
+ "subcategory": "Civilian"
+ },
+ "GAZ-3307": {
+ "category": "Unarmed",
+ "subcategory": "Civilian"
+ },
+ "MAZ-6303": {
+ "category": "Unarmed",
+ "subcategory": "Civilian"
+ },
+ "ZIL-4331": {
+ "category": "Unarmed",
+ "subcategory": "Civilian"
+ },
+ "Trolley bus": {
+ "category": "Unarmed",
+ "subcategory": "Civilian"
+ },
+ "Predator GCS": {
+ "category": "Unarmed",
+ "subcategory": "Command/Control"
+ },
+ "Predator TrojanSpirit": {
+ "category": "Unarmed",
+ "subcategory": "Command/Control"
+ },
+ "SKP-11": {
+ "category": "Unarmed",
+ "subcategory": "Command/Control"
+ },
+ "Ural-375 PBU": {
+ "category": "Unarmed",
+ "subcategory": "Command/Control"
+ },
+ "ZIL-131 KUNG": {
+ "category": "Unarmed",
+ "subcategory": "Command/Control"
+ },
+ "Willys_MB": {
+ "category": "Unarmed",
+ "subcategory": "Scout Recon"
+ },
+ "Hummer": {
+ "category": "Unarmed",
+ "subcategory": "Scout Recon"
+ },
+ "Horch_901_typ_40_kfz_21": {
+ "category": "Unarmed",
+ "subcategory": "Scout Recon"
+ },
+ "Sd_Kfz_2": {
+ "category": "Unarmed",
+ "subcategory": "Scout Recon"
+ },
+ "Kubelwagen_82": {
+ "category": "Unarmed",
+ "subcategory": "Scout Recon"
+ },
+ "Land_Rover_109_S3": {
+ "category": "Unarmed",
+ "subcategory": "Scout Recon"
+ },
+ "Tigr_233036": {
+ "category": "Unarmed",
+ "subcategory": "Scout Recon"
+ },
+ "UAZ-469": {
+ "category": "Unarmed",
+ "subcategory": "Scout Recon"
+ },
+ "M30_CC": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "Type_98_So_Da": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "HEMTT TFFT": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "tacr2a": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "Ural ATsP-6": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "AA8": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "GD-20": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "GPS_Spoofer_Blue": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "GPS_Spoofer_Red": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "Ural-4320 APA-5D": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "ZiL-131 APA-80": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "B600_drivable": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "MJ-1_drivable": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "P20_drivable": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "r11_volvo_drivable": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "TugHarlan_drivable": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "PL5EII Loadout": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "PL8 Loadout": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "SD10 Loadout": {
+ "category": "Unarmed",
+ "subcategory": "Logistics/Supt"
+ },
+ "E-3A": {
+ "category": "Aerial",
+ "subcategory": "AWACS"
+ },
+ "KJ-2000": {
+ "category": "Aerial",
+ "subcategory": "AWACS"
+ },
+ "E-2C": {
+ "category": "Aerial",
+ "subcategory": "AWACS-1"
+ },
+ "A-50": {
+ "category": "Aerial",
+ "subcategory": "AWACS-3"
+ },
+ "An-26B": {
+ "category": "Aerial",
+ "subcategory": "Transport"
+ },
+ "An-30M": {
+ "category": "Aerial",
+ "subcategory": "Transport-1"
+ },
+ "C-130": {
+ "category": "Aerial",
+ "subcategory": "Transport-2"
+ },
+ "C-130J-30": {
+ "category": "Aerial",
+ "subcategory": "Transport-3"
+ },
+ "C-17A": {
+ "category": "Aerial",
+ "subcategory": "Transport-4"
+ },
+ "C-47": {
+ "category": "Aerial",
+ "subcategory": "Transport-5"
+ },
+ "Hercules": {
+ "category": "Aerial",
+ "subcategory": "Transport-6"
+ },
+ "IL-76MD": {
+ "category": "Aerial",
+ "subcategory": "Transport-7"
+ }
+};
diff --git a/src/data/unitLookupFromCatalog.js b/src/data/unitLookupFromCatalog.js
new file mode 100644
index 0000000..0acc77b
--- /dev/null
+++ b/src/data/unitLookupFromCatalog.js
@@ -0,0 +1,8 @@
+/**
+ * Generated at runtime from UNIT_CATALOG (single-source-of-truth).
+ * Edit UNIT_CATALOG only.
+ */
+import { UNIT_CATALOG } from "./unitCatalog-Editme";
+import { buildUnitLookup } from "./buildUnitLookup";
+
+export const UNIT_LOOKUP = buildUnitLookup(UNIT_CATALOG);
diff --git a/src/hooks/useElectronIpcListeners.js b/src/hooks/useElectronIpcListeners.js
index d1054b6..cfcd516 100644
--- a/src/hooks/useElectronIpcListeners.js
+++ b/src/hooks/useElectronIpcListeners.js
@@ -5,14 +5,18 @@ import { uiActions } from "../store/ui";
import { useDispatch, useSelector } from "react-redux";
import { throttle } from "lodash";
+
const { ipcRenderer } = window.require("electron");
const useElectronIpcListeners = () => {
const dispatch = useDispatch();
const { lat, long, elev, module } = useSelector((state) => state.dcsPoint);
+ const dcsWaypointsCount = useSelector(
+ (state) => state.waypoints.dcsWaypoints.length,
+ );
useEffect(() => {
- ipcRenderer.on("saveWaypoint", () => {
+ const onSaveWaypoint = () => {
if (module && lat && long) {
dispatch(
waypointsActions.addDcsWaypoint({
@@ -22,41 +26,125 @@ const useElectronIpcListeners = () => {
}),
);
}
- });
- ipcRenderer.on("fileOpened", (event, msg) => {
+ };
+
+ const onUnitImportAddWaypoints = (_event, units) => {
+ if (!Array.isArray(units)) return;
+
+ const startIndex = dcsWaypointsCount;
+
+ units.forEach((u, idx) => {
+ const latNum = Number(u.lat);
+ const longNum = Number(u.lon ?? u.long);
+ const elevNumRaw = Number(u.elevM ?? u.elev);
+ const elevNum = Number.isFinite(elevNumRaw) ? elevNumRaw : 1;
+
+ if (!Number.isFinite(latNum) || !Number.isFinite(longNum)) return;
+
+ const typeStr =
+ (typeof u.type === "string" && u.type.trim()) ||
+ (typeof u.unitType === "string" && u.unitType.trim()) ||
+ (typeof u.className === "string" && u.className.trim()) ||
+ (typeof u.name === "string" && u.name.trim()) ||
+ "UNIT";
+
+ const wpNumber = startIndex + idx + 1;
+
+ dispatch(
+ waypointsActions.addDcsWaypoint({
+ lat: latNum,
+ long: longNum,
+ elev: elevNum,
+ name: `${wpNumber} ${typeStr}`,
+ }),
+ );
+ });
+ };
+
+ const onFileOpened = (_event, msg) => {
dispatch(waypointsActions.appendWaypoints(msg));
- });
- ipcRenderer.on("deleteWaypoints", () => {
+ };
+
+ const onDeleteWaypoints = () => {
dispatch(waypointsActions.deleteAll());
- });
- ipcRenderer.on("deleteLastWaypoint", () => {
+ };
+
+ const onDeleteLastWaypoint = () => {
dispatch(waypointsActions.deleteLast());
- });
- ipcRenderer.on("preferencesReceived", (e, preferences) => {
+ };
+
+ const onPreferencesReceived = (_e, preferences) => {
dispatch(uiActions.setUserPreferences(preferences));
- });
- return () => {
- ipcRenderer.removeAllListeners("saveWaypoint");
- ipcRenderer.removeAllListeners("fileOpened");
- ipcRenderer.removeAllListeners("deleteWaypoints");
- ipcRenderer.removeAllListeners("deleteLastWaypoint");
- ipcRenderer.removeAllListeners("preferencesReceived");
};
- }, [lat, long, elev, module]);
- useEffect(() => {
- ipcRenderer.send("getPreferences");
- ipcRenderer.on(
- "dataReceived",
- throttle((event, msg) => {
- dispatch(dcsPointActions.changeCoords(JSON.parse(msg)));
- }, 100),
- );
+ ipcRenderer.removeListener("saveWaypoint", onSaveWaypoint);
+ ipcRenderer.removeListener("unitImport:addWaypoints", onUnitImportAddWaypoints);
+ ipcRenderer.removeListener("fileOpened", onFileOpened);
+ ipcRenderer.removeListener("deleteWaypoints", onDeleteWaypoints);
+ ipcRenderer.removeListener("deleteLastWaypoint", onDeleteLastWaypoint);
+ ipcRenderer.removeListener("preferencesReceived", onPreferencesReceived);
+
+ ipcRenderer.on("saveWaypoint", onSaveWaypoint);
+ ipcRenderer.on("unitImport:addWaypoints", onUnitImportAddWaypoints);
+ ipcRenderer.on("fileOpened", onFileOpened);
+ ipcRenderer.on("deleteWaypoints", onDeleteWaypoints);
+ ipcRenderer.on("deleteLastWaypoint", onDeleteLastWaypoint);
+ ipcRenderer.on("preferencesReceived", onPreferencesReceived);
return () => {
- ipcRenderer.removeAllListeners("dataReceived");
+ ipcRenderer.removeListener("saveWaypoint", onSaveWaypoint);
+ ipcRenderer.removeListener("unitImport:addWaypoints", onUnitImportAddWaypoints);
+ ipcRenderer.removeListener("fileOpened", onFileOpened);
+ ipcRenderer.removeListener("deleteWaypoints", onDeleteWaypoints);
+ ipcRenderer.removeListener("deleteLastWaypoint", onDeleteLastWaypoint);
+ ipcRenderer.removeListener("preferencesReceived", onPreferencesReceived);
};
- }, []);
+ }, [lat, long, elev, module, dcsWaypointsCount, dispatch]);
+
+useEffect(() => {
+ ipcRenderer.send("getPreferences");
+
+ let lastBusy = null;
+ let lastBusyTs = null;
+
+ const onDataReceived = throttle((_event, msg) => {
+ const data = JSON.parse(msg);
+
+ // expose busy globally for transfer modules
+ window.__thewayBusy = data?.busy === true;
+
+ const now = performance.now();
+
+ if (data?.busy !== lastBusy) {
+ const delta =
+ lastBusyTs == null ? "first" : `${Math.round(now - lastBusyTs)}ms`;
+
+ console.log(
+ `[BUSY] ${Math.round(now)}ms | ${lastBusy} -> ${data?.busy} | gap: ${delta}`
+ );
+
+ lastBusy = data?.busy;
+ lastBusyTs = now;
+ }
+
+ dispatch(dcsPointActions.changeCoords(data));
+
+ if (data?.HandleData) {
+ window.tnl3100Row1 = data.HandleData.TNL3100_ROW1;
+ window.tnl3100Row2 = data.HandleData.TNL3100_ROW2;
+ }
+ }, 100);
+
+ ipcRenderer.removeAllListeners("dataReceived");
+ ipcRenderer.on("dataReceived", onDataReceived);
+
+ return () => {
+ ipcRenderer.removeListener("dataReceived", onDataReceived);
+ if (typeof onDataReceived.cancel === "function") {
+ onDataReceived.cancel();
+ }
+ };
+}, [dispatch]);
};
export default useElectronIpcListeners;
diff --git a/src/index.js b/src/index.js
index 73ea53a..3ef4e7a 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,12 +1,13 @@
-import React from "react";
-import ReactDOM from "react-dom/client";
-import App from "./App";
-import { Provider } from "react-redux";
-import index from "./store";
-
-const root = ReactDOM.createRoot(document.getElementById("root"));
-root.render(
-
-
- ,
-);
+//v3
+import React from "react";
+import ReactDOM from "react-dom/client";
+import App from "./App";
+import { Provider } from "react-redux";
+import index from "./store";
+
+const root = ReactDOM.createRoot(document.getElementById("root"));
+root.render(
+
+
+ ,
+);
diff --git a/src/models/ButtonPress.js b/src/models/ButtonPress.js
index d2fe31f..a504d1a 100644
--- a/src/models/ButtonPress.js
+++ b/src/models/ButtonPress.js
@@ -1,15 +1,15 @@
-const createButtonPress = (
- device,
- code,
- delay,
- activate = 1,
- addDepress = true,
-) => ({
- device,
- code,
- delay,
- activate,
- addDepress: addDepress.toString(),
-});
-
-export default createButtonPress;
+const createButtonPress = (
+ device,
+ code,
+ delay,
+ activate = 1,
+ addDepress = true,
+) => ({
+ device,
+ code,
+ delay,
+ activate,
+ addDepress: addDepress.toString(),
+});
+
+export default createButtonPress;
diff --git a/src/moduleCommands/GetModuleCommands.js b/src/moduleCommands/GetModuleCommands.js
index 2d616e6..6bf89a6 100644
--- a/src/moduleCommands/GetModuleCommands.js
+++ b/src/moduleCommands/GetModuleCommands.js
@@ -1,25 +1,109 @@
-import f16 from "./f16";
-import f15e from "./f15e";
-import fa18 from "./fa18";
-import ah64 from "./ah64";
+//v2
import a10 from "./a10";
-import m2000 from "./m2000";
+import ah6j from "./ah6j";
+import ah64 from "./ah64";
import av8b from "./av8b";
+import c130j from "./c130j";
+import ch47f from "./ch47f";
+import f15e from "./f15e";
+import f16 from "./f16";
+import fa18 from "./fa18";
+import hercules from "./Hercules";
import ka50 from "./ka50";
+import m2000 from "./m2000";
import miragef1 from "./miragef1";
-import uh60l from "./uh60l";
-import hercules from "./Hercules";
import oh58d from "./oh58d";
+import sa342 from "./sa342";
+import uh60l from "./uh60l";
+import ns430 from "./ns430";
+
+function applyExtraDelay(commands, buttonExtraDelay) {
+ return commands.map((cmd) => ({
+ ...cmd,
+ delay: (cmd.delay ?? 0) + buttonExtraDelay,
+ }));
+}
export default function getModuleCommands(module, waypoints, buttonExtraDelay) {
+ let commands = [];
+
switch (module) {
- case "F-15ESE_pilotA":
- case "F-15ESE_wsoA":
- case "F-15ESE_pilotB":
- case "F-15ESE_wsoB":
+
+ case "a10ADD":
+ case "a10NEW":
+ a10.slotVariant = module;
+ a10.extraDelay = 0;
+ commands = a10.createButtonCommands(waypoints);
+ break;
+
+ case "AH-6J_ADD":
+ case "AH-6J_REPLACE":
+ case "MH-6J_ADD":
+ case "MH-6J_REPLACE":
+ ah6j.slotVariant = module;
+ ah6j.extraDelay = 0;
+ commands = [];
+ break;
+
+ case "AH-64D_BLK_IIgunner":
+ ah64.extraDelay = 0;
+ ah64.slotVariant = "AH-64D_BLK_IIgunner";
+ commands = ah64.createButtonCommands(waypoints);
+ break;
+
+ case "AH-64D_BLK_IIpilot":
+ ah64.extraDelay = 0;
+ ah64.slotVariant = "AH-64D_BLK_IIpilot";
+ commands = ah64.createButtonCommands(waypoints);
+ break;
+
+ case "AV8BNA_TRGPT":
+ case "AV8BNA_WPT":
+ av8b.slotVariant = module;
+ av8b.extraDelay = 0;
+ commands = av8b.createButtonCommands(waypoints);
+ break;
+
+ case "c130CoPlt":
+ case "c130Plt":
+ c130j.slotVariant = module;
+ c130j.extraDelay = 0;
+ commands = c130j.createButtonCommands(waypoints);
+ break;
+
+ case "ch47ADD":
+ case "ch47ALTADD":
+ case "ch47ALTNEW":
+ case "ch47NEW":
+ ch47f.slotVariant = module;
+ ch47f.extraDelay = 0;
+ commands = ch47f.createButtonCommands(waypoints);
+ break;
+
+ case "EA-18G":
+ case "FA-18C_hornet":
+ case "FA-18C_hornetPP1":
+ case "FA-18C_hornetPP2":
+ case "FA-18C_hornetPP3":
+ case "FA-18C_hornetPP4":
+ case "FA-18E":
+ case "FA-18F":
+ fa18.slotVariant = module;
+ fa18.extraDelay = 0;
+ commands = fa18.createButtonCommands(waypoints);
+ break;
+
+ case "F-15ESE_pilot_targetpoints":
+ case "F-15ESE_pilot_txfr":
+ case "F-15ESE_pilot_waypoints":
+ case "F-15ESE_wso_targetpoints":
+ case "F-15ESE_wso_txfr":
+ case "F-15ESE_wso_waypoints":
f15e.slotVariant = module;
- f15e.extraDelay = buttonExtraDelay;
- return f15e.createButtonCommands(waypoints);
+ f15e.extraDelay = 0;
+ commands = f15e.createButtonCommands(waypoints);
+ break;
+
case "F-16C_50":
case "F-16D_50":
case "F-16D_50_NS":
@@ -27,68 +111,100 @@ export default function getModuleCommands(module, waypoints, buttonExtraDelay) {
case "F-16D_52_NS":
case "F-16D_Barak_30":
case "F-16D_Barak_40":
- case "F-16I": {
- f16.extraDelay = buttonExtraDelay;
- return f16.createButtonCommands(waypoints);
- }
- case "FA-18C_hornet":
- case "FA-18E":
- case "FA-18F":
- case "EA-18G": {
- fa18.extraDelay = buttonExtraDelay;
- return fa18.createButtonCommands(waypoints);
- }
- case "AH-64D_BLK_IIpilot": {
- ah64.extraDelay = buttonExtraDelay;
- ah64.slotVariant = "AH-64D_BLK_IIpilot";
- return ah64.createButtonCommands(waypoints);
- }
- case "AH-64D_BLK_IIgunner": {
- ah64.extraDelay = buttonExtraDelay;
- ah64.slotVariant = "AH-64D_BLK_IIgunner";
- return ah64.createButtonCommands(waypoints);
- }
- case "A-10C_2":
- case "A-10C": {
- a10.extraDelay = buttonExtraDelay;
- return a10.createButtonCommands(waypoints);
- }
- case "M-2000C": {
- m2000.extraDelay = buttonExtraDelay;
- return m2000.createButtonCommands(waypoints);
- }
- case "AV8BNA": {
- av8b.extraDelay = buttonExtraDelay;
- return av8b.createButtonCommands(waypoints);
- }
- case "Ka-50":
- case "Ka-50_3": {
- ka50.extraDelay = buttonExtraDelay;
- return ka50.createButtonCommands(waypoints);
- }
- case "Mirage-F1EE": {
- miragef1.extraDelay = buttonExtraDelay;
- return miragef1.createButtonCommands(waypoints);
- }
- case "UH-60L": {
- uh60l.extraDelay = buttonExtraDelay;
- return uh60l.createButtonCommands(waypoints);
- }
- case "Hercules": {
- hercules.extraDelay = buttonExtraDelay;
- return hercules.createButtonCommands(waypoints);
- }
- case "OH58Dright-seat": {
- oh58d.extraDelay = buttonExtraDelay;
- oh58d.slotVariant = "OH58Dright-seat";
- return oh58d.createButtonCommands(waypoints);
- }
- case "OH58Dleft-seat": {
- oh58d.extraDelay = buttonExtraDelay;
+ case "F-16I":
+ f16.extraDelay = 0;
+ commands = f16.createButtonCommands(waypoints);
+ break;
+
+ case "Hercules":
+ hercules.extraDelay = 0;
+ commands = hercules.createButtonCommands(waypoints);
+ break;
+
+ case "ka50TGT":
+ case "ka50WPT":
+ ka50.slotVariant = module;
+ ka50.extraDelay = 0;
+ commands = ka50.createButtonCommands(waypoints);
+ break;
+
+ case "M-2000C":
+ m2000.extraDelay = 0;
+ commands = m2000.createButtonCommands(waypoints);
+ break;
+
+ case "Mirage-F1EE":
+ miragef1.extraDelay = 0;
+ commands = miragef1.createButtonCommands(waypoints);
+ break;
+
+ case "OH58Dleft-seat":
+ oh58d.extraDelay = 0;
oh58d.slotVariant = "OH58Dleft-seat";
- return oh58d.createButtonCommands(waypoints);
- }
+ commands = oh58d.createButtonCommands(waypoints);
+ break;
+
+ case "OH58Dright-seat":
+ oh58d.extraDelay = 0;
+ oh58d.slotVariant = "OH58Dright-seat";
+ commands = oh58d.createButtonCommands(waypoints);
+ break;
+
+ case "SA342L":
+ case "SA342M":
+ case "SA342Minigun":
+ sa342.extraDelay = 0;
+ commands = sa342.createButtonCommands(waypoints);
+ break;
+
+ case "UH-60L":
+ case "UH-60L_DAP":
+ uh60l.extraDelay = 0;
+ commands = uh60l.createButtonCommands(waypoints);
+ break;
+
+ case "A-10A":
+ case "AJS37":
+ case "Bf-109K-4":
+ case "C-101CC":
+ case "C-101EB":
+ case "Christen Eagle II":
+ case "F-14B":
+ case "F-15C":
+ case "F-5E-3":
+ case "F-86F Sabre":
+ case "FW-190D9":
+ case "I-16":
+ case "JF-17":
+ case "J-11A":
+ case "L-39C":
+ case "L-39ZA":
+ case "Mi-24P":
+ case "Mi-8MT":
+ case "MiG-15bis":
+ case "MiG-21bis":
+ case "MiG-29 Fulcrum":
+ case "MiG-29A":
+ case "MiG-29G":
+ case "MiG-29S":
+ case "P-51D":
+ case "SpitfireLFMkIX":
+ case "SpitfireLFMkIXCW":
+ case "Su-25":
+ case "Su-25T":
+ case "Su-27":
+ case "Su-33":
+ case "TF-51D":
+ case "UH-1H":
+ case "Yak-52":
+ ns430.extraDelay = 0;
+ commands = [];
+ break;
+
default:
- return [];
+ commands = [];
+ break;
}
-}
+
+ return applyExtraDelay(commands, buttonExtraDelay);
+}
\ No newline at end of file
diff --git a/src/moduleCommands/Hercules.js b/src/moduleCommands/Hercules.js
index 74ff002..b857704 100644
--- a/src/moduleCommands/Hercules.js
+++ b/src/moduleCommands/Hercules.js
@@ -1,206 +1,206 @@
-class hercules {
- static extraDelay = 0;
- static #device_id = 20;
- static #delay_value = 50 + this.extraDelay;
- static #kuKeycodes = {
- "NavCTRL": 3213,
- "L1": 3201,
- "L2": 3202,
- "L3": 3203,
- "L4": 3204,
- "L5": 3205,
- "L6": 3206,
- "R1": 3207,
- "R2": 3208,
- "R3": 3209,
- "R4": 3210,
- "R5": 3211,
- "R6": 3212,
- "1": 3214,
- "2": 3215,
- "3": 3216,
- "4": 3217,
- "5": 3218,
- "6": 3219,
- "7": 3220,
- "8": 3221,
- "9": 3222,
- "0": 3223,
- //letters
- "a": 3224,
- "b": 3225,
- "c": 3226,
- "d": 3227,
- "e": 3228,
- "f": 3229,
- "g": 3230,
- "h": 3231,
- "i": 3232,
- "j": 3233,
- "k": 3234,
- "l": 3235,
- "m": 3236,
- "n": 3237,
- "o": 3238,
- "p": 3239,
- "q": 3240,
- "r": 3241,
- "s": 3242,
- "t": 3243,
- "u": 3244,
- "v": 3245,
- "w": 3246,
- "x": 3247,
- "y": 3248,
- "z": 3249,
- " ": 3250
- };
- static #codesPayload = [];
-
- static #addKeyboardCode(character) {
- const characterCode = this.#kuKeycodes[character.toLowerCase()];
- if (typeof characterCode === "object") {
- for (const code of characterCode) {
- this.#codesPayload.push({
- device: this.#device_id,
- code: code,
- delay: this.#delay_value,
- activate: 1,
- addDepress: "false",
- });
- }
- }
- else {
- if (characterCode !== undefined)
- this.#codesPayload.push({
- device: this.#device_id,
- code: characterCode,
- delay: this.#delay_value,
- activate: 1,
- addDepress: "false",
- });
- }
- }
-
- static createButtonCommands(waypoints) {
- this.#codesPayload = [];
- // Go to WP page
- this.#codesPayload.push(
- {
- device: this.#device_id,
- code: this.#kuKeycodes["NavCTRL"],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "false",
- }
- )
-
- for (const waypoint of waypoints) {
-
- //Type name (max 8 chars on display)
- for (let i = 0; i < 8; i++) {
- this.#addKeyboardCode(waypoint.name.charAt(i));
- };
-
- // Select L2 to store WP name
- this.#codesPayload.push({
- device: this.#device_id,
- code: this.#kuKeycodes["L2"],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "false",
- });
-
- //check if latitude is N or S
- if (waypoint.latHem === "N") {
- this.#codesPayload.push({
- device: this.#device_id,
- code: this.#kuKeycodes["n"],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "false",
- });
- } else {
- this.#codesPayload.push({
- device: this.#device_id,
- code: this.#kuKeycodes["s"],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "false",
- });
- }
-
- //Type lat
- for (let i = 0; i < waypoint.lat.length; i++) {
- waypoint.lat.charAt(i) !== "." &&
- this.#addKeyboardCode(waypoint.lat.charAt(i));
- }
-
- // Select L5 to store Lat
- this.#codesPayload.push({
- device: this.#device_id,
- code: this.#kuKeycodes["L5"],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "false",
- });
-
- //check if longitude is E or W
- if (waypoint.longHem === "E") {
- this.#codesPayload.push({
- device: this.#device_id,
- code: this.#kuKeycodes["e"],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "false",
- });
- } else {
- this.#codesPayload.push({
- device: this.#device_id,
- code: this.#kuKeycodes["w"],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "false",
- });
- }
-
- //Type long
- for (let i = 0; i < waypoint.long.length; i++) {
- waypoint.long.charAt(i) !== "." &&
- this.#addKeyboardCode(waypoint.long.charAt(i));
- }
-
- // Select L6 to store Long
- this.#codesPayload.push({
- device: this.#device_id,
- code: this.#kuKeycodes["L6"],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "false",
- })
-
- //increment WP
- this.#codesPayload.push({
- device: this.#device_id,
- code: this.#kuKeycodes["R1"],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "false",
- });
- }
- // DEC back to WP 1
- for (let i = 0; i < waypoints.length; i++) {
- this.#codesPayload.push({
- device: this.#device_id,
- code: this.#kuKeycodes["R2"],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "false",
- })
- }
-
- return this.#codesPayload;
- }
-}
-
+class hercules {
+ static extraDelay = 0;
+ static #device_id = 20;
+ static #delay_value = 50 + this.extraDelay;
+ static #kuKeycodes = {
+ "NavCTRL": 3213,
+ "L1": 3201,
+ "L2": 3202,
+ "L3": 3203,
+ "L4": 3204,
+ "L5": 3205,
+ "L6": 3206,
+ "R1": 3207,
+ "R2": 3208,
+ "R3": 3209,
+ "R4": 3210,
+ "R5": 3211,
+ "R6": 3212,
+ "1": 3214,
+ "2": 3215,
+ "3": 3216,
+ "4": 3217,
+ "5": 3218,
+ "6": 3219,
+ "7": 3220,
+ "8": 3221,
+ "9": 3222,
+ "0": 3223,
+ //letters
+ "a": 3224,
+ "b": 3225,
+ "c": 3226,
+ "d": 3227,
+ "e": 3228,
+ "f": 3229,
+ "g": 3230,
+ "h": 3231,
+ "i": 3232,
+ "j": 3233,
+ "k": 3234,
+ "l": 3235,
+ "m": 3236,
+ "n": 3237,
+ "o": 3238,
+ "p": 3239,
+ "q": 3240,
+ "r": 3241,
+ "s": 3242,
+ "t": 3243,
+ "u": 3244,
+ "v": 3245,
+ "w": 3246,
+ "x": 3247,
+ "y": 3248,
+ "z": 3249,
+ " ": 3250
+ };
+ static #codesPayload = [];
+
+ static #addKeyboardCode(character) {
+ const characterCode = this.#kuKeycodes[character.toLowerCase()];
+ if (typeof characterCode === "object") {
+ for (const code of characterCode) {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: code,
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "false",
+ });
+ }
+ }
+ else {
+ if (characterCode !== undefined)
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: characterCode,
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "false",
+ });
+ }
+ }
+
+ static createButtonCommands(waypoints) {
+ this.#codesPayload = [];
+ // Go to WP page
+ this.#codesPayload.push(
+ {
+ device: this.#device_id,
+ code: this.#kuKeycodes["NavCTRL"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "false",
+ }
+ )
+
+ for (const waypoint of waypoints) {
+
+ //Type name (max 8 chars on display)
+ for (let i = 0; i < 8; i++) {
+ this.#addKeyboardCode(waypoint.name.charAt(i));
+ };
+
+ // Select L2 to store WP name
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["L2"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "false",
+ });
+
+ //check if latitude is N or S
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["n"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "false",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["s"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "false",
+ });
+ }
+
+ //Type lat
+ for (let i = 0; i < waypoint.lat.length; i++) {
+ waypoint.lat.charAt(i) !== "." &&
+ this.#addKeyboardCode(waypoint.lat.charAt(i));
+ }
+
+ // Select L5 to store Lat
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["L5"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "false",
+ });
+
+ //check if longitude is E or W
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["e"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "false",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["w"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "false",
+ });
+ }
+
+ //Type long
+ for (let i = 0; i < waypoint.long.length; i++) {
+ waypoint.long.charAt(i) !== "." &&
+ this.#addKeyboardCode(waypoint.long.charAt(i));
+ }
+
+ // Select L6 to store Long
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["L6"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "false",
+ })
+
+ //increment WP
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["R1"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "false",
+ });
+ }
+ // DEC back to WP 1
+ for (let i = 0; i < waypoints.length; i++) {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["R2"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "false",
+ })
+ }
+
+ return this.#codesPayload;
+ }
+}
+
export default hercules;
\ No newline at end of file
diff --git a/src/moduleCommands/a10.js b/src/moduleCommands/a10.js
index b9278aa..c7cefa6 100644
--- a/src/moduleCommands/a10.js
+++ b/src/moduleCommands/a10.js
@@ -1,167 +1,202 @@
-class a10 {
- static extraDelay = 0;
- static #delay100 = 100 + this.extraDelay;
- static #kuKeycodes = {
- 1: 3015,
- 2: 3016,
- 3: 3017,
- 4: 3018,
- 5: 3019,
- 6: 3020,
- 7: 3021,
- 8: 3022,
- 9: 3023,
- 0: 3024,
- " ": 3057,
- ".": 3025,
- ",": 3025,
- "/": 3026,
- a: 3027,
- b: 3028,
- c: 3029,
- d: 3030,
- e: 3031,
- f: 3032,
- g: 3033,
- h: 3034,
- i: 3035,
- j: 3036,
- k: 3037,
- l: 3038,
- m: 3039,
- n: 3040,
- o: 3041,
- p: 3042,
- q: 3043,
- r: 3044,
- s: 3045,
- t: 3046,
- u: 3047,
- v: 3048,
- w: 3049,
- x: 3050,
- y: 3051,
- z: 3052,
- };
- static #codesPayload = [];
-
- static #addKeyboardCode(character) {
- const characterCode = this.#kuKeycodes[character.toLowerCase()];
- if (characterCode !== undefined)
- this.#codesPayload.push({
- device: 9,
- code: characterCode,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
-
- static createButtonCommands(waypoints) {
- this.#codesPayload = [];
- this.#codesPayload.push(
- {
- device: 9,
- code: 3011,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- {
- device: 9,
- code: 3005,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- );
-
- for (const waypoint of waypoints) {
- this.#codesPayload.push({
- device: 9,
- code: 3007,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //Type hem
- if (waypoint.latHem === "N") {
- this.#codesPayload.push({
- device: 9,
- code: 3040,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: 9,
- code: 3045,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- //Type lat
- for (let i = 0; i < waypoint.lat.length; i++) {
- waypoint.lat.charAt(i) !== "." &&
- this.#addKeyboardCode(waypoint.lat.charAt(i));
- }
- this.#codesPayload.push({
- device: 9,
- code: 3003,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //Type hem
- if (waypoint.longHem === "E") {
- this.#codesPayload.push({
- device: 9,
- code: 3031,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: 9,
- code: 3049,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- //Type long
- for (let i = 0; i < waypoint.long.length; i++) {
- waypoint.long.charAt(i) !== "." &&
- this.#addKeyboardCode(waypoint.long.charAt(i));
- }
- this.#codesPayload.push({
- device: 9,
- code: 3004,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //Type name
- const waypointNameLength =
- waypoint.name.length < 12 ? waypoint.name.length : 12;
- for (let i = 0; i < waypointNameLength; i++) {
- this.#addKeyboardCode(waypoint.name.charAt(i));
- }
- //Enter name
- this.#codesPayload.push({
- device: 9,
- code: 3005,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
-
- return this.#codesPayload;
- }
-}
-
-export default a10;
+class a10 {
+ static slotVariant = ""; // Holds the variant type
+ static extraDelay = 0;
+ static #delay100 = 100 + this.extraDelay;
+ static #kuKeycodes = {
+ 1: 3015,
+ 2: 3016,
+ 3: 3017,
+ 4: 3018,
+ 5: 3019,
+ 6: 3020,
+ 7: 3021,
+ 8: 3022,
+ 9: 3023,
+ 0: 3024,
+ " ": 3057,
+ ".": 3025,
+ ",": 3025,
+ "/": 3026,
+ a: 3027,
+ b: 3028,
+ c: 3029,
+ d: 3030,
+ e: 3031,
+ f: 3032,
+ g: 3033,
+ h: 3034,
+ i: 3035,
+ j: 3036,
+ k: 3037,
+ l: 3038,
+ m: 3039,
+ n: 3040,
+ o: 3041,
+ p: 3042,
+ q: 3043,
+ r: 3044,
+ s: 3045,
+ t: 3046,
+ u: 3047,
+ v: 3048,
+ w: 3049,
+ x: 3050,
+ y: 3051,
+ z: 3052,
+ };
+ static #codesPayload = [];
+
+ static #addKeyboardCode(character) {
+ const characterCode = this.#kuKeycodes[character.toLowerCase()];
+ if (characterCode !== undefined)
+ this.#codesPayload.push({
+ device: 9,
+ code: characterCode,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ static createButtonCommands(waypoints) {
+ this.#codesPayload = [];
+ this.#codesPayload.push(
+ {
+ device: 9,
+ code: 3011,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ {
+ device: 9,
+ code: 3005,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ );
+
+ for (let index = 0; index < waypoints.length; index++) {
+ const waypoint = waypoints[index]; // Access the waypoint using the index
+ const waypointNumber = index + 1; // Use the index to get the waypoint number starting at 1
+
+ //Press the CLR key, to avoid CDU INPUT ERROR
+ this.#codesPayload.push({
+ device: 9,
+ code: 3058,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ if (this.slotVariant === "a10ADD") {
+
+ //Press LSK R3 to Create new waypoint
+ this.#codesPayload.push({
+ device: 9,
+ code: 3007,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ } else if (this.slotVariant === "a10NEW") {
+ // Use the loop number as the waypoint name
+ const waypointName = waypointNumber.toString(); // Convert the waypoint number to string
+
+ for (let i = 0; i < Math.min(waypointName.length, 9); i++) {
+ this.#addKeyboardCode(waypointName.charAt(i)); // Type the waypoint number
+ }
+
+ //Press LSK L1 to select waypoint to be altered
+ this.#codesPayload.push({
+ device: 9,
+ code: 3001,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // Type hem
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push({
+ device: 9,
+ code: 3040,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: 9,
+ code: 3045,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ // Type lat
+ for (let i = 0; i < waypoint.lat.length; i++) {
+ waypoint.lat.charAt(i) !== "." &&
+ this.#addKeyboardCode(waypoint.lat.charAt(i));
+ }
+ this.#codesPayload.push({
+ device: 9,
+ code: 3003,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ // Type hem
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push({
+ device: 9,
+ code: 3031,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: 9,
+ code: 3049,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ // Type long
+ for (let i = 0; i < waypoint.long.length; i++) {
+ waypoint.long.charAt(i) !== "." &&
+ this.#addKeyboardCode(waypoint.long.charAt(i));
+ }
+ this.#codesPayload.push({
+ device: 9,
+ code: 3004,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ // Type name
+ const waypointNameLength =
+ waypoint.name.length < 12 ? waypoint.name.length : 12;
+ for (let i = 0; i < waypointNameLength; i++) {
+ this.#addKeyboardCode(waypoint.name.charAt(i));
+ }
+ // Enter name
+ this.#codesPayload.push({
+ device: 9,
+ code: 3005,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ return this.#codesPayload;
+ }
+}
+
+export default a10;
diff --git a/src/moduleCommands/ah64.js b/src/moduleCommands/ah64.js
index fd14a74..4efeb0f 100644
--- a/src/moduleCommands/ah64.js
+++ b/src/moduleCommands/ah64.js
@@ -1,210 +1,210 @@
-class ah64 {
- static extraDelay = 0;
- static #delay100 = 100 + this.extraDelay;
- static slotVariant = "";
- static #kuKeycodes = {
- 1: 3033,
- 2: 3034,
- 3: 3035,
- 4: 3036,
- 5: 3037,
- 6: 3038,
- 7: 3039,
- 8: 3040,
- 9: 3041,
- 0: 3043,
- " ": 3003,
- ".": 3042,
- ",": 3042,
- "/": 3045,
- "-": 3047,
- "+": 3046,
- a: 3007,
- b: 3008,
- c: 3009,
- d: 3010,
- e: 3011,
- f: 3012,
- g: 3013,
- h: 3014,
- i: 3015,
- j: 3016,
- k: 3017,
- l: 3018,
- m: 3019,
- n: 3020,
- o: 3021,
- p: 3022,
- q: 3023,
- r: 3024,
- s: 3025,
- t: 3026,
- u: 3027,
- v: 3028,
- w: 3029,
- x: 3030,
- y: 3031,
- z: 3032,
- };
- static #codesPayload = [];
-
- static #addKeyboardCode(character) {
- const characterCode = this.#kuKeycodes[character.toLowerCase()];
- if (characterCode !== undefined)
- this.#codesPayload.push({
- device: this.#isPilot() ? 29 : 30,
- code: characterCode,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
-
- static #isPilot() {
- return this.slotVariant === "AH-64D_BLK_IIpilot";
- }
-
- static createButtonCommands(waypoints) {
- this.#codesPayload = [];
- //Enter TSD Page
- this.#codesPayload.push({
- device: this.#isPilot() ? 43 : 45,
- code: 3029,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //goto Point page
- this.#codesPayload.push({
- device: this.#isPilot() ? 43 : 45,
- code: 3013,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
-
- for (const waypoint of waypoints) {
- //press ADD
- this.#codesPayload.push({
- device: this.#isPilot() ? 43 : 45,
- code: 3023,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //press IDENT
- this.#codesPayload.push({
- device: this.#isPilot() ? 43 : 45,
- code: 3024,
- delay: 200,
- activate: 1,
- addDepress: "true",
- });
- //press ENTER
- this.#codesPayload.push({
- device: this.#isPilot() ? 29 : 30,
- code: 3006,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //Type name (max 3 chars)
- for (let i = 0; i < 3; i++) {
- this.#addKeyboardCode(waypoint.name.charAt(i));
- }
- //press ENTER
- this.#codesPayload.push({
- device: this.#isPilot() ? 29 : 30,
- code: 3006,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //press CLR
- this.#codesPayload.push({
- device: this.#isPilot() ? 29 : 30,
- code: 3001,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //check if latitude is N or S
- if (waypoint.latHem === "N") {
- this.#codesPayload.push({
- device: this.#isPilot() ? 29 : 30,
- code: 3020,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: this.#isPilot() ? 29 : 30,
- code: 3025,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- //Type lat
- for (let i = 0; i < waypoint.lat.length; i++) {
- waypoint.lat.charAt(i) !== "." &&
- this.#addKeyboardCode(waypoint.lat.charAt(i));
- }
- //check if longitude is E or W
- if (waypoint.longHem === "E") {
- this.#codesPayload.push({
- device: this.#isPilot() ? 29 : 30,
- code: 3011,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: this.#isPilot() ? 29 : 30,
- code: 3029,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- //Type long
- for (let i = 0; i < waypoint.long.length; i++) {
- waypoint.long.charAt(i) !== "." &&
- this.#addKeyboardCode(waypoint.long.charAt(i));
- }
- //press ENTER
- this.#codesPayload.push({
- device: this.#isPilot() ? 29 : 30,
- code: 3006,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //press CLR
- this.#codesPayload.push({
- device: this.#isPilot() ? 29 : 30,
- code: 3001,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //Type elev
- for (let i = 0; i < waypoint.elev.length; i++) {
- this.#addKeyboardCode(waypoint.elev.charAt(i));
- }
- //press ENTER
- this.#codesPayload.push({
- device: this.#isPilot() ? 29 : 30,
- code: 3006,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- return this.#codesPayload;
- }
-}
-
-export default ah64;
+class ah64 {
+ static extraDelay = 0;
+ static #delay100 = 100 + this.extraDelay;
+ static slotVariant = "";
+ static #kuKeycodes = {
+ 1: 3033,
+ 2: 3034,
+ 3: 3035,
+ 4: 3036,
+ 5: 3037,
+ 6: 3038,
+ 7: 3039,
+ 8: 3040,
+ 9: 3041,
+ 0: 3043,
+ " ": 3003,
+ ".": 3042,
+ ",": 3042,
+ "/": 3045,
+ "-": 3047,
+ "+": 3046,
+ a: 3007,
+ b: 3008,
+ c: 3009,
+ d: 3010,
+ e: 3011,
+ f: 3012,
+ g: 3013,
+ h: 3014,
+ i: 3015,
+ j: 3016,
+ k: 3017,
+ l: 3018,
+ m: 3019,
+ n: 3020,
+ o: 3021,
+ p: 3022,
+ q: 3023,
+ r: 3024,
+ s: 3025,
+ t: 3026,
+ u: 3027,
+ v: 3028,
+ w: 3029,
+ x: 3030,
+ y: 3031,
+ z: 3032,
+ };
+ static #codesPayload = [];
+
+ static #addKeyboardCode(character) {
+ const characterCode = this.#kuKeycodes[character.toLowerCase()];
+ if (characterCode !== undefined)
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 29 : 30,
+ code: characterCode,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ static #isPilot() {
+ return this.slotVariant === "AH-64D_BLK_IIpilot";
+ }
+
+ static createButtonCommands(waypoints) {
+ this.#codesPayload = [];
+ //Enter TSD Page
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 43 : 45,
+ code: 3029,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ //goto Point page
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 43 : 45,
+ code: 3013,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ for (const waypoint of waypoints) {
+ //press ADD
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 43 : 45,
+ code: 3023,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ //press IDENT
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 43 : 45,
+ code: 3024,
+ delay: 200,
+ activate: 1,
+ addDepress: "true",
+ });
+ //press ENTER
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 29 : 30,
+ code: 3006,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ //Type name (max 3 chars)
+ for (let i = 0; i < 3; i++) {
+ this.#addKeyboardCode(waypoint.name.charAt(i));
+ }
+ //press ENTER
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 29 : 30,
+ code: 3006,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ //press CLR
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 29 : 30,
+ code: 3001,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ //check if latitude is N or S
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 29 : 30,
+ code: 3020,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 29 : 30,
+ code: 3025,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ //Type lat
+ for (let i = 0; i < waypoint.lat.length; i++) {
+ waypoint.lat.charAt(i) !== "." &&
+ this.#addKeyboardCode(waypoint.lat.charAt(i));
+ }
+ //check if longitude is E or W
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 29 : 30,
+ code: 3011,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 29 : 30,
+ code: 3029,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ //Type long
+ for (let i = 0; i < waypoint.long.length; i++) {
+ waypoint.long.charAt(i) !== "." &&
+ this.#addKeyboardCode(waypoint.long.charAt(i));
+ }
+ //press ENTER
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 29 : 30,
+ code: 3006,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ //press CLR
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 29 : 30,
+ code: 3001,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ //Type elev
+ for (let i = 0; i < waypoint.elev.length; i++) {
+ this.#addKeyboardCode(waypoint.elev.charAt(i));
+ }
+ //press ENTER
+ this.#codesPayload.push({
+ device: this.#isPilot() ? 29 : 30,
+ code: 3006,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ return this.#codesPayload;
+ }
+}
+
+export default ah64;
diff --git a/src/moduleCommands/ah6j.js b/src/moduleCommands/ah6j.js
new file mode 100644
index 0000000..820f30d
--- /dev/null
+++ b/src/moduleCommands/ah6j.js
@@ -0,0 +1,1031 @@
+//====================================================
+// AH-6J TNL3100 Waypoint Entry Logic
+// State-machine structure with explicit page-change
+// delays and helper-based data-entry sections
+//====================================================
+
+class ah6j {
+
+ static slotVariant = "";
+
+ //====================================================
+ // DEVICE + TIMING
+ //====================================================
+
+ static #device = 13;
+
+ // user adjustable delay added to every button press
+ static extraDelay = 0;
+
+ static #delay0 = 10 + this.extraDelay;
+ static #delay100 = 100 + this.extraDelay;
+ static #delay200 = 200 + this.extraDelay;
+ static #delay500 = 500 + this.extraDelay;
+
+
+ //====================================================
+ // COCKPIT COMMAND CODES
+ //====================================================
+
+ static #codes = {
+ WPT: 3306,
+ FPL: 3309,
+ AUX: 3311,
+ ENT: 3313,
+ INNER: 3314,
+ OUTER: 3315,
+ };
+
+ static #codesPayload = [];
+
+ static #nameCharset = "XYZ0123456789 ABCDEFGHIJKLMNOPQRSTUVW";
+
+
+ //====================================================
+ // KNOB / BUTTON HELPERS
+ //====================================================
+
+ static #pushInnerSteps(device, steps) {
+
+ const dir = steps >= 0 ? 1 : -1;
+
+ for (let i = 0; i < Math.abs(steps); i++) {
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.INNER,
+ delay: this.#delay0,
+ activate: dir,
+ });
+ }
+
+ }
+
+
+ static #pushOuterCW(device, count = 1) {
+
+ for (let i = 0; i < count; i++) {
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.OUTER,
+ delay: this.#delay100,
+ activate: 1,
+ });
+ }
+
+ }
+
+
+ static #pushOuterCCW(device, count = 1) {
+
+ for (let i = 0; i < count; i++) {
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.OUTER,
+ delay: this.#delay100,
+ activate: -1,
+ });
+ }
+
+ }
+
+
+ static #pushOuterCWName(device, count = 1) {
+
+ for (let i = 0; i < count; i++) {
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.OUTER,
+ delay: 10 + this.extraDelay,
+ activate: 1,
+ });
+ }
+
+ }
+
+
+ static #press(device, code) {
+
+ this.#codesPayload.push({
+ device,
+ code,
+ delay: this.#delay200,
+ activate: 1,
+ addDepress: "false",
+ });
+
+ this.#codesPayload.push({
+ device,
+ code,
+ delay: this.#delay500,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ }
+
+
+ //====================================================
+ // LABEL UTILITIES
+ //====================================================
+
+ static #indexInCharset(charset, ch) {
+ const i = charset.indexOf(ch);
+ return i >= 0 ? i : 0;
+ }
+
+
+ static #shortestSteps(from, to, charset) {
+
+ const len = charset.length;
+
+ const a = this.#indexInCharset(charset, from);
+ const b = this.#indexInCharset(charset, to);
+
+ const cw = (b - a + len) % len;
+ const ccw = cw - len;
+
+ return Math.abs(cw) <= Math.abs(ccw) ? cw : ccw;
+
+ }
+
+
+ static #sanitizeName(name) {
+
+ const raw = String(name || "")
+ .toUpperCase()
+ .replace(/[^A-Z0-9 ]/g, " ");
+
+ return raw.padEnd(3, " ").slice(0, 3);
+
+ }
+
+
+ static #buildLabel(waypoint) {
+
+ const rawName = String(waypoint?.name || "").trim();
+
+ const m = rawName.match(/^Waypoint\s+(\d+)$/i);
+
+ if (m) {
+
+ const n = parseInt(m[1], 10) || 0;
+ const num = Math.max(1, Math.min(99, n));
+
+ return `WP${num}`.padEnd(4, " ");
+
+ }
+
+ return rawName
+ .toUpperCase()
+ .replace(/[^A-Z0-9 ]/g, " ")
+ .padEnd(4, " ")
+ .slice(0, 4);
+ }
+
+
+ //====================================================
+ // COORDINATE UTILITIES
+ //====================================================
+
+ static #parseCoord(coord) {
+
+ const p = String(coord || "").split(".");
+
+ if (p.length !== 3) return { deg: 0, min: 0 };
+
+ const deg = parseInt(p[0], 10) || 0;
+ const min = (parseInt(p[1], 10) || 0) * 1000 + (parseInt(p[2], 10) || 0);
+
+ return { deg, min };
+
+ }
+
+
+ static parseTNLCoord(str) {
+
+ if (!str) return null;
+
+ const m = String(str).match(
+ /(\d+)\*(\d+\.\d+)([NS])(\d+)\*(\d+\.\d+)([EW])/
+ );
+
+ if (!m) return null;
+
+ return {
+ lat: {
+ deg: parseInt(m[1], 10),
+ min: Math.round(parseFloat(m[2]) * 1000),
+ hemi: m[3],
+ },
+ lon: {
+ deg: parseInt(m[4], 10),
+ min: Math.round(parseFloat(m[5]) * 1000),
+ hemi: m[6],
+ },
+ };
+
+ }
+
+
+ static #coordObjToTheWayString(coordObj) {
+
+ if (!coordObj) return "0.0.0";
+
+ const deg = coordObj.deg || 0;
+ const minInt = coordObj.min || 0;
+ const minWhole = Math.floor(minInt / 1000);
+ const minFrac = minInt % 1000;
+
+ return `${deg}.${minWhole}.${String(minFrac).padStart(3, "0")}`;
+
+ }
+
+
+ static #directDigitSteps(a, b) {
+ return (b || 0) - (a || 0);
+ }
+
+
+ static #minuteDigits(value) {
+ return String(Math.max(0, value || 0))
+ .padStart(5, "0")
+ .slice(-5)
+ .split("")
+ .map((d) => parseInt(d, 10) || 0);
+ }
+
+
+ static #degreeDigits(value, width) {
+ return String(Math.max(0, value || 0))
+ .padStart(width, "0")
+ .slice(-width)
+ .split("")
+ .map((d) => parseInt(d, 10) || 0);
+ }
+
+
+ static #getTargetHemisphere(waypoint, axis) {
+
+ if (axis === "lat") {
+ const explicit = String(
+ waypoint?.latHemi ??
+ waypoint?.latHemisphere ??
+ waypoint?.ns ??
+ waypoint?.northSouth ??
+ ""
+ ).toUpperCase();
+
+ if (explicit === "N" || explicit === "S") return explicit;
+
+ const lat = String(waypoint?.lat ?? "").trim();
+ if (lat.startsWith("S") || lat.startsWith("-")) return "S";
+ return "N";
+ }
+
+ const explicit = String(
+ waypoint?.longHemi ??
+ waypoint?.longHemisphere ??
+ waypoint?.ew ??
+ waypoint?.eastWest ??
+ ""
+ ).toUpperCase();
+
+ if (explicit === "E" || explicit === "W") return explicit;
+
+ const lon = String(waypoint?.long ?? "").trim();
+ if (lon.startsWith("W") || lon.startsWith("-")) return "W";
+ return "E";
+
+ }
+
+
+ //====================================================
+ // STATE 1
+ // INITIAL TNL3100 MODE SETUP
+ // AUX → AUX → WPT
+ //====================================================
+
+ static buildInitialModeCommands() {
+
+ const device = this.#device;
+
+ const oldPayload = this.#codesPayload;
+ this.#codesPayload = [];
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.AUX,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.AUX,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.WPT,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.WPT,
+ delay: 500 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ const result = this.#codesPayload.slice();
+ this.#codesPayload = oldPayload;
+
+ return result;
+
+ }
+
+
+ //====================================================
+ // STATE 2
+ // CREATE WAYPOINT PAGE
+ // ADD WAYPOINT → LAT/LONG
+ //====================================================
+
+ static buildCreateLatLongPageCommands() {
+
+ const device = this.#device;
+
+ const oldPayload = this.#codesPayload;
+ this.#codesPayload = [];
+
+ this.#codesPayload.push({
+ device,
+ code: 0,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.OUTER,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.OUTER,
+ delay: 500 + this.extraDelay,
+ activate: 1,
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 500 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.OUTER,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 200 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: 0,
+ delay: 500 + this.extraDelay,
+ activate: 0,
+ });
+
+ const result = this.#codesPayload.slice();
+ this.#codesPayload = oldPayload;
+
+ return result;
+
+ }
+
+
+ //====================================================
+ // STATE 2A
+ // OPEN WAYPOINT ENTRY PAGE
+ //====================================================
+
+ static buildWaypointEntryPageCommands() {
+ return this.buildCreateLatLongPageCommands();
+ }
+
+
+ //====================================================
+ // REPLACE-MODE HELPERS
+ //====================================================
+
+ static buildDeleteCurrentWaypointCommands() {
+
+ const device = this.#device;
+ const oldPayload = this.#codesPayload;
+ this.#codesPayload = [];
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.OUTER,
+ delay: 200 + this.extraDelay,
+ activate: -1,
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 700 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ const result = this.#codesPayload.slice();
+ this.#codesPayload = oldPayload;
+
+ return result;
+
+ }
+
+
+ static buildStepToPreviousWaypointCommands() {
+
+ const device = this.#device;
+ const oldPayload = this.#codesPayload;
+ this.#codesPayload = [];
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.INNER,
+ delay: 300 + this.extraDelay,
+ activate: -1,
+ });
+
+ const result = this.#codesPayload.slice();
+ this.#codesPayload = oldPayload;
+
+ return result;
+
+ }
+
+
+ //====================================================
+ // STATE 1
+ // ENTER WAYPOINT NAME
+ //====================================================
+
+ static #enterWaypointName(device, targetLabel) {
+
+ const currentLabel = ["X", "X", "X", "X"];
+
+ for (let c = 0; c < 4; c++) {
+
+ const s = this.#shortestSteps(
+ currentLabel[c],
+ targetLabel[c],
+ this.#nameCharset
+ );
+
+ this.#pushInnerSteps(device, s);
+
+ if (c < 3) {
+ this.#pushOuterCWName(device, 1);
+ }
+
+ }
+
+ }
+
+
+ //====================================================
+ // STATE 4
+ // ENTER LAT / LONG COORDINATES
+ //
+ // Cursor order after name char 4:
+ // LAT deg digit 1 -> LAT deg digit 2 -> LAT min x5 -> LAT hemi
+ // -> LON deg digit 1 -> LON deg digit 2 -> LON deg digit 3
+ // -> LON min x5 -> LON hemi -> wraps back to row 1 char 1
+ //====================================================
+
+ static #enterCoordinates(device, prev, next) {
+
+ const prevLat = this.#parseCoord(prev.lat);
+ const nextLat = this.#parseCoord(next.lat);
+
+ const prevLon = this.#parseCoord(prev.long);
+ const nextLon = this.#parseCoord(next.long);
+
+ const prevLatHemi = String(prev.latHemi || "N").toUpperCase();
+ const nextLatHemi = this.#getTargetHemisphere(next, "lat");
+
+ const prevLonHemi = String(prev.longHemi || "E").toUpperCase();
+ const nextLonHemi = this.#getTargetHemisphere(next, "long");
+
+ const prevLatDegDigits = this.#degreeDigits(prevLat.deg, 2);
+ const nextLatDegDigits = this.#degreeDigits(nextLat.deg, 2);
+
+ const prevLatMinDigits = this.#minuteDigits(prevLat.min);
+ const nextLatMinDigits = this.#minuteDigits(nextLat.min);
+
+ const prevLonDegDigits = this.#degreeDigits(prevLon.deg, 3);
+ const nextLonDegDigits = this.#degreeDigits(nextLon.deg, 3);
+
+ const prevLonMinDigits = this.#minuteDigits(prevLon.min);
+ const nextLonMinDigits = this.#minuteDigits(nextLon.min);
+
+ this.#codesPayload.push({
+ device,
+ code: 0,
+ delay: 200 + this.extraDelay,
+ activate: 0,
+ });
+
+ // move from row 1 char 4 to latitude degree digit 1
+ this.#pushOuterCW(device, 1);
+
+ // latitude degrees (2 digits)
+ for (let i = 0; i < 2; i++) {
+ const s = this.#directDigitSteps(
+ prevLatDegDigits[i],
+ nextLatDegDigits[i],
+ );
+
+ this.#pushInnerSteps(device, s);
+ this.#pushOuterCW(device, 1);
+ }
+
+ // latitude minutes (5 digits)
+ for (let i = 0; i < 5; i++) {
+ const s = this.#directDigitSteps(
+ prevLatMinDigits[i],
+ nextLatMinDigits[i],
+ );
+
+ this.#pushInnerSteps(device, s);
+ this.#pushOuterCW(device, 1);
+ }
+
+ // latitude hemisphere
+ this.#pushInnerSteps(
+ device,
+ this.#shortestSteps(prevLatHemi, nextLatHemi, "NS")
+ );
+
+ // move to longitude degree digit 1
+ this.#pushOuterCW(device, 1);
+
+ // longitude degrees (3 digits)
+ for (let i = 0; i < 3; i++) {
+ const s = this.#directDigitSteps(
+ prevLonDegDigits[i],
+ nextLonDegDigits[i],
+ );
+
+ this.#pushInnerSteps(device, s);
+ this.#pushOuterCW(device, 1);
+ }
+
+ // longitude minutes (5 digits)
+ for (let i = 0; i < 5; i++) {
+ const s = this.#directDigitSteps(
+ prevLonMinDigits[i],
+ nextLonMinDigits[i],
+ );
+
+ this.#pushInnerSteps(device, s);
+ this.#pushOuterCW(device, 1);
+ }
+
+ // longitude hemisphere
+ this.#pushInnerSteps(
+ device,
+ this.#shortestSteps(prevLonHemi, nextLonHemi, "EW")
+ );
+
+ // wrap back to first character of waypoint name
+ this.#pushOuterCW(device, 1);
+
+ this.#codesPayload.push({
+ device,
+ code: 0,
+ delay: 400 + this.extraDelay,
+ activate: 0,
+ });
+
+ }
+
+
+ //====================================================
+ // STATE 5
+ // SAVE WAYPOINT
+ //====================================================
+
+ static #saveWaypoint(device) {
+
+ this.#codesPayload.push({
+ device,
+ code: 0,
+ delay: 100 + this.extraDelay,
+ activate: 0,
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 200 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: 0,
+ delay: 800 + this.extraDelay,
+ activate: 0,
+ });
+
+ }
+
+ //====================================================
+ // FLIGHT PLAN STATE 1
+ // DELETE CURRENT FLIGHT PLAN
+ // FPL -> ENT -> INNER CW -> ENT
+ //====================================================
+
+ static buildDeleteCurrentFlightPlanCommands() {
+
+ const device = this.#device;
+ const oldPayload = this.#codesPayload;
+ this.#codesPayload = [];
+
+ this.#codesPayload.push({
+ device,
+ code: 0,
+ delay: 100 + this.extraDelay,
+ activate: 0,
+ });
+
+ // FPL press
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.FPL,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.FPL,
+ delay: 600 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ // FPL press
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.FPL,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.FPL,
+ delay: 600 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+
+ // ENT press
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 500 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+
+ // INNER CW once
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.INNER,
+ delay: 300 + this.extraDelay,
+ activate: 1,
+ });
+
+
+ // ENT press
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 200 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ // ENT press
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 200 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ const result = this.#codesPayload.slice();
+ this.#codesPayload = oldPayload;
+
+ return result;
+
+ }
+
+
+ //====================================================
+ // FLIGHT PLAN STATE 2
+ // ADD CURRENT WAYPOINT TO FLIGHT PLAN
+ // WPT -> INNER CCW -> ENT -> FPL
+ //====================================================
+
+ static buildAddCurrentWaypointToFlightPlanCommands() {
+
+ const device = this.#device;
+ const oldPayload = this.#codesPayload;
+ this.#codesPayload = [];
+
+ this.#codesPayload.push({
+ device,
+ code: 0,
+ delay: 100 + this.extraDelay,
+ activate: 0,
+ });
+
+ // WPT press
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.WPT,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.WPT,
+ delay: 800 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: 0,
+ delay: 500 + this.extraDelay,
+ activate: 0,
+ });
+
+ // INNER CCW once
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.INNER,
+ delay: 300 + this.extraDelay,
+ activate: -1,
+ });
+
+ // ENT press
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.ENT,
+ delay: 800 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ // FPL press
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.FPL,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.FPL,
+ delay: 500 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+
+
+ const result = this.#codesPayload.slice();
+ this.#codesPayload = oldPayload;
+
+ return result;
+
+ }
+
+
+ //====================================================
+ // FLIGHT PLAN STATE 3
+ // ACTIVATE FLIGHT PLAN
+ // FPL -> FPL
+ //====================================================
+
+ static buildActivateFlightPlanCommands() {
+
+ const device = this.#device;
+ const oldPayload = this.#codesPayload;
+ this.#codesPayload = [];
+
+ this.#codesPayload.push({
+ device,
+ code: 0,
+ delay: 200 + this.extraDelay,
+ activate: 0,
+ });
+
+ // FPL press
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.FPL,
+ delay: 200 + this.extraDelay,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device,
+ code: this.#codes.FPL,
+ delay: 500 + this.extraDelay,
+ activate: 0,
+ addDepress: "false",
+ });
+
+
+ const result = this.#codesPayload.slice();
+ this.#codesPayload = oldPayload;
+
+ return result;
+
+ }
+
+
+ //====================================================
+ // FLIGHT PLAN STATE 4
+ // BUILD NEW FLIGHT PLAN FROM LIST
+ //====================================================
+
+ static buildNewFlightPlanFromListCommands(waypointCount = 0) {
+
+ const oldPayload = this.#codesPayload;
+ this.#codesPayload = [];
+
+ const count = Math.max(0, Number(waypointCount) || 0);
+
+ if (count <= 0) {
+ const result = this.#codesPayload.slice();
+ this.#codesPayload = oldPayload;
+ return result;
+ }
+
+ const deletePayload = this.buildDeleteCurrentFlightPlanCommands();
+ this.#codesPayload.push(...deletePayload);
+
+ for (let i = 0; i < count; i++) {
+ const addPayload = this.buildAddCurrentWaypointToFlightPlanCommands();
+ this.#codesPayload.push(...addPayload);
+ }
+
+ const activatePayload = this.buildActivateFlightPlanCommands();
+ this.#codesPayload.push(...activatePayload);
+
+ const result = this.#codesPayload.slice();
+ this.#codesPayload = oldPayload;
+
+ return result;
+
+ }
+
+
+ //====================================================
+ // MAIN STATE MACHINE
+ //====================================================
+
+ static createWaypointCommandsFromDisplay(nextWaypoint, waypointNumber, display) {
+
+ this.#codesPayload = [];
+
+ const device = this.#device;
+ const next = nextWaypoint || {};
+ const row2 = display?.row2 || "";
+
+ const targetLabel = this.#buildLabel(next, waypointNumber).split("");
+
+ this.#enterWaypointName(device, targetLabel);
+
+ const currentCoord = this.parseTNLCoord(row2);
+
+ const prev = currentCoord
+ ? {
+ lat: this.#coordObjToTheWayString(currentCoord.lat),
+ long: this.#coordObjToTheWayString(currentCoord.lon),
+ latHemi: currentCoord.lat.hemi,
+ longHemi: currentCoord.lon.hemi,
+ }
+ : {
+ lat: "0.0.0",
+ long: "0.0.0",
+ latHemi: "N",
+ longHemi: "E",
+ };
+
+ this.#enterCoordinates(device, prev, next);
+
+ this.#saveWaypoint(device);
+
+ return this.#codesPayload;
+
+ }
+
+}
+
+export default ah6j;
\ No newline at end of file
diff --git a/src/moduleCommands/askUserAboutSeat.js b/src/moduleCommands/askUserAboutSeat.js
index 1af3b6d..cdbe318 100644
--- a/src/moduleCommands/askUserAboutSeat.js
+++ b/src/moduleCommands/askUserAboutSeat.js
@@ -1,96 +1,208 @@
-import { TwoOptionsDialog } from "../components/TwoOptionsDialog";
-import { AlertDialog } from "../components/AlertDialog";
-
-const askUserAboutSeat = async (module, userPreferences) => {
- const moduleSpecificPreferences = userPreferences[module];
-
- if (module === "AH-64D_BLK_II") {
- if (moduleSpecificPreferences?.includes("Pilot"))
- return "AH-64D_BLK_IIpilot";
- else if (moduleSpecificPreferences?.includes("CPG/Gunner"))
- return "AH-64D_BLK_IIgunner";
- else {
- return TwoOptionsDialog({
- title: "What seat are you in?",
- op1: "Pilot",
- op2: "CPG/Gunner",
- }).then((option) =>
- option === "CPG/Gunner" ? "AH-64D_BLK_IIgunner" : "AH-64D_BLK_IIpilot",
- );
- }
- } else if (
- module === "FA-18C_hornet" ||
- module === "FA-18E" ||
- module === "FA-18F" ||
- module === "EA-18G"
- ) {
- if (moduleSpecificPreferences?.includes("Hide")) return "FA-18C_hornet";
- else {
- return AlertDialog({
- title: "Please make sure that",
- content:
- "1. PRECISE option is boxed in HSI > DATA\n" +
- "2. You are not in the TAC menu\n" +
- "3. You are in the 00°00.0000' coordinate format",
- }).then(() => "FA-18C_hornet");
- }
- } else if (module === "F-15ESE") {
- let seat;
- if (moduleSpecificPreferences?.includes("Pilot")) seat = "Pilot";
- else if (moduleSpecificPreferences?.includes("WSO")) seat = "WSO";
- else {
- await TwoOptionsDialog({
- title: "What seat are you in?",
- op1: "Pilot",
- op2: "WSO",
- }).then((chosenSeat) => (seat = chosenSeat));
- }
-
- let route;
- if (moduleSpecificPreferences?.includes("A{1/A}")) route = "A{1/A}";
- else if (moduleSpecificPreferences?.includes("B{1/B}")) route = "B{1/B}";
- else {
- await TwoOptionsDialog({
- title: "What route are you using?",
- op1: "A{1/A}", // op1: "A"
- op2: "B{1/B}", // op2: "B" // removed because its a nice touch to keep the explicitness of this here, instead of making it more inline.
- }).then((chosenRoute) => (route = chosenRoute));
- }
- return `F-15ESE_${seat.toLowerCase()}${route === "A{1/A}" ? "A" : "B"}`;
- } else if (module === "UH-60L") {
- if (moduleSpecificPreferences?.includes("Hide")) return "UH-60L";
- else {
- return AlertDialog({
- title: "Be advised:",
- content:
- "This may overwrite waypoints! " +
- "If WP/TGT is on MIZ0 (00), 01 onwards will be overwritten.",
- }).then(() => "UH-60L");
- }
- } else if (module === "Hercules") {
- if (moduleSpecificPreferences?.includes("Hide")) return "Hercules";
- else {
- return AlertDialog({
- title: "Be advised:",
- content:
- "Make sure you have downloaded the Patch for the Hercules module: https://github.com/Summit60/DCS-Hercules-TheWay-patch",
- }).then(() => "Hercules");
- }
- } else if (module === "OH58D") {
- if (moduleSpecificPreferences?.includes("Right Seat"))
- return "OH58Dright-seat";
- else if (moduleSpecificPreferences?.includes("Left Seat"))
- return "OH58Dleft-seat";
- else {
- return TwoOptionsDialog({
- title: "What seat are you in?",
- op1: "Right Seat",
- op2: "Left Seat",
- }).then((option) =>
- option === "Left Seat" ? "OH58Dleft-seat" : "OH58Dright-seat",
- );
- }
- } else return module;
-};
-
-export default askUserAboutSeat;
+import {
+ FourOptionsDialog,
+ FourOptionsSimpleDialog,
+} from "../components/FourOptionsDialog";
+import { AlertDialog } from "../components/AlertDialog";
+
+const askUserAboutSeat = async (module, userPreferences) => {
+ const moduleSpecificPreferences = userPreferences[module];
+
+ // A-10C/2
+ if (module === "A-10C" || module === "A-10C_2") {
+ if (moduleSpecificPreferences?.includes("Add Waypoints")) return "a10ADD";
+ if (moduleSpecificPreferences?.includes("Overwrite Waypoints")) return "a10NEW";
+
+ const option = await FourOptionsDialog({
+ title: "Would you like to?",
+ op1: "Add Waypoints",
+ op2: "Overwrite Waypoints",
+ });
+
+ return option === "Add Waypoints" ? "a10ADD" : "a10NEW";
+ }
+
+ // ✅ RESTORED AH-6J / MH-6J (CRITICAL)
+ else if (module === "AH-6J" || module === "MH-6J") {
+ if (moduleSpecificPreferences?.includes("Add Waypoints")) return `${module}_ADD`;
+ if (moduleSpecificPreferences?.includes("Replace Waypoints")) return `${module}_REPLACE`;
+ if (moduleSpecificPreferences?.includes("NEW FPLN FROM LIST")) return `${module}_NEWFPLN`;
+
+ const option = await FourOptionsDialog({
+ title: "Would you like to?",
+ op1: "Add Waypoints",
+ op2: "Replace Waypoints",
+ op3: "NEW FPLN FROM LIST",
+ });
+
+ switch (option) {
+ case "Replace Waypoints":
+ return `${module}_REPLACE`;
+ case "NEW FPLN FROM LIST":
+ return `${module}_NEWFPLN`;
+ default:
+ return `${module}_ADD`;
+ }
+ }
+
+ // AH-64D
+ else if (module === "AH-64D_BLK_II") {
+ if (moduleSpecificPreferences?.includes("Pilot")) return "AH-64D_BLK_IIpilot";
+ if (moduleSpecificPreferences?.includes("CPG/Gunner")) return "AH-64D_BLK_IIgunner";
+
+ return FourOptionsDialog({
+ title: "What seat are you in?",
+ op1: "Pilot",
+ op2: "CPG/Gunner",
+ }).then((option) =>
+ option === "CPG/Gunner"
+ ? "AH-64D_BLK_IIgunner"
+ : "AH-64D_BLK_IIpilot"
+ );
+ }
+
+ // AV8BNA
+ else if (module === "AV8BNA") {
+ if (moduleSpecificPreferences?.includes("Waypoints")) return "AV8BNA_WPT";
+ if (moduleSpecificPreferences?.includes("Targetpoints")) return "AV8BNA_TRGPT";
+
+ const option = await FourOptionsDialog({
+ title: "Transfer to waypoints or target points?",
+ op1: "Waypoints",
+ op2: "Targetpoints",
+ });
+
+ return option === "Targetpoints" ? "AV8BNA_TRGPT" : "AV8BNA_WPT";
+ }
+
+ // C-130J
+ else if (module === "C-130J-30") {
+ if (moduleSpecificPreferences?.includes("Left Seat")) return "c130Plt";
+ if (moduleSpecificPreferences?.includes("Right Seat")) return "c130CoPlt";
+
+ const option = await FourOptionsDialog({
+ title: "What seat are you in?",
+ op1: "Left Seat",
+ op2: "Right Seat",
+ });
+
+ return option === "Left Seat" ? "c130Plt" : "c130CoPlt";
+ }
+
+ // CH-47F
+ else if (module === "CH-47Fbl1") {
+ if (moduleSpecificPreferences?.includes("Add to FLPN")) return "ch47ADD";
+ if (moduleSpecificPreferences?.includes("Make New FLPN")) return "ch47NEW";
+ if (moduleSpecificPreferences?.includes("Add to ALT FLPN")) return "ch47ALTADD";
+ if (moduleSpecificPreferences?.includes("Make New ALT FLPN")) return "ch47ALTNEW";
+
+ const option = await FourOptionsDialog({
+ title: "Would you like to?",
+ op1: "Add to FLPN",
+ op2: "Make New FLPN",
+ op3: "Add to ALT FLPN",
+ op4: "Make New ALT FLPN",
+ });
+
+ switch (option) {
+ case "Add to FLPN": return "ch47ADD";
+ case "Make New FLPN": return "ch47NEW";
+ case "Add to ALT FLPN": return "ch47ALTADD";
+ case "Make New ALT FLPN": return "ch47ALTNEW";
+ default: throw new Error("Invalid option selected");
+ }
+ }
+
+ // F-15E
+ else if (module === "F-15ESE") {
+ let seat;
+
+ if (moduleSpecificPreferences?.includes("Pilot")) seat = "Pilot";
+ else if (moduleSpecificPreferences?.includes("WSO")) seat = "WSO";
+ else {
+ seat = await FourOptionsDialog({
+ title: "What seat are you in?",
+ op1: "Pilot",
+ op2: "WSO",
+ });
+ }
+
+ const jdam = await FourOptionsDialog({
+ title: "Input Type?",
+ op1: "Waypoints",
+ op2: "Target Points",
+ op3: "TP's and TXFR to Weapons",
+ });
+
+ const route = {
+ Waypoints: "waypoints",
+ "Target Points": "targetpoints",
+ "TP's and TXFR to Weapons": "txfr",
+ }[jdam];
+
+ return `F-15ESE_${seat.toLowerCase()}_${route}`;
+ }
+
+ // FA-18
+else if (
+ module === "FA-18C_hornet" ||
+ module === "FA-18E" ||
+ module === "FA-18F" ||
+ module === "EA-18G"
+) {
+ let PPinput;
+
+ if (moduleSpecificPreferences?.includes("YES")) PPinput = "YES";
+ else if (moduleSpecificPreferences?.includes("NO")) PPinput = "NO";
+ else {
+ PPinput = await FourOptionsDialog({
+ title: "Input as PP MSN?",
+ op1: "YES",
+ op2: "NO",
+ });
+ }
+
+ let stations = "";
+
+ if (PPinput === "YES") {
+ if (moduleSpecificPreferences?.includes("1")) stations = "1";
+ else if (moduleSpecificPreferences?.includes("2")) stations = "2";
+ else if (moduleSpecificPreferences?.includes("3")) stations = "3";
+ else if (moduleSpecificPreferences?.includes("4")) stations = "4";
+ else {
+ stations = await FourOptionsDialog({
+ title: "How many STATIONs carry this weapon?",
+ op1: "1",
+ op2: "2",
+ op3: "3",
+ op4: "4",
+ });
+ }
+ }
+
+return `FA-18C_hornet${PPinput === "YES" ? "PP" : ""}${stations}`;
+}
+
+ // OH-58D
+ else if (module === "OH58D") {
+ if (moduleSpecificPreferences?.includes("Left Seat"))
+ return "OH58Dleft-seat";
+
+ if (moduleSpecificPreferences?.includes("Right Seat"))
+ return "OH58Dright-seat";
+
+ const option = await FourOptionsDialog({
+ title: "What seat are you in?",
+ op1: "Left Seat",
+ op2: "Right Seat",
+ });
+
+ return option === "Right Seat"
+ ? "OH58Dright-seat"
+ : "OH58Dleft-seat";
+ }
+
+ // default
+ else return module;
+};
+
+export default askUserAboutSeat;
diff --git a/src/moduleCommands/av8b.js b/src/moduleCommands/av8b.js
index 84133d7..780e365 100644
--- a/src/moduleCommands/av8b.js
+++ b/src/moduleCommands/av8b.js
@@ -1,211 +1,294 @@
-class av8b {
- static extraDelay = 0;
- static #delay100 = 100 + this.extraDelay;
- static #kuKeycodes = {
- 1: 3302,
- 2: 3303,
- 3: 3304,
- 4: 3306,
- 5: 3307,
- 6: 3308,
- 7: 3310,
- 8: 3311,
- 9: 3312,
- 0: 3315,
- };
- static #codesPayload = [];
-
- static #addKeyboardCode(character) {
- const characterCode = this.#kuKeycodes[character.toLowerCase()];
- if (characterCode !== undefined)
- this.#codesPayload.push({
- device: 23,
- code: characterCode,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
-
- static createButtonCommands(waypoints) {
- this.#codesPayload = [];
- this.#codesPayload.push(
- //nav mode
- {
- device: 12,
- code: 3282,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- //left MFD menu
- {
- device: 26,
- code: 3217,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- //EHSD
- {
- device: 26,
- code: 3201,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- //DATA
- {
- device: 26,
- code: 3201,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- );
- for (const waypoint of waypoints) {
- //increment
- this.#codesPayload.push(
- {
- device: 23,
- code: 3312,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- {
- device: 23,
- code: 3312,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- //ENT
- {
- device: 23,
- code: 3314,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- //ODU 2
- {
- device: 24,
- code: 3251,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- );
- //Type hem
- if (waypoint.latHem === "N") {
- this.#codesPayload.push({
- device: 23,
- code: 3303,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: 23,
- code: 3311,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- //type lat
- for (let i = 0; i < waypoint.lat.length; i++) {
- this.#addKeyboardCode(waypoint.lat.charAt(i));
- }
- this.#codesPayload.push(
- //ENT
- {
- device: 23,
- code: 3314,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- );
- //Type hem
- if (waypoint.longHem === "E") {
- this.#codesPayload.push({
- device: 23,
- code: 3308,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: 23,
- code: 3306,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- //type long
- for (let i = 0; i < waypoint.long.length; i++) {
- this.#addKeyboardCode(waypoint.long.charAt(i));
- }
- this.#codesPayload.push(
- //ENT
- {
- device: 23,
- code: 3314,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- );
- // Select elevation
- this.#codesPayload.push({
- device: 24,
- code: 3252,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //type elevation
- for (let i = 0; i < waypoint.elev.length; i++) {
- this.#addKeyboardCode(waypoint.elev.charAt(i));
- }
- this.#codesPayload.push(
- //ENT
- {
- device: 23,
- code: 3314,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- );
- this.#codesPayload.push(
- //revert to selection mode
- {
- device: 24,
- code: 3250,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- );
- }
- this.#codesPayload.push(
- //deselect DATA
- {
- device: 26,
- code: 3201,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- );
- return this.#codesPayload;
- }
-}
-
-export default av8b;
+class av8b {
+ static slotVariant = "";
+ static extraDelay = 0;
+ static #delay100 = 100 + this.extraDelay;
+ static #kuKeycodes = {
+ 1: 3302,
+ 2: 3303,
+ 3: 3304,
+ 4: 3306,
+ 5: 3307,
+ 6: 3308,
+ 7: 3310,
+ 8: 3311,
+ 9: 3312,
+ 0: 3315,
+ ".": 3316,
+ };
+ static #codesPayload = [];
+
+ static #addKeyboardCode(character) {
+ const characterCode = this.#kuKeycodes[character.toLowerCase()];
+ if (characterCode !== undefined)
+ this.#codesPayload.push({
+ device: 23,
+ code: characterCode,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ static createButtonCommands(waypoints) {
+ this.#codesPayload = [];
+ this.#codesPayload.push(
+ //nav mode
+// {
+// device: 12,
+// code: 3282,
+// delay: this.#delay100,
+// activate: 1,
+// addDepress: "true",
+// },
+ //left MFD menu
+ {
+ device: 26,
+ code: 3217,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ //EHSD
+ {
+ device: 26,
+ code: 3201,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ //DATA
+ {
+ device: 26,
+ code: 3201,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ );
+
+ if (this.slotVariant === "AV8BNA_TRGPT"){
+ this.#codesPayload.push(
+ {
+ device: 23,
+ //UFC 1, select waypoint 1, always exists
+ code: 3302,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ {
+ device: 23,
+ //UFC enter
+ code: 3314,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ {
+ //ODU option 1, cycle to waypoint offset
+ device: 24,
+ code: 3250,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ {
+ //ODU option 1, cycle to target point
+ device: 24,
+ code: 3250,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ );
+ }
+
+ for (const waypoint of waypoints) {
+ if (this.slotVariant === "AV8BNA_WPT"){
+ //increment
+ this.#codesPayload.push(
+ {
+ device: 23,
+ code: 3312,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ {
+ device: 23,
+ code: 3312,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ //ENT
+ {
+ device: 23,
+ code: 3314,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ //ODU 2
+ {
+ device: 24,
+ code: 3251,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ );
+ } else { //if AV8BNA_TRGPT, only go up to 10 targetpoints
+ let waypointNumber = waypoints.indexOf(waypoint) + 1;
+ if (waypointNumber === 11){
+ break;
+ }
+ //needed for targetpoint 10 that is split into two digits, 1 and 0
+ for (let i = 0; i < (waypointNumber + "").length; i++) {
+ // eslint-disable-next-line default-case
+ let digit = (waypointNumber + "").charAt(i);
+ this.#codesPayload.push(
+ {
+ device: 23,
+ code: this.#kuKeycodes[digit],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ );
+ }
+ this.#codesPayload.push(
+ //ENT
+ {
+ device: 23,
+ code: 3314,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ //ODU 2
+ {
+ device: 24,
+ code: 3251,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ );
+ }
+ //Type hem
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push({
+ device: 23,
+ code: 3303,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: 23,
+ code: 3311,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ //type lat
+ for (let i = 0, j = false; i < waypoint.lat.length; i++) {
+ //ignore the first dot "." in coordinate 00.00.000
+ if ((waypoint.lat.charAt(i) === ".") && !j) j=!j;
+ else this.#addKeyboardCode(waypoint.lat.charAt(i));
+ }
+ this.#codesPayload.push(
+ //ENT
+ {
+ device: 23,
+ code: 3314,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ );
+ //Type hem
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push({
+ device: 23,
+ code: 3308,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: 23,
+ code: 3306,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ //type long
+ for (let i = 0, j = false; i < waypoint.long.length; i++) {
+ //ignore the first dot "." in coordinate 000.00.000
+ if ((waypoint.long.charAt(i) === ".") && !j) j=!j;
+ else this.#addKeyboardCode(waypoint.long.charAt(i));
+ }
+ this.#codesPayload.push(
+ //ENT
+ {
+ device: 23,
+ code: 3314,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ );
+ // Select elevation
+ this.#codesPayload.push({
+ device: 24,
+ code: 3252,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ //type elevation
+ for (let i = 0; i < waypoint.elev.length; i++) {
+ this.#addKeyboardCode(waypoint.elev.charAt(i));
+ }
+ this.#codesPayload.push(
+ //ENT
+ {
+ device: 23,
+ code: 3314,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ );
+ this.#codesPayload.push(
+ //revert to selection mode
+ {
+ device: 24,
+ code: 3250,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ );
+ }
+ this.#codesPayload.push(
+ //deselect DATA
+ {
+ device: 26,
+ code: 3201,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ );
+ return this.#codesPayload;
+ }
+}
+
+export default av8b;
diff --git a/src/moduleCommands/c130j.js b/src/moduleCommands/c130j.js
new file mode 100644
index 0000000..3a72e4e
--- /dev/null
+++ b/src/moduleCommands/c130j.js
@@ -0,0 +1,384 @@
+class c130j {
+ static slotVariant = ""; // Holds the variant type
+ static #device_id = 25; // Pilot
+ static #device_id2 = 26; // Copilot
+ static extraDelay = 0;
+ static #delay_value = this.extraDelay;
+ static #delay10 = 10 + this.extraDelay;
+ static #delay50 = 50 + this.extraDelay;
+ static #delay100 = 100 + this.extraDelay;
+
+ static #kuKeycodes = {
+ "0": 3030, "1": 3031, "2": 3032, "3": 3033, "4": 3034,
+ "5": 3035, "6": 3036, "7": 3037, "8": 3038, "9": 3039,
+ ".": 3040, "a": 3042, "b": 3043, "c": 3044, "d": 3045,
+ "e": 3046, "f": 3047, "g": 3048, "h": 3049, "i": 3050,
+ "j": 3051, "k": 3052, "l": 3053, "m": 3054, "n": 3055,
+ "o": 3056, "p": 3057, "q": 3058, "r": 3059, "s": 3060,
+ "t": 3061, "u": 3062, "v": 3063, "w": 3064, "x": 3065,
+ "y": 3066, "z": 3067,
+ "/": 3068,
+ "CLR": 3029,
+ "DEL": 3028,
+ "INDX": 3016,
+ "DIR": 3018,
+ "MARK": 3020,
+ "EXEC": 3025,
+ "NEXT": 3026,
+ "PREV": 3027,
+ "L1": 3001, "L2": 3002, "L3": 3003,
+ "L4": 3004, "L5": 3005, "L6": 3006,
+ "R1": 3007, "R2": 3008, "R3": 3009,
+ "R4": 3010, "R5": 3011, "R6": 3012,
+ };
+
+ static #codesPayload = [];
+
+ static #addKeyboardCode(character, device) {
+ const characterCode = this.#kuKeycodes[character.toLowerCase()];
+ if (characterCode !== undefined) {
+ this.#codesPayload.push({
+ device: device,
+ code: characterCode,
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ }
+
+ static createButtonCommands(waypoints) {
+ this.#codesPayload = [];
+
+ const device =
+ this.slotVariant === "c130Plt"
+ ? this.#device_id
+ : this.slotVariant === "c130CoPlt"
+ ? this.#device_id2
+ : null;
+
+ if (!device) return this.#codesPayload;
+
+ // Press INDX key
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["INDX"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Press L6 Zeroise
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["L6"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Press Next Page
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["NEXT"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Press L1 for Route 1
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["L1"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Press R6 for Verify
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["R6"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ /////////////////////////////// Create MARK POINT for Origin
+
+ // Press MARK key
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["MARK"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Press L1 to enter MP into Scratchpad
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["L1"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Press INDX key
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["INDX"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Press R1 for route 1
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["R1"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Press L1 to enter MP into Scratchpad
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["L1"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Press L6 to enter LEGS page
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["L6"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ ///////////////////////////////// Enter Waypoints into LEGS page
+
+ for (let i = 0; i < waypoints.length; i++) {
+ const waypoint = waypoints[i];
+
+ if (i > 0 && i % 5 === 0) {
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["NEXT"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ const lskL = this.#kuKeycodes[`L${(i % 5) + 1}`];
+ const lskR = this.#kuKeycodes[`R${(i % 5) + 1}`];
+
+ // Latitude hemisphere
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes[waypoint.latHem.toLowerCase()],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Latitude digits
+ for (let j = 0; j < waypoint.lat.length; j++) {
+ if (j !== 2) this.#addKeyboardCode(waypoint.lat.charAt(j), device);
+ }
+
+ // Longitude hemisphere
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes[waypoint.longHem.toLowerCase()],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Longitude digits
+ for (let j = 0; j < waypoint.long.length; j++) {
+ if (j !== 3) this.#addKeyboardCode(waypoint.long.charAt(j), device);
+ }
+
+ // Enter waypoint LAT/LON
+ this.#codesPayload.push({
+ device: device,
+ code: lskL,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // "/" for elevation
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["/"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Elevation digits
+ const elev =
+ waypoint.elev && parseInt(waypoint.elev, 10) >= 1
+ ? waypoint.elev
+ : "1";
+
+for (let j = 0; j < elev.length; j++) {
+ this.#addKeyboardCode(elev.charAt(j), device);
+}
+
+ // "/" for elevation
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["a"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Enter elevation
+ this.#codesPayload.push({
+ device: device,
+ code: lskR,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // ACTIVATE ROUTE 1
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["R6"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["R6"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["EXEC"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // DIRECT-TO Waypoint 1
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["DIR"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["L1"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["L6"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["EXEC"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Re-Activate WP trans
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["R6"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["R5"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["R5"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["R5"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["EXEC"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["L6"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // CLEAR CNI MESSAGES
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["CLR"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device: device,
+ code: this.#kuKeycodes["CLR"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ return this.#codesPayload;
+ }
+}
+
+export default c130j;
diff --git a/src/moduleCommands/ch47f.js b/src/moduleCommands/ch47f.js
new file mode 100644
index 0000000..683657c
--- /dev/null
+++ b/src/moduleCommands/ch47f.js
@@ -0,0 +1,582 @@
+class ch47f {
+ static slotVariant = ""; // Holds the variant type
+ static #device_id = 3; // Device ID, private
+ static extraDelay = 0; // Extra delay constant
+ static #delay_value = this.extraDelay; // Total delay value
+ static #delay10 = 10 + this.extraDelay; // 10 ms delay + extra delay
+ static #delay50 = 50 + this.extraDelay; // 50 ms delay + extra delay
+ static #delay100 = this.extraDelay + 100; // 100 ms delay + extra delay
+ static #kuKeycodes = {
+ "1": 3011,
+ "2": 3012,
+ "3": 3013,
+ "4": 3014,
+ "5": 3015,
+ "6": 3016,
+ "7": 3017,
+ "8": 3018,
+ "9": 3019,
+ "0": 3010,
+ "a": 3023,
+ "b": 3024,
+ "c": 3025,
+ "d": 3026,
+ "e": 3027,
+ "f": 3028,
+ "g": 3029,
+ "h": 3030,
+ "i": 3031,
+ "j": 3032,
+ "k": 3033,
+ "l": 3034,
+ "m": 3035,
+ "n": 3036,
+ "o": 3037,
+ "p": 3038,
+ "q": 3039,
+ "r": 3040,
+ "s": 3041,
+ "t": 3042,
+ "u": 3043,
+ "v": 3044,
+ "w": 3045,
+ "x": 3046,
+ "y": 3047,
+ "z": 3048,
+ ".": 3020,
+ "/": 3021,
+ "UP": 3006,
+ "DOWN": 3007,
+ "CLR": 3008,
+ "FPLN": 3071,
+ "IDX": 3064,
+ "DIR": 3073,
+ "L1": 3050,
+ "L2": 3051,
+ "L3": 3052,
+ "L4": 3053,
+ "L5": 3054,
+ "L6": 3055,
+ "R1": 3056,
+ "R2": 3057,
+ "R3": 3058,
+ "R4": 3059,
+ "R5": 3060,
+ "R6": 3061,
+ };
+ static #codesPayload = []; // Holds the generated codes
+
+ // Method to add keyboard codes
+ static #addKeyboardCode(character) {
+ const characterCode = this.#kuKeycodes[character.toLowerCase()];
+ if (characterCode !== undefined) {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: characterCode,
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ }
+
+ // Main method to create button commands based on waypoints
+ static createButtonCommands(waypoints) {
+ this.#codesPayload = []; // Reset the payload
+
+
+ // Add to Existing Flight Plan actions
+
+ if (this.slotVariant === "ch47ADD") {
+
+ // Enter DIR mode
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["DIR"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Press Down arrow a lot to get to end of any current flight plan
+ for (let k = 0; k < 50; k++) {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["DOWN"],
+ delay: this.#delay50,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["DOWN"],
+ delay: this.#delay50,
+ activate: 0,
+ addDepress: "false",
+ });
+ }
+
+ // Clear the Scratchpad
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["CLR"],
+ delay: 1000,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Loop through waypoints
+ for (const waypoint of waypoints) {
+
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["DOWN"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Type hem for latitude
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["n"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["s"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // Type latitude
+ for (let i = 0; i < waypoint.lat.length; i++) {
+ if (i !== 2) {
+ this.#addKeyboardCode(waypoint.lat.charAt(i));
+ }
+ }
+
+ // Type hem for longitude
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["e"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["w"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // Type longitude
+ for (let i = 0; i < waypoint.long.length; i++) {
+ if (i !== 3) {
+ this.#addKeyboardCode(waypoint.long.charAt(i));
+ }
+ }
+
+ // Press LSK L5 to enter Waypoint into Flight Plan
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["L5"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+
+ // New Flight Plan Actions
+
+ } else if (this.slotVariant === "ch47NEW") {
+
+ // Enter DIR mode
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["DIR"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Delete Current Flight Plan
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["L6"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["R1"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Return to DIR mode
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["DIR"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Clear the Scratchpad
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["CLR"],
+ delay: 1000,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Loop through waypoints
+ for (let i = 0; i < waypoints.length; i++) {
+ const waypoint = waypoints[i];
+
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["DOWN"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Type hem for latitude
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["n"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["s"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // Type latitude (skip 3rd char)
+ for (let j = 0; j < waypoint.lat.length; j++) {
+ if (j !== 2) {
+ this.#addKeyboardCode(waypoint.lat.charAt(j));
+ }
+ }
+
+ // Type hem for longitude
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["e"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["w"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // Type longitude (skip 4th char)
+ for (let j = 0; j < waypoint.long.length; j++) {
+ if (j !== 3) {
+ this.#addKeyboardCode(waypoint.long.charAt(j));
+ }
+ }
+
+ // Press LSK L1 for first waypoint, LSK L5 for others
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: i === 0 ? this.#kuKeycodes["L1"] : this.#kuKeycodes["L5"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+}
+
+ // Add to Existing Alternate Flight Plan actions
+
+ if (this.slotVariant === "ch47ALTADD") {
+
+ // Enter ALTN FPLN mode
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["IDX"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Enter ALTN FPLN mode
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["L4"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Press Down arrow a lot to get to end of any current flight plan
+ for (let k = 0; k < 50; k++) {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["DOWN"],
+ delay: this.#delay50,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["DOWN"],
+ delay: this.#delay50,
+ activate: 0,
+ addDepress: "false",
+ });
+ }
+
+ // Clear the Scratchpad
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["CLR"],
+ delay: 1000,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Loop through waypoints
+ for (const waypoint of waypoints) {
+
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["DOWN"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Type hem for latitude
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["n"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["s"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // Type latitude
+ for (let i = 0; i < waypoint.lat.length; i++) {
+ if (i !== 2) {
+ this.#addKeyboardCode(waypoint.lat.charAt(i));
+ }
+ }
+
+ // Type hem for longitude
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["e"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["w"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // Type longitude
+ for (let i = 0; i < waypoint.long.length; i++) {
+ if (i !== 3) {
+ this.#addKeyboardCode(waypoint.long.charAt(i));
+ }
+ }
+
+ // Press LSK L5 to enter Waypoint into Flight Plan
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["L5"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // New Alternate Flight Plan Actions
+
+ } else if (this.slotVariant === "ch47ALTNEW") {
+ // Enter ALTN FPLN mode
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["IDX"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Enter ALTN FPLN mode
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["L4"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Delete Current Flight Plan
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["L6"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["R1"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Clear the Scratchpad
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["CLR"],
+ delay: 1000,
+ activate: 1,
+ addDepress: "true",
+ });
+
+
+ // Loop through waypoints
+ for (let i = 0; i < waypoints.length; i++) {
+ const waypoint = waypoints[i];
+
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["DOWN"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Type hem for latitude
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["n"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["s"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // Type latitude (skip 3rd char)
+ for (let j = 0; j < waypoint.lat.length; j++) {
+ if (j !== 2) {
+ this.#addKeyboardCode(waypoint.lat.charAt(j));
+ }
+ }
+
+ // Type hem for longitude
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["e"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["w"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // Type longitude (skip 4th char)
+ for (let j = 0; j < waypoint.long.length; j++) {
+ if (j !== 3) {
+ this.#addKeyboardCode(waypoint.long.charAt(j));
+ }
+ }
+
+ // Press LSK L2 for first waypoint, LSK L5 for others
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: i === 0 ? this.#kuKeycodes["L2"] : this.#kuKeycodes["L5"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ }
+
+ // Enter FPLN mode
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["FPLN"],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ return this.#codesPayload; // Return the generated payload
+ }
+}
+
+export default ch47f;
diff --git a/src/moduleCommands/f15e.js b/src/moduleCommands/f15e.js
index e3df0cb..972b5b2 100644
--- a/src/moduleCommands/f15e.js
+++ b/src/moduleCommands/f15e.js
@@ -1,334 +1,268 @@
-class f15e {
- static slotVariant = "";
- static extraDelay = 0;
- static delay = 100 + this.extraDelay;
- static #f15eNumberCodes = {
- 0: 3036,
- 1: 3020,
- 2: 3021,
- 3: 3022,
- 4: 3025,
- 5: 3026,
- 6: 3027,
- 7: 3030,
- 8: 3031,
- 9: 3032,
- menu: 3038,
- clear: 3035,
- shift: 3033,
- A: 3020,
- B: 3022,
- };
-
- static createButtonCommands(waypoints) {
- let f15eUFCDevice;
- if (["F-15ESE_pilotA", "F-15ESE_pilotB"].includes(this.slotVariant)) {
- f15eUFCDevice = 56;
- } else {
- f15eUFCDevice = 57;
- }
- let route; // This should reduce length of code by a lot, and make it more readable.
- if (
- this.slotVariant === "F-15ESE_pilotA" ||
- this.slotVariant === "F-15ESE_wsoA"
- ) {
- route = this.#f15eNumberCodes["A"];
- } else {
- route = this.#f15eNumberCodes["B"];
- }
-
- {
- // Doesnt need to be Splice-Cleared because payload is created *each time* the function is called, and isnt static.
- let payload = [
- {
- // Clear UFC button
- device: f15eUFCDevice,
- code: 3035,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- {
- // Clear UFC button
- device: f15eUFCDevice,
- code: 3035,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- {
- // Clear UFC button
- device: f15eUFCDevice,
- code: 3035,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- {
- // Menu UFC button
- device: f15eUFCDevice,
- code: 3038,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- {
- // Press Shift
- device: f15eUFCDevice,
- code: 3033,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- {
- // Press B / 3 (This switches to the universal Base point / INS Align point)
- device: f15eUFCDevice,
- code: this.#f15eNumberCodes["B"],
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- {
- // Enter B into UFC button 10
- device: f15eUFCDevice,
- code: 3010,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- {
- // Press UFC button 10
- device: f15eUFCDevice,
- code: 3010,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- ];
- // =================== end of setup ===================
-
- // =================== Enter Waypoint numbers ===================
- for (const waypoint of waypoints) {
- let waypointNumber = waypoints.indexOf(waypoint) + 1;
- for (let i = 0; i < (waypointNumber + "").length; i++) {
- // eslint-disable-next-line default-case
- let digit = (waypointNumber + "").charAt(i);
- payload.push({
- // Waypoint Digit
- device: f15eUFCDevice,
- code: this.#f15eNumberCodes[digit],
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
- }
- payload.push(
- {
- // press shift
- device: f15eUFCDevice,
- code: this.#f15eNumberCodes["shift"],
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- {
- // route letter
- device: f15eUFCDevice,
- code: route,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- );
-
- // ============================================================
-
- payload.push({
- // Press PB 1
- device: f15eUFCDevice,
- code: 3001,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
-
- payload.push({
- // Press Shift
- device: f15eUFCDevice,
- code: this.#f15eNumberCodes["shift"],
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
-
- if (waypoint.latHem === "N") {
- // North
- payload.push({
- device: f15eUFCDevice,
- code: this.#f15eNumberCodes[2],
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
- } else {
- payload.push({
- // South
- device: f15eUFCDevice,
- code: this.#f15eNumberCodes[8],
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
- }
-
- //Type lat
- for (let i = 0; i < waypoint.lat.length; i++) {
- // enter each digit of lat into scratchpad
- // eslint-disable-next-line default-case
- for (const char of waypoint.lat.charAt(i)) {
- if (char !== ".") {
- payload.push({
- device: f15eUFCDevice,
- code: this.#f15eNumberCodes[char],
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
- }
- }
- }
-
- payload.push({
- // enter latitutde into UFC
- device: f15eUFCDevice,
- code: 3002,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
-
- payload.push({
- // Press Shift
- device: f15eUFCDevice,
- code: 3033,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
-
- if (waypoint.longHem === "E") {
- // Type hemisphere into scratchpad
- payload.push({
- device: f15eUFCDevice,
- code: this.#f15eNumberCodes[6],
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
- } else {
- payload.push({
- device: f15eUFCDevice,
- code: this.#f15eNumberCodes[4],
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
- }
-
- for (let i = 0; i < waypoint.long.length; i++) {
- // enter each digit of longtitude into scratchpad
- // eslint-disable-next-line default-case
- for (const char of waypoint.long.charAt(i)) {
- if (char !== ".") {
- payload.push({
- device: f15eUFCDevice,
- code: this.#f15eNumberCodes[char],
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
- }
- }
- }
-
- payload.push({
- // enter longtitude into UFC
- device: f15eUFCDevice,
- code: 3003,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
-
- for (let i = 0; i < waypoint.elev.length; i++) {
- // enter each digit of elevation into scratchpad
- // eslint-disable-next-line default-case
- for (const char of waypoint.elev.charAt(i)) {
- payload.push({
- device: f15eUFCDevice,
- code: this.#f15eNumberCodes[char],
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
- }
- }
-
- payload.push({
- // enter elevation into UFC
- device: f15eUFCDevice,
- code: 3007,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- });
- }
- payload.push(
- {
- // Menu UFC button
- device: f15eUFCDevice,
- code: 3038,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- {
- // Enter 1 button (This re-selects the first waypoint)
- device: f15eUFCDevice,
- code: this.#f15eNumberCodes[1],
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- {
- // press shift
- device: f15eUFCDevice,
- code: 3033,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- {
- // enter route letter
- device: f15eUFCDevice,
- code: route,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- {
- // Waypoint UFC button
- device: f15eUFCDevice,
- code: 3010,
- delay: this.delay,
- activate: 1,
- addDepress: "true",
- },
- );
-
- return payload;
- }
- }
-}
-
-export default f15e;
+class f15e {
+ static slotVariant = "";
+ static delay = 50;
+ static #f15eNumberCodes = {
+ 0: 3036,
+ 1: 3020,
+ 2: 3021,
+ 3: 3022,
+ 4: 3025,
+ 5: 3026,
+ 6: 3027,
+ 7: 3030,
+ 8: 3031,
+ 9: 3032,
+ L1: 3001,
+ L2: 3002,
+ L3: 3003,
+ R1: 3010,
+ R4: 3007,
+ menu: 3038,
+ clear: 3035,
+ shift: 3033,
+ A: 3020,
+ B: 3022,
+ dot: 3029,
+ PB1: 3061,
+ PB2: 3062,
+ AG: 3127
+ };
+
+ static #codesPayload = [];
+
+ static createButtonCommands(waypoints) {
+ // Clear previous payload
+ this.#codesPayload = [];
+
+ /*/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+============================================================== F-15ESE SLOT LOGIC =========================================================
+////////////////////////////////////////////////////////*//////////////////////////////////////////////////////////////////////////////////
+
+// Determine seat once
+const isPilot = this.slotVariant.includes("_pilot_");
+
+const f15eUFCDevice = isPilot ? 56 : 57;
+const f15eMPDDevice = isPilot ? 36 : 39;
+const f15eACCDevice = isPilot ? 32 : null; // WSO may not use ACC
+
+
+
+/*///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+===================================================================== WAYPOINTS =============================================================
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
+
+if (this.slotVariant.includes("_waypoints")) {
+
+ // Clear the scratchpad and menu ///////////////////
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.clear, delay: this.delay, activate: 1, addDepress: "true"});
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.clear, delay: this.delay, activate: 1, addDepress: "true"});
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.clear, delay: this.delay, activate: 1, addDepress: "true"});
+
+ // Enter MENU page ///////////////////////////////
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.menu, delay: this.delay, activate: 1, addDepress: "true"});
+
+ // Enter B into Scratchpad ///////////////////////
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.shift, delay: this.delay, activate: 1, addDepress: "true"});
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.B, delay: this.delay, activate: 1, addDepress: "true"});
+
+ //Enter B into UFC PB10 to enter BASE mode////////////////////////////
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.R1, delay: this.delay, activate: 1, addDepress: "true"});
+
+ //UFC PB10 For Waypoint menu //////////////////////////
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.R1, delay: this.delay, activate: 1, addDepress: "true"});
+
+ // Waypoint loop //////////////////////////////////////
+ for (let i = 0; i < waypoints.length; i++) {
+ const wp = waypoints[i];
+ const wpNum = i + 1;
+
+ // Enter WAYPOINT IDENT Number into scratchpad////////////////////////////
+ for (const digit of ("" + wpNum)) {
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[digit], delay: this.delay, activate: 1, addDepress: "true" });}
+
+ // Enter A into scratchpad /////////////////////////////////////////
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.shift, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.A, delay: this.delay, activate: 1, addDepress: "true" });
+
+ // PB1 press to set Waypoint menu to currently transferring waypoint ////////////////////////////////////////
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.L1, delay: this.delay, activate: 1, addDepress: "true" });
+
+
+
+ // LAT N or S /////////////////////////////////////
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.shift, delay: this.delay, activate: 1, addDepress: "true" }); //Press Shift
+ if (wp.latHem === "N")
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[2], delay: this.delay, activate: 1, addDepress: "true" }); //Enters N
+ else this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[8], delay: this.delay, activate: 1, addDepress: "true" }); //Enters S
+
+ //Enter LAT Coordinate DIGITS //////////////////////////////////
+ for (const char of wp.lat) if (char !== ".") this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[char], delay: this.delay, activate: 1, addDepress: "true" }); //Types all LAT coordinates
+
+ //Press LSK L2 to enter Latitude into UFC
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.L2, delay: this.delay, activate: 1, addDepress: "true" });
+
+ // LON E or W /////////////////////////////////////////
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.shift, delay: this.delay, activate: 1, addDepress: "true" }); // Press Shift
+ if (wp.longHem === "E") this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[6], delay: this.delay, activate: 1, addDepress: "true" }); //Enters E
+ else this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[4], delay: this.delay, activate: 1, addDepress: "true" }); //Enters W
+
+ //Enter LAT DIGITS ////////////////////////////////////
+ for (const char of wp.long) if (char !== ".") this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[char], delay: this.delay, activate: 1, addDepress: "true" }); //Types all LON coordinates
+
+ //Press LSK L3 to enter Longitude into UFC
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.L3, delay: this.delay, activate: 1, addDepress: "true" });
+
+ //Enter ELEV DIGITS ////////////////////////////////////
+ for (const char of wp.elev) this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[char], delay: this.delay, activate: 1, addDepress: "true" });
+
+ //Press LSK R4 to enter Elevation into UFC
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.R4, delay: this.delay, activate: 1, addDepress: "true" });}
+ //^This is the end of waypoint entry loop
+
+
+ // BACK TO AND SELECT WP 1
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.menu, delay: this.delay, activate: 1, addDepress: "true" }); // Press UFC Menu button
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[1], delay: this.delay, activate: 1, addDepress: "true" }); // Press 1
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.shift, delay: this.delay, activate: 1, addDepress: "true" }); // Press Shift
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.A, delay: this.delay, activate: 1, addDepress: "true" }); // Press A
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.R1, delay: this.delay, activate: 1, addDepress: "true" }); // Press UFC LSK R1
+
+ }
+
+
+
+
+ /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ ============================================================== TARGET POINTS ===================================================================
+ ////////////////////////////////////////////////////////*///////////////////////////////////////////////////////////////////////////////////////
+
+ if (this.slotVariant.includes("_targetpoints") || this.slotVariant.includes("_txfr")) {
+
+ // Clear and Menu
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.clear, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.clear, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.clear, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.menu, delay: this.delay, activate: 1, addDepress: "true" });
+
+ // A/G Mode
+ this.#codesPayload.push({ device: f15eACCDevice, code: this.#f15eNumberCodes["AG"], delay: this.delay, activate: 1, addDepress: "true", });
+
+ // Basepoint B
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.shift, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.B, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.R1, delay: this.delay, activate: 1, addDepress: "true" });
+
+ // Waypoint Page
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.R1, delay: this.delay, activate: 1, addDepress: "true" });
+
+ // Waypoint IDENT
+ for (let i = 0; i < waypoints.length; i++) {
+ const wp = waypoints[i];
+ const wpNum = i + 1;
+
+ for (const digit of ("" + wpNum)) this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[digit], delay: this.delay, activate: 1, addDepress: "true" });
+
+ // dot
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.dot, delay: this.delay, activate: 1, addDepress: "true" });
+
+ // Route B
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.shift, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.B, delay: this.delay, activate: 1, addDepress: "true" });
+
+ // PB1 press
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.L1, delay: this.delay, activate: 1, addDepress: "true" });
+
+ // LAT loop
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.shift, delay: this.delay, activate: 1, addDepress: "true" });
+ if (wp.latHem === "N") this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[2], delay: this.delay, activate: 1, addDepress: "true" });
+ else this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[8], delay: this.delay, activate: 1, addDepress: "true" });
+
+ for (const char of wp.lat) if (char !== ".") this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[char], delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: 3002, delay: this.delay, activate: 1, addDepress: "true" });
+
+
+ // LON loop
+ this.#codesPayload.push({ device: f15eUFCDevice, code: 3033, delay: this.delay, activate: 1, addDepress: "true" });
+ if (wp.longHem === "E") this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[6], delay: this.delay, activate: 1, addDepress: "true" });
+ else this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[4], delay: this.delay, activate: 1, addDepress: "true" });
+
+ for (const char of wp.long) if (char !== ".") this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[char], delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: 3003, delay: this.delay, activate: 1, addDepress: "true" });
+
+ //ELEV loop
+ for (const char of wp.elev) this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[char], delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: 3007, delay: this.delay, activate: 1, addDepress: "true" });
+ }
+ // BACK TO AND SELECT TP 1
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.menu, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[1], delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.dot, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.shift, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.B, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.R1, delay: this.delay, activate: 1, addDepress: "true" });
+ }
+
+
+
+
+
+/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+========================================================== TRANSFER TO WEAPONS =====================================================================
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
+if (this.slotVariant.includes("_txfr")) {
+
+
+
+ const numWaypoints = waypoints.length;
+
+
+ //This block sets a specific number of weapon station jumps
+ function getPB2PressCount(numWaypoints, waypointNumber) {
+ const lookup = {
+ 7: [3, 6, 6, 3, 2, 6, 2], // index 0 → WP1
+ 8: [3, 7, 3, 4, 3, 3, 7, 2], // index 0 → WP1
+ 9: [4, 8, 8, 4, 4, 4, 3, 8, 2] // index 0 → WP1
+ };
+ if (lookup[numWaypoints]) {
+ return lookup[numWaypoints][waypointNumber - 1] ?? 1;
+ }
+ return 1;} // End of block
+
+ // Sets A/G mode, required to transfer Target-Point to Weapon.
+ this.#codesPayload.push({ device: f15eACCDevice, code: this.#f15eNumberCodes.AG, delay: this.delay, activate: 1, addDepress: "true" }); // Enters A/G mode
+
+ for (let i = 0; i < numWaypoints; i++) {
+
+ const wpNum = i + 1;
+
+ // IDENT only (e.g. 1.B)
+ for (const digit of String(wpNum)) { this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[digit], delay: this.delay, activate: 1, addDepress: "true", });} //Enters number
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.dot, delay: this.delay, activate: 1, addDepress: "true" }); // Enters dot
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.shift, delay: this.delay, activate: 1, addDepress: "true" }); //Presses Shift
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.B, delay: this.delay, activate: 1, addDepress: "true" }); // Makes a B
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.R1, delay: this.delay, activate: 1, addDepress: "true" }); // Enter it to LSK R1
+
+ // MPD PB1 — transfer to bomb - has a delay to allow for coordinate transfer
+ this.#codesPayload.push({ device: f15eMPDDevice, code: this.#f15eNumberCodes.PB1, delay: 2000, activate: 1, addDepress: "true", });
+
+ // PB2 — This does the actual pushing of MPD PB2
+ const presses =
+ (numWaypoints === 7 ||numWaypoints === 8 || numWaypoints === 9)
+ ? getPB2PressCount(numWaypoints, wpNum)
+ : 1;
+ for (let j = 0; j < presses; j++) {
+ this.#codesPayload.push({ device: f15eMPDDevice, code: this.#f15eNumberCodes.PB2, delay: this.delay, activate: 1, addDepress: "true",}); }
+ }
+
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes[1], delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.dot, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.shift, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.B, delay: this.delay, activate: 1, addDepress: "true" });
+ this.#codesPayload.push({ device: f15eUFCDevice, code: this.#f15eNumberCodes.R1, delay: this.delay, activate: 1, addDepress: "true" });
+}
+
+ return this.#codesPayload;
+ }
+}
+
+export default f15e;
diff --git a/src/moduleCommands/f16.js b/src/moduleCommands/f16.js
index af1755a..860aa34 100644
--- a/src/moduleCommands/f16.js
+++ b/src/moduleCommands/f16.js
@@ -1,99 +1,100 @@
-import createButtonPress from "../models/ButtonPress";
-
-class f16 {
- static extraDelay = 0;
- static #delay150 = 150 + this.extraDelay;
- static #delay10 = 10 + this.extraDelay;
- static #kuKeycodes = {
- 1: 3003,
- 2: 3004,
- 3: 3005,
- 4: 3006,
- 5: 3007,
- 6: 3008,
- 7: 3009,
- 8: 3010,
- 9: 3011,
- 0: 3002,
- };
- static #codesPayload = [];
-
- static #addKeyboardCode(character) {
- const characterCode = this.#kuKeycodes[character.toLowerCase()];
- if (characterCode !== undefined)
- this.#codesPayload.push(
- createButtonPress(17, characterCode, this.#delay10),
- );
- }
-
- static createButtonCommands(waypoints) {
- this.#codesPayload = [];
- this.#codesPayload.push(
- createButtonPress(17, 3032, this.#delay150, -1),
- createButtonPress(17, 3006, this.#delay150),
- );
- for (const waypoint of waypoints) {
- this.#codesPayload.push(
- createButtonPress(17, 3030, this.#delay150),
- createButtonPress(17, 3035, this.#delay150, -1),
- createButtonPress(17, 3035, this.#delay150, -1),
- );
- //Type hem
- if (waypoint.latHem === "N") {
- this.#codesPayload.push(createButtonPress(17, 3004, this.#delay10));
- } else {
- this.#codesPayload.push(createButtonPress(17, 3010, this.#delay10));
- }
-
- for (let i = 0; i < waypoint.lat.length; i++) {
- // Type Latitude into UFC
- this.#addKeyboardCode(waypoint.lat.charAt(i));
- }
-
- //enter
- this.#codesPayload.push(createButtonPress(17, 3016, this.#delay10));
- //dobber to long
- this.#codesPayload.push(createButtonPress(17, 3035, this.#delay150, -1));
- //Type hem
- if (waypoint.longHem === "E") {
- this.#codesPayload.push(createButtonPress(17, 3008, this.#delay10));
- } else {
- this.#codesPayload.push(createButtonPress(17, 3006, this.#delay10));
- }
-
- //type long
- for (let i = 0; i < waypoint.long.length; i++) {
- // Type Longtitude into UFC
- this.#addKeyboardCode(waypoint.long.charAt(i));
- }
-
- //enter
- this.#codesPayload.push(createButtonPress(17, 3016, this.#delay10));
- //dobber to elev
- this.#codesPayload.push(createButtonPress(17, 3035, this.#delay150, -1));
-
- //type elev
- for (let i = 0; i < waypoint.elev.length; i++) {
- // Type Elevation into UFC
- this.#addKeyboardCode(waypoint.elev.charAt(i));
- }
-
- //enter
- this.#codesPayload.push(createButtonPress(17, 3016, this.#delay10));
- //back to steerpoint field
- this.#codesPayload.push(
- createButtonPress(17, 3034, this.#delay150),
- createButtonPress(17, 3034, this.#delay150),
- createButtonPress(17, 3034, this.#delay150),
- createButtonPress(17, 3034, this.#delay150),
- );
- }
-
- //main page
- this.#codesPayload.push(createButtonPress(17, 3032, this.#delay150, -1));
-
- return this.#codesPayload;
- }
-}
-
-export default f16;
+import createButtonPress from "../models/ButtonPress";
+
+class f16 {
+ static extraDelay = 0;
+ static #delay150 = 150 + this.extraDelay;
+ static #delay10 = 10 + this.extraDelay;
+ static #kuKeycodes = {
+ 1: 3003,
+ 2: 3004,
+ 3: 3005,
+ 4: 3006,
+ 5: 3007,
+ 6: 3008,
+ 7: 3009,
+ 8: 3010,
+ 9: 3011,
+ 0: 3002,
+ };
+ static #codesPayload = [];
+
+ static #addKeyboardCode(character) {
+ const characterCode = this.#kuKeycodes[character.toLowerCase()];
+ if (characterCode !== undefined)
+ this.#codesPayload.push(
+ createButtonPress(17, characterCode, this.#delay10),
+ );
+ }
+
+ static createButtonCommands(waypoints) {
+ this.#codesPayload = [];
+ this.#codesPayload.push(
+ createButtonPress(17, 3032, this.#delay150, -1),
+ createButtonPress(17, 3006, this.#delay150),
+ );
+ for (const waypoint of waypoints) {
+ this.#codesPayload.push(
+ createButtonPress(17, 3030, this.#delay150),
+ createButtonPress(17, 3034, this.#delay150, 1),
+ createButtonPress(17, 3034, this.#delay150, 1),
+ createButtonPress(17, 3034, this.#delay150, 1),
+ createButtonPress(17, 3034, this.#delay150, 1),
+ );
+ //Type hem
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push(createButtonPress(17, 3004, this.#delay10));
+ } else {
+ this.#codesPayload.push(createButtonPress(17, 3010, this.#delay10));
+ }
+
+ for (let i = 0; i < waypoint.lat.length; i++) {
+ // Type Latitude into UFC
+ this.#addKeyboardCode(waypoint.lat.charAt(i));
+ }
+
+ //enter
+ this.#codesPayload.push(createButtonPress(17, 3016, this.#delay10));
+ //dobber to long
+ this.#codesPayload.push(createButtonPress(17, 3035, this.#delay150, -1));
+ //Type hem
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push(createButtonPress(17, 3008, this.#delay10));
+ } else {
+ this.#codesPayload.push(createButtonPress(17, 3006, this.#delay10));
+ }
+
+ //type long
+ for (let i = 0; i < waypoint.long.length; i++) {
+ // Type Longitude into UFC
+ this.#addKeyboardCode(waypoint.long.charAt(i));
+ }
+
+ //enter
+ this.#codesPayload.push(createButtonPress(17, 3016, this.#delay10));
+ //dobber to elev
+ this.#codesPayload.push(createButtonPress(17, 3035, this.#delay150, -1));
+
+ //type elev
+ for (let i = 0; i < waypoint.elev.length; i++) {
+ // Type Elevation into UFC
+ this.#addKeyboardCode(waypoint.elev.charAt(i));
+ }
+ //enter
+ this.#codesPayload.push(createButtonPress(17, 3016, this.#delay10));
+
+
+ //back to steerpoint field
+ this.#codesPayload.push(
+ createButtonPress(17, 3035, this.#delay150, -1),
+ createButtonPress(17, 3035, this.#delay150, -1),
+ );
+ }
+
+ //main page
+ this.#codesPayload.push(createButtonPress(17, 3032, this.#delay150, -1));
+
+ return this.#codesPayload;
+ }
+}
+
+export default f16;
diff --git a/src/moduleCommands/fa18.js b/src/moduleCommands/fa18.js
index f24c09b..595e81d 100644
--- a/src/moduleCommands/fa18.js
+++ b/src/moduleCommands/fa18.js
@@ -1,218 +1,617 @@
-class fa18 {
- static extraDelay = 0;
- static #delay100 = this.extraDelay + 100;
- static #delay200 = this.extraDelay + 200;
- static #delay500 = this.extraDelay + 500;
- static #delay800 = this.extraDelay + 800;
- static #delay1000 = this.extraDelay + 1000;
- static #kuKeycodes = {
- 1: 3019,
- 2: 3020,
- 3: 3021,
- 4: 3022,
- 5: 3023,
- 6: 3024,
- 7: 3025,
- 8: 3026,
- 9: 3027,
- 0: 3018,
- };
- static #codesPayload = [];
-
- static #addKeyboardCode(character) {
- const characterCode = this.#kuKeycodes[character.toLowerCase()];
- if (characterCode !== undefined)
- this.#codesPayload.push({
- device: 25,
- code: characterCode,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
-
- static createButtonCommands(waypoints) {
- this.#codesPayload = [];
- //enter the SUPT menu
- this.#codesPayload.push({
- device: 37,
- code: 3028,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- this.#codesPayload.push({
- device: 37,
- code: 3028,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //select HSD
- this.#codesPayload.push({
- device: 37,
- code: 3012,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //select DATA
- this.#codesPayload.push({
- device: 37,
- code: 3020,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
-
- for (const waypoint of waypoints) {
- //increment waypoint
- this.#codesPayload.push({
- device: 37,
- code: 3022,
- delay: this.#delay500,
- activate: 1,
- addDepress: "true",
- });
- //press UFC
- this.#codesPayload.push({
- device: 37,
- code: 3015,
- delay: this.#delay800,
- activate: 1,
- addDepress: "true",
- });
- //press position 1
- this.#codesPayload.push({
- device: 25,
- code: 3010,
- delay: this.#delay200,
- activate: 1,
- addDepress: "true",
- });
- //Type hem
- if (waypoint.latHem === "N") {
- this.#codesPayload.push({
- device: 25,
- code: 3020,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: 25,
- code: 3026,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- //enter first lat digits
- const firstLat = waypoint.lat.substring(0, waypoint.lat.length - 5);
- const last4Lat = waypoint.lat.substring(waypoint.lat.length - 5);
- //Type lat
- for (let i = 0; i < firstLat.length; i++) {
- this.#addKeyboardCode(firstLat.charAt(i));
- }
- //press enter
- this.#codesPayload.push({
- device: 25,
- code: 3029,
- delay: this.#delay1000,
- activate: 1,
- addDepress: "true",
- });
- //enter last 4 digits
- for (let i = 0; i < last4Lat.length; i++) {
- this.#addKeyboardCode(last4Lat.charAt(i));
- }
- //press enter
- this.#codesPayload.push({
- device: 25,
- code: 3029,
- delay: this.#delay1000,
- activate: 1,
- addDepress: "true",
- });
- //Type hem
- if (waypoint.longHem === "E") {
- this.#codesPayload.push({
- device: 25,
- code: 3024,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: 25,
- code: 3022,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- //enter first long digits
-
- const firstLong = waypoint.long.substring(0, waypoint.long.length - 4);
- const last4Long = waypoint.long.substring(waypoint.long.length - 4);
- //Type long
- for (let i = 0; i < firstLong.length; i++) {
- this.#addKeyboardCode(firstLong.charAt(i));
- }
- //press enter
- this.#codesPayload.push({
- device: 25,
- code: 3029,
- delay: this.#delay1000,
- activate: 1,
- addDepress: "true",
- });
- //enter last 4 digits
- for (let i = 0; i < last4Long.length; i++) {
- this.#addKeyboardCode(last4Long.charAt(i));
- }
- //press enter
- this.#codesPayload.push({
- device: 25,
- code: 3029,
- delay: this.#delay1000,
- activate: 1,
- addDepress: "true",
- });
- //press position 3 to select elevation
- this.#codesPayload.push({
- device: 25,
- code: 3012,
- delay: this.#delay200,
- activate: 1,
- addDepress: "true",
- });
- //press position 1 to select feet
- this.#codesPayload.push({
- device: 25,
- code: 3010,
- delay: this.#delay200,
- activate: 1,
- addDepress: "true",
- });
- //Type elev
- for (let i = 0; i < waypoint.elev.length; i++) {
- this.#addKeyboardCode(waypoint.elev.charAt(i));
- }
- //press enter
- this.#codesPayload.push({
- device: 25,
- code: 3029,
- delay: this.#delay500,
- activate: 1,
- addDepress: "true",
- });
- }
-
- return this.#codesPayload;
- }
-}
-
-export default fa18;
+import { AlertDialog } from "../components/AlertDialog";
+class fa18 {
+ static slotVariant = "";
+ static stations = 4;
+ static extraDelay = 0;
+ static #delay100 = this.extraDelay + 100;
+ static #delay200 = this.extraDelay + 200;
+ static #delay500 = this.extraDelay + 500;
+ static #delay800 = this.extraDelay + 800;
+ static #delay1000 = this.extraDelay + 1000;
+ static device_UFC = 25;
+ static device_AMPCD = 37;
+ static device_MDILeft = 35;
+ static #kuKeycodes = {
+ 1: 3019,
+ 2: 3020,
+ 3: 3021,
+ 4: 3022,
+ 5: 3023,
+ 6: 3024,
+ 7: 3025,
+ 8: 3026,
+ 9: 3027,
+ 0: 3018,
+ PB1: 3011,
+ PB2: 3012,
+ PB3: 3013,
+ PB4: 3014,
+ PB5: 3015,
+ PB6: 3016,
+ PB7: 3017,
+ PB8: 3018,
+ PB9: 3019,
+ PB10: 3020,
+ PB11: 3021,
+ PB12: 3022,
+ PB13: 3023,
+ PB14: 3024,
+ PB15: 3025,
+ PB16: 3026,
+ PB17: 3027,
+ PB18: 3028,
+ PB19: 3029,
+ PB20: 3030,
+ UFCOpt1: 3010,
+ UFCOpt2: 3011,
+ UFCOpt3: 3012,
+ UFCOpt4: 3013,
+ UFCOpt5: 3014,
+ UFCEnter: 3029,
+ UFCNorth: 3020,
+ UFCSouth: 3026,
+ UFCEast: 3024,
+ UFCWest: 3022,
+ };
+ static #codesPayload = [];
+
+ static #addKeyboardCode(character, wait) {
+ const characterCode = this.#kuKeycodes[character.toLowerCase()];
+ if (characterCode !== undefined)
+ if (wait === true) {
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: characterCode,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: characterCode,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ }
+
+ static generatePPMSNCommands(wpt, index) {
+
+ // Press the Step button
+ this.#codesPayload.push({
+ device: this.device_MDILeft,
+ code: this.#kuKeycodes["PB13"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device: this.device_MDILeft,
+ code: this.#kuKeycodes["PB13"],
+ delay: this.#delay500,
+ activate: 0,
+ addDepress: "false",
+ });
+
+
+ //select TGT UFC
+ this.#codesPayload.push({
+ device: this.device_MDILeft,
+ code: this.#kuKeycodes["PB14"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device: this.device_MDILeft,
+ code: this.#kuKeycodes["PB14"],
+ delay: this.#delay500,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ //select UFC POSN
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCOpt3"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCOpt3"],
+ delay: this.#delay500,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ //=============== Input Latitude ===============
+ //select UFC LAT
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCOpt1"],
+ delay: this.#delay200,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ //Type hem
+ if (wpt.latHem === "N") {
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCNorth"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCSouth"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ //enter first lat digits
+ const firstLat = wpt.lat.substring(0, wpt.lat.length - 5);
+ const format_last4lat = (
+ (Number(wpt.lat.substring(wpt.lat.length - 4)) * 60) /
+ 10000.0
+ )
+ .toFixed(2)
+ .toString();
+
+ let add_to_firstLat = format_last4lat.substring(
+ 0,
+ format_last4lat.length - 3,
+ );
+
+ // only one number left, add a '0' before it
+ if (add_to_firstLat.length < 2) {
+ add_to_firstLat = "0" + add_to_firstLat;
+ }
+
+ const first_lat_str = firstLat + add_to_firstLat;
+ const second_lat_str = format_last4lat.substring(
+ format_last4lat.length - 2,
+ );
+ //Type lat
+ for (let i = 0; i < first_lat_str.length; i++) {
+ this.#addKeyboardCode(first_lat_str.charAt(i), true);
+ }
+ //press enter
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCEnter"],
+ delay: this.#delay500,
+ activate: 1,
+ addDepress: "true",
+ });
+ //enter last 2 digits
+ for (let i = 0; i < second_lat_str.length; i++) {
+ this.#addKeyboardCode(second_lat_str.charAt(i), true);
+ }
+ //press enter
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCEnter"],
+ delay: this.#delay500,
+ activate: 1,
+ addDepress: "true",
+ });
+ //=============== end of Input Latitude ===============
+
+ //=============== Input Longitude ===============
+ //select UFC LON
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCOpt3"],
+ delay: this.#delay200,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ //Type hem
+ if (wpt.longHem === "E") {
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCEast"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCWest"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ //enter first long digits
+
+ const firstLong = wpt.long.substring(0, wpt.long.length - 4);
+ const format_last4long = (
+ (Number(wpt.long.substring(wpt.long.length - 4)) * 60) /
+ 10000.0
+ )
+ .toFixed(2)
+ .toString();
+
+ let add_to_firstLong = format_last4long.substring(
+ 0,
+ format_last4long.length - 3,
+ );
+
+ // only one number left, add a '0' before it
+ if (add_to_firstLong.length < 2) {
+ add_to_firstLong = "0" + add_to_firstLong;
+ }
+
+ let first_long_str = firstLong + add_to_firstLong;
+ const second_long_str = format_last4long.substring(
+ format_last4long.length - 2,
+ );
+
+ // remove starting '0' in longitude, if it is 1xx°, then everything's fine
+ if (first_long_str.charAt(0) === "0") {
+ first_long_str = first_long_str.substring(1);
+ }
+
+ //Type long
+ for (let i = 0; i < first_long_str.length; i++) {
+ this.#addKeyboardCode(first_long_str.charAt(i), true);
+ }
+ //press enter
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCEnter"],
+ delay: this.#delay500,
+ activate: 1,
+ addDepress: "true",
+ });
+ //enter last 4 digits
+ for (let i = 0; i < second_long_str.length; i++) {
+ this.#addKeyboardCode(second_long_str.charAt(i), true);
+ }
+ //press enter
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCEnter"],
+ delay: this.#delay500,
+ activate: 1,
+ addDepress: "true",
+ });
+ //=============== end of Input Longitude ===============
+
+ //select TGT UFC
+ this.#codesPayload.push({
+ device: this.device_MDILeft,
+ code: this.#kuKeycodes["PB14"],
+ delay: this.#delay800,
+ activate: 1,
+ addDepress: "true",
+ });
+ //select TGT UFC
+ this.#codesPayload.push({
+ device: this.device_MDILeft,
+ code: this.#kuKeycodes["PB14"],
+ delay: this.#delay800,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ //=============== Input Elevation ===============
+ //select UFC ELEV
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCOpt4"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCOpt4"],
+ delay: this.#delay800,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ //press position 3 to select feet
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCOpt3"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCOpt3"],
+ delay: this.#delay800,
+ activate: 0,
+ addDepress: "false",
+ });
+
+ //Type Elevation
+ for (let i = 0; i < wpt.elev.length; i++) {
+ this.#addKeyboardCode(wpt.elev.charAt(i), true);
+ }
+ //press Enter
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCEnter"],
+ delay: this.#delay500,
+ activate: 1,
+ addDepress: "true",
+ });
+ //=============== end of Input Elevation ===============
+
+ //select TGT UFC
+ this.#codesPayload.push({
+ device: this.device_MDILeft,
+ code: this.#kuKeycodes["PB14"],
+ delay: this.#delay800,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ static createPPInputCommands(waypoints) {
+ let inputNum = this.stations * 5;
+
+ let ppmsn = 0;
+ let sta = 0;
+ let count = 0;
+ for (const wpt of waypoints) {
+ if (sta === this.stations) {
+ ppmsn += 1;
+ sta = 0;
+ //select next PP MSN
+ if (ppmsn < 5) {
+ this.#codesPayload.push({
+ device: this.device_MDILeft,
+ code: this.#kuKeycodes["PB6"] + ppmsn,
+ delay: this.#delay500,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ for (let i = 0; i < this.stations - 1; i++) {
+ //select STEP
+ this.#codesPayload.push({
+ device: this.device_MDILeft,
+ code: this.#kuKeycodes["PB13"],
+ delay: this.#delay500,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ //select next PP MSN
+ this.#codesPayload.push({
+ device: this.device_MDILeft,
+ code: this.#kuKeycodes["PB6"] + ppmsn,
+ delay: this.#delay500,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ }
+ }
+ this.generatePPMSNCommands(wpt);
+ count++;
+ sta += 1;
+ if (count === inputNum) {
+ break;
+ }
+ }
+ }
+
+ static createButtonCommands(waypoints) {
+ this.#codesPayload = [];
+ if (
+ this.slotVariant === "FA-18C_hornetPP1" ||
+ this.slotVariant === "FA-18C_hornetPP2" ||
+ this.slotVariant === "FA-18C_hornetPP3" ||
+ this.slotVariant === "FA-18C_hornetPP4"
+ ) {
+ if (this.slotVariant === "FA-18C_hornetPP1") {
+ this.stations = 1;
+ } else if (this.slotVariant === "FA-18C_hornetPP2") {
+ this.stations = 2;
+ } else if (this.slotVariant === "FA-18C_hornetPP3") {
+ this.stations = 3;
+ } else {
+ this.stations = 4;
+ }
+ this.createPPInputCommands(waypoints);
+ } else {
+ //enter the SUPT menu
+ this.#codesPayload.push({
+ device: this.device_AMPCD,
+ code: this.#kuKeycodes["PB18"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ this.#codesPayload.push({
+ device: this.device_AMPCD,
+ code: this.#kuKeycodes["PB18"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ //select HSD
+ this.#codesPayload.push({
+ device: this.device_AMPCD,
+ code: this.#kuKeycodes["PB2"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ //select DATA
+ this.#codesPayload.push({
+ device: this.device_AMPCD,
+ code: this.#kuKeycodes["PB10"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ for (const waypoint of waypoints) {
+ //increment waypoint
+ this.#codesPayload.push({
+ device: this.device_AMPCD,
+ code: this.#kuKeycodes["PB12"],
+ delay: this.#delay500,
+ activate: 1,
+ addDepress: "true",
+ });
+ //press UFC
+ this.#codesPayload.push({
+ device: this.device_AMPCD,
+ code: this.#kuKeycodes["PB5"],
+ delay: this.#delay800,
+ activate: 1,
+ addDepress: "true",
+ });
+ //press position 1
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCOpt1"],
+ delay: this.#delay200,
+ activate: 1,
+ addDepress: "true",
+ });
+ //Type hem
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCNorth"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCSouth"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ //enter first lat digits
+ const firstLat = waypoint.lat.substring(0, waypoint.lat.length - 5);
+ const last4Lat = waypoint.lat.substring(waypoint.lat.length - 5);
+ //Type lat
+ for (let i = 0; i < firstLat.length; i++) {
+ this.#addKeyboardCode(firstLat.charAt(i), false);
+ }
+ //press enter
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCEnter"],
+ delay: this.#delay1000,
+ activate: 1,
+ addDepress: "true",
+ });
+ //enter last 4 digits
+ for (let i = 0; i < last4Lat.length; i++) {
+ this.#addKeyboardCode(last4Lat.charAt(i), false);
+ }
+ //press enter
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCEnter"],
+ delay: this.#delay1000,
+ activate: 1,
+ addDepress: "true",
+ });
+ //Type hem
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCEast"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCWest"],
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ //enter first long digits
+
+ const firstLong = waypoint.long.substring(0, waypoint.long.length - 4);
+ const last4Long = waypoint.long.substring(waypoint.long.length - 4);
+ //Type long
+ for (let i = 0; i < firstLong.length; i++) {
+ this.#addKeyboardCode(firstLong.charAt(i), false);
+ }
+ //press enter
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCEnter"],
+ delay: this.#delay1000,
+ activate: 1,
+ addDepress: "true",
+ });
+ //enter last 4 digits
+ for (let i = 0; i < last4Long.length; i++) {
+ this.#addKeyboardCode(last4Long.charAt(i), false);
+ }
+ //press enter
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCEnter"],
+ delay: this.#delay1000,
+ activate: 1,
+ addDepress: "true",
+ });
+ //press position 3 to select elevation
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCOpt3"],
+ delay: this.#delay200,
+ activate: 1,
+ addDepress: "true",
+ });
+ //press position 1 to select feet
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCOpt1"],
+ delay: this.#delay200,
+ activate: 1,
+ addDepress: "true",
+ });
+ //Type elev
+ for (let i = 0; i < waypoint.elev.length; i++) {
+ this.#addKeyboardCode(waypoint.elev.charAt(i), false);
+ }
+ //press enter
+ this.#codesPayload.push({
+ device: this.device_UFC,
+ code: this.#kuKeycodes["UFCEnter"],
+ delay: this.#delay500,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ }
+ return this.#codesPayload;
+ }
+}
+
+export default fa18;
\ No newline at end of file
diff --git a/src/moduleCommands/ka50.js b/src/moduleCommands/ka50.js
index 747856a..012a762 100644
--- a/src/moduleCommands/ka50.js
+++ b/src/moduleCommands/ka50.js
@@ -1,200 +1,122 @@
-class ka50 {
- static extraDelay = 0;
- static #delay100 = 100 + this.extraDelay;
- static #kuKeycodes = {
- 1: 3002,
- 2: 3003,
- 3: 3004,
- 4: 3005,
- 5: 3006,
- 6: 3007,
- 7: 3008,
- 8: 3009,
- 9: 3010,
- 0: 3001,
- };
- static #codesPayload = [];
-
- static #addKeyboardCode(character) {
- const characterCode = this.#kuKeycodes[character.toLowerCase()];
- if (characterCode !== undefined)
- this.#codesPayload.push({
- device: 20,
- code: characterCode,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
-
- static createButtonCommands(waypoints) {
- /*
- button list, all are device 20
- Waypoint button 3011
- Enter 3018
- PVI mode ENT 3026 rotary value 0.2
- PVI Mode OPER 3026 rotary value 0.3
-
- 0/+ 3001 +for north and east
- 1/- 3002 -for south and west
- 2 3003
- 3 3004
- 4 3005
- 5 3006
- 6 3007
- 7 3008
- 8 3009
- 9 3010
-
- */
- this.#codesPayload = [];
- this.#codesPayload.push(
- //PVI to Entry mode
- {
- device: 20,
- code: 3026,
- delay: this.#delay100,
- activate: 0.2,
- addDepress: "false",
- },
- //Press waypoint button
- {
- device: 20,
- code: 3011,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- );
-
- for (let i = 1; i <= waypoints.length; i++) {
- //Press the corresponding waypoint number
- // eslint-disable-next-line default-case
- switch (i) {
- case 1: {
- this.#codesPayload.push({
- device: 20,
- code: 3002,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- break;
- }
- case 2: {
- this.#codesPayload.push({
- device: 20,
- code: 3003,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- break;
- }
- case 3: {
- this.#codesPayload.push({
- device: 20,
- code: 3004,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- break;
- }
- case 4: {
- this.#codesPayload.push({
- device: 20,
- code: 3005,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- break;
- }
- case 5: {
- this.#codesPayload.push({
- device: 20,
- code: 3006,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- break;
- }
- case 6: {
- this.#codesPayload.push({
- device: 20,
- code: 3007,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- break;
- }
- }
- //Type hem
- if (waypoints[i - 1].latHem === "N") {
- this.#codesPayload.push({
- device: 20,
- code: 3001,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: 20,
- code: 3002,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- //type lat
- for (let j = 0; j < waypoints[i - 1].lat.length; j++) {
- this.#addKeyboardCode(waypoints[i - 1].lat.charAt(j));
- }
- //Type hem
- if (waypoints[i - 1].longHem === "E") {
- this.#codesPayload.push({
- device: 20,
- code: 3001,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: 20,
- code: 3002,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- //type long
- for (let j = 0; j < waypoints[i - 1].long.length; j++) {
- this.#addKeyboardCode(waypoints[i - 1].long.charAt(j));
- }
- //Press Enter
- this.#codesPayload.push({
- device: 20,
- code: 3018,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- //PVI to OPER
- this.#codesPayload.push({
- device: 20,
- code: 3026,
- delay: this.#delay100,
- activate: 0.3,
- addDepress: "false",
- });
- return this.#codesPayload;
- }
-}
-
-export default ka50;
+class ka50 {
+ static slotVariant = ""; // Slot variant should be 'ka50WPT' or 'ka50TGT'
+ static extraDelay = 0;
+ static #delay100 = 100 + this.extraDelay;
+ static #kuKeycodes = {
+ 1: 3002, // Start waypoints from 3002 (button 1)
+ 2: 3003,
+ 3: 3004,
+ 4: 3005,
+ 5: 3006,
+ 6: 3007,
+ 7: 3008,
+ 8: 3009,
+ 9: 3010,
+ 0: 3001, // Button 0 for the 10th targetpoint
+ };
+ static #codesPayload = [];
+
+ static #addKeyboardCode(character) {
+ const characterCode = this.#kuKeycodes[character.toLowerCase()];
+ if (characterCode !== undefined)
+ this.#codesPayload.push({
+ device: 20,
+ code: characterCode,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ static createButtonCommands(waypoints) {
+ // Clear the payload for each call
+ this.#codesPayload = [];
+
+ // PVI to Entry mode
+ this.#codesPayload.push(
+ {
+ device: 20,
+ code: 3026,
+ delay: this.#delay100,
+ activate: 0.2,
+ addDepress: "false",
+ },
+ // Conditionally press the waypoint or targetpoint button
+ {
+ device: 20,
+ code: this.slotVariant === "ka50TGT" ? 3017 : 3011, // Press button 3017 for targetpoints, 3011 for waypoints
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ }
+ );
+
+ // Determine the max number of points based on the slotVariant
+ const maxPoints = this.slotVariant === "ka50TGT" ? 10 : 6;
+
+ // Process waypoints (or targetpoints if slotVariant is set to 'ka50TGT')
+ for (let i = 1; i <= Math.min(waypoints.length, maxPoints); i++) {
+ // For waypoints: Start from button 3002 (i=1 maps to 3002, i=2 maps to 3003, etc.)
+ // For targetpoints: Continue from button 3001 to 3010 for 10 points
+ const buttonCode = this.slotVariant === "ka50TGT" ? 3001 + (i % 10) : 3002 + (i - 1);
+
+ this.#codesPayload.push({
+ device: 20,
+ code: buttonCode,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Type latitude hemisphere (N/S)
+ this.#codesPayload.push({
+ device: 20,
+ code: waypoints[i - 1].latHem === "N" ? 3001 : 3002,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Type latitude
+ for (let j = 0; j < waypoints[i - 1].lat.length; j++) {
+ this.#addKeyboardCode(waypoints[i - 1].lat.charAt(j));
+ }
+
+ // Type longitude hemisphere (E/W)
+ this.#codesPayload.push({
+ device: 20,
+ code: waypoints[i - 1].longHem === "E" ? 3001 : 3002,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Type longitude
+ for (let j = 0; j < waypoints[i - 1].long.length; j++) {
+ this.#addKeyboardCode(waypoints[i - 1].long.charAt(j));
+ }
+
+ // Press Enter
+ this.#codesPayload.push({
+ device: 20,
+ code: 3018,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // PVI to OPER
+ this.#codesPayload.push({
+ device: 20,
+ code: 3026,
+ delay: this.#delay100,
+ activate: 0.3,
+ addDepress: "false",
+ });
+
+ return this.#codesPayload;
+ }
+}
+
+export default ka50;
\ No newline at end of file
diff --git a/src/moduleCommands/m2000.js b/src/moduleCommands/m2000.js
index 875adff..25abf96 100644
--- a/src/moduleCommands/m2000.js
+++ b/src/moduleCommands/m2000.js
@@ -1,174 +1,174 @@
-class m2000 {
- static extraDelay = 0;
- static #delay100 = 100 + this.extraDelay;
- static #kuKeycodes = {
- 1: 3584,
- 2: 3585,
- 3: 3586,
- 4: 3587,
- 5: 3588,
- 6: 3589,
- 7: 3590,
- 8: 3591,
- 9: 3592,
- 0: 3593,
- };
- static #codesPayload = [];
-
- static #addKeyboardCode(character) {
- const characterCode = this.#kuKeycodes[character.toLowerCase()];
- if (characterCode !== undefined)
- this.#codesPayload.push({
- device: 9,
- code: characterCode,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
-
- static createButtonCommands(waypoints) {
- this.#codesPayload = [];
- for (const waypoint of waypoints) {
- this.#codesPayload.push(
- {
- device: 9,
- code: 3574,
- delay: this.#delay100,
- activate: 0.4,
- addDepress: "false",
- },
- {
- device: 9,
- code: 3110,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- {
- device: 9,
- code: 3570,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- {
- device: 9,
- code: 3570,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- {
- device: 9,
- code: 3584,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- );
- //Type hem
- if (waypoint.latHem === "N") {
- this.#codesPayload.push({
- device: 9,
- code: 3585,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: 9,
- code: 3591,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- //type lat
- for (let i = 0; i < waypoint.lat.length; i++) {
- this.#addKeyboardCode(waypoint.lat.charAt(i));
- }
- this.#codesPayload.push({
- device: 9,
- code: 3596,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- this.#codesPayload.push({
- device: 9,
- code: 3586,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- //Type hem
- if (waypoint.longHem === "E") {
- this.#codesPayload.push({
- device: 9,
- code: 3589,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: 9,
- code: 3587,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- //type long
- for (let i = 0; i < waypoint.long.length; i++) {
- this.#addKeyboardCode(waypoint.long.charAt(i));
- }
- this.#codesPayload.push({
- device: 9,
- code: 3596,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- this.#codesPayload.push({
- device: 9,
- code: 3574,
- delay: this.#delay100,
- activate: 0.3,
- addDepress: "false",
- });
- this.#codesPayload.push(
- {
- device: 9,
- code: 3584,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- {
- device: 9,
- code: 3584,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- },
- );
- //type elev
- for (let i = 0; i < waypoint.elev.length; i++) {
- this.#addKeyboardCode(waypoint.elev.charAt(i));
- }
- this.#codesPayload.push({
- device: 9,
- code: 3596,
- delay: this.#delay100,
- activate: 1,
- addDepress: "true",
- });
- }
- return this.#codesPayload;
- }
-}
-
-export default m2000;
+class m2000 {
+ static extraDelay = 0;
+ static #delay100 = 100 + this.extraDelay;
+ static #kuKeycodes = {
+ 1: 3584,
+ 2: 3585,
+ 3: 3586,
+ 4: 3587,
+ 5: 3588,
+ 6: 3589,
+ 7: 3590,
+ 8: 3591,
+ 9: 3592,
+ 0: 3593,
+ };
+ static #codesPayload = [];
+
+ static #addKeyboardCode(character) {
+ const characterCode = this.#kuKeycodes[character.toLowerCase()];
+ if (characterCode !== undefined)
+ this.#codesPayload.push({
+ device: 9,
+ code: characterCode,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ static createButtonCommands(waypoints) {
+ this.#codesPayload = [];
+ for (const waypoint of waypoints) {
+ this.#codesPayload.push(
+ {
+ device: 9,
+ code: 3574,
+ delay: this.#delay100,
+ activate: 0.4,
+ addDepress: "false",
+ },
+ {
+ device: 9,
+ code: 3110,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ {
+ device: 9,
+ code: 3570,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ {
+ device: 9,
+ code: 3570,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ {
+ device: 9,
+ code: 3584,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ );
+ //Type hem
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push({
+ device: 9,
+ code: 3585,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: 9,
+ code: 3591,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ //type lat
+ for (let i = 0; i < waypoint.lat.length; i++) {
+ this.#addKeyboardCode(waypoint.lat.charAt(i));
+ }
+ this.#codesPayload.push({
+ device: 9,
+ code: 3596,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ this.#codesPayload.push({
+ device: 9,
+ code: 3586,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ //Type hem
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push({
+ device: 9,
+ code: 3589,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: 9,
+ code: 3587,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ //type long
+ for (let i = 0; i < waypoint.long.length; i++) {
+ this.#addKeyboardCode(waypoint.long.charAt(i));
+ }
+ this.#codesPayload.push({
+ device: 9,
+ code: 3596,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ this.#codesPayload.push({
+ device: 9,
+ code: 3574,
+ delay: this.#delay100,
+ activate: 0.3,
+ addDepress: "false",
+ });
+ this.#codesPayload.push(
+ {
+ device: 9,
+ code: 3584,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ {
+ device: 9,
+ code: 3584,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ },
+ );
+ //type elev
+ for (let i = 0; i < waypoint.elev.length; i++) {
+ this.#addKeyboardCode(waypoint.elev.charAt(i));
+ }
+ this.#codesPayload.push({
+ device: 9,
+ code: 3596,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ return this.#codesPayload;
+ }
+}
+
+export default m2000;
diff --git a/src/moduleCommands/miragef1.js b/src/moduleCommands/miragef1.js
index 3321cfc..c1d26a9 100644
--- a/src/moduleCommands/miragef1.js
+++ b/src/moduleCommands/miragef1.js
@@ -1,83 +1,83 @@
-class miragef1 {
- static extraDelay = 0;
- static #delay100 = 100 + this.extraDelay;
- static #kuKeycodes = {
- 1: 3698,
- 2: 3699, // N
- 3: 3700,
- 4: 3701, // W
- 5: 3702,
- 6: 3703, // E
- 7: 3704,
- 8: 3705, // S
- 9: 3706,
- 0: 3697,
- INSER: 3711,
- MODE: 3692,
- PARAM: 3690,
- WAYPOINT: 3694,
- };
- static #codesPayload = [];
-
- static #keypadPress(key, value = 1, depress = true) {
- return {
- device: 1,
- code: this.#kuKeycodes[key],
- delay: this.#delay100,
- activate: value,
- addDepress: depress ? "true" : "false",
- };
- }
-
- static createButtonCommands(waypoints) {
- this.#codesPayload = [];
- this.#codesPayload.push(
- //Mode NAV
- this.#keypadPress("MODE", 0.1, false),
- //Parameter POS
- this.#keypadPress("PARAM", 0, false),
- );
- for (const waypoint of waypoints) {
- //Increment waypoint
- this.#codesPayload.push(this.#keypadPress("WAYPOINT", 0.111, false));
-
- //Hemisphere
- if (waypoint.latHem === "N") {
- this.#codesPayload.push(this.#keypadPress(2));
- } else {
- this.#codesPayload.push(this.#keypadPress(8));
- }
-
- //Latitude
- for (const c of waypoint.lat) {
- if (isNaN(c)) continue;
- this.#codesPayload.push(this.#keypadPress(parseInt(c)));
- }
-
- //Insert
- this.#codesPayload.push(this.#keypadPress("INSER"));
-
- //Hemisphere
- if (waypoint.longHem === "E") {
- this.#codesPayload.push(this.#keypadPress(6));
- } else {
- this.#codesPayload.push(this.#keypadPress(4));
- }
-
- //Longitude
- for (const c of waypoint.long) {
- if (isNaN(c)) continue;
- this.#codesPayload.push(this.#keypadPress(parseInt(c)));
- }
-
- //Insert
- this.#codesPayload.push(this.#keypadPress("INSER"));
- }
-
- //Parameter PP
- this.#codesPayload.push(this.#keypadPress("PARAM", 0.2, false));
- return this.#codesPayload;
- }
-}
-
-export default miragef1;
+class miragef1 {
+ static extraDelay = 0;
+ static #delay100 = 100 + this.extraDelay;
+ static #kuKeycodes = {
+ 1: 3698,
+ 2: 3699, // N
+ 3: 3700,
+ 4: 3701, // W
+ 5: 3702,
+ 6: 3703, // E
+ 7: 3704,
+ 8: 3705, // S
+ 9: 3706,
+ 0: 3697,
+ INSER: 3711,
+ MODE: 3692,
+ PARAM: 3690,
+ WAYPOINT: 3694,
+ };
+ static #codesPayload = [];
+
+ static #keypadPress(key, value = 1, depress = true) {
+ return {
+ device: 1,
+ code: this.#kuKeycodes[key],
+ delay: this.#delay100,
+ activate: value,
+ addDepress: depress ? "true" : "false",
+ };
+ }
+
+ static createButtonCommands(waypoints) {
+ this.#codesPayload = [];
+ this.#codesPayload.push(
+ //Mode NAV
+ this.#keypadPress("MODE", 0.1, false),
+ //Parameter POS
+ this.#keypadPress("PARAM", 0, false),
+ );
+ for (const waypoint of waypoints) {
+ //Increment waypoint
+ this.#codesPayload.push(this.#keypadPress("WAYPOINT", 0.111, false));
+
+ //Hemisphere
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push(this.#keypadPress(2));
+ } else {
+ this.#codesPayload.push(this.#keypadPress(8));
+ }
+
+ //Latitude
+ for (const c of waypoint.lat) {
+ if (isNaN(c)) continue;
+ this.#codesPayload.push(this.#keypadPress(parseInt(c)));
+ }
+
+ //Insert
+ this.#codesPayload.push(this.#keypadPress("INSER"));
+
+ //Hemisphere
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push(this.#keypadPress(6));
+ } else {
+ this.#codesPayload.push(this.#keypadPress(4));
+ }
+
+ //Longitude
+ for (const c of waypoint.long) {
+ if (isNaN(c)) continue;
+ this.#codesPayload.push(this.#keypadPress(parseInt(c)));
+ }
+
+ //Insert
+ this.#codesPayload.push(this.#keypadPress("INSER"));
+ }
+
+ //Parameter PP
+ this.#codesPayload.push(this.#keypadPress("PARAM", 0.2, false));
+ return this.#codesPayload;
+ }
+}
+
+export default miragef1;
diff --git a/src/moduleCommands/moduleCommands.test.js b/src/moduleCommands/moduleCommands.test.js
index 1645d5f..ed3dbff 100644
--- a/src/moduleCommands/moduleCommands.test.js
+++ b/src/moduleCommands/moduleCommands.test.js
@@ -1,76 +1,86 @@
-import {
- a10Buttons,
- a10Waypoints,
- ah64Buttons,
- ah64Waypoints,
- av8bButtons,
- av8bWaypoints,
- f15eButtons,
- f15eWaypoints,
- f16Buttons,
- f16Waypoints,
- fa18Buttons,
- fa18Waypoints,
- ka50Buttons,
- ka50Waypoints,
- m2000Buttons,
- m2000Waypoints,
- miragef1Buttons,
- miragef1Waypoints,
- uh60Buttons,
- uh60Waypoints,
-} from "../../testing/constants";
-import a10 from "./a10";
-import ah64 from "./ah64";
-import av8b from "./av8b";
-import f15e from "./f15e";
-import fa18 from "./fa18";
-import ka50 from "./ka50";
-import m2000 from "./m2000";
-import miragef1 from "./miragef1";
-import uh60l from "./uh60l";
-import f16 from "./f16";
-
-describe("Converts module waypoints to button presses", () => {
- test("A-10", () => {
- const buttonPresses = a10.createButtonCommands(a10Waypoints);
- expect(buttonPresses).toEqual(a10Buttons);
- });
- test("AH-64", () => {
- ah64.slotVariant = "AH-64D_BLK_IIpilot";
- const buttonPresses = ah64.createButtonCommands(ah64Waypoints);
- expect(buttonPresses).toEqual(ah64Buttons);
- });
- test("AV-8B", () => {
- const buttonPresses = av8b.createButtonCommands(av8bWaypoints);
- expect(buttonPresses).toEqual(av8bButtons);
- });
- test("F-15E", () => {
- const buttonPresses = f15e.createButtonCommands(f15eWaypoints);
- expect(buttonPresses).toEqual(f15eButtons);
- });
- test("F-16", () => {
- const buttonPresses = f16.createButtonCommands(f16Waypoints);
- expect(buttonPresses).toEqual(f16Buttons);
- });
- test("F/A-18", () => {
- const buttonPresses = fa18.createButtonCommands(fa18Waypoints);
- expect(buttonPresses).toEqual(fa18Buttons);
- });
- test("Ka-50", () => {
- const buttonPresses = ka50.createButtonCommands(ka50Waypoints);
- expect(buttonPresses).toEqual(ka50Buttons);
- });
- test("M-2000", () => {
- const buttonPresses = m2000.createButtonCommands(m2000Waypoints);
- expect(buttonPresses).toEqual(m2000Buttons);
- });
- test("Mirage F1", () => {
- const buttonPresses = miragef1.createButtonCommands(miragef1Waypoints);
- expect(buttonPresses).toEqual(miragef1Buttons);
- });
- test("UH-60", () => {
- const buttonPresses = uh60l.createButtonCommands(uh60Waypoints);
- expect(buttonPresses).toEqual(uh60Buttons);
- });
-});
+import {
+ a10Buttons,
+ a10Waypoints,
+ ah64Buttons,
+ ah64Waypoints,
+ av8bButtons,
+ av8bWaypoints,
+ f15eButtons,
+ f15eWaypoints,
+ f16Buttons,
+ f16Waypoints,
+ fa18Buttons,
+ fa18Waypoints,
+ ka50Buttons,
+ ka50Waypoints,
+ m2000Buttons,
+ m2000Waypoints,
+ miragef1Buttons,
+ miragef1Waypoints,
+ uh60Buttons,
+ uh60Waypoints,
+} from "../../testing/constants";
+import a10 from "./a10";
+import ah64 from "./ah64";
+import av8b from "./av8b";
+import f15e from "./f15e";
+import fa18 from "./fa18";
+import ka50 from "./ka50";
+import m2000 from "./m2000";
+import miragef1 from "./miragef1";
+import uh60l from "./uh60l";
+import f16 from "./f16";
+import ch47f from "./ch47f";
+import c130j from "./c130j";
+
+describe("Converts module waypoints to button presses", () => {
+ test("A-10", () => {
+ const buttonPresses = a10.createButtonCommands(a10Waypoints);
+ expect(buttonPresses).toEqual(a10Buttons);
+ });
+ test("AH-64", () => {
+ ah64.slotVariant = "AH-64D_BLK_IIpilot";
+ const buttonPresses = ah64.createButtonCommands(ah64Waypoints);
+ expect(buttonPresses).toEqual(ah64Buttons);
+ });
+ test("AV-8B", () => {
+ const buttonPresses = av8b.createButtonCommands(av8bWaypoints);
+ expect(buttonPresses).toEqual(av8bButtons);
+ });
+ test("F-15E", () => {
+ const buttonPresses = f15e.createButtonCommands(f15eWaypoints);
+ expect(buttonPresses).toEqual(f15eButtons);
+ });
+ test("F-16", () => {
+ const buttonPresses = f16.createButtonCommands(f16Waypoints);
+ expect(buttonPresses).toEqual(f16Buttons);
+ });
+ test("F/A-18", () => {
+ const buttonPresses = fa18.createButtonCommands(fa18Waypoints);
+ expect(buttonPresses).toEqual(fa18Buttons);
+ });
+ test("Ka-50", () => {
+ const buttonPresses = ka50.createButtonCommands(ka50Waypoints);
+ expect(buttonPresses).toEqual(ka50Buttons);
+ });
+ test("M-2000", () => {
+ const buttonPresses = m2000.createButtonCommands(m2000Waypoints);
+ expect(buttonPresses).toEqual(m2000Buttons);
+ });
+ test("Mirage F1", () => {
+ const buttonPresses = miragef1.createButtonCommands(miragef1Waypoints);
+ expect(buttonPresses).toEqual(miragef1Buttons);
+ });
+ test("UH-60", () => {
+ const buttonPresses = uh60l.createButtonCommands(uh60Waypoints);
+ expect(buttonPresses).toEqual(uh60Buttons);
+ });
+ test("CH-47Fbl1", () => {
+ const buttonPresses = ch47f.createButtonCommands(ch47fWaypoints);
+ expect(buttonPresses).toEqual(ch47fButtons);
+ });
+ test("C-130J-30", () => {
+ const buttonPresses = c130j.createButtonCommands(c130jWaypoints);
+ expect(buttonPresses).toEqual(c130jButtons);
+ });
+});
diff --git a/src/moduleCommands/ns430.js b/src/moduleCommands/ns430.js
new file mode 100644
index 0000000..81cc0ca
--- /dev/null
+++ b/src/moduleCommands/ns430.js
@@ -0,0 +1,736 @@
+// ============================================================
+// NS430 waypoint entry helpers
+// Shared across aircraft that host the ED NS430
+// ============================================================
+
+class ns430 {
+
+ // ============================================================
+ // MODULE METADATA
+ // ============================================================
+
+ static slotVariant = "";
+ static #device = 257;
+ static extraDelay = 0;
+
+ static #buttondown = 100;
+ static #buttonup = 200;
+
+ static #rotarydown = 150;
+ static #rotaryup = 100;
+
+ static #rotarypushdown = 200;
+ static #rotarypushup = 200;
+
+ static #holdup = 3000;
+
+ // ============================================================
+ // NS430 COMMAND CODES
+ // ============================================================
+
+ static #codes = {
+ CLRD: 3023,
+ CLRU: 3044,
+ DIRD: 3021,
+ DIRU: 3042,
+ ENTD: 3024,
+ ENTU: 3045,
+ FPLD: 3017,
+ FPLU: 3038,
+ MENUD: 3022,
+ MENUU: 3043,
+ MSGU: 3016,
+ MSGD: 3037,
+ RIGHT_BIG: 3026,
+ RIGHT_SMALL: 3028,
+ RIGHT_SMALL_PUSHD: 3027,
+ RIGHT_SMALL_PUSHU: 3046,
+ };
+
+ static #codesPayload = [];
+ static #lastEnteredName = " ";
+
+ // ============================================================
+ // CHARACTER SETS
+ // ============================================================
+
+ static #nameCharset = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+ static #coordCharsets = [
+ "NS",
+ "012345678",
+ "0123456789",
+ "012345",
+ "0123456789",
+ "0123456789",
+ "0123456789",
+ "EW",
+ "01",
+ "01234567",
+ "0123456789",
+ "012345",
+ "0123456789",
+ "0123456789",
+ "0123456789",
+ ];
+
+ // ============================================================
+ // PAYLOAD MANAGEMENT
+ // ============================================================
+
+ static resetPayload() {
+ this.#codesPayload = [];
+ }
+
+ static getPayload() {
+ return this.#codesPayload.slice();
+ }
+
+ // ============================================================
+ // LOW LEVEL INPUT HELPERS
+ // ============================================================
+
+ static #pause(ms) {
+ this.#codesPayload.push({
+ device: 0,
+ code: 0,
+ delay: ms,
+ activate: 0,
+ });
+ }
+
+ static #button(
+ codeDown,
+ codeUp,
+ downMs = this.#buttondown,
+ upMs = this.#buttonup,
+ ) {
+ this.#codesPayload.push({
+ device: this.#device,
+ code: codeDown,
+ delay: downMs,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device: this.#device,
+ code: codeUp,
+ delay: upMs,
+ activate: 0,
+ addDepress: "false",
+ });
+ }
+
+ static #rotary(
+ code,
+ dir,
+ addDepress = "false",
+ downMs = this.#rotarydown,
+ upMs = this.#rotaryup,
+ ) {
+ this.#codesPayload.push({
+ device: this.#device,
+ code,
+ delay: downMs,
+ activate: dir,
+ addDepress,
+ });
+ this.#codesPayload.push({
+ device: this.#device,
+ code,
+ delay: upMs,
+ activate: 0,
+ addDepress,
+ });
+ }
+
+ static #rotaryPush(
+ codeDown,
+ codeUp,
+ downMs = this.#rotarypushdown,
+ upMs = this.#rotarypushup,
+ ) {
+ this.#codesPayload.push({
+ device: this.#device,
+ code: codeDown,
+ delay: downMs,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device: this.#device,
+ code: codeUp,
+ delay: upMs,
+ activate: 0,
+ addDepress: "false",
+ });
+ }
+
+ static #holdButton(
+ codeDown,
+ codeUp,
+ holdMs = this.#holdup,
+ upMs = this.#buttonup,
+ ) {
+ this.#codesPayload.push({
+ device: this.#device,
+ code: codeDown,
+ delay: holdMs,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device: this.#device,
+ code: codeUp,
+ delay: upMs,
+ activate: 0,
+ addDepress: "false",
+ });
+ }
+
+ // ============================================================
+ // NAMED BUTTON / ROTARY HELPERS
+ // ============================================================
+
+ static #CLR() {
+ this.#button(this.#codes.CLRD, this.#codes.CLRU);
+ }
+
+ static #DIR() {
+ this.#button(this.#codes.DIRD, this.#codes.DIRU);
+ }
+
+ static #ENT(releaseMs = this.#buttonup) {
+ this.#button(
+ this.#codes.ENTD,
+ this.#codes.ENTU,
+ this.#buttondown,
+ releaseMs,
+ );
+ }
+
+ static #FPL() {
+ this.#button(this.#codes.FPLD, this.#codes.FPLU);
+ }
+
+ static #MENU() {
+ this.#button(this.#codes.MENUD, this.#codes.MENUU);
+ }
+
+ static #MSG() {
+ this.#button(this.#codes.MSGD, this.#codes.MSGU);
+ }
+
+ static #BIGr(addDepress = "false") {
+ this.#rotary(
+ this.#codes.RIGHT_BIG,
+ 1,
+ addDepress,
+ this.#rotarydown,
+ this.#rotaryup,
+ );
+ }
+
+ static #BIGl(addDepress = "false") {
+ this.#rotary(
+ this.#codes.RIGHT_BIG,
+ -1,
+ addDepress,
+ this.#rotarydown,
+ this.#rotaryup,
+ );
+ }
+
+ static #SMALLr(addDepress = "false") {
+ this.#rotary(
+ this.#codes.RIGHT_SMALL,
+ 1,
+ addDepress,
+ this.#rotarydown,
+ this.#rotaryup,
+ );
+ }
+
+ static #SMALLl(addDepress = "false") {
+ this.#rotary(
+ this.#codes.RIGHT_SMALL,
+ -1,
+ addDepress,
+ this.#rotarydown,
+ this.#rotaryup,
+ );
+ }
+
+ static #SMALLp() {
+ this.#rotaryPush(
+ this.#codes.RIGHT_SMALL_PUSHD,
+ this.#codes.RIGHT_SMALL_PUSHU,
+ this.#rotarypushdown,
+ this.#rotarypushup,
+ );
+ }
+
+ static #holdCLR() {
+ this.#holdButton(
+ this.#codes.CLRD,
+ this.#codes.CLRU,
+ this.#holdup,
+ this.#buttonup,
+ );
+ }
+
+ // ============================================================
+ // AUTOMATIC / CALCULATION HELPERS
+ // ============================================================
+
+ static #indexInCharset(charset, ch) {
+ const idx = charset.indexOf(ch);
+ return idx >= 0 ? idx : 0;
+ }
+
+ static #shortestSteps(from, to, charset) {
+ const len = charset.length;
+ const a = this.#indexInCharset(charset, from);
+ const b = this.#indexInCharset(charset, to);
+ const cw = (b - a + len) % len;
+ const ccw = cw - len;
+ return Math.abs(cw) <= Math.abs(ccw) ? cw : ccw;
+ }
+
+ static #sanitizeName(name) {
+ return String(name || "")
+ .toUpperCase()
+ .replace(/[^A-Z0-9]/g, " ")
+ .padEnd(5, " ")
+ .slice(0, 5);
+ }
+
+ static #formatDecimalDegreesToEditableChars(value, isLat) {
+ const numericValue = Number(value);
+ if (!Number.isFinite(numericValue)) {
+ throw new Error(
+ `Invalid ${isLat ? "latitude" : "longitude"} snapshot value: ${value}`,
+ );
+ }
+
+ const hemi = numericValue >= 0
+ ? (isLat ? "N" : "E")
+ : (isLat ? "S" : "W");
+
+ let absValue = Math.abs(numericValue);
+ let degrees = Math.floor(absValue);
+ let minutesHundredths = Math.round((absValue - degrees) * 60 * 100);
+
+ if (minutesHundredths >= 6000) {
+ degrees += 1;
+ minutesHundredths = 0;
+ }
+
+ const maxDegrees = isLat ? 89 : 179;
+ if (degrees > maxDegrees) {
+ degrees = maxDegrees;
+ minutesHundredths = 5999;
+ }
+
+ const degreeWidth = isLat ? 2 : 3;
+ const degreeDigits = String(degrees).padStart(degreeWidth, "0");
+ const minuteWhole = Math.floor(minutesHundredths / 100);
+ const minuteDecimal = minutesHundredths % 100;
+ const minuteDigits =
+ `${String(minuteWhole).padStart(2, "0")}${String(minuteDecimal).padStart(2, "0")}`;
+
+ return [hemi, ...degreeDigits.split(""), ...minuteDigits.split("")];
+ }
+
+ static #formatWaypointFieldToEditableChars(coordText, hemi, isLat) {
+ const safeHemi = String(hemi || (isLat ? "N" : "E")).toUpperCase();
+ const digitsOnly = String(coordText || "").replace(/\D/g, "");
+ const expectedLength = isLat ? 6 : 7;
+ const paddedDigits = digitsOnly.padStart(expectedLength, "0").slice(-expectedLength);
+ return [safeHemi, ...paddedDigits.split("")];
+ }
+
+ static #waypointToTargetChars(waypoint) {
+ const latHem = waypoint?.latHemi || waypoint?.latHem || "N";
+ const longHem = waypoint?.longHemi || waypoint?.longHem || "E";
+
+ const latChars = this.#formatWaypointFieldToEditableChars(
+ waypoint?.lat,
+ latHem,
+ true,
+ );
+ const longChars = this.#formatWaypointFieldToEditableChars(
+ waypoint?.long,
+ longHem,
+ false,
+ );
+
+ return [...latChars, ...longChars];
+ }
+
+ static #snapshotToCurrentChars(snapshot) {
+ const latValue = snapshot?.lat ?? snapshot?.Lat ?? snapshot?.latitude;
+ const longValue = snapshot?.long ?? snapshot?.lon ?? snapshot?.Long ?? snapshot?.longitude;
+
+ const latChars = this.#formatDecimalDegreesToEditableChars(latValue, true);
+ const longChars = this.#formatDecimalDegreesToEditableChars(longValue, false);
+
+ return [...latChars, ...longChars];
+ }
+
+ static #turnSmallBySteps(steps) {
+ if (steps === 0) {
+ return;
+ }
+
+ const stepFn = steps > 0 ? this.#SMALLr.bind(this) : this.#SMALLl.bind(this);
+ for (let s = 0; s < Math.abs(steps); s += 1) {
+ stepFn();
+ }
+ }
+
+ static #setCharacter(fromChar, toChar, charset) {
+ const steps = this.#shortestSteps(fromChar, toChar, charset);
+ this.#turnSmallBySteps(steps);
+ return toChar;
+ }
+
+ static #applyNameCharacter(current, target, index) {
+ const previousChar = current[index];
+ current[index] = this.#setCharacter(
+ previousChar,
+ target[index],
+ this.#nameCharset,
+ );
+
+ if (previousChar !== target[index]) {
+ for (let j = index + 1; j < current.length; j += 1) {
+ current[j] = " ";
+ }
+ }
+ }
+
+ static #enterNameAutomatically(name) {
+ const target = this.#sanitizeName(name).split("");
+ const current = this.#lastEnteredName.split("");
+
+ for (let i = 0; i < current.length; i += 1) {
+ this.#applyNameCharacter(current, target, i);
+
+ if (i < current.length - 1) {
+ this.#BIGr();
+ }
+ }
+
+ this.#lastEnteredName = target.join("");
+ }
+
+ static #enterFlightPlanWaypointName(name) {
+ const target = this.#sanitizeName(name).split("");
+ const current = ["K", " ", " ", " ", " "];
+
+ for (let i = 0; i < current.length; i += 1) {
+ current[i] = this.#setCharacter(
+ current[i],
+ target[i],
+ this.#nameCharset,
+ );
+
+ if (i < current.length - 1) {
+ this.#BIGr();
+ }
+ }
+ }
+
+ static #enterCoordinatesAutomatically(current, target) {
+ current[0] = this.#setCharacter(current[0], target[0], this.#coordCharsets[0]);
+
+ this.#pause(100);
+ this.#BIGr();
+ this.#pause(100);
+
+ for (let i = 1; i < this.#coordCharsets.length; i += 1) {
+ current[i] = this.#setCharacter(current[i], target[i], this.#coordCharsets[i]);
+
+ if (i < this.#coordCharsets.length - 1) {
+ this.#BIGr();
+ }
+ }
+ }
+
+ // =================================================================================================================================================================
+ // INITIAL SETUP COMMANDS
+ // =================================================================================================================================================================
+
+ static buildSetupCommands(deletePasses = 10) {
+ this.resetPayload();
+ this.#lastEnteredName = " ";
+
+ this.#CLR();
+ this.#SMALLr();
+ this.#SMALLl();
+ this.#BIGr();
+ this.#BIGl();
+ this.#pause(100);
+
+ // DELETE CURRENT FLIGHT PLAN
+ this.#FPL();
+ this.#MENU();
+ this.#SMALLr();
+ this.#SMALLr();
+ this.#SMALLr();
+ this.#ENT();
+ this.#ENT();
+ this.#SMALLp();
+ this.#pause(300);
+
+ //DELETE CURRENT USER WAYPOINTS X10
+ this.#holdCLR();
+ this.#BIGr();
+
+ for (let i = 0; i < 10; i += 1) {
+ this.#SMALLr();
+ }
+
+ for (let i = 0; i < deletePasses; i += 1) {
+ this.#MENU();
+ this.#SMALLr("true");
+ this.#ENT();
+ this.#ENT();
+ }
+
+ this.#holdCLR();
+ this.#pause(400);
+ this.#BIGl();
+ this.#BIGr();
+ this.#pause(600);
+
+ this.#SMALLp();
+ this.#pause(300);
+
+ return this.getPayload();
+ }
+
+ // ============================================================
+ // NAME ENTRY COMMANDS
+ // ============================================================
+
+ static buildNameCharactersCommands(name) {
+ this.resetPayload();
+
+ this.#pause(100);
+ this.#SMALLr();
+ this.#pause(200);
+
+ this.#enterNameAutomatically(name);
+
+ this.#pause(500);
+
+ return this.getPayload();
+ }
+
+ static buildConfirmNameCommands() {
+ this.resetPayload();
+
+ this.#ENT();
+
+ return this.getPayload();
+ }
+
+ static buildNameEntryCommands(name) {
+ return [
+ ...this.buildNameCharactersCommands(name),
+ ...this.buildConfirmNameCommands(),
+ ];
+ }
+
+ // ============================================================
+ // MOVE TO POSITION FIELD
+ // ============================================================
+
+ static buildMoveToPositionFieldCommands() {
+ this.resetPayload();
+
+ this.#pause(100);
+
+ for (let i = 0; i < 6; i += 1) {
+ this.#ENT();
+ }
+
+ this.#pause(100);
+
+ this.#SMALLr();
+ for (let i = 0; i < 7; i += 1) {
+ this.#BIGl();
+ }
+
+ this.#pause(200);
+
+ return this.getPayload();
+ }
+
+ // ============================================================
+ // COORDINATE ENTRY COMMANDS
+ // ============================================================
+
+ static buildCoordinateEntryCommands(currentPosition, waypoint) {
+ this.resetPayload();
+
+ const current = this.#snapshotToCurrentChars(currentPosition);
+ const target = this.#waypointToTargetChars(waypoint);
+
+ this.#enterCoordinatesAutomatically(current, target);
+
+ this.#pause(500);
+ this.#BIGr();
+ this.#BIGr();
+ this.#BIGr();
+ this.#BIGr();
+ this.#ENT();
+ this.#pause(500);
+ this.#ENT();
+ this.#pause(500);
+
+ return this.getPayload();
+ }
+
+ // ============================================================
+ // SAVE WAYPOINT COMMANDS
+ // ============================================================
+
+ static buildSaveWaypointCommands() {
+ this.resetPayload();
+
+ this.#pause(800);
+
+ this.#SMALLp();
+ this.#MSG();
+ this.#MSG();
+
+ return this.getPayload();
+ }
+
+ // ============================================================
+ // FLIGHT PLAN COMMANDS
+ // ============================================================
+
+ static buildFlightPlanCreateStartCommands() {
+ this.resetPayload();
+
+
+ this.#FPL();
+
+ this.#SMALLp();
+
+ this.#pause(300);
+
+ return this.getPayload();
+ }
+
+ static buildFlightPlanAddWaypointFromListCommands(waypointNumber) {
+ this.resetPayload();
+
+ const listSteps = Math.max(0, (Number(waypointNumber) || 0) - 1);
+ const bigRightSteps = Math.max(0, (Number(waypointNumber) || 0) - 6);
+
+ this.#pause(150);
+ this.#SMALLr();
+
+ // Move first character from K to W (correct number of steps)
+ this.#setCharacter("K", "W", this.#nameCharset);
+ this.#pause(200);
+
+ this.#BIGr();
+
+ // Move first character from K to W (correct number of steps)
+ this.#setCharacter(" ", "A", this.#nameCharset);
+ this.#pause(200);
+
+ // Open the user waypoint list
+ this.#ENT();
+ this.#pause(300);
+
+ // Move down to the correct waypoint in the list
+ for (let i = 0; i < listSteps; i += 1) {
+ this.#SMALLr();
+ }
+
+ this.#pause(300);
+
+ // Select highlighted waypoint
+ this.#ENT();
+ this.#pause(300);
+
+ // Accept it
+ this.#ENT();
+ this.#pause(300);
+
+ // Move across to the next visible flight plan slot if needed
+ for (let i = 0; i < bigRightSteps; i += 1) {
+ this.#BIGr();
+ }
+
+ this.#BIGr();
+ this.#BIGr();
+ this.#BIGr();
+ this.#BIGr();
+
+ return this.getPayload();
+}
+
+ static buildFlightPlanAddWaypointCommands(name, waypointNumber) {
+ this.resetPayload();
+
+ const bigRightSteps = Math.max(0, (Number(waypointNumber) || 0) - 6);
+
+ this.#pause(150);
+
+ this.#SMALLr();
+ this.#pause(200);
+
+ this.#enterFlightPlanWaypointName(name);
+
+ this.#pause(300);
+ this.#ENT();
+ this.#pause(300);
+ this.#ENT();
+ this.#pause(300);
+ this.#ENT();
+ this.#pause(300);
+
+ for (let i = 0; i < bigRightSteps; i += 1) {
+ this.#BIGr();
+ }
+
+ this.#BIGr();
+ this.#BIGr();
+ this.#BIGr();
+ this.#BIGr();
+
+ return this.getPayload();
+}
+
+ static buildFlightPlanActivateCommands(waypointCount) {
+ this.resetPayload();
+
+ const stepsLeft = Math.max(0, Number(waypointCount) || 0) + 5;
+
+ this.#pause(200);
+
+ for (let i = 0; i < stepsLeft; i += 1) {
+ this.#BIGl();
+ }
+
+ this.#pause(200);
+
+ this.#MENU();
+ this.#ENT();
+ this.#ENT();
+ this.#MSG();
+ this.#MSG();
+
+ return this.getPayload();
+}
+}
+
+export default ns430;
\ No newline at end of file
diff --git a/src/moduleCommands/oh58d.js b/src/moduleCommands/oh58d.js
index 81022f8..e9c60df 100644
--- a/src/moduleCommands/oh58d.js
+++ b/src/moduleCommands/oh58d.js
@@ -1,171 +1,172 @@
-import createButtonPress from "../models/ButtonPress";
-
-class oh58d {
- static extraDelay = 100;
- static #delay100 = 100 + this.extraDelay;
- static slotVariant = "";
- static #oh58dkeycodes = {
- // Keyboard keys (device 14)
- a: 3063,
- b: 3064,
- c: 3065,
- d: 3066,
- e: 3067,
- f: 3068,
- g: 3069,
- h: 3070,
- i: 3071,
- j: 3072,
- k: 3073,
- l: 3074,
- m: 3075,
- n: 3076,
- o: 3077,
- p: 3078,
- q: 3079,
- r: 3080,
- s: 3081,
- t: 3082,
- u: 3083,
- v: 3084,
- w: 3085,
- x: 3086,
- y: 3087,
- z: 3088,
- // number keys
- 0: 3062,
- 1: 3053,
- 2: 3054,
- 3: 3055,
- 4: 3056,
- 5: 3057,
- 6: 3058,
- 7: 3059,
- 8: 3060,
- 9: 3061,
- // special keys
- " ": 3094,
- ".": 3090,
- "-": 3095,
- enter: 3089,
- clear: 3091,
- // pilot MFD (device 11) copilot MFD (device 23)
- vsd: 3008,
- hsd: 3009,
- l1: 3001,
- l2: 3002,
- l3: 3003,
- l4: 3004,
- l5: 3005,
-
- r1: 3013,
- r2: 3014,
- r3: 3015,
- r4: 3016,
- r5: 3017,
- };
- static #codesPayload = [];
-
- static #isPilot() {
- return this.slotVariant === "OH58Dright-seat";
- }
-
- static createButtonCommands(waypoints) {
- this.#codesPayload = [];
- this.#codesPayload.push(
- // enter nav setup on pilot MFD
- createButtonPress(
- this.#isPilot() ? 11 : 23,
- this.#oh58dkeycodes["hsd"],
- this.#delay100,
- ),
- createButtonPress(
- this.#isPilot() ? 11 : 23,
- this.#oh58dkeycodes["r2"],
- this.#delay100,
- ),
- createButtonPress(
- this.#isPilot() ? 11 : 23,
- this.#oh58dkeycodes["l4"],
- this.#delay100,
- ),
- );
-
- for (const waypoint of waypoints) {
- this.#codesPayload.push(
- // enter waypoint
- createButtonPress(
- this.#isPilot() ? 11 : 23,
- this.#oh58dkeycodes["l2"],
- this.#delay100,
- ),
- createButtonPress(14, this.#oh58dkeycodes["clear"], this.#delay100),
- );
- const [zone, squareId, easting, northing] = waypoint.MGRS.split(" ");
- let convertedWaypoint = `${zone} ${squareId} ${easting.slice(
- 0,
- 4,
- )} ${northing.slice(0, 4)}`;
- for (let i = 0; i < convertedWaypoint.length; i++) {
- if (convertedWaypoint.charAt(i) !== " ") {
- this.#codesPayload.push(
- // enter each digit of MGRS into scratchpad
- createButtonPress(
- 14,
- this.#oh58dkeycodes[convertedWaypoint.charAt(i).toLowerCase()],
- this.#delay100,
- ),
- );
- }
- }
- this.#codesPayload.push(
- // enter MGRS into UFC
- createButtonPress(14, this.#oh58dkeycodes["enter"], this.#delay100),
- );
-
- this.#codesPayload.push(
- // select altitude on mfd
- createButtonPress(
- this.#isPilot() ? 11 : 23,
- this.#oh58dkeycodes["l4"],
- this.#delay100,
- ),
- createButtonPress(14, this.#oh58dkeycodes["clear"], this.#delay100), // clear current alt
- );
-
- for (let i = 0; i < waypoint.elev.length; i++) {
- // enter in waypoint elevation
- this.#codesPayload.push(
- createButtonPress(
- 14,
- this.#oh58dkeycodes[waypoint.elev.charAt(i).toLowerCase()],
- this.#delay100,
- ),
- );
- }
-
- this.#codesPayload.push(
- // press enter on scratchpad
- createButtonPress(14, this.#oh58dkeycodes["enter"], this.#delay100),
- createButtonPress(
- this.#isPilot() ? 11 : 23,
- this.#oh58dkeycodes["r5"],
- this.#delay100,
- ),
- );
- }
-
- this.#codesPayload.push(
- // exit nav setup on pilot MFD
- createButtonPress(
- this.#isPilot() ? 11 : 23,
- this.#oh58dkeycodes["vsd"],
- this.#delay100,
- ),
- );
-
- return this.#codesPayload;
- }
-}
-
-export default oh58d;
+import createButtonPress from "../models/ButtonPress";
+
+class oh58d {
+ static extraDelay = 100;
+ static #delay100 = 100 + this.extraDelay;
+ static slotVariant = "";
+ static #oh58dkeycodes = {
+ // Keyboard keys (device 14)
+ a: 3063,
+ b: 3064,
+ c: 3065,
+ d: 3066,
+ e: 3067,
+ f: 3068,
+ g: 3069,
+ h: 3070,
+ i: 3071,
+ j: 3072,
+ k: 3073,
+ l: 3074,
+ m: 3075,
+ n: 3076,
+ o: 3077,
+ p: 3078,
+ q: 3079,
+ r: 3080,
+ s: 3081,
+ t: 3082,
+ u: 3083,
+ v: 3084,
+ w: 3085,
+ x: 3086,
+ y: 3087,
+ z: 3088,
+ // number keys
+ 0: 3062,
+ 1: 3053,
+ 2: 3054,
+ 3: 3055,
+ 4: 3056,
+ 5: 3057,
+ 6: 3058,
+ 7: 3059,
+ 8: 3060,
+ 9: 3061,
+ // special keys
+ " ": 3094,
+ ".": 3090,
+ "-": 3095,
+ enter: 3089,
+ clear: 3091,
+ // pilot MFD (device 11) copilot MFD (device 23)
+ vsd: 3008,
+ hsd: 3009,
+ l1: 3001,
+ l2: 3002,
+ l3: 3003,
+ l4: 3004,
+ l5: 3005,
+
+ r1: 3013,
+ r2: 3014,
+ r3: 3015,
+ r4: 3016,
+ r5: 3017,
+ };
+ static #codesPayload = [];
+
+ static #isPilot() {
+ return this.slotVariant === "OH58Dright-seat";
+ }
+
+ static createButtonCommands(waypoints) {
+ this.#codesPayload = [];
+ this.#codesPayload.push(
+ // enter nav setup on pilot MFD
+ createButtonPress(
+ this.#isPilot() ? 11 : 23,
+ this.#oh58dkeycodes["hsd"],
+ this.#delay100,
+ ),
+ createButtonPress(
+ this.#isPilot() ? 11 : 23,
+ this.#oh58dkeycodes["r2"],
+ this.#delay100,
+ ),
+ createButtonPress(
+ this.#isPilot() ? 11 : 23,
+ this.#oh58dkeycodes["l4"],
+ this.#delay100,
+ ),
+ );
+
+ for (const waypoint of waypoints) {
+ this.#codesPayload.push(
+ // enter waypoint
+ createButtonPress(
+ this.#isPilot() ? 11 : 23,
+ this.#oh58dkeycodes["l2"],
+ this.#delay100,
+ ),
+ createButtonPress(14, this.#oh58dkeycodes["clear"], this.#delay100),
+ );
+ const [zone, squareId, easting, northing] = waypoint.MGRS.split(" ");
+ let convertedWaypoint = `${zone} ${squareId} ${easting.slice(
+ 0,
+ 4,
+ )} ${northing.slice(0, 4)}`;
+ for (let i = 0; i < convertedWaypoint.length; i++) {
+ if (convertedWaypoint.charAt(i) !== " ") {
+ this.#codesPayload.push(
+ // enter each digit of MGRS into scratchpad
+ createButtonPress(
+ 14,
+ this.#oh58dkeycodes[convertedWaypoint.charAt(i).toLowerCase()],
+ this.#delay100,
+ ),
+ );
+ }
+ }
+ this.#codesPayload.push(
+ // enter MGRS into UFC
+ createButtonPress(14, this.#oh58dkeycodes["enter"], this.#delay100),
+ );
+
+ this.#codesPayload.push(
+ // select altitude on mfd
+ createButtonPress(
+ this.#isPilot() ? 11 : 23,
+ this.#oh58dkeycodes["l4"],
+ this.#delay100,
+ ),
+ createButtonPress(14, this.#oh58dkeycodes["clear"], this.#delay100), // clear current alt
+ );
+ waypoint.elev = waypoint.elev * 0.3048; // convert feet to meters
+
+ for (let i = 0; i < waypoint.elev.length; i++) {
+ // enter in waypoint elevation
+ this.#codesPayload.push(
+ createButtonPress(
+ 14,
+ this.#oh58dkeycodes[waypoint.elev.charAt(i).toLowerCase()],
+ this.#delay100,
+ ),
+ );
+ }
+
+ this.#codesPayload.push(
+ // press enter on scratchpad
+ createButtonPress(14, this.#oh58dkeycodes["enter"], this.#delay100),
+ createButtonPress(
+ this.#isPilot() ? 11 : 23,
+ this.#oh58dkeycodes["r5"],
+ this.#delay100,
+ ),
+ );
+ }
+
+ this.#codesPayload.push(
+ // exit nav setup on pilot MFD
+ createButtonPress(
+ this.#isPilot() ? 11 : 23,
+ this.#oh58dkeycodes["vsd"],
+ this.#delay100,
+ ),
+ );
+
+ return this.#codesPayload;
+ }
+}
+
+export default oh58d;
\ No newline at end of file
diff --git a/src/moduleCommands/sa342.js b/src/moduleCommands/sa342.js
new file mode 100644
index 0000000..a9fd064
--- /dev/null
+++ b/src/moduleCommands/sa342.js
@@ -0,0 +1,175 @@
+class sa342 {
+ static extraDelay = 0;
+ static #delay50 = 50;
+ static #delay100 = 100 + this.extraDelay;
+ static #delay200 = 200 + this.extraDelay;
+ static #delay500 = 500 + this.extraDelay;
+ static #kuKeycodes = {
+ 1: 3010,
+ 2: 3011, //N
+ 3: 3012,
+ 4: 3013, //W
+ 5: 3014,
+ 6: 3015, //E
+ 7: 3016,
+ 8: 3017, //S
+ 9: 3018,
+ 0: 3009,
+ DOWN: 3008,
+ ENT: 3004,
+ DEL: 3023,
+ };
+ static #codesPayload = [];
+
+ static #addKeyboardCode(character) {
+ const characterCode = this.#kuKeycodes[character.toLowerCase()];
+ if (characterCode !== undefined)
+ this.#codesPayload.push({
+ device: 22,
+ code: characterCode,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ static createButtonCommands(waypoints) {
+ this.#codesPayload = [];
+ this.#codesPayload.push({
+ device: 22,
+ code: 3003,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "false",
+ });
+
+ for (let index = 0; index < waypoints.length; index++) {
+ const waypoint = waypoints[index]; // Access the waypoint using the index
+ const waypointNumber = index + 1; // Use the index to get the waypoint number starting at 1
+
+ // Use the loop number as the waypoint name
+ const waypointName = waypointNumber.toString(); // Convert the waypoint number to string
+
+ for (let i = 0; i < Math.min(waypointName.length, 9); i++) {
+ this.#addKeyboardCode(waypointName.charAt(i)); // Type the waypoint number
+ }
+
+ // Press ENT to edit waypoint
+ this.#codesPayload.push({
+ device: 22,
+ code: 3004,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Press DEL*6 to Delete Waypoint data
+ for (let i = 0; i < 10; i++) {
+ this.#codesPayload.push({
+ device: 22,
+ code: 3023,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device: 22,
+ code: 3023,
+ delay: this.#delay100,
+ activate: 0,
+ addDepress: "false",
+ });
+ }
+
+ // Type hem
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push({
+ device: 22,
+ code: 3011,
+ delay: this.#delay200,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: 22,
+ code: 3017,
+ delay: this.#delay200,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // Type lat
+ for (let i = 0; i < waypoint.lat.length; i++) {
+ waypoint.lat.charAt(i) !== "." &&
+ this.#addKeyboardCode(waypoint.lat.charAt(i));
+ }
+
+ // Press the Down arrow For Longitude
+ this.#codesPayload.push({
+ device: 22,
+ code: 3008,
+ delay: this.#delay500,
+ activate: 1,
+ addDepress: "true",
+ });
+
+ // Press DEL*7 to Delete Waypoint data
+ for (let i = 0; i < 10; i++) {
+ this.#codesPayload.push({
+ device: 22,
+ code: 3023,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "false",
+ });
+ this.#codesPayload.push({
+ device: 22,
+ code: 3023,
+ delay: this.#delay100,
+ activate: 0,
+ addDepress: "false",
+ });
+ }
+
+ // Type long hem
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push({
+ device: 22,
+ code: 3015,
+ delay: this.#delay200,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: 22,
+ code: 3013,
+ delay: this.#delay200,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ // Type long
+ for (let i = 0; i < waypoint.long.length; i++) {
+ waypoint.long.charAt(i) !== "." &&
+ this.#addKeyboardCode(waypoint.long.charAt(i));
+ }
+
+ //Press Enter
+ this.#codesPayload.push({
+ device: 22,
+ code: 3004,
+ delay: this.#delay100,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+
+ return this.#codesPayload;
+ }
+}
+
+export default sa342;
diff --git a/src/moduleCommands/uh60l.js b/src/moduleCommands/uh60l.js
index 246c6a5..ac52015 100644
--- a/src/moduleCommands/uh60l.js
+++ b/src/moduleCommands/uh60l.js
@@ -1,186 +1,186 @@
-class uh60l {
- static extraDelay = 0;
- static #device_id = 23;
- static #mode_value = 0.04;
- static #display_value = 0.05;
- static #delay_value = 50 + this.extraDelay;
- static #kuKeycodes = {
- mode: 3235,
- display: 3236,
- kbd: 3237,
- left: 3238,
- mid: 3239,
- right: 3240,
- f1: 3241,
- 1: 3242,
- 2: 3243,
- 3: 3244,
- tgtstr: 3245,
- 4: 3246,
- 5: 3247,
- 6: 3248,
- inc: 3249,
- 7: 3250,
- 8: 3251,
- 9: 3252,
- dec: 3253,
- clr: 3254,
- 0: 3255,
- ent: 3256,
- // letters...
- a: [3238, 3242],
- b: [3239, 3242],
- c: [3240, 3242],
- d: [3238, 3243],
- e: [3239, 3243],
- f: [3240, 3243],
- g: [3238, 3244],
- h: [3239, 3244],
- i: [3240, 3244],
- j: [3238, 3246],
- k: [3239, 3246],
- l: [3240, 3246],
- m: [3238, 3247],
- n: [3239, 3247],
- o: [3240, 3247],
- p: [3238, 3248],
- q: [3239, 3248],
- r: [3240, 3248],
- s: [3238, 3250],
- t: [3239, 3250],
- u: [3240, 3250],
- v: [3238, 3251],
- w: [3239, 3251],
- x: [3240, 3251],
- y: [3238, 3252],
- z: [3239, 3252],
- "*": [3240, 3252],
- " ": [3239, 3255],
- };
- static #codesPayload = [];
-
- static #addKeyboardCode(character) {
- const characterCode = this.#kuKeycodes[character.toLowerCase()];
- if (typeof characterCode === "object") {
- for (const code of characterCode) {
- this.#codesPayload.push({
- device: this.#device_id,
- code: code,
- delay: this.#delay_value,
- activate: 1,
- addDepress: "true",
- });
- }
- } else {
- if (characterCode !== undefined)
- this.#codesPayload.push({
- device: this.#device_id,
- code: characterCode,
- delay: this.#delay_value,
- activate: 1,
- addDepress: "true",
- });
- }
- }
-
- static #addKeyboardValue(character, value) {
- const characterCode = this.#kuKeycodes[character.toLowerCase()];
- if (characterCode !== undefined && value !== undefined) {
- this.#codesPayload.push({
- device: this.#device_id,
- code: characterCode,
- delay: this.#delay_value,
- activate: value,
- addDepress: "false",
- });
- }
- }
-
- static createButtonCommands(waypoints) {
- this.#codesPayload = [];
-
- // Set mode to LAT/LONG - SelectMode value 0.03
- this.#addKeyboardValue("mode", this.#mode_value);
-
- // Switch to waypoint page (device this.#device_id, SelectDisplay, value 0.05
- this.#addKeyboardValue("display", this.#display_value);
-
- for (const waypoint of waypoints) {
- // increment waypoint
- this.#addKeyboardCode("inc");
-
- // kbd for input
- this.#addKeyboardCode("kbd");
-
- //Type name (max 13 chars on display)
- for (let i = 0; i < 13; i++) {
- this.#addKeyboardCode(waypoint.name.charAt(i));
- }
-
- // kbd
- this.#addKeyboardCode("kbd");
-
- //check if latitude is N or S
- if (waypoint.latHem === "N") {
- this.#codesPayload.push({
- device: this.#device_id,
- code: this.#kuKeycodes["n"][1],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: this.#device_id,
- code: this.#kuKeycodes["s"][1],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "true",
- });
- }
- //Type lat
- for (let i = 0; i < waypoint.lat.length; i++) {
- waypoint.lat.charAt(i) !== "." &&
- this.#addKeyboardCode(waypoint.lat.charAt(i));
- }
-
- // kbd
- this.#addKeyboardCode("kbd");
-
- //check if longitude is E or W
- if (waypoint.longHem === "E") {
- this.#codesPayload.push({
- device: this.#device_id,
- code: this.#kuKeycodes["e"][1],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "true",
- });
- } else {
- this.#codesPayload.push({
- device: this.#device_id,
- code: this.#kuKeycodes["w"][1],
- delay: this.#delay_value,
- activate: 1,
- addDepress: "true",
- });
- }
- //Type long
- for (let i = 0; i < waypoint.long.length; i++) {
- waypoint.long.charAt(i) !== "." &&
- this.#addKeyboardCode(waypoint.long.charAt(i));
- }
-
- // enter
- this.#addKeyboardCode("ent");
- }
- // DEC back to MIZ0 after the INCs
- for (let i = 0; i < waypoints.length; i++) {
- this.#addKeyboardCode("dec");
- }
-
- return this.#codesPayload;
- }
-}
-
-export default uh60l;
+class uh60l {
+ static extraDelay = 0;
+ static #device_id = 26;
+ static #mode_value = 0.04;
+ static #display_value = 0.05;
+ static #delay_value = 50 + this.extraDelay;
+ static #kuKeycodes = {
+ mode: 3235,
+ display: 3236,
+ kbd: 3237,
+ left: 3238,
+ mid: 3239,
+ right: 3240,
+ f1: 3241,
+ 1: 3242,
+ 2: 3243,
+ 3: 3244,
+ tgtstr: 3245,
+ 4: 3246,
+ 5: 3247,
+ 6: 3248,
+ inc: 3249,
+ 7: 3250,
+ 8: 3251,
+ 9: 3252,
+ dec: 3253,
+ clr: 3254,
+ 0: 3255,
+ ent: 3256,
+ // letters...
+ a: [3238, 3242],
+ b: [3239, 3242],
+ c: [3240, 3242],
+ d: [3238, 3243],
+ e: [3239, 3243],
+ f: [3240, 3243],
+ g: [3238, 3244],
+ h: [3239, 3244],
+ i: [3240, 3244],
+ j: [3238, 3246],
+ k: [3239, 3246],
+ l: [3240, 3246],
+ m: [3238, 3247],
+ n: [3239, 3247],
+ o: [3240, 3247],
+ p: [3238, 3248],
+ q: [3239, 3248],
+ r: [3240, 3248],
+ s: [3238, 3250],
+ t: [3239, 3250],
+ u: [3240, 3250],
+ v: [3238, 3251],
+ w: [3239, 3251],
+ x: [3240, 3251],
+ y: [3238, 3252],
+ z: [3239, 3252],
+ "*": [3240, 3252],
+ " ": [3239, 3255],
+ };
+ static #codesPayload = [];
+
+ static #addKeyboardCode(character) {
+ const characterCode = this.#kuKeycodes[character.toLowerCase()];
+ if (typeof characterCode === "object") {
+ for (const code of characterCode) {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: code,
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ } else {
+ if (characterCode !== undefined)
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: characterCode,
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ }
+
+ static #addKeyboardValue(character, value) {
+ const characterCode = this.#kuKeycodes[character.toLowerCase()];
+ if (characterCode !== undefined && value !== undefined) {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: characterCode,
+ delay: this.#delay_value,
+ activate: value,
+ addDepress: "false",
+ });
+ }
+ }
+
+ static createButtonCommands(waypoints) {
+ this.#codesPayload = [];
+
+ // Set mode to LAT/LONG - SelectMode value 0.03
+ this.#addKeyboardValue("mode", this.#mode_value);
+
+ // Switch to waypoint page (device this.#device_id, SelectDisplay, value 0.05
+ this.#addKeyboardValue("display", this.#display_value);
+
+ for (const waypoint of waypoints) {
+ // increment waypoint
+ this.#addKeyboardCode("inc");
+
+ // kbd for input
+ this.#addKeyboardCode("kbd");
+
+ //Type name (max 13 chars on display)
+ for (let i = 0; i < 13; i++) {
+ this.#addKeyboardCode(waypoint.name.charAt(i));
+ }
+
+ // kbd
+ this.#addKeyboardCode("kbd");
+
+ //check if latitude is N or S
+ if (waypoint.latHem === "N") {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["n"][1],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["s"][1],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ //Type lat
+ for (let i = 0; i < waypoint.lat.length; i++) {
+ waypoint.lat.charAt(i) !== "." &&
+ this.#addKeyboardCode(waypoint.lat.charAt(i));
+ }
+
+ // kbd
+ this.#addKeyboardCode("kbd");
+
+ //check if longitude is E or W
+ if (waypoint.longHem === "E") {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["e"][1],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ } else {
+ this.#codesPayload.push({
+ device: this.#device_id,
+ code: this.#kuKeycodes["w"][1],
+ delay: this.#delay_value,
+ activate: 1,
+ addDepress: "true",
+ });
+ }
+ //Type long
+ for (let i = 0; i < waypoint.long.length; i++) {
+ waypoint.long.charAt(i) !== "." &&
+ this.#addKeyboardCode(waypoint.long.charAt(i));
+ }
+
+ // enter
+ this.#addKeyboardCode("ent");
+ }
+ // DEC back to MIZ0 after the INCs
+ for (let i = 0; i < waypoints.length; i++) {
+ this.#addKeyboardCode("dec");
+ }
+
+ return this.#codesPayload;
+ }
+}
+
+export default uh60l;
diff --git a/src/moduleTransfers/GetTransferCommands.js b/src/moduleTransfers/GetTransferCommands.js
new file mode 100644
index 0000000..bed8045
--- /dev/null
+++ b/src/moduleTransfers/GetTransferCommands.js
@@ -0,0 +1,61 @@
+import ah6jTransfer, { requestAbortAH6JTransfer } from "./ah6jTransfer";
+import ns430Transfer, { requestAbortNS430Transfer } from "./ns430Transfer";
+
+export default function getTransferCommands(module) {
+ switch (module) {
+ case "AH-6J":
+ case "MH-6J":
+ case "AH-6J_ADD":
+ case "AH-6J_REPLACE":
+ case "AH-6J_NEWFPLN":
+ case "MH-6J_ADD":
+ case "MH-6J_REPLACE":
+ case "MH-6J_NEWFPLN":
+ return {
+ runTransfer: ah6jTransfer,
+ requestAbort: requestAbortAH6JTransfer,
+ };
+
+ case "A-10A":
+ case "AJS37":
+ case "Bf-109K-4":
+ case "C-101CC":
+ case "C-101EB":
+ case "Christen Eagle II":
+ case "F-14B":
+ case "F-15C":
+ case "F-5E-3":
+ case "F-86F Sabre":
+ case "FW-190D9":
+ case "I-16":
+ case "JF-17":
+ case "J-11A":
+ case "L-39C":
+ case "L-39ZA":
+ case "Mi-24P":
+ case "Mi-8MT":
+ case "MiG-15bis":
+ case "MiG-21bis":
+ case "MiG-29 Fulcrum":
+ case "MiG-29A":
+ case "MiG-29G":
+ case "MiG-29S":
+ case "P-51D":
+ case "SpitfireLFMkIX":
+ case "SpitfireLFMkIXCW":
+ case "Su-25":
+ case "Su-25T":
+ case "Su-27":
+ case "Su-33":
+ case "TF-51D":
+ case "UH-1H":
+ case "Yak-52":
+ return {
+ runTransfer: ns430Transfer,
+ requestAbort: requestAbortNS430Transfer,
+ };
+
+ default:
+ return null;
+ }
+}
diff --git a/src/moduleTransfers/ah6jTransfer.js b/src/moduleTransfers/ah6jTransfer.js
new file mode 100644
index 0000000..36a94a7
--- /dev/null
+++ b/src/moduleTransfers/ah6jTransfer.js
@@ -0,0 +1,224 @@
+import ah6j from "../moduleCommands/ah6j";
+
+const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
+
+let abortRequested = false;
+
+function applyExtraDelay(commands, buttonExtraDelay) {
+ return (commands || []).map((cmd) => ({
+ ...cmd,
+ delay: (cmd.delay ?? 0) + buttonExtraDelay,
+ }));
+}
+
+function isReplaceModule(module) {
+ return /(?:_|-)REPLACE$/i.test(module || "");
+}
+
+function isNewFlightPlanModule(module) {
+ return /(?:_|-)NEWFPLN$/i.test(module || "");
+}
+
+function row1IsWP0(row1) {
+ return /\bWP0\b/i.test(String(row1 || "").trim());
+}
+
+async function readRow1(ipcRenderer) {
+ const response = await ipcRenderer.invoke("messageToDcsRequest", {
+ cmd: "GET_PARAM_HANDLES",
+ handles: ["TNL3100_ROW1"],
+ });
+
+ return response?.HandleData?.TNL3100_ROW1 || "";
+}
+
+async function readRow2(ipcRenderer) {
+ const response = await ipcRenderer.invoke("messageToDcsRequest", {
+ cmd: "GET_PARAM_HANDLES",
+ handles: ["TNL3100_ROW2"],
+ });
+
+ return response?.HandleData?.TNL3100_ROW2 || "";
+}
+
+async function sendCommands(ipcRenderer, commands) {
+ ipcRenderer.send("messageToDcs", {
+ type: "waypoints",
+ payload: commands,
+ });
+}
+
+export function requestAbortAH6JTransfer() {
+ abortRequested = true;
+}
+
+export function clearAbortAH6JTransfer() {
+ abortRequested = false;
+}
+
+function throwIfAborted() {
+ if (abortRequested) {
+ throw new Error("AH6J_TRANSFER_ABORTED");
+ }
+}
+
+function getBusy() {
+ return window.__thewayBusy === true;
+}
+
+async function waitForBusyCycle({
+ appearTimeout = 5000,
+ clearTimeout = 15000,
+ settleMs = 300,
+ pollMs = 50,
+} = {}) {
+ const appearStart = Date.now();
+
+ // wait for busy to go true
+ while (!getBusy()) {
+ throwIfAborted();
+
+ if (Date.now() - appearStart > appearTimeout) {
+ throw new Error("AH6J_BUSY_DID_NOT_GO_TRUE");
+ }
+
+ await sleep(pollMs);
+ }
+
+ const clearStart = Date.now();
+
+ // wait for busy to go false
+ while (getBusy()) {
+ throwIfAborted();
+
+ if (Date.now() - clearStart > clearTimeout) {
+ throw new Error("AH6J_BUSY_DID_NOT_CLEAR");
+ }
+
+ await sleep(pollMs);
+ }
+
+ // settle before next batch
+ await sleep(settleMs);
+ throwIfAborted();
+}
+
+async function sendCommandsAndWaitForBusy(ipcRenderer, commands) {
+ await sendCommands(ipcRenderer, commands);
+ await waitForBusyCycle();
+}
+
+async function prepareReplaceMode({ buttonExtraDelay, ipcRenderer }) {
+ let wp0SeenInARow = 0;
+
+ while (wp0SeenInARow < 3) {
+ throwIfAborted();
+
+ const row1 = await readRow1(ipcRenderer);
+
+ if (row1IsWP0(row1)) {
+ wp0SeenInARow += 1;
+ } else {
+ wp0SeenInARow = 0;
+
+ const deleteCurrent = applyExtraDelay(
+ ah6j.buildDeleteCurrentWaypointCommands(),
+ buttonExtraDelay,
+ );
+
+ await sendCommandsAndWaitForBusy(ipcRenderer, deleteCurrent);
+ throwIfAborted();
+ }
+
+ const stepPrevious = applyExtraDelay(
+ ah6j.buildStepToPreviousWaypointCommands(),
+ buttonExtraDelay,
+ );
+
+ await sendCommandsAndWaitForBusy(ipcRenderer, stepPrevious);
+ }
+}
+
+export default async function ah6jTransfer({
+ module,
+ moduleWaypoints,
+ buttonExtraDelay,
+ ipcRenderer,
+ setRunning,
+}) {
+ clearAbortAH6JTransfer();
+ setRunning(true);
+
+ try {
+ if (module) {
+ ah6j.slotVariant = module;
+ }
+
+ throwIfAborted();
+
+ const initial = applyExtraDelay(
+ ah6j.buildInitialModeCommands(),
+ buttonExtraDelay,
+ );
+
+ await sendCommandsAndWaitForBusy(ipcRenderer, initial);
+ throwIfAborted();
+
+ const newFlightPlanMode = isNewFlightPlanModule(module);
+
+ if (isReplaceModule(module) || newFlightPlanMode) {
+ await prepareReplaceMode({ buttonExtraDelay, ipcRenderer });
+ throwIfAborted();
+ }
+
+ for (let i = 0; i < moduleWaypoints.length; i++) {
+ throwIfAborted();
+
+ const openCreate = applyExtraDelay(
+ ah6j.buildWaypointEntryPageCommands(),
+ buttonExtraDelay,
+ );
+
+ await sendCommandsAndWaitForBusy(ipcRenderer, openCreate);
+ throwIfAborted();
+
+ const display = {
+ row2: await readRow2(ipcRenderer),
+ };
+
+ // keep this for now, even with busy-driven batching
+ await sleep(300 + buttonExtraDelay);
+
+ const commands = applyExtraDelay(
+ ah6j.createWaypointCommandsFromDisplay(
+ moduleWaypoints[i],
+ i + 1,
+ display,
+ ),
+ buttonExtraDelay,
+ );
+
+ await sendCommandsAndWaitForBusy(ipcRenderer, commands);
+ throwIfAborted();
+ }
+
+ if (newFlightPlanMode) {
+ throwIfAborted();
+
+ const buildFlightPlan = applyExtraDelay(
+ ah6j.buildNewFlightPlanFromListCommands(moduleWaypoints.length),
+ buttonExtraDelay,
+ );
+
+ await sendCommandsAndWaitForBusy(ipcRenderer, buildFlightPlan);
+ throwIfAborted();
+ }
+ } catch (err) {
+ if (err?.message !== "AH6J_TRANSFER_ABORTED") {
+ throw err;
+ }
+ } finally {
+ clearAbortAH6JTransfer();
+ setRunning(false);
+ }
+}
\ No newline at end of file
diff --git a/src/moduleTransfers/ns430Transfer.js b/src/moduleTransfers/ns430Transfer.js
new file mode 100644
index 0000000..765ee22
--- /dev/null
+++ b/src/moduleTransfers/ns430Transfer.js
@@ -0,0 +1,359 @@
+// ============================================================
+// NS430 TRANSFER CONTROLLER
+// Handles sending waypoint data from TheWay to the NS430
+// by generating command sequences and transmitting them to DCS
+// ============================================================
+
+import ns430 from "../moduleCommands/ns430";
+
+// ============================================================
+// BASIC UTILITY HELPERS
+// ============================================================
+
+const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
+
+let abortRequested = false;
+
+// ============================================================
+// COMMAND TIMING HELPERS
+// ============================================================
+
+function applyExtraDelay(commands, buttonExtraDelay) {
+ return (commands || []).map((cmd) => ({
+ ...cmd,
+ delay: (cmd.delay ?? 0) + buttonExtraDelay,
+ }));
+}
+
+// ============================================================
+// DCS COMMUNICATION
+// ============================================================
+
+async function sendCommands(ipcRenderer, commands) {
+ ipcRenderer.send("messageToDcs", {
+ type: "waypoints",
+ payload: commands,
+ });
+}
+
+// ============================================================
+// BUSY-DRIVEN WAIT HELPERS
+// ============================================================
+
+function getBusy() {
+ return window.__thewayBusy === true;
+}
+
+async function waitForBusyCycle({
+ appearTimeout = 5000,
+ clearTimeout = 60000,
+ settleMs = 300,
+ pollMs = 50,
+} = {}) {
+ const appearStart = Date.now();
+
+ // Wait for busy to become true
+ while (!getBusy()) {
+ throwIfAborted();
+
+ if (Date.now() - appearStart > appearTimeout) {
+ throw new Error("NS430_BUSY_DID_NOT_GO_TRUE");
+ }
+
+ await sleep(pollMs);
+ }
+
+ const clearStart = Date.now();
+
+ // Wait for busy to become false
+ while (getBusy()) {
+ throwIfAborted();
+
+ if (Date.now() - clearStart > clearTimeout) {
+ throw new Error("NS430_BUSY_DID_NOT_CLEAR");
+ }
+
+ await sleep(pollMs);
+ }
+
+ // Small settle pause before next batch
+ await sleep(settleMs);
+ throwIfAborted();
+}
+
+async function sendCommandsAndWaitForBusy(ipcRenderer, commands) {
+ await sendCommands(ipcRenderer, commands);
+ await waitForBusyCycle();
+}
+
+// ============================================================
+// WAYPOINT NAME FORMATTER
+// ============================================================
+
+function generateSequentialDefaultName(index) {
+ const num = Math.max(1, (Number(index) || 0) + 1);
+ return `WAY${String(num).padStart(2, "0")}`.slice(0, 5);
+}
+
+function isDefaultWaypointName(rawName, fallbackIndex) {
+ const trimmed = String(rawName ?? "").trim();
+
+ if (!trimmed) {
+ return true;
+ }
+
+ if (/^Waypoint\s+\d+$/i.test(trimmed)) {
+ return true;
+ }
+
+ return trimmed.toUpperCase() === generateSequentialDefaultName(fallbackIndex);
+}
+
+function formatNs430WaypointName(rawName, fallbackIndex) {
+ const trimmed = String(rawName ?? "").trim();
+
+ if (!trimmed) {
+ return generateSequentialDefaultName(fallbackIndex);
+ }
+
+ const standardWaypointMatch = /^Waypoint\s+(\d+)$/i.exec(trimmed);
+ if (standardWaypointMatch) {
+ return generateSequentialDefaultName(fallbackIndex);
+ }
+
+ let name = trimmed
+ .toUpperCase()
+ .replace(/[^A-Z0-9]/g, "")
+ .slice(0, 5);
+
+ if (!name) {
+ return generateSequentialDefaultName(fallbackIndex);
+ }
+
+ if (name.length < 5) {
+ const remaining = 5 - name.length;
+ const suffix = String(fallbackIndex + 1)
+ .padStart(remaining, "0")
+ .slice(-remaining);
+ name = (name + suffix).slice(0, 5);
+ }
+
+ return name;
+}
+
+// ============================================================
+// AIRCRAFT POSITION REQUEST
+// ============================================================
+
+async function requestOwnPositionSnapshot(ipcRenderer) {
+ const response = await ipcRenderer.invoke("messageToDcsRequest", {
+ cmd: "GET_OWN_POSITION",
+ });
+
+ if (!response || response.ok !== true) {
+ const errorList = Array.isArray(response?.ErrorList)
+ ? response.ErrorList.filter(Boolean)
+ : [];
+ const fallbackError = response?.error || response?.raw || "GET_OWN_POSITION failed";
+ throw new Error(errorList[0] || fallbackError);
+ }
+
+ const ownPosition = response.OwnPosition || {};
+ const lat = Number(ownPosition.lat);
+ const long = Number(ownPosition.long);
+ const alt = Number(ownPosition.alt ?? 0);
+
+ if (!Number.isFinite(lat) || !Number.isFinite(long)) {
+ throw new Error(`Invalid own-position snapshot received: ${JSON.stringify(response)}`);
+ }
+
+ return {
+ lat,
+ long,
+ alt,
+ model: ownPosition.model || null,
+ };
+}
+
+// ============================================================
+// TRANSFER ABORT CONTROL
+// ============================================================
+
+export function requestAbortNS430Transfer() {
+ abortRequested = true;
+}
+
+export function clearAbortNS430Transfer() {
+ abortRequested = false;
+}
+
+function throwIfAborted() {
+ if (abortRequested) {
+ throw new Error("NS430_TRANSFER_ABORTED");
+ }
+}
+
+// ============================================================
+// MAIN NS430 WAYPOINT TRANSFER FUNCTION
+// ============================================================
+
+export default async function ns430Transfer({
+ module,
+ moduleWaypoints,
+ buttonExtraDelay,
+ ipcRenderer,
+ setRunning,
+}) {
+ clearAbortNS430Transfer();
+ setRunning(true);
+
+ try {
+ if (module) {
+ ns430.slotVariant = module;
+ }
+
+ throwIfAborted();
+
+ // ========================================================
+ // INITIAL SETUP
+ // Reset NS430 state and delete existing user waypoints
+ // ========================================================
+
+ const setup = applyExtraDelay(ns430.buildSetupCommands(), buttonExtraDelay);
+ await sendCommandsAndWaitForBusy(ipcRenderer, setup);
+ throwIfAborted();
+
+
+ // ========================================================
+ // MAIN WAYPOINT ENTRY LOOP
+ // Each iteration creates one waypoint
+ // ========================================================
+
+ for (let i = 0; i < moduleWaypoints.length; i += 1) {
+ throwIfAborted();
+
+ const wp = moduleWaypoints[i];
+ const waypointName = formatNs430WaypointName(wp?.name, i);
+
+ // ----------------------------------------------------
+ // Enter waypoint name characters
+ // ----------------------------------------------------
+
+ const nameChars = applyExtraDelay(
+ ns430.buildNameCharactersCommands(waypointName),
+ buttonExtraDelay,
+ );
+ await sendCommandsAndWaitForBusy(ipcRenderer, nameChars);
+ throwIfAborted();
+
+ // ----------------------------------------------------
+ // Capture aircraft position
+ // Needed because NS430 fills coordinate fields
+ // with current aircraft position after name entry
+ // ----------------------------------------------------
+
+ const currentPosition = await requestOwnPositionSnapshot(ipcRenderer);
+ throwIfAborted();
+
+ // ----------------------------------------------------
+ // Confirm waypoint name
+ // ----------------------------------------------------
+
+ const confirmName = applyExtraDelay(
+ ns430.buildConfirmNameCommands(),
+ buttonExtraDelay,
+ );
+ await sendCommandsAndWaitForBusy(ipcRenderer, confirmName);
+ throwIfAborted();
+
+ // ----------------------------------------------------
+ // Move cursor to coordinate entry field
+ // ----------------------------------------------------
+
+ const moveToPos = applyExtraDelay(
+ ns430.buildMoveToPositionFieldCommands(),
+ buttonExtraDelay,
+ );
+ await sendCommandsAndWaitForBusy(ipcRenderer, moveToPos);
+ throwIfAborted();
+
+ // ----------------------------------------------------
+ // Adjust coordinates from current aircraft position
+ // to the target waypoint coordinates
+ // ----------------------------------------------------
+
+ const coordinateEntry = applyExtraDelay(
+ ns430.buildCoordinateEntryCommands(currentPosition, wp),
+ buttonExtraDelay,
+ );
+
+ await sendCommandsAndWaitForBusy(ipcRenderer, coordinateEntry);
+ throwIfAborted();
+ }
+
+ // ========================================================
+ // FINAL SAVE
+ // Saves the newly entered waypoints to the NS430 database
+ // ========================================================
+
+ const saveWaypoint = applyExtraDelay(
+ ns430.buildSaveWaypointCommands(),
+ buttonExtraDelay,
+ );
+ await sendCommandsAndWaitForBusy(ipcRenderer, saveWaypoint);
+ throwIfAborted();
+
+ // ========================================================
+ // CREATE FLIGHT PLAN
+ // ========================================================
+
+ const createFlightPlanStart = applyExtraDelay(
+ ns430.buildFlightPlanCreateStartCommands(),
+ buttonExtraDelay,
+ );
+ await sendCommandsAndWaitForBusy(ipcRenderer, createFlightPlanStart);
+ throwIfAborted();
+
+ // ========================================================
+ // FLIGHT PLAN WAYPOINT LOOP
+ // Add each entered waypoint name into the new FPL
+ // ========================================================
+
+ for (let i = 0; i < moduleWaypoints.length; i += 1) {
+ throwIfAborted();
+
+ const wp = moduleWaypoints[i];
+ const waypointName = formatNs430WaypointName(wp?.name, i);
+ const useFastListMethod = isDefaultWaypointName(wp?.name, i);
+
+ const addFlightPlanWaypoint = applyExtraDelay(
+ useFastListMethod
+ ? ns430.buildFlightPlanAddWaypointFromListCommands(i + 1)
+ : ns430.buildFlightPlanAddWaypointCommands(waypointName, i + 1),
+ buttonExtraDelay,
+ );
+
+ await sendCommandsAndWaitForBusy(ipcRenderer, addFlightPlanWaypoint);
+ throwIfAborted();
+ }
+
+ // ========================================================
+ // ACTIVATE FLIGHT PLAN
+ // ========================================================
+
+ const activateFlightPlan = applyExtraDelay(
+ ns430.buildFlightPlanActivateCommands(moduleWaypoints.length),
+ buttonExtraDelay,
+ );
+ await sendCommandsAndWaitForBusy(ipcRenderer, activateFlightPlan);
+ throwIfAborted();
+
+ } catch (err) {
+ if (err?.message !== "NS430_TRANSFER_ABORTED") {
+ throw err;
+ }
+ } finally {
+ clearAbortNS430Transfer();
+ setRunning(false);
+ }
+}
\ No newline at end of file
diff --git a/src/store/dcsPoint.js b/src/store/dcsPoint.js
index d7be137..525a1c3 100644
--- a/src/store/dcsPoint.js
+++ b/src/store/dcsPoint.js
@@ -1,20 +1,21 @@
-import { createSlice } from "@reduxjs/toolkit";
-
-const initialState = { module: null, lat: null, long: null, elev: null };
-
-const dcsPointSlice = createSlice({
- name: "dcsPoint",
- initialState,
- reducers: {
- changeCoords(state, action) {
- // { "model": "F-16C_50", "coords": { "lat": "41.905925545347", "long": "43.783457188399"} , "elev": "1677.6647949219"}
- state.module = action.payload.model;
- state.lat = Number(action.payload.coords.lat);
- state.long = Number(action.payload.coords.long);
- state.elev = Number(action.payload.elev);
- },
- },
-});
-
-export const dcsPointActions = dcsPointSlice.actions;
-export default dcsPointSlice.reducer;
+import { createSlice } from "@reduxjs/toolkit";
+
+const initialState = { module: null, lat: null, long: null, elev: null, busy: null };
+
+const dcsPointSlice = createSlice({
+ name: "dcsPoint",
+ initialState,
+ reducers: {
+ changeCoords(state, action) {
+ // { "model": "F-16C_50", "coords": { "lat": "41.905925545347", "long": "43.783457188399"} , "elev": "1677.6647949219", "busy": false}
+ state.module = action.payload.model;
+ state.lat = Number(action.payload.coords.lat);
+ state.long = Number(action.payload.coords.long);
+ state.elev = Number(action.payload.elev);
+ state.busy = action.payload.busy;
+ },
+ },
+});
+
+export const dcsPointActions = dcsPointSlice.actions;
+export default dcsPointSlice.reducer;
\ No newline at end of file
diff --git a/src/store/index.js b/src/store/index.js
index 2f4af31..485c560 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -1,14 +1,14 @@
-import { configureStore } from "@reduxjs/toolkit";
-import dcsReducer from "./dcsPoint";
-import uiReducer from "./ui";
-import waypointsReducer from "./waypoints";
-
-const index = configureStore({
- reducer: {
- dcsPoint: dcsReducer,
- ui: uiReducer,
- waypoints: waypointsReducer,
- },
-});
-
-export default index;
+import { configureStore } from "@reduxjs/toolkit";
+import dcsReducer from "./dcsPoint";
+import uiReducer from "./ui";
+import waypointsReducer from "./waypoints";
+
+const index = configureStore({
+ reducer: {
+ dcsPoint: dcsReducer,
+ ui: uiReducer,
+ waypoints: waypointsReducer,
+ },
+});
+
+export default index;
diff --git a/src/store/ui.js b/src/store/ui.js
index e3c07ad..9525035 100644
--- a/src/store/ui.js
+++ b/src/store/ui.js
@@ -1,29 +1,29 @@
-import { createSlice } from "@reduxjs/toolkit";
-
-const initialState = { pendingWaypoint: false, userPreferences: {} };
-
-const uiSlice = createSlice({
- name: "ui",
- initialState,
- reducers: {
- changePendingWaypoint(state, action) {
- state.pendingWaypoint = action.payload;
- },
- setSettingsPreference(state, action) {
- const { key, value } = action.payload;
- state.userPreferences[key] = value;
- },
- setModulePreference(state, action) {
- const preference = action.payload;
- const { module, option } = preference;
- state.userPreferences[module]
- ? state.userPreferences[module].push(option)
- : (state.userPreferences[module] = [option]);
- },
- setUserPreferences(state, action) {
- state.userPreferences = action.payload;
- },
- },
-});
-export const uiActions = uiSlice.actions;
-export default uiSlice.reducer;
+import { createSlice } from "@reduxjs/toolkit";
+
+const initialState = { pendingWaypoint: false, userPreferences: {} };
+
+const uiSlice = createSlice({
+ name: "ui",
+ initialState,
+ reducers: {
+ changePendingWaypoint(state, action) {
+ state.pendingWaypoint = action.payload;
+ },
+ setSettingsPreference(state, action) {
+ const { key, value } = action.payload;
+ state.userPreferences[key] = value;
+ },
+ setModulePreference(state, action) {
+ const preference = action.payload;
+ const { module, option } = preference;
+ state.userPreferences[module]
+ ? state.userPreferences[module].push(option)
+ : (state.userPreferences[module] = [option]);
+ },
+ setUserPreferences(state, action) {
+ state.userPreferences = action.payload;
+ },
+ },
+});
+export const uiActions = uiSlice.actions;
+export default uiSlice.reducer;
diff --git a/src/store/waypoints.js b/src/store/waypoints.js
index 487bcbb..10de3c2 100644
--- a/src/store/waypoints.js
+++ b/src/store/waypoints.js
@@ -3,47 +3,67 @@ import { arrayMove } from "@dnd-kit/sortable";
const initialState = { dcsWaypoints: [], idCounter: 1 };
+const normalizeImportedName = (name, fallbackId) => {
+ const raw = typeof name === "string" ? name.trim() : "";
+ if (!raw) return `Waypoint ${fallbackId}`;
+
+ const cleaned = raw.replace(/,+\s*$/, "").trim();
+ const m = cleaned.match(/^(\d+)\s+(.+)$/);
+
+ if (m) {
+ return `${m[2].trim()} ${m[1]}`;
+ }
+
+ return cleaned;
+};
+
const waypointsSlice = createSlice({
name: "waypoints",
initialState,
reducers: {
addDcsWaypoint(state, action) {
- const payload = action.payload;
+ const payload = action.payload ?? {};
state.dcsWaypoints.push({
id: state.idCounter,
- name: `Waypoint ${state.idCounter}`,
+ name: normalizeImportedName(payload.name, state.idCounter),
lat: payload.lat,
long: payload.long,
elev: payload.elev,
});
state.idCounter++;
},
+
changeName(state, action) {
const index = state.dcsWaypoints.findIndex(
(i) => i.id === action.payload.id,
);
state.dcsWaypoints[index]["name"] = action.payload.name;
},
+
changeElevation(state, action) {
const index = state.dcsWaypoints.findIndex(
(i) => i.id === action.payload.id,
);
state.dcsWaypoints[index]["elev"] = action.payload.elev;
},
+
delete(state, action) {
const index = state.dcsWaypoints.findIndex(
(i) => i.id === action.payload,
);
state.dcsWaypoints.splice(index, 1);
},
+
deleteAll(state) {
state.dcsWaypoints = [];
state.idCounter = 1;
},
+
deleteLast(state) {
state.dcsWaypoints.pop();
},
+
changeOrder(state, action) {
const oldIndex = state.dcsWaypoints.findIndex(
(i) => i.id === action.payload.over,
@@ -53,11 +73,12 @@ const waypointsSlice = createSlice({
);
state.dcsWaypoints = arrayMove(state.dcsWaypoints, newIndex, oldIndex);
},
+
appendWaypoints(state, action) {
for (const waypoint of action.payload) {
state.dcsWaypoints.push({
id: state.idCounter,
- name: waypoint.name,
+ name: normalizeImportedName(waypoint?.name, state.idCounter),
lat: waypoint.lat,
long: waypoint.long,
elev: waypoint.elev,
@@ -67,5 +88,6 @@ const waypointsSlice = createSlice({
},
},
});
+
export const waypointsActions = waypointsSlice.actions;
export default waypointsSlice.reducer;
diff --git a/src/theme/TheWayTheme.js b/src/theme/TheWayTheme.js
index 2ab50cd..65fc796 100644
--- a/src/theme/TheWayTheme.js
+++ b/src/theme/TheWayTheme.js
@@ -1,20 +1,20 @@
-import { green, grey, red } from "@mui/material/colors";
-
-const theWayTheme = {
- palette: {
- mode: "dark",
- primary: green,
- secondary: red,
- neutral: { main: grey[800] },
- background: {
- default: grey[900],
- paper: grey[900],
- },
- },
- typography: {
- fontSize: 12,
- },
- // spacing: 6,
-};
-
-export default theWayTheme;
+import { green, grey, red } from "@mui/material/colors";
+
+const theWayTheme = {
+ palette: {
+ mode: "dark",
+ primary: green,
+ secondary: red,
+ neutral: { main: grey[800] },
+ background: {
+ default: grey[900],
+ paper: grey[900],
+ },
+ },
+ typography: {
+ fontSize: 12,
+ },
+ // spacing: 6,
+};
+
+export default theWayTheme;
diff --git a/src/utils/ConvertModuleWaypoints.js b/src/utils/ConvertModuleWaypoints.js
index 08346dd..7865fdf 100644
--- a/src/utils/ConvertModuleWaypoints.js
+++ b/src/utils/ConvertModuleWaypoints.js
@@ -1,7 +1,46 @@
+//v2
import Convertors from "./Convertors";
const convert = (dcsWaypoints, module) => {
switch (module) {
+ case "AH-6J":
+ case "MH-6J": {
+ // lat 00.00.000 DMM
+ // long 000.00.000
+ // AH/MH-6J also carries explicit hemisphere fields for TNL3100 entry
+ let waypoints = [];
+ for (const dcsWaypoint of dcsWaypoints) {
+ const name = dcsWaypoint.name;
+ const id = dcsWaypoint.id;
+ const dmmLat = Convertors.decimalToDMM(dcsWaypoint.lat);
+ const dmmLong = Convertors.decimalToDMM(dcsWaypoint.long);
+ const lat =
+ dmmLat.deg.toString().padStart(2, "0") +
+ "." +
+ dmmLat.min.toFixed(3).toString().padStart(6, "0");
+ const long =
+ dmmLong.deg.toString().padStart(3, "0") +
+ "." +
+ dmmLong.min.toFixed(3).toString().padStart(6, "0");
+ const elev = Math.trunc(Convertors.mToF(dcsWaypoint.elev)).toString();
+ const latHem = dcsWaypoint.lat >= 0 ? "N" : "S";
+ const longHem = dcsWaypoint.long >= 0 ? "E" : "W";
+
+ waypoints.push({
+ name,
+ id,
+ lat,
+ long,
+ elev,
+ latHem,
+ longHem,
+ latHemi: latHem,
+ longHemi: longHem,
+ });
+ }
+ return waypoints;
+ }
+
default:
case "F-15ESE":
case "F-16C_50":
@@ -15,12 +54,14 @@ const convert = (dcsWaypoints, module) => {
case "A-10C_2":
case "A-10C":
case "Hercules":
- case "M-2000C": {
+ case "M-2000C":
+ case "AV8BNA":
+ case "CH-47Fbl1":
+ case "C-130J-30": {
// lat 00.00.000 DMM
- //long 000.00.000
+ // long 000.00.000
let waypoints = [];
for (const dcsWaypoint of dcsWaypoints) {
-
const name = dcsWaypoint.name;
const id = dcsWaypoint.id;
const dmmLat = Convertors.decimalToDMM(dcsWaypoint.lat);
@@ -48,10 +89,44 @@ const convert = (dcsWaypoints, module) => {
}
return waypoints;
}
- case "AH-64D_BLK_II":
- case "UH-60L": {
- // lat 00.00.00 DMM
- //long 000.00.00
+
+
+ case "A-10A":
+ case "AJS37":
+ case "Bf-109K-4":
+ case "C-101CC":
+ case "C-101EB":
+ case "Christen Eagle II":
+ case "F-14B":
+ case "F-15C":
+ case "F-5E-3":
+ case "F-86F Sabre":
+ case "FW-190D9":
+ case "I-16":
+ case "JF-17":
+ case "J-11A":
+ case "L-39C":
+ case "L-39ZA":
+ case "Mi-24P":
+ case "Mi-8MT":
+ case "MiG-15bis":
+ case "MiG-21bis":
+ case "MiG-29 Fulcrum":
+ case "MiG-29A":
+ case "MiG-29G":
+ case "MiG-29S":
+ case "P-51D":
+ case "SpitfireLFMkIX":
+ case "SpitfireLFMkIXCW":
+ case "Su-25":
+ case "Su-25T":
+ case "Su-27":
+ case "Su-33":
+ case "TF-51D":
+ case "UH-1H":
+ case "Yak-52": {
+ // lat 00.00 DMM
+ // long 000.00 DMM
let waypoints = [];
for (const dcsWaypoint of dcsWaypoints) {
const name = dcsWaypoint.name;
@@ -67,8 +142,9 @@ const convert = (dcsWaypoints, module) => {
"." +
dmmLong.min.toFixed(2).toString().padStart(5, "0");
const elev = Math.trunc(Convertors.mToF(dcsWaypoint.elev)).toString();
- const latHem = dcsWaypoint.lat > 0 ? "N" : "S";
- const longHem = dcsWaypoint.long > 0 ? "E" : "W";
+ const latHem = dcsWaypoint.lat >= 0 ? "N" : "S";
+ const longHem = dcsWaypoint.long >= 0 ? "E" : "W";
+
waypoints.push({
name,
id,
@@ -77,31 +153,32 @@ const convert = (dcsWaypoints, module) => {
elev,
latHem,
longHem,
+ latHemi: latHem,
+ longHemi: longHem,
});
}
return waypoints;
}
- case "AV8BNA": {
- // lat 00.00.00 DMS
+
+ case "AH-64D_BLK_II":
+ case "UH-60L":
+ case "UH-60L_DAP": {
+ // lat 00.00.00 DMM
// long 000.00.00
let waypoints = [];
for (const dcsWaypoint of dcsWaypoints) {
const name = dcsWaypoint.name;
const id = dcsWaypoint.id;
- const dmsLat = Convertors.decimalToDMS(dcsWaypoint.lat);
- const dmsLong = Convertors.decimalToDMS(dcsWaypoint.long);
+ const dmmLat = Convertors.decimalToDMM(dcsWaypoint.lat);
+ const dmmLong = Convertors.decimalToDMM(dcsWaypoint.long);
const lat =
- dmsLat.deg.toString().padStart(2, "0") +
- "." +
- dmsLat.min.toString().padStart(2, "0") +
+ dmmLat.deg.toString().padStart(2, "0") +
"." +
- dmsLat.sec.toString().padStart(2, "0");
+ dmmLat.min.toFixed(2).toString().padStart(5, "0");
const long =
- dmsLong.deg.toString().padStart(3, "0") +
- "." +
- dmsLong.min.toString().padStart(2, "0") +
+ dmmLong.deg.toString().padStart(3, "0") +
"." +
- dmsLong.sec.toString().padStart(2, "0");
+ dmmLong.min.toFixed(2).toString().padStart(5, "0");
const elev = Math.trunc(Convertors.mToF(dcsWaypoint.elev)).toString();
const latHem = dcsWaypoint.lat > 0 ? "N" : "S";
const longHem = dcsWaypoint.long > 0 ? "E" : "W";
@@ -117,11 +194,15 @@ const convert = (dcsWaypoints, module) => {
}
return waypoints;
}
+
case "Mirage-F1EE":
case "Ka-50":
- case "Ka-50_3": {
+ case "Ka-50_3":
+ case "SA342L":
+ case "SA342M":
+ case "SA342Minigun": {
// lat 00.00.0 DMM
- //long 000.00.0
+ // long 000.00.0
let waypoints = [];
for (const dcsWaypoint of dcsWaypoints) {
const name = dcsWaypoint.name;
@@ -156,12 +237,13 @@ const convert = (dcsWaypoints, module) => {
}
return waypoints;
}
+
case "FA-18C_hornet":
case "FA-18E":
case "FA-18F":
case "EA-18G": {
// lat 00.00.0000 DMM
- //long 000.00.0000
+ // long 000.00.0000
let waypoints = [];
for (const dcsWaypoint of dcsWaypoints) {
const name = dcsWaypoint.name;
@@ -191,6 +273,7 @@ const convert = (dcsWaypoints, module) => {
}
return waypoints;
}
+
case "OH58D": {
let waypoints = [];
for (const dcsWaypoint of dcsWaypoints) {
diff --git a/src/utils/ConvertModuleWaypoints.test.js b/src/utils/ConvertModuleWaypoints.test.js
index b27546e..aa974e7 100644
--- a/src/utils/ConvertModuleWaypoints.test.js
+++ b/src/utils/ConvertModuleWaypoints.test.js
@@ -1,151 +1,151 @@
-import ConvertModuleWaypoints from "./ConvertModuleWaypoints";
-
-let dcsWaypoints;
-beforeAll(() => {
- dcsWaypoints = [
- {
- id: 1,
- name: "W1",
- lat: 43.999756838301,
- long: 40.143417541493,
- elev: 735.44140625,
- },
- {
- id: 2,
- name: "W2",
- lat: 44.0000001,
- long: 40.0000001,
- elev: 735.44140625,
- },
- { id: 3, name: "W3", lat: -59.09332, long: 46.91491, elev: 735.44140625 },
- { id: 4, name: "W4", lat: 55.78044, long: 117.05243, elev: 735.44140625 },
- ];
-});
-describe("converting DMM coordinates", () => {
- test("00.00.000", function () {
- const converted = ConvertModuleWaypoints(dcsWaypoints, "F-16C_50");
- const expected = [
- {
- id: 1,
- name: "W1",
- lat: "43.59.985",
- latHem: "N",
- long: "040.08.605",
- longHem: "E",
- elev: "2412",
- },
- {
- id: 2,
- name: "W2",
- lat: "44.00.000",
- latHem: "N",
- long: "040.00.000",
- longHem: "E",
- elev: "2412",
- },
- {
- id: 3,
- name: "W3",
- lat: "59.05.599",
- latHem: "S",
- long: "046.54.895",
- longHem: "E",
- elev: "2412",
- },
- {
- id: 4,
- name: "W4",
- lat: "55.46.826",
- latHem: "N",
- long: "117.03.146",
- longHem: "E",
- elev: "2412",
- },
- ];
- expect(converted).toEqual(expected);
- });
- test("00.00.00", function () {
- const converted = ConvertModuleWaypoints(dcsWaypoints, "AH-64D_BLK_II");
- const expected = [
- {
- id: 1,
- name: "W1",
- lat: "43.59.99",
- latHem: "N",
- long: "040.08.61",
- longHem: "E",
- elev: "2412",
- },
- {
- id: 2,
- name: "W2",
- lat: "44.00.00",
- latHem: "N",
- long: "040.00.00",
- longHem: "E",
- elev: "2412",
- },
- {
- id: 3,
- name: "W3",
- lat: "59.05.60",
- latHem: "S",
- long: "046.54.89",
- longHem: "E",
- elev: "2412",
- },
- {
- id: 4,
- name: "W4",
- lat: "55.46.83",
- latHem: "N",
- long: "117.03.15",
- longHem: "E",
- elev: "2412",
- },
- ];
- expect(converted).toEqual(expected);
- });
- test("00.00.0", function () {
- const converted = ConvertModuleWaypoints(dcsWaypoints, "Ka-50");
- const expected = [
- {
- id: 1,
- name: "W1",
- lat: "44.00.0",
- latHem: "N",
- long: "040.08.6",
- longHem: "E",
- elev: "2412",
- },
- {
- id: 2,
- name: "W2",
- lat: "44.00.0",
- latHem: "N",
- long: "040.00.0",
- longHem: "E",
- elev: "2412",
- },
- {
- id: 3,
- name: "W3",
- lat: "59.05.6",
- latHem: "S",
- long: "046.54.9",
- longHem: "E",
- elev: "2412",
- },
- {
- id: 4,
- name: "W4",
- lat: "55.46.8",
- latHem: "N",
- long: "117.03.1",
- longHem: "E",
- elev: "2412",
- },
- ];
- expect(converted).toEqual(expected);
- });
-});
+import ConvertModuleWaypoints from "./ConvertModuleWaypoints";
+
+let dcsWaypoints;
+beforeAll(() => {
+ dcsWaypoints = [
+ {
+ id: 1,
+ name: "W1",
+ lat: 43.999756838301,
+ long: 40.143417541493,
+ elev: 735.44140625,
+ },
+ {
+ id: 2,
+ name: "W2",
+ lat: 44.0000001,
+ long: 40.0000001,
+ elev: 735.44140625,
+ },
+ { id: 3, name: "W3", lat: -59.09332, long: 46.91491, elev: 735.44140625 },
+ { id: 4, name: "W4", lat: 55.78044, long: 117.05243, elev: 735.44140625 },
+ ];
+});
+describe("converting DMM coordinates", () => {
+ test("00.00.000", function () {
+ const converted = ConvertModuleWaypoints(dcsWaypoints, "F-16C_50");
+ const expected = [
+ {
+ id: 1,
+ name: "W1",
+ lat: "43.59.985",
+ latHem: "N",
+ long: "040.08.605",
+ longHem: "E",
+ elev: "2412",
+ },
+ {
+ id: 2,
+ name: "W2",
+ lat: "44.00.000",
+ latHem: "N",
+ long: "040.00.000",
+ longHem: "E",
+ elev: "2412",
+ },
+ {
+ id: 3,
+ name: "W3",
+ lat: "59.05.599",
+ latHem: "S",
+ long: "046.54.895",
+ longHem: "E",
+ elev: "2412",
+ },
+ {
+ id: 4,
+ name: "W4",
+ lat: "55.46.826",
+ latHem: "N",
+ long: "117.03.146",
+ longHem: "E",
+ elev: "2412",
+ },
+ ];
+ expect(converted).toEqual(expected);
+ });
+ test("00.00.00", function () {
+ const converted = ConvertModuleWaypoints(dcsWaypoints, "AH-64D_BLK_II");
+ const expected = [
+ {
+ id: 1,
+ name: "W1",
+ lat: "43.59.99",
+ latHem: "N",
+ long: "040.08.61",
+ longHem: "E",
+ elev: "2412",
+ },
+ {
+ id: 2,
+ name: "W2",
+ lat: "44.00.00",
+ latHem: "N",
+ long: "040.00.00",
+ longHem: "E",
+ elev: "2412",
+ },
+ {
+ id: 3,
+ name: "W3",
+ lat: "59.05.60",
+ latHem: "S",
+ long: "046.54.89",
+ longHem: "E",
+ elev: "2412",
+ },
+ {
+ id: 4,
+ name: "W4",
+ lat: "55.46.83",
+ latHem: "N",
+ long: "117.03.15",
+ longHem: "E",
+ elev: "2412",
+ },
+ ];
+ expect(converted).toEqual(expected);
+ });
+ test("00.00.0", function () {
+ const converted = ConvertModuleWaypoints(dcsWaypoints, "Ka-50");
+ const expected = [
+ {
+ id: 1,
+ name: "W1",
+ lat: "44.00.0",
+ latHem: "N",
+ long: "040.08.6",
+ longHem: "E",
+ elev: "2412",
+ },
+ {
+ id: 2,
+ name: "W2",
+ lat: "44.00.0",
+ latHem: "N",
+ long: "040.00.0",
+ longHem: "E",
+ elev: "2412",
+ },
+ {
+ id: 3,
+ name: "W3",
+ lat: "59.05.6",
+ latHem: "S",
+ long: "046.54.9",
+ longHem: "E",
+ elev: "2412",
+ },
+ {
+ id: 4,
+ name: "W4",
+ lat: "55.46.8",
+ latHem: "N",
+ long: "117.03.1",
+ longHem: "E",
+ elev: "2412",
+ },
+ ];
+ expect(converted).toEqual(expected);
+ });
+});
diff --git a/src/utils/Convertors.js b/src/utils/Convertors.js
index a924578..ac1df88 100644
--- a/src/utils/Convertors.js
+++ b/src/utils/Convertors.js
@@ -1,93 +1,93 @@
-class Convertors {
- static decimalToDMM(decimalCoordinate) {
- const deg = Math.abs(Math.trunc(decimalCoordinate));
- const min = Number("0." + decimalCoordinate.toString().split(".")[1]) * 60;
- return { deg, min };
- }
-
- static decimalToDMS(decimalCoordinate) {
- const deg = Math.trunc(decimalCoordinate);
- const minDec =
- Number("0." + decimalCoordinate.toString().split(".")[1]) * 60;
- const min = Math.trunc(minDec);
- const sec = Math.trunc(Number("0." + minDec.toString().split(".")[1]) * 60);
- return { deg, min, sec };
- }
-
- static mToF(m) {
- return m * 3.280839895;
- }
-
- static fToM(f) {
- return f / 3.280839895;
- }
-
- static decimalToMGRS(Lat, Long) {
- // source: https://stackoverflow.com/questions/46728319/how-to-convert-between-lat-long-and-mgrs-using-javascript-without-dependence-on
- if (Lat < -80) return "Too far South";
- if (Lat > 84) return "Too far North";
- const c = 1 + Math.floor((Long + 180) / 6);
- const e = c * 6 - 183;
- const k = (Lat * Math.PI) / 180;
- const l = (Long * Math.PI) / 180;
- const m = (e * Math.PI) / 180;
- const n = Math.cos(k);
- const o = 0.006739496819936062 * Math.pow(n, 2);
- const p = 40680631590769 / (6356752.314 * Math.sqrt(1 + o));
- const q = Math.tan(k);
- const r = q * q;
- const t = l - m;
- const u = 1.0 - r + o;
- const v = 5.0 - r + 9 * o + 4.0 * (o * o);
- const w = 5.0 - 18.0 * r + r * r + 14.0 * o - 58.0 * r * o;
- const x = 61.0 - 58.0 * r + r * r + 270.0 * o - 330.0 * r * o;
- const y = 61.0 - 479.0 * r + 179.0 * (r * r) - r * r * r;
- const z = 1385.0 - 3111.0 * r + 543.0 * (r * r) - r * r * r;
- let aa =
- p * n * t +
- (p / 6.0) * Math.pow(n, 3) * u * Math.pow(t, 3) +
- (p / 120.0) * Math.pow(n, 5) * w * Math.pow(t, 5) +
- (p / 5040.0) * Math.pow(n, 7) * y * Math.pow(t, 7);
- let ab =
- 6367449.14570093 *
- (k -
- 0.00251882794504 * Math.sin(2 * k) +
- 0.00000264354112 * Math.sin(4 * k) -
- 0.00000000345262 * Math.sin(6 * k) +
- 0.000000000004892 * Math.sin(8 * k)) +
- (q / 2.0) * p * Math.pow(n, 2) * Math.pow(t, 2) +
- (q / 24.0) * p * Math.pow(n, 4) * v * Math.pow(t, 4) +
- (q / 720.0) * p * Math.pow(n, 6) * x * Math.pow(t, 6) +
- (q / 40320.0) * p * Math.pow(n, 8) * z * Math.pow(t, 8);
- aa = aa * 0.9996 + 500000.0;
- ab = ab * 0.9996;
- if (ab < 0.0) ab += 10000000.0;
- const ad = "CDEFGHJKLMNPQRSTUVWXX".charAt(Math.floor(Lat / 8 + 10));
- const ae = Math.floor(aa / 100000);
- const af = ["ABCDEFGH", "JKLMNPQR", "STUVWXYZ"][(c - 1) % 3].charAt(ae - 1);
- const ag = Math.floor(ab / 100000) % 20;
- const ah = ["ABCDEFGHJKLMNPQRSTUV", "FGHJKLMNPQRSTUVABCDE"][
- (c - 1) % 2
- ].charAt(ag);
-
- function pad(val) {
- if (val < 10) {
- val = "0000" + val;
- } else if (val < 100) {
- val = "000" + val;
- } else if (val < 1000) {
- val = "00" + val;
- } else if (val < 10000) {
- val = "0" + val;
- }
- return val;
- }
- aa = Math.floor(aa % 100000);
- aa = pad(aa);
- ab = Math.floor(ab % 100000);
- ab = pad(ab);
- return c + ad + " " + af + ah + " " + aa + " " + ab;
- }
-}
-
-export default Convertors;
+class Convertors {
+ static decimalToDMM(decimalCoordinate) {
+ const deg = Math.abs(Math.trunc(decimalCoordinate));
+ const min = Number("0." + decimalCoordinate.toString().split(".")[1]) * 60;
+ return { deg, min };
+ }
+
+ static decimalToDMS(decimalCoordinate) {
+ const deg = Math.trunc(decimalCoordinate);
+ const minDec =
+ Number("0." + decimalCoordinate.toString().split(".")[1]) * 60;
+ const min = Math.trunc(minDec);
+ const sec = Math.trunc(Number("0." + minDec.toString().split(".")[1]) * 60);
+ return { deg, min, sec };
+ }
+
+ static mToF(m) {
+ return m * 3.280839895;
+ }
+
+ static fToM(f) {
+ return f / 3.280839895;
+ }
+
+ static decimalToMGRS(Lat, Long) {
+ // source: https://stackoverflow.com/questions/46728319/how-to-convert-between-lat-long-and-mgrs-using-javascript-without-dependence-on
+ if (Lat < -80) return "Too far South";
+ if (Lat > 84) return "Too far North";
+ const c = 1 + Math.floor((Long + 180) / 6);
+ const e = c * 6 - 183;
+ const k = (Lat * Math.PI) / 180;
+ const l = (Long * Math.PI) / 180;
+ const m = (e * Math.PI) / 180;
+ const n = Math.cos(k);
+ const o = 0.006739496819936062 * Math.pow(n, 2);
+ const p = 40680631590769 / (6356752.314 * Math.sqrt(1 + o));
+ const q = Math.tan(k);
+ const r = q * q;
+ const t = l - m;
+ const u = 1.0 - r + o;
+ const v = 5.0 - r + 9 * o + 4.0 * (o * o);
+ const w = 5.0 - 18.0 * r + r * r + 14.0 * o - 58.0 * r * o;
+ const x = 61.0 - 58.0 * r + r * r + 270.0 * o - 330.0 * r * o;
+ const y = 61.0 - 479.0 * r + 179.0 * (r * r) - r * r * r;
+ const z = 1385.0 - 3111.0 * r + 543.0 * (r * r) - r * r * r;
+ let aa =
+ p * n * t +
+ (p / 6.0) * Math.pow(n, 3) * u * Math.pow(t, 3) +
+ (p / 120.0) * Math.pow(n, 5) * w * Math.pow(t, 5) +
+ (p / 5040.0) * Math.pow(n, 7) * y * Math.pow(t, 7);
+ let ab =
+ 6367449.14570093 *
+ (k -
+ 0.00251882794504 * Math.sin(2 * k) +
+ 0.00000264354112 * Math.sin(4 * k) -
+ 0.00000000345262 * Math.sin(6 * k) +
+ 0.000000000004892 * Math.sin(8 * k)) +
+ (q / 2.0) * p * Math.pow(n, 2) * Math.pow(t, 2) +
+ (q / 24.0) * p * Math.pow(n, 4) * v * Math.pow(t, 4) +
+ (q / 720.0) * p * Math.pow(n, 6) * x * Math.pow(t, 6) +
+ (q / 40320.0) * p * Math.pow(n, 8) * z * Math.pow(t, 8);
+ aa = aa * 0.9996 + 500000.0;
+ ab = ab * 0.9996;
+ if (ab < 0.0) ab += 10000000.0;
+ const ad = "CDEFGHJKLMNPQRSTUVWXX".charAt(Math.floor(Lat / 8 + 10));
+ const ae = Math.floor(aa / 100000);
+ const af = ["ABCDEFGH", "JKLMNPQR", "STUVWXYZ"][(c - 1) % 3].charAt(ae - 1);
+ const ag = Math.floor(ab / 100000) % 20;
+ const ah = ["ABCDEFGHJKLMNPQRSTUV", "FGHJKLMNPQRSTUVABCDE"][
+ (c - 1) % 2
+ ].charAt(ag);
+
+ function pad(val) {
+ if (val < 10) {
+ val = "0000" + val;
+ } else if (val < 100) {
+ val = "000" + val;
+ } else if (val < 1000) {
+ val = "00" + val;
+ } else if (val < 10000) {
+ val = "0" + val;
+ }
+ return val;
+ }
+ aa = Math.floor(aa % 100000);
+ aa = pad(aa);
+ ab = Math.floor(ab % 100000);
+ ab = pad(ab);
+ return c + ad + " " + af + ah + " " + aa + " " + ab;
+ }
+}
+
+export default Convertors;
diff --git a/src/utils/constants.js b/src/utils/constants.js
index cd9d5b3..6daf296 100644
--- a/src/utils/constants.js
+++ b/src/utils/constants.js
@@ -1,29 +1,14 @@
-export const inputMethods = ["F10 Map", "From a file"];
+export const inputMethods = ["F10 Map", "Recon Request", "From a file"];
+
export const supportedModules = [
- "Spectator",
- "F-15ESE",
- "F-16C_50",
- //IDF project mods
- "F-16I",
- "F-16D_50",
- "F-16D_50_NS",
- "F-16D_52",
- "F-16D_52_NS",
- "F-16D_Barak_30",
- "F-16D_Barak_40",
- "FA-18C_hornet",
- "FA-18E",
- "FA-18F",
- "EA-18G",
- "AH-64D_BLK_II",
- "A-10C_2",
- "A-10C",
- "M-2000C",
- "AV8BNA",
- "Ka-50",
- "Ka-50_3",
- "Mirage-F1EE",
- "UH-60L",
- "Hercules",
- "OH58D",
+ "Spectator","F-15ESE","F-16C_50",
+ "F-16I","F-16D_50","F-16D_50_NS","F-16D_52","F-16D_52_NS","F-16D_Barak_30","F-16D_Barak_40",
+ "FA-18C_hornet","FA-18E","FA-18F","EA-18G",
+ "AH-64D_BLK_II","A-10C_2","A-10C","M-2000C","AV8BNA","Ka-50","Ka-50_3","Mirage-F1EE",
+ "UH-60L","UH-60L_DAP","Hercules","OH58D","CH-47Fbl1","SA342L","SA342M","SA342Minigun","C-130J-30",
+ "AH-6J","MH-6J",
+ "A-10A","AJS37","Bf-109K-4","C-101CC","C-101EB","Christen Eagle II","F-14B","F-15C","F-5E-3",
+ "F-86F Sabre","FW-190D9","I-16","JF-17","J-11A","L-39C","L-39ZA","Mi-24P","Mi-8MT","MiG-15bis",
+ "MiG-21bis","MiG-29 Fulcrum","MiG-29A","MiG-29G","MiG-29S","P-51D","SpitfireLFMkIX",
+ "SpitfireLFMkIXCW","Su-25","Su-25T","Su-27","Su-33","TF-51D","UH-1H","Yak-52"
];
diff --git a/src/utils/savePreferences.js b/src/utils/savePreferences.js
index 6c50ac2..b8be05f 100644
--- a/src/utils/savePreferences.js
+++ b/src/utils/savePreferences.js
@@ -1,7 +1,7 @@
-const { ipcRenderer } = window.require("electron");
-export const saveModulePreferences = (newPreferences) => {
- ipcRenderer.send("saveModulePreferences", newPreferences);
-};
-export const saveSettingsPreferences = (newPreferences) => {
- ipcRenderer.send("saveSettingsPreferences", newPreferences);
-};
+const { ipcRenderer } = window.require("electron");
+export const saveModulePreferences = (newPreferences) => {
+ ipcRenderer.send("saveModulePreferences", newPreferences);
+};
+export const saveSettingsPreferences = (newPreferences) => {
+ ipcRenderer.send("saveSettingsPreferences", newPreferences);
+};
diff --git a/src/utils/savePreferences.test.js b/src/utils/savePreferences.test.js
index 3625f4e..ad0b1fd 100644
--- a/src/utils/savePreferences.test.js
+++ b/src/utils/savePreferences.test.js
@@ -1,24 +1,24 @@
-import saveModulePreferences from "./savePreferences";
-
-const { ipcRenderer } = require("electron");
-
-jest.mock(
- "electron",
- () => {
- return { ipcRenderer: { send: jest.fn() } };
- },
- { virtual: true },
-);
-
-describe.skip("saveUserPreferences", () => {
- it("should send data to electron", () => {
- saveModulePreferences({ hideDialogs: true });
- expect(ipcRenderer.send).toBeCalledWith("savePreferences", {
- hideDialogs: true,
- });
- saveModulePreferences({ hideDialogs: false });
- expect(ipcRenderer.send).toBeCalledWith("savePreferences", {
- hideDialogs: false,
- });
- });
-});
+import saveModulePreferences from "./savePreferences";
+
+const { ipcRenderer } = require("electron");
+
+jest.mock(
+ "electron",
+ () => {
+ return { ipcRenderer: { send: jest.fn() } };
+ },
+ { virtual: true },
+);
+
+describe.skip("saveUserPreferences", () => {
+ it("should send data to electron", () => {
+ saveModulePreferences({ hideDialogs: true });
+ expect(ipcRenderer.send).toBeCalledWith("savePreferences", {
+ hideDialogs: true,
+ });
+ saveModulePreferences({ hideDialogs: false });
+ expect(ipcRenderer.send).toBeCalledWith("savePreferences", {
+ hideDialogs: false,
+ });
+ });
+});