diff --git a/backend/core/include/commands.h b/backend/core/include/commands.h index 64e6a2ba..ab472c03 100644 --- a/backend/core/include/commands.h +++ b/backend/core/include/commands.h @@ -18,6 +18,7 @@ namespace SetCommandType { FORMATION = 5, RTB_ON_BINGO = 6, SILENCE = 7, + ALARM_STATE = 9, RTB_ON_OUT_OF_AMMO = 10, ECM_USING = 13, PROHIBIT_AA = 14, @@ -45,6 +46,14 @@ namespace ROE { }; } +namespace ALARM_STATE { + enum ALARM_STATEs { + AUTO = 0, + GREEN = 1, + RED = 2, + }; +} + namespace ReactionToThreat { enum ReactionsToThreat { NO_REACTION = 0, diff --git a/backend/core/include/datatypes.h b/backend/core/include/datatypes.h index faff08ea..fd1c0fac 100644 --- a/backend/core/include/datatypes.h +++ b/backend/core/include/datatypes.h @@ -7,7 +7,7 @@ namespace DataIndex { startOfData = 0, category, alive, - radarState, + alarmState, human, controlled, coalition, diff --git a/backend/core/include/unit.h b/backend/core/include/unit.h index 84a49776..2fe90c01 100644 --- a/backend/core/include/unit.h +++ b/backend/core/include/unit.h @@ -96,6 +96,7 @@ public: virtual void setTargetID(unsigned int newValue) { updateValue(targetID, newValue, DataIndex::targetID); } virtual void setTargetPosition(Coords newValue) { updateValue(targetPosition, newValue, DataIndex::targetPosition); } virtual void setROE(unsigned char newValue, bool force = false); + virtual void commandAlarmState(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 setTACAN(DataTypes::TACAN newValue, bool force = false); @@ -112,12 +113,12 @@ public: virtual void setRacetrackLength(double newValue) { updateValue(racetrackLength, newValue, DataIndex::racetrackLength); } virtual void setRacetrackAnchor(Coords newValue) { updateValue(racetrackAnchor, newValue, DataIndex::racetrackAnchor); } virtual void setRacetrackBearing(double newValue) { updateValue(racetrackBearing, newValue, DataIndex::racetrackBearing); } - virtual void setRadarState(string newValue) { updateValue(radarState, newValue, DataIndex::radarState); } + virtual void setAlarmState(string newValue) { updateValue(alarmState, newValue, DataIndex::alarmState); } /********** Getters **********/ virtual string getCategory() { return category; }; virtual bool getAlive() { return alive; } - virtual string getRadarState() { return radarState; } + virtual string getAlarmState() { return alarmState; } virtual bool getHuman() { return human; } virtual bool getControlled() { return controlled; } virtual unsigned char getCoalition() { return coalition; } @@ -180,7 +181,7 @@ protected: string callsign = ""; string groupName = ""; unsigned char state = State::NONE; - string radarState = ""; + string alarmState = ""; string task = ""; bool hasTask = false; Coords position = Coords(NULL); diff --git a/backend/core/src/scheduler.cpp b/backend/core/src/scheduler.cpp index 5e167a53..62b306ac 100644 --- a/backend/core/src/scheduler.cpp +++ b/backend/core/src/scheduler.cpp @@ -114,6 +114,19 @@ json::value Scheduler::getCommandModeOptions() { return json; } +/* Convert from string to alarm state enum value */ +ALARM_STATE::ALARM_STATEs stringToAlarmState(const std::wstring& state) { + if (state == L"red") { + return ALARM_STATE::RED; + } else if (state == L"green") { + return ALARM_STATE::GREEN; + } else if (state == L"auto") { + return ALARM_STATE::AUTO; + } else { + throw invalid_argument("Stato non valido: " + std::string(state.begin(), state.end())); + } +} + bool Scheduler::checkSpawnPoints(int spawnPoints, string coalition) { if (!getRestrictSpawns()) return true; @@ -402,6 +415,20 @@ 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); } } + else if (key.compare("commandAlarmState") == 0) + { + unsigned int ID = value[L"ID"].as_integer(); + unitsManager->acquireControl(ID); + Unit* unit = unitsManager->getGroupLeader(ID); + if (unit != nullptr) { + unsigned char alarmState = value[L"alarmState"].as_integer(); + log(username + " is trying to set unit " + unit->getUnitName() + "(" + unit->getName() + ") alarm state to " + to_string(alarmState), true); + unit->commandAlarmState(alarmState); + log(username + " set unit " + unit->getUnitName() + "(" + unit->getName() + ") alarm state to " + to_string(alarmState), true); + } else { + log("Error while setting commandAlarmState. Unit does not exist."); + } + } /************************/ else if (key.compare("setReactionToThreat") == 0) { diff --git a/backend/core/src/unit.cpp b/backend/core/src/unit.cpp index 0828ec42..227228d2 100644 --- a/backend/core/src/unit.cpp +++ b/backend/core/src/unit.cpp @@ -83,9 +83,9 @@ void Unit::update(json::value json, double dt) if (json.has_boolean_field(L"isAlive")) setAlive(json[L"isAlive"].as_bool()); - if (json.has_string_field(L"radarState")) { - log("Unit " + to_string(json[L"unitName"]) + " has radarState: " + to_string(json[L"radarState"])); - setRadarState(to_string(json[L"radarState"])); + if (json.has_string_field(L"alarmState")) { + // log("Unit " + to_string(json[L"unitName"]) + " has alarmState: " + to_string(json[L"alarmState"])); + setAlarmState(to_string(json[L"alarmState"])); } if (json.has_boolean_field(L"isHuman")) @@ -213,7 +213,7 @@ void Unit::refreshLeaderData(unsigned long long time) { case DataIndex::operateAs: updateValue(operateAs, leader->operateAs, datumIndex); break; case DataIndex::shotsScatter: updateValue(shotsScatter, leader->shotsScatter, datumIndex); break; case DataIndex::shotsIntensity: updateValue(shotsIntensity, leader->shotsIntensity, datumIndex); break; - case DataIndex::radarState: updateValue(radarState, leader->radarState, datumIndex); break; + case DataIndex::alarmState: updateValue(alarmState, leader->alarmState, datumIndex); break; } } } @@ -257,7 +257,7 @@ void Unit::getData(stringstream& ss, unsigned long long time) switch (datumIndex) { case DataIndex::category: appendString(ss, datumIndex, category); break; case DataIndex::alive: appendNumeric(ss, datumIndex, alive); break; - case DataIndex::radarState: appendString(ss, datumIndex, radarState); break; + case DataIndex::alarmState: appendString(ss, datumIndex, alarmState); break; case DataIndex::human: appendNumeric(ss, datumIndex, human); break; case DataIndex::controlled: appendNumeric(ss, datumIndex, controlled); break; case DataIndex::coalition: appendNumeric(ss, datumIndex, coalition); break; @@ -463,6 +463,13 @@ void Unit::setROE(unsigned char newROE, bool force) } } +void Unit::commandAlarmState(unsigned char newAlarmState, bool force) +{ + Command* command = dynamic_cast(new SetOption(groupName, SetCommandType::ALARM_STATE, static_cast(newAlarmState))); + scheduler->appendCommand(command); + triggerUpdate(DataIndex::alarmState); +} + void Unit::setReactionToThreat(unsigned char newReactionToThreat, bool force) { if (reactionToThreat != newReactionToThreat || force) { diff --git a/frontend/react/src/constants/constants.ts b/frontend/react/src/constants/constants.ts index 3e74f4cd..9d6cec9b 100644 --- a/frontend/react/src/constants/constants.ts +++ b/frontend/react/src/constants/constants.ts @@ -97,7 +97,7 @@ export const states: string[] = [ ]; export const ROEs: string[] = ["free", "designated", "", "return", "hold"]; -export const alarmStates: string[] = ["green", "auto", "red"]; +export const alarmStates: string[] = ["auto", "green", "red"]; export const reactionsToThreat: string[] = ["none", "manoeuvre", "passive", "evade"]; export const emissionsCountermeasures: string[] = ["silent", "attack", "defend", "free"]; @@ -449,7 +449,7 @@ export enum DataIndexes { startOfData = 0, category, alive, - radarState, + alarmState, human, controlled, coalition, diff --git a/frontend/react/src/server/servermanager.ts b/frontend/react/src/server/servermanager.ts index f80ce8ee..04b20afb 100644 --- a/frontend/react/src/server/servermanager.ts +++ b/frontend/react/src/server/servermanager.ts @@ -17,6 +17,7 @@ import { } from "../constants/constants"; import { AirbasesData, + AlarmState, BullseyesData, CommandModeOptions, GeneralSettings, @@ -410,6 +411,12 @@ export class ServerManager { this.PUT(data, callback); } + setAlarmState(ID: number, alarmState: number, callback: CallableFunction = () => {}) { + var command = { ID: ID, alarmState: alarmState }; + var data = { commandAlarmState: command }; + this.PUT(data, callback); + } + setReactionToThreat(ID: number, reactionToThreat: string, callback: CallableFunction = () => {}) { var command = { ID: ID, diff --git a/frontend/react/src/ui/panels/unitcontrolmenu.tsx b/frontend/react/src/ui/panels/unitcontrolmenu.tsx index 369c09cf..fef20cf4 100644 --- a/frontend/react/src/ui/panels/unitcontrolmenu.tsx +++ b/frontend/react/src/ui/panels/unitcontrolmenu.tsx @@ -841,12 +841,6 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
Sets the alarm state of the unit, in order:
-
- {" "} - Green: The unit will not engage with its sensors in any circumstances. The unit will be able to move. -
{" "} void }) { Auto: The unit will use its sensors to engage based on its ROE.
+
+ {" "} + Green: The unit will not engage with its sensors in any circumstances. The unit will be able to move. +
{" "} void }) { { - // TODO: set alarm state - // getApp() - // .getUnitsManager() - // .setROE(ROEs[convertROE(idx)], null, () => - // setForcedUnitsData({ - // ...forcedUnitsData, - // ROE: ROEs[convertROE(idx)], - // }) - // ); + getApp() + .getUnitsManager() + .setAlarmState(idx, null, () => + setForcedUnitsData({ + ...forcedUnitsData, + alarmState: Object.values(AlarmState)[idx], + }) + ); }} active={selectedUnitsData.alarmState === alarmStates[idx]} icon={icon} diff --git a/frontend/react/src/unit/unit.ts b/frontend/react/src/unit/unit.ts index 399b926c..5f231584 100644 --- a/frontend/react/src/unit/unit.ts +++ b/frontend/react/src/unit/unit.ts @@ -492,17 +492,17 @@ export abstract class Unit extends CustomMarker { this.setAlive(dataExtractor.extractBool()); updateMarker = true; break; - case DataIndexes.radarState: + case DataIndexes.alarmState: let stringAlarmState = dataExtractor.extractString(); switch (stringAlarmState) { case 'RED': - this.setRadarState(AlarmState.RED); + this.setAlarmState(AlarmState.RED); break; case 'GREEN': - this.setRadarState(AlarmState.GREEN); + this.setAlarmState(AlarmState.GREEN); break; case '': - this.setRadarState(AlarmState.AUTO); + this.setAlarmState(AlarmState.AUTO); default: break; } @@ -785,11 +785,10 @@ export abstract class Unit extends CustomMarker { } } - setRadarState(newRadarState: AlarmState) { - if (newRadarState != this.#alarmState) { - this.#alarmState = newRadarState; - // TODO: check if an event is needed -- surely yes to update the UI - console.log('----radar state updated: ', this.#alarmState); + setAlarmState(newAlarmState: AlarmState) { + if (newAlarmState != this.#alarmState) { + this.#alarmState = newAlarmState; + console.log('---- alarm state updated: ', this.#alarmState); this.#updateMarker(); } } @@ -1070,9 +1069,9 @@ export abstract class Unit extends CustomMarker { /* Radar state indicator */ if (this.#alarmState) { - var radarStateIcon = document.createElement("div"); - radarStateIcon.classList.add("unit-radar-state"); - el.append(radarStateIcon); + var alarmStateIcon = document.createElement("div"); + alarmStateIcon.classList.add("unit-radar-state"); + el.append(alarmStateIcon); } /* Ammo indicator */ @@ -1295,6 +1294,10 @@ export abstract class Unit extends CustomMarker { if (!this.#human) getApp().getServerManager().setROE(this.ID, ROE); } + commandAlarmState(alarmState: number) { + if (!this.#human) getApp().getServerManager().setAlarmState(this.ID, alarmState); + } + setReactionToThreat(reactionToThreat: string) { if (!this.#human) getApp().getServerManager().setReactionToThreat(this.ID, reactionToThreat); } diff --git a/frontend/react/src/unit/unitsmanager.ts b/frontend/react/src/unit/unitsmanager.ts index f8a2c8bb..47274b28 100644 --- a/frontend/react/src/unit/unitsmanager.ts +++ b/frontend/react/src/unit/unitsmanager.ts @@ -13,11 +13,11 @@ import { msToKnots, } from "../other/utils"; import { CoalitionPolygon } from "../map/coalitionarea/coalitionpolygon"; -import { DELETE_CYCLE_TIME, DELETE_SLOW_THRESHOLD, DataIndexes, GAME_MASTER, IADSDensities, OlympusState, UnitControlSubState } from "../constants/constants"; +import { DELETE_CYCLE_TIME, DELETE_SLOW_THRESHOLD, DataIndexes, GAME_MASTER, IADSDensities, OlympusState, UnitControlSubState, alarmStates } from "../constants/constants"; import { DataExtractor } from "../server/dataextractor"; import { citiesDatabase } from "./databases/citiesdatabase"; import { TemporaryUnitMarker } from "../map/markers/temporaryunitmarker"; -import { Contact, GeneralSettings, Radio, TACAN, UnitBlueprint, UnitData, UnitSpawnTable } from "../interfaces"; +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"; @@ -719,6 +719,27 @@ export class UnitsManager { this.#protectionCallback = callback; } else callback(units); } + + /** Set a specific Alarm State to all the selected units + * + * @param AlarmState Value to set, see constants for acceptable values + * @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units. + */ + setAlarmState(alarmState: number, units: Unit[] | null = null, onExecution: () => void = () => {}) { + if (units === null) units = this.getSelectedUnits(); + units = units.filter((unit) => !unit.getHuman()); + + let callback = (units) => { + onExecution(); + units.forEach((unit: Unit) => unit.commandAlarmState(alarmState)); + this.#showActionMessage(units, `Alarm State set to ${alarmState.toString()}`); + }; + + if (getApp().getMap().getOptions().protectDCSUnits && !units.every((unit) => unit.isControlledByOlympus())) { + getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.PROTECTION); + this.#protectionCallback = callback; + } else callback(units); + } /** Set a specific reaction to threat to all the selected units * * @param reactionToThreat Value to set, see constants for acceptable values diff --git a/scripts/lua/backend/OlympusCommand.lua b/scripts/lua/backend/OlympusCommand.lua index cc029dc3..a872673f 100644 --- a/scripts/lua/backend/OlympusCommand.lua +++ b/scripts/lua/backend/OlympusCommand.lua @@ -1281,14 +1281,14 @@ function Olympus.setUnitsData(arg, time) if unit:isActive() and unit:hasSensors(Unit.SensorType.RADAR) then -- Olympus.log:info("Unit Has Sensor Radar " .. tostring(unit:hasSensors(Unit.SensorType.RADAR))); - table["radarState"] = "AUTO" + table["alarmState"] = "AUTO" if unit:getRadar() then - -- Olympus.log:info("radarState: unit active and getRadar is true, setting RED.") - table["radarState"] = "RED" + -- Olympus.log:info("alarmState: unit active and getRadar is true, setting RED.") + table["alarmState"] = "RED" else - -- Olympus.log:info("radarState: unit active and getRadar is false, setting GREEN.") - table["radarState"] = "GREEN" + -- Olympus.log:info("alarmState: unit active and getRadar is false, setting GREEN.") + table["alarmState"] = "GREEN" end end