diff --git a/backend/core/include/airunit.h b/backend/core/include/airunit.h index 66610466..ce559b68 100644 --- a/backend/core/include/airunit.h +++ b/backend/core/include/airunit.h @@ -22,6 +22,8 @@ public: virtual void setRacetrackLength(double newValue); virtual void setRacetrackAnchor(Coords newValue); virtual void setRacetrackBearing(double newValue); + + virtual void setCargoWeight(double newValue); protected: virtual void AIloop(); diff --git a/backend/core/include/commands.h b/backend/core/include/commands.h index 5119a9eb..b4fa3e68 100644 --- a/backend/core/include/commands.h +++ b/backend/core/include/commands.h @@ -538,4 +538,44 @@ public: private: const unsigned int spotID; const Coords destination; +}; + +/* Set cargo weight */ +class SetCargoWeight : public Command +{ + public: + SetCargoWeight(unsigned int ID, double weight, function callback = []() {}) : + Command(callback), + ID(ID), + weight(weight) + { + priority = CommandPriority::LOW; + }; + virtual string getString(); + virtual unsigned int getLoad() { return 5; } + +private: + const unsigned int ID; + const double weight; +}; + +/* Register draw argument */ +class RegisterDrawArgument : public Command +{ + public: + RegisterDrawArgument(unsigned int ID, unsigned int argument, bool active, function callback = []() {}) : + Command(callback), + ID(ID), + argument(argument), + active(active) + { + priority = CommandPriority::LOW; + }; + virtual string getString(); + virtual unsigned int getLoad() { return 5; } + +private: + const unsigned int ID; + const unsigned int argument; + const bool active; }; \ No newline at end of file diff --git a/backend/core/include/datatypes.h b/backend/core/include/datatypes.h index d4a9a3b0..fb52e979 100644 --- a/backend/core/include/datatypes.h +++ b/backend/core/include/datatypes.h @@ -70,6 +70,8 @@ namespace DataIndex { aimMethodRange, acquisitionRange, airborne, + cargoWeight, + drawArguments, lastIndex, endOfData = 255 }; @@ -159,6 +161,11 @@ namespace DataTypes { unsigned int ID = 0; unsigned char detectionMethod = 0; }; + + struct DrawArgument { + unsigned int argument = 0; + double value = 0.0; + }; } #pragma pack(pop) @@ -167,6 +174,7 @@ bool operator==(const DataTypes::Radio& lhs, const DataTypes::Radio& rhs); bool operator==(const DataTypes::GeneralSettings& lhs, const DataTypes::GeneralSettings& rhs); bool operator==(const DataTypes::Ammo& lhs, const DataTypes::Ammo& rhs); bool operator==(const DataTypes::Contact& lhs, const DataTypes::Contact& rhs); +bool operator==(const DataTypes::DrawArgument& lhs, const DataTypes::DrawArgument& rhs); struct SpawnOptions { string unitType; diff --git a/backend/core/include/scheduler.h b/backend/core/include/scheduler.h index 304a9da1..c64045ec 100644 --- a/backend/core/include/scheduler.h +++ b/backend/core/include/scheduler.h @@ -19,6 +19,7 @@ public: return true; } } + return false; } void setFrameRate(double newFrameRate) { frameRate = newFrameRate; } diff --git a/backend/core/include/unit.h b/backend/core/include/unit.h index 0ff980ba..fad95eee 100644 --- a/backend/core/include/unit.h +++ b/backend/core/include/unit.h @@ -130,9 +130,11 @@ public: virtual void setAcquisitionRange(double newValue) { updateValue(acquisitionRange, newValue, DataIndex::acquisitionRange); } virtual void setRadarState(bool newValue) { updateValue(radarState, newValue, DataIndex::radarState); } virtual void setAirborne(bool newValue) { updateValue(airborne, newValue, DataIndex::airborne); } + virtual void setCargoWeight(double newValue) { updateValue(cargoWeight, newValue, DataIndex::cargoWeight); } + virtual void setDrawArguments(vector newValue); /********** Getters **********/ - virtual string getCategory() { return category; }; + virtual string getCategory() { return category; } virtual bool getAlive() { return alive; } virtual unsigned char getAlarmState() { return alarmState; } virtual bool getHuman() { return human; } @@ -197,6 +199,8 @@ public: virtual double getAcquisitionRange() { return acquisitionRange; } virtual bool getRadarState() { return radarState; } virtual bool getAirborne() { return airborne; } + virtual double getCargoWeight() { return cargoWeight; } + virtual vector getDrawArguments() { return drawArguments; } protected: unsigned int ID; @@ -267,6 +271,8 @@ protected: double aimMethodRange = 0; double acquisitionRange = 0; bool airborne = false; + double cargoWeight = 0; + vector drawArguments; /********** Other **********/ unsigned int taskCheckCounter = 0; diff --git a/backend/core/src/airunit.cpp b/backend/core/src/airunit.cpp index baa33bb0..d41941e4 100644 --- a/backend/core/src/airunit.cpp +++ b/backend/core/src/airunit.cpp @@ -428,4 +428,14 @@ void AirUnit::setRacetrackBearing(double newRacetrackBearing) { triggerUpdate(DataIndex::racetrackBearing); } +} + +void AirUnit::setCargoWeight(double newCargoWeight) { + if (cargoWeight != newCargoWeight) { + cargoWeight = newCargoWeight; + triggerUpdate(DataIndex::cargoWeight); + + Command* command = dynamic_cast(new SetCargoWeight(this->ID, cargoWeight)); + scheduler->appendCommand(command); + } } \ No newline at end of file diff --git a/backend/core/src/commands.cpp b/backend/core/src/commands.cpp index b0b0c93d..75f1cf65 100644 --- a/backend/core/src/commands.cpp +++ b/backend/core/src/commands.cpp @@ -318,4 +318,27 @@ string DeleteSpot::getString() commandSS << "Olympus.deleteSpot, " << spotID; return commandSS.str(); +} + +/* SetCargoWeight command */ +string SetCargoWeight::getString() +{ + std::ostringstream commandSS; + commandSS.precision(10); + commandSS << "Olympus.setCargoWeight, " + << ID << ", " + << weight; + return commandSS.str(); +} + +/* RegisterDrawArgument command */ +string RegisterDrawArgument::getString() +{ + std::ostringstream commandSS; + commandSS.precision(10); + commandSS << "Olympus.registerDrawArgument, " + << ID << ", " + << argument << ", " + << active; + return commandSS.str(); } \ No newline at end of file diff --git a/backend/core/src/datatypes.cpp b/backend/core/src/datatypes.cpp index eb0c52cd..9ea68425 100644 --- a/backend/core/src/datatypes.cpp +++ b/backend/core/src/datatypes.cpp @@ -12,19 +12,24 @@ bool operator==(const DataTypes::Radio& lhs, const DataTypes::Radio& rhs) bool operator==(const DataTypes::GeneralSettings& lhs, const DataTypes::GeneralSettings& rhs) { - return lhs.prohibitAA == rhs.prohibitAA && lhs.prohibitAfterburner == rhs.prohibitAfterburner && lhs.prohibitAG == rhs.prohibitAG && + return lhs.prohibitAA == rhs.prohibitAA && lhs.prohibitAfterburner == rhs.prohibitAfterburner && lhs.prohibitAG == rhs.prohibitAG && lhs.prohibitAirWpn == rhs.prohibitAirWpn && lhs.prohibitJettison == rhs.prohibitJettison; } bool operator==(const DataTypes::Ammo& lhs, const DataTypes::Ammo& rhs) { - return lhs.category == rhs.category && lhs.guidance == rhs.guidance && lhs.missileCategory == rhs.missileCategory && + return lhs.category == rhs.category && lhs.guidance == rhs.guidance && lhs.missileCategory == rhs.missileCategory && lhs.quantity == rhs.quantity && strcmp(lhs.name, rhs.name) == 0; } +bool operator==(const DataTypes::DrawArgument& lhs, const DataTypes::DrawArgument& rhs) +{ + return lhs.argument == rhs.argument && lhs.value == rhs.value; +} + bool operator==(const DataTypes::Contact& lhs, const DataTypes::Contact& rhs) { - return lhs.detectionMethod == rhs.detectionMethod && lhs.ID == rhs.ID; + return lhs.detectionMethod == rhs.detectionMethod && lhs.ID == rhs.ID; } diff --git a/backend/core/src/scheduler.cpp b/backend/core/src/scheduler.cpp index 4013ceb5..8d17619d 100644 --- a/backend/core/src/scheduler.cpp +++ b/backend/core/src/scheduler.cpp @@ -827,6 +827,31 @@ void Scheduler::handleRequest(string key, json::value value, string username, js unitsManager->loadDatabases(); } /************************/ + else if (key.compare("setCargoWeight") == 0) + { + unsigned int ID = value[L"ID"].as_integer(); + Unit* unit = unitsManager->getUnit(ID); + if (unit != nullptr) { + double weight = value[L"weight"].as_double(); + unit->setCargoWeight(weight); + log(username + " set weight to unit " + unit->getUnitName() + "(" + unit->getName() + "), " + to_string(weight), true); + } + } + /************************/ + else if (key.compare("registerDrawArgument") == 0) + { + unsigned int ID = value[L"ID"].as_integer(); + Unit* unit = unitsManager->getUnit(ID); + if (unit != nullptr) { + int argument = value[L"argument"].as_integer(); + bool active = value[L"active"].as_bool(); + + command = dynamic_cast(new RegisterDrawArgument(ID, argument, active)); + + log(username + " registered draw argument " + to_string(argument) + " for unit " + unit->getUnitName() + "(" + unit->getName() + "), value:" + to_string(active), true); + } + } + /************************/ else { log("Unknown command: " + key); diff --git a/backend/core/src/unit.cpp b/backend/core/src/unit.cpp index 33aa8158..8392f542 100644 --- a/backend/core/src/unit.cpp +++ b/backend/core/src/unit.cpp @@ -128,6 +128,20 @@ void Unit::update(json::value json, double dt) setAmmo(ammo); } + if (json.has_object_field(L"drawArguments")) { + vector drawArguments; + for (auto const& el : json[L"drawArguments"].as_object()) { + DataTypes::DrawArgument drawArgumentItem; + auto drawArgumentJson = el.second; + if (drawArgumentJson.has_number_field(L"argument")) + drawArgumentItem.argument = drawArgumentJson[L"argument"].as_number().to_uint32(); + if (drawArgumentJson.has_number_field(L"value")) + drawArgumentItem.value = drawArgumentJson[L"value"].as_number().to_double(); + drawArguments.push_back(drawArgumentItem); + } + setDrawArguments(drawArguments); + } + if (json.has_object_field(L"contacts")) { vector contacts; for (auto const& el : json[L"contacts"].as_object()) { @@ -328,6 +342,8 @@ void Unit::getData(stringstream& ss, unsigned long long time) case DataIndex::aimMethodRange: appendNumeric(ss, datumIndex, aimMethodRange); break; case DataIndex::acquisitionRange: appendNumeric(ss, datumIndex, acquisitionRange); break; case DataIndex::airborne: appendNumeric(ss, datumIndex, airborne); break; + case DataIndex::cargoWeight: appendNumeric(ss, datumIndex, cargoWeight); break; + case DataIndex::drawArguments: appendVector(ss, datumIndex, drawArguments); break; } } } @@ -699,6 +715,24 @@ void Unit::setGeneralSettings(DataTypes::GeneralSettings newGeneralSettings, boo } } +void Unit::setDrawArguments(vector newDrawArguments) +{ + if (drawArguments.size() == newDrawArguments.size()) { + bool equal = true; + for (int i = 0; i < drawArguments.size(); i++) { + if (drawArguments.at(i) != newDrawArguments.at(i)) + { + equal = false; + break; + } + } + if (equal) + return; + } + drawArguments = newDrawArguments; + triggerUpdate(DataIndex::drawArguments); +} + void Unit::setDesiredSpeed(double newDesiredSpeed) { if (desiredSpeed != newDesiredSpeed) { diff --git a/backend/utils/src/utils.cpp b/backend/utils/src/utils.cpp index 40643055..d2097a39 100644 --- a/backend/utils/src/utils.cpp +++ b/backend/utils/src/utils.cpp @@ -66,7 +66,7 @@ std::string random_string(size_t length) bool operator== (const Coords& a, const Coords& b) { return a.lat == b.lat && a.lng == b.lng && a.alt == b.alt && a.threshold == b.threshold; } bool operator!= (const Coords& a, const Coords& b) { return !(a == b); } -bool operator== (const Coords& a, const double& b) { return a.lat == b && a.lng == b && a.alt == b && a.threshold == b } +bool operator== (const Coords& a, const double& b) { return a.lat == b && a.lng == b && a.alt == b && a.threshold == b; } bool operator!= (const Coords& a, const double& b) { return !(a == b); } bool operator== (const Offset& a, const Offset& b) { return a.x == b.x && a.y == b.y && a.z == b.z; } diff --git a/frontend/react/.vscode/tasks.json b/frontend/react/.vscode/tasks.json index 586890ef..fb796833 100644 --- a/frontend/react/.vscode/tasks.json +++ b/frontend/react/.vscode/tasks.json @@ -1,12 +1,6 @@ { "version": "2.0.0", "tasks": [ - { - "label": "check-setup", - "type": "shell", - "command": "cd .. ; ./check_setup.bat", - "isBackground": false - }, { "type": "npm", "script": "dev", diff --git a/frontend/react/src/constants/constants.ts b/frontend/react/src/constants/constants.ts index b2c39e16..e95c7267 100644 --- a/frontend/react/src/constants/constants.ts +++ b/frontend/react/src/constants/constants.ts @@ -547,6 +547,8 @@ export enum DataIndexes { aimMethodRange, acquisitionRange, airborne, + cargoWeight, + drawingArguments, endOfData = 255, } diff --git a/frontend/react/src/interfaces.ts b/frontend/react/src/interfaces.ts index 281ea04d..f32c5bb8 100644 --- a/frontend/react/src/interfaces.ts +++ b/frontend/react/src/interfaces.ts @@ -219,6 +219,11 @@ export interface Offset { z: number; } +export interface DrawingArgument { + argument: number; + value: number; +} + export interface UnitData { category: string; markerCategory: string; @@ -286,6 +291,8 @@ export interface UnitData { aimMethodRange: number; acquisitionRange: number; airborne: boolean; + cargoWeight: number; + drawingArguments: DrawingArgument[]; } export interface LoadoutItemBlueprint { diff --git a/frontend/react/src/map/latlng.ts b/frontend/react/src/map/latlng.ts new file mode 100644 index 00000000..87c5d949 --- /dev/null +++ b/frontend/react/src/map/latlng.ts @@ -0,0 +1,18 @@ +import * as L from "leaflet"; + +export class LatLng extends L.LatLng { + threshold: number; + + constructor(lat: number, lng: number, alt: number, threshold: number) { + super(lat, lng, alt); + this.threshold = threshold; + } + + setThreshold(threshold: number) { + this.threshold = threshold; + } + + getThreshold() { + return this.threshold; + } +} diff --git a/frontend/react/src/server/dataextractor.ts b/frontend/react/src/server/dataextractor.ts index 5eec77cb..9b2e87d6 100644 --- a/frontend/react/src/server/dataextractor.ts +++ b/frontend/react/src/server/dataextractor.ts @@ -1,5 +1,5 @@ import { LatLng } from "leaflet"; -import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN } from "../interfaces"; +import { Ammo, Contact, DrawingArgument, GeneralSettings, Offset, Radio, TACAN } from "../interfaces"; export class DataExtractor { #seekPosition = 0; @@ -58,7 +58,9 @@ export class DataExtractor { } extractLatLng() { - return new LatLng(this.extractFloat64(), this.extractFloat64(), this.extractFloat64()); + let latlng = new LatLng(this.extractFloat64(), this.extractFloat64(), this.extractFloat64()); + let threshold = this.extractFloat64(); + return latlng; } extractFromBitmask(bitmask: number, position: number) { @@ -104,6 +106,14 @@ export class DataExtractor { return value; } + extractDrawingArgument() { + const value: DrawingArgument = { + argument: this.extractUInt32(), + value: this.extractFloat64(), + }; + return value; + } + extractGeneralSettings() { const value: GeneralSettings = { prohibitJettison: this.extractBool(), @@ -159,4 +169,13 @@ export class DataExtractor { }; return value; } + + extractDrawingArguments() { + const value: DrawingArgument[] = []; + const size = this.extractUInt16(); + for (let idx = 0; idx < size; idx++) { + value.push(this.extractDrawingArgument()); + } + return value; + } } diff --git a/frontend/react/src/unit/unit.ts b/frontend/react/src/unit/unit.ts index ed07cd62..d8074cad 100644 --- a/frontend/react/src/unit/unit.ts +++ b/frontend/react/src/unit/unit.ts @@ -1,4 +1,4 @@ -import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map, Point, LeafletMouseEvent, DomEvent, DomUtil, Circle } from "leaflet"; +import { LatLng, Polyline, DivIcon, CircleMarker, Map, Point, DomEvent } from "leaflet"; import { getApp } from "../olympusapp"; import { enumToCoalition, @@ -54,7 +54,7 @@ import { } from "../constants/constants"; import { DataExtractor } from "../server/dataextractor"; import { Weapon } from "../weapon/weapon"; -import { AlarmState, Ammo, Contact, GeneralSettings, LoadoutBlueprint, ObjectIconOptions, Offset, Radio, TACAN, UnitBlueprint, UnitData } from "../interfaces"; +import { AlarmState, Ammo, Contact, DrawingArgument, GeneralSettings, LoadoutBlueprint, ObjectIconOptions, Offset, Radio, TACAN, UnitBlueprint, UnitData } from "../interfaces"; import { RangeCircle } from "../map/rangecircle"; import { Group } from "./group"; import { ContextActionSet } from "./contextactionset"; @@ -159,6 +159,8 @@ export abstract class Unit extends CustomMarker { #racetrackAnchor: LatLng = new LatLng(0, 0); #racetrackBearing: number = 0; #airborne: boolean = false; + #cargoWeight: number = 0; + #drawingArguments: DrawingArgument[] = []; /* Other members used to draw the unit, mostly ancillary stuff like targets, ranges and so on */ #blueprint: UnitBlueprint | null = null; @@ -406,6 +408,12 @@ export abstract class Unit extends CustomMarker { getAirborne() { return this.#airborne; } + getCargoWeight() { + return this.#cargoWeight; + } + getDrawingArguments() { + return this.#drawingArguments; + } static getConstructor(type: string) { if (type === "GroundUnit") return GroundUnit; @@ -797,6 +805,12 @@ export abstract class Unit extends CustomMarker { case DataIndexes.airborne: this.#airborne = dataExtractor.extractBool(); break; + case DataIndexes.cargoWeight: + this.#cargoWeight = dataExtractor.extractFloat64(); + break; + case DataIndexes.drawingArguments: + this.#drawingArguments = dataExtractor.extractDrawingArguments(); + break; default: break; } @@ -920,6 +934,8 @@ export abstract class Unit extends CustomMarker { aimMethodRange: this.#aimMethodRange, acquisitionRange: this.#acquisitionRange, airborne: this.#airborne, + cargoWeight: this.#cargoWeight, + drawingArguments: this.#drawingArguments, }; } diff --git a/scripts/lua/backend/OlympusCommand.lua b/scripts/lua/backend/OlympusCommand.lua index 4d3e8146..c098635c 100644 --- a/scripts/lua/backend/OlympusCommand.lua +++ b/scripts/lua/backend/OlympusCommand.lua @@ -23,6 +23,7 @@ Olympus.unitIndex = 0 -- Counter used to spread the computational load of data Olympus.unitStep = 50 -- Max number of units that get updated each cycle Olympus.units = {} -- Table holding references to all the currently existing units Olympus.unitsInitialLife = {} -- getLife0 returns 0 for ships, so we need to store the initial life of units +Olympus.drawArguments = {} -- Table that sets what drawArguments to read for each unit Olympus.weaponIndex = 0 -- Counter used to spread the computational load of data retrievial from DCS Olympus.weaponStep = 50 -- Max number of weapons that get updated each cycle @@ -1087,10 +1088,38 @@ function Olympus.setOnOff(groupName, onOff) end end +-- Get the unit description function getUnitDescription(unit) return unit:getDescr() end +-- Set the unit cargo weight +function Olympus.setCargoWeight(ID, weight) + Olympus.debug("Olympus.setCargoWeight " .. ID .. " " .. tostring(weight), 2) + + local unit = Olympus.getUnitByID(ID) + if unit ~= nil and unit:isExist() then + trigger.action.setUnitInternalCargo(unit:getName(), weight) + end +end + +-- Register a drawArgument to be read for a unit +function Olympus.registerDrawArgument(ID, argument, active) + Olympus.debug("Olympus.registerDrawArgument " .. ID .. " " .. tostring(argument) .. " " .. tostring(active), 2) + + -- Create the table if it does not exist + if Olympus.drawArguments[ID] == nil then + Olympus.drawArguments[ID] = {} + end + + -- Set the draw argument to true or false + if active then + Olympus.drawArguments[ID][argument] = true + else + Olympus.drawArguments[ID][argument] = false + end +end + -- This function gets the navpoints from the DCS mission function Olympus.getNavPoints() local function extract_tag(str) @@ -1293,6 +1322,20 @@ function Olympus.setUnitsData(arg, time) table["radarState"] = false end end ]] + + -- Read the draw arguments + local drawArguments = {} + if Olympus.drawArguments[ID] ~= nil then + for argument, active in pairs(Olympus.drawArguments[ID]) do + if active then + drawArguments[#drawArguments + 1] = { + argument = argument, + value = unit:getDrawArgumentValue(argument) + } + end + end + end + table["drawArguments"] = drawArguments local group = unit:getGroup() if group ~= nil then diff --git a/scripts/python/API/.vscode/launch.json b/scripts/python/API/.vscode/launch.json index 59e84c31..9870d49a 100644 --- a/scripts/python/API/.vscode/launch.json +++ b/scripts/python/API/.vscode/launch.json @@ -27,6 +27,30 @@ "program": "example_disembarked_infantry.py", "console": "integratedTerminal", "justMyCode": false, + }, + { + "name": "Example set cargo weight", + "type": "debugpy", + "request": "launch", + "program": "example_set_cargo_weight.py", + "console": "integratedTerminal", + "justMyCode": false, + }, + { + "name": "Example draw argument", + "type": "debugpy", + "request": "launch", + "program": "example_draw_argument.py", + "console": "integratedTerminal", + "justMyCode": false, + }, + { + "name": "Example precise movement", + "type": "debugpy", + "request": "launch", + "program": "example_precise_movement.py", + "console": "integratedTerminal", + "justMyCode": false, } ] } \ No newline at end of file diff --git a/scripts/python/API/data/data_extractor.py b/scripts/python/API/data/data_extractor.py index de1608dd..bdcfebaf 100644 --- a/scripts/python/API/data/data_extractor.py +++ b/scripts/python/API/data/data_extractor.py @@ -1,6 +1,6 @@ import struct from typing import List -from data.data_types import LatLng, TACAN, Radio, GeneralSettings, Ammo, Contact, Offset +from data.data_types import DrawArgument, LatLng, TACAN, Radio, GeneralSettings, Ammo, Contact, Offset class DataExtractor: def __init__(self, buffer: bytes): @@ -48,6 +48,7 @@ class DataExtractor: lat = self.extract_float64() lng = self.extract_float64() alt = self.extract_float64() + threshold = self.extract_float64() return LatLng(lat, lng, alt) def extract_from_bitmask(self, bitmask: int, position: int) -> bool: @@ -136,4 +137,14 @@ class DataExtractor: x=self.extract_float64(), y=self.extract_float64(), z=self.extract_float64() - ) \ No newline at end of file + ) + + def extract_draw_arguments(self) -> List[DrawArgument]: + value = [] + size = self.extract_uint16() + for _ in range(size): + value.append(DrawArgument( + argument=self.extract_uint32(), + value=self.extract_float64() + )) + return value \ No newline at end of file diff --git a/scripts/python/API/data/data_indexes.py b/scripts/python/API/data/data_indexes.py index 38155fb2..54715d97 100644 --- a/scripts/python/API/data/data_indexes.py +++ b/scripts/python/API/data/data_indexes.py @@ -67,4 +67,6 @@ class DataIndexes(Enum): AIM_METHOD_RANGE = 63 ACQUISITION_RANGE = 64 AIRBORNE = 65 + CARGO_WEIGHT = 66 + DRAW_ARGUMENTS = 67 END_OF_DATA = 255 \ No newline at end of file diff --git a/scripts/python/API/data/data_types.py b/scripts/python/API/data/data_types.py index c4017a31..ffd73f27 100644 --- a/scripts/python/API/data/data_types.py +++ b/scripts/python/API/data/data_types.py @@ -8,13 +8,15 @@ class LatLng: lat: float lng: float alt: float - + threshold: Optional[float] = 0 # Optional threshold for proximity checks + def toJSON(self): """Convert LatLng to a JSON serializable dictionary.""" return { "lat": self.lat, "lng": self.lng, - "alt": self.alt + "alt": self.alt, + "threshold": self.threshold } def project_with_bearing_and_distance(self, d, bearing): @@ -88,4 +90,9 @@ class Contact: class Offset: x: float y: float - z: float \ No newline at end of file + z: float + +@dataclass +class DrawArgument: + argument: int + value: float \ No newline at end of file diff --git a/scripts/python/API/example_draw_argument.py b/scripts/python/API/example_draw_argument.py new file mode 100644 index 00000000..b297c099 --- /dev/null +++ b/scripts/python/API/example_draw_argument.py @@ -0,0 +1,31 @@ +from api import API + +def on_api_startup(api: API): + units = api.update_units() + for unit in units.values(): + if unit.name == "UH-1H": + # Register draw argument 43 for UH-1H + unit.register_draw_argument(43) + +def on_api_update(api: API): + units = api.get_units() + for unit in units.values(): + if unit.name == "UH-1H": + print(f"Draw Arguments for {unit.name}:") + for draw_arg in unit.draw_arguments: + print(f" Argument: {draw_arg.argument}, Value: {draw_arg.value}") + +############################################################################################## +# Main entry point for the script. It registers the callbacks and starts the API. +############################################################################################## +if __name__ == "__main__": + # Initialize the API + api = API() + + # Register the callbacks + api.register_on_update_callback(on_api_update) + api.register_on_startup_callback(on_api_startup) + + # Start the API, this will run forever until stopped + api.run() + \ No newline at end of file diff --git a/scripts/python/API/main.py b/scripts/python/API/example_precise_movement.py similarity index 51% rename from scripts/python/API/main.py rename to scripts/python/API/example_precise_movement.py index 5804c9f6..fc4bf013 100644 --- a/scripts/python/API/main.py +++ b/scripts/python/API/example_precise_movement.py @@ -1,5 +1,13 @@ from api import API -from kronos.kronos import Kronos + +def on_api_startup(api: API): + units = api.update_units() + for unit in units.values(): + if unit.name == "Infantry AK Ins": + current_pos = unit.position + next_pos = current_pos.project_with_bearing_and_distance(20, 0) # Move 20 meters north + next_pos.threshold = 2 # Set threshold to 1 meter, very precise + unit.set_path([next_pos]) ############################################################################################## # Main entry point for the script. It registers the callbacks and starts the API. @@ -8,13 +16,9 @@ if __name__ == "__main__": # Initialize the API api = API() - # Initialize Kronos with the API - kronos = Kronos(api) - # Register the callbacks - api.register_on_startup_callback(kronos.on_startup) + api.register_on_startup_callback(on_api_startup) # Start the API, this will run forever until stopped api.run() - \ No newline at end of file diff --git a/scripts/python/API/example_set_cargo_weight.py b/scripts/python/API/example_set_cargo_weight.py new file mode 100644 index 00000000..c62cabe9 --- /dev/null +++ b/scripts/python/API/example_set_cargo_weight.py @@ -0,0 +1,29 @@ +from api import API + +def on_api_startup(api: API): + units = api.update_units() + for unit in units.values(): + if unit.name == "UH-1H": + # Set cargo weight to 5000 kg + unit.set_cargo_weight(5000.0) + +def on_api_update(api: API): + units = api.get_units() + for unit in units.values(): + if unit.name == "UH-1H": + print(f"Cargo Weight for {unit.name}: {unit.cargo_weight} kg") + +############################################################################################## +# Main entry point for the script. It registers the callbacks and starts the API. +############################################################################################## +if __name__ == "__main__": + # Initialize the API + api = API() + + # Register the callbacks + api.register_on_update_callback(on_api_update) + api.register_on_startup_callback(on_api_startup) + + # Start the API, this will run forever until stopped + api.run() + \ No newline at end of file diff --git a/scripts/python/API/kronos/kronos.py b/scripts/python/API/kronos/kronos.py deleted file mode 100644 index b1c2193d..00000000 --- a/scripts/python/API/kronos/kronos.py +++ /dev/null @@ -1,16 +0,0 @@ -# Setup a logger for the module -import logging -logger = logging.getLogger("Kronos") -logger.setLevel(logging.INFO) -handler = logging.StreamHandler() -formatter = logging.Formatter('[%(asctime)s] %(name)s - %(levelname)s - %(message)s') -handler.setFormatter(formatter) -logger.addHandler(handler) - -class Kronos(): - def __init__(self, api): - self.api = api - - def on_startup(self): - logger.info("Kronos API started") - \ No newline at end of file diff --git a/scripts/python/API/olympus.json b/scripts/python/API/olympus.json index 7159c6ee..b81d60e5 100644 --- a/scripts/python/API/olympus.json +++ b/scripts/python/API/olympus.json @@ -4,7 +4,7 @@ "port": 4512 }, "authentication": { - "gameMasterPassword": "a474219e5e9503c84d59500bb1bda3d9ade81e52d9fa1c234278770892a6dd74", + "gameMasterPassword": "a00a5973aacb17e4659125fbe10f4160d096dd84b2f586d2d75669462a30106d", "blueCommanderPassword": "7d2e1ef898b21db7411f725a945b76ec8dcad340ed705eaf801bc82be6fe8a4a", "redCommanderPassword": "abc5de7abdb8ed98f6d11d22c9d17593e339fde9cf4b9e170541b4f41af937e3" }, diff --git a/scripts/python/API/unit/temp_replace.py b/scripts/python/API/unit/temp_replace.py deleted file mode 100644 index 8fe34a54..00000000 --- a/scripts/python/API/unit/temp_replace.py +++ /dev/null @@ -1,18 +0,0 @@ -import re - -# Read the file -with open('unit.py', 'r', encoding='utf-8') as f: - content = f.read() - -# Pattern to match callback invocations -pattern = r'self\.on_property_change_callbacks\[\"(\w+)\"\]\(self, self\.(\w+)\)' -replacement = r'self._trigger_callback("\1", self.\2)' - -# Replace all matches -new_content = re.sub(pattern, replacement, content) - -# Write back to file -with open('unit.py', 'w', encoding='utf-8') as f: - f.write(new_content) - -print('Updated all callback invocations') diff --git a/scripts/python/API/unit/unit.py b/scripts/python/API/unit/unit.py index f64cdfa5..dc8e1d0f 100644 --- a/scripts/python/API/unit/unit.py +++ b/scripts/python/API/unit/unit.py @@ -3,7 +3,7 @@ import asyncio from data.data_extractor import DataExtractor from data.data_indexes import DataIndexes -from data.data_types import LatLng, TACAN, Radio, GeneralSettings, Ammo, Contact, Offset +from data.data_types import DrawArgument, LatLng, TACAN, Radio, GeneralSettings, Ammo, Contact, Offset from data.roes import ROES from data.states import states from utils.utils import enum_to_coalition @@ -81,6 +81,8 @@ class Unit: self.targeting_range = 0.0 self.aim_method_range = 0.0 self.acquisition_range = 0.0 + self.cargo_weight = 0.0 + self.draw_arguments: List[DrawArgument] = [] self.previous_total_ammo = 0 self.total_ammo = 0 @@ -654,6 +656,20 @@ class Unit: # Trigger callbacks for property change if "airborne" in self.on_property_change_callbacks: self._trigger_callback("airborne", self.airborne) + elif datum_index == DataIndexes.CARGO_WEIGHT.value: + cargo_weight = data_extractor.extract_float64() + if cargo_weight != self.cargo_weight: + self.cargo_weight = cargo_weight + # Trigger callbacks for property change + if "cargo_weight" in self.on_property_change_callbacks: + self._trigger_callback("cargo_weight", self.cargo_weight) + elif datum_index == DataIndexes.DRAW_ARGUMENTS.value: + draw_arguments = data_extractor.extract_draw_arguments() + if draw_arguments != self.draw_arguments: + self.draw_arguments = draw_arguments + # Trigger callbacks for property change + if "draw_arguments" in self.on_property_change_callbacks: + self._trigger_callback("draw_arguments", self.draw_arguments) # --- API functions requiring ID --- def set_path(self, path: List[LatLng]): @@ -758,6 +774,8 @@ class Unit: def set_engagement_properties(self, barrel_height, muzzle_velocity, aim_time, shots_to_fire, shots_base_interval, shots_base_scatter, engagement_range, targeting_range, aim_method_range, acquisition_range): return self.api.send_command({"setEngagementProperties": {"ID": self.ID, "barrelHeight": barrel_height, "muzzleVelocity": muzzle_velocity, "aimTime": aim_time, "shotsToFire": shots_to_fire, "shotsBaseInterval": shots_base_interval, "shotsBaseScatter": shots_base_scatter, "engagementRange": engagement_range, "targetingRange": targeting_range, "aimMethodRange": aim_method_range, "acquisitionRange": acquisition_range}}) - + def set_cargo_weight(self, cargo_weight: float): + return self.api.send_command({"setCargoWeight": {"ID": self.ID, "weight": cargo_weight}}) - \ No newline at end of file + def register_draw_argument(self, argument: int, active: bool = True): + return self.api.send_command({"registerDrawArgument": {"ID": self.ID, "argument": argument, "active": active}}) \ No newline at end of file