diff --git a/Moose Development/Moose/Functional/Mantis.lua b/Moose Development/Moose/Functional/Mantis.lua index 8ff110925..10f0bf1a6 100644 --- a/Moose Development/Moose/Functional/Mantis.lua +++ b/Moose Development/Moose/Functional/Mantis.lua @@ -191,7 +191,17 @@ MANTIS = { ShoradLink = false, ShoradTime = 600, ShoradActDistance = 15000, - UseEmOnOff = false, + UseEmOnOff = false, + TimeStamp = 0, + state2flag = false, +} + +--- Advanced state enumerator +-- @type MANTIS.AdvancedState +MANTIS.AdvancedState = { + GREEN = 0, + AMBER = 1, + RED = 2, } ----------------------------------------------------------------------- @@ -263,7 +273,10 @@ do self.ShoradLink = false self.ShoradTime = 600 self.ShoradActDistance = 15000 - -- TODO: add emissions on/off when available .... in 2 weeks + self.TimeStamp = timer.getAbsTime() + self.relointerval = math.random(1800,3600) -- random between 30 and 60 mins + self.state2flag = false + if EmOnOff then if EmOnOff == false then self.UseEmOnOff = false @@ -279,7 +292,7 @@ do end -- Inherit everything from BASE class. - local self = BASE:Inherit(self, BASE:New()) -- #MANTIS + local self = BASE:Inherit(self, FSM:New()) -- #MANTIS -- Set the string id for output to DCS.log file. self.lid=string.format("MANTIS %s | ", self.name) @@ -310,27 +323,122 @@ do end -- @field #string version - self.version="0.4.2" + self.version="0.5.1" self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) - return self - end + --- FSM Functions --- + + -- Start State. + self:SetStartState("Stopped") + + -- Add FSM transitions. + -- From State --> Event --> To State + self:AddTransition("Stopped", "Start", "Running") -- Start FSM. + self:AddTransition("*", "Status", "*") -- MANTIS status update. + self:AddTransition("*", "Relocating", "*") -- MANTIS HQ and EWR are relocating. + self:AddTransition("*", "GreenState", "*") -- MANTIS A SAM switching to GREEN state. + self:AddTransition("*", "RedState", "*") -- MANTIS A SAM switching to RED state. + self:AddTransition("*", "AdvStateChange", "*") -- MANTIS advanced mode state change. + self:AddTransition("*", "ShoradActivated", "*") -- MANTIS woke up a connected SHORAD. + self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. + + ------------------------ + --- Pseudo Functions --- + ------------------------ + + --- Triggers the FSM event "Start". Starts the MANTIS. Initializes parameters and starts event handlers. + -- @function [parent=#MANTIS] Start + -- @param #MANTIS self + + --- Triggers the FSM event "Start" after a delay. Starts the MANTIS. Initializes parameters and starts event handlers. + -- @function [parent=#MANTIS] __Start + -- @param #MANTIS self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Stop". Stops the MANTIS and all its event handlers. + -- @param #MANTIS self + + --- Triggers the FSM event "Stop" after a delay. Stops the MANTIS and all its event handlers. + -- @function [parent=#MANTIS] __Stop + -- @param #MANTIS self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Status". + -- @function [parent=#MANTIS] Status + -- @param #MANTIS self + + --- Triggers the FSM event "Status" after a delay. + -- @function [parent=#MANTIS] __Status + -- @param #MANTIS self + -- @param #number delay Delay in seconds. + + --- On After "Relocating" event. HQ and/or EWR moved. + -- @function [parent=#MANTIS] OnAfterRelocating + -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State + -- @return #MANTIS self + + --- On After "GreenState" event. A SAM group was switched to GREEN alert. + -- @function [parent=#MANTIS] OnAfterGreenState + -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State + -- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed + -- @return #MANTIS self + + --- On After "RedState" event. A SAM group was switched to RED alert. + -- @function [parent=#MANTIS] OnAfterRedState + -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State + -- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed + -- @return #MANTIS self + + --- On After "AdvStateChange" event. Advanced state changed, influencing detection speed. + -- @function [parent=#MANTIS] OnAfterAdvStateChange + -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State + -- @param #number Oldstate Old state - 0 = green, 1 = amber, 2 = red + -- @param #number Newstate New state - 0 = green, 1 = amber, 2 = red + -- @param #number Interval Calculated detection interval based on state and advanced feature setting + -- @return #MANTIS self + + --- On After "ShoradActivated" event. Mantis has activated a SHORAD. + -- @function [parent=#MANTIS] OnAfterShoradActivated + -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State + -- @param #string Name Name of the GROUP which SHORAD shall protect + -- @param #number Radius Radius around the named group to find SHORAD groups + -- @param #number Ontime Seconds the SHORAD will stay active + + return self + end ----------------------------------------------------------------------- -- MANTIS helper functions ----------------------------------------------------------------------- - --- [internal] Function to get the self.SAM_Table + --- [Internal] Function to get the self.SAM_Table -- @param #MANTIS self -- @return #table table function MANTIS:_GetSAMTable() + self:T(self.lid .. "GetSAMTable") return self.SAM_Table end - --- [internal] Function to set the self.SAM_Table + --- [Internal] Function to set the self.SAM_Table -- @param #MANTIS self -- @return #MANTIS self function MANTIS:_SetSAMTable(table) + self:T(self.lid .. "SetSAMTable") self.SAM_Table = table return self end @@ -339,41 +447,50 @@ do -- @param #MANTIS self -- @param #number radius Radius upon which detected objects will be grouped function MANTIS:SetEWRGrouping(radius) + self:T(self.lid .. "SetEWRGrouping") local radius = radius or 5000 self.grouping = radius + return self end --- Function to set the detection radius of the EWR in meters -- @param #MANTIS self -- @param #number radius Radius of the EWR detection zone function MANTIS:SetEWRRange(radius) + self:T(self.lid .. "SetEWRRange") local radius = radius or 80000 self.acceptrange = radius + return self end --- Function to set switch-on/off zone for the SAM sites in meters -- @param #MANTIS self -- @param #number radius Radius of the firing zone function MANTIS:SetSAMRadius(radius) + self:T(self.lid .. "SetSAMRadius") local radius = radius or 25000 self.checkradius = radius + return self end --- 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:SetSAMRange(range) + self:T(self.lid .. "SetSAMRange") local range = range or 75 if range < 0 or range > 100 then range = 75 end self.engagerange = range + return self end --- Function to set a new SAM firing engage range, use this method to adjust range while running MANTIS, e.g. for different setups day and night -- @param #MANTIS self -- @param #number range Percent of the max fire range function MANTIS:SetNewSAMRangeWhileRunning(range) + self:T(self.lid .. "SetNewSAMRangeWhileRunning") local range = range or 75 if range < 0 or range > 100 then range = 75 @@ -381,20 +498,32 @@ do self.engagerange = range self:_RefreshSAMTable() self.mysead.EngagementRange = range + return self end --- Function to set switch-on/off the debug state -- @param #MANTIS self -- @param #boolean onoff Set true to switch on function MANTIS:Debug(onoff) + self:T(self.lid .. "SetDebug") local onoff = onoff or false self.debug = onoff + if onoff then + -- Debug trace. + BASE:TraceOn() + BASE:TraceClass("MANTIS") + BASE:TraceLevel(1) + else + BASE:TraceOff() + end + return self end --- Function to get the HQ object for further use -- @param #MANTIS self -- @return Wrapper.GROUP#GROUP The HQ #GROUP object or *nil* if it doesn't exist function MANTIS:GetCommandCenter() + self:T(self.lid .. "GetCommandCenter") if self.HQ_CC then return self.HQ_CC else @@ -406,26 +535,31 @@ do -- @param #MANTIS self -- @param #string prefix Name of the AWACS group in the mission editor function MANTIS:SetAwacs(prefix) + self:T(self.lid .. "SetAwacs") if prefix ~= nil then if type(prefix) == "string" then self.AWACS_Prefix = prefix self.advAwacs = true end end + return self end --- Function to set AWACS detection range. Defaults to 250.000m (250km) - use **before** starting your Mantis! -- @param #MANTIS self -- @param #number range Detection range of the AWACS group function MANTIS:SetAwacsRange(range) - local range = range or 250000 - self.awacsrange = range + self:T(self.lid .. "SetAwacsRange") + local range = range or 250000 + self.awacsrange = range + return self end --- Function to set the HQ object for further use -- @param #MANTIS self -- @param Wrapper.GROUP#GROUP group The #GROUP object to be set as HQ function MANTIS:SetCommandCenter(group) + self:T(self.lid .. "SetCommandCenter") local group = group or nil if group ~= nil then if type(group) == "string" then @@ -436,14 +570,17 @@ do self.HQ_Template_CC = group:GetName() end end + return self end --- Function to set the detection interval -- @param #MANTIS self -- @param #number interval The interval in seconds function MANTIS:SetDetectInterval(interval) + self:T(self.lid .. "SetDetectInterval") local interval = interval or 30 self.detectinterval = interval + return self end --- Function to set Advanded Mode @@ -453,7 +590,8 @@ do -- @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}) + self:T(self.lid .. "SetAdvancedMode") + self:T({onoff, ratio}) local onoff = onoff or false local ratio = ratio or 100 if (type(self.HQ_Template_CC) == "string") and onoff and self.dynamic then @@ -461,53 +599,58 @@ do 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)) + self:I(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) + self:E(text) end + return self end --- Set using Emissions on/off instead of changing alarm state -- @param #MANTIS self -- @param #boolean switch Decide if we are changing alarm state or Emission state function MANTIS:SetUsingEmOnOff(switch) + self:T(self.lid .. "SetUsingEmOnOff") self.UseEmOnOff = switch or false + return self end --- [Internal] Function to check if HQ is alive -- @param #MANTIS self -- @return #boolean True if HQ is alive, else false function MANTIS:_CheckHQState() + self:T(self.lid .. "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 + if self.verbose then self:I(text) end -- start check if self.advanced then 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 - env.info(self.lid.." HQ is alive!") + self:T(self.lid.." HQ is alive!") return true else - env.info(self.lid.." HQ is dead!") + self:T(self.lid.." HQ is dead!") return false end end - end + end + return self 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() + self:T(self.lid .. "CheckEWRState") local text = self.lid.." Checking EWR State" - self:F(text) + self:T(text) local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) - if self.verbose then env.info(text) end + if self.verbose then self:I(text) end -- start check if self.advanced then local EWR_Group = self.Adv_EWR_Group @@ -521,24 +664,26 @@ do end end end - env.info(self.lid..string.format(" No of EWR alive is %d", nalive)) + self:T(self.lid..string.format(" No of EWR alive is %d", nalive)) if nalive > 0 then return true else return false end - end + end + return self 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) + function MANTIS:_CalcAdvState() + self:T(self.lid .. "CalcAdvState") + local text = self.lid.." Calculating Advanced State" + self:T(text) local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) - if self.verbose then env.info(text) end + if self.verbose then self:I(text) end -- start check local currstate = self.adv_state -- save curr state for comparison later local EWR_State = self:_CheckEWRState() @@ -557,9 +702,9 @@ do 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) + self:T(text) local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) - if self.verbose then env.info(text) end + if self.verbose then self:I(text) end return newinterval, currstate end @@ -568,7 +713,8 @@ do -- @param #boolean hq If true, will relocate HQ object -- @param #boolean ewr If true, will relocate EWR objects function MANTIS:SetAutoRelocate(hq, ewr) - self:F({hq, ewr}) + self:T(self.lid .. "SetAutoRelocate") + self:T({hq, ewr}) local hqrel = hq or false local ewrel = ewr or false if hqrel or ewrel then @@ -576,23 +722,25 @@ do self.autorelocateunits = { HQ = hqrel, EWR = ewrel } self:T({self.autorelocate, self.autorelocateunits}) end + return self end --- [Internal] Function to execute the relocation -- @param #MANTIS self function MANTIS:_RelocateGroups() - self:T(self.lid.." Relocating Groups") + self:T(self.lid .. "RelocateGroups") local text = self.lid.." Relocating Groups" local m= MESSAGE:New(text,10,"MANTIS",true):ToAllIf(self.debug) - if self.verbose then env.info(text) end + if self.verbose then self:I(text) end if self.autorelocate then -- relocate HQ - if self.autorelocateunits.HQ and self.HQ_CC then --only relocate if HQ exists + local HQGroup = self.HQ_CC + if self.autorelocateunits.HQ and self.HQ_CC and HQGroup:IsAlive() 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() - _hqgrp:RelocateGroundRandomInRadius(20,500,true,true) + --local m= MESSAGE:New(text,10,"MANTIS"):ToAll() + _hqgrp:RelocateGroundRandomInRadius(20,500,true,true) end --relocate EWR -- TODO: maybe dependent on AlarmState? Observed: SA11 SR only relocates if no objects in reach @@ -601,26 +749,27 @@ do local EWR_GRP = SET_GROUP:New():FilterPrefixes(self.EWR_Templates_Prefix):FilterCoalitions(self.Coalition):FilterOnce() local EWR_Grps = EWR_GRP.Set --table of objects in SET_GROUP for _,_grp in pairs (EWR_Grps) do - if _grp:IsGround() then + if _grp:IsAlive() and _grp:IsGround() then self:T(self.lid.." Relocating EWR ".._grp:GetName()) local text = self.lid.." Relocating EWR ".._grp:GetName() local m= MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) - if self.verbose then env.info(text) end + if self.verbose then self:I(text) end _grp:RelocateGroundRandomInRadius(20,500,true,true) end end end end + return self end - --- (Internal) Function to check if any object is in the given SAM zone + --- [Internal] Function to check if any object is in the given SAM zone -- @param #MANTIS self -- @param #table dectset Table of coordinates of detected items - -- @param samcoordinate Core.Point#COORDINATE Coordinate object. + -- @param Core.Point#COORDINATE samcoordinate Coordinate object. -- @return #boolean True if in any zone, else false -- @return #number Distance Target distance in meters or zero when no object is in zone function MANTIS:CheckObjectInZone(dectset, samcoordinate) - self:F(self.lid.."CheckObjectInZone Called") + self:T(self.lid.."CheckObjectInZone") -- check if non of the coordinate is in the given defense zone local radius = self.checkradius local set = dectset @@ -632,7 +781,7 @@ do local targetdistance = samcoordinate:DistanceFromPointVec2(coord) local text = string.format("Checking SAM at % s - Distance %d m - Target %s", samstring, targetdistance, dectstring) local m = MESSAGE:New(text,10,"Check"):ToAllIf(self.debug) - if self.verbose then env.info(self.lid..text) end + if self.verbose then self:I(self.lid..text) end -- end output to cross-check if targetdistance <= radius then return true, targetdistance @@ -641,11 +790,11 @@ do return false, 0 end - --- (Internal) Function to start the detection via EWR groups + --- [Internal] Function to start the detection via EWR groups -- @param #MANTIS self -- @return Functional.Detection #DETECTION_AREAS The running detection set function MANTIS:StartDetection() - self:F(self.lid.."Starting Detection") + self:T(self.lid.."Starting Detection") -- start detection local groupset = self.EWR_Group @@ -653,14 +802,14 @@ do local acceptrange = self.acceptrange or 80000 local interval = self.detectinterval or 60 - --@param Functional.Detection #DETECTION_AREAS _MANTISdetection [internal] The MANTIS detection object - _MANTISdetection = DETECTION_AREAS:New( groupset, grouping ) --[internal] Grouping detected objects to 5000m zones - _MANTISdetection:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER }) - _MANTISdetection:SetAcceptRange(acceptrange) - _MANTISdetection:SetRefreshTimeInterval(interval) - _MANTISdetection:Start() + --@param Functional.Detection #DETECTION_AREAS _MANTISdetection [Internal] The MANTIS detection object + local MANTISdetection = DETECTION_AREAS:New( groupset, grouping ) --[Internal] Grouping detected objects to 5000m zones + MANTISdetection:FilterCategories({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER }) + MANTISdetection:SetAcceptRange(acceptrange) + MANTISdetection:SetRefreshTimeInterval(interval) + MANTISdetection:Start() - function _MANTISdetection:OnAfterDetectedItem(From,Event,To,DetectedItem) + function MANTISdetection:OnAfterDetectedItem(From,Event,To,DetectedItem) --BASE:I( { From, Event, To, DetectedItem }) local debug = false if DetectedItem.IsDetected and debug then @@ -669,14 +818,14 @@ do local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) end end - return _MANTISdetection + return MANTISdetection end - --- (Internal) Function to start the detection via AWACS if defined as separate + --- [Internal] 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") + self:T(self.lid.."Starting Awacs Detection") -- start detection local group = self.AWACS_Prefix @@ -685,14 +834,14 @@ do --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() + --@param Functional.Detection #DETECTION_AREAS _MANTISdetection [Internal] The MANTIS detection object + local 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) + function MANTISAwacs:OnAfterDetectedItem(From,Event,To,DetectedItem) --BASE:I( { From, Event, To, DetectedItem }) local debug = false if DetectedItem.IsDetected and debug then @@ -701,15 +850,15 @@ do local m = MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) end end - return _MANTISAwacs + return MANTISAwacs end - --- (Internal) Function to set the SAM start state + --- [Internal] Function to set the SAM start state -- @param #MANTIS self -- @return #MANTIS self function MANTIS:SetSAMStartState() -- DONE: if using dynamic filtering, update SAM_Table and the (active) SEAD groups, pull req #1405/#1406 - self:F(self.lid.."Setting SAM Start States") + self:T(self.lid.."Setting SAM Start States") -- get SAM Group local SAM_SET = self.SAM_Group local SAM_Grps = SAM_SET.Set --table of objects @@ -742,11 +891,11 @@ do return self end - --- (Internal) Function to update SAM table and SEAD state + --- [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") + self:T(self.lid.."RefreshSAMTable") -- Requires SEAD 0.2.2 or better -- get SAM Group local SAM_SET = self.SAM_Group @@ -779,6 +928,7 @@ do -- @param Functional.Shorad#SHORAD Shorad The #SHORAD object -- @param #number Shoradtime Number of seconds #SHORAD stays active post wake-up function MANTIS:AddShorad(Shorad,Shoradtime) + self:T(self.lid.."AddShorad") local Shorad = Shorad or nil local ShoradTime = Shoradtime or 600 local ShoradLink = true @@ -787,184 +937,279 @@ do self.Shorad = Shorad --#SHORAD self.ShoradTime = Shoradtime -- #number end + return self end --- Function to unlink #MANTIS from a #SHORAD installation -- @param #MANTIS self function MANTIS:RemoveShorad() + self:T(self.lid.."RemoveShorad") self.ShoradLink = false + return self end ----------------------------------------------------------------------- -- MANTIS main functions ----------------------------------------------------------------------- - --- Function to set the SAM start state + --- [Internal] Check detection function + -- @param #MANTIS self + -- @param Functional.Detection#DETECTION_AREAS detection Detection object + -- @return #MANTIS self + function MANTIS:_Check(detection) + self:T(self.lid .. "Check") + --get detected set + local detset = detection:GetDetectedItemCoordinates() + self:T("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 + local samcoordinate = _data[2] + local name = _data[1] + local samgroup = GROUP:FindByName(name) + local IsInZone, Distance = self:CheckObjectInZone(detset, samcoordinate) + if IsInZone then --check any target in zone + if samgroup:IsAlive() then + -- switch on SAM + if self.UseEmOnOff then + -- TODO: add emissions on/off + --samgroup:SetAIOn() + samgroup:EnableEmission(true) + end + samgroup:OptionAlarmStateRed() + self:__RedState(1,samgroup) + -- link in to SHORAD if available + -- DONE: Test integration fully + if self.ShoradLink and Distance < self.ShoradActDistance then -- don't give SHORAD position away too early + local Shorad = self.Shorad + local radius = self.checkradius + local ontime = self.ShoradTime + Shorad:WakeUpShorad(name, radius, ontime) + self:__ShoradActivated(1,name, radius, ontime) + end + -- debug output + local text = string.format("SAM %s switched to alarm state RED!", name) + local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) + if self.verbose then self:I(self.lid..text) end + end --end alive + else + if samgroup:IsAlive() then + -- switch off SAM + if self.UseEmOnOff then + -- TODO: add emissions on/off + samgroup:EnableEmission(false) + self:__GreenState(1,samgroup) + --samgroup:SetAIOff() + else + samgroup:OptionAlarmStateGreen() + self:__GreenState(1,samgroup) + end + --samgroup:OptionROEWeaponFree() + --samgroup:SetAIOn() + local text = string.format("SAM %s switched to alarm state GREEN!", name) + local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) + if self.verbose then self:I(self.lid..text) end + end --end alive + end --end check + end --for for loop + return self + end + + --- [Internal] Relocation relay function -- @param #MANTIS self -- @return #MANTIS self - function MANTIS:Start() - 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 - local samcoordinate = _data[2] - local name = _data[1] - local samgroup = GROUP:FindByName(name) - local IsInZone, Distance = self:CheckObjectInZone(detset, samcoordinate) - if IsInZone then --check any target in zone + function MANTIS:_Relocate() + self:T(self.lid .. "Relocate") + self:_RelocateGroups() + return self + end + + --- [Internal] Check advanced state + -- @param #MANTIS self + -- @return #MANTIS self + function MANTIS:_CheckAdvState() + self:T(self.lid .. "CheckAdvSate") + local interval, oldstate = self:_CalcAdvState() + local newstate = self.adv_state + if newstate ~= oldstate then + -- deal with new state + self:__AdvStateChange(1,oldstate,newstate,interval) + if newstate == 2 then + -- switch alarm state RED + self.state2flag = true + 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 - -- switch on SAM if self.UseEmOnOff then -- TODO: add emissions on/off --samgroup:SetAIOn() samgroup:EnableEmission(true) end samgroup:OptionAlarmStateRed() - -- link in to SHORAD if available - -- DONE: Test integration fully - if self.ShoradLink and Distance < self.ShoradActDistance then -- don't give SHORAD position away too early - local Shorad = self.Shorad - local radius = self.checkradius - local ontime = self.ShoradTime - Shorad:WakeUpShorad(name, radius, ontime) - end - -- debug output - local text = string.format("SAM %s switched to alarm state RED!", name) - local m=MESSAGE:New(text,10,"MANTIS"):ToAllIf(self.debug) - if self.verbose then env.info(self.lid..text) end - end --end alive - else - if samgroup:IsAlive() then - -- switch off SAM - if self.UseEmOnOff then - -- TODO: add emissions on/off - samgroup:EnableEmission(false) - --samgroup:SetAIOff() - else - samgroup:OptionAlarmStateGreen() - end - --samgroup:OptionROEWeaponFree() - --samgroup:SetAIOn() - local text = string.format("SAM %s switched to alarm state GREEN!", name) - 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 - end --for for loop - end --end function - -- relocation relay function - local function relocate() - self:_RelocateGroups() - end - -- check advanced state - local function checkadvstate() - 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 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 - local name = _data[1] - local samgroup = GROUP:FindByName(name) - if samgroup:IsAlive() then - if self.UseEmOnOff then - -- TODO: add emissions on/off - --samgroup:SetAIOn() - samgroup:EnableEmission(true) - end - 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 - 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 - -- 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 + end -- end alive + end -- end for loop + elseif newstate <= 1 then + -- change MantisTimer to slow down or speed up + self.detectinterval = interval + self.state2flag = false + end + end -- end newstate vs oldstate + return self + end + + --- [Internal] Function to set start state + -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State + -- @return #MANTIS self + function MANTIS:onafterStart(From, Event, To) + self:T({From, Event, To}) + self:T(self.lid.."Starting MANTIS") + self:SetSAMStartState() + self.Detection = self:StartDetection() 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 - 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 - if self.advanced then - self.MantisAdvTimer = TIMER:New(checkadvstate) - self.MantisAdvTimer:Start(30,interval*5,nil) + self.AWACS_Detection = self:StartAwacsDetection() end + self:__Status(self.detectinterval) return self end - --- Function to stop MANTIS + --- [Internal] Before status function for MANTIS -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State -- @return #MANTIS self - function MANTIS:Stop() - if self.MantisTimer.isrunning then - self.MantisTimer:Stop() + function MANTIS:onbeforeStatus(From, Event, To) + self:T({From, Event, To}) + -- check detection + if not self.state2flag then + self:_Check(self.Detection) end - if self.MantisATimer.isrunning then - self.MantisATimer:Stop() + + -- check Awacs + if self.advAwacs and not self.state2flag then + self:_Check(self.AWACS_Detection) end + + -- relocate HQ and EWR if self.autorelocate then - self.MantisReloTimer:Stop() + local relointerval = self.relointerval + local thistime = timer.getAbsTime() + local timepassed = thistime - self.TimeStamp + + local halfintv = math.floor(timepassed / relointerval) + + --self:T({timepassed=timepassed, halfintv=halfintv}) + + if halfintv >= 1 then + self.TimeStamp = timer.getAbsTime() + self:_Relocate() + self:__Relocating(1) + end end + + -- timer for advanced state check if self.advanced then - self.MantisAdvTimer:Stop() + self:_CheckAdvState() end - return self + + return self + end + + --- [Internal] Status function for MANTIS + -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State + -- @return #MANTIS self + function MANTIS:onafterStatus(From,Event,To) + self:T({From, Event, To}) + local interval = self.detectinterval * -1 + self:__Status(interval) + return self end + --- [Internal] Function to stop MANTIS + -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State + -- @return #MANTIS self + function MANTIS:onafterStop(From, Event, To) + self:T({From, Event, To}) + return self + end + + --- [Internal] Function triggered by Event Relocating + -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State + -- @return #MANTIS self + function MANTIS:onafterRelocating(From, Event, To) + self:T({From, Event, To}) + return self + end + + --- [Internal] Function triggered by Event GreenState + -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State + -- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed + -- @return #MANTIS self + function MANTIS:onafterGreenState(From, Event, To, Group) + self:T({From, Event, To, Group}) + return self + end + + --- [Internal] Function triggered by Event RedState + -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State + -- @param Wrapper.Group#GROUP Group The GROUP object whose state was changed + -- @return #MANTIS self + function MANTIS:onafterRedState(From, Event, To, Group) + self:T({From, Event, To, Group}) + return self + end + + --- [Internal] Function triggered by Event AdvStateChange + -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State + -- @param #number Oldstate Old state - 0 = green, 1 = amber, 2 = red + -- @param #number Newstate New state - 0 = green, 1 = amber, 2 = red + -- @param #number Interval Calculated detection interval based on state and advanced feature setting + -- @return #MANTIS self + function MANTIS:onafterAdvStateChange(From, Event, To, Oldstate, Newstate, Interval) + self:T({From, Event, To, Oldstate, Newstate, Interval}) + return self + end + + --- [Internal] Function triggered by Event ShoradActivated + -- @param #MANTIS self + -- @param #string From The From State + -- @param #string Event The Event + -- @param #string To The To State + -- @param #string Name Name of the GROUP which SHORAD shall protect + -- @param #number Radius Radius around the named group to find SHORAD groups + -- @param #number Ontime Seconds the SHORAD will stay active + function MANTIS:onafterShoradActivated(From, Event, To, Name, Radius, Ontime) + self:T({From, Event, To, Name, Radius, Ontime}) + return self + end end ----------------------------------------------------------------------- -- MANTIS end diff --git a/Moose Development/Moose/Ops/CSAR.lua b/Moose Development/Moose/Ops/CSAR.lua index aa2015393..c3dda1dff 100644 --- a/Moose Development/Moose/Ops/CSAR.lua +++ b/Moose Development/Moose/Ops/CSAR.lua @@ -662,7 +662,9 @@ end -- @param #string _description (optional) Description. -- @param #boolean _randomPoint (optional) Random yes or no. -- @param #boolean _nomessage (optional) If true, don\'t send a message to SAR. -function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _nomessage) +-- @param #string unitname (optional) Name of the lost unit. +-- @param #string typename (optional) Type of plane. +function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _nomessage, unitname, typename) self:T(self.lid .. " _SpawnCsarAtZone") local freq = self:_GenerateADFFrequency() local _triggerZone = ZONE:New(_zone) -- trigger to use as reference position @@ -671,7 +673,9 @@ function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _ return end - local _description = _description or "Unknown" + local _description = _description or "PoW" + local unitname = unitname or "Old Rusty" + local typename = typename or "Phantom II" local pos = {} if _randomPoint then @@ -690,7 +694,7 @@ function CSAR:_SpawnCsarAtZone( _zone, _coalition, _description, _randomPoint, _ _country = country.id.UN_PEACEKEEPERS end - self:_AddCsar(_coalition, _country, pos, "PoW", _description, nil, freq, _nomessage, _description) + self:_AddCsar(_coalition, _country, pos, typename, unitname, _description, freq, _nomessage, _description) return self end @@ -702,12 +706,14 @@ end -- @param #string Description (optional) Description. -- @param #boolean RandomPoint (optional) Random yes or no. -- @param #boolean Nomessage (optional) If true, don\'t send a message to SAR. +-- @param #string unitname (optional) Name of the lost unit. +-- @param #string typename (optional) Type of plane. -- @usage If missions designers want to spawn downed pilots into the field, e.g. at mission begin, to give the helicopter guys works, they can do this like so: -- -- -- Create downed "Pilot Wagner" in #ZONE "CSAR_Start_1" at a random point for the blue coalition --- my_csar:SpawnCSARAtZone( "CSAR_Start_1", coalition.side.BLUE, "Pilot Wagner", true ) -function CSAR:SpawnCSARAtZone(Zone, Coalition, Description, RandomPoint, Nomessage) - self:_SpawnCsarAtZone(Zone, Coalition, Description, RandomPoint, Nomessage) +-- my_csar:SpawnCSARAtZone( "CSAR_Start_1", coalition.side.BLUE, "Wagner", true, false, "Charly-1-1", "F5E" ) +function CSAR:SpawnCSARAtZone(Zone, Coalition, Description, RandomPoint, Nomessage, Unitname, Typename) + self:_SpawnCsarAtZone(Zone, Coalition, Description, RandomPoint, Nomessage, Unitname, Typename) return self end @@ -1199,7 +1205,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG end if _time <= 0 or _distance < self.loadDistance then if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then - self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in, bugger!", self.messageTime, true) + self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true) return true else self.landedStatus[_lookupKeyHeli] = nil @@ -1211,7 +1217,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG else if (_distance < self.loadDistance) then if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then - self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in, honk!", self.messageTime, true) + self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true) return true else self:_PickupUnit(_heliUnit, _pilotName, _woundedGroup, _woundedGroupName) @@ -1252,7 +1258,7 @@ function CSAR:_CheckCloseWoundedGroup(_distance, _heliUnit, _heliName, _woundedG self:_DisplayMessageToSAR(_heliUnit, "Hovering above " .. _pilotName .. ". \n\nHold hover for " .. _time .. " seconds to winch them up. \n\nIf the countdown stops you\'re too far away!", self.messageTime, true) else if self.pilotmustopendoors and not self:_IsLoadingDoorOpen(_heliName) then - self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in, noob!", self.messageTime, true) + self:_DisplayMessageToSAR(_heliUnit, "Open the door to let me in!", self.messageTime, true) return true else self.hoverStatus[_lookupKeyHeli] = nil diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index f910323d7..8db791730 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -410,6 +410,7 @@ do -- my_ctld.enableHercules = true -- my_ctld.HercMinAngels = 155 -- for troop/cargo drop via chute in meters, ca 470 ft -- my_ctld.HercMaxAngels = 2000 -- for troop/cargo drop via chute in meters, ca 6000 ft +-- my_ctld.HercMaxSpeed = 77 -- 77mps or 270 kph or 150 kn -- -- Also, the following options need to be set to `true`: -- @@ -478,7 +479,7 @@ CTLD = { -- @field #table vhfbeacon Beacon info as #CTLD.ZoneBeacon --- Zone Type Info. --- @type CTLD. +-- @type CTLD.CargoZoneType CTLD.CargoZoneType = { LOAD = "load", DROP = "drop", @@ -651,6 +652,7 @@ function CTLD:New(Coalition, Prefixes, Alias) self.enableHercules = false self.HercMinAngels = 165 -- for troop/cargo drop via chute self.HercMaxAngels = 2000 -- for troop/cargo drop via chute + self.HercMaxSpeed = 77 -- 280 kph or 150kn eq 77 mps -- message suppression self.suppressmessages = false @@ -2283,10 +2285,12 @@ end local aheight = uheight - gheight -- height above ground local maxh = self.HercMinAngels-- 1500m local minh = self.HercMaxAngels -- 5000m - local mspeed = 2 -- 2 m/s - -- TODO:Add speed test for Herc, should not be above 280kph/150kn - --self:Tstring.format("%s Unit parameters: at %dm AGL with %dmps",self.lid,aheight,uspeed)) - if (aheight <= maxh) and (aheight >= minh) then + local maxspeed = self.HercMaxSpeed -- 77 mps + -- TODO: TEST - Speed test for Herc, should not be above 280kph/150kn + local kmspeed = uspeed * 3.6 + local knspeed = kmspeed / 1.86 + self:T(string.format("%s Unit parameters: at %dm AGL with %dmps | %dkph | %dkn",self.lid,aheight,uspeed,kmspeed,knspeed)) + if (aheight <= maxh) and (aheight >= minh) and (uspeed <= maxspeed) then -- yep within parameters outcome = true end @@ -2302,7 +2306,14 @@ end local inhover = self:IsCorrectHover(Unit) local htxt = "true" if not inhover then htxt = "false" end - local text = string.format("Hover parameters (autoload/drop):\n - Min height %dm \n - Max height %dm \n - Max speed 2mps \n - In parameter: %s", self.minimumHoverHeight, self.maximumHoverHeight, htxt) + local text = "" + if _SETTINGS:IsMetric() then + text = string.format("Hover parameters (autoload/drop):\n - Min height %dm \n - Max height %dm \n - Max speed 2mps \n - In parameter: %s", self.minimumHoverHeight, self.maximumHoverHeight, htxt) + else + local minheight = UTILS.MetersToFeet(self.minimumHoverHeight) + local maxheight = UTILS.MetersToFeet(self.maximumHoverHeight) + text = string.format("Hover parameters (autoload/drop):\n - Min height %dm \n - Max height %dm \n - Max speed 6fts \n - In parameter: %s", minheight, maxheight, htxt) + end self:_SendMessage(text, 10, false, Group) --local m = MESSAGE:New(text,10,"CTLD",false):ToGroup(Group) return self @@ -2316,9 +2327,16 @@ end local inhover = self:IsCorrectFlightParameters(Unit) local htxt = "true" if not inhover then htxt = "false" end - local minheight = UTILS.MetersToFeet(self.HercMinAngels) - local maxheight = UTILS.MetersToFeet(self.HercMaxAngels) - local text = string.format("Flight parameters (airdrop):\n - Min height %dft \n - Max height %dft \n - In parameter: %s", minheight, maxheight, htxt) + local text = "" + if _SETTINGS:IsImperial() then + local minheight = UTILS.MetersToFeet(self.HercMinAngels) + local maxheight = UTILS.MetersToFeet(self.HercMaxAngels) + text = string.format("Flight parameters (airdrop):\n - Min height %dft \n - Max height %dft \n - In parameter: %s", minheight, maxheight, htxt) + else + local minheight = self.HercMinAngels + local maxheight = self.HercMaxAngels + text = string.format("Flight parameters (airdrop):\n - Min height %dm \n - Max height %dm \n - In parameter: %s", minheight, maxheight, htxt) + end self:_SendMessage(text, 10, false, Group) --local m = MESSAGE:New(text,15,"CTLD",false):ToGroup(Group) return self