From 3d7234de1953e4557f629ac8cb1ff8e436800257 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 21 Dec 2020 10:02:41 +0100 Subject: [PATCH 1/5] Update to 0.2.6 Corrected error in dupe function naming, added doc about default values --- Moose Development/Moose/Functional/Mantis.lua | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 698b1c8fb..d2e7109d5 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -61,20 +61,35 @@ -- Set up your EWR system in the mission editor. Name the groups with common prefix like "Red EWR". Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc. -- [optional] Set up your HQ. Can be any group, e.g. a command vehicle. -- --- Start up your MANTIS +-- # 1. Start up your MANTIS -- -- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)` -- -- [optional] Use +-- -- * `MANTIS:SetEWRGrouping(radius)` -- * `MANTIS:SetEWRRange(radius)` -- * `MANTIS:SetSAMRadius(radius)` -- * `MANTIS:SetDetectInterval(interval)` --- * `MANTIS:SetAutoRelocate(hq, ewr)` +-- * `MANTIS:SetAutoRelocate(hq, ewr)` +-- -- to fine-tune your setup. -- -- `myredmantis:Start()` -- +-- # 2. Default settings +-- +-- By default, the following settings are active: +-- +-- * checkradius = 25000 (meters) - SAMs will engage enemy flights, if they are within a 25km around each SAM site - `MANTIS:SetSAMRadius(radius)` +-- * grouping = 5000 (meters) - Detection (EWR) will group enemy flights to areas of 5km for tracking - `MANTIS:SetEWRGrouping(radius)` +-- * acceptrange = 80000 (meters) - Detection (EWR) will on consider flights inside a 80km radius - `MANTIS:SetEWRRange(radius)` +-- * detectinterval = 30 (seconds) - MANTIS will decide every 30 seconds which SAM to activate - `MANTIS:SetDetectInterval(interval)` +-- * engagerange = 75 (percent) - SAMs will only fire if flights are inside of a 75% radius of their max firerange - `MANTIS:SetSAMRange(range)` +-- * dynamic = false - Group filtering is set to once, i.e. newly added groups will not be part of the setup by default - `MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic)` +-- * autorelocate = false - HQ and (mobile) EWR system will not relocate in random intervals between 30mins and 1 hour - `MANTIS:SetAutoRelocate(hq, ewr)` +-- * debug = false - Debugging reports on screen are set to off - `MANTIS:Debug(onoff)` +-- -- -- @field #MANTIS MANTIS = { @@ -115,7 +130,7 @@ do --@return #MANTIS self function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic) - -- TODO: Create some user functions for these + -- DONE: Create some user functions for these -- TODO: Make HQ useful -- TODO: Set SAMs to auto if EWR dies @@ -136,7 +151,7 @@ do self.verbose = false -- @field #string version - self.version="0.2.5" + self.version="0.2.6" env.info(string.format("***** Starting MANTIS Version %s *****", self.version)) -- Set the string id for output to DCS.log file. @@ -219,7 +234,7 @@ do --- Function to set SAM firing engage range, 0-100 percent, e.g. 75 -- @param #MANTIS self -- @param #number range Percent of the max fire range - function MANTIS:SetSAMRadius(range) + function MANTIS:SetSAMRange(range) local range = range or 75 if range < 0 or range > 100 then range = 75 From 478934ddd12fc2ba1ad67868b25c6c7b07fcb540 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Mon, 21 Dec 2020 10:31:06 +0100 Subject: [PATCH 2/5] Update Sead.lua to 0.2.2 -- corrected error when a single string instead of a table is given to SEAD:New() -- added a function to extend the watched set of SAMs --- Moose Development/Moose/Functional/Sead.lua | 29 ++++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index 62f71e137..f7e591897 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -15,7 +15,9 @@ -- -- === -- --- ### Authors: **FlightControl** +-- ### Authors: **FlightControl**, **applevangelist** +-- +-- Last Update: Dec 2020 -- -- === -- @@ -52,7 +54,7 @@ SEAD = { --- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles. -- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions... -- Chances are big that the missile will miss. --- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCSRTE on which evasive actions need to be taken. +-- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCS mission editor on which evasive actions need to be taken. -- @return SEAD -- @usage -- -- CCCP SEAD Defenses @@ -68,14 +70,33 @@ function SEAD:New( SEADGroupPrefixes ) self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix end else - self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes + self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes end self:HandleEvent( EVENTS.Shot ) - self:I("*** SEAD - Started Version 0.2.0") + self:I("*** SEAD - Started Version 0.2.2") return self end +--- Update the active SEAD Set +-- @param #SEAD self +-- @param #table SEADGroupPrefixes The prefixes to add, note: can also be a single #string +-- @return #SEAD self +function SEAD:UpdateSet( SEADGroupPrefixes ) + + self:F( SEADGroupPrefixes ) + + if type( SEADGroupPrefixes ) == 'table' then + for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do + self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix + end + else + self.SEADGroupPrefixes[SEADGroupPrefixes] = SEADGroupPrefixes + end + + return self +end + --- Sets the engagement range of the SAMs. Defaults to 75% to make it more deadly. Feature Request #1355 -- @param #SEAD self -- @param #number range Set the engagement range in percent, e.g. 50 From 9a19dd254e80b44fad7867c1a47d492a19ee37c1 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Tue, 22 Dec 2020 15:19:29 +0100 Subject: [PATCH 3/5] Mantis 0.3.0 - added advanced mode options Mantis 0.3.0 - added advanced mode options --- Moose Development/Moose/Functional/Mantis.lua | 208 ++++++++++++++++-- 1 file changed, 192 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index d2e7109d5..bd7560952 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -31,6 +31,7 @@ -- @field @{Core.Set#GROUP_SET} SAM_Group The SAM #GROUP_SET -- @field #string EWR_Templates_Prefix Prefix to build the #GROUP_SET for EWR group -- @field @{Core.Set#GROUP_SET} EWR_Group The EWR #GROUP_SET +-- @field @{Core.Set#GROUP_SET} Adv_EWR_Group The EWR #GROUP_SET used for advanced mode -- @field #string SEAD_Template_CC The ME name of the HQ object -- @field @{Wrapper.Group#GROUP} SEAD_CC The #GROUP object of the HQ -- @field #table SAM_Table Table of SAM sites @@ -44,6 +45,9 @@ -- @field #number detectinterval Interval in seconds for the target detection -- @field #number engagerange Firing engage range of the SAMs, see [https://wiki.hoggitworld.com/view/DCS_option_engagementRange] -- @field #boolean autorelocate Relocate HQ and EWR groups in random intervals. Note: You need to select units for this which are *actually mobile* +-- @field #boolean advanced Use advanced mode, will decrease reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an HQ object +-- @field #number adv_ratio Percentage to use for advanced mode, defaults to 100% +-- @field #number adv_state Advanced mode state tracker -- @extends @{Core.Base#BASE} @@ -80,7 +84,9 @@ -- # 2. Default settings -- -- By default, the following settings are active: --- +-- +-- * SAM_Templates_Prefix = "Red SAM" - SAM site group names in the mission editor begin with "Red SAM" +-- * EWR_Templates_Prefix = "Red EWR" - EWR group names in the mission editor begin with "Red EWR" - can also be AWACS -- * checkradius = 25000 (meters) - SAMs will engage enemy flights, if they are within a 25km around each SAM site - `MANTIS:SetSAMRadius(radius)` -- * grouping = 5000 (meters) - Detection (EWR) will group enemy flights to areas of 5km for tracking - `MANTIS:SetEWRGrouping(radius)` -- * acceptrange = 80000 (meters) - Detection (EWR) will on consider flights inside a 80km radius - `MANTIS:SetEWRRange(radius)` @@ -99,6 +105,7 @@ MANTIS = { SAM_Group = nil, EWR_Templates_Prefix = "", EWR_Group = nil, + Adv_EWR_Group = nil, SEAD_Template_CC = "", SEAD_CC = nil, SAM_Table = {}, @@ -111,6 +118,9 @@ MANTIS = { detectinterval = 30, engagerange = 75, autorelocate = false, + advanced = false, + adv_ratio = 100, + adv_state = 0, verbose = false } @@ -131,8 +141,9 @@ do function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic) -- DONE: Create some user functions for these - -- TODO: Make HQ useful - -- TODO: Set SAMs to auto if EWR dies + -- DONE: Make HQ useful + -- DONE: Set SAMs to auto if EWR dies + -- TODO: Refresh SAM table in dynamic mode self.name = name or "mymantis" self.SAM_Templates_Prefix = samprefix or "Red SAM" @@ -148,10 +159,14 @@ do self.engagerange = 75 self.autorelocate = false self.autorelocateunits = { HQ = false, EWR = false} + self.advanced = false + self.adv_ratio = 100 + self.adv_state = 0 self.verbose = false + self.Adv_EWR_Group = nil -- @field #string version - self.version="0.2.6" + self.version="0.3.0" env.info(string.format("***** Starting MANTIS Version %s *****", self.version)) -- Set the string id for output to DCS.log file. @@ -260,6 +275,22 @@ do return nil end end + + --- Function to set the HQ object for further use + -- @param #MANTIS self + -- @param Wrapper.GROUP#GROUP The HQ #GROUP object to be set as HQ + function MANTIS:SetCommandCenter(group) + local group = group or nil + if group ~= nil then + if type(group) == "string" then + self.SEAD_CC = GROUP:FindByName(group) + self.SEAD_Template_CC = group + else + self.SEAD_CC = group + self.SEAD_Template_CC = group:GetName() + end + end + end --- Function to set the detection interval -- @param #MANTIS self @@ -269,6 +300,108 @@ do self.detectinterval = interval end + --- Function to set Advanded Mode + -- @param #MANTIS self + -- @param #boolean onoff If true, will activate Advanced Mode + -- @param #number ratio [optional] Percentage to use for advanced mode, defaults to 100% + -- @usage Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an **HQ** object + -- E.g. `mymantis:SetAdvancedMode(true, 90)` + function MANTIS:SetAdvancedMode(onoff, ratio) + self:F({onoff, ratio}) + local onoff = onoff or false + local ratio = ratio or 100 + if (type(self.SEAD_Template_CC) == "string") and onoff and self.dynamic then + self.adv_ratio = ratio + self.advanced = true + self.adv_state = 0 + self.Adv_EWR_Group = SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterStart() + env.info(string.format("***** Starting Advanced Mode MANTIS Version %s *****", self.version)) + else + local text = self.lid.." Advanced Mode requires a HQ and dynamic to be set. Revisit your MANTIS:New() statement to add both." + local m= MESSAGE:New(text,10,"MANTIS",true):ToAll() + BASE:E(text) + end + end + + --- [Internal] Function to check if HQ is alive + -- @param #MANTIS self + -- @return #boolean True if HQ is alive, else false + function MANTIS:_CheckHQState() + local text = self.lid.." Checking HQ State" + self:T(text) + local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) + if self.verbose then env.info(text) end + -- start check + if self.advanced then + local hq = self.SEAD_Template_CC + local hqgrp = GROUP:FindByName(hq) + if hqgrp then + if hqgrp:IsAlive() then -- ok we're on, hq exists and as alive + env.info(self.lid.." HQ is alive!") + return true + else + env.info(self.lid.." HQ is dead!") + return false + end + end + end + end + + --- [Internal] Function to check if EWR is (at least partially) alive + -- @param #MANTIS self + -- @return #boolean True if EWR is alive, else false + function MANTIS:_CheckEWRState() + local text = self.lid.." Checking EWR State" + self:F(text) + local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) + if self.verbose then env.info(text) end + -- start check + if self.advanced then + local EWR_Group = self.Adv_EWR_Group + --local EWR_Set = EWR_Group.Set + local nalive = EWR_Group:CountAlive() + env.info(self.lid..string.format(" No of EWR alive is %d", nalive)) + if nalive > 0 then + return true + else + return false + end + end + end + + --- [Internal] Function to determine state of the advanced mode + -- @param #MANTIS self + -- @return #number Newly calculated interval + -- @return #number Previous state for tracking 0, 1, or 2 + function MANTIS:_CheckAdvState() + local text = self.lid.." Checking Advanced State" + self:F(text) + local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) + if self.verbose then env.info(text) end + -- start check + local currstate = self.adv_state -- save curr state for comparison later + local EWR_State = self:_CheckEWRState() + local HQ_State = self:_CheckHQState() + -- set state + if EWR_State and HQ_State then -- both alive + self.adv_state = 0 --everything is fine + elseif EWR_State or HQ_State then -- one alive + self.adv_state = 1 --slow down level 1 + else -- none alive + self.adv_state = 2 --slow down level 2 + end + -- calculate new detectioninterval + local interval = self.detectinterval -- e.g. 30 + local ratio = self.adv_ratio / 100 -- e.g. 80/100 = 0.8 + ratio = ratio * self.adv_state -- e.g 0.8*2 = 1.6 + local newinterval = interval + (interval * ratio) -- e.g. 30+(30*1.6) = 78 + local text = self.lid..string.format(" Calculated OldState/NewState/Interval: %d / %d / %d", currstate, self.adv_state, newinterval) + self:F(text) + local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) + if self.verbose then env.info(text) end + return newinterval, currstate + end + --- Function to set autorelocation for HQ and EWR objects. Note: Units must be actually mobile in DCS! -- @param #MANTIS self -- @param #boolean hq If true, will relocate HQ object @@ -289,7 +422,7 @@ do function MANTIS:_RelocateGroups() self:T(self.lid.." Relocating Groups") local text = self.lid.." Relocating Groups" - local m= MESSAGE:New(text,15,"MANTIS",true):ToAllIf(self.debug) + local m= MESSAGE:New(text,10,"MANTIS",true):ToAllIf(self.debug) if self.verbose then env.info(text) end if self.autorelocate then -- relocate HQ @@ -297,7 +430,7 @@ do local _hqgrp = self.SEAD_CC self:T(self.lid.." Relocating HQ") local text = self.lid.." Relocating HQ" - local m= MESSAGE:New(text,15,"MANTIS"):ToAll() + local m= MESSAGE:New(text,10,"MANTIS"):ToAll() _hqgrp:RelocateGroundRandomInRadius(20,500,true,true) end --relocate EWR @@ -310,7 +443,7 @@ do if _grp:IsGround() then self:T(self.lid.." Relocating EWR ".._grp:GetName()) local text = self.lid.." Relocating EWR ".._grp:GetName() - local m= MESSAGE:New(text,15,"MANTIS"):ToAllIf(self.debug) + local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) if self.verbose then env.info(text) end _grp:RelocateGroundRandomInRadius(20,500,true,true) end @@ -381,6 +514,7 @@ do -- @param #MANTIS self -- @return #MANTIS self function MANTIS:SetSAMStartState() + -- TODO: if using dynamic filtering, update SAM_Table and the (active) SEAD groups, pull req #1405/#1406 self:F(self.lid.."Setting SAM Start States") -- get SAM Group local SAM_SET = self.SAM_Group @@ -399,8 +533,9 @@ do if group:IsGround() then local grpname = group:GetName() local grpcoord = group:GetCoordinate() - local grpzone = ZONE_UNIT:New(grpname,group:GetUnit(1),5000) -- defense zone around each SAM site 5000 meters - table.insert( SAM_Tbl, {grpname, grpcoord, grpzone}) + --local grpzone = ZONE_UNIT:New(grpname,group:GetUnit(1),5000) -- defense zone around each SAM site 5000 meters + --table.insert( SAM_Tbl, {grpname, grpcoord, grpzone}) + table.insert( SAM_Tbl, {grpname, grpcoord}) -- make the table lighter, as I don't really use the zone here table.insert( SEAD_Grps, grpname ) end end @@ -427,7 +562,7 @@ do local detset = detection:GetDetectedItemCoordinates() self:F("Check:", {detset}) -- switch SAMs on/off if (n)one of the detected groups is inside their reach - local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates and i.3=zones of SAM sites + local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates for _,_data in pairs (samset) do local samcoordinate = _data[2] local name = _data[1] @@ -439,7 +574,7 @@ do --samgroup:OptionROEWeaponFree() --samgroup:SetAIOn() local text = string.format("SAM %s switched to alarm state RED!", name) - local m=MESSAGE:New(text,15,"MANTIS"):ToAllIf(self.debug) + local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) if self.verbose then env.info(self.lid..text) end end --end alive else @@ -449,7 +584,7 @@ do --samgroup:OptionROEWeaponFree() --samgroup:SetAIOn() local text = string.format("SAM %s switched to alarm state GREEN!", name) - local m=MESSAGE:New(text,15,"MANTIS"):ToAllIf(self.debug) + local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) if self.verbose then env.info(self.lid..text) end end --end alive end --end check @@ -459,14 +594,52 @@ do local function relocate() self:_RelocateGroups() end - -- timer to run the system + -- check advanced state + local function checkadvstate() + if self.advanced then + local interval, oldstate = self:_CheckAdvState() + local newstate = self.adv_state + if newstate ~= oldstate then + -- deal with new state + if newstate == 2 then + -- switch alarm state RED + if self.MantisTimer.isrunning then + self.MantisTimer:Stop() + self.MantisTimer.isrunning = false + end -- stop timer + local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates + for _,_data in pairs (samset) do + local name = _data[1] + local samgroup = GROUP:FindByName(name) + if samgroup:IsAlive() then + samgroup:OptionAlarmStateRed() + end -- end alive + end -- end for loop + elseif newstate <= 1 then + -- change MantisTimer to slow down or speed up + if self.MantisTimer.isrunning then + self.MantisTimer:Stop() + self.MantisTimer.isrunning = false + end + self.MantisTimer = TIMER:New(check,self.Detection) + self.MantisTimer:Start(5,interval,nil) + self.MantisTimer.isrunning = true + end + end -- end newstate vs oldstate + end -- end advanced + end + -- timers to run the system local interval = self.detectinterval self.MantisTimer = TIMER:New(check,self.Detection) self.MantisTimer:Start(5,interval,nil) - -- relocate HQ and EWR + self.MantisTimer.isrunning = true + -- timer to relocate HQ and EWR local relointerval = math.random(1800,3600) -- random between 30 and 60 mins self.MantisReloTimer = TIMER:New(relocate) self.MantisReloTimer:Start(relointerval,relointerval,nil) + -- timer for advanced state check + self.MantisAdvTimer = TIMER:New(checkadvstate) + self.MantisAdvTimer:Start(30,interval*5,nil) return self end @@ -474,12 +647,15 @@ do -- @param #MANTIS self -- @return #MANTIS self function MANTIS:Stop() - if self.MantisTimer then + if self.MantisTimer.isrunning then self.MantisTimer:Stop() end - if self.MantisReloTimer then + if self.autorelocate then self.MantisReloTimer:Stop() end + if self.advanced then + self.MantisAdvTimer:Stop() + end return self end From e08df3f9ce03d646ce6f69c436f6b99e05bbbf94 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 23 Dec 2020 16:09:49 +0100 Subject: [PATCH 4/5] Update Timer.lua --- Moose Development/Moose/Core/Timer.lua | 32 ++++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Timer.lua b/Moose Development/Moose/Core/Timer.lua index 9b8a9200b..258996d68 100644 --- a/Moose Development/Moose/Core/Timer.lua +++ b/Moose Development/Moose/Core/Timer.lua @@ -11,7 +11,7 @@ -- -- ### Author: **funkyfranky** -- @module Core.Timer --- @image CORE_Timer.png +-- @image Core_Scheduler.JPG --- TIMER class. @@ -19,6 +19,7 @@ -- @field #string ClassName Name of the class. -- @field #string lid Class id string for output to DCS log file. -- @field #number tid Timer ID returned by the DCS API function. +-- @field #number uid Unique ID of the timer. -- @field #function func Timer function. -- @field #table para Parameters passed to the timer function. -- @field #number Tstart Relative start time in seconds. @@ -26,9 +27,10 @@ -- @field #number dT Time interval between function calls in seconds. -- @field #number ncalls Counter of function calls. -- @field #number ncallsMax Max number of function calls. If reached, timer is stopped. +-- @field #boolean isrunning If `true`, timer is running. Else it was not started yet or was stopped. -- @extends Core.Base#BASE ---- *Better three hours too soon than a minute too late.* – William Shakespeare +--- *Better three hours too soon than a minute too late.* - William Shakespeare -- -- === -- @@ -36,7 +38,7 @@ -- -- # The TIMER Concept -- --- The TIMER class is the little sister of the SCHEDULER class. It does the same thing but is a bit easier to use and has less overhead. It should be sufficient in many cases. +-- The TIMER class is the little sister of the @{Core.Scheduler#SCHEDULER} class. It does the same thing but is a bit easier to use and has less overhead. It should be sufficient in many cases. -- -- It provides an easy interface to the DCS [timer.scheduleFunction](https://wiki.hoggitworld.com/view/DCS_func_scheduleFunction). -- @@ -62,20 +64,20 @@ -- -- Note that -- --- * if *Tstart* is not specified (*nil*), the first function call happens immediately. +-- * if *Tstart* is not specified (*nil*), the first function call happens immediately, i.e. after one millisecond. -- * if *dT* is not specified (*nil*), the function is called only once. -- * if *Duration* is not specified (*nil*), the timer runs forever or until stopped manually or until the max function calls are reached (see below). -- -- For example, -- -- mytimer:Start(3) -- Will call the function once after 3 seconds. --- mytimer:Start(nil, 0.5) -- Will call right now and then every 0.5 sec until all eternaty. +-- mytimer:Start(nil, 0.5) -- Will call right now and then every 0.5 sec until all eternity. -- mytimer:Start(nil, 2.0, 20) -- Will call right now and then every 2.0 sec for 20 sec. -- mytimer:Start(1.0, nil, 10) -- Does not make sense as the function is only called once anyway. -- -- ## Stopping the Timer -- --- The timer can be stopped manually by the @{#TIMER.Start}(*Delay*) function +-- The timer can be stopped manually by the @{#TIMER.Stop}(*Delay*) function -- -- mytimer:Stop() -- @@ -110,7 +112,7 @@ _TIMERID=0 --- TIMER class version. -- @field #string version -TIMER.version="0.1.0" +TIMER.version="0.1.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -142,6 +144,9 @@ function TIMER:New(Function, ...) -- Number of function calls. self.ncalls=0 + -- Not running yet. + self.isrunning=false + -- Increase counter _TIMERID=_TIMERID+1 @@ -185,6 +190,9 @@ function TIMER:Start(Tstart, dT, Duration) -- Set log id. self.lid=string.format("TIMER UID=%d/%d | ", self.uid, self.tid) + -- Is now running. + self.isrunning=true + -- Debug info. self:T(self.lid..string.format("Starting Timer in %.3f sec, dT=%s, Tstop=%s", self.Tstart-Tnow, tostring(self.dT), tostring(self.Tstop))) @@ -209,6 +217,9 @@ function TIMER:Stop(Delay) self:T(self.lid..string.format("Stopping timer by removing timer function after %d calls!", self.ncalls)) timer.removeFunction(self.tid) + -- Not running any more. + self.isrunning=false + -- Remove DB entry. --_TIMERDB[self.uid]=nil @@ -228,6 +239,13 @@ function TIMER:SetMaxFunctionCalls(Nmax) return self end +--- Check if the timer has been started and was not stopped. +-- @param #TIMER self +-- @return #boolean If `true`, the timer is running. +function TIMER:IsRunning() + return self.isrunning +end + --- Call timer function. -- @param #TIMER self -- @param #number time DCS model time in seconds. From c32cec4c5b11bfe1b93c1d68f26d4f948d7ea4b4 Mon Sep 17 00:00:00 2001 From: Applevangelist <72444570+Applevangelist@users.noreply.github.com> Date: Sun, 27 Dec 2020 16:24:13 +0100 Subject: [PATCH 5/5] Mantis.lua 0.3.5 Added option to use Awacs separate from EWR network with own detection range Extended documentation Added refresher for SAM table --- Moose Development/Moose/Functional/Mantis.lua | 251 +++++++++++++++--- 1 file changed, 207 insertions(+), 44 deletions(-) diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index bd7560952..c97068865 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -32,11 +32,12 @@ -- @field #string EWR_Templates_Prefix Prefix to build the #GROUP_SET for EWR group -- @field @{Core.Set#GROUP_SET} EWR_Group The EWR #GROUP_SET -- @field @{Core.Set#GROUP_SET} Adv_EWR_Group The EWR #GROUP_SET used for advanced mode --- @field #string SEAD_Template_CC The ME name of the HQ object --- @field @{Wrapper.Group#GROUP} SEAD_CC The #GROUP object of the HQ +-- @field #string HQ_Template_CC The ME name of the HQ object +-- @field @{Wrapper.Group#GROUP} HQ_CC The #GROUP object of the HQ -- @field #table SAM_Table Table of SAM sites -- @field #string lid Prefix for logging --- @field @{Functional.Detection#DETECTION_AREAS} Detection The #DETECTION_AREAS object +-- @field @{Functional.Detection#DETECTION_AREAS} Detection The #DETECTION_AREAS object for EWR +-- @field @{Functional.Detection#DETECTION_AREAS} AWACS_Detection The #DETECTION_AREAS object for AWACS -- @field #boolean debug Switch on extra messages -- @field #boolean verbose Switch on extra logging -- @field #number checkradius Radius of the SAM sites @@ -48,6 +49,8 @@ -- @field #boolean advanced Use advanced mode, will decrease reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an HQ object -- @field #number adv_ratio Percentage to use for advanced mode, defaults to 100% -- @field #number adv_state Advanced mode state tracker +-- @field #boolean advAwacs Boolean switch to use Awacs as a separate detection stream +-- @field #number awacsrange Detection range of an optional Awacs unit -- @extends @{Core.Base#BASE} @@ -65,7 +68,33 @@ -- Set up your EWR system in the mission editor. Name the groups with common prefix like "Red EWR". Can be e.g. AWACS or a combination of AWACS and Search Radars like e.g. EWR 1L13 etc. -- [optional] Set up your HQ. Can be any group, e.g. a command vehicle. -- --- # 1. Start up your MANTIS +-- # 1. Basic tactical considerations when setting up your SAM sites +-- +-- ## 1.1 Radar systems and AWACS +-- +-- Typically, your setup should consist of EWR (early warning) radars to detect and track targets, accompanied by AWACS if your scenario forsees that. Ensure that your EWR radars have a good coverage of the area you want to track. +-- **Location** is of highest importantance here. Whilst AWACS in DCS has almost the "all seeing eye", EWR don't have that. Choose your location wisely, against a mountain backdrop or inside a valley even the best EWR system +-- doesn't work well. Prefer higher-up locations with a good view; use F7 in-game to check where you actually placed your EWR and have a look around. Apart from the obvious choice, do also consider other radar units +-- for this role, most have "SR" (search radar) or "STR" (search and track radar) in their names, use the encyclopedia to see what they actually do. +-- +-- ## 1.2 SAM sites +-- +-- Typically your SAM should cover all attack ranges. The closer the enemy gets, the more systems you will need to deploy to defend your location. Use a combination of long-range systems like the SA-10/11, midrange like SA-6 and short-range like +-- SA-2 for defense (Patriot, Hawk, Gepard, Blindfire for the blue side). For close-up defense and defense against HARMs or low-flying aircraft, helicopters it is also advisable to deploy SA-15 TOR systems, Shilka, Strela and Tunguska units, as well as manpads (Think Gepard, Avenger, Chaparral, +-- Linebacker, Roland systems for the blue side). If possible, overlap ranges for mutual coverage. +-- +-- ## 1.3 Typical problems +-- +-- Often times, people complain because the detection cannot "see" oncoming targets and/or Mantis switches on too late. Three typial problems here are +-- +-- * bad placement of radar units, +-- * overestimation how far units can "see" and +-- * not taking into account that a SAM site will take (e.g for a SA-6) 30-40 seconds between switching to RED, acquiring the target and firing. +-- +-- An attacker doing 350knots will cover ca 180meters/second or thus more than 6km until the SA-6 fires. Use triggers zones and the ruler in the missione editor to understand distances and zones. Take into account that the ranges given by the circles +-- in the mission editor are absolute maximum ranges; in-game this is rather 50-75% of that depending on the system. Fiddle with placement and options to see what works best for your scenario, and remember **everything in here is in meters**. +-- +-- # 2. Start up your MANTIS with a basic setting -- -- `myredmantis = MANTIS:New("myredmantis","Red SAM","Red EWR",nil,"red",false)` -- @@ -80,13 +109,17 @@ -- to fine-tune your setup. -- -- `myredmantis:Start()` +-- +-- If you want to use a separate AWACS unit (default detection range: 250km) to support your EWR system, use e.g. the following setup: +-- +-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")` -- --- # 2. Default settings +-- # 3. Default settings -- -- By default, the following settings are active: -- -- * SAM_Templates_Prefix = "Red SAM" - SAM site group names in the mission editor begin with "Red SAM" --- * EWR_Templates_Prefix = "Red EWR" - EWR group names in the mission editor begin with "Red EWR" - can also be AWACS +-- * EWR_Templates_Prefix = "Red EWR" - EWR group names in the mission editor begin with "Red EWR" - can also be combined with an AWACS unit -- * checkradius = 25000 (meters) - SAMs will engage enemy flights, if they are within a 25km around each SAM site - `MANTIS:SetSAMRadius(radius)` -- * grouping = 5000 (meters) - Detection (EWR) will group enemy flights to areas of 5km for tracking - `MANTIS:SetEWRGrouping(radius)` -- * acceptrange = 80000 (meters) - Detection (EWR) will on consider flights inside a 80km radius - `MANTIS:SetEWRRange(radius)` @@ -96,6 +129,11 @@ -- * autorelocate = false - HQ and (mobile) EWR system will not relocate in random intervals between 30mins and 1 hour - `MANTIS:SetAutoRelocate(hq, ewr)` -- * debug = false - Debugging reports on screen are set to off - `MANTIS:Debug(onoff)` -- +-- # 4. Advanced Mode +-- +-- Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Awacs is counted as one EWR unit. It will set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option. +-- E.g. `mymantis:SetAdvancedMode( true, 90 )` +-- Use this option if you want to make use of or allow advanced SEAD tactics. -- -- @field #MANTIS MANTIS = { @@ -106,11 +144,12 @@ MANTIS = { EWR_Templates_Prefix = "", EWR_Group = nil, Adv_EWR_Group = nil, - SEAD_Template_CC = "", - SEAD_CC = nil, + HQ_Template_CC = "", + HQ_CC = nil, SAM_Table = {}, lid = "", Detection = nil, + AWACS_Detection = nil, debug = false, checkradius = 25000, grouping = 5000, @@ -121,7 +160,10 @@ MANTIS = { advanced = false, adv_ratio = 100, adv_state = 0, - verbose = false + AWACS_Prefix = "", + advAwacs = false, + verbose = false, + awacsrange = 250000 } ----------------------------------------------------------------------- @@ -133,22 +175,24 @@ do --@param #MANTIS self --@param #string name Name of this MANTIS for reporting --@param #string samprefix Prefixes for the SAM groups from the ME, e.g. all groups starting with "Red Sam..." - --@param #string ewrprefix Prefixes for the EWR and AWACS groups from the ME, e.g. all groups starting with "Red EWR..." + --@param #string ewrprefix Prefixes for the EWR groups from the ME, e.g. all groups starting with "Red EWR..." --@param #string hq Group name of your HQ (optional) --@param #string coaltion Coalition side of your setup, e.g. "blue", "red" or "neutral" - --@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) + --@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) (optional) + --@param #string awacs Group name of your Awacs (optional) --@return #MANTIS self - function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic) + function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs) -- DONE: Create some user functions for these -- DONE: Make HQ useful -- DONE: Set SAMs to auto if EWR dies - -- TODO: Refresh SAM table in dynamic mode + -- DONE: Refresh SAM table in dynamic mode + -- DONE: Treat Awacs separately, since they might be >80km off site self.name = name or "mymantis" self.SAM_Templates_Prefix = samprefix or "Red SAM" self.EWR_Templates_Prefix = ewrprefix or "Red EWR" - self.SEAD_Template_CC = hq or nil + self.HQ_Template_CC = hq or nil self.Coalition = coaltion or "red" self.SAM_Table = {} self.dynamic = dynamic or false @@ -164,9 +208,16 @@ do self.adv_state = 0 self.verbose = false self.Adv_EWR_Group = nil + self.AWACS_Prefix = awacs or nil + self.awacsrange = 250000 --TODO: 250km, User Function to change + if type(awacs) == "string" then + self.advAwacs = true + else + self.advAwacs = false + end -- @field #string version - self.version="0.3.0" + self.version="0.3.5" env.info(string.format("***** Starting MANTIS Version %s *****", self.version)) -- Set the string id for output to DCS.log file. @@ -193,9 +244,8 @@ do end -- set up CC - if self.SEAD_Template_CC then - self.SEAD_CC = GROUP:FindByName(self.SEAD_Template_CC) - --self.SEAD_CC = COMMANDCENTER:New(GROUP:FindByName(self.SEAD_Template_CC),self.SEAD_Template_CC) + if self.HQ_Template_CC then + self.HQ_CC = GROUP:FindByName(self.HQ_Template_CC) end -- Inherit everything from BASE class. local self = BASE:Inherit(self, BASE:New()) -- #MANTIS @@ -269,13 +319,25 @@ do -- @param #MANTIS self -- @return Wrapper.GROUP#GROUP The HQ #GROUP object or *nil* if it doesn't exist function MANTIS:GetCommandCenter() - if self.SEAD_CC then - return self.SEAD_CC + if self.HQ_CC then + return self.HQ_CC else return nil end end + --- Function to set separate AWACS detection instance + -- @param #MANTIS self + -- @param #string prefix Name of the AWACS group in the mission editor + function MANTIS:SetAwacs(prefix) + if prefix ~= nil then + if type(prefix) == "string" then + self.AWACS_Prefix = prefix + self.advAwacs = true + end + end + end + --- Function to set the HQ object for further use -- @param #MANTIS self -- @param Wrapper.GROUP#GROUP The HQ #GROUP object to be set as HQ @@ -283,11 +345,11 @@ do local group = group or nil if group ~= nil then if type(group) == "string" then - self.SEAD_CC = GROUP:FindByName(group) - self.SEAD_Template_CC = group + self.HQ_CC = GROUP:FindByName(group) + self.HQ_Template_CC = group else - self.SEAD_CC = group - self.SEAD_Template_CC = group:GetName() + self.HQ_CC = group + self.HQ_Template_CC = group:GetName() end end end @@ -304,13 +366,13 @@ do -- @param #MANTIS self -- @param #boolean onoff If true, will activate Advanced Mode -- @param #number ratio [optional] Percentage to use for advanced mode, defaults to 100% - -- @usage Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an **HQ** object + -- @usage Advanced mode will *decrease* reactivity of MANTIS, if HQ and/or EWR network dies. Set SAMs to RED state if both are dead. Requires usage of an **HQ** object and the **dynamic** option. -- E.g. `mymantis:SetAdvancedMode(true, 90)` function MANTIS:SetAdvancedMode(onoff, ratio) self:F({onoff, ratio}) local onoff = onoff or false local ratio = ratio or 100 - if (type(self.SEAD_Template_CC) == "string") and onoff and self.dynamic then + if (type(self.HQ_Template_CC) == "string") and onoff and self.dynamic then self.adv_ratio = ratio self.advanced = true self.adv_state = 0 @@ -333,7 +395,7 @@ do if self.verbose then env.info(text) end -- start check if self.advanced then - local hq = self.SEAD_Template_CC + local hq = self.HQ_Template_CC local hqgrp = GROUP:FindByName(hq) if hqgrp then if hqgrp:IsAlive() then -- ok we're on, hq exists and as alive @@ -360,6 +422,14 @@ do local EWR_Group = self.Adv_EWR_Group --local EWR_Set = EWR_Group.Set local nalive = EWR_Group:CountAlive() + if self.advAwacs then + local awacs = GROUP:FindByName(self.AWACS_Prefix) + if awacs ~= nil then + if awacs:IsAlive() then + nalive = nalive+1 + end + end + end env.info(self.lid..string.format(" No of EWR alive is %d", nalive)) if nalive > 0 then return true @@ -426,8 +496,8 @@ do if self.verbose then env.info(text) end if self.autorelocate then -- relocate HQ - if self.autorelocateunits.HQ and self.SEAD_CC then --only relocate if HQ exists - local _hqgrp = self.SEAD_CC + if self.autorelocateunits.HQ and self.HQ_CC then --only relocate if HQ exists + local _hqgrp = self.HQ_CC self:T(self.lid.." Relocating HQ") local text = self.lid.." Relocating HQ" local m= MESSAGE:New(text,10,"MANTIS"):ToAll() @@ -510,6 +580,38 @@ do return _MANTISdetection end + --- Function to start the detection via AWACS if defined as separate + -- @param #MANTIS self + -- @return Functional.Detection #DETECTION_AREAS The running detection set + function MANTIS:StartAwacsDetection() + self:F(self.lid.."Starting Awacs Detection") + + -- start detection + local group = self.AWACS_Prefix + local groupset = SET_GROUP:New():FilterPrefixes(group):FilterCoalitions(self.Coalition):FilterStart() + local grouping = self.grouping or 5000 + --local acceptrange = self.acceptrange or 80000 + local interval = self.detectinterval or 60 + + --@param Functional.Detection #DETECTION_AREAS _MANTISdetection [internal] The MANTIS detection object + _MANTISAwacs = DETECTION_AREAS:New( groupset, grouping ) --[internal] Grouping detected objects to 5000m zones + _MANTISAwacs:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER }) + _MANTISAwacs:SetAcceptRange(self.awacsrange) --250km + _MANTISAwacs:SetRefreshTimeInterval(interval) + _MANTISAwacs:Start() + + function _MANTISAwacs:OnAfterDetectedItem(From,Event,To,DetectedItem) + --BASE:I( { From, Event, To, DetectedItem }) + local debug = false + if DetectedItem.IsDetected and debug then + local Coordinate = DetectedItem.Coordinate -- Core.Point#COORDINATE + local text = "Awacs Detection at "..Coordinate:ToStringLLDMS() + local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) + end + end + return _MANTISAwacs + end + --- Function to set the SAM start state -- @param #MANTIS self -- @return #MANTIS self @@ -524,24 +626,53 @@ do local engagerange = self.engagerange -- firing range in % of max --cycle through groups and set alarm state etc for _i,_group in pairs (SAM_Grps) do - --for i=1,#SAM_Grps do local group = _group - group:OptionAlarmStateGreen() - --group:OptionROEHoldFire() - --group:SetAIOn() + group:OptionAlarmStateGreen() -- AI off + group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --default engagement will be 75% of firing range + if group:IsGround() then + local grpname = group:GetName() + local grpcoord = group:GetCoordinate() + table.insert( SAM_Tbl, {grpname, grpcoord}) + table.insert( SEAD_Grps, grpname ) + end + end + self.SAM_Table = SAM_Tbl + -- make SAMs evasive + local mysead = SEAD:New( SEAD_Grps ) + mysead:SetEngagementRange(engagerange) + self.mysead = mysead + return self + end + + --- (Internal) Function to update SAM table and SEAD state + -- @param #MANTIS self + -- @return #MANTIS self + function MANTIS:_RefreshSAMTable() + self:F(self.lid.."Setting SAM Start States") + -- Requires SEAD 0.2.2 or better + -- get SAM Group + local SAM_SET = self.SAM_Group + local SAM_Grps = SAM_SET.Set --table of objects + local SAM_Tbl = {} -- table of SAM defense zones + local SEAD_Grps = {} -- table of SAM names to make evasive + local engagerange = self.engagerange -- firing range in % of max + --cycle through groups and set alarm state etc + for _i,_group in pairs (SAM_Grps) do + local group = _group group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --engagement will be 75% of firing range if group:IsGround() then local grpname = group:GetName() local grpcoord = group:GetCoordinate() - --local grpzone = ZONE_UNIT:New(grpname,group:GetUnit(1),5000) -- defense zone around each SAM site 5000 meters - --table.insert( SAM_Tbl, {grpname, grpcoord, grpzone}) table.insert( SAM_Tbl, {grpname, grpcoord}) -- make the table lighter, as I don't really use the zone here table.insert( SEAD_Grps, grpname ) end end self.SAM_Table = SAM_Tbl -- make SAMs evasive - SEAD:New( SEAD_Grps ) + if self.mysead ~= nil then + local mysead = self.mysead + mysead:UpdateSet( SEAD_Grps ) + end return self end @@ -556,11 +687,19 @@ do self:F(self.lid.."Starting MANTIS") self:SetSAMStartState() self.Detection = self:StartDetection() - + if self.advAwacs then + self.AWACS_Detection = self:StartAwacsDetection() + end + -- detection function local function check(detection) --get detected set local detset = detection:GetDetectedItemCoordinates() self:F("Check:", {detset}) + -- randomly update SAM Table + local rand = math.random(1,100) + if rand > 65 then -- 1/3 of cases + self:_RefreshSAMTable() + end -- switch SAMs on/off if (n)one of the detected groups is inside their reach local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates for _,_data in pairs (samset) do @@ -596,7 +735,6 @@ do end -- check advanced state local function checkadvstate() - if self.advanced then local interval, oldstate = self:_CheckAdvState() local newstate = self.adv_state if newstate ~= oldstate then @@ -606,6 +744,10 @@ do if self.MantisTimer.isrunning then self.MantisTimer:Stop() self.MantisTimer.isrunning = false + end -- stop Awacs timer + if self.MantisATimer.isrunning then + self.MantisATimer:Stop() + self.MantisATimer.isrunning = false end -- stop timer local samset = self:_GetSAMTable() -- table of i.1=names, i.2=coordinates for _,_data in pairs (samset) do @@ -621,25 +763,43 @@ do self.MantisTimer:Stop() self.MantisTimer.isrunning = false end + if self.MantisATimer.isrunning then + self.MantisATimer:Stop() + self.MantisATimer.isrunning = false + end self.MantisTimer = TIMER:New(check,self.Detection) self.MantisTimer:Start(5,interval,nil) self.MantisTimer.isrunning = true + if self.advAwacs then + self.MantisATimer = TIMER:New(check,self.AWACS_Detection) + self.MantisATimer:Start(15,interval,nil) + self.MantisATimer.isrunning = true + end end end -- end newstate vs oldstate - end -- end advanced end -- timers to run the system local interval = self.detectinterval self.MantisTimer = TIMER:New(check,self.Detection) self.MantisTimer:Start(5,interval,nil) self.MantisTimer.isrunning = true + -- Awacs timer + if self.advAwacs then + self.MantisATimer = TIMER:New(check,self.AWACS_Detection) + self.MantisATimer:Start(15,interval,nil) + self.MantisATimer.isrunning = true + end -- timer to relocate HQ and EWR - local relointerval = math.random(1800,3600) -- random between 30 and 60 mins - self.MantisReloTimer = TIMER:New(relocate) - self.MantisReloTimer:Start(relointerval,relointerval,nil) + if self.autorelocate then + local relointerval = math.random(1800,3600) -- random between 30 and 60 mins + self.MantisReloTimer = TIMER:New(relocate) + self.MantisReloTimer:Start(relointerval,relointerval,nil) + end -- timer for advanced state check - self.MantisAdvTimer = TIMER:New(checkadvstate) - self.MantisAdvTimer:Start(30,interval*5,nil) + if self.advanced then + self.MantisAdvTimer = TIMER:New(checkadvstate) + self.MantisAdvTimer:Start(30,interval*5,nil) + end return self end @@ -650,6 +810,9 @@ do if self.MantisTimer.isrunning then self.MantisTimer:Stop() end + if self.MantisATimer.isrunning then + self.MantisATimer:Stop() + end if self.autorelocate then self.MantisReloTimer:Stop() end