diff --git a/Moose Development/Moose/Core/Astar.lua b/Moose Development/Moose/Core/Astar.lua index 544d18697..59a199e62 100644 --- a/Moose Development/Moose/Core/Astar.lua +++ b/Moose Development/Moose/Core/Astar.lua @@ -36,7 +36,7 @@ -- @field #table CostArg Optional arguments passed to the cost function. -- @extends Core.Base#BASE ---- When nothing goes right... Go left! +--- **When nothing goes right... Go left!** -- -- === -- @@ -510,7 +510,7 @@ function ASTAR.LoS(nodeA, nodeB, corridor) return los end ---- Function to check if two nodes have line of sight (LoS). +--- Function to check if distance between two nodes is less than a threshold distance. -- @param #ASTAR.Node nodeA First node. -- @param #ASTAR.Node nodeB Other node. -- @param #number distmax Max distance in meters. Default is 2000 m. @@ -550,7 +550,7 @@ end -- @return #number Distance between the two nodes. function ASTAR.DistRoad(nodeA, nodeB) - local path,dist,gotpath=nodeA.coordinate:GetPathOnRoad(nodeB.coordinate,IncludeEndpoints,Railroad,MarkPath,SmokePath) + local path, dist, gotpath=nodeA.coordinate:GetPathOnRoad(nodeB.coordinate,IncludeEndpoints,Railroad,MarkPath,SmokePath) if gotpath then return dist @@ -565,7 +565,6 @@ end -- Misc functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - --- Find the closest node from a given coordinate. -- @param #ASTAR self -- @param Core.Point#COORDINATE Coordinate. diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index b7ce3e4a0..b8db518b0 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -349,10 +349,10 @@ function ARMYGROUP:onafterSpawned(From, Event, To) if self.ai then -- Set default ROE option. - self:SetOptionROE(self.roe) + self:SwitchROE(self.option.ROE) -- Set default Alarm State option. - self:SetOptionAlarmstate(self.alarmstate) + self:SwitchAlarmstate(self.option.Alarm) end @@ -408,7 +408,7 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation) end -- Current set formation. - self.formation=wp.action + self.option.Formation=wp.action -- Current set speed in m/s. self.speed=wp.speed @@ -436,14 +436,14 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation) -- Current waypoint. - local current=self:GetCoordinate():WaypointGround(UTILS.MpsToKmph(self.speed), self.formation) + local current=self:GetCoordinate():WaypointGround(UTILS.MpsToKmph(self.speed), self.option.Formation) table.insert(waypoints, 1, current) table.insert(waypoints, 1, current) -- Seems to be better to add this twice. Otherwise, the passing waypoint functions is triggered to early! if #waypoints>2 then self:I(self.lid..string.format("Updateing route: WP %d-->%d-->%d (#%d), Speed=%.1f knots, Formation=%s", - self.currentwp, n, #self.waypoints, #waypoints-2, UTILS.MpsToKnots(self.speed), tostring(self.formation))) + self.currentwp, n, #self.waypoints, #waypoints-2, UTILS.MpsToKnots(self.speed), tostring(self.option.Formation))) -- Route group to all defined waypoints remaining. self:Route(waypoints) @@ -829,6 +829,11 @@ function ARMYGROUP:_InitGroup() return self end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Option Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 143b0f9e1..7c25a4a1d 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -1362,7 +1362,7 @@ function AUFTRAG:SetEngageAsGroup(Switch) return self end ---- Set engage altitude. +--- Set engage altitude. This is the altitude passed to the DCS task. In the ME it is the tickbox ALTITUDE ABOVE. -- @param #AUFTRAG self -- @param #string Altitude Altitude in feet. Default 6000 ft. -- @return #AUFTRAG self @@ -1376,7 +1376,7 @@ function AUFTRAG:SetEngageAltitude(Altitude) return self end ---- Set mission altitude. +--- Set mission altitude. This is the altitude of the waypoint create where the DCS task is executed. -- @param #AUFTRAG self -- @param #string Altitude Altitude in feet. -- @return #AUFTRAG self diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 940da0b03..cd3087562 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -3513,208 +3513,7 @@ end -- OPTION FUNCTIONS ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Set default TACAN parameters. AA TACANs are always on "Y" band. --- @param #FLIGHTGROUP self --- @param #number Channel TACAN channel. --- @param #string Morse Morse code. Default "XXX". --- @return #FLIGHTGROUP self -function FLIGHTGROUP:SetDefaultTACAN(Channel, Morse) - self.tacanChannelDefault=Channel - self.tacanMorseDefault=Morse or "XXX" - - return self -end - ---- Activate TACAN beacon. --- @param #FLIGHTGROUP self --- @param #number TACANChannel TACAN Channel. --- @param #string TACANMorse TACAN morse code. --- @return #FLIGHTGROUP self -function FLIGHTGROUP:SwitchTACANOn(TACANChannel, TACANMorse) - - if self:IsAlive() then - - local unit=self.group:GetUnit(1) --Wrapper.Unit#UNIT - - if unit and unit:IsAlive() then - - local Type=4 - local System=5 - local UnitID=unit:GetID() - local TACANMode="Y" - local Frequency=UTILS.TACANToFrequency(TACANChannel, TACANMode) - - unit:CommandActivateBeacon(Type, System, Frequency, UnitID, TACANChannel, TACANMode, true, TACANMorse, true) - - self.tacanBeacon=unit - self.tacanChannel=TACANChannel - self.tacanMorse=TACANMorse - - self.tacanOn=true - - self:I(self.lid..string.format("Switching TACAN to Channel %dY Morse %s", self.tacanChannel, tostring(self.tacanMorse))) - - end - - end - - return self -end - ---- Deactivate TACAN beacon. --- @param #FLIGHTGROUP self --- @return #FLIGHTGROUP self -function FLIGHTGROUP:SwitchTACANOff() - - if self.tacanBeacon and self.tacanBeacon:IsAlive() then - self.tacanBeacon:CommandDeactivateBeacon() - end - - self:I(self.lid..string.format("Switching TACAN OFF")) - - self.tacanOn=false - -end - ---- Set default Radio frequency and modulation. --- @param #FLIGHTGROUP self --- @param #number Frequency Radio frequency in MHz. Default 251 MHz. --- @param #number Modulation Radio modulation. Default `radio.Modulation.AM`. --- @return #FLIGHTGROUP self -function FLIGHTGROUP:SetDefaultRadio(Frequency, Modulation) - - self.radioFreqDefault=Frequency or 251 - self.radioModuDefault=Modulation or radio.modulation.AM - - self.radioOn=false - - self.radioUse=true - - return self -end - ---- Get current Radio frequency and modulation. --- @param #FLIGHTGROUP self --- @return #number Radio frequency in MHz or nil. --- @return #number Radio modulation or nil. -function FLIGHTGROUP:GetRadio() - return self.radioFreq, self.radioModu -end - ---- Turn radio on. --- @param #FLIGHTGROUP self --- @param #number Frequency Radio frequency in MHz. --- @param #number Modulation Radio modulation. Default `radio.Modulation.AM`. --- @return #FLIGHTGROUP self -function FLIGHTGROUP:SwitchRadioOn(Frequency, Modulation) - - if self:IsAlive() and Frequency then - - Modulation=Modulation or radio.Modulation.AM - - local group=self.group --Wrapper.Group#GROUP - - group:SetOption(AI.Option.Air.id.SILENCE, false) - - group:CommandSetFrequency(Frequency, Modulation) - - self.radioFreq=Frequency - self.radioModu=Modulation - self.radioOn=true - - self:I(self.lid..string.format("Switching radio to frequency %.3f MHz %s", self.radioFreq, UTILS.GetModulationName(self.radioModu))) - - end - - return self -end - ---- Turn radio off. --- @param #FLIGHTGROUP self --- @return #FLIGHTGROUP self -function FLIGHTGROUP:SwitchRadioOff() - - if self:IsAlive() then - - self.group:SetOption(AI.Option.Air.id.SILENCE, true) - - self.radioFreq=nil - self.radioModu=nil - self.radioOn=false - - self:I(self.lid..string.format("Switching radio OFF")) - - end - - return self -end - ---- Set default formation. --- @param #FLIGHTGROUP self --- @param #number Formation The formation the groups flies in. --- @return #FLIGHTGROUP self -function FLIGHTGROUP:SetDefaultFormation(Formation) - - self.formationDefault=Formation - - return self -end - ---- Switch to a specific formation. --- @param #FLIGHTGROUP self --- @param #number Formation New formation the group will fly in. --- @return #FLIGHTGROUP self -function FLIGHTGROUP:SwitchFormation(Formation) - - if self:IsAlive() and Formation then - - self.group:SetOption(AI.Option.Air.id.FORMATION, Formation) - - self.formation=Formation - - self:I(self.lid..string.format("Switching formation to %d", self.formation)) - - end - - return self -end - ---- Set default formation. --- @param #FLIGHTGROUP self --- @param #number CallsignName Callsign name. --- @param #number CallsignNumber Callsign number. --- @return #FLIGHTGROUP self -function FLIGHTGROUP:SetDefaultCallsign(CallsignName, CallsignNumber) - - self.callsignNameDefault=CallsignName - self.callsignNumberDefault=CallsignNumber or 1 - - return self -end - ---- Switch to a specific callsign. --- @param #FLIGHTGROUP self --- @param #number CallsignName Callsign name. --- @param #number CallsignNumber Callsign number. --- @return #FLIGHTGROUP self -function FLIGHTGROUP:SwitchCallsign(CallsignName, CallsignNumber) - - if self:IsAlive() and CallsignName then - - self.callsignName=CallsignName - self.callsignNumber=CallsignNumber or 1 - - self:I(self.lid..string.format("Switching callsign to %d-%d", self.callsignName, self.callsignNumber)) - - local group=self.group --Wrapper.Group#GROUP - - group:CommandSetCallsign(self.callsignName, self.callsignNumber) - - end - - return self -end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/NavyGroup.lua b/Moose Development/Moose/Ops/NavyGroup.lua index bb6a31ccd..048aac73f 100644 --- a/Moose Development/Moose/Ops/NavyGroup.lua +++ b/Moose Development/Moose/Ops/NavyGroup.lua @@ -1152,6 +1152,12 @@ function NAVYGROUP:_InitGroup() return self end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Option Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 9587aa307..894d8ecf8 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -54,34 +54,22 @@ -- @field #number traveltime Time. -- @field #boolean ispathfinding If true, group is on pathfinding route. -- --- @field #number tacanChannelDefault The default TACAN channel. --- @field #string tacanMorseDefault The default TACAN morse code. --- @field #number tacanChannel The currenly used TACAN channel. --- @field #string tacanMorse The currently used TACAN morse code. --- @field #boolean tacanOn If true, TACAN is currently active. --- @field Wrapper.Unit#UNIT tacanBeacon The unit acting as TACAN beacon. -- --- @field #number radioFreqDefault Default radio frequency in MHz. --- @field #number radioFreq Currently used radio frequency in MHz. --- @field #number radioModuDefault Default Radio modulation `radio.modulation.AM` or `radio.modulation.FM`. --- @field #number radioModu Currently used radio modulation `radio.modulation.AM` or `radio.modulation.FM`. +-- @field #OPSGROUP.Radio radio Current radio settings. +-- @field #OPSGROUP.Radio radioDefault Default radio settings. -- @field #boolean radioOn If true, radio is currently turned on. -- @field Core.RadioQueue#RADIOQUEUE radioQueue Radio queue. -- --- @field #boolean eplrsDefault Default EPLRS data link setting. --- @field #boolean eplrs If true, EPLRS data link is on. +-- @field #OPSGROUP.Beacon tacan Current TACAN settings. +-- @field #OPSGROUP.Beacon tacanDefault Default TACAN settings. +-- @field #boolean tacanOn If true, TACAN is currently active. -- --- @field #string roeDefault Default ROE setting. --- @field #string roe Current ROE setting. +-- @field #OPSGROUP.Beacon icls Current ICLS settings. +-- @field #OPSGROUP.Beacon iclsDefault Default ICLS settings. +-- @field #boolean iclsOn If true, ICLS is currently active. -- --- @field #string rotDefault Default ROT setting. --- @field #string rot Current ROT setting. --- --- @field #string alarmstateDefault Default Alarm State setting. --- @field #string alarmstate Current Alarm State setting. --- --- @field #number formationDefault Default formation setting. --- @field #number formation Current formation setting. +-- @field #OPSGROUP.Option option Current optional settings. +-- @field #OPSGROUP.Option optionDefault Default option settings. -- -- @field Core.Astar#ASTAR Astar path finding. -- @@ -129,6 +117,17 @@ OPSGROUP = { groupinitialized = nil, respawning = nil, wpcounter = 1, + radio = {}, + radioDefault = {}, + option = {}, + optionDefault = {}, + tacan = {}, + tacanDefault = {}, + icls = {}, + iclsDefault = {}, + callsign = {}, + callsignDefault = {}, + } --- Status of group element. @@ -199,6 +198,33 @@ OPSGROUP.TaskType={ -- @field DCS#Task DCStask DCS task structure table. -- @field #number WaypointIndex Waypoint number at which the enroute task is added. +--- Beacon data. +-- @type OPSGROUP.Beacon +-- @field #number Channel Channel. +-- @field #number Morse Morse Code. +-- @field #string Band Band "X" or "Y" for TACAN beacon. +-- @field #string UnitName Name of the unit acting as beacon. + +--- Radio data. +-- @type OPSGROUP.Radio +-- @field #number Freq Frequency +-- @field #number Modu Modulation. + +--- Callsign data +-- @type OPSGROUP.Callsign +-- @field #number Name +-- @field #number Number1 Number 1 +-- @field #number Number2 Number 2 + +--- Option data. +-- @type OPSGROUP.Option +-- @field #number ROE Rule of engagement. +-- @field #number ROT Reaction on threat. +-- @field #number Alarm Alarm state. +-- @field #number Formation Formation. +-- @field #boolean EPLRS data link. +-- @field #boolean Disperse Disperse under fire. + --- Ammo data. -- @type OPSGROUP.Ammo -- @field #number Total Total amount of ammo. @@ -831,8 +857,6 @@ function OPSGROUP:GetExpectedSpeed() end - - --- Remove a waypoint with a ceratin UID. -- @param #OPSGROUP self -- @param #number uid Waypoint UID. @@ -2723,7 +2747,7 @@ end -- @param #number roe ROE of group. Default is `ENUMS.ROE.ReturnFire`. -- @return #OPSGROUP self function OPSGROUP:SetDefaultROE(roe) - self.roeDefault=roe or ENUMS.ROE.ReturnFire + self.optionDefault.ROE=roe or ENUMS.ROE.ReturnFire return self end @@ -2731,13 +2755,13 @@ end -- @param #OPSGROUP self -- @param #string roe ROE of group. Default is the value defined by :SetDefaultROE(). -- @return #OPSGROUP self -function OPSGROUP:SetOptionROE(roe) +function OPSGROUP:SwitchROE(roe) - self.roe=roe or self.roeDefault + self.option.ROE=roe or self.optionDefault.ROE if self:IsAlive() then - self.group:OptionROE(self.roe) + self.group:OptionROE(self.option.ROE) self:I(self.lid..string.format("Setting current ROE=%d (0=WeaponFree, 1=OpenFireWeaponFree, 2=OpenFire, 3=ReturnFire, 4=WeaponHold)", self.roe)) else @@ -2751,15 +2775,15 @@ end -- @param #OPSGROUP self -- @return #number Current ROE. function OPSGROUP:GetROE() - return self.roe + return self.option.ROE end --- Set the default ROT for the group. This is the ROT state gets when the group is spawned or to which it defaults back after a mission. -- @param #OPSGROUP self --- @param #number roe ROE of group. Default is ENUMS.ROT.PassiveDefense. +-- @param #number rot ROT of group. Default is ENUMS.ROT.PassiveDefense. -- @return #OPSGROUP self -function OPSGROUP:SetDefaultROT(roe) - self.rotDefault=roe or ENUMS.ROT.PassiveDefense +function OPSGROUP:SetDefaultROT(rot) + self.optionDefault.ROT=rot or ENUMS.ROT.PassiveDefense return self end @@ -2767,13 +2791,13 @@ end -- @param #OPSGROUP self -- @param #string rot ROT of group. Default is the value defined by :SetDefaultROT(). -- @return #OPSGROUP self -function OPSGROUP:SetOptionROT(rot) +function OPSGROUP:SwitchROT(rot) - self.rot=rot or self.rotDefault + self.option.ROT=rot or self.optionDefault.ROT if self:IsAlive() then - self.group:OptionROT(self.rot) + self.group:OptionROT(self.option.ROT) self:T2(self.lid..string.format("Setting current ROT=%d (0=NoReaction, 1=Passive, 2=Evade, 3=ByPass, 4=AllowAbort)", self.rot)) else @@ -2783,13 +2807,20 @@ function OPSGROUP:SetOptionROT(rot) return self end +--- Get current ROT of the group. +-- @param #OPSGROUP self +-- @return #number Current ROT. +function OPSGROUP:GetROT() + return self.option.ROT +end + --- Set the default Alarm State for the group. This is the state gets when the group is spawned or to which it defaults back after a mission. -- @param #OPSGROUP self -- @param #number alarmstate Alarm state of group. Default is `AI.Option.Ground.val.ALARM_STATE.AUTO` (0). -- @return #OPSGROUP self function OPSGROUP:SetDefaultAlarmstate(alarmstate) - self.alarmstateDefault=alarmstate or 0 + self.optionDefault.Alarm=alarmstate or 0 return self end @@ -2797,24 +2828,24 @@ end -- @param #OPSGROUP self -- @param #string alarmstate Alarm state of group. Default is the value defined by :SetDefaultAlarmstate(). -- @return #OPSGROUP self -function OPSGROUP:SetOptionAlarmstate(alarmstate) +function OPSGROUP:SwitchAlarmstate(alarmstate) - self.alarmstate=alarmstate or self.alarmstateDefault + self.option.Alarm=alarmstate or self.optionDefault.Alarm if self:IsAlive() then - if self.alarmstate==0 then + if self.option.Alarm==0 then self.group:OptionAlarmStateAuto() - elseif self.alarmstate==1 then + elseif self.option.Alarm==1 then self.group:OptionAlarmStateGreen() - elseif self.alarmstate==2 then + elseif self.option.Alarm==2 then self.group:OptionAlarmStateRed() else self:E("ERROR: Unknown Alarm State! Setting to AUTO.") self.group:OptionAlarmStateAuto() end - self:I(self.lid..string.format("Setting current Alarm State=%d (0=Auto, 1=Green, 2=Red)", self.alarmstate)) + self:I(self.lid..string.format("Setting current Alarm State=%d (0=Auto, 1=Green, 2=Red)", self.option.Alarm)) else -- TODO WARNING end @@ -2826,7 +2857,217 @@ end -- @param #OPSGROUP self -- @return #number Current Alarm State. function OPSGROUP:GetAlarmstate() - return self.alarmstate + return self.option.Alarm +end + +--- Set default TACAN parameters. AA TACANs are always on "Y" band. +-- @param #OPSGROUP self +-- @param #number Channel TACAN channel. +-- @param #string Morse Morse code. Default "XXX". +-- @return #OPSGROUP self +function OPSGROUP:SetDefaultTACAN(Channel, Morse) + + self.tacanChannelDefault=Channel + self.tacanMorseDefault=Morse or "XXX" + + self.tacan.Channel=Channel + self.tacan.Band=Band + self.tacan.Morse=Morse or "XXX" + self.tacan.UnitName=UnitName + + return self +end + +--- Activate TACAN beacon. +-- @param #OPSGROUP self +-- @param #number TACANChannel TACAN Channel. +-- @param #string TACANMorse TACAN morse code. +-- @return #OPSGROUP self +function OPSGROUP:SwitchTACAN(TACANChannel, TACANMorse) + + if self:IsAlive() then + + local unit=self.group:GetUnit(1) --Wrapper.Unit#UNIT + + if unit and unit:IsAlive() then + + local Type=4 + local System=5 + local UnitID=unit:GetID() + local TACANMode="Y" + local Frequency=UTILS.TACANToFrequency(TACANChannel, TACANMode) + + unit:CommandActivateBeacon(Type, System, Frequency, UnitID, TACANChannel, TACANMode, true, TACANMorse, true) + + self.tacanBeacon=unit + self.tacanChannel=TACANChannel + self.tacanMorse=TACANMorse + + self.tacanOn=true + + self:I(self.lid..string.format("Switching TACAN to Channel %dY Morse %s", self.tacanChannel, tostring(self.tacanMorse))) + + end + + end + + return self +end + +--- Deactivate TACAN beacon. +-- @param #OPSGROUP self +-- @return #OPSGROUP self +function OPSGROUP:SwitchTACANOff() + + if self.tacanBeacon and self.tacanBeacon:IsAlive() then + self.tacanBeacon:CommandDeactivateBeacon() + end + + self:I(self.lid..string.format("Switching TACAN OFF")) + + self.tacanOn=false + +end + +--- Set default Radio frequency and modulation. +-- @param #OPSGROUP self +-- @param #number Frequency Radio frequency in MHz. Default 251 MHz. +-- @param #number Modulation Radio modulation. Default `radio.Modulation.AM`. +-- @return #OPSGROUP self +function OPSGROUP:SetDefaultRadio(Frequency, Modulation) + + self.radioDefault.Freq=Frequency or 251 + self.radioDefault.Modu=Modulation or radio.modulation.AM + + return self +end + +--- Get current Radio frequency and modulation. +-- @param #OPSGROUP self +-- @return #number Radio frequency in MHz or nil. +-- @return #number Radio modulation or nil. +function OPSGROUP:GetRadio() + return self.radio.Freq, self.radio.Modu +end + +--- Turn radio on. +-- @param #OPSGROUP self +-- @param #number Frequency Radio frequency in MHz. +-- @param #number Modulation Radio modulation. Default `radio.Modulation.AM`. +-- @return #OPSGROUP self +function OPSGROUP:SwitchRadio(Frequency, Modulation) + + if self:IsAlive() and Frequency then + + Modulation=Modulation or radio.Modulation.AM + + local group=self.group --Wrapper.Group#GROUP + + if not self.radioOn then + group:SetOption(AI.Option.Air.id.SILENCE, false) + end + + group:CommandSetFrequency(Frequency, Modulation) + + self.radio.Freq=Frequency + self.radio.Modu=Modulation + + -- Radio is on. + self.radioOn=true + + self:I(self.lid..string.format("Switching radio to frequency %.3f MHz %s", self.radioFreq, UTILS.GetModulationName(self.radioModu))) + + end + + return self +end + +--- Turn radio off. +-- @param #OPSGROUP self +-- @return #OPSGROUP self +function OPSGROUP:TurnRadioOff() + + if self:IsAlive() then + + self.group:SetOption(AI.Option.Air.id.SILENCE, true) + + --self.radioFreq=nil + --self.radioModu=nil + + -- Radio is off. + self.radioOn=false + + self:I(self.lid..string.format("Switching radio OFF")) + + end + + return self +end + +--- Set default formation. +-- @param #OPSGROUP self +-- @param #number Formation The formation the groups flies in. +-- @return #OPSGROUP self +function OPSGROUP:SetDefaultFormation(Formation) + + self.formationDefault=Formation + + return self +end + +--- Switch to a specific formation. +-- @param #OPSGROUP self +-- @param #number Formation New formation the group will fly in. +-- @return #OPSGROUP self +function OPSGROUP:SwitchFormation(Formation) + + if self:IsAlive() and Formation then + + self.group:SetOption(AI.Option.Air.id.FORMATION, Formation) + + self.formation=Formation + + self:I(self.lid..string.format("Switching formation to %d", self.formation)) + + end + + return self +end + +--- Set default formation. +-- @param #OPSGROUP self +-- @param #number CallsignName Callsign name. +-- @param #number CallsignNumber Callsign number. +-- @return #OPSGROUP self +function OPSGROUP:SetDefaultCallsign(CallsignName, CallsignNumber) + + self.callsignNameDefault=CallsignName + self.callsignNumberDefault=CallsignNumber or 1 + + return self +end + +--- Switch to a specific callsign. +-- @param #OPSGROUP self +-- @param #number CallsignName Callsign name. +-- @param #number CallsignNumber Callsign number. +-- @return #OPSGROUP self +function OPSGROUP:SwitchCallsign(CallsignName, CallsignNumber) + + if self:IsAlive() and CallsignName then + + self.callsignName=CallsignName + self.callsignNumber=CallsignNumber or 1 + + self:I(self.lid..string.format("Switching callsign to %d-%d", self.callsignName, self.callsignNumber)) + + local group=self.group --Wrapper.Group#GROUP + + group:CommandSetCallsign(self.callsignName, self.callsignNumber) + + end + + return self end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Utilities/Profiler.lua b/Moose Development/Moose/Utilities/Profiler.lua new file mode 100644 index 000000000..7d53dd3b6 --- /dev/null +++ b/Moose Development/Moose/Utilities/Profiler.lua @@ -0,0 +1,295 @@ +--- **Utils** - Lua Profiler. +-- +-- +-- +-- === +-- +-- ### Author: **TAW CougarNL**, *funkyfranky* +-- +-- @module Utilities.PROFILER +-- @image MOOSE.JPG + + +--- PROFILER class. +-- @type PROFILER +-- @field #string ClassName Name of the class. +-- @field #table Counters Counters. +-- @field #table dInfo Info. +-- @field #table fTime Function time. +-- @field #table fTimeTotal Total function time. +-- @field #table eventhandler Event handler to get mission end event. + +--- *The emperor counsels simplicity. First principles. Of each particular thing, ask: What is it in itself, in its own constitution? What is its causal nature? * +-- +-- === +-- +-- ![Banner Image](..\Presentations\Utilities\PROFILER_Main.jpg) +-- +-- # The PROFILER Concept +-- +-- Profile your lua code. +-- +-- # Prerequisites +-- +-- The modules **os** and **lfs** need to be desanizied. +-- +-- +-- # Start +-- +-- The profiler can simply be started by +-- +-- PROFILER.Start() +-- +-- The start can be delayed by specifying a the amount of seconds as argument, e.g. PROFILER.Start(60) to start profiling in 60 seconds. +-- +-- # Stop +-- +-- The profiler automatically stops when the mission ends. But it can be stopped any time by calling +-- +-- PROFILER.Stop() +-- +-- The stop call can be delayed by specifying the delay in seconds as optional argument, e.g. PROFILER.Stop(120) to stop it in 120 seconds. +-- +-- # Output +-- +-- The profiler output is written to a file in your DCS home folder +-- +-- X:\User\\Saved Games\DCS OpenBeta\Logs +-- +-- ## Sort By +-- +-- By default the output is sorted with respect to the total time a function used. +-- +-- The output can also be sorted with respect to the number of times the function was called by setting +-- +-- PROFILER.sortBy=1 +-- +-- Lua profiler. +-- @field #PROFILER +PROFILER = { + ClassName = "PROFILER", + Counters = {}, + dInfo = {}, + fTime = {}, + fTimeTotal = {}, + eventHandler = {}, + startTime = nil, + endTime = nil, + runTime = nil, + sortBy = 1, + logUnknown = false, + lowCpsThres = 5, +} + +PROFILER.sortBy=1 -- Sort reports by 0=Count, 1=Total time by function +PROFILER.logUnknown=false -- Log unknown functions +PROFILER.lowCpsThres=5 -- Skip results with less than X calls per second + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Start/Stop Profiler +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Start profiler. +function PROFILER.Start() + + PROFILER.startTime=timer.getTime() + PROFILER.endTime=0 + PROFILER.runTime=0 + + -- Set hook. + debug.sethook(PROFILER.hook, "cr") + + -- Add event handler. + world.addEventHandler(PROFILER.eventHandler) + + -- Message to screen. + local function showProfilerRunning() + timer.scheduleFunction(showProfilerRunning, nil, timer.getTime()+600) + trigger.action.outText("### Profiler running ###", 600) + end + + -- Message. + showProfilerRunning() + +end + +--- Stop profiler. +-- @param #number delay Delay before stop in seconds. +function PROFILER.Stop(delay) + + if delay and delay>0 then + + BASE:ScheduleOnce(delay, PROFILER.Stop) + + else + + -- Remove hook. + debug.sethook() + + -- Set end time. + PROFILER.endTime=timer.getTime() + + -- Run time. + PROFILER.runTime=PROFILER.endTime-PROFILER.startTime + + -- Show info. + PROFILER.showInfo() + + end + +end + +--- Event handler. +function PROFILER.eventHandler:onEvent(event) + if event.id==world.event.S_EVENT_MISSION_END then + PROFILER.Stop() + end +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Hook +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Debug hook. +-- @param #table event Event. +function PROFILER.hook(event) + + local f=debug.getinfo(2, "f").func + + if event=='call' then + + if PROFILER.Counters[f]==nil then + + PROFILER.Counters[f]=1 + PROFILER.dInfo[f]=debug.getinfo(2,"Sn") + + if PROFILER.fTimeTotal[f]==nil then + PROFILER.fTimeTotal[f]=0 + end + + else + PROFILER.Counters[f]=PROFILER.Counters[f]+1 + end + + if PROFILER.fTime[f]==nil then + PROFILER.fTime[f]=os.clock() + end + + elseif (event=='return') then + + if PROFILER.fTime[f]~=nil then + PROFILER.fTimeTotal[f]=PROFILER.fTimeTotal[f]+(os.clock()-PROFILER.fTime[f]) + PROFILER.fTime[f]=nil + end + + end + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Data +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get data. +-- @param #function func Function. +-- @param #boolean detailed Not used. +function PROFILER.getData(func, detailed) + local n=PROFILER.dInfo[func] + if n.what=="C" then + return n.name, "", "", PROFILER.fTimeTotal[func] + end + return n.name, n.short_src, n.linedefined, PROFILER.fTimeTotal[func] +end + +--- Write text to log file. +-- @param #function f The file. +-- @param #string txt The text. +function PROFILER._flog(f, txt) + f:write(txt.."\r\n") + env.info("Profiler Analysis") + env.info(txt) +end + +--- Show table. +-- @param #table t Data table. +-- @param #function f The file. +-- @param #boolean detailed Show detailed info. +function PROFILER.showTable(t, f, detailed) + for i=1, #t do + + local cps=t[i].count/PROFILER.runTime + + if (cps>=PROFILER.lowCpsThres) then + + if (detailed==false) then + PROFILER._flog(f,"- Function: "..t[i].func..": "..tostring(t[i].count).." ("..string.format("%.01f",cps).."/sec) Time: "..string.format("%g",t[i].tm).." seconds") + else + PROFILER._flog(f,"- Function: "..t[i].func..": "..tostring(t[i].count).." ("..string.format("%.01f",cps).."/sec) "..tostring(t[i].src)..":"..tostring(t[i].line).." Time: "..string.format("%g",t[i].tm).." seconds") + end + + end + end +end + +--- Write info to output file. +function PROFILER.showInfo() + + -- Output file. + local file=lfs.writedir()..[[Logs\]].."_LuaProfiler.txt" + local f=io.open(file, 'w') + + -- Gather data. + local t={} + for func, count in pairs(PROFILER.Counters) do + + local s,src,line,tm=PROFILER.getData(func, false) + + if PROFILER.logUnknown==true then + if s==nil then s="" end + end + + if (s~=nil) then + t[#t+1]= + { func=s, + src=src, + line=line, + count=count, + tm=tm, + } + end + + end + + -- Sort result. + if PROFILER.sortBy==0 then + table.sort(t, function(a,b) return a.count>b.count end ) + end + if (PROFILER.sortBy==1) then + table.sort(t, function(a,b) return a.tm>b.tm end ) + end + + -- Write data. + PROFILER._flog(f,"") + PROFILER._flog(f,"#### #### #### #### #### ##### #### #### #### #### ####") + PROFILER._flog(f,"#### #### #### ---- Profiler Report ---- #### #### ####") + PROFILER._flog(f,"#### Profiler Runtime: "..string.format("%.01f",PROFILER.runTime/60).." minutes") + PROFILER._flog(f,"#### #### #### #### #### ##### #### #### #### #### ####") + PROFILER._flog(f,"") + PROFILER.showTable(t, f, false) + + -- Detailed data. + PROFILER._flog(f,"") + PROFILER._flog(f,"#### #### #### #### #### #### #### #### #### #### #### #### ####") + PROFILER._flog(f,"#### #### #### ---- Profiler Detailed Report ---- #### #### ####") + PROFILER._flog(f,"#### #### #### #### #### #### #### #### #### #### #### #### ####") + PROFILER._flog(f,"") + PROFILER.showTable(t, f, true) + + -- Closing. + PROFILER._flog(f,"") + PROFILER._flog(f,"#### #### #### #### #### #### #### #### #### #### #### #### ####") + + -- Close file. + f:close() +end +