Merge branch 'develop' into FF/Ops

This commit is contained in:
Frank 2021-04-07 20:54:19 +02:00
commit 647fb6acbf
4 changed files with 128 additions and 35 deletions

View File

@ -20,7 +20,7 @@
-- @module Functional.Mantis -- @module Functional.Mantis
-- @image Functional.Mantis.jpg -- @image Functional.Mantis.jpg
-- Date: Feb 2021 -- Date: Apr 2021
------------------------------------------------------------------------- -------------------------------------------------------------------------
--- **MANTIS** class, extends #Core.Base#BASE --- **MANTIS** class, extends #Core.Base#BASE
@ -51,6 +51,7 @@
-- @field #number adv_state Advanced mode state tracker -- @field #number adv_state Advanced mode state tracker
-- @field #boolean advAwacs Boolean switch to use Awacs as a separate detection stream -- @field #boolean advAwacs Boolean switch to use Awacs as a separate detection stream
-- @field #number awacsrange Detection range of an optional Awacs unit -- @field #number awacsrange Detection range of an optional Awacs unit
-- @field #boolean UseAIOnOff Decide if we are using AI on/off (true) or AlarmState red/green (default)
-- @field Functional.Shorad#SHORAD Shorad SHORAD Object, if available -- @field Functional.Shorad#SHORAD Shorad SHORAD Object, if available
-- @field #boolean ShoradLink If true, #MANTIS has #SHORAD enabled -- @field #boolean ShoradLink If true, #MANTIS has #SHORAD enabled
-- @field #number ShoradTime Timer in seconds, how long #SHORAD will be active after a detection inside of the defense range -- @field #number ShoradTime Timer in seconds, how long #SHORAD will be active after a detection inside of the defense range
@ -189,7 +190,8 @@ MANTIS = {
Shorad = nil, Shorad = nil,
ShoradLink = false, ShoradLink = false,
ShoradTime = 600, ShoradTime = 600,
ShoradActDistance = 15000, ShoradActDistance = 15000,
UseAIOnOff = false,
} }
----------------------------------------------------------------------- -----------------------------------------------------------------------
@ -206,6 +208,7 @@ do
--@param #string coaltion Coalition side of your setup, e.g. "blue", "red" or "neutral" --@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) (optional) --@param #boolean dynamic Use constant (true) filtering or just filter once (false, default) (optional)
--@param #string awacs Group name of your Awacs (optional) --@param #string awacs Group name of your Awacs (optional)
--@param #boolean AIOnOff Make MANTIS switch AI on and off instead of changing the alarm state between RED and GREEN (optional)
--@return #MANTIS self --@return #MANTIS self
--@usage Start up your MANTIS with a basic setting --@usage Start up your MANTIS with a basic setting
-- --
@ -227,7 +230,7 @@ do
-- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")` -- `mybluemantis = MANTIS:New("bluemantis","Blue SAM","Blue EWR",nil,"blue",false,"Blue Awacs")`
-- `mybluemantis:Start()` -- `mybluemantis:Start()`
-- --
function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs) function MANTIS:New(name,samprefix,ewrprefix,hq,coaltion,dynamic,awacs, AIOnOff)
-- DONE: Create some user functions for these -- DONE: Create some user functions for these
-- DONE: Make HQ useful -- DONE: Make HQ useful
@ -260,6 +263,8 @@ do
self.ShoradLink = false self.ShoradLink = false
self.ShoradTime = 600 self.ShoradTime = 600
self.ShoradActDistance = 15000 self.ShoradActDistance = 15000
-- TODO: add emissions on/off when available .... in 2 weeks
self.UseAIOnOff = AIOnOff or false
if type(awacs) == "string" then if type(awacs) == "string" then
self.advAwacs = true self.advAwacs = true
@ -299,7 +304,7 @@ do
end end
-- @field #string version -- @field #string version
self.version="0.3.7" self.version="0.4.0"
self:I(string.format("***** Starting MANTIS Version %s *****", self.version)) self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
return self return self
@ -458,6 +463,13 @@ do
end end
end end
--- Set using AI on/off instead of changing alarm state
-- @param #MANTIS self
-- @param #boolean switch Decide if we are changing alarm state or AI state
function MANTIS:SetUsingAIOnOff(switch)
self.UseAIOnOff = switch or false
end
--- [Internal] Function to check if HQ is alive --- [Internal] Function to check if HQ is alive
-- @param #MANTIS self -- @param #MANTIS self
-- @return #boolean True if HQ is alive, else false -- @return #boolean True if HQ is alive, else false
@ -701,7 +713,12 @@ do
--cycle through groups and set alarm state etc --cycle through groups and set alarm state etc
for _i,_group in pairs (SAM_Grps) do for _i,_group in pairs (SAM_Grps) do
local group = _group local group = _group
group:OptionAlarmStateGreen() -- AI off -- TODO: add emissions on/off
if self.UseAIOnOff then
group:SetAIOff()
else
group:OptionAlarmStateGreen() -- AI off
end
group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --default engagement will be 75% of firing range group:SetOption(AI.Option.Ground.id.AC_ENGAGEMENT_RANGE_RESTRICTION,engagerange) --default engagement will be 75% of firing range
if group:IsGround() then if group:IsGround() then
local grpname = group:GetName() local grpname = group:GetName()
@ -804,7 +821,11 @@ do
local IsInZone, Distance = self:CheckObjectInZone(detset, samcoordinate) local IsInZone, Distance = self:CheckObjectInZone(detset, samcoordinate)
if IsInZone then --check any target in zone if IsInZone then --check any target in zone
if samgroup:IsAlive() then if samgroup:IsAlive() then
-- switch off SAM -- switch on SAM
if self.UseAIOnOff then
-- TODO: add emissions on/off
samgroup:SetAIOn()
end
samgroup:OptionAlarmStateRed() samgroup:OptionAlarmStateRed()
-- link in to SHORAD if available -- link in to SHORAD if available
-- DONE: Test integration fully -- DONE: Test integration fully
@ -822,7 +843,12 @@ do
else else
if samgroup:IsAlive() then if samgroup:IsAlive() then
-- switch off SAM -- switch off SAM
samgroup:OptionAlarmStateGreen() if self.UseAIOnOff then
-- TODO: add emissions on/off
samgroup:SetAIOff()
else
samgroup:OptionAlarmStateGreen()
end
--samgroup:OptionROEWeaponFree() --samgroup:OptionROEWeaponFree()
--samgroup:SetAIOn() --samgroup:SetAIOn()
local text = string.format("SAM %s switched to alarm state GREEN!", name) local text = string.format("SAM %s switched to alarm state GREEN!", name)
@ -857,6 +883,10 @@ do
local name = _data[1] local name = _data[1]
local samgroup = GROUP:FindByName(name) local samgroup = GROUP:FindByName(name)
if samgroup:IsAlive() then if samgroup:IsAlive() then
if self.UseAIOnOff then
-- TODO: add emissions on/off
samgroup:SetAIOn()
end
samgroup:OptionAlarmStateRed() samgroup:OptionAlarmStateRed()
end -- end alive end -- end alive
end -- end for loop end -- end for loop

View File

@ -38,6 +38,7 @@
-- @field #boolean DefendMavs Default true, intercept incoming AG-Missiles -- @field #boolean DefendMavs Default true, intercept incoming AG-Missiles
-- @field #number DefenseLowProb Default 70, minimum detection limit -- @field #number DefenseLowProb Default 70, minimum detection limit
-- @field #number DefenseHighProb Default 90, maximim detection limit -- @field #number DefenseHighProb Default 90, maximim detection limit
-- @field #boolean UseAIOnOff Decide if we are using AI on/off (true) or AlarmState red/green (default).
-- @extends Core.Base#BASE -- @extends Core.Base#BASE
--- *Good friends are worth defending.* Mr Tushman, Wonder (the Movie) --- *Good friends are worth defending.* Mr Tushman, Wonder (the Movie)
@ -94,7 +95,8 @@ SHORAD = {
DefendHarms = true, DefendHarms = true,
DefendMavs = true, DefendMavs = true,
DefenseLowProb = 70, DefenseLowProb = 70,
DefenseHighProb = 90, DefenseHighProb = 90,
UseAIOnOff = false,
} }
----------------------------------------------------------------------- -----------------------------------------------------------------------
@ -174,7 +176,8 @@ do
self.DefendMavs = true self.DefendMavs = true
self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin
self:I("*** SHORAD - Started Version 0.0.2") self.UseAIOnOff = false -- Decide if we are using AI on/off (true) or AlarmState red/green (default)
self:I("*** SHORAD - Started Version 0.1.0")
-- Set the string id for output to DCS.log file. -- Set the string id for output to DCS.log file.
self.lid=string.format("SHORAD %s | ", self.name) self.lid=string.format("SHORAD %s | ", self.name)
self:_InitState() self:_InitState()
@ -189,7 +192,11 @@ do
self:T({set = set}) self:T({set = set})
local aliveset = set:GetAliveSet() --#table local aliveset = set:GetAliveSet() --#table
for _,_group in pairs (aliveset) do for _,_group in pairs (aliveset) do
if self.UseAIOnOff then
_group:SetAIOff()
else
_group:OptionAlarmStateGreen() --Wrapper.Group#GROUP _group:OptionAlarmStateGreen() --Wrapper.Group#GROUP
end
end end
-- gather entropy -- gather entropy
for i=1,10 do for i=1,10 do
@ -272,6 +279,13 @@ do
self.Radius = radius self.Radius = radius
end end
--- Set using AI on/off instead of changing alarm state
-- @param #SHORAD self
-- @param #boolean switch Decide if we are changing alarm state or AI state
function SHORAD:SetUsingAIOnOff(switch)
self.UseAIOnOff = switch or false
end
--- Check if a HARM was fired --- Check if a HARM was fired
-- @param #SHORAD self -- @param #SHORAD self
-- @param #string WeaponName -- @param #string WeaponName
@ -396,7 +410,11 @@ do
local function SleepShorad(group) local function SleepShorad(group)
local groupname = group:GetName() local groupname = group:GetName()
self.ActiveGroups[groupname] = nil self.ActiveGroups[groupname] = nil
group:OptionAlarmStateGreen() if self.UseAIOnOff then
group:SetAIOff()
else
group:OptionAlarmStateGreen()
end
local text = string.format("Sleeping SHORAD %s", group:GetName()) local text = string.format("Sleeping SHORAD %s", group:GetName())
self:T(text) self:T(text)
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug) local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
@ -407,6 +425,9 @@ do
local text = string.format("Waking up SHORAD %s", _group:GetName()) local text = string.format("Waking up SHORAD %s", _group:GetName())
self:T(text) self:T(text)
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug) local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
if self.UseAIOnOff then
_group:SetAIOn()
end
_group:OptionAlarmStateRed() _group:OptionAlarmStateRed()
local groupname = _group:GetName() local groupname = _group:GetName()
if self.ActiveGroups[groupname] == nil then -- no timer yet for this group if self.ActiveGroups[groupname] == nil then -- no timer yet for this group

View File

@ -119,6 +119,7 @@ AIRWING = {
pointsTANKER = {}, pointsTANKER = {},
pointsAWACS = {}, pointsAWACS = {},
wingcommander = nil, wingcommander = nil,
markpoints = false,
} }
--- Squadron asset. --- Squadron asset.
@ -209,6 +210,7 @@ function AIRWING:New(warehousename, airwingname)
self.nflightsTANKERprobe=0 self.nflightsTANKERprobe=0
self.nflightsRecoveryTanker=0 self.nflightsRecoveryTanker=0
self.nflightsRescueHelo=0 self.nflightsRescueHelo=0
self.markpoints = false
------------------------ ------------------------
--- Pseudo Functions --- --- Pseudo Functions ---
@ -230,6 +232,24 @@ function AIRWING:New(warehousename, airwingname)
-- @function [parent=#AIRWING] __Stop -- @function [parent=#AIRWING] __Stop
-- @param #AIRWING self -- @param #AIRWING self
-- @param #number delay Delay in seconds. -- @param #number delay Delay in seconds.
--- On after "FlightOnMission" event. Triggered when an asset group starts a mission.
-- @function [parent=#AIRWING] OnAfterFlightOnMission
-- @param #AIRWING self
-- @param #string From The From state
-- @param #string Event The Event called
-- @param #string To The To state
-- @param Ops.FlightGroup#FLIGHTGROUP Flightgroup The Flightgroup on mission
-- @param Ops.Auftrag#AUFTRAG Mission The Auftrag of the Flightgroup
--- On after "AssetReturned" event. Triggered when an asset group returned to its airwing.
-- @function [parent=#AIRWING] OnAfterAssetReturned
-- @param #AIRWING self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.Squadron#SQUADRON Squadron The asset squadron.
-- @param #AIRWING.SquadronAsset Asset The asset that returned.
return self return self
end end
@ -647,6 +667,19 @@ function AIRWING:SetNumberTankerBoom(Nboom)
return self return self
end end
--- Set markers on the map for Patrol Points.
-- @param #AIRWING self
-- @param #boolean onoff Set to true to switch markers on.
-- @return #AIRWING self
function AIRWING:ShowPatrolPointMarkers(onoff)
if onoff then
self.markpoints = true
else
self.markpoints = false
end
return self
end
--- Set number of TANKER flights with Probe constantly in the air. --- Set number of TANKER flights with Probe constantly in the air.
-- @param #AIRWING self -- @param #AIRWING self
-- @param #number Nprobe Number of flights. Default 1. -- @param #number Nprobe Number of flights. Default 1.
@ -689,12 +722,10 @@ end
--- Update marker of the patrol point. --- Update marker of the patrol point.
-- @param #AIRWING.PatrolData point Patrol point table. -- @param #AIRWING.PatrolData point Patrol point table.
function AIRWING.UpdatePatrolPointMarker(point) function AIRWING.UpdatePatrolPointMarker(point)
local text=string.format("%s Occupied=%d\nheading=%03d, leg=%d NM, alt=%d ft, speed=%d kts",
local text=string.format("%s Occupied=%d\nheading=%03d, leg=%d NM, alt=%d ft, speed=%d kts", point.type, point.noccupied, point.heading, point.leg, point.altitude, point.speed)
point.type, point.noccupied, point.heading, point.leg, point.altitude, point.speed)
point.marker:UpdateText(text, 1)
point.marker:UpdateText(text, 1)
end end
@ -717,10 +748,12 @@ function AIRWING:NewPatrolPoint(Type, Coordinate, Altitude, Speed, Heading, LegL
patrolpoint.altitude=Altitude or math.random(10,20)*1000 patrolpoint.altitude=Altitude or math.random(10,20)*1000
patrolpoint.speed=Speed or 350 patrolpoint.speed=Speed or 350
patrolpoint.noccupied=0 patrolpoint.noccupied=0
patrolpoint.marker=MARKER:New(Coordinate, "New Patrol Point"):ToAll()
AIRWING.UpdatePatrolPointMarker(patrolpoint) if self.markpoints then
patrolpoint.marker=MARKER:New(Coordinate, "New Patrol Point"):ToAll()
AIRWING.UpdatePatrolPointMarker(patrolpoint)
end
return patrolpoint return patrolpoint
end end
@ -928,7 +961,7 @@ function AIRWING:CheckCAP()
patrol.noccupied=patrol.noccupied+1 patrol.noccupied=patrol.noccupied+1
AIRWING.UpdatePatrolPointMarker(patrol) if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
self:AddMission(missionCAP) self:AddMission(missionCAP)
@ -972,7 +1005,7 @@ function AIRWING:CheckTANKER()
patrol.noccupied=patrol.noccupied+1 patrol.noccupied=patrol.noccupied+1
AIRWING.UpdatePatrolPointMarker(patrol) if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
self:AddMission(mission) self:AddMission(mission)
@ -990,7 +1023,7 @@ function AIRWING:CheckTANKER()
patrol.noccupied=patrol.noccupied+1 patrol.noccupied=patrol.noccupied+1
AIRWING.UpdatePatrolPointMarker(patrol) if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
self:AddMission(mission) self:AddMission(mission)
@ -1018,7 +1051,7 @@ function AIRWING:CheckAWACS()
patrol.noccupied=patrol.noccupied+1 patrol.noccupied=patrol.noccupied+1
AIRWING.UpdatePatrolPointMarker(patrol) if self.markpoints then AIRWING.UpdatePatrolPointMarker(patrol) end
self:AddMission(mission) self:AddMission(mission)
@ -1431,7 +1464,9 @@ function AIRWING:onafterMissionCancel(From, Event, To, Mission)
-- Info message. -- Info message.
self:I(self.lid..string.format("Cancel mission %s", Mission.name)) self:I(self.lid..string.format("Cancel mission %s", Mission.name))
if Mission:IsPlanned() or Mission:IsQueued() or Mission:IsRequested() then local Ngroups = Mission:CountOpsGroups()
if Mission:IsPlanned() or Mission:IsQueued() or Mission:IsRequested() or Ngroups == 0 then
Mission:Done() Mission:Done()

View File

@ -1,11 +1,11 @@
--- **Ops** - Office of Military Intelligence. --- **Ops** - Office of Military Intelligence.
-- --
-- ## Main Features: -- **Main Features:**
-- --
-- * Detect and track contacts consistently -- * Detect and track contacts consistently
-- * Detect and track clusters of contacts consistently -- * Detect and track clusters of contacts consistently
-- * Use FSM events to link functionality into your scripts -- * Use FSM events to link functionality into your scripts
-- * Easy setup -- * Easy setup
-- --
-- === -- ===
-- --
@ -80,6 +80,7 @@
-- `local m = MESSAGE:New(text,15,"KGB"):ToAll()` -- `local m = MESSAGE:New(text,15,"KGB"):ToAll()`
-- `end` -- `end`
-- --
--
-- @field #INTEL -- @field #INTEL
INTEL = { INTEL = {
ClassName = "INTEL", ClassName = "INTEL",
@ -93,7 +94,7 @@ INTEL = {
ContactsUnknown = {}, ContactsUnknown = {},
Clusters = {}, Clusters = {},
clustercounter = 1, clustercounter = 1,
clusterradius = 10, clusterradius = 15,
} }
--- Detected item info. --- Detected item info.
@ -130,7 +131,7 @@ INTEL = {
--- INTEL class version. --- INTEL class version.
-- @field #string version -- @field #string version
INTEL.version="0.2.0" INTEL.version="0.2.1"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list
@ -350,7 +351,7 @@ function INTEL:RemoveRejectZone(RejectZone)
return self return self
end end
--- Set forget contacts time interval. For unknown contacts only. --- Set forget contacts time interval.
-- Previously known contacts that are not detected any more, are "lost" after this time. -- Previously known contacts that are not detected any more, are "lost" after this time.
-- This avoids fast oscillations between a contact being detected and undetected. -- This avoids fast oscillations between a contact being detected and undetected.
-- @param #INTEL self -- @param #INTEL self
@ -465,7 +466,7 @@ end
-- @param #number radius The radius of the clusters -- @param #number radius The radius of the clusters
-- @return #INTEL self -- @return #INTEL self
function INTEL:SetClusterRadius(radius) function INTEL:SetClusterRadius(radius)
local radius = radius or 10 local radius = radius or 15
self.clusterradius = radius self.clusterradius = radius
return self return self
end end
@ -1082,7 +1083,7 @@ function INTEL:CalcClusterThreatlevelSum(cluster)
threatlevel=threatlevel+contact.threatlevel threatlevel=threatlevel+contact.threatlevel
end end
cluster.threatlevelSum = threatlevel cluster.threatlevelSum = threatlevel
return threatlevel return threatlevel
end end
@ -1094,7 +1095,7 @@ function INTEL:CalcClusterThreatlevelAverage(cluster)
local threatlevel=self:CalcClusterThreatlevelSum(cluster) local threatlevel=self:CalcClusterThreatlevelSum(cluster)
threatlevel=threatlevel/cluster.size threatlevel=threatlevel/cluster.size
cluster.threatlevelAve = threatlevel cluster.threatlevelAve = threatlevel
return threatlevel return threatlevel
end end
@ -1114,7 +1115,7 @@ function INTEL:CalcClusterThreatlevelMax(cluster)
end end
end end
cluster.threatlevelMax = threatlevel cluster.threatlevelMax = threatlevel
return threatlevel return threatlevel
end end
@ -1155,7 +1156,7 @@ function INTEL:IsContactConnectedToCluster(contact, cluster)
--local dist=Contact.position:Get2DDistance(contact.position) --local dist=Contact.position:Get2DDistance(contact.position)
local dist=Contact.position:DistanceFromPointVec2(contact.position) local dist=Contact.position:DistanceFromPointVec2(contact.position)
local radius = self.clusterradius or 10 local radius = self.clusterradius or 15
if dist<radius*1000 then if dist<radius*1000 then
return true return true
end end
@ -1285,7 +1286,13 @@ function INTEL:UpdateClusterMarker(cluster)
local text=string.format("Cluster #%d. Size %d, Units %d, TLsum=%d", cluster.index, cluster.size, unitcount, cluster.threatlevelSum) local text=string.format("Cluster #%d. Size %d, Units %d, TLsum=%d", cluster.index, cluster.size, unitcount, cluster.threatlevelSum)
if not cluster.marker then if not cluster.marker then
cluster.marker=MARKER:New(cluster.coordinate, text):ToAll() if self.coalition == coalition.side.RED then
cluster.marker=MARKER:New(cluster.coordinate, text):ToRed()
elseif self.coalition == coalition.side.BLUE then
cluster.marker=MARKER:New(cluster.coordinate, text):ToBlue()
else
cluster.marker=MARKER:New(cluster.coordinate, text):ToNeutral()
end
else else
local refresh=false local refresh=false