feat(alarm state): refactor to separe alarm state from radar state. Kown issue: alarm state is not correctly mantained

This commit is contained in:
MarcoJayUsai
2025-03-24 18:42:04 +01:00
parent f7e9fc5cbc
commit 1622d663bb
15 changed files with 174 additions and 253 deletions

View File

@@ -48,9 +48,9 @@ namespace ROE {
namespace ALARM_STATE { namespace ALARM_STATE {
enum ALARM_STATEs { enum ALARM_STATEs {
AUTO = 0, AUTO = 2,
GREEN = 1, GREEN = 1,
RED = 2, RED = 0,
}; };
} }

View File

@@ -8,6 +8,7 @@ namespace DataIndex {
category, category,
alive, alive,
alarmState, alarmState,
radarState,
human, human,
controlled, controlled,
coalition, coalition,

View File

@@ -98,7 +98,7 @@ public:
virtual void setTargetID(unsigned int newValue) { updateValue(targetID, newValue, DataIndex::targetID); } virtual void setTargetID(unsigned int newValue) { updateValue(targetID, newValue, DataIndex::targetID); }
virtual void setTargetPosition(Coords newValue) { updateValue(targetPosition, newValue, DataIndex::targetPosition); } virtual void setTargetPosition(Coords newValue) { updateValue(targetPosition, newValue, DataIndex::targetPosition); }
virtual void setROE(unsigned char newValue, bool force = false); virtual void setROE(unsigned char newValue, bool force = false);
virtual void commandAlarmState(unsigned char newValue, bool force = false); virtual void setAlarmState(unsigned char newValue, bool force = false);
virtual void setReactionToThreat(unsigned char newValue, bool force = false); virtual void setReactionToThreat(unsigned char newValue, bool force = false);
virtual void setEmissionsCountermeasures(unsigned char newValue, bool force = false); virtual void setEmissionsCountermeasures(unsigned char newValue, bool force = false);
virtual void setTACAN(DataTypes::TACAN newValue, bool force = false); virtual void setTACAN(DataTypes::TACAN newValue, bool force = false);
@@ -126,12 +126,12 @@ public:
virtual void setTargetingRange(double newValue) { updateValue(targetingRange, newValue, DataIndex::targetingRange); } virtual void setTargetingRange(double newValue) { updateValue(targetingRange, newValue, DataIndex::targetingRange); }
virtual void setAimMethodRange(double newValue) { updateValue(aimMethodRange, newValue, DataIndex::aimMethodRange); } virtual void setAimMethodRange(double newValue) { updateValue(aimMethodRange, newValue, DataIndex::aimMethodRange); }
virtual void setAcquisitionRange(double newValue) { updateValue(acquisitionRange, newValue, DataIndex::acquisitionRange); } virtual void setAcquisitionRange(double newValue) { updateValue(acquisitionRange, newValue, DataIndex::acquisitionRange); }
virtual void setAlarmState(string newValue) { updateValue(alarmState, newValue, DataIndex::alarmState); } virtual void setRadarState(bool newValue) { updateValue(radarState, newValue, DataIndex::radarState); }
/********** Getters **********/ /********** Getters **********/
virtual string getCategory() { return category; }; virtual string getCategory() { return category; };
virtual bool getAlive() { return alive; } virtual bool getAlive() { return alive; }
virtual string getAlarmState() { return alarmState; } virtual unsigned char getAlarmState() { return alarmState; }
virtual bool getHuman() { return human; } virtual bool getHuman() { return human; }
virtual bool getControlled() { return controlled; } virtual bool getControlled() { return controlled; }
virtual unsigned char getCoalition() { return coalition; } virtual unsigned char getCoalition() { return coalition; }
@@ -190,6 +190,7 @@ public:
virtual double getTargetingRange() { return targetingRange; } virtual double getTargetingRange() { return targetingRange; }
virtual double getAimMethodRange() { return aimMethodRange; } virtual double getAimMethodRange() { return aimMethodRange; }
virtual double getAcquisitionRange() { return acquisitionRange; } virtual double getAcquisitionRange() { return acquisitionRange; }
virtual bool getRadarState() { return radarState; }
protected: protected:
unsigned int ID; unsigned int ID;
@@ -205,7 +206,8 @@ protected:
string callsign = ""; string callsign = "";
string groupName = ""; string groupName = "";
unsigned char state = State::NONE; unsigned char state = State::NONE;
string alarmState = ""; unsigned char alarmState = ALARM_STATE::AUTO;
bool radarState = false;
string task = ""; string task = "";
bool hasTask = false; bool hasTask = false;
Coords position = Coords(NULL); Coords position = Coords(NULL);

View File

@@ -609,7 +609,7 @@ string GroundUnit::aimAtPoint(Coords aimTarget) {
Geodesic::WGS84().Direct(position.lat, position.lng, bearing1, r, lat, lng); Geodesic::WGS84().Direct(position.lat, position.lng, bearing1, r, lat, lng);
taskString = +"Barrel elevation: " + to_string((int) round(barrelElevation)) + "m, bearing: " + to_string((int) round(bearing1)) + "deg"; taskString = +"Barrel elevation: " + to_string((int) round(barrelElevation)) + "m, bearing: " + to_string((int) round(bearing1)) + "deg";
log(unitName + "(" + name + ")" + " shooting with aim at point method. Barrel elevation: " + to_string(barrelElevation) + "m, bearing: " + to_string(bearing1) + "°"); log(unitName + "(" + name + ")" + " shooting with aim at point method. Barrel elevation: " + to_string(barrelElevation) + "m, bearing: " + to_string(bearing1) + "<EFBFBD>");
std::ostringstream taskSS; std::ostringstream taskSS;
taskSS.precision(10); taskSS.precision(10);

View File

@@ -406,17 +406,17 @@ void Scheduler::handleRequest(string key, json::value value, string username, js
log(username + " set unit " + unit->getUnitName() + "(" + unit->getName() + ") ROE to " + to_string(ROE), true); log(username + " set unit " + unit->getUnitName() + "(" + unit->getName() + ") ROE to " + to_string(ROE), true);
} }
} }
else if (key.compare("commandAlarmState") == 0) else if (key.compare("setAlarmState") == 0)
{ {
unsigned int ID = value[L"ID"].as_integer(); unsigned int ID = value[L"ID"].as_integer();
unitsManager->acquireControl(ID); unitsManager->acquireControl(ID);
Unit* unit = unitsManager->getGroupLeader(ID); Unit* unit = unitsManager->getGroupLeader(ID);
if (unit != nullptr) { if (unit != nullptr) {
unsigned char alarmState = value[L"alarmState"].as_integer(); unsigned char alarmState = value[L"alarmState"].as_integer();
unit->commandAlarmState(alarmState); unit->setAlarmState(alarmState);
log(username + " set unit " + unit->getUnitName() + "(" + unit->getName() + ") alarm state to " + to_string(alarmState), true); log(username + " set unit " + unit->getUnitName() + "(" + unit->getName() + ") alarm state to " + to_string(alarmState), true);
} else { } else {
log("Error while setting commandAlarmState. Unit does not exist."); log("Error while setting setAlarmState. Unit does not exist.");
} }
} }
/************************/ /************************/

View File

@@ -18,7 +18,7 @@ extern UnitsManager* unitsManager;
Unit::Unit(json::value json, unsigned int ID) : Unit::Unit(json::value json, unsigned int ID) :
ID(ID) ID(ID)
{ {
log("Creating unit with ID: " + to_string(ID)); // log("Creating unit with ID: " + to_string(ID));
} }
Unit::~Unit() Unit::~Unit()
@@ -83,8 +83,8 @@ void Unit::update(json::value json, double dt)
if (json.has_boolean_field(L"isAlive")) if (json.has_boolean_field(L"isAlive"))
setAlive(json[L"isAlive"].as_bool()); setAlive(json[L"isAlive"].as_bool());
if (json.has_string_field(L"alarmState")) { if (json.has_boolean_field(L"radarState")) {
setAlarmState(to_string(json[L"alarmState"])); setRadarState(json[L"radarState"].as_bool());
} }
if (json.has_boolean_field(L"isHuman")) if (json.has_boolean_field(L"isHuman"))
@@ -154,7 +154,7 @@ void Unit::update(json::value json, double dt)
void Unit::setDefaults(bool force) void Unit::setDefaults(bool force)
{ {
setAlarmState(ALARM_STATE::AUTO, force);
} }
void Unit::runAILoop() { void Unit::runAILoop() {
@@ -256,7 +256,8 @@ void Unit::getData(stringstream& ss, unsigned long long time)
switch (datumIndex) { switch (datumIndex) {
case DataIndex::category: appendString(ss, datumIndex, category); break; case DataIndex::category: appendString(ss, datumIndex, category); break;
case DataIndex::alive: appendNumeric(ss, datumIndex, alive); break; case DataIndex::alive: appendNumeric(ss, datumIndex, alive); break;
case DataIndex::alarmState: appendString(ss, datumIndex, alarmState); break; case DataIndex::alarmState: appendNumeric(ss, datumIndex, alarmState); break;
case DataIndex::radarState: appendNumeric(ss, datumIndex, radarState); break;
case DataIndex::human: appendNumeric(ss, datumIndex, human); break; case DataIndex::human: appendNumeric(ss, datumIndex, human); break;
case DataIndex::controlled: appendNumeric(ss, datumIndex, controlled); break; case DataIndex::controlled: appendNumeric(ss, datumIndex, controlled); break;
case DataIndex::coalition: appendNumeric(ss, datumIndex, coalition); break; case DataIndex::coalition: appendNumeric(ss, datumIndex, coalition); break;
@@ -473,7 +474,7 @@ void Unit::setROE(unsigned char newROE, bool force)
} }
} }
void Unit::commandAlarmState(unsigned char newAlarmState, bool force) void Unit::setAlarmState(unsigned char newAlarmState, bool force)
{ {
Command* command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::ALARM_STATE, static_cast<unsigned int>(newAlarmState))); Command* command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::ALARM_STATE, static_cast<unsigned int>(newAlarmState)));
scheduler->appendCommand(command); scheduler->appendCommand(command);

View File

@@ -97,7 +97,6 @@ export const states: string[] = [
]; ];
export const ROEs: string[] = ["free", "designated", "", "return", "hold"]; export const ROEs: string[] = ["free", "designated", "", "return", "hold"];
export const alarmStates: string[] = ["auto", "green", "red"];
export const reactionsToThreat: string[] = ["none", "manoeuvre", "passive", "evade"]; export const reactionsToThreat: string[] = ["none", "manoeuvre", "passive", "evade"];
export const emissionsCountermeasures: string[] = ["silent", "attack", "defend", "free"]; export const emissionsCountermeasures: string[] = ["silent", "attack", "defend", "free"];
@@ -453,6 +452,7 @@ export enum DataIndexes {
category, category,
alive, alive,
alarmState, alarmState,
radarState,
human, human,
controlled, controlled,
coalition, coalition,

View File

@@ -220,7 +220,7 @@ export interface UnitData {
markerCategory: string; markerCategory: string;
ID: number; ID: number;
alive: boolean; alive: boolean;
alarmState: AlarmState | undefined; alarmState: AlarmState;
human: boolean; human: boolean;
controlled: boolean; controlled: boolean;
coalition: string; coalition: string;
@@ -417,7 +417,7 @@ export interface Drawing {
} }
export enum AlarmState { export enum AlarmState {
AUTO = 'auto', RED = 'red',
GREEN = 'green', GREEN = 'green',
RED = 'red' AUTO = 'auto'
} }

View File

@@ -660,7 +660,7 @@
} }
/* Unit Radar State */ /* Unit Radar State */
.unit-radar-state { .unit-alarm-state {
height: 10px; height: 10px;
width: 10px; width: 10px;
position: absolute; position: absolute;
@@ -670,12 +670,12 @@
left: 35px; left: 35px;
bottom: 8px; bottom: 8px;
} }
.unit[data-radar-state="green"] .unit-radar-state { .unit[data-alarm-state="green"] .unit-alarm-state {
border: 1px solid white; border: 1px solid white;
background: rgb(0, 226, 0); background: rgb(0, 226, 0);
} }
.unit[data-radar-state="red"] .unit-radar-state { .unit[data-alarm-state="red"] .unit-alarm-state {
border: 1px solid white; border: 1px solid white;
background: red; background: red;
} }

View File

@@ -1,7 +1,7 @@
import { Circle, LatLng, Polygon } from "leaflet"; import { Circle, LatLng, Polygon } from "leaflet";
import * as turf from "@turf/turf"; import * as turf from "@turf/turf";
import { ROEs, emissionsCountermeasures, reactionsToThreat, states } from "../constants/constants"; import { ROEs, emissionsCountermeasures, reactionsToThreat, states } from "../constants/constants";
import { DateAndTime } from "../interfaces"; import { AlarmState, DateAndTime } from "../interfaces";
import { Converter } from "usng"; import { Converter } from "usng";
import { MGRS } from "../types/types"; import { MGRS } from "../types/types";
import { featureCollection } from "turf"; import { featureCollection } from "turf";
@@ -275,6 +275,19 @@ export function enumToROE(ROE: number) {
else return ROEs[0]; else return ROEs[0];
} }
export function enumToAlarmState(alarmState: number) {
switch (alarmState) {
case 0:
return AlarmState.RED;
case 1:
return AlarmState.GREEN;
case 2:
return AlarmState.AUTO;
default:
return AlarmState.AUTO;
}
}
export function convertROE(idx: number) { export function convertROE(idx: number) {
let roe = 0; let roe = 0;
if (idx === 0) roe = 4; if (idx === 0) roe = 4;

View File

@@ -421,7 +421,7 @@ export class ServerManager {
setAlarmState(ID: number, alarmState: number, callback: CallableFunction = () => {}) { setAlarmState(ID: number, alarmState: number, callback: CallableFunction = () => {}) {
var command = { ID: ID, alarmState: alarmState }; var command = { ID: ID, alarmState: alarmState };
var data = { commandAlarmState: command }; var data = { setAlarmState: command };
this.PUT(data, callback); this.PUT(data, callback);
} }

View File

@@ -132,7 +132,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
const [activeRadioSettings, setActiveRadioSettings] = useState(null as null | { radio: Radio; TACAN: TACAN }); const [activeRadioSettings, setActiveRadioSettings] = useState(null as null | { radio: Radio; TACAN: TACAN });
const [activeAdvancedSettings, setActiveAdvancedSettings] = useState(null as null | GeneralSettings); const [activeAdvancedSettings, setActiveAdvancedSettings] = useState(null as null | GeneralSettings);
const [lastUpdateTime, setLastUpdateTime] = useState(0); const [lastUpdateTime, setLastUpdateTime] = useState(0);
const [showScenicModes, setShowScenicModes] = useState(true); const [showScenicModes, setShowScenicModes] = useState(false);
const [showEngagementSettings, setShowEngagementSettings] = useState(false); const [showEngagementSettings, setShowEngagementSettings] = useState(false);
const [barrelHeight, setBarrelHeight] = useState(0); const [barrelHeight, setBarrelHeight] = useState(0);
const [muzzleVelocity, setMuzzleVelocity] = useState(0); const [muzzleVelocity, setMuzzleVelocity] = useState(0);
@@ -420,9 +420,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
<td className="flex gap-2 text-lg text-gray-200"> <td className="flex gap-2 text-lg text-gray-200">
<FontAwesomeIcon icon={entry[1][0] as IconDefinition} />{" "} <FontAwesomeIcon icon={entry[1][0] as IconDefinition} />{" "}
<div <div
className={` className={`text-sm text-gray-400`}
text-sm text-gray-400
`}
> >
{entry[1][1] as string} {entry[1][1] as string}
</div> </div>
@@ -802,9 +800,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
{" "} {" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsRoeHold} icon={olButtonsRoeHold}
className={` className={`my-auto min-w-8 text-white`}
my-auto min-w-8 text-white
`}
/>{" "} />{" "}
Hold fire: The unit will not shoot in any circumstance Hold fire: The unit will not shoot in any circumstance
</div> </div>
@@ -812,9 +808,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
{" "} {" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsRoeReturn} icon={olButtonsRoeReturn}
className={` className={`my-auto min-w-8 text-white`}
my-auto min-w-8 text-white
`}
/>{" "} />{" "}
Return fire: The unit will not fire unless fired upon Return fire: The unit will not fire unless fired upon
</div> </div>
@@ -822,17 +816,13 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
{" "} {" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsRoeDesignated} icon={olButtonsRoeDesignated}
className={` className={`my-auto min-w-8 text-white`}
my-auto min-w-8 text-white
`}
/>{" "} />{" "}
<div> <div>
{" "} {" "}
Fire on target: The unit will not fire unless fired upon{" "} Fire on target: The unit will not fire unless fired upon{" "}
<p <p
className={` className={`inline font-bold`}
inline font-bold
`}
> >
or or
</p>{" "} </p>{" "}
@@ -843,9 +833,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
{" "} {" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsRoeFree} icon={olButtonsRoeFree}
className={` className={`my-auto min-w-8 text-white`}
my-auto min-w-8 text-white
`}
/>{" "} />{" "}
Free: The unit will fire at any detected enemy in range Free: The unit will fire at any detected enemy in range
</div> </div>
@@ -853,25 +841,19 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
<div className="flex gap-4"> <div className="flex gap-4">
<div className="my-auto"> <div className="my-auto">
<FaExclamationCircle <FaExclamationCircle
className={` className={`animate-bounce text-xl`}
animate-bounce text-xl
`}
/> />
</div> </div>
<div> <div>
Currently, DCS blue and red ground units do not respect{" "} Currently, DCS blue and red ground units do not respect{" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsRoeReturn} icon={olButtonsRoeReturn}
className={` className={`my-auto text-white`}
my-auto text-white
`}
/>{" "} />{" "}
and{" "} and{" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsRoeDesignated} icon={olButtonsRoeDesignated}
className={` className={`my-auto text-white`}
my-auto text-white
`}
/>{" "} />{" "}
rules of engagement, so be careful, they may start shooting when you don't want them to. Use neutral units for finer rules of engagement, so be careful, they may start shooting when you don't want them to. Use neutral units for finer
control. control.
@@ -957,6 +939,18 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
tooltipRelativeToParent={true} tooltipRelativeToParent={true}
> >
{[olButtonsRoeHold, olButtonsRoeReturn, olButtonsRoeDesignated].map((icon, idx) => { {[olButtonsRoeHold, olButtonsRoeReturn, olButtonsRoeDesignated].map((icon, idx) => {
const getAlarmStateByIdx = (idx) => {
switch (idx) {
case 0:
return AlarmState.AUTO;
case 1:
return AlarmState.GREEN;
case 2:
return AlarmState.RED;
}
}
return ( return (
<OlButtonGroupItem <OlButtonGroupItem
key={idx} key={idx}
@@ -966,11 +960,11 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
.setAlarmState(idx, null, () => .setAlarmState(idx, null, () =>
setForcedUnitsData({ setForcedUnitsData({
...forcedUnitsData, ...forcedUnitsData,
alarmState: Object.values(AlarmState)[idx], alarmState: getAlarmStateByIdx(idx),
}) })
); );
}} }}
active={selectedUnitsData.alarmState === alarmStates[idx]} active={selectedUnitsData.alarmState === getAlarmStateByIdx(idx)}
icon={icon} icon={icon}
/> />
); );
@@ -1007,9 +1001,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
{" "} {" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsThreatNone} icon={olButtonsThreatNone}
className={` className={`my-auto min-w-8 text-white`}
my-auto min-w-8 text-white
`}
/>{" "} />{" "}
No reaction: The unit will not react in any circumstance No reaction: The unit will not react in any circumstance
</div> </div>
@@ -1017,9 +1009,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
{" "} {" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsThreatPassive} icon={olButtonsThreatPassive}
className={` className={`my-auto min-w-8 text-white`}
my-auto min-w-8 text-white
`}
/>{" "} />{" "}
Passive: The unit will use counter-measures, but will not alter its course Passive: The unit will use counter-measures, but will not alter its course
</div> </div>
@@ -1027,9 +1017,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
{" "} {" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsThreatManoeuvre} icon={olButtonsThreatManoeuvre}
className={` className={`my-auto min-w-8 text-white`}
my-auto min-w-8 text-white
`}
/>{" "} />{" "}
Manouevre: The unit will try to evade the threat using manoeuvres, but no counter-measures Manouevre: The unit will try to evade the threat using manoeuvres, but no counter-measures
</div> </div>
@@ -1037,9 +1025,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
{" "} {" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsThreatEvade} icon={olButtonsThreatEvade}
className={` className={`my-auto min-w-8 text-white`}
my-auto min-w-8 text-white
`}
/>{" "} />{" "}
Full evasion: the unit will try to evade the threat both manoeuvering and using counter-measures Full evasion: the unit will try to evade the threat both manoeuvering and using counter-measures
</div> </div>
@@ -1094,9 +1080,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
{" "} {" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsEmissionsSilent} icon={olButtonsEmissionsSilent}
className={` className={`my-auto min-w-8 text-white`}
my-auto min-w-8 text-white
`}
/>{" "} />{" "}
Radio silence: No radar or ECM will be used Radio silence: No radar or ECM will be used
</div> </div>
@@ -1104,9 +1088,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
{" "} {" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsEmissionsDefend} icon={olButtonsEmissionsDefend}
className={` className={`my-auto min-w-8 text-white`}
my-auto min-w-8 text-white
`}
/>{" "} />{" "}
Defensive: The unit will turn radar and ECM on only when threatened Defensive: The unit will turn radar and ECM on only when threatened
</div> </div>
@@ -1114,9 +1096,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
{" "} {" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsEmissionsAttack} icon={olButtonsEmissionsAttack}
className={` className={`my-auto min-w-8 text-white`}
my-auto min-w-8 text-white
`}
/>{" "} />{" "}
Attack: The unit will use radar and ECM when engaging other units Attack: The unit will use radar and ECM when engaging other units
</div> </div>
@@ -1124,9 +1104,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
{" "} {" "}
<FontAwesomeIcon <FontAwesomeIcon
icon={olButtonsEmissionsFree} icon={olButtonsEmissionsFree}
className={` className={`my-auto min-w-8 text-white`}
my-auto min-w-8 text-white
`}
/>{" "} />{" "}
Free: the unit will use the radar and ECM all the time Free: the unit will use the radar and ECM all the time
</div> </div>
@@ -1347,9 +1325,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
<div className="flex gap-4"> <div className="flex gap-4">
<div className="my-auto"> <div className="my-auto">
<FaExclamationCircle <FaExclamationCircle
className={` className={`animate-bounce text-xl`}
animate-bounce text-xl
`}
/> />
</div> </div>
<div> <div>
@@ -1529,9 +1505,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
{/* ============== Operate as toggle START ============== */} {/* ============== Operate as toggle START ============== */}
{selectedUnits.every((unit) => unit.getCoalition() === "neutral") && ( {selectedUnits.every((unit) => unit.getCoalition() === "neutral") && (
<div <div
className={` className={`flex content-center justify-between`}
flex content-center justify-between
`}
> >
<span <span
className={` className={`
@@ -1573,17 +1547,13 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
> >
<div className="flex align-center gap-2"> <div className="flex align-center gap-2">
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
Barrel height:{" "} Barrel height:{" "}
</div> </div>
<OlNumberInput <OlNumberInput
decimalPlaces={1} decimalPlaces={1}
className={` className={`ml-auto`}
ml-auto
`}
value={barrelHeight} value={barrelHeight}
min={0} min={0}
max={100} max={100}
@@ -1598,26 +1568,20 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
}} }}
></OlNumberInput> ></OlNumberInput>
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
m m
</div> </div>
</div> </div>
<div className="flex align-center gap-2"> <div className="flex align-center gap-2">
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
Muzzle velocity:{" "} Muzzle velocity:{" "}
</div> </div>
<OlNumberInput <OlNumberInput
decimalPlaces={0} decimalPlaces={0}
className={` className={`ml-auto`}
ml-auto
`}
value={muzzleVelocity} value={muzzleVelocity}
min={0} min={0}
max={10000} max={10000}
@@ -1632,26 +1596,20 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
}} }}
></OlNumberInput> ></OlNumberInput>
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
m/s m/s
</div> </div>
</div> </div>
<div className="flex align-center gap-2"> <div className="flex align-center gap-2">
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
Aim time:{" "} Aim time:{" "}
</div> </div>
<OlNumberInput <OlNumberInput
decimalPlaces={2} decimalPlaces={2}
className={` className={`ml-auto`}
ml-auto
`}
value={aimTime} value={aimTime}
min={0} min={0}
max={100} max={100}
@@ -1666,25 +1624,19 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
}} }}
></OlNumberInput> ></OlNumberInput>
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
s s
</div> </div>
</div> </div>
<div className="flex align-center gap-2"> <div className="flex align-center gap-2">
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
Shots to fire:{" "} Shots to fire:{" "}
</div> </div>
<OlNumberInput <OlNumberInput
className={` className={`ml-auto`}
ml-auto
`}
value={shotsToFire} value={shotsToFire}
min={0} min={0}
max={100} max={100}
@@ -1701,17 +1653,13 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
</div> </div>
<div className="flex align-center gap-2"> <div className="flex align-center gap-2">
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
Shots base interval:{" "} Shots base interval:{" "}
</div> </div>
<OlNumberInput <OlNumberInput
decimalPlaces={2} decimalPlaces={2}
className={` className={`ml-auto`}
ml-auto
`}
value={shotsBaseInterval} value={shotsBaseInterval}
min={0} min={0}
max={100} max={100}
@@ -1726,26 +1674,20 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
}} }}
></OlNumberInput> ></OlNumberInput>
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
s s
</div> </div>
</div> </div>
<div className="flex align-center gap-2"> <div className="flex align-center gap-2">
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
Shots base scatter:{" "} Shots base scatter:{" "}
</div> </div>
<OlNumberInput <OlNumberInput
decimalPlaces={2} decimalPlaces={2}
className={` className={`ml-auto`}
ml-auto
`}
value={shotsBaseScatter} value={shotsBaseScatter}
min={0} min={0}
max={50} max={50}
@@ -1760,25 +1702,19 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
}} }}
></OlNumberInput> ></OlNumberInput>
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
deg deg
</div> </div>
</div> </div>
<div className="flex align-center gap-2"> <div className="flex align-center gap-2">
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
Engagement range:{" "} Engagement range:{" "}
</div> </div>
<OlNumberInput <OlNumberInput
className={` className={`ml-auto`}
ml-auto
`}
value={engagementRange} value={engagementRange}
min={0} min={0}
max={100000} max={100000}
@@ -1793,25 +1729,19 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
}} }}
></OlNumberInput> ></OlNumberInput>
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
m m
</div> </div>
</div> </div>
<div className="flex align-center gap-2"> <div className="flex align-center gap-2">
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
Targeting range:{" "} Targeting range:{" "}
</div> </div>
<OlNumberInput <OlNumberInput
className={` className={`ml-auto`}
ml-auto
`}
value={targetingRange} value={targetingRange}
min={0} min={0}
max={100000} max={100000}
@@ -1826,25 +1756,19 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
}} }}
></OlNumberInput> ></OlNumberInput>
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
m m
</div> </div>
</div> </div>
<div className="flex align-center gap-2"> <div className="flex align-center gap-2">
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
Aim method range:{" "} Aim method range:{" "}
</div> </div>
<OlNumberInput <OlNumberInput
className={` className={`ml-auto`}
ml-auto
`}
value={aimMethodRange} value={aimMethodRange}
min={0} min={0}
max={100000} max={100000}
@@ -1859,25 +1783,19 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
}} }}
></OlNumberInput> ></OlNumberInput>
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
m m
</div> </div>
</div> </div>
<div className="flex align-center gap-2"> <div className="flex align-center gap-2">
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
Acquisition range:{" "} Acquisition range:{" "}
</div> </div>
<OlNumberInput <OlNumberInput
className={` className={`ml-auto`}
ml-auto
`}
value={acquisitionRange} value={acquisitionRange}
min={0} min={0}
max={100000} max={100000}
@@ -1892,9 +1810,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
}} }}
></OlNumberInput> ></OlNumberInput>
<div <div
className={` className={`my-auto`}
my-auto
`}
> >
m m
</div> </div>

View File

@@ -18,6 +18,7 @@ import {
computeBearingRangeString, computeBearingRangeString,
adjustBrightness, adjustBrightness,
bearingAndDistanceToLatLng, bearingAndDistanceToLatLng,
enumToAlarmState,
} from "../other/utils"; } from "../other/utils";
import { CustomMarker } from "../map/markers/custommarker"; import { CustomMarker } from "../map/markers/custommarker";
import { SVGInjector } from "@tanem/svg-injector"; import { SVGInjector } from "@tanem/svg-injector";
@@ -84,7 +85,8 @@ export abstract class Unit extends CustomMarker {
/* Data controlled directly by the backend. No setters are provided to avoid misalignments */ /* Data controlled directly by the backend. No setters are provided to avoid misalignments */
#alive: boolean = false; #alive: boolean = false;
#alarmState: AlarmState | undefined = undefined; #alarmState: AlarmState = AlarmState.AUTO;
#radarState: boolean | undefined = undefined;
#human: boolean = false; #human: boolean = false;
#controlled: boolean = false; #controlled: boolean = false;
#coalition: string = "neutral"; #coalition: string = "neutral";
@@ -352,6 +354,9 @@ export abstract class Unit extends CustomMarker {
getAlarmState() { getAlarmState() {
return this.#alarmState; return this.#alarmState;
} }
getRadarState() {
return this.#radarState;
}
getTimeToNextTasking() { getTimeToNextTasking() {
return this.#timeToNextTasking; return this.#timeToNextTasking;
} }
@@ -541,6 +546,7 @@ export abstract class Unit extends CustomMarker {
var datumIndex = 0; var datumIndex = 0;
while (datumIndex != DataIndexes.endOfData) { while (datumIndex != DataIndexes.endOfData) {
datumIndex = dataExtractor.extractUInt8(); datumIndex = dataExtractor.extractUInt8();
switch (datumIndex) { switch (datumIndex) {
case DataIndexes.category: case DataIndexes.category:
dataExtractor.extractString(); dataExtractor.extractString();
@@ -549,20 +555,8 @@ export abstract class Unit extends CustomMarker {
this.setAlive(dataExtractor.extractBool()); this.setAlive(dataExtractor.extractBool());
updateMarker = true; updateMarker = true;
break; break;
case DataIndexes.alarmState: case DataIndexes.radarState:
let stringAlarmState = dataExtractor.extractString(); this.#radarState = dataExtractor.extractBool();
switch (stringAlarmState) {
case 'RED':
this.setAlarmState(AlarmState.RED);
break;
case 'GREEN':
this.setAlarmState(AlarmState.GREEN);
break;
case '':
this.setAlarmState(AlarmState.AUTO);
default:
break;
}
updateMarker = true; updateMarker = true;
break; break;
case DataIndexes.human: case DataIndexes.human:
@@ -670,6 +664,9 @@ export abstract class Unit extends CustomMarker {
case DataIndexes.ROE: case DataIndexes.ROE:
this.#ROE = enumToROE(dataExtractor.extractUInt8()); this.#ROE = enumToROE(dataExtractor.extractUInt8());
break; break;
case DataIndexes.alarmState:
this.#alarmState = enumToAlarmState(dataExtractor.extractUInt8());
break;
case DataIndexes.reactionToThreat: case DataIndexes.reactionToThreat:
this.#reactionToThreat = enumToReactionToThreat(dataExtractor.extractUInt8()); this.#reactionToThreat = enumToReactionToThreat(dataExtractor.extractUInt8());
break; break;
@@ -890,10 +887,9 @@ export abstract class Unit extends CustomMarker {
} }
} }
setAlarmState(newAlarmState: AlarmState) { setRadarState(newRadarState: boolean) {
if (newAlarmState != this.#alarmState) { if (newRadarState != this.#radarState) {
this.#alarmState = newAlarmState; this.#radarState = newRadarState;
console.log('---- alarm state updated: ', this.#alarmState);
this.#updateMarker(); this.#updateMarker();
} }
} }
@@ -1436,7 +1432,7 @@ export abstract class Unit extends CustomMarker {
if (!this.#human) getApp().getServerManager().setROE(this.ID, ROE); if (!this.#human) getApp().getServerManager().setROE(this.ID, ROE);
} }
commandAlarmState(alarmState: number) { setAlarmState(alarmState: number) {
if (!this.#human) getApp().getServerManager().setAlarmState(this.ID, alarmState); if (!this.#human) getApp().getServerManager().setAlarmState(this.ID, alarmState);
} }
@@ -1755,7 +1751,7 @@ export abstract class Unit extends CustomMarker {
} }
if (this.getHidden()) return; // We won't draw the marker if the unit is hidden if (this.getHidden()) return; // We won't draw the marker if the unit is hidden
/* Draw the marker */ /* Draw the marker */
if (this.getLatLng().lat !== this.#position.lat || this.getLatLng().lng !== this.#position.lng) { if (this.getLatLng().lat !== this.#position.lat || this.getLatLng().lng !== this.#position.lng) {
this.setLatLng(new LatLng(this.#position.lat, this.#position.lng)); this.setLatLng(new LatLng(this.#position.lat, this.#position.lng));
@@ -1777,8 +1773,9 @@ export abstract class Unit extends CustomMarker {
/* Set dead/alive flag */ /* Set dead/alive flag */
element.querySelector(".unit")?.toggleAttribute("data-is-dead", !this.#alive); element.querySelector(".unit")?.toggleAttribute("data-is-dead", !this.#alive);
/* Set RED/GREEN state*/ /* Set radar state*/
if (this.#alarmState) element.querySelector(".unit")?.setAttribute("data-radar-state", this.#alarmState); // if (this.#radarState !== undefined) element.querySelector(".unit")?.setAttribute("data-radar-state", (this.#radarState === true ? 'on' : 'off'));
if (this.#alarmState !== AlarmState.AUTO) element.querySelector(".unit")?.setAttribute("data-alarm-state", (this.#alarmState === AlarmState.RED ? 'red' : 'green'));
/* Set current unit state */ /* Set current unit state */
if (this.#human) { if (this.#human) {
@@ -1836,28 +1833,28 @@ export abstract class Unit extends CustomMarker {
if (hasFox3 != newHasFox3) element.querySelector(".unit")?.toggleAttribute("data-has-fox-3", newHasFox3); if (hasFox3 != newHasFox3) element.querySelector(".unit")?.toggleAttribute("data-has-fox-3", newHasFox3);
if (hasOtherAmmo != newHasOtherAmmo) element.querySelector(".unit")?.toggleAttribute("data-has-other-ammo", newHasOtherAmmo); if (hasOtherAmmo != newHasOtherAmmo) element.querySelector(".unit")?.toggleAttribute("data-has-other-ammo", newHasOtherAmmo);
/* Draw the hotgroup element */ /* Draw the hotgroup element */
element.querySelector(".unit")?.toggleAttribute("data-is-in-hotgroup", this.#hotgroup != null); element.querySelector(".unit")?.toggleAttribute("data-is-in-hotgroup", this.#hotgroup != null);
if (this.#hotgroup) { if (this.#hotgroup) {
const hotgroupEl = element.querySelector(".unit-hotgroup-id") as HTMLElement; const hotgroupEl = element.querySelector(".unit-hotgroup-id") as HTMLElement;
if (hotgroupEl) hotgroupEl.innerText = String(this.#hotgroup); if (hotgroupEl) hotgroupEl.innerText = String(this.#hotgroup);
} }
/* Draw the cluster element */ /* Draw the cluster element */
element element
.querySelector(".unit") .querySelector(".unit")
?.toggleAttribute( ?.toggleAttribute(
"data-is-cluster-leader", "data-is-cluster-leader",
this.#isClusterLeader && this.#isClusterLeader &&
this.#clusterUnits.length > 1 && this.#clusterUnits.length > 1 &&
getApp().getMap().getOptions().clusterGroundUnits && getApp().getMap().getOptions().clusterGroundUnits &&
getApp().getMap().getZoom() < CLUSTERING_ZOOM_TRANSITION && getApp().getMap().getZoom() < CLUSTERING_ZOOM_TRANSITION &&
!this.getSelected() !this.getSelected()
); );
if (this.#isClusterLeader && this.#clusterUnits.length > 1) { if (this.#isClusterLeader && this.#clusterUnits.length > 1) {
const clusterEl = element.querySelector(".unit-cluster-id") as HTMLElement; const clusterEl = element.querySelector(".unit-cluster-id") as HTMLElement;
if (clusterEl) clusterEl.innerText = String(this.#clusterUnits.length); if (clusterEl) clusterEl.innerText = String(this.#clusterUnits.length);
} }
/* Set bullseyes positions */ /* Set bullseyes positions */
const bullseyes = getApp().getMissionManager().getBullseyes(); const bullseyes = getApp().getMissionManager().getBullseyes();

View File

@@ -1,36 +1,14 @@
import { DomEvent, DomUtil, LatLng, LatLngBounds } from "leaflet"; import * as turf from "@turf/turf";
import { getApp } from "../olympusapp"; import { LatLng, LatLngBounds } from "leaflet";
import { AirUnit, GroundUnit, NavyUnit, Unit } from "./unit";
import {
areaContains,
bearingAndDistanceToLatLng,
deepCopyTable,
deg2rad,
getGroundElevation,
latLngToMercator,
mToFt,
mercatorToLatLng,
msToKnots,
} from "../other/utils";
import { CoalitionPolygon } from "../map/coalitionarea/coalitionpolygon";
import { import {
BLUE_COMMANDER, BLUE_COMMANDER,
DELETE_CYCLE_TIME,
DELETE_SLOW_THRESHOLD,
DataIndexes, DataIndexes,
GAME_MASTER, GAME_MASTER,
IADSDensities, IADSDensities,
OlympusState, OlympusState,
RED_COMMANDER, RED_COMMANDER,
UnitControlSubState, alarmStates, UnitControlSubState
} from "../constants/constants"; } from "../constants/constants";
import { DataExtractor } from "../server/dataextractor";
import { citiesDatabase } from "./databases/citiesdatabase";
import { TemporaryUnitMarker } from "../map/markers/temporaryunitmarker";
import { AlarmState, Contact, GeneralSettings, Radio, TACAN, UnitBlueprint, UnitData, UnitSpawnTable } from "../interfaces";
import { Group } from "./group";
import { CoalitionCircle } from "../map/coalitionarea/coalitioncircle";
import { ContextActionSet } from "./contextactionset";
import { import {
AWACSReferenceChangedEvent, AWACSReferenceChangedEvent,
CommandModeOptionsChangedEvent, CommandModeOptionsChangedEvent,
@@ -46,11 +24,30 @@ import {
UnitsRefreshedEvent, UnitsRefreshedEvent,
UnitsUpdatedEvent, UnitsUpdatedEvent,
} from "../events"; } from "../events";
import { UnitDatabase } from "./databases/unitdatabase"; import { Contact, GeneralSettings, Radio, TACAN, UnitBlueprint, UnitData, UnitSpawnTable } from "../interfaces";
import * as turf from "@turf/turf"; import { CoalitionCircle } from "../map/coalitionarea/coalitioncircle";
import { CoalitionPolygon } from "../map/coalitionarea/coalitionpolygon";
import { PathMarker } from "../map/markers/pathmarker"; import { PathMarker } from "../map/markers/pathmarker";
import { TemporaryUnitMarker } from "../map/markers/temporaryunitmarker";
import { getApp } from "../olympusapp";
import {
areaContains,
bearingAndDistanceToLatLng,
deepCopyTable,
deg2rad,
getGroundElevation,
latLngToMercator,
mToFt,
mercatorToLatLng,
msToKnots,
} from "../other/utils";
import { DataExtractor } from "../server/dataextractor";
import { Coalition } from "../types/types"; import { Coalition } from "../types/types";
import { ClusterMarker } from "../map/markers/clustermarker"; import { ContextActionSet } from "./contextactionset";
import { citiesDatabase } from "./databases/citiesdatabase";
import { UnitDatabase } from "./databases/unitdatabase";
import { Group } from "./group";
import { AirUnit, GroundUnit, NavyUnit, Unit } from "./unit";
/** The UnitsManager handles the creation, update, and control of units. Data is strictly updated by the server ONLY. This means that any interaction from the user will always and only /** The UnitsManager handles the creation, update, and control of units. Data is strictly updated by the server ONLY. This means that any interaction from the user will always and only
* result in a command to the server, executed by means of a REST PUT request. Any subsequent change in data will be reflected only when the new data is sent back by the server. This strategy allows * result in a command to the server, executed by means of a REST PUT request. Any subsequent change in data will be reflected only when the new data is sent back by the server. This strategy allows
@@ -787,7 +784,7 @@ export class UnitsManager {
let callback = (units) => { let callback = (units) => {
onExecution(); onExecution();
units.forEach((unit: Unit) => unit.commandAlarmState(alarmState)); units.forEach((unit: Unit) => unit.setAlarmState(alarmState));
this.#showActionMessage(units, `Alarm State set to ${alarmState.toString()}`); this.#showActionMessage(units, `Alarm State set to ${alarmState.toString()}`);
}; };

View File

@@ -1263,16 +1263,10 @@ function Olympus.setUnitsData(arg, time)
table["isAlive"] = unit:isExist() and unit:isActive() and unit:getLife() >= 1 table["isAlive"] = unit:isExist() and unit:isActive() and unit:getLife() >= 1
if unit:isActive() and unit:hasSensors(Unit.SensorType.RADAR) then if unit:isActive() and unit:hasSensors(Unit.SensorType.RADAR) then
-- Olympus.log:info("Unit Has Sensor Radar " .. tostring(unit:hasSensors(Unit.SensorType.RADAR)));
table["alarmState"] = "AUTO"
if unit:getRadar() then if unit:getRadar() then
-- Olympus.log:info("alarmState: unit active and getRadar is true, setting RED.") table["radarState"] = true
table["alarmState"] = "RED"
else else
-- Olympus.log:info("alarmState: unit active and getRadar is false, setting GREEN.") table["radarState"] = false
table["alarmState"] = "GREEN"
end end
end end